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.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import dnsCallback from 'dns';
|
|
2
2
|
import http, { createServer } from 'http';
|
|
3
3
|
import https from 'https';
|
|
4
4
|
import zlib from 'zlib';
|
|
@@ -7,8 +7,8 @@ import Anthropic from '@anthropic-ai/sdk';
|
|
|
7
7
|
import OpenAI, { AzureOpenAI } from 'openai';
|
|
8
8
|
import { HarmBlockThreshold, HarmCategory, GoogleGenAI } from '@google/genai';
|
|
9
9
|
import Database2 from 'better-sqlite3';
|
|
10
|
-
import
|
|
11
|
-
import
|
|
10
|
+
import path36, { dirname } from 'path';
|
|
11
|
+
import fs33, { mkdirSync, writeFileSync, existsSync, readFileSync, promises } from 'fs';
|
|
12
12
|
import os10 from 'os';
|
|
13
13
|
import crypto, { randomUUID, randomBytes, webcrypto, timingSafeEqual, createHash } from 'crypto';
|
|
14
14
|
import { render, Box, Text, useStdout, useApp, useInput, Static } from 'ink';
|
|
@@ -30,6 +30,7 @@ import { simpleGit } from 'simple-git';
|
|
|
30
30
|
import PDFDocument from 'pdfkit';
|
|
31
31
|
import dns2 from 'dns/promises';
|
|
32
32
|
import net from 'net';
|
|
33
|
+
import { Agent } from 'undici';
|
|
33
34
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
34
35
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
35
36
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
@@ -1795,7 +1796,7 @@ var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SEC
|
|
|
1795
1796
|
var init_constants = __esm({
|
|
1796
1797
|
"src/constants.ts"() {
|
|
1797
1798
|
init_pricing();
|
|
1798
|
-
CASCADE_VERSION = "0.
|
|
1799
|
+
CASCADE_VERSION = "0.78.0";
|
|
1799
1800
|
CASCADE_CONFIG_FILE = ".cascade/config.json";
|
|
1800
1801
|
CASCADE_DB_FILE = ".cascade/memory.db";
|
|
1801
1802
|
CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
|
|
@@ -1808,64 +1809,88 @@ var init_constants = __esm({
|
|
|
1808
1809
|
DEFAULT_CONTEXT_LIMIT = 2e5;
|
|
1809
1810
|
DEFAULT_AUTO_SUMMARIZE_AT = 15e4;
|
|
1810
1811
|
MODELS = {
|
|
1811
|
-
// Anthropic
|
|
1812
|
-
"claude-
|
|
1813
|
-
id: "claude-
|
|
1814
|
-
name: "Claude
|
|
1812
|
+
// Anthropic — active Claude API models (2026-08-27).
|
|
1813
|
+
"claude-fable-5": {
|
|
1814
|
+
id: "claude-fable-5",
|
|
1815
|
+
name: "Claude Fable 5",
|
|
1815
1816
|
provider: "anthropic",
|
|
1816
|
-
contextWindow:
|
|
1817
|
+
contextWindow: 1e6,
|
|
1817
1818
|
isVisionCapable: true,
|
|
1818
|
-
inputCostPer1kTokens: 0.
|
|
1819
|
-
outputCostPer1kTokens: 0.
|
|
1820
|
-
maxOutputTokens:
|
|
1819
|
+
inputCostPer1kTokens: 0.01,
|
|
1820
|
+
outputCostPer1kTokens: 0.05,
|
|
1821
|
+
maxOutputTokens: 128e3,
|
|
1821
1822
|
supportsStreaming: true,
|
|
1822
1823
|
isLocal: false
|
|
1823
1824
|
},
|
|
1824
|
-
"claude-
|
|
1825
|
-
id: "claude-
|
|
1826
|
-
name: "Claude
|
|
1825
|
+
"claude-opus-5": {
|
|
1826
|
+
id: "claude-opus-5",
|
|
1827
|
+
name: "Claude Opus 5",
|
|
1827
1828
|
provider: "anthropic",
|
|
1828
|
-
contextWindow:
|
|
1829
|
+
contextWindow: 1e6,
|
|
1829
1830
|
isVisionCapable: true,
|
|
1830
|
-
inputCostPer1kTokens:
|
|
1831
|
-
outputCostPer1kTokens: 0.
|
|
1832
|
-
maxOutputTokens:
|
|
1831
|
+
inputCostPer1kTokens: 5e-3,
|
|
1832
|
+
outputCostPer1kTokens: 0.025,
|
|
1833
|
+
maxOutputTokens: 128e3,
|
|
1833
1834
|
supportsStreaming: true,
|
|
1834
1835
|
isLocal: false
|
|
1835
1836
|
},
|
|
1836
|
-
"claude-
|
|
1837
|
-
id: "claude-
|
|
1838
|
-
name: "Claude
|
|
1837
|
+
"claude-opus-4-8": {
|
|
1838
|
+
id: "claude-opus-4-8",
|
|
1839
|
+
name: "Claude Opus 4.8",
|
|
1839
1840
|
provider: "anthropic",
|
|
1840
|
-
contextWindow:
|
|
1841
|
+
contextWindow: 1e6,
|
|
1841
1842
|
isVisionCapable: true,
|
|
1842
|
-
inputCostPer1kTokens:
|
|
1843
|
-
outputCostPer1kTokens:
|
|
1844
|
-
maxOutputTokens:
|
|
1843
|
+
inputCostPer1kTokens: 5e-3,
|
|
1844
|
+
outputCostPer1kTokens: 0.025,
|
|
1845
|
+
maxOutputTokens: 128e3,
|
|
1845
1846
|
supportsStreaming: true,
|
|
1846
1847
|
isLocal: false
|
|
1847
1848
|
},
|
|
1848
|
-
"claude-
|
|
1849
|
-
id: "claude-
|
|
1850
|
-
name: "Claude
|
|
1849
|
+
"claude-opus-4-7": {
|
|
1850
|
+
id: "claude-opus-4-7",
|
|
1851
|
+
name: "Claude Opus 4.7",
|
|
1851
1852
|
provider: "anthropic",
|
|
1852
|
-
contextWindow:
|
|
1853
|
+
contextWindow: 1e6,
|
|
1853
1854
|
isVisionCapable: true,
|
|
1854
|
-
inputCostPer1kTokens:
|
|
1855
|
-
outputCostPer1kTokens:
|
|
1856
|
-
maxOutputTokens:
|
|
1855
|
+
inputCostPer1kTokens: 5e-3,
|
|
1856
|
+
outputCostPer1kTokens: 0.025,
|
|
1857
|
+
maxOutputTokens: 128e3,
|
|
1857
1858
|
supportsStreaming: true,
|
|
1858
1859
|
isLocal: false
|
|
1859
1860
|
},
|
|
1860
|
-
"claude-opus-4-
|
|
1861
|
-
id: "claude-opus-4-
|
|
1862
|
-
name: "Claude Opus 4.
|
|
1861
|
+
"claude-opus-4-6": {
|
|
1862
|
+
id: "claude-opus-4-6",
|
|
1863
|
+
name: "Claude Opus 4.6",
|
|
1864
|
+
provider: "anthropic",
|
|
1865
|
+
contextWindow: 1e6,
|
|
1866
|
+
isVisionCapable: true,
|
|
1867
|
+
inputCostPer1kTokens: 5e-3,
|
|
1868
|
+
outputCostPer1kTokens: 0.025,
|
|
1869
|
+
maxOutputTokens: 128e3,
|
|
1870
|
+
supportsStreaming: true,
|
|
1871
|
+
isLocal: false
|
|
1872
|
+
},
|
|
1873
|
+
"claude-opus-4-5": {
|
|
1874
|
+
id: "claude-opus-4-5-20251101",
|
|
1875
|
+
name: "Claude Opus 4.5",
|
|
1863
1876
|
provider: "anthropic",
|
|
1864
1877
|
contextWindow: 2e5,
|
|
1865
1878
|
isVisionCapable: true,
|
|
1866
|
-
inputCostPer1kTokens:
|
|
1867
|
-
outputCostPer1kTokens: 0.
|
|
1868
|
-
maxOutputTokens:
|
|
1879
|
+
inputCostPer1kTokens: 5e-3,
|
|
1880
|
+
outputCostPer1kTokens: 0.025,
|
|
1881
|
+
maxOutputTokens: 64e3,
|
|
1882
|
+
supportsStreaming: true,
|
|
1883
|
+
isLocal: false
|
|
1884
|
+
},
|
|
1885
|
+
"claude-sonnet-5": {
|
|
1886
|
+
id: "claude-sonnet-5",
|
|
1887
|
+
name: "Claude Sonnet 5",
|
|
1888
|
+
provider: "anthropic",
|
|
1889
|
+
contextWindow: 1e6,
|
|
1890
|
+
isVisionCapable: true,
|
|
1891
|
+
inputCostPer1kTokens: 2e-3,
|
|
1892
|
+
outputCostPer1kTokens: 0.01,
|
|
1893
|
+
maxOutputTokens: 128e3,
|
|
1869
1894
|
supportsStreaming: true,
|
|
1870
1895
|
isLocal: false
|
|
1871
1896
|
},
|
|
@@ -1873,76 +1898,135 @@ var init_constants = __esm({
|
|
|
1873
1898
|
id: "claude-sonnet-4-6",
|
|
1874
1899
|
name: "Claude Sonnet 4.6",
|
|
1875
1900
|
provider: "anthropic",
|
|
1901
|
+
contextWindow: 1e6,
|
|
1902
|
+
isVisionCapable: true,
|
|
1903
|
+
inputCostPer1kTokens: 3e-3,
|
|
1904
|
+
outputCostPer1kTokens: 0.015,
|
|
1905
|
+
maxOutputTokens: 64e3,
|
|
1906
|
+
supportsStreaming: true,
|
|
1907
|
+
isLocal: false
|
|
1908
|
+
},
|
|
1909
|
+
"claude-sonnet-4-5": {
|
|
1910
|
+
id: "claude-sonnet-4-5-20250929",
|
|
1911
|
+
name: "Claude Sonnet 4.5",
|
|
1912
|
+
provider: "anthropic",
|
|
1876
1913
|
contextWindow: 2e5,
|
|
1877
1914
|
isVisionCapable: true,
|
|
1878
1915
|
inputCostPer1kTokens: 3e-3,
|
|
1879
1916
|
outputCostPer1kTokens: 0.015,
|
|
1880
|
-
maxOutputTokens:
|
|
1917
|
+
maxOutputTokens: 64e3,
|
|
1881
1918
|
supportsStreaming: true,
|
|
1882
1919
|
isLocal: false
|
|
1883
1920
|
},
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1921
|
+
"claude-haiku-4-5": {
|
|
1922
|
+
id: "claude-haiku-4-5-20251001",
|
|
1923
|
+
name: "Claude Haiku 4.5",
|
|
1924
|
+
provider: "anthropic",
|
|
1925
|
+
contextWindow: 2e5,
|
|
1926
|
+
isVisionCapable: true,
|
|
1927
|
+
inputCostPer1kTokens: 1e-3,
|
|
1928
|
+
outputCostPer1kTokens: 5e-3,
|
|
1929
|
+
maxOutputTokens: 64e3,
|
|
1930
|
+
supportsStreaming: true,
|
|
1931
|
+
isLocal: false
|
|
1932
|
+
},
|
|
1933
|
+
// OpenAI — active chat-capable models used by Cascade's Chat Completions path.
|
|
1934
|
+
"gpt-5.6-sol": {
|
|
1935
|
+
id: "gpt-5.6-sol",
|
|
1936
|
+
name: "GPT-5.6 Sol",
|
|
1888
1937
|
provider: "openai",
|
|
1889
|
-
contextWindow:
|
|
1938
|
+
contextWindow: 105e4,
|
|
1939
|
+
isVisionCapable: true,
|
|
1940
|
+
inputCostPer1kTokens: 4e-3,
|
|
1941
|
+
outputCostPer1kTokens: 0.02,
|
|
1942
|
+
maxOutputTokens: 128e3,
|
|
1943
|
+
supportsStreaming: true,
|
|
1944
|
+
isLocal: false
|
|
1945
|
+
},
|
|
1946
|
+
"gpt-5.6-terra": {
|
|
1947
|
+
id: "gpt-5.6-terra",
|
|
1948
|
+
name: "GPT-5.6 Terra",
|
|
1949
|
+
provider: "openai",
|
|
1950
|
+
contextWindow: 105e4,
|
|
1951
|
+
isVisionCapable: true,
|
|
1952
|
+
inputCostPer1kTokens: 2e-3,
|
|
1953
|
+
outputCostPer1kTokens: 0.012,
|
|
1954
|
+
maxOutputTokens: 128e3,
|
|
1955
|
+
supportsStreaming: true,
|
|
1956
|
+
isLocal: false
|
|
1957
|
+
},
|
|
1958
|
+
"gpt-5.6-luna": {
|
|
1959
|
+
id: "gpt-5.6-luna",
|
|
1960
|
+
name: "GPT-5.6 Luna",
|
|
1961
|
+
provider: "openai",
|
|
1962
|
+
contextWindow: 105e4,
|
|
1963
|
+
isVisionCapable: true,
|
|
1964
|
+
inputCostPer1kTokens: 2e-4,
|
|
1965
|
+
outputCostPer1kTokens: 12e-4,
|
|
1966
|
+
maxOutputTokens: 128e3,
|
|
1967
|
+
supportsStreaming: true,
|
|
1968
|
+
isLocal: false
|
|
1969
|
+
},
|
|
1970
|
+
"gpt-5.5": {
|
|
1971
|
+
id: "gpt-5.5",
|
|
1972
|
+
name: "GPT-5.5",
|
|
1973
|
+
provider: "openai",
|
|
1974
|
+
contextWindow: 105e4,
|
|
1890
1975
|
isVisionCapable: true,
|
|
1891
1976
|
inputCostPer1kTokens: 5e-3,
|
|
1892
|
-
outputCostPer1kTokens: 0.
|
|
1893
|
-
maxOutputTokens:
|
|
1977
|
+
outputCostPer1kTokens: 0.03,
|
|
1978
|
+
maxOutputTokens: 128e3,
|
|
1894
1979
|
supportsStreaming: true,
|
|
1895
1980
|
isLocal: false
|
|
1896
1981
|
},
|
|
1897
|
-
"gpt-
|
|
1898
|
-
id: "gpt-
|
|
1899
|
-
name: "GPT-
|
|
1982
|
+
"gpt-5.4": {
|
|
1983
|
+
id: "gpt-5.4",
|
|
1984
|
+
name: "GPT-5.4",
|
|
1900
1985
|
provider: "openai",
|
|
1901
|
-
contextWindow:
|
|
1986
|
+
contextWindow: 105e4,
|
|
1902
1987
|
isVisionCapable: true,
|
|
1903
|
-
inputCostPer1kTokens:
|
|
1904
|
-
outputCostPer1kTokens:
|
|
1905
|
-
maxOutputTokens:
|
|
1988
|
+
inputCostPer1kTokens: 25e-4,
|
|
1989
|
+
outputCostPer1kTokens: 0.015,
|
|
1990
|
+
maxOutputTokens: 128e3,
|
|
1906
1991
|
supportsStreaming: true,
|
|
1907
1992
|
isLocal: false
|
|
1908
1993
|
},
|
|
1909
|
-
"gpt-4
|
|
1910
|
-
id: "gpt-4
|
|
1911
|
-
name: "GPT-4
|
|
1994
|
+
"gpt-5.4-mini": {
|
|
1995
|
+
id: "gpt-5.4-mini",
|
|
1996
|
+
name: "GPT-5.4 Mini",
|
|
1912
1997
|
provider: "openai",
|
|
1913
|
-
contextWindow:
|
|
1998
|
+
contextWindow: 4e5,
|
|
1914
1999
|
isVisionCapable: true,
|
|
1915
|
-
inputCostPer1kTokens:
|
|
1916
|
-
outputCostPer1kTokens:
|
|
1917
|
-
maxOutputTokens:
|
|
2000
|
+
inputCostPer1kTokens: 75e-5,
|
|
2001
|
+
outputCostPer1kTokens: 45e-4,
|
|
2002
|
+
maxOutputTokens: 128e3,
|
|
1918
2003
|
supportsStreaming: true,
|
|
1919
2004
|
isLocal: false
|
|
1920
2005
|
},
|
|
1921
|
-
"gpt-4
|
|
1922
|
-
id: "gpt-4
|
|
1923
|
-
name: "GPT-4
|
|
2006
|
+
"gpt-5.4-nano": {
|
|
2007
|
+
id: "gpt-5.4-nano",
|
|
2008
|
+
name: "GPT-5.4 Nano",
|
|
1924
2009
|
provider: "openai",
|
|
1925
|
-
contextWindow:
|
|
2010
|
+
contextWindow: 4e5,
|
|
1926
2011
|
isVisionCapable: true,
|
|
1927
|
-
inputCostPer1kTokens:
|
|
1928
|
-
outputCostPer1kTokens:
|
|
1929
|
-
maxOutputTokens:
|
|
2012
|
+
inputCostPer1kTokens: 2e-4,
|
|
2013
|
+
outputCostPer1kTokens: 125e-5,
|
|
2014
|
+
maxOutputTokens: 128e3,
|
|
1930
2015
|
supportsStreaming: true,
|
|
1931
2016
|
isLocal: false
|
|
1932
2017
|
},
|
|
1933
|
-
"gpt-
|
|
1934
|
-
id: "gpt-
|
|
1935
|
-
name: "GPT-
|
|
2018
|
+
"gpt-5.2": {
|
|
2019
|
+
id: "gpt-5.2",
|
|
2020
|
+
name: "GPT-5.2",
|
|
1936
2021
|
provider: "openai",
|
|
1937
|
-
contextWindow:
|
|
2022
|
+
contextWindow: 4e5,
|
|
1938
2023
|
isVisionCapable: true,
|
|
1939
|
-
inputCostPer1kTokens:
|
|
1940
|
-
outputCostPer1kTokens:
|
|
1941
|
-
maxOutputTokens:
|
|
2024
|
+
inputCostPer1kTokens: 175e-5,
|
|
2025
|
+
outputCostPer1kTokens: 0.014,
|
|
2026
|
+
maxOutputTokens: 128e3,
|
|
1942
2027
|
supportsStreaming: true,
|
|
1943
2028
|
isLocal: false
|
|
1944
2029
|
},
|
|
1945
|
-
// GPT-5 family (offline baseline pricing; the live fetch keeps it current).
|
|
1946
2030
|
"gpt-5": {
|
|
1947
2031
|
id: "gpt-5",
|
|
1948
2032
|
name: "GPT-5",
|
|
@@ -1979,79 +2063,149 @@ var init_constants = __esm({
|
|
|
1979
2063
|
supportsStreaming: true,
|
|
1980
2064
|
isLocal: false
|
|
1981
2065
|
},
|
|
1982
|
-
|
|
1983
|
-
|
|
1984
|
-
|
|
1985
|
-
"gpt-5.5": {
|
|
1986
|
-
id: "gpt-5.5",
|
|
1987
|
-
name: "GPT-5.5",
|
|
2066
|
+
"gpt-4.1": {
|
|
2067
|
+
id: "gpt-4.1",
|
|
2068
|
+
name: "GPT-4.1",
|
|
1988
2069
|
provider: "openai",
|
|
1989
|
-
contextWindow:
|
|
2070
|
+
contextWindow: 1047576,
|
|
1990
2071
|
isVisionCapable: true,
|
|
1991
|
-
inputCostPer1kTokens:
|
|
1992
|
-
outputCostPer1kTokens:
|
|
1993
|
-
maxOutputTokens:
|
|
2072
|
+
inputCostPer1kTokens: 2e-3,
|
|
2073
|
+
outputCostPer1kTokens: 8e-3,
|
|
2074
|
+
maxOutputTokens: 32768,
|
|
1994
2075
|
supportsStreaming: true,
|
|
1995
2076
|
isLocal: false
|
|
1996
2077
|
},
|
|
1997
|
-
"gpt-
|
|
1998
|
-
id: "gpt-
|
|
1999
|
-
name: "GPT-
|
|
2078
|
+
"gpt-4.1-mini": {
|
|
2079
|
+
id: "gpt-4.1-mini",
|
|
2080
|
+
name: "GPT-4.1 Mini",
|
|
2000
2081
|
provider: "openai",
|
|
2001
|
-
contextWindow:
|
|
2082
|
+
contextWindow: 1047576,
|
|
2002
2083
|
isVisionCapable: true,
|
|
2003
|
-
inputCostPer1kTokens:
|
|
2084
|
+
inputCostPer1kTokens: 4e-4,
|
|
2085
|
+
outputCostPer1kTokens: 16e-4,
|
|
2086
|
+
maxOutputTokens: 32768,
|
|
2087
|
+
supportsStreaming: true,
|
|
2088
|
+
isLocal: false
|
|
2089
|
+
},
|
|
2090
|
+
"gpt-4.1-nano": {
|
|
2091
|
+
id: "gpt-4.1-nano",
|
|
2092
|
+
name: "GPT-4.1 Nano",
|
|
2093
|
+
provider: "openai",
|
|
2094
|
+
contextWindow: 1047576,
|
|
2095
|
+
isVisionCapable: true,
|
|
2096
|
+
inputCostPer1kTokens: 1e-4,
|
|
2097
|
+
outputCostPer1kTokens: 4e-4,
|
|
2098
|
+
maxOutputTokens: 32768,
|
|
2099
|
+
supportsStreaming: true,
|
|
2100
|
+
isLocal: false
|
|
2101
|
+
},
|
|
2102
|
+
"gpt-4o": {
|
|
2103
|
+
id: "gpt-4o",
|
|
2104
|
+
name: "GPT-4o",
|
|
2105
|
+
provider: "openai",
|
|
2106
|
+
contextWindow: 128e3,
|
|
2107
|
+
isVisionCapable: true,
|
|
2108
|
+
inputCostPer1kTokens: 25e-4,
|
|
2004
2109
|
outputCostPer1kTokens: 0.01,
|
|
2005
|
-
maxOutputTokens:
|
|
2110
|
+
maxOutputTokens: 16e3,
|
|
2006
2111
|
supportsStreaming: true,
|
|
2007
2112
|
isLocal: false
|
|
2008
2113
|
},
|
|
2009
|
-
"gpt-
|
|
2010
|
-
id: "gpt-
|
|
2011
|
-
name: "GPT-
|
|
2114
|
+
"gpt-4o-mini": {
|
|
2115
|
+
id: "gpt-4o-mini",
|
|
2116
|
+
name: "GPT-4o Mini",
|
|
2012
2117
|
provider: "openai",
|
|
2013
|
-
contextWindow:
|
|
2118
|
+
contextWindow: 128e3,
|
|
2014
2119
|
isVisionCapable: true,
|
|
2015
|
-
inputCostPer1kTokens:
|
|
2016
|
-
outputCostPer1kTokens:
|
|
2017
|
-
maxOutputTokens:
|
|
2120
|
+
inputCostPer1kTokens: 15e-5,
|
|
2121
|
+
outputCostPer1kTokens: 6e-4,
|
|
2122
|
+
maxOutputTokens: 16e3,
|
|
2018
2123
|
supportsStreaming: true,
|
|
2019
2124
|
isLocal: false
|
|
2020
2125
|
},
|
|
2021
|
-
// Google
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2126
|
+
// Google Gemini — active GA + preview chat models. Gemini 2.0/1.5 are no
|
|
2127
|
+
// longer advertised because the Gemini API shut those generations down.
|
|
2128
|
+
"gemini-3.7-flash": {
|
|
2129
|
+
id: "gemini-3.7-flash",
|
|
2130
|
+
name: "Gemini 3.7 Flash",
|
|
2025
2131
|
provider: "gemini",
|
|
2026
|
-
contextWindow:
|
|
2132
|
+
contextWindow: 1048576,
|
|
2027
2133
|
isVisionCapable: true,
|
|
2028
|
-
inputCostPer1kTokens:
|
|
2029
|
-
outputCostPer1kTokens:
|
|
2030
|
-
maxOutputTokens:
|
|
2134
|
+
inputCostPer1kTokens: 75e-5,
|
|
2135
|
+
outputCostPer1kTokens: 375e-5,
|
|
2136
|
+
maxOutputTokens: 65536,
|
|
2031
2137
|
supportsStreaming: true,
|
|
2032
2138
|
isLocal: false
|
|
2033
2139
|
},
|
|
2034
|
-
"gemini-
|
|
2035
|
-
id: "gemini-
|
|
2036
|
-
name: "Gemini
|
|
2140
|
+
"gemini-3.6-flash": {
|
|
2141
|
+
id: "gemini-3.6-flash",
|
|
2142
|
+
name: "Gemini 3.6 Flash",
|
|
2037
2143
|
provider: "gemini",
|
|
2038
|
-
contextWindow:
|
|
2144
|
+
contextWindow: 1048576,
|
|
2039
2145
|
isVisionCapable: true,
|
|
2040
|
-
inputCostPer1kTokens:
|
|
2041
|
-
outputCostPer1kTokens:
|
|
2042
|
-
maxOutputTokens:
|
|
2146
|
+
inputCostPer1kTokens: 75e-5,
|
|
2147
|
+
outputCostPer1kTokens: 375e-5,
|
|
2148
|
+
maxOutputTokens: 65536,
|
|
2043
2149
|
supportsStreaming: true,
|
|
2044
2150
|
isLocal: false
|
|
2045
2151
|
},
|
|
2046
|
-
"gemini-
|
|
2047
|
-
id: "gemini-
|
|
2048
|
-
name: "Gemini
|
|
2152
|
+
"gemini-3.5-flash": {
|
|
2153
|
+
id: "gemini-3.5-flash",
|
|
2154
|
+
name: "Gemini 3.5 Flash",
|
|
2049
2155
|
provider: "gemini",
|
|
2050
|
-
contextWindow:
|
|
2156
|
+
contextWindow: 1048576,
|
|
2051
2157
|
isVisionCapable: true,
|
|
2052
|
-
inputCostPer1kTokens:
|
|
2053
|
-
outputCostPer1kTokens:
|
|
2054
|
-
maxOutputTokens:
|
|
2158
|
+
inputCostPer1kTokens: 15e-4,
|
|
2159
|
+
outputCostPer1kTokens: 9e-3,
|
|
2160
|
+
maxOutputTokens: 65536,
|
|
2161
|
+
supportsStreaming: true,
|
|
2162
|
+
isLocal: false
|
|
2163
|
+
},
|
|
2164
|
+
"gemini-3.5-flash-lite": {
|
|
2165
|
+
id: "gemini-3.5-flash-lite",
|
|
2166
|
+
name: "Gemini 3.5 Flash-Lite",
|
|
2167
|
+
provider: "gemini",
|
|
2168
|
+
contextWindow: 1048576,
|
|
2169
|
+
isVisionCapable: true,
|
|
2170
|
+
inputCostPer1kTokens: 3e-4,
|
|
2171
|
+
outputCostPer1kTokens: 25e-4,
|
|
2172
|
+
maxOutputTokens: 65536,
|
|
2173
|
+
supportsStreaming: true,
|
|
2174
|
+
isLocal: false
|
|
2175
|
+
},
|
|
2176
|
+
"gemini-3.1-flash-lite": {
|
|
2177
|
+
id: "gemini-3.1-flash-lite",
|
|
2178
|
+
name: "Gemini 3.1 Flash-Lite",
|
|
2179
|
+
provider: "gemini",
|
|
2180
|
+
contextWindow: 1048576,
|
|
2181
|
+
isVisionCapable: true,
|
|
2182
|
+
inputCostPer1kTokens: 25e-5,
|
|
2183
|
+
outputCostPer1kTokens: 15e-4,
|
|
2184
|
+
maxOutputTokens: 65536,
|
|
2185
|
+
supportsStreaming: true,
|
|
2186
|
+
isLocal: false
|
|
2187
|
+
},
|
|
2188
|
+
"gemini-3.1-pro-preview": {
|
|
2189
|
+
id: "gemini-3.1-pro-preview",
|
|
2190
|
+
name: "Gemini 3.1 Pro Preview",
|
|
2191
|
+
provider: "gemini",
|
|
2192
|
+
contextWindow: 1048576,
|
|
2193
|
+
isVisionCapable: true,
|
|
2194
|
+
inputCostPer1kTokens: 2e-3,
|
|
2195
|
+
outputCostPer1kTokens: 0.012,
|
|
2196
|
+
maxOutputTokens: 65536,
|
|
2197
|
+
supportsStreaming: true,
|
|
2198
|
+
isLocal: false
|
|
2199
|
+
},
|
|
2200
|
+
"gemini-3-flash-preview": {
|
|
2201
|
+
id: "gemini-3-flash-preview",
|
|
2202
|
+
name: "Gemini 3 Flash Preview",
|
|
2203
|
+
provider: "gemini",
|
|
2204
|
+
contextWindow: 1048576,
|
|
2205
|
+
isVisionCapable: true,
|
|
2206
|
+
inputCostPer1kTokens: 5e-4,
|
|
2207
|
+
outputCostPer1kTokens: 3e-3,
|
|
2208
|
+
maxOutputTokens: 65536,
|
|
2055
2209
|
supportsStreaming: true,
|
|
2056
2210
|
isLocal: false
|
|
2057
2211
|
},
|
|
@@ -2059,7 +2213,7 @@ var init_constants = __esm({
|
|
|
2059
2213
|
id: "gemini-2.5-pro",
|
|
2060
2214
|
name: "Gemini 2.5 Pro",
|
|
2061
2215
|
provider: "gemini",
|
|
2062
|
-
contextWindow:
|
|
2216
|
+
contextWindow: 1048576,
|
|
2063
2217
|
isVisionCapable: true,
|
|
2064
2218
|
inputCostPer1kTokens: 125e-5,
|
|
2065
2219
|
outputCostPer1kTokens: 0.01,
|
|
@@ -2071,10 +2225,22 @@ var init_constants = __esm({
|
|
|
2071
2225
|
id: "gemini-2.5-flash",
|
|
2072
2226
|
name: "Gemini 2.5 Flash",
|
|
2073
2227
|
provider: "gemini",
|
|
2074
|
-
contextWindow:
|
|
2228
|
+
contextWindow: 1048576,
|
|
2075
2229
|
isVisionCapable: true,
|
|
2076
|
-
inputCostPer1kTokens:
|
|
2077
|
-
outputCostPer1kTokens:
|
|
2230
|
+
inputCostPer1kTokens: 3e-4,
|
|
2231
|
+
outputCostPer1kTokens: 25e-4,
|
|
2232
|
+
maxOutputTokens: 65536,
|
|
2233
|
+
supportsStreaming: true,
|
|
2234
|
+
isLocal: false
|
|
2235
|
+
},
|
|
2236
|
+
"gemini-2.5-flash-lite": {
|
|
2237
|
+
id: "gemini-2.5-flash-lite",
|
|
2238
|
+
name: "Gemini 2.5 Flash-Lite",
|
|
2239
|
+
provider: "gemini",
|
|
2240
|
+
contextWindow: 1048576,
|
|
2241
|
+
isVisionCapable: true,
|
|
2242
|
+
inputCostPer1kTokens: 1e-4,
|
|
2243
|
+
outputCostPer1kTokens: 4e-4,
|
|
2078
2244
|
maxOutputTokens: 65536,
|
|
2079
2245
|
supportsStreaming: true,
|
|
2080
2246
|
isLocal: false
|
|
@@ -2144,43 +2310,57 @@ var init_constants = __esm({
|
|
|
2144
2310
|
_m.pricingUnknown = false;
|
|
2145
2311
|
}
|
|
2146
2312
|
T1_MODEL_PRIORITY = [
|
|
2313
|
+
"gpt-5.6-sol",
|
|
2314
|
+
"claude-fable-5",
|
|
2315
|
+
"claude-opus-5",
|
|
2147
2316
|
"gpt-5.5",
|
|
2148
2317
|
"gpt-5.4",
|
|
2318
|
+
"gemini-3.1-pro-preview",
|
|
2319
|
+
"claude-opus-4-8",
|
|
2320
|
+
"claude-sonnet-5",
|
|
2321
|
+
"gemini-3.7-flash",
|
|
2322
|
+
"gpt-5.2",
|
|
2149
2323
|
"gpt-5",
|
|
2150
|
-
"claude-opus-4",
|
|
2151
|
-
"claude-sonnet-4",
|
|
2152
2324
|
"gemini-2.5-pro",
|
|
2153
2325
|
"gpt-4.1",
|
|
2154
|
-
"gpt-4o"
|
|
2155
|
-
"gemini-1.5-pro"
|
|
2326
|
+
"gpt-4o"
|
|
2156
2327
|
];
|
|
2157
2328
|
T2_MODEL_PRIORITY = [
|
|
2329
|
+
"gpt-5.6-terra",
|
|
2330
|
+
"claude-sonnet-5",
|
|
2331
|
+
"gemini-3.7-flash",
|
|
2332
|
+
"gemini-3.6-flash",
|
|
2158
2333
|
"gpt-5.4-mini",
|
|
2159
2334
|
"gpt-5-mini",
|
|
2160
|
-
"claude-sonnet-4",
|
|
2335
|
+
"claude-sonnet-4-6",
|
|
2161
2336
|
"claude-haiku-4-5",
|
|
2162
|
-
"
|
|
2337
|
+
"gemini-3.5-flash",
|
|
2163
2338
|
"gemini-2.5-flash",
|
|
2164
2339
|
"gpt-4.1-mini",
|
|
2165
2340
|
"gpt-4o-mini",
|
|
2166
|
-
"gemini-2.0-flash",
|
|
2167
2341
|
"llama3:70b"
|
|
2168
2342
|
];
|
|
2169
2343
|
T3_MODEL_PRIORITY = [
|
|
2344
|
+
"gpt-5.6-luna",
|
|
2345
|
+
"gpt-5.4-nano",
|
|
2346
|
+
"gemini-3.5-flash-lite",
|
|
2347
|
+
"gemini-3.1-flash-lite",
|
|
2348
|
+
"claude-haiku-4-5",
|
|
2170
2349
|
"gpt-5-nano",
|
|
2350
|
+
"gemini-2.5-flash-lite",
|
|
2171
2351
|
"llama3.2:3b",
|
|
2172
2352
|
"mistral:7b",
|
|
2173
|
-
"claude-haiku-4-5",
|
|
2174
|
-
"claude-haiku-3-5",
|
|
2175
2353
|
"gpt-4.1-nano",
|
|
2176
2354
|
"gpt-4o-mini",
|
|
2177
|
-
"gemini-2.5-flash"
|
|
2178
|
-
"gemini-2.0-flash"
|
|
2355
|
+
"gemini-2.5-flash"
|
|
2179
2356
|
];
|
|
2180
2357
|
VISION_MODEL_PRIORITY = [
|
|
2181
|
-
"
|
|
2358
|
+
"gpt-5.6-sol",
|
|
2359
|
+
"claude-fable-5",
|
|
2360
|
+
"claude-sonnet-5",
|
|
2361
|
+
"gemini-3.7-flash",
|
|
2182
2362
|
"gpt-4o",
|
|
2183
|
-
"gemini-2.
|
|
2363
|
+
"gemini-2.5-flash",
|
|
2184
2364
|
"llava"
|
|
2185
2365
|
];
|
|
2186
2366
|
COMPLEXITY_T2_COUNT = {
|
|
@@ -2237,8 +2417,8 @@ function normalizeEndpoint(url) {
|
|
|
2237
2417
|
if (!raw) return "";
|
|
2238
2418
|
try {
|
|
2239
2419
|
const u = new URL(raw);
|
|
2240
|
-
const
|
|
2241
|
-
return `${u.protocol}//${u.host.toLowerCase()}${
|
|
2420
|
+
const path41 = stripTrailingSlashes(`${u.pathname}${u.search}`);
|
|
2421
|
+
return `${u.protocol}//${u.host.toLowerCase()}${path41}`;
|
|
2242
2422
|
} catch {
|
|
2243
2423
|
return raw.toLowerCase();
|
|
2244
2424
|
}
|
|
@@ -2370,7 +2550,7 @@ var CLIENT_VERSION_SEGMENT, REDIRECT_STATUSES, NULL_BODY_STATUSES, MAX_REDIRECTS
|
|
|
2370
2550
|
var init_net = __esm({
|
|
2371
2551
|
"src/utils/net.ts"() {
|
|
2372
2552
|
try {
|
|
2373
|
-
|
|
2553
|
+
dnsCallback.setDefaultResultOrder("ipv4first");
|
|
2374
2554
|
} catch {
|
|
2375
2555
|
}
|
|
2376
2556
|
CLIENT_VERSION_SEGMENT = {
|
|
@@ -3102,6 +3282,56 @@ var init_openai = __esm({
|
|
|
3102
3282
|
}
|
|
3103
3283
|
});
|
|
3104
3284
|
|
|
3285
|
+
// src/providers/cloud-model-catalog.json
|
|
3286
|
+
var cloud_model_catalog_default;
|
|
3287
|
+
var init_cloud_model_catalog = __esm({
|
|
3288
|
+
"src/providers/cloud-model-catalog.json"() {
|
|
3289
|
+
cloud_model_catalog_default = {
|
|
3290
|
+
schemaVersion: 1,
|
|
3291
|
+
verifiedAt: "2026-08-27",
|
|
3292
|
+
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.",
|
|
3293
|
+
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.",
|
|
3294
|
+
models: [
|
|
3295
|
+
{ id: "gpt-5.6-sol", name: "GPT-5.6 Sol", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3296
|
+
{ id: "gpt-5.6-terra", name: "GPT-5.6 Terra", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3297
|
+
{ id: "gpt-5.6-luna", name: "GPT-5.6 Luna", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3298
|
+
{ id: "gpt-5.5", name: "GPT-5.5", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3299
|
+
{ id: "gpt-5.4", name: "GPT-5.4", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3300
|
+
{ id: "gpt-5.4-mini", name: "GPT-5.4 Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3301
|
+
{ id: "gpt-5.4-nano", name: "GPT-5.4 Nano", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3302
|
+
{ id: "gpt-5", name: "GPT-5", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3303
|
+
{ id: "gpt-5-mini", name: "GPT-5 Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3304
|
+
{ id: "gpt-5-nano", name: "GPT-5 Nano", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3305
|
+
{ id: "gpt-4.1", name: "GPT-4.1", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 1047576, maxOutputTokens: 32768, vision: true, toolUse: true },
|
|
3306
|
+
{ id: "gpt-4.1-mini", name: "GPT-4.1 Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 1047576, maxOutputTokens: 32768, vision: true, toolUse: true },
|
|
3307
|
+
{ id: "gpt-4.1-nano", name: "GPT-4.1 Nano", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 1047576, maxOutputTokens: 32768, vision: true, toolUse: true },
|
|
3308
|
+
{ id: "gpt-4o", name: "GPT-4o", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 128e3, maxOutputTokens: 16384, vision: true, toolUse: true },
|
|
3309
|
+
{ id: "gpt-4o-mini", name: "GPT-4o Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 128e3, maxOutputTokens: 16384, vision: true, toolUse: true },
|
|
3310
|
+
{ id: "claude-fable-5", name: "Claude Fable 5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3311
|
+
{ id: "claude-opus-5", name: "Claude Opus 5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3312
|
+
{ id: "claude-opus-4-8", name: "Claude Opus 4.8", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3313
|
+
{ id: "claude-opus-4-7", name: "Claude Opus 4.7", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3314
|
+
{ id: "claude-opus-4-6", name: "Claude Opus 4.6", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, vision: true, toolUse: true },
|
|
3315
|
+
{ id: "claude-opus-4-5-20251101", name: "Claude Opus 4.5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 2e5, vision: true, toolUse: true },
|
|
3316
|
+
{ id: "claude-sonnet-5", name: "Claude Sonnet 5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3317
|
+
{ id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
3318
|
+
{ id: "claude-sonnet-4-5-20250929", name: "Claude Sonnet 4.5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 2e5, vision: true, toolUse: true },
|
|
3319
|
+
{ id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 2e5, maxOutputTokens: 64e3, vision: true, toolUse: true },
|
|
3320
|
+
{ id: "gemini-3.7-flash", name: "Gemini 3.7 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
3321
|
+
{ id: "gemini-3.6-flash", name: "Gemini 3.6 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
3322
|
+
{ id: "gemini-3.5-flash", name: "Gemini 3.5 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
3323
|
+
{ id: "gemini-3.5-flash-lite", name: "Gemini 3.5 Flash-Lite", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
3324
|
+
{ id: "gemini-3.1-flash-lite", name: "Gemini 3.1 Flash-Lite", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
3325
|
+
{ id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro", providers: ["gemini"], lifecycle: "preview", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
3326
|
+
{ id: "gemini-3-flash-preview", name: "Gemini 3 Flash", providers: ["gemini"], lifecycle: "preview", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
3327
|
+
{ id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
3328
|
+
{ id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
3329
|
+
{ id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash-Lite", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true }
|
|
3330
|
+
]
|
|
3331
|
+
};
|
|
3332
|
+
}
|
|
3333
|
+
});
|
|
3334
|
+
|
|
3105
3335
|
// src/providers/azure.ts
|
|
3106
3336
|
var azure_exports = {};
|
|
3107
3337
|
__export(azure_exports, {
|
|
@@ -3110,36 +3340,81 @@ __export(azure_exports, {
|
|
|
3110
3340
|
azureModelForDeployment: () => azureModelForDeployment,
|
|
3111
3341
|
inferAzureBaseModel: () => inferAzureBaseModel
|
|
3112
3342
|
});
|
|
3343
|
+
function compactModelId(id) {
|
|
3344
|
+
return id.toLowerCase().replace(/[^a-z0-9.]/g, "");
|
|
3345
|
+
}
|
|
3346
|
+
function catalogEntry(id) {
|
|
3347
|
+
const n = id.toLowerCase();
|
|
3348
|
+
return CLOUD_MODELS.find((m) => m.id.toLowerCase() === n);
|
|
3349
|
+
}
|
|
3113
3350
|
function inferAzureBaseModel(deploymentName) {
|
|
3114
3351
|
const n = deploymentName.toLowerCase();
|
|
3115
|
-
|
|
3352
|
+
const compact = compactModelId(n);
|
|
3353
|
+
const hasBareGpt56Alias = compact.includes("gpt5.6");
|
|
3354
|
+
for (const model of AZURE_CATALOG) {
|
|
3355
|
+
if (hasBareGpt56Alias && model.id === "gpt-5") continue;
|
|
3356
|
+
if (n.includes(model.id.toLowerCase()) || compact.includes(compactModelId(model.id))) {
|
|
3357
|
+
return model.id;
|
|
3358
|
+
}
|
|
3359
|
+
}
|
|
3360
|
+
if (hasBareGpt56Alias) return "gpt-5.6-sol";
|
|
3361
|
+
const gpt5At = compact.indexOf("gpt5");
|
|
3362
|
+
if (gpt5At !== -1) {
|
|
3363
|
+
const suffix = compact.slice(gpt5At + "gpt5".length);
|
|
3364
|
+
if (suffix.includes("nano")) return "gpt-5-nano";
|
|
3365
|
+
if (suffix.includes("mini")) return "gpt-5-mini";
|
|
3366
|
+
return "gpt-5";
|
|
3367
|
+
}
|
|
3368
|
+
if (compact.includes("gpt4.1nano")) return "gpt-4.1-nano";
|
|
3369
|
+
if (compact.includes("gpt4.1mini")) return "gpt-4.1-mini";
|
|
3370
|
+
if (compact.includes("gpt4.1")) return "gpt-4.1";
|
|
3371
|
+
if (compact.includes("gpt4omini")) return "gpt-4o-mini";
|
|
3372
|
+
if (compact.includes("gpt4o")) return "gpt-4o";
|
|
3116
3373
|
return null;
|
|
3117
3374
|
}
|
|
3375
|
+
function catalogModelInfo(baseModelId) {
|
|
3376
|
+
const entry = catalogEntry(baseModelId);
|
|
3377
|
+
if (!entry?.contextWindow) return void 0;
|
|
3378
|
+
return {
|
|
3379
|
+
id: entry.id,
|
|
3380
|
+
name: entry.name,
|
|
3381
|
+
provider: "openai",
|
|
3382
|
+
contextWindow: entry.contextWindow,
|
|
3383
|
+
isVisionCapable: entry.vision ?? false,
|
|
3384
|
+
inputCostPer1kTokens: 0,
|
|
3385
|
+
outputCostPer1kTokens: 0,
|
|
3386
|
+
maxOutputTokens: entry.maxOutputTokens ?? 16e3,
|
|
3387
|
+
supportsStreaming: true,
|
|
3388
|
+
isLocal: false,
|
|
3389
|
+
supportsToolUse: entry.toolUse ?? true,
|
|
3390
|
+
pricingUnknown: true
|
|
3391
|
+
};
|
|
3392
|
+
}
|
|
3118
3393
|
function azureModelForDeployment(cfg) {
|
|
3119
3394
|
if (cfg.type !== "azure" || !cfg.deploymentName?.trim()) return null;
|
|
3120
3395
|
const id = cfg.deploymentName.trim();
|
|
3121
3396
|
const name = cfg.label?.trim() || id;
|
|
3122
3397
|
const baseModelId = cfg.model?.trim() || inferAzureBaseModel(id) || void 0;
|
|
3123
|
-
const base = baseModelId ? MODELS[baseModelId] : void 0;
|
|
3124
|
-
if (base) {
|
|
3398
|
+
const base = baseModelId ? MODELS[baseModelId] ?? catalogModelInfo(baseModelId) : void 0;
|
|
3399
|
+
if (base && baseModelId) {
|
|
3125
3400
|
const azurePrice = resolvePricing(
|
|
3126
3401
|
{ id, provider: "azure", isLocal: false, baseModelId },
|
|
3127
3402
|
{ region: cfg.region }
|
|
3128
3403
|
);
|
|
3404
|
+
const exactAzurePrice = !azurePrice.unknown && !azurePrice.estimatedFromProvider;
|
|
3405
|
+
const nativeToolsOnThisTransport = /^gpt-5\.6-(?:sol|terra|luna)$/i.test(baseModelId) ? false : base.supportsToolUse ?? true;
|
|
3129
3406
|
return {
|
|
3130
3407
|
...base,
|
|
3131
3408
|
id,
|
|
3132
|
-
// callable deployment name
|
|
3133
3409
|
name,
|
|
3134
3410
|
provider: "azure",
|
|
3135
3411
|
baseModelId,
|
|
3136
|
-
|
|
3137
|
-
|
|
3138
|
-
...azurePrice.unknown ? {} : {
|
|
3412
|
+
supportsToolUse: nativeToolsOnThisTransport,
|
|
3413
|
+
...!azurePrice.unknown ? {
|
|
3139
3414
|
inputCostPer1kTokens: azurePrice.input,
|
|
3140
3415
|
outputCostPer1kTokens: azurePrice.output,
|
|
3141
|
-
pricingUnknown:
|
|
3142
|
-
}
|
|
3416
|
+
pricingUnknown: !exactAzurePrice
|
|
3417
|
+
} : { pricingUnknown: true }
|
|
3143
3418
|
};
|
|
3144
3419
|
}
|
|
3145
3420
|
return {
|
|
@@ -3154,39 +3429,34 @@ function azureModelForDeployment(cfg) {
|
|
|
3154
3429
|
maxOutputTokens: 16e3,
|
|
3155
3430
|
supportsStreaming: true,
|
|
3156
3431
|
isLocal: false,
|
|
3157
|
-
supportsToolUse: true
|
|
3432
|
+
supportsToolUse: true,
|
|
3433
|
+
pricingUnknown: true
|
|
3158
3434
|
};
|
|
3159
3435
|
}
|
|
3160
|
-
|
|
3436
|
+
function stripTrailingSlashes3(value) {
|
|
3437
|
+
let end = value.length;
|
|
3438
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) end--;
|
|
3439
|
+
return value.slice(0, end);
|
|
3440
|
+
}
|
|
3441
|
+
var DEFAULT_AZURE_API_VERSION, CLOUD_MODELS, AZURE_CATALOG, AZURE_BASE_MODELS, AzureOpenAIProvider;
|
|
3161
3442
|
var init_azure = __esm({
|
|
3162
3443
|
"src/providers/azure.ts"() {
|
|
3163
3444
|
init_constants();
|
|
3164
3445
|
init_openai();
|
|
3165
3446
|
init_pricing();
|
|
3447
|
+
init_cloud_model_catalog();
|
|
3166
3448
|
DEFAULT_AZURE_API_VERSION = "2024-12-01-preview";
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
[/gpt-?5\.4/, "gpt-5.4"],
|
|
3171
|
-
[/gpt-?5.*nano/, "gpt-5-nano"],
|
|
3172
|
-
[/gpt-?5.*mini/, "gpt-5-mini"],
|
|
3173
|
-
[/gpt-?5/, "gpt-5"],
|
|
3174
|
-
[/gpt-?4\.1-nano/, "gpt-4.1-nano"],
|
|
3175
|
-
[/gpt-?4\.1-mini/, "gpt-4.1-mini"],
|
|
3176
|
-
[/gpt-?4\.1/, "gpt-4.1"],
|
|
3177
|
-
[/gpt-?4o-mini/, "gpt-4o-mini"],
|
|
3178
|
-
[/gpt-?4o/, "gpt-4o"]
|
|
3179
|
-
];
|
|
3180
|
-
AZURE_BASE_MODELS = AZURE_BASE_MODEL_RULES.map(([, id]) => id);
|
|
3449
|
+
CLOUD_MODELS = cloud_model_catalog_default.models;
|
|
3450
|
+
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));
|
|
3451
|
+
AZURE_BASE_MODELS = AZURE_CATALOG.map((m) => m.id);
|
|
3181
3452
|
AzureOpenAIProvider = class extends OpenAIProvider {
|
|
3182
3453
|
constructor(config, model) {
|
|
3183
3454
|
const rawUrl = config.baseUrl ?? AZURE_BASE_URL_TEMPLATE.replace("{resource}", "YOUR_RESOURCE");
|
|
3184
|
-
const endpoint = rawUrl
|
|
3455
|
+
const endpoint = stripTrailingSlashes3(rawUrl);
|
|
3185
3456
|
super(
|
|
3186
3457
|
{
|
|
3187
3458
|
...config,
|
|
3188
3459
|
baseUrl: endpoint
|
|
3189
|
-
// Kept for superclass compatibility if it reads it
|
|
3190
3460
|
},
|
|
3191
3461
|
model
|
|
3192
3462
|
);
|
|
@@ -4170,16 +4440,16 @@ var require_isarray = __commonJS({
|
|
|
4170
4440
|
}
|
|
4171
4441
|
});
|
|
4172
4442
|
|
|
4173
|
-
// node_modules/
|
|
4443
|
+
// node_modules/readable-stream/lib/internal/streams/stream.js
|
|
4174
4444
|
var require_stream = __commonJS({
|
|
4175
|
-
"node_modules/
|
|
4445
|
+
"node_modules/readable-stream/lib/internal/streams/stream.js"(exports, module) {
|
|
4176
4446
|
module.exports = __require("stream");
|
|
4177
4447
|
}
|
|
4178
4448
|
});
|
|
4179
4449
|
|
|
4180
|
-
// node_modules/
|
|
4450
|
+
// node_modules/readable-stream/node_modules/safe-buffer/index.js
|
|
4181
4451
|
var require_safe_buffer = __commonJS({
|
|
4182
|
-
"node_modules/
|
|
4452
|
+
"node_modules/readable-stream/node_modules/safe-buffer/index.js"(exports, module) {
|
|
4183
4453
|
var buffer = __require("buffer");
|
|
4184
4454
|
var Buffer2 = buffer.Buffer;
|
|
4185
4455
|
function copyProps(src, dst) {
|
|
@@ -4297,7 +4567,7 @@ var require_util = __commonJS({
|
|
|
4297
4567
|
typeof arg === "undefined";
|
|
4298
4568
|
}
|
|
4299
4569
|
exports.isPrimitive = isPrimitive;
|
|
4300
|
-
exports.isBuffer = Buffer.isBuffer;
|
|
4570
|
+
exports.isBuffer = __require("buffer").Buffer.isBuffer;
|
|
4301
4571
|
function objectToString(o) {
|
|
4302
4572
|
return Object.prototype.toString.call(o);
|
|
4303
4573
|
}
|
|
@@ -4350,9 +4620,9 @@ var require_inherits = __commonJS({
|
|
|
4350
4620
|
}
|
|
4351
4621
|
});
|
|
4352
4622
|
|
|
4353
|
-
// node_modules/
|
|
4623
|
+
// node_modules/readable-stream/lib/internal/streams/BufferList.js
|
|
4354
4624
|
var require_BufferList = __commonJS({
|
|
4355
|
-
"node_modules/
|
|
4625
|
+
"node_modules/readable-stream/lib/internal/streams/BufferList.js"(exports, module) {
|
|
4356
4626
|
function _classCallCheck(instance, Constructor) {
|
|
4357
4627
|
if (!(instance instanceof Constructor)) {
|
|
4358
4628
|
throw new TypeError("Cannot call a class as a function");
|
|
@@ -4427,9 +4697,9 @@ var require_BufferList = __commonJS({
|
|
|
4427
4697
|
}
|
|
4428
4698
|
});
|
|
4429
4699
|
|
|
4430
|
-
// node_modules/
|
|
4700
|
+
// node_modules/readable-stream/lib/internal/streams/destroy.js
|
|
4431
4701
|
var require_destroy = __commonJS({
|
|
4432
|
-
"node_modules/
|
|
4702
|
+
"node_modules/readable-stream/lib/internal/streams/destroy.js"(exports, module) {
|
|
4433
4703
|
var pna = require_process_nextick_args();
|
|
4434
4704
|
function destroy(err, cb) {
|
|
4435
4705
|
var _this = this;
|
|
@@ -4502,9 +4772,9 @@ var require_node = __commonJS({
|
|
|
4502
4772
|
}
|
|
4503
4773
|
});
|
|
4504
4774
|
|
|
4505
|
-
// node_modules/
|
|
4775
|
+
// node_modules/readable-stream/lib/_stream_writable.js
|
|
4506
4776
|
var require_stream_writable = __commonJS({
|
|
4507
|
-
"node_modules/
|
|
4777
|
+
"node_modules/readable-stream/lib/_stream_writable.js"(exports, module) {
|
|
4508
4778
|
var pna = require_process_nextick_args();
|
|
4509
4779
|
module.exports = Writable;
|
|
4510
4780
|
function CorkedRequest(state) {
|
|
@@ -4941,9 +5211,9 @@ var require_stream_writable = __commonJS({
|
|
|
4941
5211
|
}
|
|
4942
5212
|
});
|
|
4943
5213
|
|
|
4944
|
-
// node_modules/
|
|
5214
|
+
// node_modules/readable-stream/lib/_stream_duplex.js
|
|
4945
5215
|
var require_stream_duplex = __commonJS({
|
|
4946
|
-
"node_modules/
|
|
5216
|
+
"node_modules/readable-stream/lib/_stream_duplex.js"(exports, module) {
|
|
4947
5217
|
var pna = require_process_nextick_args();
|
|
4948
5218
|
var objectKeys = Object.keys || function(obj) {
|
|
4949
5219
|
var keys2 = [];
|
|
@@ -5017,10 +5287,67 @@ var require_stream_duplex = __commonJS({
|
|
|
5017
5287
|
}
|
|
5018
5288
|
});
|
|
5019
5289
|
|
|
5020
|
-
// node_modules/
|
|
5290
|
+
// node_modules/string_decoder/node_modules/safe-buffer/index.js
|
|
5291
|
+
var require_safe_buffer2 = __commonJS({
|
|
5292
|
+
"node_modules/string_decoder/node_modules/safe-buffer/index.js"(exports, module) {
|
|
5293
|
+
var buffer = __require("buffer");
|
|
5294
|
+
var Buffer2 = buffer.Buffer;
|
|
5295
|
+
function copyProps(src, dst) {
|
|
5296
|
+
for (var key in src) {
|
|
5297
|
+
dst[key] = src[key];
|
|
5298
|
+
}
|
|
5299
|
+
}
|
|
5300
|
+
if (Buffer2.from && Buffer2.alloc && Buffer2.allocUnsafe && Buffer2.allocUnsafeSlow) {
|
|
5301
|
+
module.exports = buffer;
|
|
5302
|
+
} else {
|
|
5303
|
+
copyProps(buffer, exports);
|
|
5304
|
+
exports.Buffer = SafeBuffer;
|
|
5305
|
+
}
|
|
5306
|
+
function SafeBuffer(arg, encodingOrOffset, length) {
|
|
5307
|
+
return Buffer2(arg, encodingOrOffset, length);
|
|
5308
|
+
}
|
|
5309
|
+
copyProps(Buffer2, SafeBuffer);
|
|
5310
|
+
SafeBuffer.from = function(arg, encodingOrOffset, length) {
|
|
5311
|
+
if (typeof arg === "number") {
|
|
5312
|
+
throw new TypeError("Argument must not be a number");
|
|
5313
|
+
}
|
|
5314
|
+
return Buffer2(arg, encodingOrOffset, length);
|
|
5315
|
+
};
|
|
5316
|
+
SafeBuffer.alloc = function(size, fill, encoding) {
|
|
5317
|
+
if (typeof size !== "number") {
|
|
5318
|
+
throw new TypeError("Argument must be a number");
|
|
5319
|
+
}
|
|
5320
|
+
var buf = Buffer2(size);
|
|
5321
|
+
if (fill !== void 0) {
|
|
5322
|
+
if (typeof encoding === "string") {
|
|
5323
|
+
buf.fill(fill, encoding);
|
|
5324
|
+
} else {
|
|
5325
|
+
buf.fill(fill);
|
|
5326
|
+
}
|
|
5327
|
+
} else {
|
|
5328
|
+
buf.fill(0);
|
|
5329
|
+
}
|
|
5330
|
+
return buf;
|
|
5331
|
+
};
|
|
5332
|
+
SafeBuffer.allocUnsafe = function(size) {
|
|
5333
|
+
if (typeof size !== "number") {
|
|
5334
|
+
throw new TypeError("Argument must be a number");
|
|
5335
|
+
}
|
|
5336
|
+
return Buffer2(size);
|
|
5337
|
+
};
|
|
5338
|
+
SafeBuffer.allocUnsafeSlow = function(size) {
|
|
5339
|
+
if (typeof size !== "number") {
|
|
5340
|
+
throw new TypeError("Argument must be a number");
|
|
5341
|
+
}
|
|
5342
|
+
return buffer.SlowBuffer(size);
|
|
5343
|
+
};
|
|
5344
|
+
}
|
|
5345
|
+
});
|
|
5346
|
+
|
|
5347
|
+
// node_modules/string_decoder/lib/string_decoder.js
|
|
5021
5348
|
var require_string_decoder = __commonJS({
|
|
5022
|
-
"node_modules/
|
|
5023
|
-
var Buffer2 =
|
|
5349
|
+
"node_modules/string_decoder/lib/string_decoder.js"(exports) {
|
|
5350
|
+
var Buffer2 = require_safe_buffer2().Buffer;
|
|
5024
5351
|
var isEncoding = Buffer2.isEncoding || function(encoding) {
|
|
5025
5352
|
encoding = "" + encoding;
|
|
5026
5353
|
switch (encoding && encoding.toLowerCase()) {
|
|
@@ -5254,9 +5581,9 @@ var require_string_decoder = __commonJS({
|
|
|
5254
5581
|
}
|
|
5255
5582
|
});
|
|
5256
5583
|
|
|
5257
|
-
// node_modules/
|
|
5584
|
+
// node_modules/readable-stream/lib/_stream_readable.js
|
|
5258
5585
|
var require_stream_readable = __commonJS({
|
|
5259
|
-
"node_modules/
|
|
5586
|
+
"node_modules/readable-stream/lib/_stream_readable.js"(exports, module) {
|
|
5260
5587
|
var pna = require_process_nextick_args();
|
|
5261
5588
|
module.exports = Readable2;
|
|
5262
5589
|
var isArray = require_isarray();
|
|
@@ -5939,9 +6266,9 @@ var require_stream_readable = __commonJS({
|
|
|
5939
6266
|
}
|
|
5940
6267
|
});
|
|
5941
6268
|
|
|
5942
|
-
// node_modules/
|
|
6269
|
+
// node_modules/readable-stream/lib/_stream_transform.js
|
|
5943
6270
|
var require_stream_transform = __commonJS({
|
|
5944
|
-
"node_modules/
|
|
6271
|
+
"node_modules/readable-stream/lib/_stream_transform.js"(exports, module) {
|
|
5945
6272
|
module.exports = Transform;
|
|
5946
6273
|
var Duplex = require_stream_duplex();
|
|
5947
6274
|
var util = Object.create(require_util());
|
|
@@ -6038,9 +6365,9 @@ var require_stream_transform = __commonJS({
|
|
|
6038
6365
|
}
|
|
6039
6366
|
});
|
|
6040
6367
|
|
|
6041
|
-
// node_modules/
|
|
6368
|
+
// node_modules/readable-stream/lib/_stream_passthrough.js
|
|
6042
6369
|
var require_stream_passthrough = __commonJS({
|
|
6043
|
-
"node_modules/
|
|
6370
|
+
"node_modules/readable-stream/lib/_stream_passthrough.js"(exports, module) {
|
|
6044
6371
|
module.exports = PassThrough;
|
|
6045
6372
|
var Transform = require_stream_transform();
|
|
6046
6373
|
var util = Object.create(require_util());
|
|
@@ -6056,9 +6383,9 @@ var require_stream_passthrough = __commonJS({
|
|
|
6056
6383
|
}
|
|
6057
6384
|
});
|
|
6058
6385
|
|
|
6059
|
-
// node_modules/
|
|
6386
|
+
// node_modules/readable-stream/readable.js
|
|
6060
6387
|
var require_readable = __commonJS({
|
|
6061
|
-
"node_modules/
|
|
6388
|
+
"node_modules/readable-stream/readable.js"(exports, module) {
|
|
6062
6389
|
var Stream = __require("stream");
|
|
6063
6390
|
if (process.env.READABLE_STREAM === "disable" && Stream) {
|
|
6064
6391
|
module.exports = Stream;
|
|
@@ -6967,8 +7294,8 @@ var require_utils = __commonJS({
|
|
|
6967
7294
|
var result = transform[inputType][outputType](input);
|
|
6968
7295
|
return result;
|
|
6969
7296
|
};
|
|
6970
|
-
exports.resolve = function(
|
|
6971
|
-
var parts =
|
|
7297
|
+
exports.resolve = function(path41) {
|
|
7298
|
+
var parts = path41.split("/");
|
|
6972
7299
|
var result = [];
|
|
6973
7300
|
for (var index = 0; index < parts.length; index++) {
|
|
6974
7301
|
var part = parts[index];
|
|
@@ -12783,18 +13110,18 @@ var require_object = __commonJS({
|
|
|
12783
13110
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
12784
13111
|
this.files[name] = object;
|
|
12785
13112
|
};
|
|
12786
|
-
var parentFolder = function(
|
|
12787
|
-
if (
|
|
12788
|
-
|
|
13113
|
+
var parentFolder = function(path41) {
|
|
13114
|
+
if (path41.slice(-1) === "/") {
|
|
13115
|
+
path41 = path41.substring(0, path41.length - 1);
|
|
12789
13116
|
}
|
|
12790
|
-
var lastSlash =
|
|
12791
|
-
return lastSlash > 0 ?
|
|
13117
|
+
var lastSlash = path41.lastIndexOf("/");
|
|
13118
|
+
return lastSlash > 0 ? path41.substring(0, lastSlash) : "";
|
|
12792
13119
|
};
|
|
12793
|
-
var forceTrailingSlash = function(
|
|
12794
|
-
if (
|
|
12795
|
-
|
|
13120
|
+
var forceTrailingSlash = function(path41) {
|
|
13121
|
+
if (path41.slice(-1) !== "/") {
|
|
13122
|
+
path41 += "/";
|
|
12796
13123
|
}
|
|
12797
|
-
return
|
|
13124
|
+
return path41;
|
|
12798
13125
|
};
|
|
12799
13126
|
var folderAdd = function(name, createFolders) {
|
|
12800
13127
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -13793,12 +14120,12 @@ var init_audit_logger = __esm({
|
|
|
13793
14120
|
constructor(workspacePath, debugMode = false) {
|
|
13794
14121
|
this.workspacePath = workspacePath;
|
|
13795
14122
|
this.debugMode = debugMode;
|
|
13796
|
-
const cascadeDir =
|
|
13797
|
-
if (!
|
|
13798
|
-
|
|
14123
|
+
const cascadeDir = path36.join(workspacePath, ".cascade");
|
|
14124
|
+
if (!fs33.existsSync(cascadeDir)) {
|
|
14125
|
+
fs33.mkdirSync(cascadeDir, { recursive: true });
|
|
13799
14126
|
}
|
|
13800
|
-
this.keyPath =
|
|
13801
|
-
this.dbPath =
|
|
14127
|
+
this.keyPath = path36.join(cascadeDir, "audit_log.key");
|
|
14128
|
+
this.dbPath = path36.join(cascadeDir, "audit_log.db");
|
|
13802
14129
|
this.initEncryptionKey();
|
|
13803
14130
|
this.db = new Database2(this.dbPath);
|
|
13804
14131
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -13828,11 +14155,11 @@ var init_audit_logger = __esm({
|
|
|
13828
14155
|
return crypto.createHash("sha256").update(`${prevHash}|${timestamp}|${eventType}|${tierId}|${encryptedPayload}`).digest("hex");
|
|
13829
14156
|
}
|
|
13830
14157
|
initEncryptionKey() {
|
|
13831
|
-
if (
|
|
13832
|
-
this.encryptionKey =
|
|
14158
|
+
if (fs33.existsSync(this.keyPath)) {
|
|
14159
|
+
this.encryptionKey = fs33.readFileSync(this.keyPath);
|
|
13833
14160
|
} else {
|
|
13834
14161
|
this.encryptionKey = crypto.randomBytes(32);
|
|
13835
|
-
|
|
14162
|
+
fs33.writeFileSync(this.keyPath, this.encryptionKey);
|
|
13836
14163
|
}
|
|
13837
14164
|
}
|
|
13838
14165
|
encrypt(text) {
|
|
@@ -13910,9 +14237,9 @@ var init_audit_logger = __esm({
|
|
|
13910
14237
|
dumpDebugIfNeeded() {
|
|
13911
14238
|
if (!this.debugMode) return;
|
|
13912
14239
|
try {
|
|
13913
|
-
const dumpPath =
|
|
14240
|
+
const dumpPath = path36.join(os10.tmpdir(), "cascade_audit_logs_debug.json");
|
|
13914
14241
|
const entries = this.getAllLogs();
|
|
13915
|
-
|
|
14242
|
+
fs33.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
13916
14243
|
} catch (err) {
|
|
13917
14244
|
console.error("Failed to dump debug audit logs", err);
|
|
13918
14245
|
}
|
|
@@ -13967,7 +14294,7 @@ var Keystore = class {
|
|
|
13967
14294
|
const creds = await this.keytar.findCredentials(KEYTAR_SERVICE);
|
|
13968
14295
|
this.cache = Object.fromEntries(creds.map((c) => [c.account, c.password]));
|
|
13969
14296
|
this.backend = "keytar";
|
|
13970
|
-
if (password &&
|
|
14297
|
+
if (password && fs33.existsSync(this.storePath)) {
|
|
13971
14298
|
try {
|
|
13972
14299
|
const fileEntries = this.decryptFile(password);
|
|
13973
14300
|
for (const [k, v] of Object.entries(fileEntries)) {
|
|
@@ -13986,7 +14313,7 @@ var Keystore = class {
|
|
|
13986
14313
|
"Keystore unlock requires a password because the OS keychain (keytar) is not available on this system."
|
|
13987
14314
|
);
|
|
13988
14315
|
}
|
|
13989
|
-
if (!
|
|
14316
|
+
if (!fs33.existsSync(this.storePath)) {
|
|
13990
14317
|
const salt = crypto.randomBytes(SALT_LEN);
|
|
13991
14318
|
this.masterKey = this.deriveKey(password, salt);
|
|
13992
14319
|
this.writeWithSalt({}, salt);
|
|
@@ -14000,7 +14327,7 @@ var Keystore = class {
|
|
|
14000
14327
|
}
|
|
14001
14328
|
/** Synchronous legacy unlock kept for AES-only environments. */
|
|
14002
14329
|
unlockSync(password) {
|
|
14003
|
-
if (!
|
|
14330
|
+
if (!fs33.existsSync(this.storePath)) {
|
|
14004
14331
|
const salt = crypto.randomBytes(SALT_LEN);
|
|
14005
14332
|
this.masterKey = this.deriveKey(password, salt);
|
|
14006
14333
|
this.writeWithSalt({}, salt);
|
|
@@ -14058,7 +14385,7 @@ var Keystore = class {
|
|
|
14058
14385
|
}
|
|
14059
14386
|
}
|
|
14060
14387
|
decryptFile(password, knownSalt) {
|
|
14061
|
-
if (!
|
|
14388
|
+
if (!fs33.existsSync(this.storePath)) return {};
|
|
14062
14389
|
try {
|
|
14063
14390
|
const { salt, ciphertext, iv, tag } = this.readRaw();
|
|
14064
14391
|
const useSalt = knownSalt ?? salt;
|
|
@@ -14080,8 +14407,8 @@ var Keystore = class {
|
|
|
14080
14407
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
14081
14408
|
const tag = cipher.getAuthTag();
|
|
14082
14409
|
const out = Buffer.concat([raw.salt, iv, tag, ciphertext]);
|
|
14083
|
-
|
|
14084
|
-
|
|
14410
|
+
fs33.mkdirSync(path36.dirname(this.storePath), { recursive: true });
|
|
14411
|
+
fs33.writeFileSync(this.storePath, out, { mode: 384 });
|
|
14085
14412
|
}
|
|
14086
14413
|
writeWithSalt(data, salt) {
|
|
14087
14414
|
if (!this.masterKey) throw new Error("writeWithSalt called before masterKey was set");
|
|
@@ -14091,11 +14418,11 @@ var Keystore = class {
|
|
|
14091
14418
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
14092
14419
|
const tag = cipher.getAuthTag();
|
|
14093
14420
|
const out = Buffer.concat([salt, iv, tag, ciphertext]);
|
|
14094
|
-
|
|
14095
|
-
|
|
14421
|
+
fs33.mkdirSync(path36.dirname(this.storePath), { recursive: true });
|
|
14422
|
+
fs33.writeFileSync(this.storePath, out, { mode: 384 });
|
|
14096
14423
|
}
|
|
14097
14424
|
readRaw() {
|
|
14098
|
-
const buf =
|
|
14425
|
+
const buf = fs33.readFileSync(this.storePath);
|
|
14099
14426
|
let offset = 0;
|
|
14100
14427
|
const salt = buf.subarray(offset, offset + SALT_LEN);
|
|
14101
14428
|
offset += SALT_LEN;
|
|
@@ -14128,7 +14455,7 @@ var CascadeIgnore = class {
|
|
|
14128
14455
|
]);
|
|
14129
14456
|
}
|
|
14130
14457
|
async load(workspacePath) {
|
|
14131
|
-
const filePath =
|
|
14458
|
+
const filePath = path36.join(workspacePath, ".cascadeignore");
|
|
14132
14459
|
try {
|
|
14133
14460
|
const content = await fs11.readFile(filePath, "utf-8");
|
|
14134
14461
|
const lines = content.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
|
|
@@ -14139,7 +14466,7 @@ var CascadeIgnore = class {
|
|
|
14139
14466
|
}
|
|
14140
14467
|
isIgnored(filePath, workspacePath) {
|
|
14141
14468
|
try {
|
|
14142
|
-
const relative = workspacePath ?
|
|
14469
|
+
const relative = workspacePath ? path36.relative(workspacePath, filePath) : filePath;
|
|
14143
14470
|
return this.ig.ignores(relative);
|
|
14144
14471
|
} catch {
|
|
14145
14472
|
return false;
|
|
@@ -14150,7 +14477,7 @@ var CascadeIgnore = class {
|
|
|
14150
14477
|
}
|
|
14151
14478
|
};
|
|
14152
14479
|
async function createDefaultIgnoreFile(workspacePath) {
|
|
14153
|
-
const filePath =
|
|
14480
|
+
const filePath = path36.join(workspacePath, ".cascadeignore");
|
|
14154
14481
|
const content = `# .cascadeignore \u2014 Files Cascade agents cannot read or modify
|
|
14155
14482
|
# Syntax identical to .gitignore
|
|
14156
14483
|
|
|
@@ -14180,7 +14507,7 @@ Thumbs.db
|
|
|
14180
14507
|
await fs11.writeFile(filePath, content, "utf-8");
|
|
14181
14508
|
}
|
|
14182
14509
|
async function loadCascadeMd(workspacePath) {
|
|
14183
|
-
const filePath =
|
|
14510
|
+
const filePath = path36.join(workspacePath, "CASCADE.md");
|
|
14184
14511
|
try {
|
|
14185
14512
|
const raw = await fs11.readFile(filePath, "utf-8");
|
|
14186
14513
|
return parseCascadeMd(raw);
|
|
@@ -14209,7 +14536,7 @@ ${raw.trim()}`;
|
|
|
14209
14536
|
return { raw, sections, systemPrompt };
|
|
14210
14537
|
}
|
|
14211
14538
|
async function createDefaultCascadeMd(workspacePath) {
|
|
14212
|
-
const filePath =
|
|
14539
|
+
const filePath = path36.join(workspacePath, "CASCADE.md");
|
|
14213
14540
|
const content = `# Cascade Project Instructions
|
|
14214
14541
|
|
|
14215
14542
|
This file contains project-specific instructions for the Cascade AI agent.
|
|
@@ -14244,7 +14571,7 @@ List any areas the agent should not touch.
|
|
|
14244
14571
|
var MemoryStore = class _MemoryStore {
|
|
14245
14572
|
db;
|
|
14246
14573
|
constructor(dbPath) {
|
|
14247
|
-
|
|
14574
|
+
fs33.mkdirSync(path36.dirname(dbPath), { recursive: true });
|
|
14248
14575
|
try {
|
|
14249
14576
|
this.db = new Database2(dbPath, { timeout: 5e3 });
|
|
14250
14577
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -15142,13 +15469,38 @@ var WebSearchConfigSchema = z.object({
|
|
|
15142
15469
|
/** Tavily API key — get one at https://tavily.com */
|
|
15143
15470
|
tavilyApiKey: z.string().optional(),
|
|
15144
15471
|
/** Max results per search (default 5) */
|
|
15145
|
-
maxResults: z.number().default(5)
|
|
15472
|
+
maxResults: z.number().default(5),
|
|
15473
|
+
/**
|
|
15474
|
+
* Send `searxngUrl` through the SSRF guard. Off for a local run, where the
|
|
15475
|
+
* URL is the machine owner's own config and a self-hosted SearXNG is
|
|
15476
|
+
* routinely on a private address; on for a host that accepts it from a
|
|
15477
|
+
* request. See `src/tools/web-search.ts`.
|
|
15478
|
+
*
|
|
15479
|
+
* It has to be named HERE, not just on the TypeScript interfaces: the config
|
|
15480
|
+
* passes through `CascadeConfigSchema.parse()` on its way into a run, and
|
|
15481
|
+
* zod drops every key this object does not declare.
|
|
15482
|
+
*/
|
|
15483
|
+
guardSearxngUrl: z.boolean().optional()
|
|
15146
15484
|
});
|
|
15147
15485
|
var ToolsConfigSchema = z.object({
|
|
15148
15486
|
shellAllowlist: z.array(z.string()).default([]),
|
|
15149
15487
|
shellBlocklist: z.array(z.string()).default(["rm -rf", "sudo rm", "format", "mkfs"]),
|
|
15150
15488
|
requireApprovalFor: z.array(z.string()).default([]),
|
|
15151
15489
|
browserEnabled: z.boolean().default(false),
|
|
15490
|
+
/**
|
|
15491
|
+
* Let a run ACT on the page open in the desktop's built-in browser — click,
|
|
15492
|
+
* type, submit — not just read it.
|
|
15493
|
+
*
|
|
15494
|
+
* Distinct from `browserEnabled`, which turns on the headless Playwright
|
|
15495
|
+
* tool. That one drives a throwaway browser with an empty cookie jar; this
|
|
15496
|
+
* one drives the session the user is signed into, which is a different order
|
|
15497
|
+
* of consequence and so gets its own switch rather than riding on that one.
|
|
15498
|
+
*
|
|
15499
|
+
* Off by default. Turning it on does not hand anything over silently: the
|
|
15500
|
+
* tool still goes through the approval chain, and the browser panel carries a
|
|
15501
|
+
* stop control for the run that is acting.
|
|
15502
|
+
*/
|
|
15503
|
+
agentBrowserControl: z.boolean().default(false),
|
|
15152
15504
|
mcpServers: z.array(McpServerConfigSchema).optional(),
|
|
15153
15505
|
mcpTrusted: z.array(z.string()).optional(),
|
|
15154
15506
|
/** Web search backends — at least one should be configured for best results */
|
|
@@ -15551,7 +15903,7 @@ function credentialEndpointsConflict(type, rowEndpoint, credentialEndpoint) {
|
|
|
15551
15903
|
|
|
15552
15904
|
// src/config/global-credentials.ts
|
|
15553
15905
|
function credentialsPath(globalDir2) {
|
|
15554
|
-
return
|
|
15906
|
+
return path36.join(globalDir2, GLOBAL_CREDENTIALS_FILE);
|
|
15555
15907
|
}
|
|
15556
15908
|
function providerKey(p) {
|
|
15557
15909
|
return p.type;
|
|
@@ -15581,7 +15933,7 @@ function isPersistable(p) {
|
|
|
15581
15933
|
}
|
|
15582
15934
|
function loadGlobalCredentials(globalDir2) {
|
|
15583
15935
|
try {
|
|
15584
|
-
const raw =
|
|
15936
|
+
const raw = fs33.readFileSync(credentialsPath(globalDir2), "utf-8");
|
|
15585
15937
|
const parsed = JSON.parse(raw);
|
|
15586
15938
|
if (!Array.isArray(parsed.providers)) return [];
|
|
15587
15939
|
return parsed.providers.filter(
|
|
@@ -15593,13 +15945,13 @@ function loadGlobalCredentials(globalDir2) {
|
|
|
15593
15945
|
}
|
|
15594
15946
|
function saveGlobalCredentials(globalDir2, providers) {
|
|
15595
15947
|
const filePath = credentialsPath(globalDir2);
|
|
15596
|
-
|
|
15948
|
+
fs33.mkdirSync(globalDir2, { recursive: true, mode: 448 });
|
|
15597
15949
|
const body = { version: 1, providers: providers.filter(isPersistable) };
|
|
15598
15950
|
const tmp = `${filePath}.tmp`;
|
|
15599
|
-
|
|
15600
|
-
|
|
15951
|
+
fs33.writeFileSync(tmp, JSON.stringify(body, null, 2), { encoding: "utf-8", mode: 384 });
|
|
15952
|
+
fs33.renameSync(tmp, filePath);
|
|
15601
15953
|
try {
|
|
15602
|
-
|
|
15954
|
+
fs33.chmodSync(filePath, 384);
|
|
15603
15955
|
} catch {
|
|
15604
15956
|
}
|
|
15605
15957
|
}
|
|
@@ -16004,7 +16356,7 @@ var ConfigManager = class {
|
|
|
16004
16356
|
/** `globalDirOverride` exists for tests — never point it at the real home dir there. */
|
|
16005
16357
|
constructor(workspacePath = process.cwd(), globalDirOverride) {
|
|
16006
16358
|
this.workspacePath = workspacePath;
|
|
16007
|
-
this.globalDir = globalDirOverride ??
|
|
16359
|
+
this.globalDir = globalDirOverride ?? path36.join(os10.homedir(), GLOBAL_CONFIG_DIR);
|
|
16008
16360
|
}
|
|
16009
16361
|
async load() {
|
|
16010
16362
|
this.config = await this.loadConfig();
|
|
@@ -16018,8 +16370,8 @@ var ConfigManager = class {
|
|
|
16018
16370
|
this.ignore = new CascadeIgnore();
|
|
16019
16371
|
await this.ignore.load(this.workspacePath);
|
|
16020
16372
|
this.cascadeMd = await loadCascadeMd(this.workspacePath);
|
|
16021
|
-
this.keystore = new Keystore(
|
|
16022
|
-
this.store = new MemoryStore(
|
|
16373
|
+
this.keystore = new Keystore(path36.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
|
|
16374
|
+
this.store = new MemoryStore(path36.join(this.workspacePath, CASCADE_DB_FILE));
|
|
16023
16375
|
const globalRevoked = stripRevokedCredentials(loadGlobalCredentials(this.globalDir));
|
|
16024
16376
|
const globalCreds = filterRetiredCredentials(globalRevoked.kept);
|
|
16025
16377
|
if (globalRevoked.removed > 0) this.revokedCredentials += globalRevoked.removed;
|
|
@@ -16092,8 +16444,8 @@ var ConfigManager = class {
|
|
|
16092
16444
|
* never reached the disk (see `commitSettings`).
|
|
16093
16445
|
*/
|
|
16094
16446
|
async save(config = this.config) {
|
|
16095
|
-
const configPath =
|
|
16096
|
-
await fs11.mkdir(
|
|
16447
|
+
const configPath = path36.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
16448
|
+
await fs11.mkdir(path36.dirname(configPath), { recursive: true });
|
|
16097
16449
|
await fs11.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
16098
16450
|
try {
|
|
16099
16451
|
saveGlobalCredentials(this.globalDir, config.providers);
|
|
@@ -16143,7 +16495,7 @@ var ConfigManager = class {
|
|
|
16143
16495
|
this.revokedCredentials = 0;
|
|
16144
16496
|
this.revokedPins = [];
|
|
16145
16497
|
this.revokedNotice = void 0;
|
|
16146
|
-
const configPath =
|
|
16498
|
+
const configPath = path36.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
16147
16499
|
try {
|
|
16148
16500
|
const raw = await fs11.readFile(configPath, "utf-8");
|
|
16149
16501
|
const parsed = JSON.parse(raw);
|
|
@@ -16237,7 +16589,7 @@ var ConfigManager = class {
|
|
|
16237
16589
|
return onResource;
|
|
16238
16590
|
}
|
|
16239
16591
|
async persistClearedPins(tiers) {
|
|
16240
|
-
const configPath =
|
|
16592
|
+
const configPath = path36.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
16241
16593
|
try {
|
|
16242
16594
|
const raw = JSON.parse(await fs11.readFile(configPath, "utf-8"));
|
|
16243
16595
|
const models = raw["models"];
|
|
@@ -17961,7 +18313,7 @@ init_pricing();
|
|
|
17961
18313
|
var DEFAULT_SNAPSHOT_URL = "https://raw.githubusercontent.com/Varun-SV/Cascade-AI/main/src/core/router/benchmark-data.json";
|
|
17962
18314
|
var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
|
|
17963
18315
|
var FETCH_TIMEOUT_MS = 8e3;
|
|
17964
|
-
var DEFAULT_CACHE_FILE =
|
|
18316
|
+
var DEFAULT_CACHE_FILE = path36.join(os10.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
|
|
17965
18317
|
function normalizeModelId2(id) {
|
|
17966
18318
|
let s = id.toLowerCase();
|
|
17967
18319
|
const slash = s.lastIndexOf("/");
|
|
@@ -18108,7 +18460,7 @@ var LiveDataProvider = class {
|
|
|
18108
18460
|
}
|
|
18109
18461
|
async saveCache() {
|
|
18110
18462
|
try {
|
|
18111
|
-
await fs11.mkdir(
|
|
18463
|
+
await fs11.mkdir(path36.dirname(this.opts.cacheFile), { recursive: true });
|
|
18112
18464
|
const cache = {
|
|
18113
18465
|
fetchedAt: this.fetchedAt,
|
|
18114
18466
|
snapshot: this.snapshot ?? void 0,
|
|
@@ -18215,9 +18567,15 @@ var FAMILY_BENCHMARKS = {
|
|
|
18215
18567
|
"claude-opus": { code: 95, analysis: 92, creative: 90, data: 88 },
|
|
18216
18568
|
"claude-sonnet": { code: 93, analysis: 88, creative: 87, data: 85 },
|
|
18217
18569
|
"claude-haiku": { code: 80, analysis: 75, creative: 76, data: 72 },
|
|
18218
|
-
// OpenAI GPT-5 family
|
|
18219
|
-
//
|
|
18220
|
-
//
|
|
18570
|
+
// OpenAI GPT-5 family. GPT-5.6 is split by durable capability tier instead
|
|
18571
|
+
// of being folded into generic gpt-5: Sol, Terra and Luna have materially
|
|
18572
|
+
// different benchmark/cost envelopes and should compete independently.
|
|
18573
|
+
// Scores are the committed normalized snapshot; provenance is in
|
|
18574
|
+
// scripts/benchmarks/sources/openai-gpt-5.6-2026-08.json.
|
|
18575
|
+
"gpt-5.6-sol": { code: 100, analysis: 98, creative: 88, data: 88 },
|
|
18576
|
+
"gpt-5.6-terra": { code: 97, analysis: 92, creative: 82, data: 85 },
|
|
18577
|
+
"gpt-5.6-luna": { code: 93, analysis: 85, creative: 74, data: 60 },
|
|
18578
|
+
// Older GPT-5 point releases remain distinct where public data supports it.
|
|
18221
18579
|
"gpt-5.5": { code: 97, analysis: 96, creative: 94, data: 95 },
|
|
18222
18580
|
"gpt-5.4": { code: 94, analysis: 94, creative: 92, data: 92 },
|
|
18223
18581
|
"gpt-5.4-mini": { code: 86, analysis: 85, creative: 85, data: 83 },
|
|
@@ -18253,13 +18611,19 @@ var FAMILY_BENCHMARKS = {
|
|
|
18253
18611
|
"llama-small": { code: 55, analysis: 56, creative: 60, data: 54 }
|
|
18254
18612
|
};
|
|
18255
18613
|
var FAMILY_MATCHERS = [
|
|
18614
|
+
[/fable/i, "claude-opus"],
|
|
18256
18615
|
[/opus/i, "claude-opus"],
|
|
18257
18616
|
[/sonnet/i, "claude-sonnet"],
|
|
18258
18617
|
[/haiku/i, "claude-haiku"],
|
|
18259
|
-
// GPT-5 family — ordered most-specific first.
|
|
18260
|
-
//
|
|
18261
|
-
//
|
|
18618
|
+
// GPT-5 family — ordered most-specific first. GPT-5.6 durable tiers carry
|
|
18619
|
+
// their own profiles. 5.4 Nano intentionally maps to the nano class rather
|
|
18620
|
+
// than being swallowed by the generic 5.4 rule.
|
|
18621
|
+
[/gpt-?5\.6.*sol/i, "gpt-5.6-sol"],
|
|
18622
|
+
[/gpt-?5\.6.*terra/i, "gpt-5.6-terra"],
|
|
18623
|
+
[/gpt-?5\.6.*luna/i, "gpt-5.6-luna"],
|
|
18624
|
+
[/gpt-?5\.6(?:\s|$)/i, "gpt-5.6-sol"],
|
|
18262
18625
|
[/gpt-?5\.5/i, "gpt-5.5"],
|
|
18626
|
+
[/gpt-?5\.4.*nano/i, "gpt-5-nano"],
|
|
18263
18627
|
[/gpt-?5\.4.*mini/i, "gpt-5.4-mini"],
|
|
18264
18628
|
[/gpt-?5\.4/i, "gpt-5.4"],
|
|
18265
18629
|
[/gpt-?5.*nano/i, "gpt-5-nano"],
|
|
@@ -21061,6 +21425,23 @@ Now execute your subtask using this context where relevant.`
|
|
|
21061
21425
|
this.setStatus("FAILED", finalOutput);
|
|
21062
21426
|
this.peerBus?.publish(this.id, assignment.subtaskId, finalOutput, "FAILED");
|
|
21063
21427
|
return this.buildResult("ESCALATED", finalOutput, { checksRun, passed, failed }, issues, correctionAttempts);
|
|
21428
|
+
} finally {
|
|
21429
|
+
this.releaseBrowserControl();
|
|
21430
|
+
}
|
|
21431
|
+
}
|
|
21432
|
+
/**
|
|
21433
|
+
* End this worker's hold on the shared browser.
|
|
21434
|
+
*
|
|
21435
|
+
* Duck-typed rather than imported: `browser_control` is registered only by a
|
|
21436
|
+
* host that HAS a browser (the desktop), so in the CLI and hosted runs there
|
|
21437
|
+
* is no such tool and this is a no-op. Importing the class to instanceof-check
|
|
21438
|
+
* it would couple the worker to a tool that usually is not there.
|
|
21439
|
+
*/
|
|
21440
|
+
releaseBrowserControl() {
|
|
21441
|
+
try {
|
|
21442
|
+
const tool = this.toolRegistry.getTool?.("browser_control");
|
|
21443
|
+
tool?.releaseActor?.(this.id);
|
|
21444
|
+
} catch {
|
|
21064
21445
|
}
|
|
21065
21446
|
}
|
|
21066
21447
|
sendToPeer(toId, content) {
|
|
@@ -21319,8 +21700,8 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : "") + textToolSuffix,
|
|
|
21319
21700
|
// Media generation can run for a minute; without this a cancelled run
|
|
21320
21701
|
// still pays for an image nobody will see.
|
|
21321
21702
|
...this.signal ? { signal: this.signal } : {},
|
|
21322
|
-
saveSnapshot: async (
|
|
21323
|
-
this.store?.addFileSnapshot(this.taskId,
|
|
21703
|
+
saveSnapshot: async (path41, content) => {
|
|
21704
|
+
this.store?.addFileSnapshot(this.taskId, path41, content);
|
|
21324
21705
|
},
|
|
21325
21706
|
sendPeerSync: (to, syncType, content) => {
|
|
21326
21707
|
this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
|
|
@@ -21523,7 +21904,7 @@ ${assignment.expectedOutput}`;
|
|
|
21523
21904
|
const { promisify: promisify4 } = await import('util');
|
|
21524
21905
|
const execFileAsync2 = promisify4(execFile3);
|
|
21525
21906
|
for (const artifactPath of artifactPaths) {
|
|
21526
|
-
const absolutePath =
|
|
21907
|
+
const absolutePath = path36.resolve(this.artifactRoot(), artifactPath);
|
|
21527
21908
|
try {
|
|
21528
21909
|
const stat = await fs11.stat(absolutePath);
|
|
21529
21910
|
if (!stat.isFile()) {
|
|
@@ -21558,7 +21939,7 @@ ${assignment.expectedOutput}`;
|
|
|
21558
21939
|
issues.push(`PDF artifact looks too small to be valid: ${artifactPath}`);
|
|
21559
21940
|
continue;
|
|
21560
21941
|
}
|
|
21561
|
-
const ext =
|
|
21942
|
+
const ext = path36.extname(absolutePath).toLowerCase();
|
|
21562
21943
|
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;
|
|
21563
21944
|
if (check) {
|
|
21564
21945
|
try {
|
|
@@ -21650,7 +22031,7 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
21650
22031
|
async checkAcceptance(assignment) {
|
|
21651
22032
|
const criteria = assignment.acceptance ?? [];
|
|
21652
22033
|
if (!criteria.length) return [];
|
|
21653
|
-
const resolve2 = (target) =>
|
|
22034
|
+
const resolve2 = (target) => path36.resolve(this.artifactRoot(), target);
|
|
21654
22035
|
const workerCanWriteFiles = canProduceFiles(this.tools.map((t) => t.name));
|
|
21655
22036
|
return evaluateAcceptance(criteria, assignment.files ?? [], {
|
|
21656
22037
|
stat: async (target) => {
|
|
@@ -24961,16 +25342,16 @@ function resolveInWorkspace(workspaceRoot, input) {
|
|
|
24961
25342
|
if (typeof input !== "string" || input.length === 0) {
|
|
24962
25343
|
throw new WorkspaceSandboxError(String(input), workspaceRoot);
|
|
24963
25344
|
}
|
|
24964
|
-
const root =
|
|
24965
|
-
const abs =
|
|
24966
|
-
const rel =
|
|
24967
|
-
if (rel === "" || rel === ".") ; else if (rel.startsWith("..") ||
|
|
25345
|
+
const root = path36.resolve(workspaceRoot);
|
|
25346
|
+
const abs = path36.isAbsolute(input) ? path36.resolve(input) : path36.resolve(root, input);
|
|
25347
|
+
const rel = path36.relative(root, abs);
|
|
25348
|
+
if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path36.isAbsolute(rel)) {
|
|
24968
25349
|
throw new WorkspaceSandboxError(input, root);
|
|
24969
25350
|
}
|
|
24970
25351
|
try {
|
|
24971
|
-
const real =
|
|
24972
|
-
const realRel =
|
|
24973
|
-
if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") ||
|
|
25352
|
+
const real = fs33.realpathSync(abs);
|
|
25353
|
+
const realRel = path36.relative(root, real);
|
|
25354
|
+
if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path36.isAbsolute(realRel))) {
|
|
24974
25355
|
throw new WorkspaceSandboxError(input, root);
|
|
24975
25356
|
}
|
|
24976
25357
|
} catch (e) {
|
|
@@ -25519,9 +25900,9 @@ async function animatePptx(bytes, scheme) {
|
|
|
25519
25900
|
const zip = await import_jszip.default.loadAsync(bytes);
|
|
25520
25901
|
const slidePaths = Object.keys(zip.files).filter((n) => /^ppt\/slides\/slide\d+\.xml$/.test(n));
|
|
25521
25902
|
if (slidePaths.length === 0) return bytes;
|
|
25522
|
-
for (const
|
|
25523
|
-
const xml = await zip.file(
|
|
25524
|
-
zip.file(
|
|
25903
|
+
for (const path41 of slidePaths) {
|
|
25904
|
+
const xml = await zip.file(path41).async("string");
|
|
25905
|
+
zip.file(path41, animateSlideXml(xml, scheme));
|
|
25525
25906
|
}
|
|
25526
25907
|
return await zip.generateAsync({ type: "uint8array", compression: "DEFLATE" });
|
|
25527
25908
|
} catch {
|
|
@@ -25902,7 +26283,7 @@ var GenerateDocumentTool = class extends BaseTool {
|
|
|
25902
26283
|
}
|
|
25903
26284
|
}
|
|
25904
26285
|
const bytes = await renderDocument(ext, content, { name: filePath, loadImageBytes });
|
|
25905
|
-
await fs11.mkdir(
|
|
26286
|
+
await fs11.mkdir(path36.dirname(absPath), { recursive: true });
|
|
25906
26287
|
await fs11.writeFile(absPath, bytes);
|
|
25907
26288
|
const charts = countCharts(content);
|
|
25908
26289
|
return [
|
|
@@ -26123,7 +26504,7 @@ var FileWriteTool = class extends BaseTool {
|
|
|
26123
26504
|
} catch {
|
|
26124
26505
|
}
|
|
26125
26506
|
}
|
|
26126
|
-
await fs11.mkdir(
|
|
26507
|
+
await fs11.mkdir(path36.dirname(absPath), { recursive: true });
|
|
26127
26508
|
await fs11.writeFile(absPath, content, "utf-8");
|
|
26128
26509
|
return `Written ${content.length} characters to ${filePath}`;
|
|
26129
26510
|
}
|
|
@@ -26481,12 +26862,10 @@ ${data.description}`;
|
|
|
26481
26862
|
}
|
|
26482
26863
|
}
|
|
26483
26864
|
};
|
|
26484
|
-
|
|
26485
|
-
// src/tools/browser.ts
|
|
26486
26865
|
var BROWSER_LAUNCH_TIMEOUT_MS = 15e3;
|
|
26487
26866
|
var BrowserTool = class extends BaseTool {
|
|
26488
26867
|
name = "browser";
|
|
26489
|
-
description = "Control a browser: navigate to URLs, click elements, fill forms, take screenshots.
|
|
26868
|
+
description = "Control a browser: navigate to URLs, click elements, fill forms, take screenshots. Screenshots are saved to a file for image_analyze to read.";
|
|
26490
26869
|
inputSchema = {
|
|
26491
26870
|
type: "object",
|
|
26492
26871
|
properties: {
|
|
@@ -26553,7 +26932,14 @@ var BrowserTool = class extends BaseTool {
|
|
|
26553
26932
|
}
|
|
26554
26933
|
case "screenshot": {
|
|
26555
26934
|
const buf = await page2.screenshot({ type: "png" });
|
|
26556
|
-
|
|
26935
|
+
const rel = path36.join(".cascade", "screenshots", `screenshot-${Date.now()}-${randomUUID().slice(0, 8)}.png`);
|
|
26936
|
+
const abs = resolveInWorkspace(this.workspaceRoot, rel);
|
|
26937
|
+
await fs11.mkdir(path36.dirname(abs), { recursive: true });
|
|
26938
|
+
await fs11.writeFile(abs, buf);
|
|
26939
|
+
return [
|
|
26940
|
+
`Screenshot saved to ${abs} (${Math.round(buf.byteLength / 1024)} KB).`,
|
|
26941
|
+
`Call image_analyze with that path to see what is on it \u2014 that needs a vision-capable model.`
|
|
26942
|
+
].join(" ");
|
|
26557
26943
|
}
|
|
26558
26944
|
case "evaluate": {
|
|
26559
26945
|
const result = await page2.evaluate(input["script"]);
|
|
@@ -26621,7 +27007,7 @@ var ImageAnalyzeTool = class extends BaseTool {
|
|
|
26621
27007
|
};
|
|
26622
27008
|
async function fileToImageAttachment(filePath) {
|
|
26623
27009
|
const data = await fs11.readFile(filePath);
|
|
26624
|
-
const ext =
|
|
27010
|
+
const ext = path36.extname(filePath).toLowerCase();
|
|
26625
27011
|
const mimeMap = {
|
|
26626
27012
|
".jpg": "image/jpeg",
|
|
26627
27013
|
".jpeg": "image/jpeg",
|
|
@@ -26655,14 +27041,14 @@ var PDFCreateTool = class extends BaseTool {
|
|
|
26655
27041
|
const filePath = input["path"];
|
|
26656
27042
|
const content = input["content"];
|
|
26657
27043
|
const title = input["title"];
|
|
26658
|
-
const dir =
|
|
26659
|
-
if (!
|
|
26660
|
-
|
|
27044
|
+
const dir = path36.dirname(filePath);
|
|
27045
|
+
if (!fs33.existsSync(dir)) {
|
|
27046
|
+
fs33.mkdirSync(dir, { recursive: true });
|
|
26661
27047
|
}
|
|
26662
27048
|
return new Promise((resolve2, reject) => {
|
|
26663
27049
|
try {
|
|
26664
27050
|
const doc = new PDFDocument({ margin: 50 });
|
|
26665
|
-
const stream =
|
|
27051
|
+
const stream = fs33.createWriteStream(filePath);
|
|
26666
27052
|
doc.pipe(stream);
|
|
26667
27053
|
if (title) {
|
|
26668
27054
|
doc.info["Title"] = title;
|
|
@@ -26740,22 +27126,22 @@ var CodeInterpreterTool = class extends BaseTool {
|
|
|
26740
27126
|
}
|
|
26741
27127
|
cmdPrefix = NODE_CMD;
|
|
26742
27128
|
}
|
|
26743
|
-
const tmpDir =
|
|
26744
|
-
if (!
|
|
26745
|
-
|
|
27129
|
+
const tmpDir = path36.join(this.workspaceRoot, ".cascade", "tmp");
|
|
27130
|
+
if (!fs33.existsSync(tmpDir)) {
|
|
27131
|
+
fs33.mkdirSync(tmpDir, { recursive: true });
|
|
26746
27132
|
}
|
|
26747
27133
|
const extension = language === "python" ? "py" : "js";
|
|
26748
27134
|
const fileName = `intp_${randomUUID().slice(0, 8)}.${extension}`;
|
|
26749
|
-
const filePath =
|
|
26750
|
-
|
|
27135
|
+
const filePath = path36.join(tmpDir, fileName);
|
|
27136
|
+
fs33.writeFileSync(filePath, code, "utf-8");
|
|
26751
27137
|
const execArgs = [filePath, ...args];
|
|
26752
27138
|
return new Promise((resolve2) => {
|
|
26753
27139
|
const startMs = Date.now();
|
|
26754
27140
|
execFile(cmdPrefix, execArgs, { cwd: this.workspaceRoot, timeout: 3e4 }, (error, stdout, stderr) => {
|
|
26755
27141
|
const duration = Date.now() - startMs;
|
|
26756
27142
|
try {
|
|
26757
|
-
if (
|
|
26758
|
-
|
|
27143
|
+
if (fs33.existsSync(filePath)) {
|
|
27144
|
+
fs33.unlinkSync(filePath);
|
|
26759
27145
|
}
|
|
26760
27146
|
} catch (cleanupErr) {
|
|
26761
27147
|
console.error(`Failed to cleanup interpreter script ${filePath}:`, cleanupErr);
|
|
@@ -26835,18 +27221,178 @@ ${formatted}`;
|
|
|
26835
27221
|
return `Unknown action: ${action}`;
|
|
26836
27222
|
}
|
|
26837
27223
|
};
|
|
27224
|
+
var SsrfBlockedError = class extends Error {
|
|
27225
|
+
constructor(message) {
|
|
27226
|
+
super(message);
|
|
27227
|
+
this.name = "SsrfBlockedError";
|
|
27228
|
+
}
|
|
27229
|
+
};
|
|
27230
|
+
var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
27231
|
+
var MAX_REDIRECTS2 = 5;
|
|
27232
|
+
function allowLocal() {
|
|
27233
|
+
return process.env["CASCADE_ALLOW_LOCAL_FETCH"] === "1";
|
|
27234
|
+
}
|
|
27235
|
+
function isPrivateAddress(ip) {
|
|
27236
|
+
const type = net.isIP(ip);
|
|
27237
|
+
if (type === 4) return isPrivateIPv4(ip);
|
|
27238
|
+
if (type === 6) return isPrivateIPv6(ip);
|
|
27239
|
+
return true;
|
|
27240
|
+
}
|
|
27241
|
+
function isPrivateIPv4(ip) {
|
|
27242
|
+
const parts = ip.split(".").map((p) => Number(p));
|
|
27243
|
+
if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255)) return true;
|
|
27244
|
+
const [a, b] = parts;
|
|
27245
|
+
if (a === 0) return true;
|
|
27246
|
+
if (a === 10) return true;
|
|
27247
|
+
if (a === 127) return true;
|
|
27248
|
+
if (a === 169 && b === 254) return true;
|
|
27249
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
27250
|
+
if (a === 192 && b === 168) return true;
|
|
27251
|
+
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
27252
|
+
if (a >= 224) return true;
|
|
27253
|
+
return false;
|
|
27254
|
+
}
|
|
27255
|
+
function ipv6Groups(ip) {
|
|
27256
|
+
let s = ip.toLowerCase().replace(/^\[|\]$/g, "");
|
|
27257
|
+
const zone = s.indexOf("%");
|
|
27258
|
+
if (zone !== -1) s = s.slice(0, zone);
|
|
27259
|
+
const dotted = /^(.*:)(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(s);
|
|
27260
|
+
if (dotted) {
|
|
27261
|
+
const o = [dotted[2], dotted[3], dotted[4], dotted[5]].map((p) => Number(p));
|
|
27262
|
+
if (o.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return null;
|
|
27263
|
+
s = `${dotted[1]}${(o[0] << 8 | o[1]).toString(16)}:${(o[2] << 8 | o[3]).toString(16)}`;
|
|
27264
|
+
}
|
|
27265
|
+
const halves = s.split("::");
|
|
27266
|
+
if (halves.length > 2) return null;
|
|
27267
|
+
const split = (part) => part ? part.split(":") : [];
|
|
27268
|
+
const head = split(halves[0] ?? "");
|
|
27269
|
+
const tail = halves.length === 2 ? split(halves[1] ?? "") : [];
|
|
27270
|
+
const zeros = halves.length === 2 ? 8 - head.length - tail.length : 0;
|
|
27271
|
+
if (zeros < 0) return null;
|
|
27272
|
+
const groups = [...head, ...Array(zeros).fill("0"), ...tail];
|
|
27273
|
+
if (groups.length !== 8) return null;
|
|
27274
|
+
const out = [];
|
|
27275
|
+
for (const g of groups) {
|
|
27276
|
+
if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
|
|
27277
|
+
out.push(parseInt(g, 16));
|
|
27278
|
+
}
|
|
27279
|
+
return out;
|
|
27280
|
+
}
|
|
27281
|
+
function embeddedIPv4(g) {
|
|
27282
|
+
const quad = (hi, lo) => `${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`;
|
|
27283
|
+
const leadingZero = (n) => g.slice(0, n).every((x) => x === 0);
|
|
27284
|
+
if (leadingZero(5) && g[5] === 65535) return quad(g[6], g[7]);
|
|
27285
|
+
if (leadingZero(6)) return quad(g[6], g[7]);
|
|
27286
|
+
if (g[0] === 100 && g[1] === 65435) {
|
|
27287
|
+
if (g[2] === 1) {
|
|
27288
|
+
return quad(g[3], (g[4] & 255) << 8 | g[5] >> 8);
|
|
27289
|
+
}
|
|
27290
|
+
if (g[2] === 0 && g[3] === 0 && g[4] === 0 && g[5] === 0) return quad(g[6], g[7]);
|
|
27291
|
+
return null;
|
|
27292
|
+
}
|
|
27293
|
+
if (g[0] === 8194) return quad(g[1], g[2]);
|
|
27294
|
+
return null;
|
|
27295
|
+
}
|
|
27296
|
+
function isPrivateIPv6(ip) {
|
|
27297
|
+
const g = ipv6Groups(ip);
|
|
27298
|
+
if (!g) return true;
|
|
27299
|
+
const v4 = embeddedIPv4(g);
|
|
27300
|
+
if (v4) return isPrivateIPv4(v4);
|
|
27301
|
+
if (g[0] === 100 && g[1] === 65435) return true;
|
|
27302
|
+
if (g.every((x) => x === 0)) return true;
|
|
27303
|
+
if (leading7Zero(g) && g[7] === 1) return true;
|
|
27304
|
+
if ((g[0] & 65472) === 65152) return true;
|
|
27305
|
+
if ((g[0] & 65024) === 64512) return true;
|
|
27306
|
+
if ((g[0] & 65280) === 65280) return true;
|
|
27307
|
+
return false;
|
|
27308
|
+
}
|
|
27309
|
+
function leading7Zero(g) {
|
|
27310
|
+
return g.slice(0, 7).every((x) => x === 0);
|
|
27311
|
+
}
|
|
27312
|
+
async function assertPublicUrl(rawUrl) {
|
|
27313
|
+
let url;
|
|
27314
|
+
try {
|
|
27315
|
+
url = new URL(rawUrl);
|
|
27316
|
+
} catch {
|
|
27317
|
+
throw new SsrfBlockedError(`Invalid URL: ${rawUrl}`);
|
|
27318
|
+
}
|
|
27319
|
+
if (!ALLOWED_PROTOCOLS.has(url.protocol)) {
|
|
27320
|
+
throw new SsrfBlockedError(`Blocked URL scheme "${url.protocol}" \u2014 only http and https are allowed.`);
|
|
27321
|
+
}
|
|
27322
|
+
if (allowLocal()) return url;
|
|
27323
|
+
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
27324
|
+
if (net.isIP(host)) {
|
|
27325
|
+
if (isPrivateAddress(host)) {
|
|
27326
|
+
throw new SsrfBlockedError(`Blocked request to non-public address ${host}.`);
|
|
27327
|
+
}
|
|
27328
|
+
return url;
|
|
27329
|
+
}
|
|
27330
|
+
if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) {
|
|
27331
|
+
throw new SsrfBlockedError(`Blocked request to local hostname "${host}".`);
|
|
27332
|
+
}
|
|
27333
|
+
let addresses;
|
|
27334
|
+
try {
|
|
27335
|
+
const records = await dns2.lookup(host, { all: true });
|
|
27336
|
+
addresses = records.map((r) => r.address);
|
|
27337
|
+
} catch {
|
|
27338
|
+
throw new SsrfBlockedError(`Could not resolve host "${host}".`);
|
|
27339
|
+
}
|
|
27340
|
+
if (addresses.length === 0 || addresses.some((addr) => isPrivateAddress(addr))) {
|
|
27341
|
+
throw new SsrfBlockedError(`Blocked request to "${host}" \u2014 resolves to a non-public address.`);
|
|
27342
|
+
}
|
|
27343
|
+
return url;
|
|
27344
|
+
}
|
|
27345
|
+
var ssrfAgent = new Agent({
|
|
27346
|
+
connect: {
|
|
27347
|
+
lookup(hostname, options, callback) {
|
|
27348
|
+
if (allowLocal()) {
|
|
27349
|
+
dnsCallback.lookup(hostname, options, callback);
|
|
27350
|
+
return;
|
|
27351
|
+
}
|
|
27352
|
+
dnsCallback.lookup(hostname, { ...options, all: true }, (err, addresses) => {
|
|
27353
|
+
if (err) return callback(err, "", 0);
|
|
27354
|
+
const list = Array.isArray(addresses) ? addresses : [addresses];
|
|
27355
|
+
const bad = list.find((a) => isPrivateAddress(a.address));
|
|
27356
|
+
if (bad) {
|
|
27357
|
+
return callback(
|
|
27358
|
+
new SsrfBlockedError(`Blocked connection to "${hostname}" \u2014 resolved to non-public address ${bad.address}.`),
|
|
27359
|
+
"",
|
|
27360
|
+
0
|
|
27361
|
+
);
|
|
27362
|
+
}
|
|
27363
|
+
if (options.all) return callback(null, list);
|
|
27364
|
+
const first = list[0];
|
|
27365
|
+
return callback(null, first.address, first.family);
|
|
27366
|
+
});
|
|
27367
|
+
}
|
|
27368
|
+
}
|
|
27369
|
+
});
|
|
27370
|
+
async function safeFetch(rawUrl, init = {}) {
|
|
27371
|
+
let currentUrl = (await assertPublicUrl(rawUrl)).toString();
|
|
27372
|
+
for (let i = 0; i <= MAX_REDIRECTS2; i++) {
|
|
27373
|
+
const resp = await fetch(currentUrl, { ...init, redirect: "manual", dispatcher: ssrfAgent });
|
|
27374
|
+
if (resp.status < 300 || resp.status >= 400) return resp;
|
|
27375
|
+
const location = resp.headers.get("location");
|
|
27376
|
+
if (!location) return resp;
|
|
27377
|
+
const next = new URL(location, currentUrl);
|
|
27378
|
+
await assertPublicUrl(next.toString());
|
|
27379
|
+
currentUrl = next.toString();
|
|
27380
|
+
}
|
|
27381
|
+
throw new SsrfBlockedError(`Too many redirects (>${MAX_REDIRECTS2}).`);
|
|
27382
|
+
}
|
|
26838
27383
|
|
|
26839
27384
|
// src/tools/web-search.ts
|
|
26840
|
-
async function searchSearXNG(query, baseUrl, maxResults) {
|
|
27385
|
+
async function searchSearXNG(query, baseUrl, maxResults, guardUrl = false) {
|
|
26841
27386
|
const url = new URL("/search", baseUrl);
|
|
26842
27387
|
url.searchParams.set("q", query);
|
|
26843
27388
|
url.searchParams.set("format", "json");
|
|
26844
27389
|
url.searchParams.set("categories", "general");
|
|
26845
27390
|
url.searchParams.set("engines", "google,bing,duckduckgo");
|
|
26846
|
-
const
|
|
27391
|
+
const init = {
|
|
26847
27392
|
headers: { "User-Agent": "Cascade-AI/1.0 WebSearchTool" },
|
|
26848
27393
|
signal: AbortSignal.timeout(1e4)
|
|
26849
|
-
}
|
|
27394
|
+
};
|
|
27395
|
+
const resp = guardUrl ? await safeFetch(url.toString(), init) : await fetch(url.toString(), init);
|
|
26850
27396
|
if (!resp.ok) {
|
|
26851
27397
|
throw new Error(`SearXNG returned HTTP ${resp.status}`);
|
|
26852
27398
|
}
|
|
@@ -26975,7 +27521,11 @@ var WebSearchTool = class extends BaseTool {
|
|
|
26975
27521
|
searxngUrl: config.searxngUrl ?? process.env["SEARXNG_URL"],
|
|
26976
27522
|
braveApiKey: config.braveApiKey ?? process.env["BRAVE_SEARCH_API_KEY"],
|
|
26977
27523
|
tavilyApiKey: config.tavilyApiKey ?? process.env["TAVILY_API_KEY"],
|
|
26978
|
-
maxResults: config.maxResults ?? 5
|
|
27524
|
+
maxResults: config.maxResults ?? 5,
|
|
27525
|
+
// Note this reads only from `config`, never from the environment: the
|
|
27526
|
+
// guard tracks the trustworthiness of the URL the host passed in, and an
|
|
27527
|
+
// env var says nothing about that.
|
|
27528
|
+
guardSearxngUrl: config.guardSearxngUrl ?? false
|
|
26979
27529
|
};
|
|
26980
27530
|
}
|
|
26981
27531
|
async execute(input, _options) {
|
|
@@ -26990,7 +27540,7 @@ var WebSearchTool = class extends BaseTool {
|
|
|
26990
27540
|
let anyBackendReachable = false;
|
|
26991
27541
|
if (this.config.searxngUrl) {
|
|
26992
27542
|
try {
|
|
26993
|
-
results = await searchSearXNG(query, this.config.searxngUrl, maxResults);
|
|
27543
|
+
results = await searchSearXNG(query, this.config.searxngUrl, maxResults, this.config.guardSearxngUrl);
|
|
26994
27544
|
anyBackendReachable = true;
|
|
26995
27545
|
if (results.length > 0) return this.formatResults(query, results);
|
|
26996
27546
|
errors.push("SearXNG: returned 0 results");
|
|
@@ -27067,7 +27617,7 @@ var GlobTool = class extends BaseTool {
|
|
|
27067
27617
|
};
|
|
27068
27618
|
async execute(input, _options) {
|
|
27069
27619
|
const pattern = input["pattern"];
|
|
27070
|
-
const searchPath = input["path"] ?
|
|
27620
|
+
const searchPath = input["path"] ? path36.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
|
|
27071
27621
|
const matches = await glob(pattern, {
|
|
27072
27622
|
cwd: searchPath,
|
|
27073
27623
|
ignore: ["node_modules/**", ".git/**", "dist/**", "build/**"],
|
|
@@ -27080,7 +27630,7 @@ var GlobTool = class extends BaseTool {
|
|
|
27080
27630
|
const withMtime = await Promise.all(
|
|
27081
27631
|
matches.map(async (rel) => {
|
|
27082
27632
|
try {
|
|
27083
|
-
const stat = await fs11.stat(
|
|
27633
|
+
const stat = await fs11.stat(path36.join(searchPath, rel));
|
|
27084
27634
|
return { rel, mtime: stat.mtimeMs };
|
|
27085
27635
|
} catch {
|
|
27086
27636
|
return { rel, mtime: 0 };
|
|
@@ -27129,7 +27679,7 @@ var GrepTool = class extends BaseTool {
|
|
|
27129
27679
|
};
|
|
27130
27680
|
async execute(input, _options) {
|
|
27131
27681
|
const pattern = input["pattern"];
|
|
27132
|
-
const searchPath = input["path"] ?
|
|
27682
|
+
const searchPath = input["path"] ? path36.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
|
|
27133
27683
|
const globPattern = input["glob"];
|
|
27134
27684
|
const outputMode = input["output_mode"] ?? "content";
|
|
27135
27685
|
const context = input["context"] ?? 0;
|
|
@@ -27183,12 +27733,12 @@ var GrepTool = class extends BaseTool {
|
|
|
27183
27733
|
nodir: true
|
|
27184
27734
|
});
|
|
27185
27735
|
} catch {
|
|
27186
|
-
files = [
|
|
27736
|
+
files = [path36.relative(searchPath, searchPath) || "."];
|
|
27187
27737
|
}
|
|
27188
27738
|
const results = [];
|
|
27189
27739
|
let totalCount = 0;
|
|
27190
27740
|
for (const rel of files) {
|
|
27191
|
-
const abs =
|
|
27741
|
+
const abs = path36.join(searchPath, rel);
|
|
27192
27742
|
let content;
|
|
27193
27743
|
try {
|
|
27194
27744
|
content = await fs11.readFile(abs, "utf-8");
|
|
@@ -27229,92 +27779,6 @@ Total: ${totalCount} matches`);
|
|
|
27229
27779
|
return results.join("\n");
|
|
27230
27780
|
}
|
|
27231
27781
|
};
|
|
27232
|
-
var SsrfBlockedError = class extends Error {
|
|
27233
|
-
constructor(message) {
|
|
27234
|
-
super(message);
|
|
27235
|
-
this.name = "SsrfBlockedError";
|
|
27236
|
-
}
|
|
27237
|
-
};
|
|
27238
|
-
var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
27239
|
-
var MAX_REDIRECTS2 = 5;
|
|
27240
|
-
function allowLocal() {
|
|
27241
|
-
return process.env["CASCADE_ALLOW_LOCAL_FETCH"] === "1";
|
|
27242
|
-
}
|
|
27243
|
-
function isPrivateAddress(ip) {
|
|
27244
|
-
const type = net.isIP(ip);
|
|
27245
|
-
if (type === 4) return isPrivateIPv4(ip);
|
|
27246
|
-
if (type === 6) return isPrivateIPv6(ip);
|
|
27247
|
-
return true;
|
|
27248
|
-
}
|
|
27249
|
-
function isPrivateIPv4(ip) {
|
|
27250
|
-
const parts = ip.split(".").map((p) => Number(p));
|
|
27251
|
-
if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255)) return true;
|
|
27252
|
-
const [a, b] = parts;
|
|
27253
|
-
if (a === 0) return true;
|
|
27254
|
-
if (a === 10) return true;
|
|
27255
|
-
if (a === 127) return true;
|
|
27256
|
-
if (a === 169 && b === 254) return true;
|
|
27257
|
-
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
27258
|
-
if (a === 192 && b === 168) return true;
|
|
27259
|
-
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
27260
|
-
if (a >= 224) return true;
|
|
27261
|
-
return false;
|
|
27262
|
-
}
|
|
27263
|
-
function isPrivateIPv6(ip) {
|
|
27264
|
-
const lower = ip.toLowerCase().replace(/^\[|\]$/g, "");
|
|
27265
|
-
if (lower === "::1" || lower === "::") return true;
|
|
27266
|
-
if (lower.startsWith("fe80")) return true;
|
|
27267
|
-
if (lower.startsWith("fc") || lower.startsWith("fd")) return true;
|
|
27268
|
-
const mapped = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/.exec(lower);
|
|
27269
|
-
if (mapped) return isPrivateIPv4(mapped[1]);
|
|
27270
|
-
return false;
|
|
27271
|
-
}
|
|
27272
|
-
async function assertPublicUrl(rawUrl) {
|
|
27273
|
-
let url;
|
|
27274
|
-
try {
|
|
27275
|
-
url = new URL(rawUrl);
|
|
27276
|
-
} catch {
|
|
27277
|
-
throw new SsrfBlockedError(`Invalid URL: ${rawUrl}`);
|
|
27278
|
-
}
|
|
27279
|
-
if (!ALLOWED_PROTOCOLS.has(url.protocol)) {
|
|
27280
|
-
throw new SsrfBlockedError(`Blocked URL scheme "${url.protocol}" \u2014 only http and https are allowed.`);
|
|
27281
|
-
}
|
|
27282
|
-
if (allowLocal()) return url;
|
|
27283
|
-
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
27284
|
-
if (net.isIP(host)) {
|
|
27285
|
-
if (isPrivateAddress(host)) {
|
|
27286
|
-
throw new SsrfBlockedError(`Blocked request to non-public address ${host}.`);
|
|
27287
|
-
}
|
|
27288
|
-
return url;
|
|
27289
|
-
}
|
|
27290
|
-
if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) {
|
|
27291
|
-
throw new SsrfBlockedError(`Blocked request to local hostname "${host}".`);
|
|
27292
|
-
}
|
|
27293
|
-
let addresses;
|
|
27294
|
-
try {
|
|
27295
|
-
const records = await dns2.lookup(host, { all: true });
|
|
27296
|
-
addresses = records.map((r) => r.address);
|
|
27297
|
-
} catch {
|
|
27298
|
-
throw new SsrfBlockedError(`Could not resolve host "${host}".`);
|
|
27299
|
-
}
|
|
27300
|
-
if (addresses.length === 0 || addresses.some((addr) => isPrivateAddress(addr))) {
|
|
27301
|
-
throw new SsrfBlockedError(`Blocked request to "${host}" \u2014 resolves to a non-public address.`);
|
|
27302
|
-
}
|
|
27303
|
-
return url;
|
|
27304
|
-
}
|
|
27305
|
-
async function safeFetch(rawUrl, init = {}) {
|
|
27306
|
-
let currentUrl = (await assertPublicUrl(rawUrl)).toString();
|
|
27307
|
-
for (let i = 0; i <= MAX_REDIRECTS2; i++) {
|
|
27308
|
-
const resp = await fetch(currentUrl, { ...init, redirect: "manual" });
|
|
27309
|
-
if (resp.status < 300 || resp.status >= 400) return resp;
|
|
27310
|
-
const location = resp.headers.get("location");
|
|
27311
|
-
if (!location) return resp;
|
|
27312
|
-
const next = new URL(location, currentUrl);
|
|
27313
|
-
await assertPublicUrl(next.toString());
|
|
27314
|
-
currentUrl = next.toString();
|
|
27315
|
-
}
|
|
27316
|
-
throw new SsrfBlockedError(`Too many redirects (>${MAX_REDIRECTS2}).`);
|
|
27317
|
-
}
|
|
27318
27782
|
|
|
27319
27783
|
// src/tools/web-fetch.ts
|
|
27320
27784
|
var MAX_CHARS = 5e4;
|
|
@@ -27587,10 +28051,10 @@ var ToolRegistry = class extends EventEmitter {
|
|
|
27587
28051
|
}
|
|
27588
28052
|
isIgnored(filePath) {
|
|
27589
28053
|
if (!filePath) return false;
|
|
27590
|
-
const abs =
|
|
27591
|
-
const rel =
|
|
27592
|
-
if (!rel || rel.startsWith("..") ||
|
|
27593
|
-
const posixRel = rel.split(
|
|
28054
|
+
const abs = path36.resolve(this.workspaceRoot, filePath);
|
|
28055
|
+
const rel = path36.relative(this.workspaceRoot, abs);
|
|
28056
|
+
if (!rel || rel.startsWith("..") || path36.isAbsolute(rel)) return true;
|
|
28057
|
+
const posixRel = rel.split(path36.sep).join("/");
|
|
27594
28058
|
return this.ignoreMatcher.ignores(posixRel);
|
|
27595
28059
|
}
|
|
27596
28060
|
};
|
|
@@ -27824,22 +28288,22 @@ var FileMcpOAuthStore = class {
|
|
|
27824
28288
|
filePath;
|
|
27825
28289
|
load() {
|
|
27826
28290
|
try {
|
|
27827
|
-
return JSON.parse(
|
|
28291
|
+
return JSON.parse(fs33.readFileSync(this.filePath, "utf-8"));
|
|
27828
28292
|
} catch {
|
|
27829
28293
|
return void 0;
|
|
27830
28294
|
}
|
|
27831
28295
|
}
|
|
27832
28296
|
save(state) {
|
|
27833
|
-
|
|
27834
|
-
|
|
28297
|
+
fs33.mkdirSync(path36.dirname(this.filePath), { recursive: true, mode: 448 });
|
|
28298
|
+
fs33.writeFileSync(this.filePath, JSON.stringify(state, null, 2), { mode: 384 });
|
|
27835
28299
|
try {
|
|
27836
|
-
|
|
28300
|
+
fs33.chmodSync(this.filePath, 384);
|
|
27837
28301
|
} catch {
|
|
27838
28302
|
}
|
|
27839
28303
|
}
|
|
27840
28304
|
clear() {
|
|
27841
28305
|
try {
|
|
27842
|
-
|
|
28306
|
+
fs33.rmSync(this.filePath);
|
|
27843
28307
|
} catch {
|
|
27844
28308
|
}
|
|
27845
28309
|
}
|
|
@@ -28659,7 +29123,7 @@ var TaskAnalyzer = class {
|
|
|
28659
29123
|
|
|
28660
29124
|
// src/core/router/model-performance-tracker.ts
|
|
28661
29125
|
init_pricing();
|
|
28662
|
-
var DEFAULT_STATS_FILE =
|
|
29126
|
+
var DEFAULT_STATS_FILE = path36.join(os10.homedir(), ".cascade", "model-perf.json");
|
|
28663
29127
|
var ModelPerformanceTracker = class _ModelPerformanceTracker {
|
|
28664
29128
|
stats = /* @__PURE__ */ new Map();
|
|
28665
29129
|
featureStats = /* @__PURE__ */ new Map();
|
|
@@ -28699,7 +29163,7 @@ var ModelPerformanceTracker = class _ModelPerformanceTracker {
|
|
|
28699
29163
|
async save() {
|
|
28700
29164
|
if (this.readOnly) return;
|
|
28701
29165
|
try {
|
|
28702
|
-
await fs11.mkdir(
|
|
29166
|
+
await fs11.mkdir(path36.dirname(this.statsFile), { recursive: true });
|
|
28703
29167
|
const modelsObj = {};
|
|
28704
29168
|
const featuresObj = {};
|
|
28705
29169
|
for (const [key, stat] of this.stats) modelsObj[key] = stat;
|
|
@@ -29332,7 +29796,7 @@ Required capability: ${description.slice(0, 300)}`;
|
|
|
29332
29796
|
* any dangerous action, so a silently-reloaded tool can't act without approval. */
|
|
29333
29797
|
async loadPersistedTools() {
|
|
29334
29798
|
if (!this.workspacePath || !this.persistEnabled) return;
|
|
29335
|
-
const file =
|
|
29799
|
+
const file = path36.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
|
|
29336
29800
|
try {
|
|
29337
29801
|
const raw = await fs11.readFile(file, "utf-8");
|
|
29338
29802
|
const specs = JSON.parse(raw);
|
|
@@ -29356,8 +29820,8 @@ Required capability: ${description.slice(0, 300)}`;
|
|
|
29356
29820
|
}
|
|
29357
29821
|
async persist() {
|
|
29358
29822
|
if (!this.workspacePath || !this.persistEnabled) return;
|
|
29359
|
-
const dir =
|
|
29360
|
-
const file =
|
|
29823
|
+
const dir = path36.join(this.workspacePath, ".cascade");
|
|
29824
|
+
const file = path36.join(dir, DYNAMIC_TOOLS_FILE);
|
|
29361
29825
|
try {
|
|
29362
29826
|
await fs11.mkdir(dir, { recursive: true });
|
|
29363
29827
|
await fs11.writeFile(file, JSON.stringify(Array.from(this.specs.values()), null, 2), "utf-8");
|
|
@@ -29377,12 +29841,12 @@ var WorldStateDB = class {
|
|
|
29377
29841
|
constructor(workspacePath, debugMode = false) {
|
|
29378
29842
|
this.workspacePath = workspacePath;
|
|
29379
29843
|
this.debugMode = debugMode;
|
|
29380
|
-
const cascadeDir =
|
|
29381
|
-
if (!
|
|
29382
|
-
|
|
29844
|
+
const cascadeDir = path36.join(workspacePath, ".cascade");
|
|
29845
|
+
if (!fs33.existsSync(cascadeDir)) {
|
|
29846
|
+
fs33.mkdirSync(cascadeDir, { recursive: true });
|
|
29383
29847
|
}
|
|
29384
|
-
this.keyPath =
|
|
29385
|
-
this.dbPath =
|
|
29848
|
+
this.keyPath = path36.join(cascadeDir, "world_state.key");
|
|
29849
|
+
this.dbPath = path36.join(cascadeDir, "world_state.db");
|
|
29386
29850
|
this.initEncryptionKey();
|
|
29387
29851
|
this.db = new Database2(this.dbPath);
|
|
29388
29852
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -29439,11 +29903,11 @@ var WorldStateDB = class {
|
|
|
29439
29903
|
).run(e, r, row.encrypted_value, row.source_worker, row.timestamp, validTo, change);
|
|
29440
29904
|
}
|
|
29441
29905
|
initEncryptionKey() {
|
|
29442
|
-
if (
|
|
29443
|
-
this.encryptionKey =
|
|
29906
|
+
if (fs33.existsSync(this.keyPath)) {
|
|
29907
|
+
this.encryptionKey = fs33.readFileSync(this.keyPath);
|
|
29444
29908
|
} else {
|
|
29445
29909
|
this.encryptionKey = crypto.randomBytes(32);
|
|
29446
|
-
|
|
29910
|
+
fs33.writeFileSync(this.keyPath, this.encryptionKey);
|
|
29447
29911
|
}
|
|
29448
29912
|
}
|
|
29449
29913
|
encrypt(text) {
|
|
@@ -29729,9 +30193,9 @@ var WorldStateDB = class {
|
|
|
29729
30193
|
dumpDebugIfNeeded() {
|
|
29730
30194
|
if (!this.debugMode) return;
|
|
29731
30195
|
try {
|
|
29732
|
-
const dumpPath =
|
|
30196
|
+
const dumpPath = path36.join(os10.tmpdir(), "cascade_world_state_debug.json");
|
|
29733
30197
|
const entries = this.getAllEntries();
|
|
29734
|
-
|
|
30198
|
+
fs33.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
29735
30199
|
} catch (err) {
|
|
29736
30200
|
console.error("Failed to dump debug world state", err);
|
|
29737
30201
|
}
|
|
@@ -29788,7 +30252,7 @@ var ResumeStore = class {
|
|
|
29788
30252
|
createdAt: checkpoint.createdAt ?? new Date(timestamp).toISOString()
|
|
29789
30253
|
};
|
|
29790
30254
|
await promises.mkdir(this.dir, { recursive: true });
|
|
29791
|
-
const finalPath =
|
|
30255
|
+
const finalPath = path36.join(this.dir, `${timestamp}-${safeId(record.taskId)}.json`);
|
|
29792
30256
|
const tempPath = `${finalPath}.${process.pid}.tmp`;
|
|
29793
30257
|
await promises.writeFile(tempPath, JSON.stringify(record, null, 2), "utf-8");
|
|
29794
30258
|
await promises.rename(tempPath, finalPath);
|
|
@@ -29811,7 +30275,7 @@ var ResumeStore = class {
|
|
|
29811
30275
|
const checkpoints = [];
|
|
29812
30276
|
for (const candidate of candidates2) {
|
|
29813
30277
|
try {
|
|
29814
|
-
const raw = await promises.readFile(
|
|
30278
|
+
const raw = await promises.readFile(path36.join(this.dir, candidate.name), "utf-8");
|
|
29815
30279
|
const parsed = JSON.parse(raw);
|
|
29816
30280
|
if (isCheckpoint(parsed)) checkpoints.push(parsed);
|
|
29817
30281
|
} catch {
|
|
@@ -29850,7 +30314,7 @@ var ResumeStore = class {
|
|
|
29850
30314
|
const source = entries.find((name) => FILE_PATTERN.exec(name)?.[2] === wanted);
|
|
29851
30315
|
if (!source) return null;
|
|
29852
30316
|
const claimId = `${this.now()}-${safeId(target.taskId)}`;
|
|
29853
|
-
await promises.rename(
|
|
30317
|
+
await promises.rename(path36.join(this.dir, source), path36.join(this.dir, `${claimId}${CLAIM_SUFFIX}`));
|
|
29854
30318
|
return { checkpoint: target, claimId };
|
|
29855
30319
|
} catch {
|
|
29856
30320
|
return null;
|
|
@@ -29860,8 +30324,8 @@ var ResumeStore = class {
|
|
|
29860
30324
|
async release(claimId) {
|
|
29861
30325
|
try {
|
|
29862
30326
|
await promises.rename(
|
|
29863
|
-
|
|
29864
|
-
|
|
30327
|
+
path36.join(this.dir, `${claimId}${CLAIM_SUFFIX}`),
|
|
30328
|
+
path36.join(this.dir, `${claimId}.json`)
|
|
29865
30329
|
);
|
|
29866
30330
|
} catch {
|
|
29867
30331
|
}
|
|
@@ -29869,7 +30333,7 @@ var ResumeStore = class {
|
|
|
29869
30333
|
/** Discard a claim for good — the resumed run has taken ownership. */
|
|
29870
30334
|
async settle(claimId) {
|
|
29871
30335
|
try {
|
|
29872
|
-
await promises.rm(
|
|
30336
|
+
await promises.rm(path36.join(this.dir, `${claimId}${CLAIM_SUFFIX}`), { force: true });
|
|
29873
30337
|
} catch {
|
|
29874
30338
|
}
|
|
29875
30339
|
}
|
|
@@ -29898,8 +30362,8 @@ var ResumeStore = class {
|
|
|
29898
30362
|
const stamp2 = Number(/^(\d+)-/.exec(name)?.[1]);
|
|
29899
30363
|
if (!Number.isFinite(stamp2) || stamp2 >= cutoff) continue;
|
|
29900
30364
|
await promises.rename(
|
|
29901
|
-
|
|
29902
|
-
|
|
30365
|
+
path36.join(this.dir, name),
|
|
30366
|
+
path36.join(this.dir, `${name.slice(0, -CLAIM_SUFFIX.length)}.json`)
|
|
29903
30367
|
);
|
|
29904
30368
|
reclaimed++;
|
|
29905
30369
|
}
|
|
@@ -29915,7 +30379,7 @@ var ResumeStore = class {
|
|
|
29915
30379
|
await Promise.all(entries.map(async (name) => {
|
|
29916
30380
|
const match = FILE_PATTERN.exec(name);
|
|
29917
30381
|
if (match?.[2] === wanted) {
|
|
29918
|
-
await promises.rm(
|
|
30382
|
+
await promises.rm(path36.join(this.dir, name), { force: true });
|
|
29919
30383
|
}
|
|
29920
30384
|
}));
|
|
29921
30385
|
} catch {
|
|
@@ -29930,7 +30394,7 @@ var ResumeStore = class {
|
|
|
29930
30394
|
const doomed = files.filter((f, index) => f.timestamp < cutoff || index >= this.maxCheckpoints);
|
|
29931
30395
|
const staleTemps = entries.filter((name) => name.endsWith(".tmp")).filter((name) => (Number(FILE_PATTERN.exec(name.replace(/\.\d+\.tmp$/, ".json"))?.[1]) || 0) < cutoff);
|
|
29932
30396
|
await Promise.all([...doomed.map((f) => f.name), ...staleTemps].map(
|
|
29933
|
-
(name) => promises.rm(
|
|
30397
|
+
(name) => promises.rm(path36.join(this.dir, name), { force: true })
|
|
29934
30398
|
));
|
|
29935
30399
|
} catch {
|
|
29936
30400
|
}
|
|
@@ -30307,7 +30771,7 @@ var WorkspaceIndex = class {
|
|
|
30307
30771
|
isIgnored;
|
|
30308
30772
|
chunker;
|
|
30309
30773
|
constructor(opts) {
|
|
30310
|
-
this.root =
|
|
30774
|
+
this.root = path36.resolve(opts.root);
|
|
30311
30775
|
this.db = opts.db;
|
|
30312
30776
|
this.namespace = opts.namespace ?? "code";
|
|
30313
30777
|
this.extensions = new Set((opts.extensions ?? DEFAULT_EXTENSIONS).map((e) => e.replace(/^\./, "").toLowerCase()));
|
|
@@ -30339,14 +30803,14 @@ var WorkspaceIndex = class {
|
|
|
30339
30803
|
return;
|
|
30340
30804
|
}
|
|
30341
30805
|
for (const entry of entries) {
|
|
30342
|
-
const abs =
|
|
30806
|
+
const abs = path36.join(dir, entry.name);
|
|
30343
30807
|
if (entry.isDirectory()) {
|
|
30344
30808
|
if (HARD_SKIP_DIRS.has(entry.name) || this.isIgnored(abs)) continue;
|
|
30345
30809
|
await walk(abs);
|
|
30346
30810
|
} else if (entry.isFile()) {
|
|
30347
|
-
const ext =
|
|
30811
|
+
const ext = path36.extname(entry.name).slice(1).toLowerCase();
|
|
30348
30812
|
if (!this.extensions.has(ext) || this.isIgnored(abs)) continue;
|
|
30349
|
-
out.push(
|
|
30813
|
+
out.push(path36.relative(this.root, abs));
|
|
30350
30814
|
}
|
|
30351
30815
|
}
|
|
30352
30816
|
};
|
|
@@ -30376,7 +30840,7 @@ var WorkspaceIndex = class {
|
|
|
30376
30840
|
const entries = [];
|
|
30377
30841
|
for (const rel of rels) {
|
|
30378
30842
|
try {
|
|
30379
|
-
const abs =
|
|
30843
|
+
const abs = path36.join(this.root, rel);
|
|
30380
30844
|
const stat = await fs11.stat(abs);
|
|
30381
30845
|
if (stat.size > this.maxFileBytes) continue;
|
|
30382
30846
|
const buf = await fs11.readFile(abs);
|
|
@@ -30781,6 +31245,127 @@ var CurrentPageTool = class extends BaseTool {
|
|
|
30781
31245
|
}
|
|
30782
31246
|
};
|
|
30783
31247
|
|
|
31248
|
+
// src/tools/browser-control.ts
|
|
31249
|
+
var BrowserControlTool = class extends BaseTool {
|
|
31250
|
+
name = "browser_control";
|
|
31251
|
+
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.";
|
|
31252
|
+
inputSchema = {
|
|
31253
|
+
type: "object",
|
|
31254
|
+
properties: {
|
|
31255
|
+
action: {
|
|
31256
|
+
type: "string",
|
|
31257
|
+
enum: ["navigate", "click", "fill", "press", "wait_for", "extract_text"],
|
|
31258
|
+
description: "What to do."
|
|
31259
|
+
},
|
|
31260
|
+
url: { type: "string", description: "For navigate: the http(s) address to open." },
|
|
31261
|
+
selector: {
|
|
31262
|
+
type: "string",
|
|
31263
|
+
description: "CSS selector for click, fill and wait_for. Optional for extract_text (defaults to the whole page)."
|
|
31264
|
+
},
|
|
31265
|
+
value: { type: "string", description: "For fill: the text to type." },
|
|
31266
|
+
key: { type: "string", description: 'For press: a key name such as "Enter", "Tab" or "Escape".' },
|
|
31267
|
+
timeoutMs: { type: "number", description: "For wait_for: how long to wait before giving up (default 10000, max 30000)." }
|
|
31268
|
+
},
|
|
31269
|
+
required: ["action"]
|
|
31270
|
+
};
|
|
31271
|
+
controller;
|
|
31272
|
+
release;
|
|
31273
|
+
/** One-way. See `revoke`. */
|
|
31274
|
+
revoked = false;
|
|
31275
|
+
constructor(controller, release) {
|
|
31276
|
+
super();
|
|
31277
|
+
this.controller = controller;
|
|
31278
|
+
this.release = release;
|
|
31279
|
+
}
|
|
31280
|
+
/**
|
|
31281
|
+
* A worker has finished. Ends its hold on the browser.
|
|
31282
|
+
*
|
|
31283
|
+
* Called from the tier's terminal path rather than by the model, because a
|
|
31284
|
+
* model cannot be relied on to announce that it is done — which is why the
|
|
31285
|
+
* host cannot use idle time as the boundary instead.
|
|
31286
|
+
*/
|
|
31287
|
+
releaseActor(actorId) {
|
|
31288
|
+
this.release?.(actorId);
|
|
31289
|
+
}
|
|
31290
|
+
/**
|
|
31291
|
+
* Permanently disable this tool, whatever the registry still holds.
|
|
31292
|
+
*
|
|
31293
|
+
* Registration-time gates cannot express "this run turned out to be
|
|
31294
|
+
* unattended": a host that wires the controller and only then declares the
|
|
31295
|
+
* run unattended has already registered the tool, and a check that ran at
|
|
31296
|
+
* registration has no second chance. Enforcing at EXECUTION time makes the
|
|
31297
|
+
* guarantee independent of call order, which is the whole point of stating
|
|
31298
|
+
* the invariant rather than relying on the scheduler's current sequence.
|
|
31299
|
+
*
|
|
31300
|
+
* One-way on purpose. A safety gate that can be re-opened by a later call is
|
|
31301
|
+
* a gate that a refactor re-opens by accident.
|
|
31302
|
+
*/
|
|
31303
|
+
revoke() {
|
|
31304
|
+
this.revoked = true;
|
|
31305
|
+
}
|
|
31306
|
+
// Acting on a session the user is signed into is the highest-consequence
|
|
31307
|
+
// thing in the tool set — higher than shell, arguably, because the blast
|
|
31308
|
+
// radius is someone else's server rather than this machine.
|
|
31309
|
+
isDangerous() {
|
|
31310
|
+
return true;
|
|
31311
|
+
}
|
|
31312
|
+
async execute(input, options) {
|
|
31313
|
+
if (this.revoked) {
|
|
31314
|
+
return "Error: browser control is not available in this run. It requires a person watching who can stop it, and nobody is.";
|
|
31315
|
+
}
|
|
31316
|
+
const kind = input["action"];
|
|
31317
|
+
if (!kind) return "Error: action is required.";
|
|
31318
|
+
if (options?.signal?.aborted) return "Error: the run was cancelled before this action ran.";
|
|
31319
|
+
const missing = requiredFieldFor(kind, input);
|
|
31320
|
+
if (missing) return `Error: "${kind}" needs ${missing}.`;
|
|
31321
|
+
const action = {
|
|
31322
|
+
kind,
|
|
31323
|
+
...typeof input["url"] === "string" ? { url: input["url"] } : {},
|
|
31324
|
+
...typeof input["selector"] === "string" ? { selector: input["selector"] } : {},
|
|
31325
|
+
...typeof input["value"] === "string" ? { value: input["value"] } : {},
|
|
31326
|
+
...typeof input["key"] === "string" ? { key: input["key"] } : {},
|
|
31327
|
+
...typeof input["timeoutMs"] === "number" ? { timeoutMs: Math.min(Math.max(input["timeoutMs"], 0), 3e4) } : {}
|
|
31328
|
+
};
|
|
31329
|
+
let outcome;
|
|
31330
|
+
try {
|
|
31331
|
+
outcome = await this.controller(action, {
|
|
31332
|
+
// `sessionId` is the run. The host scopes both revocation and its
|
|
31333
|
+
// single-owner lease on it, so passing it is not telemetry — drop it
|
|
31334
|
+
// and a Stop in one run silently stops every later one.
|
|
31335
|
+
sessionId: options?.sessionId ?? "",
|
|
31336
|
+
actorId: options?.tierId ?? options?.sessionId ?? "",
|
|
31337
|
+
...options?.signal ? { signal: options.signal } : {}
|
|
31338
|
+
});
|
|
31339
|
+
} catch (err) {
|
|
31340
|
+
return `Error: the browser could not perform "${kind}" \u2014 ${err instanceof Error ? err.message : String(err)}`;
|
|
31341
|
+
}
|
|
31342
|
+
const where = outcome.url ? `
|
|
31343
|
+
Page: ${outcome.title ? `${outcome.title} \u2014 ` : ""}${outcome.url}` : "";
|
|
31344
|
+
return `${outcome.ok ? "" : "Failed: "}${outcome.detail}${where}`;
|
|
31345
|
+
}
|
|
31346
|
+
};
|
|
31347
|
+
function requiredFieldFor(kind, input) {
|
|
31348
|
+
const has = (k) => typeof input[k] === "string" && input[k].length > 0;
|
|
31349
|
+
const given = (k) => typeof input[k] === "string";
|
|
31350
|
+
switch (kind) {
|
|
31351
|
+
case "navigate":
|
|
31352
|
+
return has("url") ? null : "a url";
|
|
31353
|
+
case "click":
|
|
31354
|
+
return has("selector") ? null : "a selector";
|
|
31355
|
+
case "fill":
|
|
31356
|
+
return has("selector") ? given("value") ? null : "a value" : "a selector";
|
|
31357
|
+
case "press":
|
|
31358
|
+
return has("key") ? null : "a key";
|
|
31359
|
+
case "wait_for":
|
|
31360
|
+
return has("selector") ? null : "a selector";
|
|
31361
|
+
// extract_text works with or without a selector.
|
|
31362
|
+
case "extract_text":
|
|
31363
|
+
return null;
|
|
31364
|
+
default:
|
|
31365
|
+
return null;
|
|
31366
|
+
}
|
|
31367
|
+
}
|
|
31368
|
+
|
|
30784
31369
|
// src/core/cascade.ts
|
|
30785
31370
|
var ESCALATION_DECISION_TIMEOUT_MS = 5 * 6e4;
|
|
30786
31371
|
function buildContextualPrompt(prompt, history = []) {
|
|
@@ -30866,13 +31451,13 @@ var Cascade = class _Cascade extends EventEmitter {
|
|
|
30866
31451
|
this.toolRegistry = new ToolRegistry(this.config.tools, workspacePath);
|
|
30867
31452
|
try {
|
|
30868
31453
|
this.router.setDeadModelStore(new DeadModelStore(
|
|
30869
|
-
fileDeadModelPersistence(
|
|
31454
|
+
fileDeadModelPersistence(path36.join(workspacePath, ".cascade", "dead-models.json"))
|
|
30870
31455
|
));
|
|
30871
31456
|
} catch {
|
|
30872
31457
|
}
|
|
30873
31458
|
try {
|
|
30874
31459
|
this.resumeStore = new ResumeStore({
|
|
30875
|
-
dir:
|
|
31460
|
+
dir: path36.join(workspacePath, ".cascade", "resume")
|
|
30876
31461
|
});
|
|
30877
31462
|
} catch {
|
|
30878
31463
|
}
|
|
@@ -30960,8 +31545,8 @@ var Cascade = class _Cascade extends EventEmitter {
|
|
|
30960
31545
|
this.emit("log", { level: "warn", message: "Code index enabled but no embeddings-capable provider is configured \u2014 skipping." });
|
|
30961
31546
|
return;
|
|
30962
31547
|
}
|
|
30963
|
-
const dbPath = ci.dbPath ||
|
|
30964
|
-
|
|
31548
|
+
const dbPath = ci.dbPath || path36.join(this.workspacePath, ".cascade", "code-index.db");
|
|
31549
|
+
fs33.mkdirSync(path36.dirname(dbPath), { recursive: true });
|
|
30965
31550
|
const db = new Database2(dbPath);
|
|
30966
31551
|
db.pragma("journal_mode = WAL");
|
|
30967
31552
|
const complete = chatCompleterFromProviders(this.config.providers);
|
|
@@ -31273,6 +31858,49 @@ ${input}` }],
|
|
|
31273
31858
|
if ((this.config.tools?.disabledTools ?? []).includes("read_current_page")) return;
|
|
31274
31859
|
this.toolRegistry.register(new CurrentPageTool(provider));
|
|
31275
31860
|
}
|
|
31861
|
+
/**
|
|
31862
|
+
* True when nobody is watching this run — a scheduled/cron task, which
|
|
31863
|
+
* auto-approves every tool because there is no one to ask.
|
|
31864
|
+
*
|
|
31865
|
+
* That auto-approval is fine for ordinary tools and disqualifying for this
|
|
31866
|
+
* one: `browser_control` drives the signed-in browser, and "every run asks
|
|
31867
|
+
* first" is the promise the Settings copy makes. The property used to hold
|
|
31868
|
+
* only because `runScheduledTask` happens not to call `setBrowserController`
|
|
31869
|
+
* — a guarantee resting on an ABSENT call, which is exactly what a later
|
|
31870
|
+
* refactor adds without noticing. Stated and enforced instead.
|
|
31871
|
+
*/
|
|
31872
|
+
unattended = false;
|
|
31873
|
+
/**
|
|
31874
|
+
* Declare that no human is watching this run, so capabilities that depend on
|
|
31875
|
+
* a person being able to see and stop them are refused outright.
|
|
31876
|
+
*/
|
|
31877
|
+
setUnattended(on) {
|
|
31878
|
+
this.unattended = on;
|
|
31879
|
+
if (!on) return;
|
|
31880
|
+
const tool = this.toolRegistry.getTool("browser_control");
|
|
31881
|
+
tool?.revoke?.();
|
|
31882
|
+
}
|
|
31883
|
+
/**
|
|
31884
|
+
* Wire the host's browser so a run can ACT on the open page, not just read it.
|
|
31885
|
+
*
|
|
31886
|
+
* Two gates before this registers anything, and both matter:
|
|
31887
|
+
*
|
|
31888
|
+
* - `tools.agentBrowserControl` must be on. Unlike read_current_page, which
|
|
31889
|
+
* reaches nothing the user has not already opened themselves, this drives
|
|
31890
|
+
* the session they are signed into. That is opt-in, not a default.
|
|
31891
|
+
* - `disabledTools` still removes it, the same as every other tool.
|
|
31892
|
+
* - The run must not be UNATTENDED. See `setUnattended`.
|
|
31893
|
+
*
|
|
31894
|
+
* Host-supplied like the page provider: the tool exists only where there is a
|
|
31895
|
+
* real browser to drive, so it never has to be refused in the CLI or a hosted
|
|
31896
|
+
* run — it is simply not there.
|
|
31897
|
+
*/
|
|
31898
|
+
setBrowserController(controller, release) {
|
|
31899
|
+
if (this.unattended) return;
|
|
31900
|
+
if (!this.config.tools?.agentBrowserControl) return;
|
|
31901
|
+
if ((this.config.tools?.disabledTools ?? []).includes("browser_control")) return;
|
|
31902
|
+
this.toolRegistry.register(new BrowserControlTool(controller, release));
|
|
31903
|
+
}
|
|
31276
31904
|
registerMediaTools(workspacePath) {
|
|
31277
31905
|
const denied = new Set(this.config.tools?.disabledTools ?? []);
|
|
31278
31906
|
const permitted = (name) => !denied.has(name);
|
|
@@ -31878,6 +32506,7 @@ ${prompt}` : prompt;
|
|
|
31878
32506
|
this.router.setRunSignal(options.signal);
|
|
31879
32507
|
const startMs = Date.now();
|
|
31880
32508
|
const taskId = randomUUID();
|
|
32509
|
+
this.emit("run:started", { taskId });
|
|
31881
32510
|
this.decisionLog = [];
|
|
31882
32511
|
this.currentRunCompleted = [];
|
|
31883
32512
|
this.wroteCheckpointThisRun = false;
|
|
@@ -32429,14 +33058,14 @@ ${finalOutput}` : ""
|
|
|
32429
33058
|
init_constants();
|
|
32430
33059
|
var CLOUD_SESSION_FILE = "cloud-session.json";
|
|
32431
33060
|
function globalDir(homeDir = os10.homedir()) {
|
|
32432
|
-
return
|
|
33061
|
+
return path36.join(homeDir, GLOBAL_CONFIG_DIR);
|
|
32433
33062
|
}
|
|
32434
33063
|
function cloudSessionPath(dir = globalDir()) {
|
|
32435
|
-
return
|
|
33064
|
+
return path36.join(dir, CLOUD_SESSION_FILE);
|
|
32436
33065
|
}
|
|
32437
33066
|
function loadCloudSession(dir = globalDir()) {
|
|
32438
33067
|
try {
|
|
32439
|
-
const raw =
|
|
33068
|
+
const raw = fs33.readFileSync(cloudSessionPath(dir), "utf-8");
|
|
32440
33069
|
const parsed = JSON.parse(raw);
|
|
32441
33070
|
if (!parsed?.refreshToken || !parsed?.serverUrl) return null;
|
|
32442
33071
|
return parsed;
|
|
@@ -32445,17 +33074,17 @@ function loadCloudSession(dir = globalDir()) {
|
|
|
32445
33074
|
}
|
|
32446
33075
|
}
|
|
32447
33076
|
function saveCloudSession(session, dir = globalDir()) {
|
|
32448
|
-
|
|
33077
|
+
fs33.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
32449
33078
|
const file = cloudSessionPath(dir);
|
|
32450
|
-
|
|
33079
|
+
fs33.writeFileSync(file, JSON.stringify(session, null, 2), { mode: 384 });
|
|
32451
33080
|
try {
|
|
32452
|
-
|
|
33081
|
+
fs33.chmodSync(file, 384);
|
|
32453
33082
|
} catch {
|
|
32454
33083
|
}
|
|
32455
33084
|
}
|
|
32456
33085
|
function clearCloudSession(dir = globalDir()) {
|
|
32457
33086
|
try {
|
|
32458
|
-
|
|
33087
|
+
fs33.rmSync(cloudSessionPath(dir));
|
|
32459
33088
|
} catch {
|
|
32460
33089
|
}
|
|
32461
33090
|
}
|
|
@@ -32627,16 +33256,16 @@ var CloudClient = class _CloudClient {
|
|
|
32627
33256
|
return (await this.refresh()).accessToken;
|
|
32628
33257
|
}
|
|
32629
33258
|
/** Authenticated request that refreshes once on a 401 (e.g. server-side revoke). */
|
|
32630
|
-
async authed(
|
|
32631
|
-
const call = (token) => fetch(this.url(
|
|
33259
|
+
async authed(path41, init = {}) {
|
|
33260
|
+
const call = (token) => fetch(this.url(path41), { ...init, headers: { ...init.headers ?? {}, Authorization: `Bearer ${token}` } });
|
|
32632
33261
|
let res = await call(await this.accessToken());
|
|
32633
33262
|
if (res.status === 401) res = await call((await this.refresh()).accessToken);
|
|
32634
33263
|
if (!res.ok) throw new Error(`Request failed (${res.status}).`);
|
|
32635
33264
|
const text = await res.text();
|
|
32636
33265
|
return text ? JSON.parse(text) : {};
|
|
32637
33266
|
}
|
|
32638
|
-
authedGet(
|
|
32639
|
-
return this.authed(
|
|
33267
|
+
authedGet(path41) {
|
|
33268
|
+
return this.authed(path41);
|
|
32640
33269
|
}
|
|
32641
33270
|
async fetchMe(token) {
|
|
32642
33271
|
const res = await fetch(this.url("/api/me"), { headers: { Authorization: `Bearer ${token}` } });
|
|
@@ -34410,9 +35039,9 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, s
|
|
|
34410
35039
|
}
|
|
34411
35040
|
} catch {
|
|
34412
35041
|
}
|
|
34413
|
-
const configPath =
|
|
35042
|
+
const configPath = path36.join(workspacePath, ".cascade", "config.json");
|
|
34414
35043
|
try {
|
|
34415
|
-
await fs11.mkdir(
|
|
35044
|
+
await fs11.mkdir(path36.dirname(configPath), { recursive: true });
|
|
34416
35045
|
await fs11.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
34417
35046
|
} catch (err) {
|
|
34418
35047
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -34464,9 +35093,9 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, s
|
|
|
34464
35093
|
if (msg.includes("non-text parts")) return;
|
|
34465
35094
|
originalLog(...args);
|
|
34466
35095
|
};
|
|
34467
|
-
const store = new MemoryStore(
|
|
35096
|
+
const store = new MemoryStore(path36.join(workspacePath, CASCADE_DB_FILE));
|
|
34468
35097
|
storeRef.current = store;
|
|
34469
|
-
globalStoreRef.current = new MemoryStore(
|
|
35098
|
+
globalStoreRef.current = new MemoryStore(path36.join(os10.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE));
|
|
34470
35099
|
const identityRows = store.listIdentities().map((i) => ({ id: i.id, name: i.name, isDefault: i.isDefault }));
|
|
34471
35100
|
setIdentities(identityRows);
|
|
34472
35101
|
let initialIdentityId = config.defaultIdentityId ?? identityRows.find((i) => i.isDefault)?.id ?? identityRows[0]?.id;
|
|
@@ -34546,7 +35175,7 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, s
|
|
|
34546
35175
|
onThemeChange: (name) => setTheme(getTheme(name)),
|
|
34547
35176
|
onExport: async (fmt) => {
|
|
34548
35177
|
const stamp2 = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
|
|
34549
|
-
const exportPath =
|
|
35178
|
+
const exportPath = path36.join(workspacePath, `cascade-export-${stamp2}.${fmt === "json" ? "json" : "md"}`);
|
|
34550
35179
|
if (fmt === "json") {
|
|
34551
35180
|
await fs11.writeFile(exportPath, JSON.stringify({ sessionId: sessionIdRef.current, messages: state.messages }, null, 2), "utf-8");
|
|
34552
35181
|
} else {
|
|
@@ -35466,7 +36095,7 @@ function stringifySlashOutput(val) {
|
|
|
35466
36095
|
}
|
|
35467
36096
|
async function searchSessionsAndMessages(query, workspacePath) {
|
|
35468
36097
|
if (!query) return "Usage: /search <query>";
|
|
35469
|
-
const dbPath =
|
|
36098
|
+
const dbPath = path36.join(workspacePath, CASCADE_DB_FILE);
|
|
35470
36099
|
try {
|
|
35471
36100
|
await fs11.access(dbPath);
|
|
35472
36101
|
} catch {
|
|
@@ -35500,7 +36129,7 @@ async function searchSessionsAndMessages(query, workspacePath) {
|
|
|
35500
36129
|
async function diagnoseRuntime(config, workspacePath) {
|
|
35501
36130
|
const providers = config.providers.map((p) => `${p.type}${p.apiKey ? " (key set)" : " (no key)"}`).join("\n");
|
|
35502
36131
|
const models = [`T1: ${config.models.t1 ?? "default"}`, `T2: ${config.models.t2 ?? "default"}`, `T3: ${config.models.t3 ?? "default"}`].join("\n");
|
|
35503
|
-
const store = new MemoryStore(
|
|
36132
|
+
const store = new MemoryStore(path36.join(workspacePath, CASCADE_DB_FILE));
|
|
35504
36133
|
try {
|
|
35505
36134
|
const sessions2 = store.listSessions(void 0, 3);
|
|
35506
36135
|
return [
|
|
@@ -35519,7 +36148,7 @@ async function diagnoseRuntime(config, workspacePath) {
|
|
|
35519
36148
|
}
|
|
35520
36149
|
async function showRecentLogs(args, workspacePath) {
|
|
35521
36150
|
const limit = Number.parseInt(args[0] ?? "10", 10) || 10;
|
|
35522
|
-
const store = new MemoryStore(
|
|
36151
|
+
const store = new MemoryStore(path36.join(workspacePath, CASCADE_DB_FILE));
|
|
35523
36152
|
try {
|
|
35524
36153
|
const logs = store.listRuntimeNodeLogs(void 0, void 0, limit);
|
|
35525
36154
|
if (!logs.length) return "No recent runtime logs.";
|
|
@@ -35531,7 +36160,7 @@ async function showRecentLogs(args, workspacePath) {
|
|
|
35531
36160
|
async function loadSessionSnapshot(args, workspacePath) {
|
|
35532
36161
|
const sessionId = args[0];
|
|
35533
36162
|
if (!sessionId) return "Usage: /resume <sessionId>";
|
|
35534
|
-
const store = new MemoryStore(
|
|
36163
|
+
const store = new MemoryStore(path36.join(workspacePath, CASCADE_DB_FILE));
|
|
35535
36164
|
try {
|
|
35536
36165
|
const session = store.getSession(sessionId);
|
|
35537
36166
|
if (!session) return `Session not found: ${sessionId}`;
|
|
@@ -35951,9 +36580,9 @@ function SetupWizard({ workspacePath, onComplete }) {
|
|
|
35951
36580
|
...anyAuto ? { cascadeAuto: true } : {}
|
|
35952
36581
|
};
|
|
35953
36582
|
const config = CascadeConfigSchema.parse(rawConfig);
|
|
35954
|
-
const configDir =
|
|
36583
|
+
const configDir = path36.join(workspacePath, ".cascade");
|
|
35955
36584
|
await fs11.mkdir(configDir, { recursive: true });
|
|
35956
|
-
const configPath =
|
|
36585
|
+
const configPath = path36.join(workspacePath, CASCADE_CONFIG_FILE);
|
|
35957
36586
|
await fs11.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
35958
36587
|
savedConfigRef.current = config;
|
|
35959
36588
|
dispatchRef.current({ type: "GO_DONE" });
|
|
@@ -36294,14 +36923,14 @@ function printTelemetryBanner() {
|
|
|
36294
36923
|
async function initCommand(workspacePath = process.cwd()) {
|
|
36295
36924
|
const spin = ora({ text: "Initializing Cascade project\u2026", color: "magenta" }).start();
|
|
36296
36925
|
try {
|
|
36297
|
-
const configDir =
|
|
36926
|
+
const configDir = path36.join(workspacePath, ".cascade");
|
|
36298
36927
|
await fs11.mkdir(configDir, { recursive: true });
|
|
36299
|
-
const mdPath =
|
|
36928
|
+
const mdPath = path36.join(workspacePath, "CASCADE.md");
|
|
36300
36929
|
if (!await fileExists(mdPath)) {
|
|
36301
36930
|
await createDefaultCascadeMd(workspacePath);
|
|
36302
36931
|
spin.succeed(chalk8.green("Created CASCADE.md"));
|
|
36303
36932
|
}
|
|
36304
|
-
const ignorePath =
|
|
36933
|
+
const ignorePath = path36.join(workspacePath, ".cascadeignore");
|
|
36305
36934
|
if (!await fileExists(ignorePath)) {
|
|
36306
36935
|
await createDefaultIgnoreFile(workspacePath);
|
|
36307
36936
|
spin.succeed(chalk8.green("Created .cascadeignore"));
|
|
@@ -36310,7 +36939,7 @@ async function initCommand(workspacePath = process.cwd()) {
|
|
|
36310
36939
|
console.log();
|
|
36311
36940
|
console.log(chalk8.magenta(" \u25C8 Cascade AI \u2014 Project initialized"));
|
|
36312
36941
|
console.log();
|
|
36313
|
-
const configPath =
|
|
36942
|
+
const configPath = path36.join(workspacePath, CASCADE_CONFIG_FILE);
|
|
36314
36943
|
if (await fileExists(configPath)) {
|
|
36315
36944
|
console.log(chalk8.yellow(" .cascade/config.json already exists \u2014 launching wizard to reconfigure."));
|
|
36316
36945
|
console.log();
|
|
@@ -36461,8 +37090,8 @@ function fromEnv(env) {
|
|
|
36461
37090
|
return out;
|
|
36462
37091
|
}
|
|
36463
37092
|
async function fromClaudeCode(home, env) {
|
|
36464
|
-
const dir = str(env["CLAUDE_CONFIG_DIR"]) ??
|
|
36465
|
-
const file =
|
|
37093
|
+
const dir = str(env["CLAUDE_CONFIG_DIR"]) ?? path36.join(home, ".claude");
|
|
37094
|
+
const file = path36.join(dir, ".credentials.json");
|
|
36466
37095
|
const data = await readJson(file);
|
|
36467
37096
|
if (!data) return [];
|
|
36468
37097
|
const found = [];
|
|
@@ -36486,7 +37115,7 @@ async function fromClaudeCode(home, env) {
|
|
|
36486
37115
|
return found;
|
|
36487
37116
|
}
|
|
36488
37117
|
async function fromCodex(home) {
|
|
36489
|
-
const file =
|
|
37118
|
+
const file = path36.join(home, ".codex", "auth.json");
|
|
36490
37119
|
const data = await readJson(file);
|
|
36491
37120
|
if (!data) return [];
|
|
36492
37121
|
const apiKey = str(data["OPENAI_API_KEY"]);
|
|
@@ -36509,7 +37138,7 @@ async function fromCodex(home) {
|
|
|
36509
37138
|
return [];
|
|
36510
37139
|
}
|
|
36511
37140
|
async function fromGemini(home) {
|
|
36512
|
-
const file =
|
|
37141
|
+
const file = path36.join(home, ".gemini", "oauth_creds.json");
|
|
36513
37142
|
const data = await readJson(file);
|
|
36514
37143
|
const accessToken = str(data?.["access_token"]);
|
|
36515
37144
|
if (!accessToken) return [];
|
|
@@ -36525,7 +37154,7 @@ async function fromGemini(home) {
|
|
|
36525
37154
|
}
|
|
36526
37155
|
async function fromCopilot(home) {
|
|
36527
37156
|
for (const name of ["apps.json", "hosts.json"]) {
|
|
36528
|
-
const file =
|
|
37157
|
+
const file = path36.join(home, ".config", "github-copilot", name);
|
|
36529
37158
|
const data = await readJson(file);
|
|
36530
37159
|
if (!data) continue;
|
|
36531
37160
|
for (const value of Object.values(data)) {
|
|
@@ -36826,7 +37455,7 @@ async function doctorCommand() {
|
|
|
36826
37455
|
checks.push({
|
|
36827
37456
|
label: "Cascade config",
|
|
36828
37457
|
ok: true,
|
|
36829
|
-
detail: `Loaded ${
|
|
37458
|
+
detail: `Loaded ${path36.join(process.cwd(), CASCADE_CONFIG_FILE)}`
|
|
36830
37459
|
});
|
|
36831
37460
|
const providers = [
|
|
36832
37461
|
{ type: "anthropic", name: "Anthropic" },
|
|
@@ -36936,7 +37565,7 @@ async function checkEndpoint(url) {
|
|
|
36936
37565
|
}
|
|
36937
37566
|
}
|
|
36938
37567
|
async function indexCommand(dirPath) {
|
|
36939
|
-
const workspace =
|
|
37568
|
+
const workspace = path36.resolve(dirPath || process.cwd());
|
|
36940
37569
|
const cm = new ConfigManager(workspace);
|
|
36941
37570
|
await cm.load();
|
|
36942
37571
|
const config = cm.getConfig();
|
|
@@ -36947,8 +37576,8 @@ async function indexCommand(dirPath) {
|
|
|
36947
37576
|
process.exitCode = 1;
|
|
36948
37577
|
return;
|
|
36949
37578
|
}
|
|
36950
|
-
const dbPath = config.codeIndex?.dbPath ||
|
|
36951
|
-
|
|
37579
|
+
const dbPath = config.codeIndex?.dbPath || path36.join(workspace, ".cascade", "code-index.db");
|
|
37580
|
+
fs33.mkdirSync(path36.dirname(dbPath), { recursive: true });
|
|
36952
37581
|
const db = new Database2(dbPath);
|
|
36953
37582
|
db.pragma("journal_mode = WAL");
|
|
36954
37583
|
const complete = chatCompleterFromProviders(config.providers);
|
|
@@ -37480,7 +38109,7 @@ function openBrowser2(url) {
|
|
|
37480
38109
|
}
|
|
37481
38110
|
function storePathFor(name) {
|
|
37482
38111
|
const safe = name.replace(/[^a-z0-9._-]/gi, "_").slice(0, 64) || "server";
|
|
37483
|
-
return
|
|
38112
|
+
return path36.join(os10.homedir(), ".cascade-ai", "mcp-oauth", `${safe}.json`);
|
|
37484
38113
|
}
|
|
37485
38114
|
function defaultName(url) {
|
|
37486
38115
|
try {
|
|
@@ -37775,6 +38404,7 @@ function applySettingsPayload(config, data) {
|
|
|
37775
38404
|
if (["auto", "T1", "T2", "T3"].includes(a["forceTier"])) config.routing = { ...config.routing ?? {}, forceTier: a["forceTier"] };
|
|
37776
38405
|
if (typeof a["benchmarksLive"] === "boolean") config.benchmarks = { ...config.benchmarks ?? {}, live: a["benchmarksLive"] };
|
|
37777
38406
|
if (["isolate", "worker", "auto"].includes(a["dynamicToolSandbox"])) config.tools = { ...config.tools ?? {}, dynamicToolSandbox: a["dynamicToolSandbox"] };
|
|
38407
|
+
if (typeof a["agentBrowserControl"] === "boolean") config.tools = { ...config.tools ?? {}, agentBrowserControl: a["agentBrowserControl"] };
|
|
37778
38408
|
if (typeof a["factsExtraction"] === "boolean") config.knowledge = { ...config.knowledge ?? {}, factsExtraction: a["factsExtraction"] };
|
|
37779
38409
|
if (typeof a["rememberSessions"] === "boolean") config.memory = { ...config.memory ?? {}, rememberSessions: a["rememberSessions"] };
|
|
37780
38410
|
if (typeof a["enableToolCreation"] === "boolean") config.enableToolCreation = a["enableToolCreation"];
|
|
@@ -37832,6 +38462,7 @@ function settingsSnapshot(config) {
|
|
|
37832
38462
|
forceTier: config.routing?.forceTier,
|
|
37833
38463
|
benchmarksLive: config.benchmarks?.live,
|
|
37834
38464
|
dynamicToolSandbox: config.tools?.dynamicToolSandbox,
|
|
38465
|
+
agentBrowserControl: config.tools?.agentBrowserControl,
|
|
37835
38466
|
factsExtraction: config.knowledge?.factsExtraction,
|
|
37836
38467
|
rememberSessions: config.memory?.rememberSessions,
|
|
37837
38468
|
enableToolCreation: config.enableToolCreation,
|
|
@@ -37867,8 +38498,8 @@ function replaceInPlace(target, source) {
|
|
|
37867
38498
|
}
|
|
37868
38499
|
function writeConfigFile(configPath, config) {
|
|
37869
38500
|
try {
|
|
37870
|
-
|
|
37871
|
-
|
|
38501
|
+
fs33.mkdirSync(path36.dirname(configPath), { recursive: true });
|
|
38502
|
+
fs33.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
37872
38503
|
return { ok: true };
|
|
37873
38504
|
} catch (err) {
|
|
37874
38505
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -38284,7 +38915,7 @@ function aggregateCostStats(sessions2, opts = {}) {
|
|
|
38284
38915
|
}
|
|
38285
38916
|
|
|
38286
38917
|
// src/dashboard/server.ts
|
|
38287
|
-
var __dirname$1 =
|
|
38918
|
+
var __dirname$1 = path36.dirname(fileURLToPath(import.meta.url));
|
|
38288
38919
|
var ORPHANED_ESCALATION_GRACE_MS = 3e4;
|
|
38289
38920
|
var MASK = "***";
|
|
38290
38921
|
function masked(key, value) {
|
|
@@ -38342,6 +38973,14 @@ var DashboardServer = class {
|
|
|
38342
38973
|
* always failing.
|
|
38343
38974
|
*/
|
|
38344
38975
|
currentPageProvider;
|
|
38976
|
+
/**
|
|
38977
|
+
* Supplied by the desktop shell so a run can ACT on that page — click, type,
|
|
38978
|
+
* submit — not just read it. Same reasoning as the provider above: absent in
|
|
38979
|
+
* the CLI, so the tool is not registered rather than present and failing.
|
|
38980
|
+
* Cascade applies the `tools.agentBrowserControl` gate on top of this.
|
|
38981
|
+
*/
|
|
38982
|
+
browserController;
|
|
38983
|
+
browserActorRelease;
|
|
38345
38984
|
activeControllers = /* @__PURE__ */ new Map();
|
|
38346
38985
|
/**
|
|
38347
38986
|
* Run taskIds per chat session — file snapshots are keyed by the run's
|
|
@@ -38403,6 +39042,7 @@ var DashboardServer = class {
|
|
|
38403
39042
|
});
|
|
38404
39043
|
this.socket.onConfigUpdate(async (data) => {
|
|
38405
39044
|
const result = await commitSettings(this.config, data, (committed) => this.persistConfig(committed));
|
|
39045
|
+
if (result.ok) this.settingsChangeHook?.(this.config);
|
|
38406
39046
|
const refused = result.refused.map((r) => ({
|
|
38407
39047
|
type: r.type,
|
|
38408
39048
|
reason: r.reason,
|
|
@@ -38426,6 +39066,7 @@ var DashboardServer = class {
|
|
|
38426
39066
|
const cascade = new Cascade(cfg, this.workspacePath, this.store);
|
|
38427
39067
|
this.activeSessions.set(sessionId, cascade);
|
|
38428
39068
|
if (this.currentPageProvider) cascade.setCurrentPageProvider(this.currentPageProvider);
|
|
39069
|
+
if (this.browserController) cascade.setBrowserController(this.browserController, this.browserActorRelease);
|
|
38429
39070
|
cascade.on("stream:token", (e) => {
|
|
38430
39071
|
this.emitToSessionClients(socketId, sessionId, "stream:token", { sessionId, tierId: e.tierId, text: e.text, primary: e.primary });
|
|
38431
39072
|
});
|
|
@@ -38449,13 +39090,16 @@ var DashboardServer = class {
|
|
|
38449
39090
|
if (ev.requestId) this.clearPendingEscalation(ev.requestId);
|
|
38450
39091
|
this.emitToSessionClients(socketId, sessionId, "escalation:timeout", { sessionId, ...e });
|
|
38451
39092
|
});
|
|
39093
|
+
cascade.on("run:started", (e) => {
|
|
39094
|
+
const id = e.taskId;
|
|
39095
|
+
if (id) this.recordSessionTask(sessionId, id);
|
|
39096
|
+
});
|
|
38452
39097
|
try {
|
|
38453
39098
|
const result = await cascade.run({
|
|
38454
39099
|
prompt: runPrompt,
|
|
38455
39100
|
signal: abortController.signal,
|
|
38456
39101
|
approvalCallback: this.makeApprovalCallback(sessionId)
|
|
38457
39102
|
});
|
|
38458
|
-
this.recordSessionTask(sessionId, result.taskId);
|
|
38459
39103
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED", result);
|
|
38460
39104
|
this.captureWhy(sessionId, cascade, result);
|
|
38461
39105
|
this.emitToSessionClients(socketId, sessionId, "session:complete", { sessionId, result });
|
|
@@ -38521,6 +39165,49 @@ var DashboardServer = class {
|
|
|
38521
39165
|
setCurrentPageProvider(provider) {
|
|
38522
39166
|
this.currentPageProvider = provider;
|
|
38523
39167
|
}
|
|
39168
|
+
/** Let runs act on that view, not just read it (desktop only). */
|
|
39169
|
+
setBrowserController(controller, release) {
|
|
39170
|
+
this.browserController = controller;
|
|
39171
|
+
this.browserActorRelease = release;
|
|
39172
|
+
}
|
|
39173
|
+
/**
|
|
39174
|
+
* Told whenever settings are committed through THIS server.
|
|
39175
|
+
*
|
|
39176
|
+
* Settings reach the live config by two routes — the desktop's Electron IPC
|
|
39177
|
+
* handler and this server's `config:update` socket — and both run the same
|
|
39178
|
+
* `commitSettings`. The desktop mirrors `tools.agentBrowserControl` into its
|
|
39179
|
+
* browser module after its own route, but had no way to learn about the
|
|
39180
|
+
* other, so a socket write left the module's copy stale: an enable the module
|
|
39181
|
+
* still refused, or worse a disable the module still honoured for a run
|
|
39182
|
+
* already holding the tool. This is the same sibling-writer drift
|
|
39183
|
+
* `settings-payload.ts` exists to prevent, one layer up.
|
|
39184
|
+
*/
|
|
39185
|
+
settingsChangeHook;
|
|
39186
|
+
onSettingsChanged(hook) {
|
|
39187
|
+
this.settingsChangeHook = hook;
|
|
39188
|
+
}
|
|
39189
|
+
/**
|
|
39190
|
+
* Told when a run ends, so a host holding run-scoped state can release it.
|
|
39191
|
+
*
|
|
39192
|
+
* The desktop uses this to retire a run's browser Stop control: that has to
|
|
39193
|
+
* outlive each individual action (the gap between two actions is when the
|
|
39194
|
+
* user most wants it) but must not outlive the run.
|
|
39195
|
+
*/
|
|
39196
|
+
/**
|
|
39197
|
+
* Fired when a run ends, carrying BOTH identifiers explicitly.
|
|
39198
|
+
*
|
|
39199
|
+
* They are different things and conflating them was a live bug: a chat
|
|
39200
|
+
* `sessionId` can contain many runs (hence `sessionTaskIds`), while
|
|
39201
|
+
* `Cascade.run()` mints a fresh random `taskId` per run — and it is the
|
|
39202
|
+
* taskId that tools see as `ToolExecuteOptions.sessionId`. Passing only the
|
|
39203
|
+
* chat id meant browser cleanup keyed on the task id matched nothing, so a
|
|
39204
|
+
* finished run kept its Stop banner armed and held its browser lease until
|
|
39205
|
+
* the deadlock ceiling.
|
|
39206
|
+
*/
|
|
39207
|
+
runEndedHook;
|
|
39208
|
+
onRunEnded(hook) {
|
|
39209
|
+
this.runEndedHook = hook;
|
|
39210
|
+
}
|
|
38524
39211
|
async start() {
|
|
38525
39212
|
const isLoopback = this.host === "127.0.0.1" || this.host === "::1" || this.host === "localhost";
|
|
38526
39213
|
if (!isLoopback) {
|
|
@@ -38603,7 +39290,7 @@ var DashboardServer = class {
|
|
|
38603
39290
|
* silently and still typecheck.
|
|
38604
39291
|
*/
|
|
38605
39292
|
persistConfig(config) {
|
|
38606
|
-
const result = writeConfigFile(
|
|
39293
|
+
const result = writeConfigFile(path36.join(this.workspacePath, CASCADE_CONFIG_FILE), config);
|
|
38607
39294
|
if (!result.ok) console.warn(`[dashboard] Failed to persist config: ${result.error}`);
|
|
38608
39295
|
return result;
|
|
38609
39296
|
}
|
|
@@ -38622,7 +39309,7 @@ var DashboardServer = class {
|
|
|
38622
39309
|
*/
|
|
38623
39310
|
syncGlobalCredentials() {
|
|
38624
39311
|
try {
|
|
38625
|
-
saveGlobalCredentials(
|
|
39312
|
+
saveGlobalCredentials(path36.join(os10.homedir(), GLOBAL_CONFIG_DIR), this.config.providers ?? []);
|
|
38626
39313
|
} catch (err) {
|
|
38627
39314
|
console.warn(`[dashboard] Failed to sync global credentials: ${err instanceof Error ? err.message : String(err)}`);
|
|
38628
39315
|
}
|
|
@@ -38637,15 +39324,15 @@ var DashboardServer = class {
|
|
|
38637
39324
|
resolveDashboardSecret() {
|
|
38638
39325
|
const fromConfig = this.config.dashboard.secret ?? process.env["CASCADE_DASHBOARD_SECRET"];
|
|
38639
39326
|
if (fromConfig) return fromConfig;
|
|
38640
|
-
const secretPath =
|
|
39327
|
+
const secretPath = path36.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
|
|
38641
39328
|
try {
|
|
38642
|
-
if (
|
|
38643
|
-
const existing =
|
|
39329
|
+
if (fs33.existsSync(secretPath)) {
|
|
39330
|
+
const existing = fs33.readFileSync(secretPath, "utf-8").trim();
|
|
38644
39331
|
if (existing.length >= 16) return existing;
|
|
38645
39332
|
}
|
|
38646
39333
|
const generated = randomUUID();
|
|
38647
|
-
|
|
38648
|
-
|
|
39334
|
+
fs33.mkdirSync(path36.dirname(secretPath), { recursive: true });
|
|
39335
|
+
fs33.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
|
|
38649
39336
|
if (this.config.dashboard.auth) {
|
|
38650
39337
|
console.warn(
|
|
38651
39338
|
`Dashboard auth enabled with no secret configured; persisted a generated secret to ${secretPath}. Set CASCADE_DASHBOARD_SECRET or config.dashboard.secret to override.`
|
|
@@ -38672,7 +39359,7 @@ var DashboardServer = class {
|
|
|
38672
39359
|
// ── Setup ─────────────────────────────────────
|
|
38673
39360
|
getGlobalStore() {
|
|
38674
39361
|
if (!this.globalStore) {
|
|
38675
|
-
const globalDbPath =
|
|
39362
|
+
const globalDbPath = path36.join(os10.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
38676
39363
|
this.globalStore = new MemoryStore(globalDbPath);
|
|
38677
39364
|
}
|
|
38678
39365
|
return this.globalStore;
|
|
@@ -38711,6 +39398,8 @@ var DashboardServer = class {
|
|
|
38711
39398
|
}
|
|
38712
39399
|
/** Record run end: the assistant reply (when there is one) and the runtime row's final status. */
|
|
38713
39400
|
persistRunEnd(sessionId, title, latestPrompt, reply, status, result) {
|
|
39401
|
+
const endedTaskId = result?.taskId ?? this.sessionTaskIds.get(sessionId)?.at(-1);
|
|
39402
|
+
this.runEndedHook?.({ sessionId, ...endedTaskId ? { taskId: endedTaskId } : {} });
|
|
38714
39403
|
try {
|
|
38715
39404
|
if (reply && reply.trim()) {
|
|
38716
39405
|
this.store.addMessage({ id: randomUUID(), sessionId, role: "assistant", content: reply, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -38804,6 +39493,7 @@ var DashboardServer = class {
|
|
|
38804
39493
|
const prompt = task.prompt;
|
|
38805
39494
|
const title = this.persistRunStart(sessionId, `[${task.name}] ${prompt}`);
|
|
38806
39495
|
const cascade = new Cascade(this.config, task.workspacePath ?? this.workspacePath, this.store);
|
|
39496
|
+
cascade.setUnattended(true);
|
|
38807
39497
|
this.activeSessions.set(sessionId, cascade);
|
|
38808
39498
|
cascade.on("tier:status", (e) => {
|
|
38809
39499
|
this.socket.broadcast("tier:status", { sessionId, ...e });
|
|
@@ -38811,13 +39501,16 @@ var DashboardServer = class {
|
|
|
38811
39501
|
cascade.on("peer:message", (e) => {
|
|
38812
39502
|
this.socket.emitPeerMessage(e);
|
|
38813
39503
|
});
|
|
39504
|
+
cascade.on("run:started", (e) => {
|
|
39505
|
+
const id = e.taskId;
|
|
39506
|
+
if (id) this.recordSessionTask(sessionId, id);
|
|
39507
|
+
});
|
|
38814
39508
|
try {
|
|
38815
39509
|
const result = await cascade.run({
|
|
38816
39510
|
prompt,
|
|
38817
39511
|
identityId: task.identityId,
|
|
38818
39512
|
approvalCallback: async () => ({ approved: true, always: false })
|
|
38819
39513
|
});
|
|
38820
|
-
this.recordSessionTask(sessionId, result.taskId);
|
|
38821
39514
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED", result);
|
|
38822
39515
|
this.captureWhy(sessionId, cascade, result);
|
|
38823
39516
|
this.socket.broadcast("session:complete", { sessionId, result });
|
|
@@ -39052,12 +39745,12 @@ ${prompt}`;
|
|
|
39052
39745
|
}
|
|
39053
39746
|
}
|
|
39054
39747
|
watchRuntimeChanges() {
|
|
39055
|
-
const workspaceDbPath =
|
|
39056
|
-
const globalDbPath =
|
|
39748
|
+
const workspaceDbPath = path36.join(this.workspacePath, CASCADE_DB_FILE);
|
|
39749
|
+
const globalDbPath = path36.join(os10.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
39057
39750
|
const watchPaths = [workspaceDbPath, globalDbPath].filter((p, index, arr) => arr.indexOf(p) === index);
|
|
39058
39751
|
for (const watchPath of watchPaths) {
|
|
39059
|
-
if (!
|
|
39060
|
-
|
|
39752
|
+
if (!fs33.existsSync(watchPath)) continue;
|
|
39753
|
+
fs33.watchFile(watchPath, { interval: 3e3 }, () => {
|
|
39061
39754
|
this.throttledBroadcast(watchPath === globalDbPath ? "global" : "workspace");
|
|
39062
39755
|
});
|
|
39063
39756
|
}
|
|
@@ -39179,7 +39872,7 @@ ${prompt}`;
|
|
|
39179
39872
|
const sessionId = req.params.id;
|
|
39180
39873
|
this.store.deleteSession(sessionId);
|
|
39181
39874
|
this.store.deleteRuntimeSession(sessionId);
|
|
39182
|
-
const globalDbPath =
|
|
39875
|
+
const globalDbPath = path36.join(os10.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
39183
39876
|
const globalStore = new MemoryStore(globalDbPath);
|
|
39184
39877
|
try {
|
|
39185
39878
|
globalStore.deleteRuntimeSession(sessionId);
|
|
@@ -39272,7 +39965,7 @@ ${prompt}`;
|
|
|
39272
39965
|
});
|
|
39273
39966
|
this.app.delete("/api/sessions", auth2, (req, res) => {
|
|
39274
39967
|
const body = req.body;
|
|
39275
|
-
const globalDbPath =
|
|
39968
|
+
const globalDbPath = path36.join(os10.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
39276
39969
|
if (body?.ids && Array.isArray(body.ids) && body.ids.length > 0) {
|
|
39277
39970
|
const globalStore = new MemoryStore(globalDbPath);
|
|
39278
39971
|
try {
|
|
@@ -39295,7 +39988,7 @@ ${prompt}`;
|
|
|
39295
39988
|
});
|
|
39296
39989
|
this.app.delete("/api/runtime", auth2, (_req, res) => {
|
|
39297
39990
|
this.store.deleteAllRuntimeNodes();
|
|
39298
|
-
const globalDbPath =
|
|
39991
|
+
const globalDbPath = path36.join(os10.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
39299
39992
|
const globalStore = new MemoryStore(globalDbPath);
|
|
39300
39993
|
try {
|
|
39301
39994
|
globalStore.deleteAllRuntimeNodes();
|
|
@@ -39380,12 +40073,12 @@ ${prompt}`;
|
|
|
39380
40073
|
if (body["tierLimits"]) this.config.tierLimits = { ...this.config.tierLimits, ...body["tierLimits"] };
|
|
39381
40074
|
if (body["budget"]) this.config.budget = { ...this.config.budget, ...body["budget"] };
|
|
39382
40075
|
try {
|
|
39383
|
-
const configPath =
|
|
39384
|
-
const existing =
|
|
40076
|
+
const configPath = path36.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
40077
|
+
const existing = fs33.existsSync(configPath) ? JSON.parse(fs33.readFileSync(configPath, "utf-8")) : {};
|
|
39385
40078
|
const updated = { ...existing, tierLimits: this.config.tierLimits, budget: this.config.budget };
|
|
39386
40079
|
const tmp = configPath + ".tmp";
|
|
39387
|
-
|
|
39388
|
-
|
|
40080
|
+
fs33.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
|
|
40081
|
+
fs33.renameSync(tmp, configPath);
|
|
39389
40082
|
res.json({ ok: true });
|
|
39390
40083
|
} catch (err) {
|
|
39391
40084
|
res.status(500).json({ error: `Failed to save config: ${err instanceof Error ? err.message : String(err)}` });
|
|
@@ -39413,7 +40106,7 @@ ${prompt}`;
|
|
|
39413
40106
|
this.app.get("/api/runtime", auth2, (req, res) => {
|
|
39414
40107
|
const scope = req.query["scope"] ?? "workspace";
|
|
39415
40108
|
if (scope === "global") {
|
|
39416
|
-
const globalDbPath =
|
|
40109
|
+
const globalDbPath = path36.join(os10.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
39417
40110
|
const globalStore = new MemoryStore(globalDbPath);
|
|
39418
40111
|
try {
|
|
39419
40112
|
res.json({
|
|
@@ -39450,6 +40143,7 @@ ${prompt}`;
|
|
|
39450
40143
|
const cascade = new Cascade(this.config, this.workspacePath, this.store);
|
|
39451
40144
|
this.activeSessions.set(sessionId, cascade);
|
|
39452
40145
|
if (this.currentPageProvider) cascade.setCurrentPageProvider(this.currentPageProvider);
|
|
40146
|
+
if (this.browserController) cascade.setBrowserController(this.browserController, this.browserActorRelease);
|
|
39453
40147
|
cascade.on("stream:token", (e) => {
|
|
39454
40148
|
this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, tierId: e.tierId, text: e.text, primary: e.primary });
|
|
39455
40149
|
});
|
|
@@ -39473,13 +40167,16 @@ ${prompt}`;
|
|
|
39473
40167
|
if (ev.requestId) this.clearPendingEscalation(ev.requestId);
|
|
39474
40168
|
this.socket.broadcastToRoom(`session:${sessionId}`, "escalation:timeout", { sessionId, ...e });
|
|
39475
40169
|
});
|
|
40170
|
+
cascade.on("run:started", (e) => {
|
|
40171
|
+
const id = e.taskId;
|
|
40172
|
+
if (id) this.recordSessionTask(sessionId, id);
|
|
40173
|
+
});
|
|
39476
40174
|
try {
|
|
39477
40175
|
const result = await cascade.run({
|
|
39478
40176
|
prompt: runPrompt,
|
|
39479
40177
|
identityId: body.identityId,
|
|
39480
40178
|
approvalCallback: this.makeApprovalCallback(sessionId)
|
|
39481
40179
|
});
|
|
39482
|
-
this.recordSessionTask(sessionId, result.taskId);
|
|
39483
40180
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED", result);
|
|
39484
40181
|
this.captureWhy(sessionId, cascade, result);
|
|
39485
40182
|
this.socket.broadcast("cost:update", {
|
|
@@ -39754,13 +40451,13 @@ ${prompt}`;
|
|
|
39754
40451
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
39755
40452
|
}
|
|
39756
40453
|
});
|
|
39757
|
-
const prodPath =
|
|
39758
|
-
const devPath =
|
|
39759
|
-
const webDistPath =
|
|
39760
|
-
if (
|
|
40454
|
+
const prodPath = path36.resolve(__dirname$1, "../web/dist");
|
|
40455
|
+
const devPath = path36.resolve(__dirname$1, "../../web/dist");
|
|
40456
|
+
const webDistPath = fs33.existsSync(prodPath) ? prodPath : devPath;
|
|
40457
|
+
if (fs33.existsSync(webDistPath)) {
|
|
39761
40458
|
this.app.use(express.static(webDistPath));
|
|
39762
40459
|
this.app.get("*", (_req, res) => {
|
|
39763
|
-
res.sendFile(
|
|
40460
|
+
res.sendFile(path36.join(webDistPath, "index.html"));
|
|
39764
40461
|
});
|
|
39765
40462
|
} else {
|
|
39766
40463
|
this.app.get("/", (_req, res) => {
|
|
@@ -39781,7 +40478,7 @@ init_constants();
|
|
|
39781
40478
|
async function dashboardCommand(config, workspacePath = process.cwd()) {
|
|
39782
40479
|
const port = config.dashboard.port ?? DEFAULT_DASHBOARD_PORT;
|
|
39783
40480
|
const spin = ora({ text: `Starting dashboard on port ${port}\u2026`, color: "magenta" }).start();
|
|
39784
|
-
const store = new MemoryStore(
|
|
40481
|
+
const store = new MemoryStore(path36.join(workspacePath, CASCADE_DB_FILE));
|
|
39785
40482
|
const server = new DashboardServer(config, store, workspacePath);
|
|
39786
40483
|
server.watchRuntimeChanges();
|
|
39787
40484
|
const gThis = globalThis;
|
|
@@ -39813,7 +40510,7 @@ init_constants();
|
|
|
39813
40510
|
function makeIdentityCommand(workspacePath = process.cwd()) {
|
|
39814
40511
|
const identity = new Command("identity").alias("id").description("Manage Cascade identities");
|
|
39815
40512
|
identity.command("list").description("List all available identities").action(() => {
|
|
39816
|
-
const store = new MemoryStore(
|
|
40513
|
+
const store = new MemoryStore(path36.join(workspacePath, CASCADE_DB_FILE));
|
|
39817
40514
|
try {
|
|
39818
40515
|
const identities = store.listIdentities();
|
|
39819
40516
|
console.log(chalk8.bold("\n Identities:"));
|
|
@@ -39833,7 +40530,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
|
|
|
39833
40530
|
}
|
|
39834
40531
|
});
|
|
39835
40532
|
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) => {
|
|
39836
|
-
const store = new MemoryStore(
|
|
40533
|
+
const store = new MemoryStore(path36.join(workspacePath, CASCADE_DB_FILE));
|
|
39837
40534
|
try {
|
|
39838
40535
|
if (options.default) {
|
|
39839
40536
|
const existingDefault = store.getDefaultIdentity();
|
|
@@ -39859,7 +40556,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
|
|
|
39859
40556
|
}
|
|
39860
40557
|
});
|
|
39861
40558
|
identity.command("set-default <name>").description("Set an identity as default by name or ID").action((query) => {
|
|
39862
|
-
const store = new MemoryStore(
|
|
40559
|
+
const store = new MemoryStore(path36.join(workspacePath, CASCADE_DB_FILE));
|
|
39863
40560
|
try {
|
|
39864
40561
|
const identities = store.listIdentities();
|
|
39865
40562
|
const match = identities.find((i) => i.id === query || i.name.toLowerCase() === query.toLowerCase());
|
|
@@ -40008,8 +40705,8 @@ async function exportCommand(options = {}) {
|
|
|
40008
40705
|
let store;
|
|
40009
40706
|
try {
|
|
40010
40707
|
const workspacePath = options.workspacePath ?? process.cwd();
|
|
40011
|
-
const workspaceDbPath =
|
|
40012
|
-
const globalDbPath =
|
|
40708
|
+
const workspaceDbPath = path36.join(workspacePath, CASCADE_DB_FILE);
|
|
40709
|
+
const globalDbPath = path36.join(os10.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE);
|
|
40013
40710
|
let dbPath = globalDbPath;
|
|
40014
40711
|
try {
|
|
40015
40712
|
await fs11.access(workspaceDbPath);
|
|
@@ -40051,7 +40748,7 @@ async function exportCommand(options = {}) {
|
|
|
40051
40748
|
const ext = format === "json" ? ".json" : ".md";
|
|
40052
40749
|
const safeName = session.title.replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").slice(0, 60);
|
|
40053
40750
|
const defaultFile = `cascade-export-${safeName}${ext}`;
|
|
40054
|
-
const outPath = options.output ?
|
|
40751
|
+
const outPath = options.output ? path36.resolve(options.output) : path36.join(process.cwd(), defaultFile);
|
|
40055
40752
|
await fs11.writeFile(outPath, content, "utf-8");
|
|
40056
40753
|
spin.succeed(chalk8.green(`Exported to ${chalk8.white(outPath)}`));
|
|
40057
40754
|
const messageCount = Array.isArray(session.messages) ? session.messages.length : 0;
|
|
@@ -40191,7 +40888,7 @@ var SHOW_CURSOR = "\x1B[?25h";
|
|
|
40191
40888
|
function writeNow(stdout, text) {
|
|
40192
40889
|
if (typeof stdout.fd === "number") {
|
|
40193
40890
|
try {
|
|
40194
|
-
|
|
40891
|
+
fs33.writeSync(stdout.fd, text);
|
|
40195
40892
|
return;
|
|
40196
40893
|
} catch {
|
|
40197
40894
|
}
|
|
@@ -40214,11 +40911,11 @@ function restoreTerminal(stdin, stdout) {
|
|
|
40214
40911
|
dotenv.config();
|
|
40215
40912
|
function warnIfBuildIsStale() {
|
|
40216
40913
|
try {
|
|
40217
|
-
const here =
|
|
40914
|
+
const here = path36.dirname(fileURLToPath(import.meta.url));
|
|
40218
40915
|
for (const rel of ["..", "../.."]) {
|
|
40219
|
-
const pkgPath =
|
|
40220
|
-
if (!
|
|
40221
|
-
const pkg = JSON.parse(
|
|
40916
|
+
const pkgPath = path36.join(here, rel, "package.json");
|
|
40917
|
+
if (!fs33.existsSync(pkgPath)) continue;
|
|
40918
|
+
const pkg = JSON.parse(fs33.readFileSync(pkgPath, "utf-8"));
|
|
40222
40919
|
if (pkg.name !== "cascade-ai") continue;
|
|
40223
40920
|
if (pkg.version && pkg.version !== CASCADE_VERSION) {
|
|
40224
40921
|
console.error(
|