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/index.cjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var Database2 = require('better-sqlite3');
|
|
4
|
-
var
|
|
5
|
-
var
|
|
4
|
+
var path28 = require('path');
|
|
5
|
+
var fs27 = require('fs');
|
|
6
6
|
var os6 = require('os');
|
|
7
7
|
var crypto = require('crypto');
|
|
8
8
|
var EventEmitter = require('events');
|
|
9
9
|
var glob = require('glob');
|
|
10
10
|
var Anthropic = require('@anthropic-ai/sdk');
|
|
11
|
-
var
|
|
11
|
+
var dnsCallback = require('dns');
|
|
12
12
|
var http = require('http');
|
|
13
13
|
var https = require('https');
|
|
14
14
|
var zlib = require('zlib');
|
|
@@ -24,6 +24,7 @@ var simpleGit = require('simple-git');
|
|
|
24
24
|
var PDFDocument = require('pdfkit');
|
|
25
25
|
var dns2 = require('dns/promises');
|
|
26
26
|
var net = require('net');
|
|
27
|
+
var undici = require('undici');
|
|
27
28
|
var index_js = require('@modelcontextprotocol/sdk/client/index.js');
|
|
28
29
|
var stdio_js = require('@modelcontextprotocol/sdk/client/stdio.js');
|
|
29
30
|
var streamableHttp_js = require('@modelcontextprotocol/sdk/client/streamableHttp.js');
|
|
@@ -61,13 +62,13 @@ function _interopNamespace(e) {
|
|
|
61
62
|
}
|
|
62
63
|
|
|
63
64
|
var Database2__default = /*#__PURE__*/_interopDefault(Database2);
|
|
64
|
-
var
|
|
65
|
-
var
|
|
65
|
+
var path28__default = /*#__PURE__*/_interopDefault(path28);
|
|
66
|
+
var fs27__default = /*#__PURE__*/_interopDefault(fs27);
|
|
66
67
|
var os6__default = /*#__PURE__*/_interopDefault(os6);
|
|
67
68
|
var crypto__default = /*#__PURE__*/_interopDefault(crypto);
|
|
68
69
|
var EventEmitter__default = /*#__PURE__*/_interopDefault(EventEmitter);
|
|
69
70
|
var Anthropic__default = /*#__PURE__*/_interopDefault(Anthropic);
|
|
70
|
-
var
|
|
71
|
+
var dnsCallback__default = /*#__PURE__*/_interopDefault(dnsCallback);
|
|
71
72
|
var http__default = /*#__PURE__*/_interopDefault(http);
|
|
72
73
|
var https__default = /*#__PURE__*/_interopDefault(https);
|
|
73
74
|
var zlib__default = /*#__PURE__*/_interopDefault(zlib);
|
|
@@ -178,16 +179,16 @@ var require_isarray = __commonJS({
|
|
|
178
179
|
}
|
|
179
180
|
});
|
|
180
181
|
|
|
181
|
-
// node_modules/
|
|
182
|
+
// node_modules/readable-stream/lib/internal/streams/stream.js
|
|
182
183
|
var require_stream = __commonJS({
|
|
183
|
-
"node_modules/
|
|
184
|
+
"node_modules/readable-stream/lib/internal/streams/stream.js"(exports, module) {
|
|
184
185
|
module.exports = __require("stream");
|
|
185
186
|
}
|
|
186
187
|
});
|
|
187
188
|
|
|
188
|
-
// node_modules/
|
|
189
|
+
// node_modules/readable-stream/node_modules/safe-buffer/index.js
|
|
189
190
|
var require_safe_buffer = __commonJS({
|
|
190
|
-
"node_modules/
|
|
191
|
+
"node_modules/readable-stream/node_modules/safe-buffer/index.js"(exports, module) {
|
|
191
192
|
var buffer = __require("buffer");
|
|
192
193
|
var Buffer2 = buffer.Buffer;
|
|
193
194
|
function copyProps(src, dst) {
|
|
@@ -305,7 +306,7 @@ var require_util = __commonJS({
|
|
|
305
306
|
typeof arg === "undefined";
|
|
306
307
|
}
|
|
307
308
|
exports.isPrimitive = isPrimitive;
|
|
308
|
-
exports.isBuffer = Buffer.isBuffer;
|
|
309
|
+
exports.isBuffer = __require("buffer").Buffer.isBuffer;
|
|
309
310
|
function objectToString(o) {
|
|
310
311
|
return Object.prototype.toString.call(o);
|
|
311
312
|
}
|
|
@@ -358,9 +359,9 @@ var require_inherits = __commonJS({
|
|
|
358
359
|
}
|
|
359
360
|
});
|
|
360
361
|
|
|
361
|
-
// node_modules/
|
|
362
|
+
// node_modules/readable-stream/lib/internal/streams/BufferList.js
|
|
362
363
|
var require_BufferList = __commonJS({
|
|
363
|
-
"node_modules/
|
|
364
|
+
"node_modules/readable-stream/lib/internal/streams/BufferList.js"(exports, module) {
|
|
364
365
|
function _classCallCheck(instance, Constructor) {
|
|
365
366
|
if (!(instance instanceof Constructor)) {
|
|
366
367
|
throw new TypeError("Cannot call a class as a function");
|
|
@@ -435,9 +436,9 @@ var require_BufferList = __commonJS({
|
|
|
435
436
|
}
|
|
436
437
|
});
|
|
437
438
|
|
|
438
|
-
// node_modules/
|
|
439
|
+
// node_modules/readable-stream/lib/internal/streams/destroy.js
|
|
439
440
|
var require_destroy = __commonJS({
|
|
440
|
-
"node_modules/
|
|
441
|
+
"node_modules/readable-stream/lib/internal/streams/destroy.js"(exports, module) {
|
|
441
442
|
var pna = require_process_nextick_args();
|
|
442
443
|
function destroy(err, cb) {
|
|
443
444
|
var _this = this;
|
|
@@ -510,9 +511,9 @@ var require_node = __commonJS({
|
|
|
510
511
|
}
|
|
511
512
|
});
|
|
512
513
|
|
|
513
|
-
// node_modules/
|
|
514
|
+
// node_modules/readable-stream/lib/_stream_writable.js
|
|
514
515
|
var require_stream_writable = __commonJS({
|
|
515
|
-
"node_modules/
|
|
516
|
+
"node_modules/readable-stream/lib/_stream_writable.js"(exports, module) {
|
|
516
517
|
var pna = require_process_nextick_args();
|
|
517
518
|
module.exports = Writable;
|
|
518
519
|
function CorkedRequest(state) {
|
|
@@ -949,9 +950,9 @@ var require_stream_writable = __commonJS({
|
|
|
949
950
|
}
|
|
950
951
|
});
|
|
951
952
|
|
|
952
|
-
// node_modules/
|
|
953
|
+
// node_modules/readable-stream/lib/_stream_duplex.js
|
|
953
954
|
var require_stream_duplex = __commonJS({
|
|
954
|
-
"node_modules/
|
|
955
|
+
"node_modules/readable-stream/lib/_stream_duplex.js"(exports, module) {
|
|
955
956
|
var pna = require_process_nextick_args();
|
|
956
957
|
var objectKeys = Object.keys || function(obj) {
|
|
957
958
|
var keys2 = [];
|
|
@@ -1025,10 +1026,67 @@ var require_stream_duplex = __commonJS({
|
|
|
1025
1026
|
}
|
|
1026
1027
|
});
|
|
1027
1028
|
|
|
1028
|
-
// node_modules/
|
|
1029
|
+
// node_modules/string_decoder/node_modules/safe-buffer/index.js
|
|
1030
|
+
var require_safe_buffer2 = __commonJS({
|
|
1031
|
+
"node_modules/string_decoder/node_modules/safe-buffer/index.js"(exports, module) {
|
|
1032
|
+
var buffer = __require("buffer");
|
|
1033
|
+
var Buffer2 = buffer.Buffer;
|
|
1034
|
+
function copyProps(src, dst) {
|
|
1035
|
+
for (var key in src) {
|
|
1036
|
+
dst[key] = src[key];
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
if (Buffer2.from && Buffer2.alloc && Buffer2.allocUnsafe && Buffer2.allocUnsafeSlow) {
|
|
1040
|
+
module.exports = buffer;
|
|
1041
|
+
} else {
|
|
1042
|
+
copyProps(buffer, exports);
|
|
1043
|
+
exports.Buffer = SafeBuffer;
|
|
1044
|
+
}
|
|
1045
|
+
function SafeBuffer(arg, encodingOrOffset, length) {
|
|
1046
|
+
return Buffer2(arg, encodingOrOffset, length);
|
|
1047
|
+
}
|
|
1048
|
+
copyProps(Buffer2, SafeBuffer);
|
|
1049
|
+
SafeBuffer.from = function(arg, encodingOrOffset, length) {
|
|
1050
|
+
if (typeof arg === "number") {
|
|
1051
|
+
throw new TypeError("Argument must not be a number");
|
|
1052
|
+
}
|
|
1053
|
+
return Buffer2(arg, encodingOrOffset, length);
|
|
1054
|
+
};
|
|
1055
|
+
SafeBuffer.alloc = function(size, fill, encoding) {
|
|
1056
|
+
if (typeof size !== "number") {
|
|
1057
|
+
throw new TypeError("Argument must be a number");
|
|
1058
|
+
}
|
|
1059
|
+
var buf = Buffer2(size);
|
|
1060
|
+
if (fill !== void 0) {
|
|
1061
|
+
if (typeof encoding === "string") {
|
|
1062
|
+
buf.fill(fill, encoding);
|
|
1063
|
+
} else {
|
|
1064
|
+
buf.fill(fill);
|
|
1065
|
+
}
|
|
1066
|
+
} else {
|
|
1067
|
+
buf.fill(0);
|
|
1068
|
+
}
|
|
1069
|
+
return buf;
|
|
1070
|
+
};
|
|
1071
|
+
SafeBuffer.allocUnsafe = function(size) {
|
|
1072
|
+
if (typeof size !== "number") {
|
|
1073
|
+
throw new TypeError("Argument must be a number");
|
|
1074
|
+
}
|
|
1075
|
+
return Buffer2(size);
|
|
1076
|
+
};
|
|
1077
|
+
SafeBuffer.allocUnsafeSlow = function(size) {
|
|
1078
|
+
if (typeof size !== "number") {
|
|
1079
|
+
throw new TypeError("Argument must be a number");
|
|
1080
|
+
}
|
|
1081
|
+
return buffer.SlowBuffer(size);
|
|
1082
|
+
};
|
|
1083
|
+
}
|
|
1084
|
+
});
|
|
1085
|
+
|
|
1086
|
+
// node_modules/string_decoder/lib/string_decoder.js
|
|
1029
1087
|
var require_string_decoder = __commonJS({
|
|
1030
|
-
"node_modules/
|
|
1031
|
-
var Buffer2 =
|
|
1088
|
+
"node_modules/string_decoder/lib/string_decoder.js"(exports) {
|
|
1089
|
+
var Buffer2 = require_safe_buffer2().Buffer;
|
|
1032
1090
|
var isEncoding = Buffer2.isEncoding || function(encoding) {
|
|
1033
1091
|
encoding = "" + encoding;
|
|
1034
1092
|
switch (encoding && encoding.toLowerCase()) {
|
|
@@ -1262,9 +1320,9 @@ var require_string_decoder = __commonJS({
|
|
|
1262
1320
|
}
|
|
1263
1321
|
});
|
|
1264
1322
|
|
|
1265
|
-
// node_modules/
|
|
1323
|
+
// node_modules/readable-stream/lib/_stream_readable.js
|
|
1266
1324
|
var require_stream_readable = __commonJS({
|
|
1267
|
-
"node_modules/
|
|
1325
|
+
"node_modules/readable-stream/lib/_stream_readable.js"(exports, module) {
|
|
1268
1326
|
var pna = require_process_nextick_args();
|
|
1269
1327
|
module.exports = Readable2;
|
|
1270
1328
|
var isArray = require_isarray();
|
|
@@ -1947,9 +2005,9 @@ var require_stream_readable = __commonJS({
|
|
|
1947
2005
|
}
|
|
1948
2006
|
});
|
|
1949
2007
|
|
|
1950
|
-
// node_modules/
|
|
2008
|
+
// node_modules/readable-stream/lib/_stream_transform.js
|
|
1951
2009
|
var require_stream_transform = __commonJS({
|
|
1952
|
-
"node_modules/
|
|
2010
|
+
"node_modules/readable-stream/lib/_stream_transform.js"(exports, module) {
|
|
1953
2011
|
module.exports = Transform;
|
|
1954
2012
|
var Duplex = require_stream_duplex();
|
|
1955
2013
|
var util = Object.create(require_util());
|
|
@@ -2046,9 +2104,9 @@ var require_stream_transform = __commonJS({
|
|
|
2046
2104
|
}
|
|
2047
2105
|
});
|
|
2048
2106
|
|
|
2049
|
-
// node_modules/
|
|
2107
|
+
// node_modules/readable-stream/lib/_stream_passthrough.js
|
|
2050
2108
|
var require_stream_passthrough = __commonJS({
|
|
2051
|
-
"node_modules/
|
|
2109
|
+
"node_modules/readable-stream/lib/_stream_passthrough.js"(exports, module) {
|
|
2052
2110
|
module.exports = PassThrough;
|
|
2053
2111
|
var Transform = require_stream_transform();
|
|
2054
2112
|
var util = Object.create(require_util());
|
|
@@ -2064,9 +2122,9 @@ var require_stream_passthrough = __commonJS({
|
|
|
2064
2122
|
}
|
|
2065
2123
|
});
|
|
2066
2124
|
|
|
2067
|
-
// node_modules/
|
|
2125
|
+
// node_modules/readable-stream/readable.js
|
|
2068
2126
|
var require_readable = __commonJS({
|
|
2069
|
-
"node_modules/
|
|
2127
|
+
"node_modules/readable-stream/readable.js"(exports, module) {
|
|
2070
2128
|
var Stream = __require("stream");
|
|
2071
2129
|
if (process.env.READABLE_STREAM === "disable" && Stream) {
|
|
2072
2130
|
module.exports = Stream;
|
|
@@ -2975,8 +3033,8 @@ var require_utils = __commonJS({
|
|
|
2975
3033
|
var result = transform[inputType][outputType](input);
|
|
2976
3034
|
return result;
|
|
2977
3035
|
};
|
|
2978
|
-
exports.resolve = function(
|
|
2979
|
-
var parts =
|
|
3036
|
+
exports.resolve = function(path30) {
|
|
3037
|
+
var parts = path30.split("/");
|
|
2980
3038
|
var result = [];
|
|
2981
3039
|
for (var index = 0; index < parts.length; index++) {
|
|
2982
3040
|
var part = parts[index];
|
|
@@ -8791,18 +8849,18 @@ var require_object = __commonJS({
|
|
|
8791
8849
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
8792
8850
|
this.files[name] = object;
|
|
8793
8851
|
};
|
|
8794
|
-
var parentFolder = function(
|
|
8795
|
-
if (
|
|
8796
|
-
|
|
8852
|
+
var parentFolder = function(path30) {
|
|
8853
|
+
if (path30.slice(-1) === "/") {
|
|
8854
|
+
path30 = path30.substring(0, path30.length - 1);
|
|
8797
8855
|
}
|
|
8798
|
-
var lastSlash =
|
|
8799
|
-
return lastSlash > 0 ?
|
|
8856
|
+
var lastSlash = path30.lastIndexOf("/");
|
|
8857
|
+
return lastSlash > 0 ? path30.substring(0, lastSlash) : "";
|
|
8800
8858
|
};
|
|
8801
|
-
var forceTrailingSlash = function(
|
|
8802
|
-
if (
|
|
8803
|
-
|
|
8859
|
+
var forceTrailingSlash = function(path30) {
|
|
8860
|
+
if (path30.slice(-1) !== "/") {
|
|
8861
|
+
path30 += "/";
|
|
8804
8862
|
}
|
|
8805
|
-
return
|
|
8863
|
+
return path30;
|
|
8806
8864
|
};
|
|
8807
8865
|
var folderAdd = function(name, createFolders) {
|
|
8808
8866
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -9801,12 +9859,12 @@ var init_audit_logger = __esm({
|
|
|
9801
9859
|
constructor(workspacePath, debugMode = false) {
|
|
9802
9860
|
this.workspacePath = workspacePath;
|
|
9803
9861
|
this.debugMode = debugMode;
|
|
9804
|
-
const cascadeDir =
|
|
9805
|
-
if (!
|
|
9806
|
-
|
|
9862
|
+
const cascadeDir = path28__default.default.join(workspacePath, ".cascade");
|
|
9863
|
+
if (!fs27__default.default.existsSync(cascadeDir)) {
|
|
9864
|
+
fs27__default.default.mkdirSync(cascadeDir, { recursive: true });
|
|
9807
9865
|
}
|
|
9808
|
-
this.keyPath =
|
|
9809
|
-
this.dbPath =
|
|
9866
|
+
this.keyPath = path28__default.default.join(cascadeDir, "audit_log.key");
|
|
9867
|
+
this.dbPath = path28__default.default.join(cascadeDir, "audit_log.db");
|
|
9810
9868
|
this.initEncryptionKey();
|
|
9811
9869
|
this.db = new Database2__default.default(this.dbPath);
|
|
9812
9870
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -9836,11 +9894,11 @@ var init_audit_logger = __esm({
|
|
|
9836
9894
|
return crypto__default.default.createHash("sha256").update(`${prevHash}|${timestamp}|${eventType}|${tierId}|${encryptedPayload}`).digest("hex");
|
|
9837
9895
|
}
|
|
9838
9896
|
initEncryptionKey() {
|
|
9839
|
-
if (
|
|
9840
|
-
this.encryptionKey =
|
|
9897
|
+
if (fs27__default.default.existsSync(this.keyPath)) {
|
|
9898
|
+
this.encryptionKey = fs27__default.default.readFileSync(this.keyPath);
|
|
9841
9899
|
} else {
|
|
9842
9900
|
this.encryptionKey = crypto__default.default.randomBytes(32);
|
|
9843
|
-
|
|
9901
|
+
fs27__default.default.writeFileSync(this.keyPath, this.encryptionKey);
|
|
9844
9902
|
}
|
|
9845
9903
|
}
|
|
9846
9904
|
encrypt(text) {
|
|
@@ -9918,9 +9976,9 @@ var init_audit_logger = __esm({
|
|
|
9918
9976
|
dumpDebugIfNeeded() {
|
|
9919
9977
|
if (!this.debugMode) return;
|
|
9920
9978
|
try {
|
|
9921
|
-
const dumpPath =
|
|
9979
|
+
const dumpPath = path28__default.default.join(os6__default.default.tmpdir(), "cascade_audit_logs_debug.json");
|
|
9922
9980
|
const entries = this.getAllLogs();
|
|
9923
|
-
|
|
9981
|
+
fs27__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
9924
9982
|
} catch (err) {
|
|
9925
9983
|
console.error("Failed to dump debug audit logs", err);
|
|
9926
9984
|
}
|
|
@@ -11611,7 +11669,7 @@ function reconcilePrice(model, live) {
|
|
|
11611
11669
|
}
|
|
11612
11670
|
|
|
11613
11671
|
// src/constants.ts
|
|
11614
|
-
var CASCADE_VERSION = "0.
|
|
11672
|
+
var CASCADE_VERSION = "0.78.0";
|
|
11615
11673
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
11616
11674
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
11617
11675
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -11632,64 +11690,88 @@ var DEFAULT_AUTO_SUMMARIZE_AT = 15e4;
|
|
|
11632
11690
|
var DEFAULT_MAX_SESSION_MESSAGES = 1e3;
|
|
11633
11691
|
var DEFAULT_RETENTION_DAYS = 90;
|
|
11634
11692
|
var MODELS = {
|
|
11635
|
-
// Anthropic
|
|
11636
|
-
"claude-
|
|
11637
|
-
id: "claude-
|
|
11638
|
-
name: "Claude
|
|
11693
|
+
// Anthropic — active Claude API models (2026-08-27).
|
|
11694
|
+
"claude-fable-5": {
|
|
11695
|
+
id: "claude-fable-5",
|
|
11696
|
+
name: "Claude Fable 5",
|
|
11639
11697
|
provider: "anthropic",
|
|
11640
|
-
contextWindow:
|
|
11698
|
+
contextWindow: 1e6,
|
|
11641
11699
|
isVisionCapable: true,
|
|
11642
|
-
inputCostPer1kTokens: 0.
|
|
11643
|
-
outputCostPer1kTokens: 0.
|
|
11644
|
-
maxOutputTokens:
|
|
11700
|
+
inputCostPer1kTokens: 0.01,
|
|
11701
|
+
outputCostPer1kTokens: 0.05,
|
|
11702
|
+
maxOutputTokens: 128e3,
|
|
11645
11703
|
supportsStreaming: true,
|
|
11646
11704
|
isLocal: false
|
|
11647
11705
|
},
|
|
11648
|
-
"claude-
|
|
11649
|
-
id: "claude-
|
|
11650
|
-
name: "Claude
|
|
11706
|
+
"claude-opus-5": {
|
|
11707
|
+
id: "claude-opus-5",
|
|
11708
|
+
name: "Claude Opus 5",
|
|
11651
11709
|
provider: "anthropic",
|
|
11652
|
-
contextWindow:
|
|
11710
|
+
contextWindow: 1e6,
|
|
11653
11711
|
isVisionCapable: true,
|
|
11654
|
-
inputCostPer1kTokens:
|
|
11655
|
-
outputCostPer1kTokens: 0.
|
|
11656
|
-
maxOutputTokens:
|
|
11712
|
+
inputCostPer1kTokens: 5e-3,
|
|
11713
|
+
outputCostPer1kTokens: 0.025,
|
|
11714
|
+
maxOutputTokens: 128e3,
|
|
11657
11715
|
supportsStreaming: true,
|
|
11658
11716
|
isLocal: false
|
|
11659
11717
|
},
|
|
11660
|
-
"claude-
|
|
11661
|
-
id: "claude-
|
|
11662
|
-
name: "Claude
|
|
11718
|
+
"claude-opus-4-8": {
|
|
11719
|
+
id: "claude-opus-4-8",
|
|
11720
|
+
name: "Claude Opus 4.8",
|
|
11663
11721
|
provider: "anthropic",
|
|
11664
|
-
contextWindow:
|
|
11722
|
+
contextWindow: 1e6,
|
|
11665
11723
|
isVisionCapable: true,
|
|
11666
|
-
inputCostPer1kTokens:
|
|
11667
|
-
outputCostPer1kTokens:
|
|
11668
|
-
maxOutputTokens:
|
|
11724
|
+
inputCostPer1kTokens: 5e-3,
|
|
11725
|
+
outputCostPer1kTokens: 0.025,
|
|
11726
|
+
maxOutputTokens: 128e3,
|
|
11669
11727
|
supportsStreaming: true,
|
|
11670
11728
|
isLocal: false
|
|
11671
11729
|
},
|
|
11672
|
-
"claude-
|
|
11673
|
-
id: "claude-
|
|
11674
|
-
name: "Claude
|
|
11730
|
+
"claude-opus-4-7": {
|
|
11731
|
+
id: "claude-opus-4-7",
|
|
11732
|
+
name: "Claude Opus 4.7",
|
|
11675
11733
|
provider: "anthropic",
|
|
11676
|
-
contextWindow:
|
|
11734
|
+
contextWindow: 1e6,
|
|
11677
11735
|
isVisionCapable: true,
|
|
11678
|
-
inputCostPer1kTokens:
|
|
11679
|
-
outputCostPer1kTokens:
|
|
11680
|
-
maxOutputTokens:
|
|
11736
|
+
inputCostPer1kTokens: 5e-3,
|
|
11737
|
+
outputCostPer1kTokens: 0.025,
|
|
11738
|
+
maxOutputTokens: 128e3,
|
|
11681
11739
|
supportsStreaming: true,
|
|
11682
11740
|
isLocal: false
|
|
11683
11741
|
},
|
|
11684
|
-
"claude-opus-4-
|
|
11685
|
-
id: "claude-opus-4-
|
|
11686
|
-
name: "Claude Opus 4.
|
|
11742
|
+
"claude-opus-4-6": {
|
|
11743
|
+
id: "claude-opus-4-6",
|
|
11744
|
+
name: "Claude Opus 4.6",
|
|
11745
|
+
provider: "anthropic",
|
|
11746
|
+
contextWindow: 1e6,
|
|
11747
|
+
isVisionCapable: true,
|
|
11748
|
+
inputCostPer1kTokens: 5e-3,
|
|
11749
|
+
outputCostPer1kTokens: 0.025,
|
|
11750
|
+
maxOutputTokens: 128e3,
|
|
11751
|
+
supportsStreaming: true,
|
|
11752
|
+
isLocal: false
|
|
11753
|
+
},
|
|
11754
|
+
"claude-opus-4-5": {
|
|
11755
|
+
id: "claude-opus-4-5-20251101",
|
|
11756
|
+
name: "Claude Opus 4.5",
|
|
11687
11757
|
provider: "anthropic",
|
|
11688
11758
|
contextWindow: 2e5,
|
|
11689
11759
|
isVisionCapable: true,
|
|
11690
|
-
inputCostPer1kTokens:
|
|
11691
|
-
outputCostPer1kTokens: 0.
|
|
11692
|
-
maxOutputTokens:
|
|
11760
|
+
inputCostPer1kTokens: 5e-3,
|
|
11761
|
+
outputCostPer1kTokens: 0.025,
|
|
11762
|
+
maxOutputTokens: 64e3,
|
|
11763
|
+
supportsStreaming: true,
|
|
11764
|
+
isLocal: false
|
|
11765
|
+
},
|
|
11766
|
+
"claude-sonnet-5": {
|
|
11767
|
+
id: "claude-sonnet-5",
|
|
11768
|
+
name: "Claude Sonnet 5",
|
|
11769
|
+
provider: "anthropic",
|
|
11770
|
+
contextWindow: 1e6,
|
|
11771
|
+
isVisionCapable: true,
|
|
11772
|
+
inputCostPer1kTokens: 2e-3,
|
|
11773
|
+
outputCostPer1kTokens: 0.01,
|
|
11774
|
+
maxOutputTokens: 128e3,
|
|
11693
11775
|
supportsStreaming: true,
|
|
11694
11776
|
isLocal: false
|
|
11695
11777
|
},
|
|
@@ -11697,76 +11779,135 @@ var MODELS = {
|
|
|
11697
11779
|
id: "claude-sonnet-4-6",
|
|
11698
11780
|
name: "Claude Sonnet 4.6",
|
|
11699
11781
|
provider: "anthropic",
|
|
11782
|
+
contextWindow: 1e6,
|
|
11783
|
+
isVisionCapable: true,
|
|
11784
|
+
inputCostPer1kTokens: 3e-3,
|
|
11785
|
+
outputCostPer1kTokens: 0.015,
|
|
11786
|
+
maxOutputTokens: 64e3,
|
|
11787
|
+
supportsStreaming: true,
|
|
11788
|
+
isLocal: false
|
|
11789
|
+
},
|
|
11790
|
+
"claude-sonnet-4-5": {
|
|
11791
|
+
id: "claude-sonnet-4-5-20250929",
|
|
11792
|
+
name: "Claude Sonnet 4.5",
|
|
11793
|
+
provider: "anthropic",
|
|
11700
11794
|
contextWindow: 2e5,
|
|
11701
11795
|
isVisionCapable: true,
|
|
11702
11796
|
inputCostPer1kTokens: 3e-3,
|
|
11703
11797
|
outputCostPer1kTokens: 0.015,
|
|
11704
|
-
maxOutputTokens:
|
|
11798
|
+
maxOutputTokens: 64e3,
|
|
11705
11799
|
supportsStreaming: true,
|
|
11706
11800
|
isLocal: false
|
|
11707
11801
|
},
|
|
11708
|
-
|
|
11709
|
-
|
|
11710
|
-
|
|
11711
|
-
|
|
11802
|
+
"claude-haiku-4-5": {
|
|
11803
|
+
id: "claude-haiku-4-5-20251001",
|
|
11804
|
+
name: "Claude Haiku 4.5",
|
|
11805
|
+
provider: "anthropic",
|
|
11806
|
+
contextWindow: 2e5,
|
|
11807
|
+
isVisionCapable: true,
|
|
11808
|
+
inputCostPer1kTokens: 1e-3,
|
|
11809
|
+
outputCostPer1kTokens: 5e-3,
|
|
11810
|
+
maxOutputTokens: 64e3,
|
|
11811
|
+
supportsStreaming: true,
|
|
11812
|
+
isLocal: false
|
|
11813
|
+
},
|
|
11814
|
+
// OpenAI — active chat-capable models used by Cascade's Chat Completions path.
|
|
11815
|
+
"gpt-5.6-sol": {
|
|
11816
|
+
id: "gpt-5.6-sol",
|
|
11817
|
+
name: "GPT-5.6 Sol",
|
|
11712
11818
|
provider: "openai",
|
|
11713
|
-
contextWindow:
|
|
11819
|
+
contextWindow: 105e4,
|
|
11820
|
+
isVisionCapable: true,
|
|
11821
|
+
inputCostPer1kTokens: 4e-3,
|
|
11822
|
+
outputCostPer1kTokens: 0.02,
|
|
11823
|
+
maxOutputTokens: 128e3,
|
|
11824
|
+
supportsStreaming: true,
|
|
11825
|
+
isLocal: false
|
|
11826
|
+
},
|
|
11827
|
+
"gpt-5.6-terra": {
|
|
11828
|
+
id: "gpt-5.6-terra",
|
|
11829
|
+
name: "GPT-5.6 Terra",
|
|
11830
|
+
provider: "openai",
|
|
11831
|
+
contextWindow: 105e4,
|
|
11832
|
+
isVisionCapable: true,
|
|
11833
|
+
inputCostPer1kTokens: 2e-3,
|
|
11834
|
+
outputCostPer1kTokens: 0.012,
|
|
11835
|
+
maxOutputTokens: 128e3,
|
|
11836
|
+
supportsStreaming: true,
|
|
11837
|
+
isLocal: false
|
|
11838
|
+
},
|
|
11839
|
+
"gpt-5.6-luna": {
|
|
11840
|
+
id: "gpt-5.6-luna",
|
|
11841
|
+
name: "GPT-5.6 Luna",
|
|
11842
|
+
provider: "openai",
|
|
11843
|
+
contextWindow: 105e4,
|
|
11844
|
+
isVisionCapable: true,
|
|
11845
|
+
inputCostPer1kTokens: 2e-4,
|
|
11846
|
+
outputCostPer1kTokens: 12e-4,
|
|
11847
|
+
maxOutputTokens: 128e3,
|
|
11848
|
+
supportsStreaming: true,
|
|
11849
|
+
isLocal: false
|
|
11850
|
+
},
|
|
11851
|
+
"gpt-5.5": {
|
|
11852
|
+
id: "gpt-5.5",
|
|
11853
|
+
name: "GPT-5.5",
|
|
11854
|
+
provider: "openai",
|
|
11855
|
+
contextWindow: 105e4,
|
|
11714
11856
|
isVisionCapable: true,
|
|
11715
11857
|
inputCostPer1kTokens: 5e-3,
|
|
11716
|
-
outputCostPer1kTokens: 0.
|
|
11717
|
-
maxOutputTokens:
|
|
11858
|
+
outputCostPer1kTokens: 0.03,
|
|
11859
|
+
maxOutputTokens: 128e3,
|
|
11718
11860
|
supportsStreaming: true,
|
|
11719
11861
|
isLocal: false
|
|
11720
11862
|
},
|
|
11721
|
-
"gpt-
|
|
11722
|
-
id: "gpt-
|
|
11723
|
-
name: "GPT-
|
|
11863
|
+
"gpt-5.4": {
|
|
11864
|
+
id: "gpt-5.4",
|
|
11865
|
+
name: "GPT-5.4",
|
|
11724
11866
|
provider: "openai",
|
|
11725
|
-
contextWindow:
|
|
11867
|
+
contextWindow: 105e4,
|
|
11726
11868
|
isVisionCapable: true,
|
|
11727
|
-
inputCostPer1kTokens:
|
|
11728
|
-
outputCostPer1kTokens:
|
|
11729
|
-
maxOutputTokens:
|
|
11869
|
+
inputCostPer1kTokens: 25e-4,
|
|
11870
|
+
outputCostPer1kTokens: 0.015,
|
|
11871
|
+
maxOutputTokens: 128e3,
|
|
11730
11872
|
supportsStreaming: true,
|
|
11731
11873
|
isLocal: false
|
|
11732
11874
|
},
|
|
11733
|
-
"gpt-4
|
|
11734
|
-
id: "gpt-4
|
|
11735
|
-
name: "GPT-4
|
|
11875
|
+
"gpt-5.4-mini": {
|
|
11876
|
+
id: "gpt-5.4-mini",
|
|
11877
|
+
name: "GPT-5.4 Mini",
|
|
11736
11878
|
provider: "openai",
|
|
11737
|
-
contextWindow:
|
|
11879
|
+
contextWindow: 4e5,
|
|
11738
11880
|
isVisionCapable: true,
|
|
11739
|
-
inputCostPer1kTokens:
|
|
11740
|
-
outputCostPer1kTokens:
|
|
11741
|
-
maxOutputTokens:
|
|
11881
|
+
inputCostPer1kTokens: 75e-5,
|
|
11882
|
+
outputCostPer1kTokens: 45e-4,
|
|
11883
|
+
maxOutputTokens: 128e3,
|
|
11742
11884
|
supportsStreaming: true,
|
|
11743
11885
|
isLocal: false
|
|
11744
11886
|
},
|
|
11745
|
-
"gpt-4
|
|
11746
|
-
id: "gpt-4
|
|
11747
|
-
name: "GPT-4
|
|
11887
|
+
"gpt-5.4-nano": {
|
|
11888
|
+
id: "gpt-5.4-nano",
|
|
11889
|
+
name: "GPT-5.4 Nano",
|
|
11748
11890
|
provider: "openai",
|
|
11749
|
-
contextWindow:
|
|
11891
|
+
contextWindow: 4e5,
|
|
11750
11892
|
isVisionCapable: true,
|
|
11751
|
-
inputCostPer1kTokens:
|
|
11752
|
-
outputCostPer1kTokens:
|
|
11753
|
-
maxOutputTokens:
|
|
11893
|
+
inputCostPer1kTokens: 2e-4,
|
|
11894
|
+
outputCostPer1kTokens: 125e-5,
|
|
11895
|
+
maxOutputTokens: 128e3,
|
|
11754
11896
|
supportsStreaming: true,
|
|
11755
11897
|
isLocal: false
|
|
11756
11898
|
},
|
|
11757
|
-
"gpt-
|
|
11758
|
-
id: "gpt-
|
|
11759
|
-
name: "GPT-
|
|
11899
|
+
"gpt-5.2": {
|
|
11900
|
+
id: "gpt-5.2",
|
|
11901
|
+
name: "GPT-5.2",
|
|
11760
11902
|
provider: "openai",
|
|
11761
|
-
contextWindow:
|
|
11903
|
+
contextWindow: 4e5,
|
|
11762
11904
|
isVisionCapable: true,
|
|
11763
|
-
inputCostPer1kTokens:
|
|
11764
|
-
outputCostPer1kTokens:
|
|
11765
|
-
maxOutputTokens:
|
|
11905
|
+
inputCostPer1kTokens: 175e-5,
|
|
11906
|
+
outputCostPer1kTokens: 0.014,
|
|
11907
|
+
maxOutputTokens: 128e3,
|
|
11766
11908
|
supportsStreaming: true,
|
|
11767
11909
|
isLocal: false
|
|
11768
11910
|
},
|
|
11769
|
-
// GPT-5 family (offline baseline pricing; the live fetch keeps it current).
|
|
11770
11911
|
"gpt-5": {
|
|
11771
11912
|
id: "gpt-5",
|
|
11772
11913
|
name: "GPT-5",
|
|
@@ -11803,79 +11944,149 @@ var MODELS = {
|
|
|
11803
11944
|
supportsStreaming: true,
|
|
11804
11945
|
isLocal: false
|
|
11805
11946
|
},
|
|
11806
|
-
|
|
11807
|
-
|
|
11808
|
-
|
|
11809
|
-
"gpt-5.5": {
|
|
11810
|
-
id: "gpt-5.5",
|
|
11811
|
-
name: "GPT-5.5",
|
|
11947
|
+
"gpt-4.1": {
|
|
11948
|
+
id: "gpt-4.1",
|
|
11949
|
+
name: "GPT-4.1",
|
|
11812
11950
|
provider: "openai",
|
|
11813
|
-
contextWindow:
|
|
11951
|
+
contextWindow: 1047576,
|
|
11814
11952
|
isVisionCapable: true,
|
|
11815
|
-
inputCostPer1kTokens:
|
|
11816
|
-
outputCostPer1kTokens:
|
|
11817
|
-
maxOutputTokens:
|
|
11953
|
+
inputCostPer1kTokens: 2e-3,
|
|
11954
|
+
outputCostPer1kTokens: 8e-3,
|
|
11955
|
+
maxOutputTokens: 32768,
|
|
11818
11956
|
supportsStreaming: true,
|
|
11819
11957
|
isLocal: false
|
|
11820
11958
|
},
|
|
11821
|
-
"gpt-
|
|
11822
|
-
id: "gpt-
|
|
11823
|
-
name: "GPT-
|
|
11959
|
+
"gpt-4.1-mini": {
|
|
11960
|
+
id: "gpt-4.1-mini",
|
|
11961
|
+
name: "GPT-4.1 Mini",
|
|
11824
11962
|
provider: "openai",
|
|
11825
|
-
contextWindow:
|
|
11963
|
+
contextWindow: 1047576,
|
|
11826
11964
|
isVisionCapable: true,
|
|
11827
|
-
inputCostPer1kTokens:
|
|
11965
|
+
inputCostPer1kTokens: 4e-4,
|
|
11966
|
+
outputCostPer1kTokens: 16e-4,
|
|
11967
|
+
maxOutputTokens: 32768,
|
|
11968
|
+
supportsStreaming: true,
|
|
11969
|
+
isLocal: false
|
|
11970
|
+
},
|
|
11971
|
+
"gpt-4.1-nano": {
|
|
11972
|
+
id: "gpt-4.1-nano",
|
|
11973
|
+
name: "GPT-4.1 Nano",
|
|
11974
|
+
provider: "openai",
|
|
11975
|
+
contextWindow: 1047576,
|
|
11976
|
+
isVisionCapable: true,
|
|
11977
|
+
inputCostPer1kTokens: 1e-4,
|
|
11978
|
+
outputCostPer1kTokens: 4e-4,
|
|
11979
|
+
maxOutputTokens: 32768,
|
|
11980
|
+
supportsStreaming: true,
|
|
11981
|
+
isLocal: false
|
|
11982
|
+
},
|
|
11983
|
+
"gpt-4o": {
|
|
11984
|
+
id: "gpt-4o",
|
|
11985
|
+
name: "GPT-4o",
|
|
11986
|
+
provider: "openai",
|
|
11987
|
+
contextWindow: 128e3,
|
|
11988
|
+
isVisionCapable: true,
|
|
11989
|
+
inputCostPer1kTokens: 25e-4,
|
|
11828
11990
|
outputCostPer1kTokens: 0.01,
|
|
11829
|
-
maxOutputTokens:
|
|
11991
|
+
maxOutputTokens: 16e3,
|
|
11830
11992
|
supportsStreaming: true,
|
|
11831
11993
|
isLocal: false
|
|
11832
11994
|
},
|
|
11833
|
-
"gpt-
|
|
11834
|
-
id: "gpt-
|
|
11835
|
-
name: "GPT-
|
|
11995
|
+
"gpt-4o-mini": {
|
|
11996
|
+
id: "gpt-4o-mini",
|
|
11997
|
+
name: "GPT-4o Mini",
|
|
11836
11998
|
provider: "openai",
|
|
11837
|
-
contextWindow:
|
|
11999
|
+
contextWindow: 128e3,
|
|
11838
12000
|
isVisionCapable: true,
|
|
11839
|
-
inputCostPer1kTokens:
|
|
11840
|
-
outputCostPer1kTokens:
|
|
11841
|
-
maxOutputTokens:
|
|
12001
|
+
inputCostPer1kTokens: 15e-5,
|
|
12002
|
+
outputCostPer1kTokens: 6e-4,
|
|
12003
|
+
maxOutputTokens: 16e3,
|
|
11842
12004
|
supportsStreaming: true,
|
|
11843
12005
|
isLocal: false
|
|
11844
12006
|
},
|
|
11845
|
-
// Google
|
|
11846
|
-
|
|
11847
|
-
|
|
11848
|
-
|
|
12007
|
+
// Google Gemini — active GA + preview chat models. Gemini 2.0/1.5 are no
|
|
12008
|
+
// longer advertised because the Gemini API shut those generations down.
|
|
12009
|
+
"gemini-3.7-flash": {
|
|
12010
|
+
id: "gemini-3.7-flash",
|
|
12011
|
+
name: "Gemini 3.7 Flash",
|
|
11849
12012
|
provider: "gemini",
|
|
11850
|
-
contextWindow:
|
|
12013
|
+
contextWindow: 1048576,
|
|
11851
12014
|
isVisionCapable: true,
|
|
11852
|
-
inputCostPer1kTokens:
|
|
11853
|
-
outputCostPer1kTokens:
|
|
11854
|
-
maxOutputTokens:
|
|
12015
|
+
inputCostPer1kTokens: 75e-5,
|
|
12016
|
+
outputCostPer1kTokens: 375e-5,
|
|
12017
|
+
maxOutputTokens: 65536,
|
|
11855
12018
|
supportsStreaming: true,
|
|
11856
12019
|
isLocal: false
|
|
11857
12020
|
},
|
|
11858
|
-
"gemini-
|
|
11859
|
-
id: "gemini-
|
|
11860
|
-
name: "Gemini
|
|
12021
|
+
"gemini-3.6-flash": {
|
|
12022
|
+
id: "gemini-3.6-flash",
|
|
12023
|
+
name: "Gemini 3.6 Flash",
|
|
11861
12024
|
provider: "gemini",
|
|
11862
|
-
contextWindow:
|
|
12025
|
+
contextWindow: 1048576,
|
|
11863
12026
|
isVisionCapable: true,
|
|
11864
|
-
inputCostPer1kTokens:
|
|
11865
|
-
outputCostPer1kTokens:
|
|
11866
|
-
maxOutputTokens:
|
|
12027
|
+
inputCostPer1kTokens: 75e-5,
|
|
12028
|
+
outputCostPer1kTokens: 375e-5,
|
|
12029
|
+
maxOutputTokens: 65536,
|
|
11867
12030
|
supportsStreaming: true,
|
|
11868
12031
|
isLocal: false
|
|
11869
12032
|
},
|
|
11870
|
-
"gemini-
|
|
11871
|
-
id: "gemini-
|
|
11872
|
-
name: "Gemini
|
|
12033
|
+
"gemini-3.5-flash": {
|
|
12034
|
+
id: "gemini-3.5-flash",
|
|
12035
|
+
name: "Gemini 3.5 Flash",
|
|
11873
12036
|
provider: "gemini",
|
|
11874
|
-
contextWindow:
|
|
12037
|
+
contextWindow: 1048576,
|
|
11875
12038
|
isVisionCapable: true,
|
|
11876
|
-
inputCostPer1kTokens:
|
|
11877
|
-
outputCostPer1kTokens:
|
|
11878
|
-
maxOutputTokens:
|
|
12039
|
+
inputCostPer1kTokens: 15e-4,
|
|
12040
|
+
outputCostPer1kTokens: 9e-3,
|
|
12041
|
+
maxOutputTokens: 65536,
|
|
12042
|
+
supportsStreaming: true,
|
|
12043
|
+
isLocal: false
|
|
12044
|
+
},
|
|
12045
|
+
"gemini-3.5-flash-lite": {
|
|
12046
|
+
id: "gemini-3.5-flash-lite",
|
|
12047
|
+
name: "Gemini 3.5 Flash-Lite",
|
|
12048
|
+
provider: "gemini",
|
|
12049
|
+
contextWindow: 1048576,
|
|
12050
|
+
isVisionCapable: true,
|
|
12051
|
+
inputCostPer1kTokens: 3e-4,
|
|
12052
|
+
outputCostPer1kTokens: 25e-4,
|
|
12053
|
+
maxOutputTokens: 65536,
|
|
12054
|
+
supportsStreaming: true,
|
|
12055
|
+
isLocal: false
|
|
12056
|
+
},
|
|
12057
|
+
"gemini-3.1-flash-lite": {
|
|
12058
|
+
id: "gemini-3.1-flash-lite",
|
|
12059
|
+
name: "Gemini 3.1 Flash-Lite",
|
|
12060
|
+
provider: "gemini",
|
|
12061
|
+
contextWindow: 1048576,
|
|
12062
|
+
isVisionCapable: true,
|
|
12063
|
+
inputCostPer1kTokens: 25e-5,
|
|
12064
|
+
outputCostPer1kTokens: 15e-4,
|
|
12065
|
+
maxOutputTokens: 65536,
|
|
12066
|
+
supportsStreaming: true,
|
|
12067
|
+
isLocal: false
|
|
12068
|
+
},
|
|
12069
|
+
"gemini-3.1-pro-preview": {
|
|
12070
|
+
id: "gemini-3.1-pro-preview",
|
|
12071
|
+
name: "Gemini 3.1 Pro Preview",
|
|
12072
|
+
provider: "gemini",
|
|
12073
|
+
contextWindow: 1048576,
|
|
12074
|
+
isVisionCapable: true,
|
|
12075
|
+
inputCostPer1kTokens: 2e-3,
|
|
12076
|
+
outputCostPer1kTokens: 0.012,
|
|
12077
|
+
maxOutputTokens: 65536,
|
|
12078
|
+
supportsStreaming: true,
|
|
12079
|
+
isLocal: false
|
|
12080
|
+
},
|
|
12081
|
+
"gemini-3-flash-preview": {
|
|
12082
|
+
id: "gemini-3-flash-preview",
|
|
12083
|
+
name: "Gemini 3 Flash Preview",
|
|
12084
|
+
provider: "gemini",
|
|
12085
|
+
contextWindow: 1048576,
|
|
12086
|
+
isVisionCapable: true,
|
|
12087
|
+
inputCostPer1kTokens: 5e-4,
|
|
12088
|
+
outputCostPer1kTokens: 3e-3,
|
|
12089
|
+
maxOutputTokens: 65536,
|
|
11879
12090
|
supportsStreaming: true,
|
|
11880
12091
|
isLocal: false
|
|
11881
12092
|
},
|
|
@@ -11883,7 +12094,7 @@ var MODELS = {
|
|
|
11883
12094
|
id: "gemini-2.5-pro",
|
|
11884
12095
|
name: "Gemini 2.5 Pro",
|
|
11885
12096
|
provider: "gemini",
|
|
11886
|
-
contextWindow:
|
|
12097
|
+
contextWindow: 1048576,
|
|
11887
12098
|
isVisionCapable: true,
|
|
11888
12099
|
inputCostPer1kTokens: 125e-5,
|
|
11889
12100
|
outputCostPer1kTokens: 0.01,
|
|
@@ -11895,10 +12106,22 @@ var MODELS = {
|
|
|
11895
12106
|
id: "gemini-2.5-flash",
|
|
11896
12107
|
name: "Gemini 2.5 Flash",
|
|
11897
12108
|
provider: "gemini",
|
|
11898
|
-
contextWindow:
|
|
12109
|
+
contextWindow: 1048576,
|
|
11899
12110
|
isVisionCapable: true,
|
|
11900
|
-
inputCostPer1kTokens:
|
|
11901
|
-
outputCostPer1kTokens:
|
|
12111
|
+
inputCostPer1kTokens: 3e-4,
|
|
12112
|
+
outputCostPer1kTokens: 25e-4,
|
|
12113
|
+
maxOutputTokens: 65536,
|
|
12114
|
+
supportsStreaming: true,
|
|
12115
|
+
isLocal: false
|
|
12116
|
+
},
|
|
12117
|
+
"gemini-2.5-flash-lite": {
|
|
12118
|
+
id: "gemini-2.5-flash-lite",
|
|
12119
|
+
name: "Gemini 2.5 Flash-Lite",
|
|
12120
|
+
provider: "gemini",
|
|
12121
|
+
contextWindow: 1048576,
|
|
12122
|
+
isVisionCapable: true,
|
|
12123
|
+
inputCostPer1kTokens: 1e-4,
|
|
12124
|
+
outputCostPer1kTokens: 4e-4,
|
|
11902
12125
|
maxOutputTokens: 65536,
|
|
11903
12126
|
supportsStreaming: true,
|
|
11904
12127
|
isLocal: false
|
|
@@ -11968,43 +12191,57 @@ for (const _m of Object.values(MODELS)) {
|
|
|
11968
12191
|
_m.pricingUnknown = false;
|
|
11969
12192
|
}
|
|
11970
12193
|
var T1_MODEL_PRIORITY = [
|
|
12194
|
+
"gpt-5.6-sol",
|
|
12195
|
+
"claude-fable-5",
|
|
12196
|
+
"claude-opus-5",
|
|
11971
12197
|
"gpt-5.5",
|
|
11972
12198
|
"gpt-5.4",
|
|
12199
|
+
"gemini-3.1-pro-preview",
|
|
12200
|
+
"claude-opus-4-8",
|
|
12201
|
+
"claude-sonnet-5",
|
|
12202
|
+
"gemini-3.7-flash",
|
|
12203
|
+
"gpt-5.2",
|
|
11973
12204
|
"gpt-5",
|
|
11974
|
-
"claude-opus-4",
|
|
11975
|
-
"claude-sonnet-4",
|
|
11976
12205
|
"gemini-2.5-pro",
|
|
11977
12206
|
"gpt-4.1",
|
|
11978
|
-
"gpt-4o"
|
|
11979
|
-
"gemini-1.5-pro"
|
|
12207
|
+
"gpt-4o"
|
|
11980
12208
|
];
|
|
11981
12209
|
var T2_MODEL_PRIORITY = [
|
|
12210
|
+
"gpt-5.6-terra",
|
|
12211
|
+
"claude-sonnet-5",
|
|
12212
|
+
"gemini-3.7-flash",
|
|
12213
|
+
"gemini-3.6-flash",
|
|
11982
12214
|
"gpt-5.4-mini",
|
|
11983
12215
|
"gpt-5-mini",
|
|
11984
|
-
"claude-sonnet-4",
|
|
12216
|
+
"claude-sonnet-4-6",
|
|
11985
12217
|
"claude-haiku-4-5",
|
|
11986
|
-
"
|
|
12218
|
+
"gemini-3.5-flash",
|
|
11987
12219
|
"gemini-2.5-flash",
|
|
11988
12220
|
"gpt-4.1-mini",
|
|
11989
12221
|
"gpt-4o-mini",
|
|
11990
|
-
"gemini-2.0-flash",
|
|
11991
12222
|
"llama3:70b"
|
|
11992
12223
|
];
|
|
11993
12224
|
var T3_MODEL_PRIORITY = [
|
|
12225
|
+
"gpt-5.6-luna",
|
|
12226
|
+
"gpt-5.4-nano",
|
|
12227
|
+
"gemini-3.5-flash-lite",
|
|
12228
|
+
"gemini-3.1-flash-lite",
|
|
12229
|
+
"claude-haiku-4-5",
|
|
11994
12230
|
"gpt-5-nano",
|
|
12231
|
+
"gemini-2.5-flash-lite",
|
|
11995
12232
|
"llama3.2:3b",
|
|
11996
12233
|
"mistral:7b",
|
|
11997
|
-
"claude-haiku-4-5",
|
|
11998
|
-
"claude-haiku-3-5",
|
|
11999
12234
|
"gpt-4.1-nano",
|
|
12000
12235
|
"gpt-4o-mini",
|
|
12001
|
-
"gemini-2.5-flash"
|
|
12002
|
-
"gemini-2.0-flash"
|
|
12236
|
+
"gemini-2.5-flash"
|
|
12003
12237
|
];
|
|
12004
12238
|
var VISION_MODEL_PRIORITY = [
|
|
12005
|
-
"
|
|
12239
|
+
"gpt-5.6-sol",
|
|
12240
|
+
"claude-fable-5",
|
|
12241
|
+
"claude-sonnet-5",
|
|
12242
|
+
"gemini-3.7-flash",
|
|
12006
12243
|
"gpt-4o",
|
|
12007
|
-
"gemini-2.
|
|
12244
|
+
"gemini-2.5-flash",
|
|
12008
12245
|
"llava"
|
|
12009
12246
|
];
|
|
12010
12247
|
var COMPLEXITY_T2_COUNT = {
|
|
@@ -12185,7 +12422,7 @@ function isChatModel(id, methods) {
|
|
|
12185
12422
|
return !NON_CHAT.test(id);
|
|
12186
12423
|
}
|
|
12187
12424
|
try {
|
|
12188
|
-
|
|
12425
|
+
dnsCallback__default.default.setDefaultResultOrder("ipv4first");
|
|
12189
12426
|
} catch {
|
|
12190
12427
|
}
|
|
12191
12428
|
function preferIpv4Host(url) {
|
|
@@ -12201,8 +12438,8 @@ function normalizeEndpoint(url) {
|
|
|
12201
12438
|
if (!raw) return "";
|
|
12202
12439
|
try {
|
|
12203
12440
|
const u = new URL(raw);
|
|
12204
|
-
const
|
|
12205
|
-
return `${u.protocol}//${u.host.toLowerCase()}${
|
|
12441
|
+
const path30 = stripTrailingSlashes(`${u.pathname}${u.search}`);
|
|
12442
|
+
return `${u.protocol}//${u.host.toLowerCase()}${path30}`;
|
|
12206
12443
|
} catch {
|
|
12207
12444
|
return raw.toLowerCase();
|
|
12208
12445
|
}
|
|
@@ -12869,52 +13106,127 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
12869
13106
|
}
|
|
12870
13107
|
};
|
|
12871
13108
|
|
|
13109
|
+
// src/providers/cloud-model-catalog.json
|
|
13110
|
+
var cloud_model_catalog_default = {
|
|
13111
|
+
models: [
|
|
13112
|
+
{ id: "gpt-5.6-sol", name: "GPT-5.6 Sol", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13113
|
+
{ id: "gpt-5.6-terra", name: "GPT-5.6 Terra", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13114
|
+
{ id: "gpt-5.6-luna", name: "GPT-5.6 Luna", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13115
|
+
{ id: "gpt-5.5", name: "GPT-5.5", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13116
|
+
{ id: "gpt-5.4", name: "GPT-5.4", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13117
|
+
{ id: "gpt-5.4-mini", name: "GPT-5.4 Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13118
|
+
{ id: "gpt-5.4-nano", name: "GPT-5.4 Nano", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13119
|
+
{ id: "gpt-5", name: "GPT-5", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13120
|
+
{ id: "gpt-5-mini", name: "GPT-5 Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13121
|
+
{ id: "gpt-5-nano", name: "GPT-5 Nano", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13122
|
+
{ id: "gpt-4.1", name: "GPT-4.1", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 1047576, maxOutputTokens: 32768, vision: true, toolUse: true },
|
|
13123
|
+
{ id: "gpt-4.1-mini", name: "GPT-4.1 Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 1047576, maxOutputTokens: 32768, vision: true, toolUse: true },
|
|
13124
|
+
{ id: "gpt-4.1-nano", name: "GPT-4.1 Nano", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 1047576, maxOutputTokens: 32768, vision: true, toolUse: true },
|
|
13125
|
+
{ id: "gpt-4o", name: "GPT-4o", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 128e3, maxOutputTokens: 16384, vision: true, toolUse: true },
|
|
13126
|
+
{ id: "gpt-4o-mini", name: "GPT-4o Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 128e3, maxOutputTokens: 16384, vision: true, toolUse: true },
|
|
13127
|
+
{ id: "claude-fable-5", name: "Claude Fable 5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13128
|
+
{ id: "claude-opus-5", name: "Claude Opus 5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13129
|
+
{ id: "claude-opus-4-8", name: "Claude Opus 4.8", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13130
|
+
{ id: "claude-opus-4-7", name: "Claude Opus 4.7", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13131
|
+
{ id: "claude-opus-4-6", name: "Claude Opus 4.6", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, vision: true, toolUse: true },
|
|
13132
|
+
{ id: "claude-opus-4-5-20251101", name: "Claude Opus 4.5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 2e5, vision: true, toolUse: true },
|
|
13133
|
+
{ id: "claude-sonnet-5", name: "Claude Sonnet 5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13134
|
+
{ id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13135
|
+
{ id: "claude-sonnet-4-5-20250929", name: "Claude Sonnet 4.5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 2e5, vision: true, toolUse: true },
|
|
13136
|
+
{ id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 2e5, maxOutputTokens: 64e3, vision: true, toolUse: true },
|
|
13137
|
+
{ id: "gemini-3.7-flash", name: "Gemini 3.7 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13138
|
+
{ id: "gemini-3.6-flash", name: "Gemini 3.6 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13139
|
+
{ id: "gemini-3.5-flash", name: "Gemini 3.5 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13140
|
+
{ id: "gemini-3.5-flash-lite", name: "Gemini 3.5 Flash-Lite", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13141
|
+
{ id: "gemini-3.1-flash-lite", name: "Gemini 3.1 Flash-Lite", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13142
|
+
{ id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro", providers: ["gemini"], lifecycle: "preview", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13143
|
+
{ id: "gemini-3-flash-preview", name: "Gemini 3 Flash", providers: ["gemini"], lifecycle: "preview", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13144
|
+
{ id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13145
|
+
{ id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13146
|
+
{ id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash-Lite", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true }
|
|
13147
|
+
]
|
|
13148
|
+
};
|
|
13149
|
+
|
|
12872
13150
|
// src/providers/azure.ts
|
|
12873
13151
|
var DEFAULT_AZURE_API_VERSION = "2024-12-01-preview";
|
|
12874
|
-
var
|
|
12875
|
-
|
|
12876
|
-
|
|
12877
|
-
|
|
12878
|
-
[/
|
|
12879
|
-
|
|
12880
|
-
|
|
12881
|
-
|
|
12882
|
-
|
|
12883
|
-
|
|
12884
|
-
[/gpt-?4o-mini/, "gpt-4o-mini"],
|
|
12885
|
-
[/gpt-?4o/, "gpt-4o"]
|
|
12886
|
-
];
|
|
12887
|
-
var AZURE_BASE_MODELS = AZURE_BASE_MODEL_RULES.map(([, id]) => id);
|
|
13152
|
+
var CLOUD_MODELS = cloud_model_catalog_default.models;
|
|
13153
|
+
var 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));
|
|
13154
|
+
var AZURE_BASE_MODELS = AZURE_CATALOG.map((m) => m.id);
|
|
13155
|
+
function compactModelId(id) {
|
|
13156
|
+
return id.toLowerCase().replace(/[^a-z0-9.]/g, "");
|
|
13157
|
+
}
|
|
13158
|
+
function catalogEntry(id) {
|
|
13159
|
+
const n = id.toLowerCase();
|
|
13160
|
+
return CLOUD_MODELS.find((m) => m.id.toLowerCase() === n);
|
|
13161
|
+
}
|
|
12888
13162
|
function inferAzureBaseModel(deploymentName) {
|
|
12889
13163
|
const n = deploymentName.toLowerCase();
|
|
12890
|
-
|
|
13164
|
+
const compact = compactModelId(n);
|
|
13165
|
+
const hasBareGpt56Alias = compact.includes("gpt5.6");
|
|
13166
|
+
for (const model of AZURE_CATALOG) {
|
|
13167
|
+
if (hasBareGpt56Alias && model.id === "gpt-5") continue;
|
|
13168
|
+
if (n.includes(model.id.toLowerCase()) || compact.includes(compactModelId(model.id))) {
|
|
13169
|
+
return model.id;
|
|
13170
|
+
}
|
|
13171
|
+
}
|
|
13172
|
+
if (hasBareGpt56Alias) return "gpt-5.6-sol";
|
|
13173
|
+
const gpt5At = compact.indexOf("gpt5");
|
|
13174
|
+
if (gpt5At !== -1) {
|
|
13175
|
+
const suffix = compact.slice(gpt5At + "gpt5".length);
|
|
13176
|
+
if (suffix.includes("nano")) return "gpt-5-nano";
|
|
13177
|
+
if (suffix.includes("mini")) return "gpt-5-mini";
|
|
13178
|
+
return "gpt-5";
|
|
13179
|
+
}
|
|
13180
|
+
if (compact.includes("gpt4.1nano")) return "gpt-4.1-nano";
|
|
13181
|
+
if (compact.includes("gpt4.1mini")) return "gpt-4.1-mini";
|
|
13182
|
+
if (compact.includes("gpt4.1")) return "gpt-4.1";
|
|
13183
|
+
if (compact.includes("gpt4omini")) return "gpt-4o-mini";
|
|
13184
|
+
if (compact.includes("gpt4o")) return "gpt-4o";
|
|
12891
13185
|
return null;
|
|
12892
13186
|
}
|
|
13187
|
+
function catalogModelInfo(baseModelId) {
|
|
13188
|
+
const entry = catalogEntry(baseModelId);
|
|
13189
|
+
if (!entry?.contextWindow) return void 0;
|
|
13190
|
+
return {
|
|
13191
|
+
id: entry.id,
|
|
13192
|
+
name: entry.name,
|
|
13193
|
+
provider: "openai",
|
|
13194
|
+
contextWindow: entry.contextWindow,
|
|
13195
|
+
isVisionCapable: entry.vision ?? false,
|
|
13196
|
+
inputCostPer1kTokens: 0,
|
|
13197
|
+
outputCostPer1kTokens: 0,
|
|
13198
|
+
maxOutputTokens: entry.maxOutputTokens ?? 16e3,
|
|
13199
|
+
supportsStreaming: true,
|
|
13200
|
+
isLocal: false,
|
|
13201
|
+
supportsToolUse: entry.toolUse ?? true,
|
|
13202
|
+
pricingUnknown: true
|
|
13203
|
+
};
|
|
13204
|
+
}
|
|
12893
13205
|
function azureModelForDeployment(cfg) {
|
|
12894
13206
|
if (cfg.type !== "azure" || !cfg.deploymentName?.trim()) return null;
|
|
12895
13207
|
const id = cfg.deploymentName.trim();
|
|
12896
13208
|
const name = cfg.label?.trim() || id;
|
|
12897
13209
|
const baseModelId = cfg.model?.trim() || inferAzureBaseModel(id) || void 0;
|
|
12898
|
-
const base = baseModelId ? MODELS[baseModelId] : void 0;
|
|
12899
|
-
if (base) {
|
|
13210
|
+
const base = baseModelId ? MODELS[baseModelId] ?? catalogModelInfo(baseModelId) : void 0;
|
|
13211
|
+
if (base && baseModelId) {
|
|
12900
13212
|
const azurePrice = resolvePricing(
|
|
12901
13213
|
{ id, provider: "azure", isLocal: false, baseModelId },
|
|
12902
13214
|
{ region: cfg.region }
|
|
12903
13215
|
);
|
|
13216
|
+
const exactAzurePrice = !azurePrice.unknown && !azurePrice.estimatedFromProvider;
|
|
13217
|
+
const nativeToolsOnThisTransport = /^gpt-5\.6-(?:sol|terra|luna)$/i.test(baseModelId) ? false : base.supportsToolUse ?? true;
|
|
12904
13218
|
return {
|
|
12905
13219
|
...base,
|
|
12906
13220
|
id,
|
|
12907
|
-
// callable deployment name
|
|
12908
13221
|
name,
|
|
12909
13222
|
provider: "azure",
|
|
12910
13223
|
baseModelId,
|
|
12911
|
-
|
|
12912
|
-
|
|
12913
|
-
...azurePrice.unknown ? {} : {
|
|
13224
|
+
supportsToolUse: nativeToolsOnThisTransport,
|
|
13225
|
+
...!azurePrice.unknown ? {
|
|
12914
13226
|
inputCostPer1kTokens: azurePrice.input,
|
|
12915
13227
|
outputCostPer1kTokens: azurePrice.output,
|
|
12916
|
-
pricingUnknown:
|
|
12917
|
-
}
|
|
13228
|
+
pricingUnknown: !exactAzurePrice
|
|
13229
|
+
} : { pricingUnknown: true }
|
|
12918
13230
|
};
|
|
12919
13231
|
}
|
|
12920
13232
|
return {
|
|
@@ -12929,18 +13241,23 @@ function azureModelForDeployment(cfg) {
|
|
|
12929
13241
|
maxOutputTokens: 16e3,
|
|
12930
13242
|
supportsStreaming: true,
|
|
12931
13243
|
isLocal: false,
|
|
12932
|
-
supportsToolUse: true
|
|
13244
|
+
supportsToolUse: true,
|
|
13245
|
+
pricingUnknown: true
|
|
12933
13246
|
};
|
|
12934
13247
|
}
|
|
13248
|
+
function stripTrailingSlashes3(value) {
|
|
13249
|
+
let end = value.length;
|
|
13250
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) end--;
|
|
13251
|
+
return value.slice(0, end);
|
|
13252
|
+
}
|
|
12935
13253
|
var AzureOpenAIProvider = class extends OpenAIProvider {
|
|
12936
13254
|
constructor(config, model) {
|
|
12937
13255
|
const rawUrl = config.baseUrl ?? AZURE_BASE_URL_TEMPLATE.replace("{resource}", "YOUR_RESOURCE");
|
|
12938
|
-
const endpoint = rawUrl
|
|
13256
|
+
const endpoint = stripTrailingSlashes3(rawUrl);
|
|
12939
13257
|
super(
|
|
12940
13258
|
{
|
|
12941
13259
|
...config,
|
|
12942
13260
|
baseUrl: endpoint
|
|
12943
|
-
// Kept for superclass compatibility if it reads it
|
|
12944
13261
|
},
|
|
12945
13262
|
model
|
|
12946
13263
|
);
|
|
@@ -14632,13 +14949,13 @@ var DeadModelStore = class {
|
|
|
14632
14949
|
function fileDeadModelPersistence(filePath) {
|
|
14633
14950
|
return {
|
|
14634
14951
|
load() {
|
|
14635
|
-
if (!
|
|
14636
|
-
const raw = JSON.parse(
|
|
14952
|
+
if (!fs27.existsSync(filePath)) return [];
|
|
14953
|
+
const raw = JSON.parse(fs27.readFileSync(filePath, "utf-8"));
|
|
14637
14954
|
return Array.isArray(raw) ? raw : [];
|
|
14638
14955
|
},
|
|
14639
14956
|
save(records) {
|
|
14640
|
-
|
|
14641
|
-
|
|
14957
|
+
fs27.mkdirSync(path28.dirname(filePath), { recursive: true });
|
|
14958
|
+
fs27.writeFileSync(filePath, JSON.stringify(records, null, 2));
|
|
14642
14959
|
}
|
|
14643
14960
|
};
|
|
14644
14961
|
}
|
|
@@ -14683,7 +15000,7 @@ var Keystore = class {
|
|
|
14683
15000
|
const creds = await this.keytar.findCredentials(KEYTAR_SERVICE);
|
|
14684
15001
|
this.cache = Object.fromEntries(creds.map((c) => [c.account, c.password]));
|
|
14685
15002
|
this.backend = "keytar";
|
|
14686
|
-
if (password &&
|
|
15003
|
+
if (password && fs27__default.default.existsSync(this.storePath)) {
|
|
14687
15004
|
try {
|
|
14688
15005
|
const fileEntries = this.decryptFile(password);
|
|
14689
15006
|
for (const [k, v] of Object.entries(fileEntries)) {
|
|
@@ -14702,7 +15019,7 @@ var Keystore = class {
|
|
|
14702
15019
|
"Keystore unlock requires a password because the OS keychain (keytar) is not available on this system."
|
|
14703
15020
|
);
|
|
14704
15021
|
}
|
|
14705
|
-
if (!
|
|
15022
|
+
if (!fs27__default.default.existsSync(this.storePath)) {
|
|
14706
15023
|
const salt = crypto__default.default.randomBytes(SALT_LEN);
|
|
14707
15024
|
this.masterKey = this.deriveKey(password, salt);
|
|
14708
15025
|
this.writeWithSalt({}, salt);
|
|
@@ -14716,7 +15033,7 @@ var Keystore = class {
|
|
|
14716
15033
|
}
|
|
14717
15034
|
/** Synchronous legacy unlock kept for AES-only environments. */
|
|
14718
15035
|
unlockSync(password) {
|
|
14719
|
-
if (!
|
|
15036
|
+
if (!fs27__default.default.existsSync(this.storePath)) {
|
|
14720
15037
|
const salt = crypto__default.default.randomBytes(SALT_LEN);
|
|
14721
15038
|
this.masterKey = this.deriveKey(password, salt);
|
|
14722
15039
|
this.writeWithSalt({}, salt);
|
|
@@ -14774,7 +15091,7 @@ var Keystore = class {
|
|
|
14774
15091
|
}
|
|
14775
15092
|
}
|
|
14776
15093
|
decryptFile(password, knownSalt) {
|
|
14777
|
-
if (!
|
|
15094
|
+
if (!fs27__default.default.existsSync(this.storePath)) return {};
|
|
14778
15095
|
try {
|
|
14779
15096
|
const { salt, ciphertext, iv, tag } = this.readRaw();
|
|
14780
15097
|
const useSalt = knownSalt ?? salt;
|
|
@@ -14796,8 +15113,8 @@ var Keystore = class {
|
|
|
14796
15113
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
14797
15114
|
const tag = cipher.getAuthTag();
|
|
14798
15115
|
const out = Buffer.concat([raw.salt, iv, tag, ciphertext]);
|
|
14799
|
-
|
|
14800
|
-
|
|
15116
|
+
fs27__default.default.mkdirSync(path28__default.default.dirname(this.storePath), { recursive: true });
|
|
15117
|
+
fs27__default.default.writeFileSync(this.storePath, out, { mode: 384 });
|
|
14801
15118
|
}
|
|
14802
15119
|
writeWithSalt(data, salt) {
|
|
14803
15120
|
if (!this.masterKey) throw new Error("writeWithSalt called before masterKey was set");
|
|
@@ -14807,11 +15124,11 @@ var Keystore = class {
|
|
|
14807
15124
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
14808
15125
|
const tag = cipher.getAuthTag();
|
|
14809
15126
|
const out = Buffer.concat([salt, iv, tag, ciphertext]);
|
|
14810
|
-
|
|
14811
|
-
|
|
15127
|
+
fs27__default.default.mkdirSync(path28__default.default.dirname(this.storePath), { recursive: true });
|
|
15128
|
+
fs27__default.default.writeFileSync(this.storePath, out, { mode: 384 });
|
|
14812
15129
|
}
|
|
14813
15130
|
readRaw() {
|
|
14814
|
-
const buf =
|
|
15131
|
+
const buf = fs27__default.default.readFileSync(this.storePath);
|
|
14815
15132
|
let offset = 0;
|
|
14816
15133
|
const salt = buf.subarray(offset, offset + SALT_LEN);
|
|
14817
15134
|
offset += SALT_LEN;
|
|
@@ -14844,7 +15161,7 @@ var CascadeIgnore = class {
|
|
|
14844
15161
|
]);
|
|
14845
15162
|
}
|
|
14846
15163
|
async load(workspacePath) {
|
|
14847
|
-
const filePath =
|
|
15164
|
+
const filePath = path28__default.default.join(workspacePath, ".cascadeignore");
|
|
14848
15165
|
try {
|
|
14849
15166
|
const content = await fs11__default.default.readFile(filePath, "utf-8");
|
|
14850
15167
|
const lines = content.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
|
|
@@ -14855,7 +15172,7 @@ var CascadeIgnore = class {
|
|
|
14855
15172
|
}
|
|
14856
15173
|
isIgnored(filePath, workspacePath) {
|
|
14857
15174
|
try {
|
|
14858
|
-
const relative = workspacePath ?
|
|
15175
|
+
const relative = workspacePath ? path28__default.default.relative(workspacePath, filePath) : filePath;
|
|
14859
15176
|
return this.ig.ignores(relative);
|
|
14860
15177
|
} catch {
|
|
14861
15178
|
return false;
|
|
@@ -14866,7 +15183,7 @@ var CascadeIgnore = class {
|
|
|
14866
15183
|
}
|
|
14867
15184
|
};
|
|
14868
15185
|
async function loadCascadeMd(workspacePath) {
|
|
14869
|
-
const filePath =
|
|
15186
|
+
const filePath = path28__default.default.join(workspacePath, "CASCADE.md");
|
|
14870
15187
|
try {
|
|
14871
15188
|
const raw = await fs11__default.default.readFile(filePath, "utf-8");
|
|
14872
15189
|
return parseCascadeMd(raw);
|
|
@@ -14897,7 +15214,7 @@ ${raw.trim()}`;
|
|
|
14897
15214
|
var MemoryStore = class _MemoryStore {
|
|
14898
15215
|
db;
|
|
14899
15216
|
constructor(dbPath) {
|
|
14900
|
-
|
|
15217
|
+
fs27__default.default.mkdirSync(path28__default.default.dirname(dbPath), { recursive: true });
|
|
14901
15218
|
try {
|
|
14902
15219
|
this.db = new Database2__default.default(dbPath, { timeout: 5e3 });
|
|
14903
15220
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -15795,13 +16112,38 @@ var WebSearchConfigSchema = zod.z.object({
|
|
|
15795
16112
|
/** Tavily API key — get one at https://tavily.com */
|
|
15796
16113
|
tavilyApiKey: zod.z.string().optional(),
|
|
15797
16114
|
/** Max results per search (default 5) */
|
|
15798
|
-
maxResults: zod.z.number().default(5)
|
|
16115
|
+
maxResults: zod.z.number().default(5),
|
|
16116
|
+
/**
|
|
16117
|
+
* Send `searxngUrl` through the SSRF guard. Off for a local run, where the
|
|
16118
|
+
* URL is the machine owner's own config and a self-hosted SearXNG is
|
|
16119
|
+
* routinely on a private address; on for a host that accepts it from a
|
|
16120
|
+
* request. See `src/tools/web-search.ts`.
|
|
16121
|
+
*
|
|
16122
|
+
* It has to be named HERE, not just on the TypeScript interfaces: the config
|
|
16123
|
+
* passes through `CascadeConfigSchema.parse()` on its way into a run, and
|
|
16124
|
+
* zod drops every key this object does not declare.
|
|
16125
|
+
*/
|
|
16126
|
+
guardSearxngUrl: zod.z.boolean().optional()
|
|
15799
16127
|
});
|
|
15800
16128
|
var ToolsConfigSchema = zod.z.object({
|
|
15801
16129
|
shellAllowlist: zod.z.array(zod.z.string()).default([]),
|
|
15802
16130
|
shellBlocklist: zod.z.array(zod.z.string()).default(["rm -rf", "sudo rm", "format", "mkfs"]),
|
|
15803
16131
|
requireApprovalFor: zod.z.array(zod.z.string()).default([]),
|
|
15804
16132
|
browserEnabled: zod.z.boolean().default(false),
|
|
16133
|
+
/**
|
|
16134
|
+
* Let a run ACT on the page open in the desktop's built-in browser — click,
|
|
16135
|
+
* type, submit — not just read it.
|
|
16136
|
+
*
|
|
16137
|
+
* Distinct from `browserEnabled`, which turns on the headless Playwright
|
|
16138
|
+
* tool. That one drives a throwaway browser with an empty cookie jar; this
|
|
16139
|
+
* one drives the session the user is signed into, which is a different order
|
|
16140
|
+
* of consequence and so gets its own switch rather than riding on that one.
|
|
16141
|
+
*
|
|
16142
|
+
* Off by default. Turning it on does not hand anything over silently: the
|
|
16143
|
+
* tool still goes through the approval chain, and the browser panel carries a
|
|
16144
|
+
* stop control for the run that is acting.
|
|
16145
|
+
*/
|
|
16146
|
+
agentBrowserControl: zod.z.boolean().default(false),
|
|
15805
16147
|
mcpServers: zod.z.array(McpServerConfigSchema).optional(),
|
|
15806
16148
|
mcpTrusted: zod.z.array(zod.z.string()).optional(),
|
|
15807
16149
|
/** Web search backends — at least one should be configured for best results */
|
|
@@ -16199,7 +16541,7 @@ function credentialEndpointsConflict(type, rowEndpoint, credentialEndpoint) {
|
|
|
16199
16541
|
|
|
16200
16542
|
// src/config/global-credentials.ts
|
|
16201
16543
|
function credentialsPath(globalDir2) {
|
|
16202
|
-
return
|
|
16544
|
+
return path28__default.default.join(globalDir2, GLOBAL_CREDENTIALS_FILE);
|
|
16203
16545
|
}
|
|
16204
16546
|
function providerKey(p) {
|
|
16205
16547
|
return p.type;
|
|
@@ -16229,7 +16571,7 @@ function isPersistable(p) {
|
|
|
16229
16571
|
}
|
|
16230
16572
|
function loadGlobalCredentials(globalDir2) {
|
|
16231
16573
|
try {
|
|
16232
|
-
const raw =
|
|
16574
|
+
const raw = fs27__default.default.readFileSync(credentialsPath(globalDir2), "utf-8");
|
|
16233
16575
|
const parsed = JSON.parse(raw);
|
|
16234
16576
|
if (!Array.isArray(parsed.providers)) return [];
|
|
16235
16577
|
return parsed.providers.filter(
|
|
@@ -16241,13 +16583,13 @@ function loadGlobalCredentials(globalDir2) {
|
|
|
16241
16583
|
}
|
|
16242
16584
|
function saveGlobalCredentials(globalDir2, providers) {
|
|
16243
16585
|
const filePath = credentialsPath(globalDir2);
|
|
16244
|
-
|
|
16586
|
+
fs27__default.default.mkdirSync(globalDir2, { recursive: true, mode: 448 });
|
|
16245
16587
|
const body = { version: 1, providers: providers.filter(isPersistable) };
|
|
16246
16588
|
const tmp = `${filePath}.tmp`;
|
|
16247
|
-
|
|
16248
|
-
|
|
16589
|
+
fs27__default.default.writeFileSync(tmp, JSON.stringify(body, null, 2), { encoding: "utf-8", mode: 384 });
|
|
16590
|
+
fs27__default.default.renameSync(tmp, filePath);
|
|
16249
16591
|
try {
|
|
16250
|
-
|
|
16592
|
+
fs27__default.default.chmodSync(filePath, 384);
|
|
16251
16593
|
} catch {
|
|
16252
16594
|
}
|
|
16253
16595
|
}
|
|
@@ -16648,7 +16990,7 @@ var ConfigManager = class {
|
|
|
16648
16990
|
/** `globalDirOverride` exists for tests — never point it at the real home dir there. */
|
|
16649
16991
|
constructor(workspacePath = process.cwd(), globalDirOverride) {
|
|
16650
16992
|
this.workspacePath = workspacePath;
|
|
16651
|
-
this.globalDir = globalDirOverride ??
|
|
16993
|
+
this.globalDir = globalDirOverride ?? path28__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR);
|
|
16652
16994
|
}
|
|
16653
16995
|
async load() {
|
|
16654
16996
|
this.config = await this.loadConfig();
|
|
@@ -16662,8 +17004,8 @@ var ConfigManager = class {
|
|
|
16662
17004
|
this.ignore = new CascadeIgnore();
|
|
16663
17005
|
await this.ignore.load(this.workspacePath);
|
|
16664
17006
|
this.cascadeMd = await loadCascadeMd(this.workspacePath);
|
|
16665
|
-
this.keystore = new Keystore(
|
|
16666
|
-
this.store = new MemoryStore(
|
|
17007
|
+
this.keystore = new Keystore(path28__default.default.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
|
|
17008
|
+
this.store = new MemoryStore(path28__default.default.join(this.workspacePath, CASCADE_DB_FILE));
|
|
16667
17009
|
const globalRevoked = stripRevokedCredentials(loadGlobalCredentials(this.globalDir));
|
|
16668
17010
|
const globalCreds = filterRetiredCredentials(globalRevoked.kept);
|
|
16669
17011
|
if (globalRevoked.removed > 0) this.revokedCredentials += globalRevoked.removed;
|
|
@@ -16736,8 +17078,8 @@ var ConfigManager = class {
|
|
|
16736
17078
|
* never reached the disk (see `commitSettings`).
|
|
16737
17079
|
*/
|
|
16738
17080
|
async save(config = this.config) {
|
|
16739
|
-
const configPath =
|
|
16740
|
-
await fs11__default.default.mkdir(
|
|
17081
|
+
const configPath = path28__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
17082
|
+
await fs11__default.default.mkdir(path28__default.default.dirname(configPath), { recursive: true });
|
|
16741
17083
|
await fs11__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
16742
17084
|
try {
|
|
16743
17085
|
saveGlobalCredentials(this.globalDir, config.providers);
|
|
@@ -16787,7 +17129,7 @@ var ConfigManager = class {
|
|
|
16787
17129
|
this.revokedCredentials = 0;
|
|
16788
17130
|
this.revokedPins = [];
|
|
16789
17131
|
this.revokedNotice = void 0;
|
|
16790
|
-
const configPath =
|
|
17132
|
+
const configPath = path28__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
16791
17133
|
try {
|
|
16792
17134
|
const raw = await fs11__default.default.readFile(configPath, "utf-8");
|
|
16793
17135
|
const parsed = JSON.parse(raw);
|
|
@@ -16881,7 +17223,7 @@ var ConfigManager = class {
|
|
|
16881
17223
|
return onResource;
|
|
16882
17224
|
}
|
|
16883
17225
|
async persistClearedPins(tiers) {
|
|
16884
|
-
const configPath =
|
|
17226
|
+
const configPath = path28__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
16885
17227
|
try {
|
|
16886
17228
|
const raw = JSON.parse(await fs11__default.default.readFile(configPath, "utf-8"));
|
|
16887
17229
|
const models = raw["models"];
|
|
@@ -17475,7 +17817,7 @@ function computeDelegationSavings(stats, t1Model) {
|
|
|
17475
17817
|
var DEFAULT_SNAPSHOT_URL = "https://raw.githubusercontent.com/Varun-SV/Cascade-AI/main/src/core/router/benchmark-data.json";
|
|
17476
17818
|
var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
|
|
17477
17819
|
var FETCH_TIMEOUT_MS = 8e3;
|
|
17478
|
-
var DEFAULT_CACHE_FILE =
|
|
17820
|
+
var DEFAULT_CACHE_FILE = path28__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
|
|
17479
17821
|
function normalizeModelId2(id) {
|
|
17480
17822
|
let s = id.toLowerCase();
|
|
17481
17823
|
const slash = s.lastIndexOf("/");
|
|
@@ -17622,7 +17964,7 @@ var LiveDataProvider = class {
|
|
|
17622
17964
|
}
|
|
17623
17965
|
async saveCache() {
|
|
17624
17966
|
try {
|
|
17625
|
-
await fs11__default.default.mkdir(
|
|
17967
|
+
await fs11__default.default.mkdir(path28__default.default.dirname(this.opts.cacheFile), { recursive: true });
|
|
17626
17968
|
const cache = {
|
|
17627
17969
|
fetchedAt: this.fetchedAt,
|
|
17628
17970
|
snapshot: this.snapshot ?? void 0,
|
|
@@ -17729,9 +18071,15 @@ var FAMILY_BENCHMARKS = {
|
|
|
17729
18071
|
"claude-opus": { code: 95, analysis: 92, creative: 90, data: 88 },
|
|
17730
18072
|
"claude-sonnet": { code: 93, analysis: 88, creative: 87, data: 85 },
|
|
17731
18073
|
"claude-haiku": { code: 80, analysis: 75, creative: 76, data: 72 },
|
|
17732
|
-
// OpenAI GPT-5 family
|
|
17733
|
-
//
|
|
17734
|
-
//
|
|
18074
|
+
// OpenAI GPT-5 family. GPT-5.6 is split by durable capability tier instead
|
|
18075
|
+
// of being folded into generic gpt-5: Sol, Terra and Luna have materially
|
|
18076
|
+
// different benchmark/cost envelopes and should compete independently.
|
|
18077
|
+
// Scores are the committed normalized snapshot; provenance is in
|
|
18078
|
+
// scripts/benchmarks/sources/openai-gpt-5.6-2026-08.json.
|
|
18079
|
+
"gpt-5.6-sol": { code: 100, analysis: 98, creative: 88, data: 88 },
|
|
18080
|
+
"gpt-5.6-terra": { code: 97, analysis: 92, creative: 82, data: 85 },
|
|
18081
|
+
"gpt-5.6-luna": { code: 93, analysis: 85, creative: 74, data: 60 },
|
|
18082
|
+
// Older GPT-5 point releases remain distinct where public data supports it.
|
|
17735
18083
|
"gpt-5.5": { code: 97, analysis: 96, creative: 94, data: 95 },
|
|
17736
18084
|
"gpt-5.4": { code: 94, analysis: 94, creative: 92, data: 92 },
|
|
17737
18085
|
"gpt-5.4-mini": { code: 86, analysis: 85, creative: 85, data: 83 },
|
|
@@ -17767,13 +18115,19 @@ var FAMILY_BENCHMARKS = {
|
|
|
17767
18115
|
"llama-small": { code: 55, analysis: 56, creative: 60, data: 54 }
|
|
17768
18116
|
};
|
|
17769
18117
|
var FAMILY_MATCHERS = [
|
|
18118
|
+
[/fable/i, "claude-opus"],
|
|
17770
18119
|
[/opus/i, "claude-opus"],
|
|
17771
18120
|
[/sonnet/i, "claude-sonnet"],
|
|
17772
18121
|
[/haiku/i, "claude-haiku"],
|
|
17773
|
-
// GPT-5 family — ordered most-specific first.
|
|
17774
|
-
//
|
|
17775
|
-
//
|
|
18122
|
+
// GPT-5 family — ordered most-specific first. GPT-5.6 durable tiers carry
|
|
18123
|
+
// their own profiles. 5.4 Nano intentionally maps to the nano class rather
|
|
18124
|
+
// than being swallowed by the generic 5.4 rule.
|
|
18125
|
+
[/gpt-?5\.6.*sol/i, "gpt-5.6-sol"],
|
|
18126
|
+
[/gpt-?5\.6.*terra/i, "gpt-5.6-terra"],
|
|
18127
|
+
[/gpt-?5\.6.*luna/i, "gpt-5.6-luna"],
|
|
18128
|
+
[/gpt-?5\.6(?:\s|$)/i, "gpt-5.6-sol"],
|
|
17776
18129
|
[/gpt-?5\.5/i, "gpt-5.5"],
|
|
18130
|
+
[/gpt-?5\.4.*nano/i, "gpt-5-nano"],
|
|
17777
18131
|
[/gpt-?5\.4.*mini/i, "gpt-5.4-mini"],
|
|
17778
18132
|
[/gpt-?5\.4/i, "gpt-5.4"],
|
|
17779
18133
|
[/gpt-?5.*nano/i, "gpt-5-nano"],
|
|
@@ -20574,6 +20928,23 @@ Now execute your subtask using this context where relevant.`
|
|
|
20574
20928
|
this.setStatus("FAILED", finalOutput);
|
|
20575
20929
|
this.peerBus?.publish(this.id, assignment.subtaskId, finalOutput, "FAILED");
|
|
20576
20930
|
return this.buildResult("ESCALATED", finalOutput, { checksRun, passed, failed }, issues, correctionAttempts);
|
|
20931
|
+
} finally {
|
|
20932
|
+
this.releaseBrowserControl();
|
|
20933
|
+
}
|
|
20934
|
+
}
|
|
20935
|
+
/**
|
|
20936
|
+
* End this worker's hold on the shared browser.
|
|
20937
|
+
*
|
|
20938
|
+
* Duck-typed rather than imported: `browser_control` is registered only by a
|
|
20939
|
+
* host that HAS a browser (the desktop), so in the CLI and hosted runs there
|
|
20940
|
+
* is no such tool and this is a no-op. Importing the class to instanceof-check
|
|
20941
|
+
* it would couple the worker to a tool that usually is not there.
|
|
20942
|
+
*/
|
|
20943
|
+
releaseBrowserControl() {
|
|
20944
|
+
try {
|
|
20945
|
+
const tool = this.toolRegistry.getTool?.("browser_control");
|
|
20946
|
+
tool?.releaseActor?.(this.id);
|
|
20947
|
+
} catch {
|
|
20577
20948
|
}
|
|
20578
20949
|
}
|
|
20579
20950
|
sendToPeer(toId, content) {
|
|
@@ -20832,8 +21203,8 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : "") + textToolSuffix,
|
|
|
20832
21203
|
// Media generation can run for a minute; without this a cancelled run
|
|
20833
21204
|
// still pays for an image nobody will see.
|
|
20834
21205
|
...this.signal ? { signal: this.signal } : {},
|
|
20835
|
-
saveSnapshot: async (
|
|
20836
|
-
this.store?.addFileSnapshot(this.taskId,
|
|
21206
|
+
saveSnapshot: async (path30, content) => {
|
|
21207
|
+
this.store?.addFileSnapshot(this.taskId, path30, content);
|
|
20837
21208
|
},
|
|
20838
21209
|
sendPeerSync: (to, syncType, content) => {
|
|
20839
21210
|
this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
|
|
@@ -21036,7 +21407,7 @@ ${assignment.expectedOutput}`;
|
|
|
21036
21407
|
const { promisify: promisify4 } = await import('util');
|
|
21037
21408
|
const execFileAsync3 = promisify4(execFile4);
|
|
21038
21409
|
for (const artifactPath of artifactPaths) {
|
|
21039
|
-
const absolutePath =
|
|
21410
|
+
const absolutePath = path28__default.default.resolve(this.artifactRoot(), artifactPath);
|
|
21040
21411
|
try {
|
|
21041
21412
|
const stat = await fs11__default.default.stat(absolutePath);
|
|
21042
21413
|
if (!stat.isFile()) {
|
|
@@ -21071,7 +21442,7 @@ ${assignment.expectedOutput}`;
|
|
|
21071
21442
|
issues.push(`PDF artifact looks too small to be valid: ${artifactPath}`);
|
|
21072
21443
|
continue;
|
|
21073
21444
|
}
|
|
21074
|
-
const ext =
|
|
21445
|
+
const ext = path28__default.default.extname(absolutePath).toLowerCase();
|
|
21075
21446
|
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;
|
|
21076
21447
|
if (check) {
|
|
21077
21448
|
try {
|
|
@@ -21163,7 +21534,7 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
21163
21534
|
async checkAcceptance(assignment) {
|
|
21164
21535
|
const criteria = assignment.acceptance ?? [];
|
|
21165
21536
|
if (!criteria.length) return [];
|
|
21166
|
-
const resolve2 = (target) =>
|
|
21537
|
+
const resolve2 = (target) => path28__default.default.resolve(this.artifactRoot(), target);
|
|
21167
21538
|
const workerCanWriteFiles = canProduceFiles(this.tools.map((t) => t.name));
|
|
21168
21539
|
return evaluateAcceptance(criteria, assignment.files ?? [], {
|
|
21169
21540
|
stat: async (target) => {
|
|
@@ -24467,16 +24838,16 @@ function resolveInWorkspace(workspaceRoot, input) {
|
|
|
24467
24838
|
if (typeof input !== "string" || input.length === 0) {
|
|
24468
24839
|
throw new WorkspaceSandboxError(String(input), workspaceRoot);
|
|
24469
24840
|
}
|
|
24470
|
-
const root =
|
|
24471
|
-
const abs =
|
|
24472
|
-
const rel =
|
|
24473
|
-
if (rel === "" || rel === ".") ; else if (rel.startsWith("..") ||
|
|
24841
|
+
const root = path28__default.default.resolve(workspaceRoot);
|
|
24842
|
+
const abs = path28__default.default.isAbsolute(input) ? path28__default.default.resolve(input) : path28__default.default.resolve(root, input);
|
|
24843
|
+
const rel = path28__default.default.relative(root, abs);
|
|
24844
|
+
if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path28__default.default.isAbsolute(rel)) {
|
|
24474
24845
|
throw new WorkspaceSandboxError(input, root);
|
|
24475
24846
|
}
|
|
24476
24847
|
try {
|
|
24477
|
-
const real =
|
|
24478
|
-
const realRel =
|
|
24479
|
-
if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") ||
|
|
24848
|
+
const real = fs27__default.default.realpathSync(abs);
|
|
24849
|
+
const realRel = path28__default.default.relative(root, real);
|
|
24850
|
+
if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path28__default.default.isAbsolute(realRel))) {
|
|
24480
24851
|
throw new WorkspaceSandboxError(input, root);
|
|
24481
24852
|
}
|
|
24482
24853
|
} catch (e) {
|
|
@@ -25025,9 +25396,9 @@ async function animatePptx(bytes, scheme) {
|
|
|
25025
25396
|
const zip = await import_jszip.default.loadAsync(bytes);
|
|
25026
25397
|
const slidePaths = Object.keys(zip.files).filter((n) => /^ppt\/slides\/slide\d+\.xml$/.test(n));
|
|
25027
25398
|
if (slidePaths.length === 0) return bytes;
|
|
25028
|
-
for (const
|
|
25029
|
-
const xml = await zip.file(
|
|
25030
|
-
zip.file(
|
|
25399
|
+
for (const path30 of slidePaths) {
|
|
25400
|
+
const xml = await zip.file(path30).async("string");
|
|
25401
|
+
zip.file(path30, animateSlideXml(xml, scheme));
|
|
25031
25402
|
}
|
|
25032
25403
|
return await zip.generateAsync({ type: "uint8array", compression: "DEFLATE" });
|
|
25033
25404
|
} catch {
|
|
@@ -25413,7 +25784,7 @@ var GenerateDocumentTool = class extends BaseTool {
|
|
|
25413
25784
|
}
|
|
25414
25785
|
}
|
|
25415
25786
|
const bytes = await renderDocument(ext, content, { name: filePath, loadImageBytes });
|
|
25416
|
-
await fs11__default.default.mkdir(
|
|
25787
|
+
await fs11__default.default.mkdir(path28__default.default.dirname(absPath), { recursive: true });
|
|
25417
25788
|
await fs11__default.default.writeFile(absPath, bytes);
|
|
25418
25789
|
const charts = countCharts(content);
|
|
25419
25790
|
return [
|
|
@@ -25631,7 +26002,7 @@ var FileWriteTool = class extends BaseTool {
|
|
|
25631
26002
|
} catch {
|
|
25632
26003
|
}
|
|
25633
26004
|
}
|
|
25634
|
-
await fs11__default.default.mkdir(
|
|
26005
|
+
await fs11__default.default.mkdir(path28__default.default.dirname(absPath), { recursive: true });
|
|
25635
26006
|
await fs11__default.default.writeFile(absPath, content, "utf-8");
|
|
25636
26007
|
return `Written ${content.length} characters to ${filePath}`;
|
|
25637
26008
|
}
|
|
@@ -25989,12 +26360,10 @@ ${data.description}`;
|
|
|
25989
26360
|
}
|
|
25990
26361
|
}
|
|
25991
26362
|
};
|
|
25992
|
-
|
|
25993
|
-
// src/tools/browser.ts
|
|
25994
26363
|
var BROWSER_LAUNCH_TIMEOUT_MS = 15e3;
|
|
25995
26364
|
var BrowserTool = class extends BaseTool {
|
|
25996
26365
|
name = "browser";
|
|
25997
|
-
description = "Control a browser: navigate to URLs, click elements, fill forms, take screenshots.
|
|
26366
|
+
description = "Control a browser: navigate to URLs, click elements, fill forms, take screenshots. Screenshots are saved to a file for image_analyze to read.";
|
|
25998
26367
|
inputSchema = {
|
|
25999
26368
|
type: "object",
|
|
26000
26369
|
properties: {
|
|
@@ -26061,7 +26430,14 @@ var BrowserTool = class extends BaseTool {
|
|
|
26061
26430
|
}
|
|
26062
26431
|
case "screenshot": {
|
|
26063
26432
|
const buf = await page2.screenshot({ type: "png" });
|
|
26064
|
-
|
|
26433
|
+
const rel = path28__default.default.join(".cascade", "screenshots", `screenshot-${Date.now()}-${crypto.randomUUID().slice(0, 8)}.png`);
|
|
26434
|
+
const abs = resolveInWorkspace(this.workspaceRoot, rel);
|
|
26435
|
+
await fs11__default.default.mkdir(path28__default.default.dirname(abs), { recursive: true });
|
|
26436
|
+
await fs11__default.default.writeFile(abs, buf);
|
|
26437
|
+
return [
|
|
26438
|
+
`Screenshot saved to ${abs} (${Math.round(buf.byteLength / 1024)} KB).`,
|
|
26439
|
+
`Call image_analyze with that path to see what is on it \u2014 that needs a vision-capable model.`
|
|
26440
|
+
].join(" ");
|
|
26065
26441
|
}
|
|
26066
26442
|
case "evaluate": {
|
|
26067
26443
|
const result = await page2.evaluate(input["script"]);
|
|
@@ -26129,7 +26505,7 @@ var ImageAnalyzeTool = class extends BaseTool {
|
|
|
26129
26505
|
};
|
|
26130
26506
|
async function fileToImageAttachment(filePath) {
|
|
26131
26507
|
const data = await fs11__default.default.readFile(filePath);
|
|
26132
|
-
const ext =
|
|
26508
|
+
const ext = path28__default.default.extname(filePath).toLowerCase();
|
|
26133
26509
|
const mimeMap = {
|
|
26134
26510
|
".jpg": "image/jpeg",
|
|
26135
26511
|
".jpeg": "image/jpeg",
|
|
@@ -26163,14 +26539,14 @@ var PDFCreateTool = class extends BaseTool {
|
|
|
26163
26539
|
const filePath = input["path"];
|
|
26164
26540
|
const content = input["content"];
|
|
26165
26541
|
const title = input["title"];
|
|
26166
|
-
const dir =
|
|
26167
|
-
if (!
|
|
26168
|
-
|
|
26542
|
+
const dir = path28__default.default.dirname(filePath);
|
|
26543
|
+
if (!fs27__default.default.existsSync(dir)) {
|
|
26544
|
+
fs27__default.default.mkdirSync(dir, { recursive: true });
|
|
26169
26545
|
}
|
|
26170
26546
|
return new Promise((resolve2, reject) => {
|
|
26171
26547
|
try {
|
|
26172
26548
|
const doc = new PDFDocument__default.default({ margin: 50 });
|
|
26173
|
-
const stream =
|
|
26549
|
+
const stream = fs27__default.default.createWriteStream(filePath);
|
|
26174
26550
|
doc.pipe(stream);
|
|
26175
26551
|
if (title) {
|
|
26176
26552
|
doc.info["Title"] = title;
|
|
@@ -26248,22 +26624,22 @@ var CodeInterpreterTool = class extends BaseTool {
|
|
|
26248
26624
|
}
|
|
26249
26625
|
cmdPrefix = NODE_CMD;
|
|
26250
26626
|
}
|
|
26251
|
-
const tmpDir =
|
|
26252
|
-
if (!
|
|
26253
|
-
|
|
26627
|
+
const tmpDir = path28__default.default.join(this.workspaceRoot, ".cascade", "tmp");
|
|
26628
|
+
if (!fs27__default.default.existsSync(tmpDir)) {
|
|
26629
|
+
fs27__default.default.mkdirSync(tmpDir, { recursive: true });
|
|
26254
26630
|
}
|
|
26255
26631
|
const extension = language === "python" ? "py" : "js";
|
|
26256
26632
|
const fileName = `intp_${crypto.randomUUID().slice(0, 8)}.${extension}`;
|
|
26257
|
-
const filePath =
|
|
26258
|
-
|
|
26633
|
+
const filePath = path28__default.default.join(tmpDir, fileName);
|
|
26634
|
+
fs27__default.default.writeFileSync(filePath, code, "utf-8");
|
|
26259
26635
|
const execArgs = [filePath, ...args];
|
|
26260
26636
|
return new Promise((resolve2) => {
|
|
26261
26637
|
const startMs = Date.now();
|
|
26262
26638
|
child_process.execFile(cmdPrefix, execArgs, { cwd: this.workspaceRoot, timeout: 3e4 }, (error, stdout, stderr) => {
|
|
26263
26639
|
const duration = Date.now() - startMs;
|
|
26264
26640
|
try {
|
|
26265
|
-
if (
|
|
26266
|
-
|
|
26641
|
+
if (fs27__default.default.existsSync(filePath)) {
|
|
26642
|
+
fs27__default.default.unlinkSync(filePath);
|
|
26267
26643
|
}
|
|
26268
26644
|
} catch (cleanupErr) {
|
|
26269
26645
|
console.error(`Failed to cleanup interpreter script ${filePath}:`, cleanupErr);
|
|
@@ -26343,18 +26719,178 @@ ${formatted}`;
|
|
|
26343
26719
|
return `Unknown action: ${action}`;
|
|
26344
26720
|
}
|
|
26345
26721
|
};
|
|
26722
|
+
var SsrfBlockedError = class extends Error {
|
|
26723
|
+
constructor(message) {
|
|
26724
|
+
super(message);
|
|
26725
|
+
this.name = "SsrfBlockedError";
|
|
26726
|
+
}
|
|
26727
|
+
};
|
|
26728
|
+
var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
26729
|
+
var MAX_REDIRECTS2 = 5;
|
|
26730
|
+
function allowLocal() {
|
|
26731
|
+
return process.env["CASCADE_ALLOW_LOCAL_FETCH"] === "1";
|
|
26732
|
+
}
|
|
26733
|
+
function isPrivateAddress(ip) {
|
|
26734
|
+
const type = net__default.default.isIP(ip);
|
|
26735
|
+
if (type === 4) return isPrivateIPv4(ip);
|
|
26736
|
+
if (type === 6) return isPrivateIPv6(ip);
|
|
26737
|
+
return true;
|
|
26738
|
+
}
|
|
26739
|
+
function isPrivateIPv4(ip) {
|
|
26740
|
+
const parts = ip.split(".").map((p) => Number(p));
|
|
26741
|
+
if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255)) return true;
|
|
26742
|
+
const [a, b] = parts;
|
|
26743
|
+
if (a === 0) return true;
|
|
26744
|
+
if (a === 10) return true;
|
|
26745
|
+
if (a === 127) return true;
|
|
26746
|
+
if (a === 169 && b === 254) return true;
|
|
26747
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
26748
|
+
if (a === 192 && b === 168) return true;
|
|
26749
|
+
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
26750
|
+
if (a >= 224) return true;
|
|
26751
|
+
return false;
|
|
26752
|
+
}
|
|
26753
|
+
function ipv6Groups(ip) {
|
|
26754
|
+
let s = ip.toLowerCase().replace(/^\[|\]$/g, "");
|
|
26755
|
+
const zone = s.indexOf("%");
|
|
26756
|
+
if (zone !== -1) s = s.slice(0, zone);
|
|
26757
|
+
const dotted = /^(.*:)(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(s);
|
|
26758
|
+
if (dotted) {
|
|
26759
|
+
const o = [dotted[2], dotted[3], dotted[4], dotted[5]].map((p) => Number(p));
|
|
26760
|
+
if (o.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return null;
|
|
26761
|
+
s = `${dotted[1]}${(o[0] << 8 | o[1]).toString(16)}:${(o[2] << 8 | o[3]).toString(16)}`;
|
|
26762
|
+
}
|
|
26763
|
+
const halves = s.split("::");
|
|
26764
|
+
if (halves.length > 2) return null;
|
|
26765
|
+
const split = (part) => part ? part.split(":") : [];
|
|
26766
|
+
const head = split(halves[0] ?? "");
|
|
26767
|
+
const tail = halves.length === 2 ? split(halves[1] ?? "") : [];
|
|
26768
|
+
const zeros = halves.length === 2 ? 8 - head.length - tail.length : 0;
|
|
26769
|
+
if (zeros < 0) return null;
|
|
26770
|
+
const groups = [...head, ...Array(zeros).fill("0"), ...tail];
|
|
26771
|
+
if (groups.length !== 8) return null;
|
|
26772
|
+
const out = [];
|
|
26773
|
+
for (const g of groups) {
|
|
26774
|
+
if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
|
|
26775
|
+
out.push(parseInt(g, 16));
|
|
26776
|
+
}
|
|
26777
|
+
return out;
|
|
26778
|
+
}
|
|
26779
|
+
function embeddedIPv4(g) {
|
|
26780
|
+
const quad = (hi, lo) => `${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`;
|
|
26781
|
+
const leadingZero = (n) => g.slice(0, n).every((x) => x === 0);
|
|
26782
|
+
if (leadingZero(5) && g[5] === 65535) return quad(g[6], g[7]);
|
|
26783
|
+
if (leadingZero(6)) return quad(g[6], g[7]);
|
|
26784
|
+
if (g[0] === 100 && g[1] === 65435) {
|
|
26785
|
+
if (g[2] === 1) {
|
|
26786
|
+
return quad(g[3], (g[4] & 255) << 8 | g[5] >> 8);
|
|
26787
|
+
}
|
|
26788
|
+
if (g[2] === 0 && g[3] === 0 && g[4] === 0 && g[5] === 0) return quad(g[6], g[7]);
|
|
26789
|
+
return null;
|
|
26790
|
+
}
|
|
26791
|
+
if (g[0] === 8194) return quad(g[1], g[2]);
|
|
26792
|
+
return null;
|
|
26793
|
+
}
|
|
26794
|
+
function isPrivateIPv6(ip) {
|
|
26795
|
+
const g = ipv6Groups(ip);
|
|
26796
|
+
if (!g) return true;
|
|
26797
|
+
const v4 = embeddedIPv4(g);
|
|
26798
|
+
if (v4) return isPrivateIPv4(v4);
|
|
26799
|
+
if (g[0] === 100 && g[1] === 65435) return true;
|
|
26800
|
+
if (g.every((x) => x === 0)) return true;
|
|
26801
|
+
if (leading7Zero(g) && g[7] === 1) return true;
|
|
26802
|
+
if ((g[0] & 65472) === 65152) return true;
|
|
26803
|
+
if ((g[0] & 65024) === 64512) return true;
|
|
26804
|
+
if ((g[0] & 65280) === 65280) return true;
|
|
26805
|
+
return false;
|
|
26806
|
+
}
|
|
26807
|
+
function leading7Zero(g) {
|
|
26808
|
+
return g.slice(0, 7).every((x) => x === 0);
|
|
26809
|
+
}
|
|
26810
|
+
async function assertPublicUrl(rawUrl) {
|
|
26811
|
+
let url;
|
|
26812
|
+
try {
|
|
26813
|
+
url = new URL(rawUrl);
|
|
26814
|
+
} catch {
|
|
26815
|
+
throw new SsrfBlockedError(`Invalid URL: ${rawUrl}`);
|
|
26816
|
+
}
|
|
26817
|
+
if (!ALLOWED_PROTOCOLS.has(url.protocol)) {
|
|
26818
|
+
throw new SsrfBlockedError(`Blocked URL scheme "${url.protocol}" \u2014 only http and https are allowed.`);
|
|
26819
|
+
}
|
|
26820
|
+
if (allowLocal()) return url;
|
|
26821
|
+
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
26822
|
+
if (net__default.default.isIP(host)) {
|
|
26823
|
+
if (isPrivateAddress(host)) {
|
|
26824
|
+
throw new SsrfBlockedError(`Blocked request to non-public address ${host}.`);
|
|
26825
|
+
}
|
|
26826
|
+
return url;
|
|
26827
|
+
}
|
|
26828
|
+
if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) {
|
|
26829
|
+
throw new SsrfBlockedError(`Blocked request to local hostname "${host}".`);
|
|
26830
|
+
}
|
|
26831
|
+
let addresses;
|
|
26832
|
+
try {
|
|
26833
|
+
const records = await dns2__default.default.lookup(host, { all: true });
|
|
26834
|
+
addresses = records.map((r) => r.address);
|
|
26835
|
+
} catch {
|
|
26836
|
+
throw new SsrfBlockedError(`Could not resolve host "${host}".`);
|
|
26837
|
+
}
|
|
26838
|
+
if (addresses.length === 0 || addresses.some((addr) => isPrivateAddress(addr))) {
|
|
26839
|
+
throw new SsrfBlockedError(`Blocked request to "${host}" \u2014 resolves to a non-public address.`);
|
|
26840
|
+
}
|
|
26841
|
+
return url;
|
|
26842
|
+
}
|
|
26843
|
+
var ssrfAgent = new undici.Agent({
|
|
26844
|
+
connect: {
|
|
26845
|
+
lookup(hostname, options, callback) {
|
|
26846
|
+
if (allowLocal()) {
|
|
26847
|
+
dnsCallback__default.default.lookup(hostname, options, callback);
|
|
26848
|
+
return;
|
|
26849
|
+
}
|
|
26850
|
+
dnsCallback__default.default.lookup(hostname, { ...options, all: true }, (err, addresses) => {
|
|
26851
|
+
if (err) return callback(err, "", 0);
|
|
26852
|
+
const list = Array.isArray(addresses) ? addresses : [addresses];
|
|
26853
|
+
const bad = list.find((a) => isPrivateAddress(a.address));
|
|
26854
|
+
if (bad) {
|
|
26855
|
+
return callback(
|
|
26856
|
+
new SsrfBlockedError(`Blocked connection to "${hostname}" \u2014 resolved to non-public address ${bad.address}.`),
|
|
26857
|
+
"",
|
|
26858
|
+
0
|
|
26859
|
+
);
|
|
26860
|
+
}
|
|
26861
|
+
if (options.all) return callback(null, list);
|
|
26862
|
+
const first = list[0];
|
|
26863
|
+
return callback(null, first.address, first.family);
|
|
26864
|
+
});
|
|
26865
|
+
}
|
|
26866
|
+
}
|
|
26867
|
+
});
|
|
26868
|
+
async function safeFetch(rawUrl, init = {}) {
|
|
26869
|
+
let currentUrl = (await assertPublicUrl(rawUrl)).toString();
|
|
26870
|
+
for (let i = 0; i <= MAX_REDIRECTS2; i++) {
|
|
26871
|
+
const resp = await fetch(currentUrl, { ...init, redirect: "manual", dispatcher: ssrfAgent });
|
|
26872
|
+
if (resp.status < 300 || resp.status >= 400) return resp;
|
|
26873
|
+
const location = resp.headers.get("location");
|
|
26874
|
+
if (!location) return resp;
|
|
26875
|
+
const next = new URL(location, currentUrl);
|
|
26876
|
+
await assertPublicUrl(next.toString());
|
|
26877
|
+
currentUrl = next.toString();
|
|
26878
|
+
}
|
|
26879
|
+
throw new SsrfBlockedError(`Too many redirects (>${MAX_REDIRECTS2}).`);
|
|
26880
|
+
}
|
|
26346
26881
|
|
|
26347
26882
|
// src/tools/web-search.ts
|
|
26348
|
-
async function searchSearXNG(query, baseUrl, maxResults) {
|
|
26883
|
+
async function searchSearXNG(query, baseUrl, maxResults, guardUrl = false) {
|
|
26349
26884
|
const url = new URL("/search", baseUrl);
|
|
26350
26885
|
url.searchParams.set("q", query);
|
|
26351
26886
|
url.searchParams.set("format", "json");
|
|
26352
26887
|
url.searchParams.set("categories", "general");
|
|
26353
26888
|
url.searchParams.set("engines", "google,bing,duckduckgo");
|
|
26354
|
-
const
|
|
26889
|
+
const init = {
|
|
26355
26890
|
headers: { "User-Agent": "Cascade-AI/1.0 WebSearchTool" },
|
|
26356
26891
|
signal: AbortSignal.timeout(1e4)
|
|
26357
|
-
}
|
|
26892
|
+
};
|
|
26893
|
+
const resp = guardUrl ? await safeFetch(url.toString(), init) : await fetch(url.toString(), init);
|
|
26358
26894
|
if (!resp.ok) {
|
|
26359
26895
|
throw new Error(`SearXNG returned HTTP ${resp.status}`);
|
|
26360
26896
|
}
|
|
@@ -26483,7 +27019,11 @@ var WebSearchTool = class extends BaseTool {
|
|
|
26483
27019
|
searxngUrl: config.searxngUrl ?? process.env["SEARXNG_URL"],
|
|
26484
27020
|
braveApiKey: config.braveApiKey ?? process.env["BRAVE_SEARCH_API_KEY"],
|
|
26485
27021
|
tavilyApiKey: config.tavilyApiKey ?? process.env["TAVILY_API_KEY"],
|
|
26486
|
-
maxResults: config.maxResults ?? 5
|
|
27022
|
+
maxResults: config.maxResults ?? 5,
|
|
27023
|
+
// Note this reads only from `config`, never from the environment: the
|
|
27024
|
+
// guard tracks the trustworthiness of the URL the host passed in, and an
|
|
27025
|
+
// env var says nothing about that.
|
|
27026
|
+
guardSearxngUrl: config.guardSearxngUrl ?? false
|
|
26487
27027
|
};
|
|
26488
27028
|
}
|
|
26489
27029
|
async execute(input, _options) {
|
|
@@ -26498,7 +27038,7 @@ var WebSearchTool = class extends BaseTool {
|
|
|
26498
27038
|
let anyBackendReachable = false;
|
|
26499
27039
|
if (this.config.searxngUrl) {
|
|
26500
27040
|
try {
|
|
26501
|
-
results = await searchSearXNG(query, this.config.searxngUrl, maxResults);
|
|
27041
|
+
results = await searchSearXNG(query, this.config.searxngUrl, maxResults, this.config.guardSearxngUrl);
|
|
26502
27042
|
anyBackendReachable = true;
|
|
26503
27043
|
if (results.length > 0) return this.formatResults(query, results);
|
|
26504
27044
|
errors.push("SearXNG: returned 0 results");
|
|
@@ -26575,7 +27115,7 @@ var GlobTool = class extends BaseTool {
|
|
|
26575
27115
|
};
|
|
26576
27116
|
async execute(input, _options) {
|
|
26577
27117
|
const pattern = input["pattern"];
|
|
26578
|
-
const searchPath = input["path"] ?
|
|
27118
|
+
const searchPath = input["path"] ? path28__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
|
|
26579
27119
|
const matches = await glob.glob(pattern, {
|
|
26580
27120
|
cwd: searchPath,
|
|
26581
27121
|
ignore: ["node_modules/**", ".git/**", "dist/**", "build/**"],
|
|
@@ -26588,7 +27128,7 @@ var GlobTool = class extends BaseTool {
|
|
|
26588
27128
|
const withMtime = await Promise.all(
|
|
26589
27129
|
matches.map(async (rel) => {
|
|
26590
27130
|
try {
|
|
26591
|
-
const stat = await fs11__default.default.stat(
|
|
27131
|
+
const stat = await fs11__default.default.stat(path28__default.default.join(searchPath, rel));
|
|
26592
27132
|
return { rel, mtime: stat.mtimeMs };
|
|
26593
27133
|
} catch {
|
|
26594
27134
|
return { rel, mtime: 0 };
|
|
@@ -26637,7 +27177,7 @@ var GrepTool = class extends BaseTool {
|
|
|
26637
27177
|
};
|
|
26638
27178
|
async execute(input, _options) {
|
|
26639
27179
|
const pattern = input["pattern"];
|
|
26640
|
-
const searchPath = input["path"] ?
|
|
27180
|
+
const searchPath = input["path"] ? path28__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
|
|
26641
27181
|
const globPattern = input["glob"];
|
|
26642
27182
|
const outputMode = input["output_mode"] ?? "content";
|
|
26643
27183
|
const context = input["context"] ?? 0;
|
|
@@ -26691,12 +27231,12 @@ var GrepTool = class extends BaseTool {
|
|
|
26691
27231
|
nodir: true
|
|
26692
27232
|
});
|
|
26693
27233
|
} catch {
|
|
26694
|
-
files = [
|
|
27234
|
+
files = [path28__default.default.relative(searchPath, searchPath) || "."];
|
|
26695
27235
|
}
|
|
26696
27236
|
const results = [];
|
|
26697
27237
|
let totalCount = 0;
|
|
26698
27238
|
for (const rel of files) {
|
|
26699
|
-
const abs =
|
|
27239
|
+
const abs = path28__default.default.join(searchPath, rel);
|
|
26700
27240
|
let content;
|
|
26701
27241
|
try {
|
|
26702
27242
|
content = await fs11__default.default.readFile(abs, "utf-8");
|
|
@@ -26737,92 +27277,6 @@ Total: ${totalCount} matches`);
|
|
|
26737
27277
|
return results.join("\n");
|
|
26738
27278
|
}
|
|
26739
27279
|
};
|
|
26740
|
-
var SsrfBlockedError = class extends Error {
|
|
26741
|
-
constructor(message) {
|
|
26742
|
-
super(message);
|
|
26743
|
-
this.name = "SsrfBlockedError";
|
|
26744
|
-
}
|
|
26745
|
-
};
|
|
26746
|
-
var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
26747
|
-
var MAX_REDIRECTS2 = 5;
|
|
26748
|
-
function allowLocal() {
|
|
26749
|
-
return process.env["CASCADE_ALLOW_LOCAL_FETCH"] === "1";
|
|
26750
|
-
}
|
|
26751
|
-
function isPrivateAddress(ip) {
|
|
26752
|
-
const type = net__default.default.isIP(ip);
|
|
26753
|
-
if (type === 4) return isPrivateIPv4(ip);
|
|
26754
|
-
if (type === 6) return isPrivateIPv6(ip);
|
|
26755
|
-
return true;
|
|
26756
|
-
}
|
|
26757
|
-
function isPrivateIPv4(ip) {
|
|
26758
|
-
const parts = ip.split(".").map((p) => Number(p));
|
|
26759
|
-
if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255)) return true;
|
|
26760
|
-
const [a, b] = parts;
|
|
26761
|
-
if (a === 0) return true;
|
|
26762
|
-
if (a === 10) return true;
|
|
26763
|
-
if (a === 127) return true;
|
|
26764
|
-
if (a === 169 && b === 254) return true;
|
|
26765
|
-
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
26766
|
-
if (a === 192 && b === 168) return true;
|
|
26767
|
-
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
26768
|
-
if (a >= 224) return true;
|
|
26769
|
-
return false;
|
|
26770
|
-
}
|
|
26771
|
-
function isPrivateIPv6(ip) {
|
|
26772
|
-
const lower = ip.toLowerCase().replace(/^\[|\]$/g, "");
|
|
26773
|
-
if (lower === "::1" || lower === "::") return true;
|
|
26774
|
-
if (lower.startsWith("fe80")) return true;
|
|
26775
|
-
if (lower.startsWith("fc") || lower.startsWith("fd")) return true;
|
|
26776
|
-
const mapped = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/.exec(lower);
|
|
26777
|
-
if (mapped) return isPrivateIPv4(mapped[1]);
|
|
26778
|
-
return false;
|
|
26779
|
-
}
|
|
26780
|
-
async function assertPublicUrl(rawUrl) {
|
|
26781
|
-
let url;
|
|
26782
|
-
try {
|
|
26783
|
-
url = new URL(rawUrl);
|
|
26784
|
-
} catch {
|
|
26785
|
-
throw new SsrfBlockedError(`Invalid URL: ${rawUrl}`);
|
|
26786
|
-
}
|
|
26787
|
-
if (!ALLOWED_PROTOCOLS.has(url.protocol)) {
|
|
26788
|
-
throw new SsrfBlockedError(`Blocked URL scheme "${url.protocol}" \u2014 only http and https are allowed.`);
|
|
26789
|
-
}
|
|
26790
|
-
if (allowLocal()) return url;
|
|
26791
|
-
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
26792
|
-
if (net__default.default.isIP(host)) {
|
|
26793
|
-
if (isPrivateAddress(host)) {
|
|
26794
|
-
throw new SsrfBlockedError(`Blocked request to non-public address ${host}.`);
|
|
26795
|
-
}
|
|
26796
|
-
return url;
|
|
26797
|
-
}
|
|
26798
|
-
if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) {
|
|
26799
|
-
throw new SsrfBlockedError(`Blocked request to local hostname "${host}".`);
|
|
26800
|
-
}
|
|
26801
|
-
let addresses;
|
|
26802
|
-
try {
|
|
26803
|
-
const records = await dns2__default.default.lookup(host, { all: true });
|
|
26804
|
-
addresses = records.map((r) => r.address);
|
|
26805
|
-
} catch {
|
|
26806
|
-
throw new SsrfBlockedError(`Could not resolve host "${host}".`);
|
|
26807
|
-
}
|
|
26808
|
-
if (addresses.length === 0 || addresses.some((addr) => isPrivateAddress(addr))) {
|
|
26809
|
-
throw new SsrfBlockedError(`Blocked request to "${host}" \u2014 resolves to a non-public address.`);
|
|
26810
|
-
}
|
|
26811
|
-
return url;
|
|
26812
|
-
}
|
|
26813
|
-
async function safeFetch(rawUrl, init = {}) {
|
|
26814
|
-
let currentUrl = (await assertPublicUrl(rawUrl)).toString();
|
|
26815
|
-
for (let i = 0; i <= MAX_REDIRECTS2; i++) {
|
|
26816
|
-
const resp = await fetch(currentUrl, { ...init, redirect: "manual" });
|
|
26817
|
-
if (resp.status < 300 || resp.status >= 400) return resp;
|
|
26818
|
-
const location = resp.headers.get("location");
|
|
26819
|
-
if (!location) return resp;
|
|
26820
|
-
const next = new URL(location, currentUrl);
|
|
26821
|
-
await assertPublicUrl(next.toString());
|
|
26822
|
-
currentUrl = next.toString();
|
|
26823
|
-
}
|
|
26824
|
-
throw new SsrfBlockedError(`Too many redirects (>${MAX_REDIRECTS2}).`);
|
|
26825
|
-
}
|
|
26826
27280
|
|
|
26827
27281
|
// src/tools/web-fetch.ts
|
|
26828
27282
|
var MAX_CHARS = 5e4;
|
|
@@ -27095,10 +27549,10 @@ var ToolRegistry = class extends EventEmitter__default.default {
|
|
|
27095
27549
|
}
|
|
27096
27550
|
isIgnored(filePath) {
|
|
27097
27551
|
if (!filePath) return false;
|
|
27098
|
-
const abs =
|
|
27099
|
-
const rel =
|
|
27100
|
-
if (!rel || rel.startsWith("..") ||
|
|
27101
|
-
const posixRel = rel.split(
|
|
27552
|
+
const abs = path28__default.default.resolve(this.workspaceRoot, filePath);
|
|
27553
|
+
const rel = path28__default.default.relative(this.workspaceRoot, abs);
|
|
27554
|
+
if (!rel || rel.startsWith("..") || path28__default.default.isAbsolute(rel)) return true;
|
|
27555
|
+
const posixRel = rel.split(path28__default.default.sep).join("/");
|
|
27102
27556
|
return this.ignoreMatcher.ignores(posixRel);
|
|
27103
27557
|
}
|
|
27104
27558
|
};
|
|
@@ -27332,22 +27786,22 @@ var FileMcpOAuthStore = class {
|
|
|
27332
27786
|
filePath;
|
|
27333
27787
|
load() {
|
|
27334
27788
|
try {
|
|
27335
|
-
return JSON.parse(
|
|
27789
|
+
return JSON.parse(fs27__default.default.readFileSync(this.filePath, "utf-8"));
|
|
27336
27790
|
} catch {
|
|
27337
27791
|
return void 0;
|
|
27338
27792
|
}
|
|
27339
27793
|
}
|
|
27340
27794
|
save(state) {
|
|
27341
|
-
|
|
27342
|
-
|
|
27795
|
+
fs27__default.default.mkdirSync(path28__default.default.dirname(this.filePath), { recursive: true, mode: 448 });
|
|
27796
|
+
fs27__default.default.writeFileSync(this.filePath, JSON.stringify(state, null, 2), { mode: 384 });
|
|
27343
27797
|
try {
|
|
27344
|
-
|
|
27798
|
+
fs27__default.default.chmodSync(this.filePath, 384);
|
|
27345
27799
|
} catch {
|
|
27346
27800
|
}
|
|
27347
27801
|
}
|
|
27348
27802
|
clear() {
|
|
27349
27803
|
try {
|
|
27350
|
-
|
|
27804
|
+
fs27__default.default.rmSync(this.filePath);
|
|
27351
27805
|
} catch {
|
|
27352
27806
|
}
|
|
27353
27807
|
}
|
|
@@ -28185,7 +28639,7 @@ var TaskAnalyzer = class {
|
|
|
28185
28639
|
analysisCache.clear();
|
|
28186
28640
|
}
|
|
28187
28641
|
};
|
|
28188
|
-
var DEFAULT_STATS_FILE =
|
|
28642
|
+
var DEFAULT_STATS_FILE = path28__default.default.join(os6__default.default.homedir(), ".cascade", "model-perf.json");
|
|
28189
28643
|
var ModelPerformanceTracker = class _ModelPerformanceTracker {
|
|
28190
28644
|
stats = /* @__PURE__ */ new Map();
|
|
28191
28645
|
featureStats = /* @__PURE__ */ new Map();
|
|
@@ -28225,7 +28679,7 @@ var ModelPerformanceTracker = class _ModelPerformanceTracker {
|
|
|
28225
28679
|
async save() {
|
|
28226
28680
|
if (this.readOnly) return;
|
|
28227
28681
|
try {
|
|
28228
|
-
await fs11__default.default.mkdir(
|
|
28682
|
+
await fs11__default.default.mkdir(path28__default.default.dirname(this.statsFile), { recursive: true });
|
|
28229
28683
|
const modelsObj = {};
|
|
28230
28684
|
const featuresObj = {};
|
|
28231
28685
|
for (const [key, stat] of this.stats) modelsObj[key] = stat;
|
|
@@ -28858,7 +29312,7 @@ Required capability: ${description.slice(0, 300)}`;
|
|
|
28858
29312
|
* any dangerous action, so a silently-reloaded tool can't act without approval. */
|
|
28859
29313
|
async loadPersistedTools() {
|
|
28860
29314
|
if (!this.workspacePath || !this.persistEnabled) return;
|
|
28861
|
-
const file =
|
|
29315
|
+
const file = path28__default.default.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
|
|
28862
29316
|
try {
|
|
28863
29317
|
const raw = await fs11__default.default.readFile(file, "utf-8");
|
|
28864
29318
|
const specs = JSON.parse(raw);
|
|
@@ -28882,8 +29336,8 @@ Required capability: ${description.slice(0, 300)}`;
|
|
|
28882
29336
|
}
|
|
28883
29337
|
async persist() {
|
|
28884
29338
|
if (!this.workspacePath || !this.persistEnabled) return;
|
|
28885
|
-
const dir =
|
|
28886
|
-
const file =
|
|
29339
|
+
const dir = path28__default.default.join(this.workspacePath, ".cascade");
|
|
29340
|
+
const file = path28__default.default.join(dir, DYNAMIC_TOOLS_FILE);
|
|
28887
29341
|
try {
|
|
28888
29342
|
await fs11__default.default.mkdir(dir, { recursive: true });
|
|
28889
29343
|
await fs11__default.default.writeFile(file, JSON.stringify(Array.from(this.specs.values()), null, 2), "utf-8");
|
|
@@ -28903,12 +29357,12 @@ var WorldStateDB = class {
|
|
|
28903
29357
|
constructor(workspacePath, debugMode = false) {
|
|
28904
29358
|
this.workspacePath = workspacePath;
|
|
28905
29359
|
this.debugMode = debugMode;
|
|
28906
|
-
const cascadeDir =
|
|
28907
|
-
if (!
|
|
28908
|
-
|
|
29360
|
+
const cascadeDir = path28__default.default.join(workspacePath, ".cascade");
|
|
29361
|
+
if (!fs27__default.default.existsSync(cascadeDir)) {
|
|
29362
|
+
fs27__default.default.mkdirSync(cascadeDir, { recursive: true });
|
|
28909
29363
|
}
|
|
28910
|
-
this.keyPath =
|
|
28911
|
-
this.dbPath =
|
|
29364
|
+
this.keyPath = path28__default.default.join(cascadeDir, "world_state.key");
|
|
29365
|
+
this.dbPath = path28__default.default.join(cascadeDir, "world_state.db");
|
|
28912
29366
|
this.initEncryptionKey();
|
|
28913
29367
|
this.db = new Database2__default.default(this.dbPath);
|
|
28914
29368
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -28965,11 +29419,11 @@ var WorldStateDB = class {
|
|
|
28965
29419
|
).run(e, r, row.encrypted_value, row.source_worker, row.timestamp, validTo, change);
|
|
28966
29420
|
}
|
|
28967
29421
|
initEncryptionKey() {
|
|
28968
|
-
if (
|
|
28969
|
-
this.encryptionKey =
|
|
29422
|
+
if (fs27__default.default.existsSync(this.keyPath)) {
|
|
29423
|
+
this.encryptionKey = fs27__default.default.readFileSync(this.keyPath);
|
|
28970
29424
|
} else {
|
|
28971
29425
|
this.encryptionKey = crypto__default.default.randomBytes(32);
|
|
28972
|
-
|
|
29426
|
+
fs27__default.default.writeFileSync(this.keyPath, this.encryptionKey);
|
|
28973
29427
|
}
|
|
28974
29428
|
}
|
|
28975
29429
|
encrypt(text) {
|
|
@@ -29255,9 +29709,9 @@ var WorldStateDB = class {
|
|
|
29255
29709
|
dumpDebugIfNeeded() {
|
|
29256
29710
|
if (!this.debugMode) return;
|
|
29257
29711
|
try {
|
|
29258
|
-
const dumpPath =
|
|
29712
|
+
const dumpPath = path28__default.default.join(os6__default.default.tmpdir(), "cascade_world_state_debug.json");
|
|
29259
29713
|
const entries = this.getAllEntries();
|
|
29260
|
-
|
|
29714
|
+
fs27__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
29261
29715
|
} catch (err) {
|
|
29262
29716
|
console.error("Failed to dump debug world state", err);
|
|
29263
29717
|
}
|
|
@@ -29313,11 +29767,11 @@ var ResumeStore = class {
|
|
|
29313
29767
|
version: RESUME_SCHEMA_VERSION,
|
|
29314
29768
|
createdAt: checkpoint.createdAt ?? new Date(timestamp).toISOString()
|
|
29315
29769
|
};
|
|
29316
|
-
await
|
|
29317
|
-
const finalPath =
|
|
29770
|
+
await fs27.promises.mkdir(this.dir, { recursive: true });
|
|
29771
|
+
const finalPath = path28__default.default.join(this.dir, `${timestamp}-${safeId(record.taskId)}.json`);
|
|
29318
29772
|
const tempPath = `${finalPath}.${process.pid}.tmp`;
|
|
29319
|
-
await
|
|
29320
|
-
await
|
|
29773
|
+
await fs27.promises.writeFile(tempPath, JSON.stringify(record, null, 2), "utf-8");
|
|
29774
|
+
await fs27.promises.rename(tempPath, finalPath);
|
|
29321
29775
|
await this.prune();
|
|
29322
29776
|
return true;
|
|
29323
29777
|
} catch {
|
|
@@ -29328,7 +29782,7 @@ var ResumeStore = class {
|
|
|
29328
29782
|
async list() {
|
|
29329
29783
|
let entries;
|
|
29330
29784
|
try {
|
|
29331
|
-
entries = await
|
|
29785
|
+
entries = await fs27.promises.readdir(this.dir);
|
|
29332
29786
|
} catch {
|
|
29333
29787
|
return [];
|
|
29334
29788
|
}
|
|
@@ -29337,7 +29791,7 @@ var ResumeStore = class {
|
|
|
29337
29791
|
const checkpoints = [];
|
|
29338
29792
|
for (const candidate of candidates) {
|
|
29339
29793
|
try {
|
|
29340
|
-
const raw = await
|
|
29794
|
+
const raw = await fs27.promises.readFile(path28__default.default.join(this.dir, candidate.name), "utf-8");
|
|
29341
29795
|
const parsed = JSON.parse(raw);
|
|
29342
29796
|
if (isCheckpoint(parsed)) checkpoints.push(parsed);
|
|
29343
29797
|
} catch {
|
|
@@ -29371,12 +29825,12 @@ var ResumeStore = class {
|
|
|
29371
29825
|
const target = taskId ? all.find((c) => c.taskId === taskId) : all[0];
|
|
29372
29826
|
if (!target) return null;
|
|
29373
29827
|
try {
|
|
29374
|
-
const entries = await
|
|
29828
|
+
const entries = await fs27.promises.readdir(this.dir);
|
|
29375
29829
|
const wanted = safeId(target.taskId);
|
|
29376
29830
|
const source = entries.find((name) => FILE_PATTERN.exec(name)?.[2] === wanted);
|
|
29377
29831
|
if (!source) return null;
|
|
29378
29832
|
const claimId = `${this.now()}-${safeId(target.taskId)}`;
|
|
29379
|
-
await
|
|
29833
|
+
await fs27.promises.rename(path28__default.default.join(this.dir, source), path28__default.default.join(this.dir, `${claimId}${CLAIM_SUFFIX}`));
|
|
29380
29834
|
return { checkpoint: target, claimId };
|
|
29381
29835
|
} catch {
|
|
29382
29836
|
return null;
|
|
@@ -29385,9 +29839,9 @@ var ResumeStore = class {
|
|
|
29385
29839
|
/** Give a claim back, so the work stays recoverable after a failed start. */
|
|
29386
29840
|
async release(claimId) {
|
|
29387
29841
|
try {
|
|
29388
|
-
await
|
|
29389
|
-
|
|
29390
|
-
|
|
29842
|
+
await fs27.promises.rename(
|
|
29843
|
+
path28__default.default.join(this.dir, `${claimId}${CLAIM_SUFFIX}`),
|
|
29844
|
+
path28__default.default.join(this.dir, `${claimId}.json`)
|
|
29391
29845
|
);
|
|
29392
29846
|
} catch {
|
|
29393
29847
|
}
|
|
@@ -29395,7 +29849,7 @@ var ResumeStore = class {
|
|
|
29395
29849
|
/** Discard a claim for good — the resumed run has taken ownership. */
|
|
29396
29850
|
async settle(claimId) {
|
|
29397
29851
|
try {
|
|
29398
|
-
await
|
|
29852
|
+
await fs27.promises.rm(path28__default.default.join(this.dir, `${claimId}${CLAIM_SUFFIX}`), { force: true });
|
|
29399
29853
|
} catch {
|
|
29400
29854
|
}
|
|
29401
29855
|
}
|
|
@@ -29417,15 +29871,15 @@ var ResumeStore = class {
|
|
|
29417
29871
|
async reclaimStale() {
|
|
29418
29872
|
let reclaimed = 0;
|
|
29419
29873
|
try {
|
|
29420
|
-
const entries = await
|
|
29874
|
+
const entries = await fs27.promises.readdir(this.dir);
|
|
29421
29875
|
const cutoff = this.now() - this.claimTimeoutMs;
|
|
29422
29876
|
for (const name of entries) {
|
|
29423
29877
|
if (!name.endsWith(CLAIM_SUFFIX)) continue;
|
|
29424
29878
|
const stamp2 = Number(/^(\d+)-/.exec(name)?.[1]);
|
|
29425
29879
|
if (!Number.isFinite(stamp2) || stamp2 >= cutoff) continue;
|
|
29426
|
-
await
|
|
29427
|
-
|
|
29428
|
-
|
|
29880
|
+
await fs27.promises.rename(
|
|
29881
|
+
path28__default.default.join(this.dir, name),
|
|
29882
|
+
path28__default.default.join(this.dir, `${name.slice(0, -CLAIM_SUFFIX.length)}.json`)
|
|
29429
29883
|
);
|
|
29430
29884
|
reclaimed++;
|
|
29431
29885
|
}
|
|
@@ -29436,12 +29890,12 @@ var ResumeStore = class {
|
|
|
29436
29890
|
/** Remove every checkpoint for a task id. */
|
|
29437
29891
|
async delete(taskId) {
|
|
29438
29892
|
try {
|
|
29439
|
-
const entries = await
|
|
29893
|
+
const entries = await fs27.promises.readdir(this.dir);
|
|
29440
29894
|
const wanted = safeId(taskId);
|
|
29441
29895
|
await Promise.all(entries.map(async (name) => {
|
|
29442
29896
|
const match = FILE_PATTERN.exec(name);
|
|
29443
29897
|
if (match?.[2] === wanted) {
|
|
29444
|
-
await
|
|
29898
|
+
await fs27.promises.rm(path28__default.default.join(this.dir, name), { force: true });
|
|
29445
29899
|
}
|
|
29446
29900
|
}));
|
|
29447
29901
|
} catch {
|
|
@@ -29450,13 +29904,13 @@ var ResumeStore = class {
|
|
|
29450
29904
|
/** Drop expired checkpoints, then any beyond the newest `maxCheckpoints`. */
|
|
29451
29905
|
async prune() {
|
|
29452
29906
|
try {
|
|
29453
|
-
const entries = await
|
|
29907
|
+
const entries = await fs27.promises.readdir(this.dir);
|
|
29454
29908
|
const cutoff = this.now() - this.maxAgeMs;
|
|
29455
29909
|
const files = entries.map((name) => ({ name, match: FILE_PATTERN.exec(name) })).filter((e) => e.match !== null).map((e) => ({ name: e.name, timestamp: Number(e.match[1]) })).sort((a, b) => b.timestamp - a.timestamp);
|
|
29456
29910
|
const doomed = files.filter((f, index) => f.timestamp < cutoff || index >= this.maxCheckpoints);
|
|
29457
29911
|
const staleTemps = entries.filter((name) => name.endsWith(".tmp")).filter((name) => (Number(FILE_PATTERN.exec(name.replace(/\.\d+\.tmp$/, ".json"))?.[1]) || 0) < cutoff);
|
|
29458
29912
|
await Promise.all([...doomed.map((f) => f.name), ...staleTemps].map(
|
|
29459
|
-
(name) =>
|
|
29913
|
+
(name) => fs27.promises.rm(path28__default.default.join(this.dir, name), { force: true })
|
|
29460
29914
|
));
|
|
29461
29915
|
} catch {
|
|
29462
29916
|
}
|
|
@@ -29833,7 +30287,7 @@ var WorkspaceIndex = class {
|
|
|
29833
30287
|
isIgnored;
|
|
29834
30288
|
chunker;
|
|
29835
30289
|
constructor(opts) {
|
|
29836
|
-
this.root =
|
|
30290
|
+
this.root = path28__default.default.resolve(opts.root);
|
|
29837
30291
|
this.db = opts.db;
|
|
29838
30292
|
this.namespace = opts.namespace ?? "code";
|
|
29839
30293
|
this.extensions = new Set((opts.extensions ?? DEFAULT_EXTENSIONS).map((e) => e.replace(/^\./, "").toLowerCase()));
|
|
@@ -29865,14 +30319,14 @@ var WorkspaceIndex = class {
|
|
|
29865
30319
|
return;
|
|
29866
30320
|
}
|
|
29867
30321
|
for (const entry of entries) {
|
|
29868
|
-
const abs =
|
|
30322
|
+
const abs = path28__default.default.join(dir, entry.name);
|
|
29869
30323
|
if (entry.isDirectory()) {
|
|
29870
30324
|
if (HARD_SKIP_DIRS.has(entry.name) || this.isIgnored(abs)) continue;
|
|
29871
30325
|
await walk(abs);
|
|
29872
30326
|
} else if (entry.isFile()) {
|
|
29873
|
-
const ext =
|
|
30327
|
+
const ext = path28__default.default.extname(entry.name).slice(1).toLowerCase();
|
|
29874
30328
|
if (!this.extensions.has(ext) || this.isIgnored(abs)) continue;
|
|
29875
|
-
out.push(
|
|
30329
|
+
out.push(path28__default.default.relative(this.root, abs));
|
|
29876
30330
|
}
|
|
29877
30331
|
}
|
|
29878
30332
|
};
|
|
@@ -29902,7 +30356,7 @@ var WorkspaceIndex = class {
|
|
|
29902
30356
|
const entries = [];
|
|
29903
30357
|
for (const rel of rels) {
|
|
29904
30358
|
try {
|
|
29905
|
-
const abs =
|
|
30359
|
+
const abs = path28__default.default.join(this.root, rel);
|
|
29906
30360
|
const stat = await fs11__default.default.stat(abs);
|
|
29907
30361
|
if (stat.size > this.maxFileBytes) continue;
|
|
29908
30362
|
const buf = await fs11__default.default.readFile(abs);
|
|
@@ -30307,6 +30761,127 @@ var CurrentPageTool = class extends BaseTool {
|
|
|
30307
30761
|
}
|
|
30308
30762
|
};
|
|
30309
30763
|
|
|
30764
|
+
// src/tools/browser-control.ts
|
|
30765
|
+
var BrowserControlTool = class extends BaseTool {
|
|
30766
|
+
name = "browser_control";
|
|
30767
|
+
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.";
|
|
30768
|
+
inputSchema = {
|
|
30769
|
+
type: "object",
|
|
30770
|
+
properties: {
|
|
30771
|
+
action: {
|
|
30772
|
+
type: "string",
|
|
30773
|
+
enum: ["navigate", "click", "fill", "press", "wait_for", "extract_text"],
|
|
30774
|
+
description: "What to do."
|
|
30775
|
+
},
|
|
30776
|
+
url: { type: "string", description: "For navigate: the http(s) address to open." },
|
|
30777
|
+
selector: {
|
|
30778
|
+
type: "string",
|
|
30779
|
+
description: "CSS selector for click, fill and wait_for. Optional for extract_text (defaults to the whole page)."
|
|
30780
|
+
},
|
|
30781
|
+
value: { type: "string", description: "For fill: the text to type." },
|
|
30782
|
+
key: { type: "string", description: 'For press: a key name such as "Enter", "Tab" or "Escape".' },
|
|
30783
|
+
timeoutMs: { type: "number", description: "For wait_for: how long to wait before giving up (default 10000, max 30000)." }
|
|
30784
|
+
},
|
|
30785
|
+
required: ["action"]
|
|
30786
|
+
};
|
|
30787
|
+
controller;
|
|
30788
|
+
release;
|
|
30789
|
+
/** One-way. See `revoke`. */
|
|
30790
|
+
revoked = false;
|
|
30791
|
+
constructor(controller, release) {
|
|
30792
|
+
super();
|
|
30793
|
+
this.controller = controller;
|
|
30794
|
+
this.release = release;
|
|
30795
|
+
}
|
|
30796
|
+
/**
|
|
30797
|
+
* A worker has finished. Ends its hold on the browser.
|
|
30798
|
+
*
|
|
30799
|
+
* Called from the tier's terminal path rather than by the model, because a
|
|
30800
|
+
* model cannot be relied on to announce that it is done — which is why the
|
|
30801
|
+
* host cannot use idle time as the boundary instead.
|
|
30802
|
+
*/
|
|
30803
|
+
releaseActor(actorId) {
|
|
30804
|
+
this.release?.(actorId);
|
|
30805
|
+
}
|
|
30806
|
+
/**
|
|
30807
|
+
* Permanently disable this tool, whatever the registry still holds.
|
|
30808
|
+
*
|
|
30809
|
+
* Registration-time gates cannot express "this run turned out to be
|
|
30810
|
+
* unattended": a host that wires the controller and only then declares the
|
|
30811
|
+
* run unattended has already registered the tool, and a check that ran at
|
|
30812
|
+
* registration has no second chance. Enforcing at EXECUTION time makes the
|
|
30813
|
+
* guarantee independent of call order, which is the whole point of stating
|
|
30814
|
+
* the invariant rather than relying on the scheduler's current sequence.
|
|
30815
|
+
*
|
|
30816
|
+
* One-way on purpose. A safety gate that can be re-opened by a later call is
|
|
30817
|
+
* a gate that a refactor re-opens by accident.
|
|
30818
|
+
*/
|
|
30819
|
+
revoke() {
|
|
30820
|
+
this.revoked = true;
|
|
30821
|
+
}
|
|
30822
|
+
// Acting on a session the user is signed into is the highest-consequence
|
|
30823
|
+
// thing in the tool set — higher than shell, arguably, because the blast
|
|
30824
|
+
// radius is someone else's server rather than this machine.
|
|
30825
|
+
isDangerous() {
|
|
30826
|
+
return true;
|
|
30827
|
+
}
|
|
30828
|
+
async execute(input, options) {
|
|
30829
|
+
if (this.revoked) {
|
|
30830
|
+
return "Error: browser control is not available in this run. It requires a person watching who can stop it, and nobody is.";
|
|
30831
|
+
}
|
|
30832
|
+
const kind = input["action"];
|
|
30833
|
+
if (!kind) return "Error: action is required.";
|
|
30834
|
+
if (options?.signal?.aborted) return "Error: the run was cancelled before this action ran.";
|
|
30835
|
+
const missing = requiredFieldFor(kind, input);
|
|
30836
|
+
if (missing) return `Error: "${kind}" needs ${missing}.`;
|
|
30837
|
+
const action = {
|
|
30838
|
+
kind,
|
|
30839
|
+
...typeof input["url"] === "string" ? { url: input["url"] } : {},
|
|
30840
|
+
...typeof input["selector"] === "string" ? { selector: input["selector"] } : {},
|
|
30841
|
+
...typeof input["value"] === "string" ? { value: input["value"] } : {},
|
|
30842
|
+
...typeof input["key"] === "string" ? { key: input["key"] } : {},
|
|
30843
|
+
...typeof input["timeoutMs"] === "number" ? { timeoutMs: Math.min(Math.max(input["timeoutMs"], 0), 3e4) } : {}
|
|
30844
|
+
};
|
|
30845
|
+
let outcome;
|
|
30846
|
+
try {
|
|
30847
|
+
outcome = await this.controller(action, {
|
|
30848
|
+
// `sessionId` is the run. The host scopes both revocation and its
|
|
30849
|
+
// single-owner lease on it, so passing it is not telemetry — drop it
|
|
30850
|
+
// and a Stop in one run silently stops every later one.
|
|
30851
|
+
sessionId: options?.sessionId ?? "",
|
|
30852
|
+
actorId: options?.tierId ?? options?.sessionId ?? "",
|
|
30853
|
+
...options?.signal ? { signal: options.signal } : {}
|
|
30854
|
+
});
|
|
30855
|
+
} catch (err) {
|
|
30856
|
+
return `Error: the browser could not perform "${kind}" \u2014 ${err instanceof Error ? err.message : String(err)}`;
|
|
30857
|
+
}
|
|
30858
|
+
const where = outcome.url ? `
|
|
30859
|
+
Page: ${outcome.title ? `${outcome.title} \u2014 ` : ""}${outcome.url}` : "";
|
|
30860
|
+
return `${outcome.ok ? "" : "Failed: "}${outcome.detail}${where}`;
|
|
30861
|
+
}
|
|
30862
|
+
};
|
|
30863
|
+
function requiredFieldFor(kind, input) {
|
|
30864
|
+
const has = (k) => typeof input[k] === "string" && input[k].length > 0;
|
|
30865
|
+
const given = (k) => typeof input[k] === "string";
|
|
30866
|
+
switch (kind) {
|
|
30867
|
+
case "navigate":
|
|
30868
|
+
return has("url") ? null : "a url";
|
|
30869
|
+
case "click":
|
|
30870
|
+
return has("selector") ? null : "a selector";
|
|
30871
|
+
case "fill":
|
|
30872
|
+
return has("selector") ? given("value") ? null : "a value" : "a selector";
|
|
30873
|
+
case "press":
|
|
30874
|
+
return has("key") ? null : "a key";
|
|
30875
|
+
case "wait_for":
|
|
30876
|
+
return has("selector") ? null : "a selector";
|
|
30877
|
+
// extract_text works with or without a selector.
|
|
30878
|
+
case "extract_text":
|
|
30879
|
+
return null;
|
|
30880
|
+
default:
|
|
30881
|
+
return null;
|
|
30882
|
+
}
|
|
30883
|
+
}
|
|
30884
|
+
|
|
30310
30885
|
// src/core/cascade.ts
|
|
30311
30886
|
var ESCALATION_DECISION_TIMEOUT_MS = 5 * 6e4;
|
|
30312
30887
|
function buildContextualPrompt(prompt, history = []) {
|
|
@@ -30392,13 +30967,13 @@ var Cascade = class _Cascade extends EventEmitter__default.default {
|
|
|
30392
30967
|
this.toolRegistry = new ToolRegistry(this.config.tools, workspacePath);
|
|
30393
30968
|
try {
|
|
30394
30969
|
this.router.setDeadModelStore(new DeadModelStore(
|
|
30395
|
-
fileDeadModelPersistence(
|
|
30970
|
+
fileDeadModelPersistence(path28__default.default.join(workspacePath, ".cascade", "dead-models.json"))
|
|
30396
30971
|
));
|
|
30397
30972
|
} catch {
|
|
30398
30973
|
}
|
|
30399
30974
|
try {
|
|
30400
30975
|
this.resumeStore = new ResumeStore({
|
|
30401
|
-
dir:
|
|
30976
|
+
dir: path28__default.default.join(workspacePath, ".cascade", "resume")
|
|
30402
30977
|
});
|
|
30403
30978
|
} catch {
|
|
30404
30979
|
}
|
|
@@ -30486,8 +31061,8 @@ var Cascade = class _Cascade extends EventEmitter__default.default {
|
|
|
30486
31061
|
this.emit("log", { level: "warn", message: "Code index enabled but no embeddings-capable provider is configured \u2014 skipping." });
|
|
30487
31062
|
return;
|
|
30488
31063
|
}
|
|
30489
|
-
const dbPath = ci.dbPath ||
|
|
30490
|
-
|
|
31064
|
+
const dbPath = ci.dbPath || path28__default.default.join(this.workspacePath, ".cascade", "code-index.db");
|
|
31065
|
+
fs27__default.default.mkdirSync(path28__default.default.dirname(dbPath), { recursive: true });
|
|
30491
31066
|
const db = new Database2__default.default(dbPath);
|
|
30492
31067
|
db.pragma("journal_mode = WAL");
|
|
30493
31068
|
const complete = chatCompleterFromProviders(this.config.providers);
|
|
@@ -30799,6 +31374,49 @@ ${input}` }],
|
|
|
30799
31374
|
if ((this.config.tools?.disabledTools ?? []).includes("read_current_page")) return;
|
|
30800
31375
|
this.toolRegistry.register(new CurrentPageTool(provider));
|
|
30801
31376
|
}
|
|
31377
|
+
/**
|
|
31378
|
+
* True when nobody is watching this run — a scheduled/cron task, which
|
|
31379
|
+
* auto-approves every tool because there is no one to ask.
|
|
31380
|
+
*
|
|
31381
|
+
* That auto-approval is fine for ordinary tools and disqualifying for this
|
|
31382
|
+
* one: `browser_control` drives the signed-in browser, and "every run asks
|
|
31383
|
+
* first" is the promise the Settings copy makes. The property used to hold
|
|
31384
|
+
* only because `runScheduledTask` happens not to call `setBrowserController`
|
|
31385
|
+
* — a guarantee resting on an ABSENT call, which is exactly what a later
|
|
31386
|
+
* refactor adds without noticing. Stated and enforced instead.
|
|
31387
|
+
*/
|
|
31388
|
+
unattended = false;
|
|
31389
|
+
/**
|
|
31390
|
+
* Declare that no human is watching this run, so capabilities that depend on
|
|
31391
|
+
* a person being able to see and stop them are refused outright.
|
|
31392
|
+
*/
|
|
31393
|
+
setUnattended(on) {
|
|
31394
|
+
this.unattended = on;
|
|
31395
|
+
if (!on) return;
|
|
31396
|
+
const tool = this.toolRegistry.getTool("browser_control");
|
|
31397
|
+
tool?.revoke?.();
|
|
31398
|
+
}
|
|
31399
|
+
/**
|
|
31400
|
+
* Wire the host's browser so a run can ACT on the open page, not just read it.
|
|
31401
|
+
*
|
|
31402
|
+
* Two gates before this registers anything, and both matter:
|
|
31403
|
+
*
|
|
31404
|
+
* - `tools.agentBrowserControl` must be on. Unlike read_current_page, which
|
|
31405
|
+
* reaches nothing the user has not already opened themselves, this drives
|
|
31406
|
+
* the session they are signed into. That is opt-in, not a default.
|
|
31407
|
+
* - `disabledTools` still removes it, the same as every other tool.
|
|
31408
|
+
* - The run must not be UNATTENDED. See `setUnattended`.
|
|
31409
|
+
*
|
|
31410
|
+
* Host-supplied like the page provider: the tool exists only where there is a
|
|
31411
|
+
* real browser to drive, so it never has to be refused in the CLI or a hosted
|
|
31412
|
+
* run — it is simply not there.
|
|
31413
|
+
*/
|
|
31414
|
+
setBrowserController(controller, release) {
|
|
31415
|
+
if (this.unattended) return;
|
|
31416
|
+
if (!this.config.tools?.agentBrowserControl) return;
|
|
31417
|
+
if ((this.config.tools?.disabledTools ?? []).includes("browser_control")) return;
|
|
31418
|
+
this.toolRegistry.register(new BrowserControlTool(controller, release));
|
|
31419
|
+
}
|
|
30802
31420
|
registerMediaTools(workspacePath) {
|
|
30803
31421
|
const denied = new Set(this.config.tools?.disabledTools ?? []);
|
|
30804
31422
|
const permitted = (name) => !denied.has(name);
|
|
@@ -31404,6 +32022,7 @@ ${prompt}` : prompt;
|
|
|
31404
32022
|
this.router.setRunSignal(options.signal);
|
|
31405
32023
|
const startMs = Date.now();
|
|
31406
32024
|
const taskId = crypto.randomUUID();
|
|
32025
|
+
this.emit("run:started", { taskId });
|
|
31407
32026
|
this.decisionLog = [];
|
|
31408
32027
|
this.currentRunCompleted = [];
|
|
31409
32028
|
this.wroteCheckpointThisRun = false;
|
|
@@ -32169,6 +32788,7 @@ function applySettingsPayload(config, data) {
|
|
|
32169
32788
|
if (["auto", "T1", "T2", "T3"].includes(a["forceTier"])) config.routing = { ...config.routing ?? {}, forceTier: a["forceTier"] };
|
|
32170
32789
|
if (typeof a["benchmarksLive"] === "boolean") config.benchmarks = { ...config.benchmarks ?? {}, live: a["benchmarksLive"] };
|
|
32171
32790
|
if (["isolate", "worker", "auto"].includes(a["dynamicToolSandbox"])) config.tools = { ...config.tools ?? {}, dynamicToolSandbox: a["dynamicToolSandbox"] };
|
|
32791
|
+
if (typeof a["agentBrowserControl"] === "boolean") config.tools = { ...config.tools ?? {}, agentBrowserControl: a["agentBrowserControl"] };
|
|
32172
32792
|
if (typeof a["factsExtraction"] === "boolean") config.knowledge = { ...config.knowledge ?? {}, factsExtraction: a["factsExtraction"] };
|
|
32173
32793
|
if (typeof a["rememberSessions"] === "boolean") config.memory = { ...config.memory ?? {}, rememberSessions: a["rememberSessions"] };
|
|
32174
32794
|
if (typeof a["enableToolCreation"] === "boolean") config.enableToolCreation = a["enableToolCreation"];
|
|
@@ -32226,6 +32846,7 @@ function settingsSnapshot(config) {
|
|
|
32226
32846
|
forceTier: config.routing?.forceTier,
|
|
32227
32847
|
benchmarksLive: config.benchmarks?.live,
|
|
32228
32848
|
dynamicToolSandbox: config.tools?.dynamicToolSandbox,
|
|
32849
|
+
agentBrowserControl: config.tools?.agentBrowserControl,
|
|
32229
32850
|
factsExtraction: config.knowledge?.factsExtraction,
|
|
32230
32851
|
rememberSessions: config.memory?.rememberSessions,
|
|
32231
32852
|
enableToolCreation: config.enableToolCreation,
|
|
@@ -32261,8 +32882,8 @@ function replaceInPlace(target, source) {
|
|
|
32261
32882
|
}
|
|
32262
32883
|
function writeConfigFile(configPath, config) {
|
|
32263
32884
|
try {
|
|
32264
|
-
|
|
32265
|
-
|
|
32885
|
+
fs27__default.default.mkdirSync(path28__default.default.dirname(configPath), { recursive: true });
|
|
32886
|
+
fs27__default.default.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
32266
32887
|
return { ok: true };
|
|
32267
32888
|
} catch (err) {
|
|
32268
32889
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -32672,7 +33293,7 @@ function aggregateCostStats(sessions, opts = {}) {
|
|
|
32672
33293
|
}
|
|
32673
33294
|
|
|
32674
33295
|
// src/dashboard/server.ts
|
|
32675
|
-
var __dirname$1 =
|
|
33296
|
+
var __dirname$1 = path28__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
|
|
32676
33297
|
var ORPHANED_ESCALATION_GRACE_MS = 3e4;
|
|
32677
33298
|
var MASK = "***";
|
|
32678
33299
|
function masked(key, value) {
|
|
@@ -32730,6 +33351,14 @@ var DashboardServer = class {
|
|
|
32730
33351
|
* always failing.
|
|
32731
33352
|
*/
|
|
32732
33353
|
currentPageProvider;
|
|
33354
|
+
/**
|
|
33355
|
+
* Supplied by the desktop shell so a run can ACT on that page — click, type,
|
|
33356
|
+
* submit — not just read it. Same reasoning as the provider above: absent in
|
|
33357
|
+
* the CLI, so the tool is not registered rather than present and failing.
|
|
33358
|
+
* Cascade applies the `tools.agentBrowserControl` gate on top of this.
|
|
33359
|
+
*/
|
|
33360
|
+
browserController;
|
|
33361
|
+
browserActorRelease;
|
|
32733
33362
|
activeControllers = /* @__PURE__ */ new Map();
|
|
32734
33363
|
/**
|
|
32735
33364
|
* Run taskIds per chat session — file snapshots are keyed by the run's
|
|
@@ -32791,6 +33420,7 @@ var DashboardServer = class {
|
|
|
32791
33420
|
});
|
|
32792
33421
|
this.socket.onConfigUpdate(async (data) => {
|
|
32793
33422
|
const result = await commitSettings(this.config, data, (committed) => this.persistConfig(committed));
|
|
33423
|
+
if (result.ok) this.settingsChangeHook?.(this.config);
|
|
32794
33424
|
const refused = result.refused.map((r) => ({
|
|
32795
33425
|
type: r.type,
|
|
32796
33426
|
reason: r.reason,
|
|
@@ -32814,6 +33444,7 @@ var DashboardServer = class {
|
|
|
32814
33444
|
const cascade = new Cascade(cfg, this.workspacePath, this.store);
|
|
32815
33445
|
this.activeSessions.set(sessionId, cascade);
|
|
32816
33446
|
if (this.currentPageProvider) cascade.setCurrentPageProvider(this.currentPageProvider);
|
|
33447
|
+
if (this.browserController) cascade.setBrowserController(this.browserController, this.browserActorRelease);
|
|
32817
33448
|
cascade.on("stream:token", (e) => {
|
|
32818
33449
|
this.emitToSessionClients(socketId, sessionId, "stream:token", { sessionId, tierId: e.tierId, text: e.text, primary: e.primary });
|
|
32819
33450
|
});
|
|
@@ -32837,13 +33468,16 @@ var DashboardServer = class {
|
|
|
32837
33468
|
if (ev.requestId) this.clearPendingEscalation(ev.requestId);
|
|
32838
33469
|
this.emitToSessionClients(socketId, sessionId, "escalation:timeout", { sessionId, ...e });
|
|
32839
33470
|
});
|
|
33471
|
+
cascade.on("run:started", (e) => {
|
|
33472
|
+
const id = e.taskId;
|
|
33473
|
+
if (id) this.recordSessionTask(sessionId, id);
|
|
33474
|
+
});
|
|
32840
33475
|
try {
|
|
32841
33476
|
const result = await cascade.run({
|
|
32842
33477
|
prompt: runPrompt,
|
|
32843
33478
|
signal: abortController.signal,
|
|
32844
33479
|
approvalCallback: this.makeApprovalCallback(sessionId)
|
|
32845
33480
|
});
|
|
32846
|
-
this.recordSessionTask(sessionId, result.taskId);
|
|
32847
33481
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED", result);
|
|
32848
33482
|
this.captureWhy(sessionId, cascade, result);
|
|
32849
33483
|
this.emitToSessionClients(socketId, sessionId, "session:complete", { sessionId, result });
|
|
@@ -32909,6 +33543,49 @@ var DashboardServer = class {
|
|
|
32909
33543
|
setCurrentPageProvider(provider) {
|
|
32910
33544
|
this.currentPageProvider = provider;
|
|
32911
33545
|
}
|
|
33546
|
+
/** Let runs act on that view, not just read it (desktop only). */
|
|
33547
|
+
setBrowserController(controller, release) {
|
|
33548
|
+
this.browserController = controller;
|
|
33549
|
+
this.browserActorRelease = release;
|
|
33550
|
+
}
|
|
33551
|
+
/**
|
|
33552
|
+
* Told whenever settings are committed through THIS server.
|
|
33553
|
+
*
|
|
33554
|
+
* Settings reach the live config by two routes — the desktop's Electron IPC
|
|
33555
|
+
* handler and this server's `config:update` socket — and both run the same
|
|
33556
|
+
* `commitSettings`. The desktop mirrors `tools.agentBrowserControl` into its
|
|
33557
|
+
* browser module after its own route, but had no way to learn about the
|
|
33558
|
+
* other, so a socket write left the module's copy stale: an enable the module
|
|
33559
|
+
* still refused, or worse a disable the module still honoured for a run
|
|
33560
|
+
* already holding the tool. This is the same sibling-writer drift
|
|
33561
|
+
* `settings-payload.ts` exists to prevent, one layer up.
|
|
33562
|
+
*/
|
|
33563
|
+
settingsChangeHook;
|
|
33564
|
+
onSettingsChanged(hook) {
|
|
33565
|
+
this.settingsChangeHook = hook;
|
|
33566
|
+
}
|
|
33567
|
+
/**
|
|
33568
|
+
* Told when a run ends, so a host holding run-scoped state can release it.
|
|
33569
|
+
*
|
|
33570
|
+
* The desktop uses this to retire a run's browser Stop control: that has to
|
|
33571
|
+
* outlive each individual action (the gap between two actions is when the
|
|
33572
|
+
* user most wants it) but must not outlive the run.
|
|
33573
|
+
*/
|
|
33574
|
+
/**
|
|
33575
|
+
* Fired when a run ends, carrying BOTH identifiers explicitly.
|
|
33576
|
+
*
|
|
33577
|
+
* They are different things and conflating them was a live bug: a chat
|
|
33578
|
+
* `sessionId` can contain many runs (hence `sessionTaskIds`), while
|
|
33579
|
+
* `Cascade.run()` mints a fresh random `taskId` per run — and it is the
|
|
33580
|
+
* taskId that tools see as `ToolExecuteOptions.sessionId`. Passing only the
|
|
33581
|
+
* chat id meant browser cleanup keyed on the task id matched nothing, so a
|
|
33582
|
+
* finished run kept its Stop banner armed and held its browser lease until
|
|
33583
|
+
* the deadlock ceiling.
|
|
33584
|
+
*/
|
|
33585
|
+
runEndedHook;
|
|
33586
|
+
onRunEnded(hook) {
|
|
33587
|
+
this.runEndedHook = hook;
|
|
33588
|
+
}
|
|
32912
33589
|
async start() {
|
|
32913
33590
|
const isLoopback = this.host === "127.0.0.1" || this.host === "::1" || this.host === "localhost";
|
|
32914
33591
|
if (!isLoopback) {
|
|
@@ -32991,7 +33668,7 @@ var DashboardServer = class {
|
|
|
32991
33668
|
* silently and still typecheck.
|
|
32992
33669
|
*/
|
|
32993
33670
|
persistConfig(config) {
|
|
32994
|
-
const result = writeConfigFile(
|
|
33671
|
+
const result = writeConfigFile(path28__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE), config);
|
|
32995
33672
|
if (!result.ok) console.warn(`[dashboard] Failed to persist config: ${result.error}`);
|
|
32996
33673
|
return result;
|
|
32997
33674
|
}
|
|
@@ -33010,7 +33687,7 @@ var DashboardServer = class {
|
|
|
33010
33687
|
*/
|
|
33011
33688
|
syncGlobalCredentials() {
|
|
33012
33689
|
try {
|
|
33013
|
-
saveGlobalCredentials(
|
|
33690
|
+
saveGlobalCredentials(path28__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR), this.config.providers ?? []);
|
|
33014
33691
|
} catch (err) {
|
|
33015
33692
|
console.warn(`[dashboard] Failed to sync global credentials: ${err instanceof Error ? err.message : String(err)}`);
|
|
33016
33693
|
}
|
|
@@ -33025,15 +33702,15 @@ var DashboardServer = class {
|
|
|
33025
33702
|
resolveDashboardSecret() {
|
|
33026
33703
|
const fromConfig = this.config.dashboard.secret ?? process.env["CASCADE_DASHBOARD_SECRET"];
|
|
33027
33704
|
if (fromConfig) return fromConfig;
|
|
33028
|
-
const secretPath =
|
|
33705
|
+
const secretPath = path28__default.default.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
|
|
33029
33706
|
try {
|
|
33030
|
-
if (
|
|
33031
|
-
const existing =
|
|
33707
|
+
if (fs27__default.default.existsSync(secretPath)) {
|
|
33708
|
+
const existing = fs27__default.default.readFileSync(secretPath, "utf-8").trim();
|
|
33032
33709
|
if (existing.length >= 16) return existing;
|
|
33033
33710
|
}
|
|
33034
33711
|
const generated = crypto.randomUUID();
|
|
33035
|
-
|
|
33036
|
-
|
|
33712
|
+
fs27__default.default.mkdirSync(path28__default.default.dirname(secretPath), { recursive: true });
|
|
33713
|
+
fs27__default.default.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
|
|
33037
33714
|
if (this.config.dashboard.auth) {
|
|
33038
33715
|
console.warn(
|
|
33039
33716
|
`Dashboard auth enabled with no secret configured; persisted a generated secret to ${secretPath}. Set CASCADE_DASHBOARD_SECRET or config.dashboard.secret to override.`
|
|
@@ -33060,7 +33737,7 @@ var DashboardServer = class {
|
|
|
33060
33737
|
// ── Setup ─────────────────────────────────────
|
|
33061
33738
|
getGlobalStore() {
|
|
33062
33739
|
if (!this.globalStore) {
|
|
33063
|
-
const globalDbPath =
|
|
33740
|
+
const globalDbPath = path28__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
33064
33741
|
this.globalStore = new MemoryStore(globalDbPath);
|
|
33065
33742
|
}
|
|
33066
33743
|
return this.globalStore;
|
|
@@ -33099,6 +33776,8 @@ var DashboardServer = class {
|
|
|
33099
33776
|
}
|
|
33100
33777
|
/** Record run end: the assistant reply (when there is one) and the runtime row's final status. */
|
|
33101
33778
|
persistRunEnd(sessionId, title, latestPrompt, reply, status, result) {
|
|
33779
|
+
const endedTaskId = result?.taskId ?? this.sessionTaskIds.get(sessionId)?.at(-1);
|
|
33780
|
+
this.runEndedHook?.({ sessionId, ...endedTaskId ? { taskId: endedTaskId } : {} });
|
|
33102
33781
|
try {
|
|
33103
33782
|
if (reply && reply.trim()) {
|
|
33104
33783
|
this.store.addMessage({ id: crypto.randomUUID(), sessionId, role: "assistant", content: reply, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -33192,6 +33871,7 @@ var DashboardServer = class {
|
|
|
33192
33871
|
const prompt = task.prompt;
|
|
33193
33872
|
const title = this.persistRunStart(sessionId, `[${task.name}] ${prompt}`);
|
|
33194
33873
|
const cascade = new Cascade(this.config, task.workspacePath ?? this.workspacePath, this.store);
|
|
33874
|
+
cascade.setUnattended(true);
|
|
33195
33875
|
this.activeSessions.set(sessionId, cascade);
|
|
33196
33876
|
cascade.on("tier:status", (e) => {
|
|
33197
33877
|
this.socket.broadcast("tier:status", { sessionId, ...e });
|
|
@@ -33199,13 +33879,16 @@ var DashboardServer = class {
|
|
|
33199
33879
|
cascade.on("peer:message", (e) => {
|
|
33200
33880
|
this.socket.emitPeerMessage(e);
|
|
33201
33881
|
});
|
|
33882
|
+
cascade.on("run:started", (e) => {
|
|
33883
|
+
const id = e.taskId;
|
|
33884
|
+
if (id) this.recordSessionTask(sessionId, id);
|
|
33885
|
+
});
|
|
33202
33886
|
try {
|
|
33203
33887
|
const result = await cascade.run({
|
|
33204
33888
|
prompt,
|
|
33205
33889
|
identityId: task.identityId,
|
|
33206
33890
|
approvalCallback: async () => ({ approved: true, always: false })
|
|
33207
33891
|
});
|
|
33208
|
-
this.recordSessionTask(sessionId, result.taskId);
|
|
33209
33892
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED", result);
|
|
33210
33893
|
this.captureWhy(sessionId, cascade, result);
|
|
33211
33894
|
this.socket.broadcast("session:complete", { sessionId, result });
|
|
@@ -33440,12 +34123,12 @@ ${prompt}`;
|
|
|
33440
34123
|
}
|
|
33441
34124
|
}
|
|
33442
34125
|
watchRuntimeChanges() {
|
|
33443
|
-
const workspaceDbPath =
|
|
33444
|
-
const globalDbPath =
|
|
34126
|
+
const workspaceDbPath = path28__default.default.join(this.workspacePath, CASCADE_DB_FILE);
|
|
34127
|
+
const globalDbPath = path28__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
33445
34128
|
const watchPaths = [workspaceDbPath, globalDbPath].filter((p, index, arr) => arr.indexOf(p) === index);
|
|
33446
34129
|
for (const watchPath of watchPaths) {
|
|
33447
|
-
if (!
|
|
33448
|
-
|
|
34130
|
+
if (!fs27__default.default.existsSync(watchPath)) continue;
|
|
34131
|
+
fs27__default.default.watchFile(watchPath, { interval: 3e3 }, () => {
|
|
33449
34132
|
this.throttledBroadcast(watchPath === globalDbPath ? "global" : "workspace");
|
|
33450
34133
|
});
|
|
33451
34134
|
}
|
|
@@ -33567,7 +34250,7 @@ ${prompt}`;
|
|
|
33567
34250
|
const sessionId = req.params.id;
|
|
33568
34251
|
this.store.deleteSession(sessionId);
|
|
33569
34252
|
this.store.deleteRuntimeSession(sessionId);
|
|
33570
|
-
const globalDbPath =
|
|
34253
|
+
const globalDbPath = path28__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
33571
34254
|
const globalStore = new MemoryStore(globalDbPath);
|
|
33572
34255
|
try {
|
|
33573
34256
|
globalStore.deleteRuntimeSession(sessionId);
|
|
@@ -33660,7 +34343,7 @@ ${prompt}`;
|
|
|
33660
34343
|
});
|
|
33661
34344
|
this.app.delete("/api/sessions", auth2, (req, res) => {
|
|
33662
34345
|
const body = req.body;
|
|
33663
|
-
const globalDbPath =
|
|
34346
|
+
const globalDbPath = path28__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
33664
34347
|
if (body?.ids && Array.isArray(body.ids) && body.ids.length > 0) {
|
|
33665
34348
|
const globalStore = new MemoryStore(globalDbPath);
|
|
33666
34349
|
try {
|
|
@@ -33683,7 +34366,7 @@ ${prompt}`;
|
|
|
33683
34366
|
});
|
|
33684
34367
|
this.app.delete("/api/runtime", auth2, (_req, res) => {
|
|
33685
34368
|
this.store.deleteAllRuntimeNodes();
|
|
33686
|
-
const globalDbPath =
|
|
34369
|
+
const globalDbPath = path28__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
33687
34370
|
const globalStore = new MemoryStore(globalDbPath);
|
|
33688
34371
|
try {
|
|
33689
34372
|
globalStore.deleteAllRuntimeNodes();
|
|
@@ -33768,12 +34451,12 @@ ${prompt}`;
|
|
|
33768
34451
|
if (body["tierLimits"]) this.config.tierLimits = { ...this.config.tierLimits, ...body["tierLimits"] };
|
|
33769
34452
|
if (body["budget"]) this.config.budget = { ...this.config.budget, ...body["budget"] };
|
|
33770
34453
|
try {
|
|
33771
|
-
const configPath =
|
|
33772
|
-
const existing =
|
|
34454
|
+
const configPath = path28__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
34455
|
+
const existing = fs27__default.default.existsSync(configPath) ? JSON.parse(fs27__default.default.readFileSync(configPath, "utf-8")) : {};
|
|
33773
34456
|
const updated = { ...existing, tierLimits: this.config.tierLimits, budget: this.config.budget };
|
|
33774
34457
|
const tmp = configPath + ".tmp";
|
|
33775
|
-
|
|
33776
|
-
|
|
34458
|
+
fs27__default.default.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
|
|
34459
|
+
fs27__default.default.renameSync(tmp, configPath);
|
|
33777
34460
|
res.json({ ok: true });
|
|
33778
34461
|
} catch (err) {
|
|
33779
34462
|
res.status(500).json({ error: `Failed to save config: ${err instanceof Error ? err.message : String(err)}` });
|
|
@@ -33801,7 +34484,7 @@ ${prompt}`;
|
|
|
33801
34484
|
this.app.get("/api/runtime", auth2, (req, res) => {
|
|
33802
34485
|
const scope = req.query["scope"] ?? "workspace";
|
|
33803
34486
|
if (scope === "global") {
|
|
33804
|
-
const globalDbPath =
|
|
34487
|
+
const globalDbPath = path28__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
33805
34488
|
const globalStore = new MemoryStore(globalDbPath);
|
|
33806
34489
|
try {
|
|
33807
34490
|
res.json({
|
|
@@ -33838,6 +34521,7 @@ ${prompt}`;
|
|
|
33838
34521
|
const cascade = new Cascade(this.config, this.workspacePath, this.store);
|
|
33839
34522
|
this.activeSessions.set(sessionId, cascade);
|
|
33840
34523
|
if (this.currentPageProvider) cascade.setCurrentPageProvider(this.currentPageProvider);
|
|
34524
|
+
if (this.browserController) cascade.setBrowserController(this.browserController, this.browserActorRelease);
|
|
33841
34525
|
cascade.on("stream:token", (e) => {
|
|
33842
34526
|
this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, tierId: e.tierId, text: e.text, primary: e.primary });
|
|
33843
34527
|
});
|
|
@@ -33861,13 +34545,16 @@ ${prompt}`;
|
|
|
33861
34545
|
if (ev.requestId) this.clearPendingEscalation(ev.requestId);
|
|
33862
34546
|
this.socket.broadcastToRoom(`session:${sessionId}`, "escalation:timeout", { sessionId, ...e });
|
|
33863
34547
|
});
|
|
34548
|
+
cascade.on("run:started", (e) => {
|
|
34549
|
+
const id = e.taskId;
|
|
34550
|
+
if (id) this.recordSessionTask(sessionId, id);
|
|
34551
|
+
});
|
|
33864
34552
|
try {
|
|
33865
34553
|
const result = await cascade.run({
|
|
33866
34554
|
prompt: runPrompt,
|
|
33867
34555
|
identityId: body.identityId,
|
|
33868
34556
|
approvalCallback: this.makeApprovalCallback(sessionId)
|
|
33869
34557
|
});
|
|
33870
|
-
this.recordSessionTask(sessionId, result.taskId);
|
|
33871
34558
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED", result);
|
|
33872
34559
|
this.captureWhy(sessionId, cascade, result);
|
|
33873
34560
|
this.socket.broadcast("cost:update", {
|
|
@@ -34142,13 +34829,13 @@ ${prompt}`;
|
|
|
34142
34829
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
34143
34830
|
}
|
|
34144
34831
|
});
|
|
34145
|
-
const prodPath =
|
|
34146
|
-
const devPath =
|
|
34147
|
-
const webDistPath =
|
|
34148
|
-
if (
|
|
34832
|
+
const prodPath = path28__default.default.resolve(__dirname$1, "../web/dist");
|
|
34833
|
+
const devPath = path28__default.default.resolve(__dirname$1, "../../web/dist");
|
|
34834
|
+
const webDistPath = fs27__default.default.existsSync(prodPath) ? prodPath : devPath;
|
|
34835
|
+
if (fs27__default.default.existsSync(webDistPath)) {
|
|
34149
34836
|
this.app.use(express__default.default.static(webDistPath));
|
|
34150
34837
|
this.app.get("*", (_req, res) => {
|
|
34151
|
-
res.sendFile(
|
|
34838
|
+
res.sendFile(path28__default.default.join(webDistPath, "index.html"));
|
|
34152
34839
|
});
|
|
34153
34840
|
} else {
|
|
34154
34841
|
this.app.get("/", (_req, res) => {
|
|
@@ -34335,14 +35022,14 @@ function planRetrieval(ctx) {
|
|
|
34335
35022
|
}
|
|
34336
35023
|
var CLOUD_SESSION_FILE = "cloud-session.json";
|
|
34337
35024
|
function globalDir(homeDir = os6__default.default.homedir()) {
|
|
34338
|
-
return
|
|
35025
|
+
return path28__default.default.join(homeDir, GLOBAL_CONFIG_DIR);
|
|
34339
35026
|
}
|
|
34340
35027
|
function cloudSessionPath(dir = globalDir()) {
|
|
34341
|
-
return
|
|
35028
|
+
return path28__default.default.join(dir, CLOUD_SESSION_FILE);
|
|
34342
35029
|
}
|
|
34343
35030
|
function loadCloudSession(dir = globalDir()) {
|
|
34344
35031
|
try {
|
|
34345
|
-
const raw =
|
|
35032
|
+
const raw = fs27__default.default.readFileSync(cloudSessionPath(dir), "utf-8");
|
|
34346
35033
|
const parsed = JSON.parse(raw);
|
|
34347
35034
|
if (!parsed?.refreshToken || !parsed?.serverUrl) return null;
|
|
34348
35035
|
return parsed;
|
|
@@ -34351,17 +35038,17 @@ function loadCloudSession(dir = globalDir()) {
|
|
|
34351
35038
|
}
|
|
34352
35039
|
}
|
|
34353
35040
|
function saveCloudSession(session, dir = globalDir()) {
|
|
34354
|
-
|
|
35041
|
+
fs27__default.default.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
34355
35042
|
const file = cloudSessionPath(dir);
|
|
34356
|
-
|
|
35043
|
+
fs27__default.default.writeFileSync(file, JSON.stringify(session, null, 2), { mode: 384 });
|
|
34357
35044
|
try {
|
|
34358
|
-
|
|
35045
|
+
fs27__default.default.chmodSync(file, 384);
|
|
34359
35046
|
} catch {
|
|
34360
35047
|
}
|
|
34361
35048
|
}
|
|
34362
35049
|
function clearCloudSession(dir = globalDir()) {
|
|
34363
35050
|
try {
|
|
34364
|
-
|
|
35051
|
+
fs27__default.default.rmSync(cloudSessionPath(dir));
|
|
34365
35052
|
} catch {
|
|
34366
35053
|
}
|
|
34367
35054
|
}
|
|
@@ -34533,16 +35220,16 @@ var CloudClient = class _CloudClient {
|
|
|
34533
35220
|
return (await this.refresh()).accessToken;
|
|
34534
35221
|
}
|
|
34535
35222
|
/** Authenticated request that refreshes once on a 401 (e.g. server-side revoke). */
|
|
34536
|
-
async authed(
|
|
34537
|
-
const call = (token) => fetch(this.url(
|
|
35223
|
+
async authed(path30, init = {}) {
|
|
35224
|
+
const call = (token) => fetch(this.url(path30), { ...init, headers: { ...init.headers ?? {}, Authorization: `Bearer ${token}` } });
|
|
34538
35225
|
let res = await call(await this.accessToken());
|
|
34539
35226
|
if (res.status === 401) res = await call((await this.refresh()).accessToken);
|
|
34540
35227
|
if (!res.ok) throw new Error(`Request failed (${res.status}).`);
|
|
34541
35228
|
const text = await res.text();
|
|
34542
35229
|
return text ? JSON.parse(text) : {};
|
|
34543
35230
|
}
|
|
34544
|
-
authedGet(
|
|
34545
|
-
return this.authed(
|
|
35231
|
+
authedGet(path30) {
|
|
35232
|
+
return this.authed(path30);
|
|
34546
35233
|
}
|
|
34547
35234
|
async fetchMe(token) {
|
|
34548
35235
|
const res = await fetch(this.url("/api/me"), { headers: { Authorization: `Bearer ${token}` } });
|
|
@@ -34910,6 +35597,7 @@ function applySyncBundle(bundle, config, cleanup) {
|
|
|
34910
35597
|
exports.AZURE_BASE_MODELS = AZURE_BASE_MODELS;
|
|
34911
35598
|
exports.AZURE_BASE_URL_TEMPLATE = AZURE_BASE_URL_TEMPLATE;
|
|
34912
35599
|
exports.AuditLogger = AuditLogger;
|
|
35600
|
+
exports.BrowserControlTool = BrowserControlTool;
|
|
34913
35601
|
exports.CASCADE_AUDIT_FILE = CASCADE_AUDIT_FILE;
|
|
34914
35602
|
exports.CASCADE_CONFIG_DIR = CASCADE_CONFIG_DIR;
|
|
34915
35603
|
exports.CASCADE_CONFIG_FILE = CASCADE_CONFIG_FILE;
|
|
@@ -34923,6 +35611,7 @@ exports.CHARS_PER_TOKEN = CHARS_PER_TOKEN2;
|
|
|
34923
35611
|
exports.COMPLEXITY_T2_COUNT = COMPLEXITY_T2_COUNT;
|
|
34924
35612
|
exports.Cascade = Cascade;
|
|
34925
35613
|
exports.CascadeCancelledError = CascadeCancelledError;
|
|
35614
|
+
exports.CascadeConfigSchema = CascadeConfigSchema;
|
|
34926
35615
|
exports.CascadeIgnore = CascadeIgnore;
|
|
34927
35616
|
exports.CascadeRouter = CascadeRouter;
|
|
34928
35617
|
exports.CascadeToolError = CascadeToolError;
|