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.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import Database2 from 'better-sqlite3';
|
|
2
|
-
import
|
|
3
|
-
import
|
|
2
|
+
import path28, { dirname } from 'path';
|
|
3
|
+
import fs27, { promises, mkdirSync, writeFileSync, existsSync, readFileSync } from 'fs';
|
|
4
4
|
import os6 from 'os';
|
|
5
5
|
import crypto, { randomUUID, randomBytes, createHash, timingSafeEqual, webcrypto } from 'crypto';
|
|
6
6
|
import EventEmitter, { setMaxListeners } from 'events';
|
|
7
7
|
import { glob } from 'glob';
|
|
8
8
|
import Anthropic from '@anthropic-ai/sdk';
|
|
9
|
-
import
|
|
9
|
+
import dnsCallback from 'dns';
|
|
10
10
|
import http, { createServer } from 'http';
|
|
11
11
|
import https from 'https';
|
|
12
12
|
import zlib from 'zlib';
|
|
@@ -23,6 +23,7 @@ import { simpleGit } from 'simple-git';
|
|
|
23
23
|
import PDFDocument from 'pdfkit';
|
|
24
24
|
import dns2 from 'dns/promises';
|
|
25
25
|
import net from 'net';
|
|
26
|
+
import { Agent } from 'undici';
|
|
26
27
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
27
28
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
28
29
|
import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
|
|
@@ -132,16 +133,16 @@ var require_isarray = __commonJS({
|
|
|
132
133
|
}
|
|
133
134
|
});
|
|
134
135
|
|
|
135
|
-
// node_modules/
|
|
136
|
+
// node_modules/readable-stream/lib/internal/streams/stream.js
|
|
136
137
|
var require_stream = __commonJS({
|
|
137
|
-
"node_modules/
|
|
138
|
+
"node_modules/readable-stream/lib/internal/streams/stream.js"(exports, module) {
|
|
138
139
|
module.exports = __require("stream");
|
|
139
140
|
}
|
|
140
141
|
});
|
|
141
142
|
|
|
142
|
-
// node_modules/
|
|
143
|
+
// node_modules/readable-stream/node_modules/safe-buffer/index.js
|
|
143
144
|
var require_safe_buffer = __commonJS({
|
|
144
|
-
"node_modules/
|
|
145
|
+
"node_modules/readable-stream/node_modules/safe-buffer/index.js"(exports, module) {
|
|
145
146
|
var buffer = __require("buffer");
|
|
146
147
|
var Buffer2 = buffer.Buffer;
|
|
147
148
|
function copyProps(src, dst) {
|
|
@@ -259,7 +260,7 @@ var require_util = __commonJS({
|
|
|
259
260
|
typeof arg === "undefined";
|
|
260
261
|
}
|
|
261
262
|
exports.isPrimitive = isPrimitive;
|
|
262
|
-
exports.isBuffer = Buffer.isBuffer;
|
|
263
|
+
exports.isBuffer = __require("buffer").Buffer.isBuffer;
|
|
263
264
|
function objectToString(o) {
|
|
264
265
|
return Object.prototype.toString.call(o);
|
|
265
266
|
}
|
|
@@ -312,9 +313,9 @@ var require_inherits = __commonJS({
|
|
|
312
313
|
}
|
|
313
314
|
});
|
|
314
315
|
|
|
315
|
-
// node_modules/
|
|
316
|
+
// node_modules/readable-stream/lib/internal/streams/BufferList.js
|
|
316
317
|
var require_BufferList = __commonJS({
|
|
317
|
-
"node_modules/
|
|
318
|
+
"node_modules/readable-stream/lib/internal/streams/BufferList.js"(exports, module) {
|
|
318
319
|
function _classCallCheck(instance, Constructor) {
|
|
319
320
|
if (!(instance instanceof Constructor)) {
|
|
320
321
|
throw new TypeError("Cannot call a class as a function");
|
|
@@ -389,9 +390,9 @@ var require_BufferList = __commonJS({
|
|
|
389
390
|
}
|
|
390
391
|
});
|
|
391
392
|
|
|
392
|
-
// node_modules/
|
|
393
|
+
// node_modules/readable-stream/lib/internal/streams/destroy.js
|
|
393
394
|
var require_destroy = __commonJS({
|
|
394
|
-
"node_modules/
|
|
395
|
+
"node_modules/readable-stream/lib/internal/streams/destroy.js"(exports, module) {
|
|
395
396
|
var pna = require_process_nextick_args();
|
|
396
397
|
function destroy(err, cb) {
|
|
397
398
|
var _this = this;
|
|
@@ -464,9 +465,9 @@ var require_node = __commonJS({
|
|
|
464
465
|
}
|
|
465
466
|
});
|
|
466
467
|
|
|
467
|
-
// node_modules/
|
|
468
|
+
// node_modules/readable-stream/lib/_stream_writable.js
|
|
468
469
|
var require_stream_writable = __commonJS({
|
|
469
|
-
"node_modules/
|
|
470
|
+
"node_modules/readable-stream/lib/_stream_writable.js"(exports, module) {
|
|
470
471
|
var pna = require_process_nextick_args();
|
|
471
472
|
module.exports = Writable;
|
|
472
473
|
function CorkedRequest(state) {
|
|
@@ -903,9 +904,9 @@ var require_stream_writable = __commonJS({
|
|
|
903
904
|
}
|
|
904
905
|
});
|
|
905
906
|
|
|
906
|
-
// node_modules/
|
|
907
|
+
// node_modules/readable-stream/lib/_stream_duplex.js
|
|
907
908
|
var require_stream_duplex = __commonJS({
|
|
908
|
-
"node_modules/
|
|
909
|
+
"node_modules/readable-stream/lib/_stream_duplex.js"(exports, module) {
|
|
909
910
|
var pna = require_process_nextick_args();
|
|
910
911
|
var objectKeys = Object.keys || function(obj) {
|
|
911
912
|
var keys2 = [];
|
|
@@ -979,10 +980,67 @@ var require_stream_duplex = __commonJS({
|
|
|
979
980
|
}
|
|
980
981
|
});
|
|
981
982
|
|
|
982
|
-
// node_modules/
|
|
983
|
+
// node_modules/string_decoder/node_modules/safe-buffer/index.js
|
|
984
|
+
var require_safe_buffer2 = __commonJS({
|
|
985
|
+
"node_modules/string_decoder/node_modules/safe-buffer/index.js"(exports, module) {
|
|
986
|
+
var buffer = __require("buffer");
|
|
987
|
+
var Buffer2 = buffer.Buffer;
|
|
988
|
+
function copyProps(src, dst) {
|
|
989
|
+
for (var key in src) {
|
|
990
|
+
dst[key] = src[key];
|
|
991
|
+
}
|
|
992
|
+
}
|
|
993
|
+
if (Buffer2.from && Buffer2.alloc && Buffer2.allocUnsafe && Buffer2.allocUnsafeSlow) {
|
|
994
|
+
module.exports = buffer;
|
|
995
|
+
} else {
|
|
996
|
+
copyProps(buffer, exports);
|
|
997
|
+
exports.Buffer = SafeBuffer;
|
|
998
|
+
}
|
|
999
|
+
function SafeBuffer(arg, encodingOrOffset, length) {
|
|
1000
|
+
return Buffer2(arg, encodingOrOffset, length);
|
|
1001
|
+
}
|
|
1002
|
+
copyProps(Buffer2, SafeBuffer);
|
|
1003
|
+
SafeBuffer.from = function(arg, encodingOrOffset, length) {
|
|
1004
|
+
if (typeof arg === "number") {
|
|
1005
|
+
throw new TypeError("Argument must not be a number");
|
|
1006
|
+
}
|
|
1007
|
+
return Buffer2(arg, encodingOrOffset, length);
|
|
1008
|
+
};
|
|
1009
|
+
SafeBuffer.alloc = function(size, fill, encoding) {
|
|
1010
|
+
if (typeof size !== "number") {
|
|
1011
|
+
throw new TypeError("Argument must be a number");
|
|
1012
|
+
}
|
|
1013
|
+
var buf = Buffer2(size);
|
|
1014
|
+
if (fill !== void 0) {
|
|
1015
|
+
if (typeof encoding === "string") {
|
|
1016
|
+
buf.fill(fill, encoding);
|
|
1017
|
+
} else {
|
|
1018
|
+
buf.fill(fill);
|
|
1019
|
+
}
|
|
1020
|
+
} else {
|
|
1021
|
+
buf.fill(0);
|
|
1022
|
+
}
|
|
1023
|
+
return buf;
|
|
1024
|
+
};
|
|
1025
|
+
SafeBuffer.allocUnsafe = function(size) {
|
|
1026
|
+
if (typeof size !== "number") {
|
|
1027
|
+
throw new TypeError("Argument must be a number");
|
|
1028
|
+
}
|
|
1029
|
+
return Buffer2(size);
|
|
1030
|
+
};
|
|
1031
|
+
SafeBuffer.allocUnsafeSlow = function(size) {
|
|
1032
|
+
if (typeof size !== "number") {
|
|
1033
|
+
throw new TypeError("Argument must be a number");
|
|
1034
|
+
}
|
|
1035
|
+
return buffer.SlowBuffer(size);
|
|
1036
|
+
};
|
|
1037
|
+
}
|
|
1038
|
+
});
|
|
1039
|
+
|
|
1040
|
+
// node_modules/string_decoder/lib/string_decoder.js
|
|
983
1041
|
var require_string_decoder = __commonJS({
|
|
984
|
-
"node_modules/
|
|
985
|
-
var Buffer2 =
|
|
1042
|
+
"node_modules/string_decoder/lib/string_decoder.js"(exports) {
|
|
1043
|
+
var Buffer2 = require_safe_buffer2().Buffer;
|
|
986
1044
|
var isEncoding = Buffer2.isEncoding || function(encoding) {
|
|
987
1045
|
encoding = "" + encoding;
|
|
988
1046
|
switch (encoding && encoding.toLowerCase()) {
|
|
@@ -1216,9 +1274,9 @@ var require_string_decoder = __commonJS({
|
|
|
1216
1274
|
}
|
|
1217
1275
|
});
|
|
1218
1276
|
|
|
1219
|
-
// node_modules/
|
|
1277
|
+
// node_modules/readable-stream/lib/_stream_readable.js
|
|
1220
1278
|
var require_stream_readable = __commonJS({
|
|
1221
|
-
"node_modules/
|
|
1279
|
+
"node_modules/readable-stream/lib/_stream_readable.js"(exports, module) {
|
|
1222
1280
|
var pna = require_process_nextick_args();
|
|
1223
1281
|
module.exports = Readable2;
|
|
1224
1282
|
var isArray = require_isarray();
|
|
@@ -1901,9 +1959,9 @@ var require_stream_readable = __commonJS({
|
|
|
1901
1959
|
}
|
|
1902
1960
|
});
|
|
1903
1961
|
|
|
1904
|
-
// node_modules/
|
|
1962
|
+
// node_modules/readable-stream/lib/_stream_transform.js
|
|
1905
1963
|
var require_stream_transform = __commonJS({
|
|
1906
|
-
"node_modules/
|
|
1964
|
+
"node_modules/readable-stream/lib/_stream_transform.js"(exports, module) {
|
|
1907
1965
|
module.exports = Transform;
|
|
1908
1966
|
var Duplex = require_stream_duplex();
|
|
1909
1967
|
var util = Object.create(require_util());
|
|
@@ -2000,9 +2058,9 @@ var require_stream_transform = __commonJS({
|
|
|
2000
2058
|
}
|
|
2001
2059
|
});
|
|
2002
2060
|
|
|
2003
|
-
// node_modules/
|
|
2061
|
+
// node_modules/readable-stream/lib/_stream_passthrough.js
|
|
2004
2062
|
var require_stream_passthrough = __commonJS({
|
|
2005
|
-
"node_modules/
|
|
2063
|
+
"node_modules/readable-stream/lib/_stream_passthrough.js"(exports, module) {
|
|
2006
2064
|
module.exports = PassThrough;
|
|
2007
2065
|
var Transform = require_stream_transform();
|
|
2008
2066
|
var util = Object.create(require_util());
|
|
@@ -2018,9 +2076,9 @@ var require_stream_passthrough = __commonJS({
|
|
|
2018
2076
|
}
|
|
2019
2077
|
});
|
|
2020
2078
|
|
|
2021
|
-
// node_modules/
|
|
2079
|
+
// node_modules/readable-stream/readable.js
|
|
2022
2080
|
var require_readable = __commonJS({
|
|
2023
|
-
"node_modules/
|
|
2081
|
+
"node_modules/readable-stream/readable.js"(exports, module) {
|
|
2024
2082
|
var Stream = __require("stream");
|
|
2025
2083
|
if (process.env.READABLE_STREAM === "disable" && Stream) {
|
|
2026
2084
|
module.exports = Stream;
|
|
@@ -2929,8 +2987,8 @@ var require_utils = __commonJS({
|
|
|
2929
2987
|
var result = transform[inputType][outputType](input);
|
|
2930
2988
|
return result;
|
|
2931
2989
|
};
|
|
2932
|
-
exports.resolve = function(
|
|
2933
|
-
var parts =
|
|
2990
|
+
exports.resolve = function(path30) {
|
|
2991
|
+
var parts = path30.split("/");
|
|
2934
2992
|
var result = [];
|
|
2935
2993
|
for (var index = 0; index < parts.length; index++) {
|
|
2936
2994
|
var part = parts[index];
|
|
@@ -8745,18 +8803,18 @@ var require_object = __commonJS({
|
|
|
8745
8803
|
var object = new ZipObject(name, zipObjectContent, o);
|
|
8746
8804
|
this.files[name] = object;
|
|
8747
8805
|
};
|
|
8748
|
-
var parentFolder = function(
|
|
8749
|
-
if (
|
|
8750
|
-
|
|
8806
|
+
var parentFolder = function(path30) {
|
|
8807
|
+
if (path30.slice(-1) === "/") {
|
|
8808
|
+
path30 = path30.substring(0, path30.length - 1);
|
|
8751
8809
|
}
|
|
8752
|
-
var lastSlash =
|
|
8753
|
-
return lastSlash > 0 ?
|
|
8810
|
+
var lastSlash = path30.lastIndexOf("/");
|
|
8811
|
+
return lastSlash > 0 ? path30.substring(0, lastSlash) : "";
|
|
8754
8812
|
};
|
|
8755
|
-
var forceTrailingSlash = function(
|
|
8756
|
-
if (
|
|
8757
|
-
|
|
8813
|
+
var forceTrailingSlash = function(path30) {
|
|
8814
|
+
if (path30.slice(-1) !== "/") {
|
|
8815
|
+
path30 += "/";
|
|
8758
8816
|
}
|
|
8759
|
-
return
|
|
8817
|
+
return path30;
|
|
8760
8818
|
};
|
|
8761
8819
|
var folderAdd = function(name, createFolders) {
|
|
8762
8820
|
createFolders = typeof createFolders !== "undefined" ? createFolders : defaults.createFolders;
|
|
@@ -9755,12 +9813,12 @@ var init_audit_logger = __esm({
|
|
|
9755
9813
|
constructor(workspacePath, debugMode = false) {
|
|
9756
9814
|
this.workspacePath = workspacePath;
|
|
9757
9815
|
this.debugMode = debugMode;
|
|
9758
|
-
const cascadeDir =
|
|
9759
|
-
if (!
|
|
9760
|
-
|
|
9816
|
+
const cascadeDir = path28.join(workspacePath, ".cascade");
|
|
9817
|
+
if (!fs27.existsSync(cascadeDir)) {
|
|
9818
|
+
fs27.mkdirSync(cascadeDir, { recursive: true });
|
|
9761
9819
|
}
|
|
9762
|
-
this.keyPath =
|
|
9763
|
-
this.dbPath =
|
|
9820
|
+
this.keyPath = path28.join(cascadeDir, "audit_log.key");
|
|
9821
|
+
this.dbPath = path28.join(cascadeDir, "audit_log.db");
|
|
9764
9822
|
this.initEncryptionKey();
|
|
9765
9823
|
this.db = new Database2(this.dbPath);
|
|
9766
9824
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -9790,11 +9848,11 @@ var init_audit_logger = __esm({
|
|
|
9790
9848
|
return crypto.createHash("sha256").update(`${prevHash}|${timestamp}|${eventType}|${tierId}|${encryptedPayload}`).digest("hex");
|
|
9791
9849
|
}
|
|
9792
9850
|
initEncryptionKey() {
|
|
9793
|
-
if (
|
|
9794
|
-
this.encryptionKey =
|
|
9851
|
+
if (fs27.existsSync(this.keyPath)) {
|
|
9852
|
+
this.encryptionKey = fs27.readFileSync(this.keyPath);
|
|
9795
9853
|
} else {
|
|
9796
9854
|
this.encryptionKey = crypto.randomBytes(32);
|
|
9797
|
-
|
|
9855
|
+
fs27.writeFileSync(this.keyPath, this.encryptionKey);
|
|
9798
9856
|
}
|
|
9799
9857
|
}
|
|
9800
9858
|
encrypt(text) {
|
|
@@ -9872,9 +9930,9 @@ var init_audit_logger = __esm({
|
|
|
9872
9930
|
dumpDebugIfNeeded() {
|
|
9873
9931
|
if (!this.debugMode) return;
|
|
9874
9932
|
try {
|
|
9875
|
-
const dumpPath =
|
|
9933
|
+
const dumpPath = path28.join(os6.tmpdir(), "cascade_audit_logs_debug.json");
|
|
9876
9934
|
const entries = this.getAllLogs();
|
|
9877
|
-
|
|
9935
|
+
fs27.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
9878
9936
|
} catch (err) {
|
|
9879
9937
|
console.error("Failed to dump debug audit logs", err);
|
|
9880
9938
|
}
|
|
@@ -11565,7 +11623,7 @@ function reconcilePrice(model, live) {
|
|
|
11565
11623
|
}
|
|
11566
11624
|
|
|
11567
11625
|
// src/constants.ts
|
|
11568
|
-
var CASCADE_VERSION = "0.
|
|
11626
|
+
var CASCADE_VERSION = "0.78.0";
|
|
11569
11627
|
var CASCADE_CONFIG_DIR = ".cascade";
|
|
11570
11628
|
var CASCADE_MD_FILE = "CASCADE.md";
|
|
11571
11629
|
var CASCADE_IGNORE_FILE = ".cascadeignore";
|
|
@@ -11586,64 +11644,88 @@ var DEFAULT_AUTO_SUMMARIZE_AT = 15e4;
|
|
|
11586
11644
|
var DEFAULT_MAX_SESSION_MESSAGES = 1e3;
|
|
11587
11645
|
var DEFAULT_RETENTION_DAYS = 90;
|
|
11588
11646
|
var MODELS = {
|
|
11589
|
-
// Anthropic
|
|
11590
|
-
"claude-
|
|
11591
|
-
id: "claude-
|
|
11592
|
-
name: "Claude
|
|
11647
|
+
// Anthropic — active Claude API models (2026-08-27).
|
|
11648
|
+
"claude-fable-5": {
|
|
11649
|
+
id: "claude-fable-5",
|
|
11650
|
+
name: "Claude Fable 5",
|
|
11593
11651
|
provider: "anthropic",
|
|
11594
|
-
contextWindow:
|
|
11652
|
+
contextWindow: 1e6,
|
|
11595
11653
|
isVisionCapable: true,
|
|
11596
|
-
inputCostPer1kTokens: 0.
|
|
11597
|
-
outputCostPer1kTokens: 0.
|
|
11598
|
-
maxOutputTokens:
|
|
11654
|
+
inputCostPer1kTokens: 0.01,
|
|
11655
|
+
outputCostPer1kTokens: 0.05,
|
|
11656
|
+
maxOutputTokens: 128e3,
|
|
11599
11657
|
supportsStreaming: true,
|
|
11600
11658
|
isLocal: false
|
|
11601
11659
|
},
|
|
11602
|
-
"claude-
|
|
11603
|
-
id: "claude-
|
|
11604
|
-
name: "Claude
|
|
11660
|
+
"claude-opus-5": {
|
|
11661
|
+
id: "claude-opus-5",
|
|
11662
|
+
name: "Claude Opus 5",
|
|
11605
11663
|
provider: "anthropic",
|
|
11606
|
-
contextWindow:
|
|
11664
|
+
contextWindow: 1e6,
|
|
11607
11665
|
isVisionCapable: true,
|
|
11608
|
-
inputCostPer1kTokens:
|
|
11609
|
-
outputCostPer1kTokens: 0.
|
|
11610
|
-
maxOutputTokens:
|
|
11666
|
+
inputCostPer1kTokens: 5e-3,
|
|
11667
|
+
outputCostPer1kTokens: 0.025,
|
|
11668
|
+
maxOutputTokens: 128e3,
|
|
11611
11669
|
supportsStreaming: true,
|
|
11612
11670
|
isLocal: false
|
|
11613
11671
|
},
|
|
11614
|
-
"claude-
|
|
11615
|
-
id: "claude-
|
|
11616
|
-
name: "Claude
|
|
11672
|
+
"claude-opus-4-8": {
|
|
11673
|
+
id: "claude-opus-4-8",
|
|
11674
|
+
name: "Claude Opus 4.8",
|
|
11617
11675
|
provider: "anthropic",
|
|
11618
|
-
contextWindow:
|
|
11676
|
+
contextWindow: 1e6,
|
|
11619
11677
|
isVisionCapable: true,
|
|
11620
|
-
inputCostPer1kTokens:
|
|
11621
|
-
outputCostPer1kTokens:
|
|
11622
|
-
maxOutputTokens:
|
|
11678
|
+
inputCostPer1kTokens: 5e-3,
|
|
11679
|
+
outputCostPer1kTokens: 0.025,
|
|
11680
|
+
maxOutputTokens: 128e3,
|
|
11623
11681
|
supportsStreaming: true,
|
|
11624
11682
|
isLocal: false
|
|
11625
11683
|
},
|
|
11626
|
-
"claude-
|
|
11627
|
-
id: "claude-
|
|
11628
|
-
name: "Claude
|
|
11684
|
+
"claude-opus-4-7": {
|
|
11685
|
+
id: "claude-opus-4-7",
|
|
11686
|
+
name: "Claude Opus 4.7",
|
|
11629
11687
|
provider: "anthropic",
|
|
11630
|
-
contextWindow:
|
|
11688
|
+
contextWindow: 1e6,
|
|
11631
11689
|
isVisionCapable: true,
|
|
11632
|
-
inputCostPer1kTokens:
|
|
11633
|
-
outputCostPer1kTokens:
|
|
11634
|
-
maxOutputTokens:
|
|
11690
|
+
inputCostPer1kTokens: 5e-3,
|
|
11691
|
+
outputCostPer1kTokens: 0.025,
|
|
11692
|
+
maxOutputTokens: 128e3,
|
|
11635
11693
|
supportsStreaming: true,
|
|
11636
11694
|
isLocal: false
|
|
11637
11695
|
},
|
|
11638
|
-
"claude-opus-4-
|
|
11639
|
-
id: "claude-opus-4-
|
|
11640
|
-
name: "Claude Opus 4.
|
|
11696
|
+
"claude-opus-4-6": {
|
|
11697
|
+
id: "claude-opus-4-6",
|
|
11698
|
+
name: "Claude Opus 4.6",
|
|
11699
|
+
provider: "anthropic",
|
|
11700
|
+
contextWindow: 1e6,
|
|
11701
|
+
isVisionCapable: true,
|
|
11702
|
+
inputCostPer1kTokens: 5e-3,
|
|
11703
|
+
outputCostPer1kTokens: 0.025,
|
|
11704
|
+
maxOutputTokens: 128e3,
|
|
11705
|
+
supportsStreaming: true,
|
|
11706
|
+
isLocal: false
|
|
11707
|
+
},
|
|
11708
|
+
"claude-opus-4-5": {
|
|
11709
|
+
id: "claude-opus-4-5-20251101",
|
|
11710
|
+
name: "Claude Opus 4.5",
|
|
11641
11711
|
provider: "anthropic",
|
|
11642
11712
|
contextWindow: 2e5,
|
|
11643
11713
|
isVisionCapable: true,
|
|
11644
|
-
inputCostPer1kTokens:
|
|
11645
|
-
outputCostPer1kTokens: 0.
|
|
11646
|
-
maxOutputTokens:
|
|
11714
|
+
inputCostPer1kTokens: 5e-3,
|
|
11715
|
+
outputCostPer1kTokens: 0.025,
|
|
11716
|
+
maxOutputTokens: 64e3,
|
|
11717
|
+
supportsStreaming: true,
|
|
11718
|
+
isLocal: false
|
|
11719
|
+
},
|
|
11720
|
+
"claude-sonnet-5": {
|
|
11721
|
+
id: "claude-sonnet-5",
|
|
11722
|
+
name: "Claude Sonnet 5",
|
|
11723
|
+
provider: "anthropic",
|
|
11724
|
+
contextWindow: 1e6,
|
|
11725
|
+
isVisionCapable: true,
|
|
11726
|
+
inputCostPer1kTokens: 2e-3,
|
|
11727
|
+
outputCostPer1kTokens: 0.01,
|
|
11728
|
+
maxOutputTokens: 128e3,
|
|
11647
11729
|
supportsStreaming: true,
|
|
11648
11730
|
isLocal: false
|
|
11649
11731
|
},
|
|
@@ -11651,76 +11733,135 @@ var MODELS = {
|
|
|
11651
11733
|
id: "claude-sonnet-4-6",
|
|
11652
11734
|
name: "Claude Sonnet 4.6",
|
|
11653
11735
|
provider: "anthropic",
|
|
11736
|
+
contextWindow: 1e6,
|
|
11737
|
+
isVisionCapable: true,
|
|
11738
|
+
inputCostPer1kTokens: 3e-3,
|
|
11739
|
+
outputCostPer1kTokens: 0.015,
|
|
11740
|
+
maxOutputTokens: 64e3,
|
|
11741
|
+
supportsStreaming: true,
|
|
11742
|
+
isLocal: false
|
|
11743
|
+
},
|
|
11744
|
+
"claude-sonnet-4-5": {
|
|
11745
|
+
id: "claude-sonnet-4-5-20250929",
|
|
11746
|
+
name: "Claude Sonnet 4.5",
|
|
11747
|
+
provider: "anthropic",
|
|
11654
11748
|
contextWindow: 2e5,
|
|
11655
11749
|
isVisionCapable: true,
|
|
11656
11750
|
inputCostPer1kTokens: 3e-3,
|
|
11657
11751
|
outputCostPer1kTokens: 0.015,
|
|
11658
|
-
maxOutputTokens:
|
|
11752
|
+
maxOutputTokens: 64e3,
|
|
11659
11753
|
supportsStreaming: true,
|
|
11660
11754
|
isLocal: false
|
|
11661
11755
|
},
|
|
11662
|
-
|
|
11663
|
-
|
|
11664
|
-
|
|
11665
|
-
|
|
11756
|
+
"claude-haiku-4-5": {
|
|
11757
|
+
id: "claude-haiku-4-5-20251001",
|
|
11758
|
+
name: "Claude Haiku 4.5",
|
|
11759
|
+
provider: "anthropic",
|
|
11760
|
+
contextWindow: 2e5,
|
|
11761
|
+
isVisionCapable: true,
|
|
11762
|
+
inputCostPer1kTokens: 1e-3,
|
|
11763
|
+
outputCostPer1kTokens: 5e-3,
|
|
11764
|
+
maxOutputTokens: 64e3,
|
|
11765
|
+
supportsStreaming: true,
|
|
11766
|
+
isLocal: false
|
|
11767
|
+
},
|
|
11768
|
+
// OpenAI — active chat-capable models used by Cascade's Chat Completions path.
|
|
11769
|
+
"gpt-5.6-sol": {
|
|
11770
|
+
id: "gpt-5.6-sol",
|
|
11771
|
+
name: "GPT-5.6 Sol",
|
|
11666
11772
|
provider: "openai",
|
|
11667
|
-
contextWindow:
|
|
11773
|
+
contextWindow: 105e4,
|
|
11774
|
+
isVisionCapable: true,
|
|
11775
|
+
inputCostPer1kTokens: 4e-3,
|
|
11776
|
+
outputCostPer1kTokens: 0.02,
|
|
11777
|
+
maxOutputTokens: 128e3,
|
|
11778
|
+
supportsStreaming: true,
|
|
11779
|
+
isLocal: false
|
|
11780
|
+
},
|
|
11781
|
+
"gpt-5.6-terra": {
|
|
11782
|
+
id: "gpt-5.6-terra",
|
|
11783
|
+
name: "GPT-5.6 Terra",
|
|
11784
|
+
provider: "openai",
|
|
11785
|
+
contextWindow: 105e4,
|
|
11786
|
+
isVisionCapable: true,
|
|
11787
|
+
inputCostPer1kTokens: 2e-3,
|
|
11788
|
+
outputCostPer1kTokens: 0.012,
|
|
11789
|
+
maxOutputTokens: 128e3,
|
|
11790
|
+
supportsStreaming: true,
|
|
11791
|
+
isLocal: false
|
|
11792
|
+
},
|
|
11793
|
+
"gpt-5.6-luna": {
|
|
11794
|
+
id: "gpt-5.6-luna",
|
|
11795
|
+
name: "GPT-5.6 Luna",
|
|
11796
|
+
provider: "openai",
|
|
11797
|
+
contextWindow: 105e4,
|
|
11798
|
+
isVisionCapable: true,
|
|
11799
|
+
inputCostPer1kTokens: 2e-4,
|
|
11800
|
+
outputCostPer1kTokens: 12e-4,
|
|
11801
|
+
maxOutputTokens: 128e3,
|
|
11802
|
+
supportsStreaming: true,
|
|
11803
|
+
isLocal: false
|
|
11804
|
+
},
|
|
11805
|
+
"gpt-5.5": {
|
|
11806
|
+
id: "gpt-5.5",
|
|
11807
|
+
name: "GPT-5.5",
|
|
11808
|
+
provider: "openai",
|
|
11809
|
+
contextWindow: 105e4,
|
|
11668
11810
|
isVisionCapable: true,
|
|
11669
11811
|
inputCostPer1kTokens: 5e-3,
|
|
11670
|
-
outputCostPer1kTokens: 0.
|
|
11671
|
-
maxOutputTokens:
|
|
11812
|
+
outputCostPer1kTokens: 0.03,
|
|
11813
|
+
maxOutputTokens: 128e3,
|
|
11672
11814
|
supportsStreaming: true,
|
|
11673
11815
|
isLocal: false
|
|
11674
11816
|
},
|
|
11675
|
-
"gpt-
|
|
11676
|
-
id: "gpt-
|
|
11677
|
-
name: "GPT-
|
|
11817
|
+
"gpt-5.4": {
|
|
11818
|
+
id: "gpt-5.4",
|
|
11819
|
+
name: "GPT-5.4",
|
|
11678
11820
|
provider: "openai",
|
|
11679
|
-
contextWindow:
|
|
11821
|
+
contextWindow: 105e4,
|
|
11680
11822
|
isVisionCapable: true,
|
|
11681
|
-
inputCostPer1kTokens:
|
|
11682
|
-
outputCostPer1kTokens:
|
|
11683
|
-
maxOutputTokens:
|
|
11823
|
+
inputCostPer1kTokens: 25e-4,
|
|
11824
|
+
outputCostPer1kTokens: 0.015,
|
|
11825
|
+
maxOutputTokens: 128e3,
|
|
11684
11826
|
supportsStreaming: true,
|
|
11685
11827
|
isLocal: false
|
|
11686
11828
|
},
|
|
11687
|
-
"gpt-4
|
|
11688
|
-
id: "gpt-4
|
|
11689
|
-
name: "GPT-4
|
|
11829
|
+
"gpt-5.4-mini": {
|
|
11830
|
+
id: "gpt-5.4-mini",
|
|
11831
|
+
name: "GPT-5.4 Mini",
|
|
11690
11832
|
provider: "openai",
|
|
11691
|
-
contextWindow:
|
|
11833
|
+
contextWindow: 4e5,
|
|
11692
11834
|
isVisionCapable: true,
|
|
11693
|
-
inputCostPer1kTokens:
|
|
11694
|
-
outputCostPer1kTokens:
|
|
11695
|
-
maxOutputTokens:
|
|
11835
|
+
inputCostPer1kTokens: 75e-5,
|
|
11836
|
+
outputCostPer1kTokens: 45e-4,
|
|
11837
|
+
maxOutputTokens: 128e3,
|
|
11696
11838
|
supportsStreaming: true,
|
|
11697
11839
|
isLocal: false
|
|
11698
11840
|
},
|
|
11699
|
-
"gpt-4
|
|
11700
|
-
id: "gpt-4
|
|
11701
|
-
name: "GPT-4
|
|
11841
|
+
"gpt-5.4-nano": {
|
|
11842
|
+
id: "gpt-5.4-nano",
|
|
11843
|
+
name: "GPT-5.4 Nano",
|
|
11702
11844
|
provider: "openai",
|
|
11703
|
-
contextWindow:
|
|
11845
|
+
contextWindow: 4e5,
|
|
11704
11846
|
isVisionCapable: true,
|
|
11705
|
-
inputCostPer1kTokens:
|
|
11706
|
-
outputCostPer1kTokens:
|
|
11707
|
-
maxOutputTokens:
|
|
11847
|
+
inputCostPer1kTokens: 2e-4,
|
|
11848
|
+
outputCostPer1kTokens: 125e-5,
|
|
11849
|
+
maxOutputTokens: 128e3,
|
|
11708
11850
|
supportsStreaming: true,
|
|
11709
11851
|
isLocal: false
|
|
11710
11852
|
},
|
|
11711
|
-
"gpt-
|
|
11712
|
-
id: "gpt-
|
|
11713
|
-
name: "GPT-
|
|
11853
|
+
"gpt-5.2": {
|
|
11854
|
+
id: "gpt-5.2",
|
|
11855
|
+
name: "GPT-5.2",
|
|
11714
11856
|
provider: "openai",
|
|
11715
|
-
contextWindow:
|
|
11857
|
+
contextWindow: 4e5,
|
|
11716
11858
|
isVisionCapable: true,
|
|
11717
|
-
inputCostPer1kTokens:
|
|
11718
|
-
outputCostPer1kTokens:
|
|
11719
|
-
maxOutputTokens:
|
|
11859
|
+
inputCostPer1kTokens: 175e-5,
|
|
11860
|
+
outputCostPer1kTokens: 0.014,
|
|
11861
|
+
maxOutputTokens: 128e3,
|
|
11720
11862
|
supportsStreaming: true,
|
|
11721
11863
|
isLocal: false
|
|
11722
11864
|
},
|
|
11723
|
-
// GPT-5 family (offline baseline pricing; the live fetch keeps it current).
|
|
11724
11865
|
"gpt-5": {
|
|
11725
11866
|
id: "gpt-5",
|
|
11726
11867
|
name: "GPT-5",
|
|
@@ -11757,79 +11898,149 @@ var MODELS = {
|
|
|
11757
11898
|
supportsStreaming: true,
|
|
11758
11899
|
isLocal: false
|
|
11759
11900
|
},
|
|
11760
|
-
|
|
11761
|
-
|
|
11762
|
-
|
|
11763
|
-
"gpt-5.5": {
|
|
11764
|
-
id: "gpt-5.5",
|
|
11765
|
-
name: "GPT-5.5",
|
|
11901
|
+
"gpt-4.1": {
|
|
11902
|
+
id: "gpt-4.1",
|
|
11903
|
+
name: "GPT-4.1",
|
|
11766
11904
|
provider: "openai",
|
|
11767
|
-
contextWindow:
|
|
11905
|
+
contextWindow: 1047576,
|
|
11768
11906
|
isVisionCapable: true,
|
|
11769
|
-
inputCostPer1kTokens:
|
|
11770
|
-
outputCostPer1kTokens:
|
|
11771
|
-
maxOutputTokens:
|
|
11907
|
+
inputCostPer1kTokens: 2e-3,
|
|
11908
|
+
outputCostPer1kTokens: 8e-3,
|
|
11909
|
+
maxOutputTokens: 32768,
|
|
11772
11910
|
supportsStreaming: true,
|
|
11773
11911
|
isLocal: false
|
|
11774
11912
|
},
|
|
11775
|
-
"gpt-
|
|
11776
|
-
id: "gpt-
|
|
11777
|
-
name: "GPT-
|
|
11913
|
+
"gpt-4.1-mini": {
|
|
11914
|
+
id: "gpt-4.1-mini",
|
|
11915
|
+
name: "GPT-4.1 Mini",
|
|
11778
11916
|
provider: "openai",
|
|
11779
|
-
contextWindow:
|
|
11917
|
+
contextWindow: 1047576,
|
|
11780
11918
|
isVisionCapable: true,
|
|
11781
|
-
inputCostPer1kTokens:
|
|
11919
|
+
inputCostPer1kTokens: 4e-4,
|
|
11920
|
+
outputCostPer1kTokens: 16e-4,
|
|
11921
|
+
maxOutputTokens: 32768,
|
|
11922
|
+
supportsStreaming: true,
|
|
11923
|
+
isLocal: false
|
|
11924
|
+
},
|
|
11925
|
+
"gpt-4.1-nano": {
|
|
11926
|
+
id: "gpt-4.1-nano",
|
|
11927
|
+
name: "GPT-4.1 Nano",
|
|
11928
|
+
provider: "openai",
|
|
11929
|
+
contextWindow: 1047576,
|
|
11930
|
+
isVisionCapable: true,
|
|
11931
|
+
inputCostPer1kTokens: 1e-4,
|
|
11932
|
+
outputCostPer1kTokens: 4e-4,
|
|
11933
|
+
maxOutputTokens: 32768,
|
|
11934
|
+
supportsStreaming: true,
|
|
11935
|
+
isLocal: false
|
|
11936
|
+
},
|
|
11937
|
+
"gpt-4o": {
|
|
11938
|
+
id: "gpt-4o",
|
|
11939
|
+
name: "GPT-4o",
|
|
11940
|
+
provider: "openai",
|
|
11941
|
+
contextWindow: 128e3,
|
|
11942
|
+
isVisionCapable: true,
|
|
11943
|
+
inputCostPer1kTokens: 25e-4,
|
|
11782
11944
|
outputCostPer1kTokens: 0.01,
|
|
11783
|
-
maxOutputTokens:
|
|
11945
|
+
maxOutputTokens: 16e3,
|
|
11784
11946
|
supportsStreaming: true,
|
|
11785
11947
|
isLocal: false
|
|
11786
11948
|
},
|
|
11787
|
-
"gpt-
|
|
11788
|
-
id: "gpt-
|
|
11789
|
-
name: "GPT-
|
|
11949
|
+
"gpt-4o-mini": {
|
|
11950
|
+
id: "gpt-4o-mini",
|
|
11951
|
+
name: "GPT-4o Mini",
|
|
11790
11952
|
provider: "openai",
|
|
11791
|
-
contextWindow:
|
|
11953
|
+
contextWindow: 128e3,
|
|
11792
11954
|
isVisionCapable: true,
|
|
11793
|
-
inputCostPer1kTokens:
|
|
11794
|
-
outputCostPer1kTokens:
|
|
11795
|
-
maxOutputTokens:
|
|
11955
|
+
inputCostPer1kTokens: 15e-5,
|
|
11956
|
+
outputCostPer1kTokens: 6e-4,
|
|
11957
|
+
maxOutputTokens: 16e3,
|
|
11796
11958
|
supportsStreaming: true,
|
|
11797
11959
|
isLocal: false
|
|
11798
11960
|
},
|
|
11799
|
-
// Google
|
|
11800
|
-
|
|
11801
|
-
|
|
11802
|
-
|
|
11961
|
+
// Google Gemini — active GA + preview chat models. Gemini 2.0/1.5 are no
|
|
11962
|
+
// longer advertised because the Gemini API shut those generations down.
|
|
11963
|
+
"gemini-3.7-flash": {
|
|
11964
|
+
id: "gemini-3.7-flash",
|
|
11965
|
+
name: "Gemini 3.7 Flash",
|
|
11803
11966
|
provider: "gemini",
|
|
11804
|
-
contextWindow:
|
|
11967
|
+
contextWindow: 1048576,
|
|
11805
11968
|
isVisionCapable: true,
|
|
11806
|
-
inputCostPer1kTokens:
|
|
11807
|
-
outputCostPer1kTokens:
|
|
11808
|
-
maxOutputTokens:
|
|
11969
|
+
inputCostPer1kTokens: 75e-5,
|
|
11970
|
+
outputCostPer1kTokens: 375e-5,
|
|
11971
|
+
maxOutputTokens: 65536,
|
|
11809
11972
|
supportsStreaming: true,
|
|
11810
11973
|
isLocal: false
|
|
11811
11974
|
},
|
|
11812
|
-
"gemini-
|
|
11813
|
-
id: "gemini-
|
|
11814
|
-
name: "Gemini
|
|
11975
|
+
"gemini-3.6-flash": {
|
|
11976
|
+
id: "gemini-3.6-flash",
|
|
11977
|
+
name: "Gemini 3.6 Flash",
|
|
11815
11978
|
provider: "gemini",
|
|
11816
|
-
contextWindow:
|
|
11979
|
+
contextWindow: 1048576,
|
|
11817
11980
|
isVisionCapable: true,
|
|
11818
|
-
inputCostPer1kTokens:
|
|
11819
|
-
outputCostPer1kTokens:
|
|
11820
|
-
maxOutputTokens:
|
|
11981
|
+
inputCostPer1kTokens: 75e-5,
|
|
11982
|
+
outputCostPer1kTokens: 375e-5,
|
|
11983
|
+
maxOutputTokens: 65536,
|
|
11821
11984
|
supportsStreaming: true,
|
|
11822
11985
|
isLocal: false
|
|
11823
11986
|
},
|
|
11824
|
-
"gemini-
|
|
11825
|
-
id: "gemini-
|
|
11826
|
-
name: "Gemini
|
|
11987
|
+
"gemini-3.5-flash": {
|
|
11988
|
+
id: "gemini-3.5-flash",
|
|
11989
|
+
name: "Gemini 3.5 Flash",
|
|
11827
11990
|
provider: "gemini",
|
|
11828
|
-
contextWindow:
|
|
11991
|
+
contextWindow: 1048576,
|
|
11829
11992
|
isVisionCapable: true,
|
|
11830
|
-
inputCostPer1kTokens:
|
|
11831
|
-
outputCostPer1kTokens:
|
|
11832
|
-
maxOutputTokens:
|
|
11993
|
+
inputCostPer1kTokens: 15e-4,
|
|
11994
|
+
outputCostPer1kTokens: 9e-3,
|
|
11995
|
+
maxOutputTokens: 65536,
|
|
11996
|
+
supportsStreaming: true,
|
|
11997
|
+
isLocal: false
|
|
11998
|
+
},
|
|
11999
|
+
"gemini-3.5-flash-lite": {
|
|
12000
|
+
id: "gemini-3.5-flash-lite",
|
|
12001
|
+
name: "Gemini 3.5 Flash-Lite",
|
|
12002
|
+
provider: "gemini",
|
|
12003
|
+
contextWindow: 1048576,
|
|
12004
|
+
isVisionCapable: true,
|
|
12005
|
+
inputCostPer1kTokens: 3e-4,
|
|
12006
|
+
outputCostPer1kTokens: 25e-4,
|
|
12007
|
+
maxOutputTokens: 65536,
|
|
12008
|
+
supportsStreaming: true,
|
|
12009
|
+
isLocal: false
|
|
12010
|
+
},
|
|
12011
|
+
"gemini-3.1-flash-lite": {
|
|
12012
|
+
id: "gemini-3.1-flash-lite",
|
|
12013
|
+
name: "Gemini 3.1 Flash-Lite",
|
|
12014
|
+
provider: "gemini",
|
|
12015
|
+
contextWindow: 1048576,
|
|
12016
|
+
isVisionCapable: true,
|
|
12017
|
+
inputCostPer1kTokens: 25e-5,
|
|
12018
|
+
outputCostPer1kTokens: 15e-4,
|
|
12019
|
+
maxOutputTokens: 65536,
|
|
12020
|
+
supportsStreaming: true,
|
|
12021
|
+
isLocal: false
|
|
12022
|
+
},
|
|
12023
|
+
"gemini-3.1-pro-preview": {
|
|
12024
|
+
id: "gemini-3.1-pro-preview",
|
|
12025
|
+
name: "Gemini 3.1 Pro Preview",
|
|
12026
|
+
provider: "gemini",
|
|
12027
|
+
contextWindow: 1048576,
|
|
12028
|
+
isVisionCapable: true,
|
|
12029
|
+
inputCostPer1kTokens: 2e-3,
|
|
12030
|
+
outputCostPer1kTokens: 0.012,
|
|
12031
|
+
maxOutputTokens: 65536,
|
|
12032
|
+
supportsStreaming: true,
|
|
12033
|
+
isLocal: false
|
|
12034
|
+
},
|
|
12035
|
+
"gemini-3-flash-preview": {
|
|
12036
|
+
id: "gemini-3-flash-preview",
|
|
12037
|
+
name: "Gemini 3 Flash Preview",
|
|
12038
|
+
provider: "gemini",
|
|
12039
|
+
contextWindow: 1048576,
|
|
12040
|
+
isVisionCapable: true,
|
|
12041
|
+
inputCostPer1kTokens: 5e-4,
|
|
12042
|
+
outputCostPer1kTokens: 3e-3,
|
|
12043
|
+
maxOutputTokens: 65536,
|
|
11833
12044
|
supportsStreaming: true,
|
|
11834
12045
|
isLocal: false
|
|
11835
12046
|
},
|
|
@@ -11837,7 +12048,7 @@ var MODELS = {
|
|
|
11837
12048
|
id: "gemini-2.5-pro",
|
|
11838
12049
|
name: "Gemini 2.5 Pro",
|
|
11839
12050
|
provider: "gemini",
|
|
11840
|
-
contextWindow:
|
|
12051
|
+
contextWindow: 1048576,
|
|
11841
12052
|
isVisionCapable: true,
|
|
11842
12053
|
inputCostPer1kTokens: 125e-5,
|
|
11843
12054
|
outputCostPer1kTokens: 0.01,
|
|
@@ -11849,10 +12060,22 @@ var MODELS = {
|
|
|
11849
12060
|
id: "gemini-2.5-flash",
|
|
11850
12061
|
name: "Gemini 2.5 Flash",
|
|
11851
12062
|
provider: "gemini",
|
|
11852
|
-
contextWindow:
|
|
12063
|
+
contextWindow: 1048576,
|
|
11853
12064
|
isVisionCapable: true,
|
|
11854
|
-
inputCostPer1kTokens:
|
|
11855
|
-
outputCostPer1kTokens:
|
|
12065
|
+
inputCostPer1kTokens: 3e-4,
|
|
12066
|
+
outputCostPer1kTokens: 25e-4,
|
|
12067
|
+
maxOutputTokens: 65536,
|
|
12068
|
+
supportsStreaming: true,
|
|
12069
|
+
isLocal: false
|
|
12070
|
+
},
|
|
12071
|
+
"gemini-2.5-flash-lite": {
|
|
12072
|
+
id: "gemini-2.5-flash-lite",
|
|
12073
|
+
name: "Gemini 2.5 Flash-Lite",
|
|
12074
|
+
provider: "gemini",
|
|
12075
|
+
contextWindow: 1048576,
|
|
12076
|
+
isVisionCapable: true,
|
|
12077
|
+
inputCostPer1kTokens: 1e-4,
|
|
12078
|
+
outputCostPer1kTokens: 4e-4,
|
|
11856
12079
|
maxOutputTokens: 65536,
|
|
11857
12080
|
supportsStreaming: true,
|
|
11858
12081
|
isLocal: false
|
|
@@ -11922,43 +12145,57 @@ for (const _m of Object.values(MODELS)) {
|
|
|
11922
12145
|
_m.pricingUnknown = false;
|
|
11923
12146
|
}
|
|
11924
12147
|
var T1_MODEL_PRIORITY = [
|
|
12148
|
+
"gpt-5.6-sol",
|
|
12149
|
+
"claude-fable-5",
|
|
12150
|
+
"claude-opus-5",
|
|
11925
12151
|
"gpt-5.5",
|
|
11926
12152
|
"gpt-5.4",
|
|
12153
|
+
"gemini-3.1-pro-preview",
|
|
12154
|
+
"claude-opus-4-8",
|
|
12155
|
+
"claude-sonnet-5",
|
|
12156
|
+
"gemini-3.7-flash",
|
|
12157
|
+
"gpt-5.2",
|
|
11927
12158
|
"gpt-5",
|
|
11928
|
-
"claude-opus-4",
|
|
11929
|
-
"claude-sonnet-4",
|
|
11930
12159
|
"gemini-2.5-pro",
|
|
11931
12160
|
"gpt-4.1",
|
|
11932
|
-
"gpt-4o"
|
|
11933
|
-
"gemini-1.5-pro"
|
|
12161
|
+
"gpt-4o"
|
|
11934
12162
|
];
|
|
11935
12163
|
var T2_MODEL_PRIORITY = [
|
|
12164
|
+
"gpt-5.6-terra",
|
|
12165
|
+
"claude-sonnet-5",
|
|
12166
|
+
"gemini-3.7-flash",
|
|
12167
|
+
"gemini-3.6-flash",
|
|
11936
12168
|
"gpt-5.4-mini",
|
|
11937
12169
|
"gpt-5-mini",
|
|
11938
|
-
"claude-sonnet-4",
|
|
12170
|
+
"claude-sonnet-4-6",
|
|
11939
12171
|
"claude-haiku-4-5",
|
|
11940
|
-
"
|
|
12172
|
+
"gemini-3.5-flash",
|
|
11941
12173
|
"gemini-2.5-flash",
|
|
11942
12174
|
"gpt-4.1-mini",
|
|
11943
12175
|
"gpt-4o-mini",
|
|
11944
|
-
"gemini-2.0-flash",
|
|
11945
12176
|
"llama3:70b"
|
|
11946
12177
|
];
|
|
11947
12178
|
var T3_MODEL_PRIORITY = [
|
|
12179
|
+
"gpt-5.6-luna",
|
|
12180
|
+
"gpt-5.4-nano",
|
|
12181
|
+
"gemini-3.5-flash-lite",
|
|
12182
|
+
"gemini-3.1-flash-lite",
|
|
12183
|
+
"claude-haiku-4-5",
|
|
11948
12184
|
"gpt-5-nano",
|
|
12185
|
+
"gemini-2.5-flash-lite",
|
|
11949
12186
|
"llama3.2:3b",
|
|
11950
12187
|
"mistral:7b",
|
|
11951
|
-
"claude-haiku-4-5",
|
|
11952
|
-
"claude-haiku-3-5",
|
|
11953
12188
|
"gpt-4.1-nano",
|
|
11954
12189
|
"gpt-4o-mini",
|
|
11955
|
-
"gemini-2.5-flash"
|
|
11956
|
-
"gemini-2.0-flash"
|
|
12190
|
+
"gemini-2.5-flash"
|
|
11957
12191
|
];
|
|
11958
12192
|
var VISION_MODEL_PRIORITY = [
|
|
11959
|
-
"
|
|
12193
|
+
"gpt-5.6-sol",
|
|
12194
|
+
"claude-fable-5",
|
|
12195
|
+
"claude-sonnet-5",
|
|
12196
|
+
"gemini-3.7-flash",
|
|
11960
12197
|
"gpt-4o",
|
|
11961
|
-
"gemini-2.
|
|
12198
|
+
"gemini-2.5-flash",
|
|
11962
12199
|
"llava"
|
|
11963
12200
|
];
|
|
11964
12201
|
var COMPLEXITY_T2_COUNT = {
|
|
@@ -12139,7 +12376,7 @@ function isChatModel(id, methods) {
|
|
|
12139
12376
|
return !NON_CHAT.test(id);
|
|
12140
12377
|
}
|
|
12141
12378
|
try {
|
|
12142
|
-
|
|
12379
|
+
dnsCallback.setDefaultResultOrder("ipv4first");
|
|
12143
12380
|
} catch {
|
|
12144
12381
|
}
|
|
12145
12382
|
function preferIpv4Host(url) {
|
|
@@ -12155,8 +12392,8 @@ function normalizeEndpoint(url) {
|
|
|
12155
12392
|
if (!raw) return "";
|
|
12156
12393
|
try {
|
|
12157
12394
|
const u = new URL(raw);
|
|
12158
|
-
const
|
|
12159
|
-
return `${u.protocol}//${u.host.toLowerCase()}${
|
|
12395
|
+
const path30 = stripTrailingSlashes(`${u.pathname}${u.search}`);
|
|
12396
|
+
return `${u.protocol}//${u.host.toLowerCase()}${path30}`;
|
|
12160
12397
|
} catch {
|
|
12161
12398
|
return raw.toLowerCase();
|
|
12162
12399
|
}
|
|
@@ -12823,52 +13060,127 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
12823
13060
|
}
|
|
12824
13061
|
};
|
|
12825
13062
|
|
|
13063
|
+
// src/providers/cloud-model-catalog.json
|
|
13064
|
+
var cloud_model_catalog_default = {
|
|
13065
|
+
models: [
|
|
13066
|
+
{ id: "gpt-5.6-sol", name: "GPT-5.6 Sol", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13067
|
+
{ id: "gpt-5.6-terra", name: "GPT-5.6 Terra", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13068
|
+
{ id: "gpt-5.6-luna", name: "GPT-5.6 Luna", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13069
|
+
{ id: "gpt-5.5", name: "GPT-5.5", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13070
|
+
{ id: "gpt-5.4", name: "GPT-5.4", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 105e4, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13071
|
+
{ id: "gpt-5.4-mini", name: "GPT-5.4 Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13072
|
+
{ id: "gpt-5.4-nano", name: "GPT-5.4 Nano", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13073
|
+
{ id: "gpt-5", name: "GPT-5", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13074
|
+
{ id: "gpt-5-mini", name: "GPT-5 Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13075
|
+
{ id: "gpt-5-nano", name: "GPT-5 Nano", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 4e5, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13076
|
+
{ id: "gpt-4.1", name: "GPT-4.1", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 1047576, maxOutputTokens: 32768, vision: true, toolUse: true },
|
|
13077
|
+
{ id: "gpt-4.1-mini", name: "GPT-4.1 Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 1047576, maxOutputTokens: 32768, vision: true, toolUse: true },
|
|
13078
|
+
{ id: "gpt-4.1-nano", name: "GPT-4.1 Nano", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 1047576, maxOutputTokens: 32768, vision: true, toolUse: true },
|
|
13079
|
+
{ id: "gpt-4o", name: "GPT-4o", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 128e3, maxOutputTokens: 16384, vision: true, toolUse: true },
|
|
13080
|
+
{ id: "gpt-4o-mini", name: "GPT-4o Mini", providers: ["openai", "azure"], lifecycle: "ga", contextWindow: 128e3, maxOutputTokens: 16384, vision: true, toolUse: true },
|
|
13081
|
+
{ id: "claude-fable-5", name: "Claude Fable 5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13082
|
+
{ id: "claude-opus-5", name: "Claude Opus 5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13083
|
+
{ id: "claude-opus-4-8", name: "Claude Opus 4.8", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13084
|
+
{ id: "claude-opus-4-7", name: "Claude Opus 4.7", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13085
|
+
{ id: "claude-opus-4-6", name: "Claude Opus 4.6", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, vision: true, toolUse: true },
|
|
13086
|
+
{ id: "claude-opus-4-5-20251101", name: "Claude Opus 4.5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 2e5, vision: true, toolUse: true },
|
|
13087
|
+
{ id: "claude-sonnet-5", name: "Claude Sonnet 5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13088
|
+
{ id: "claude-sonnet-4-6", name: "Claude Sonnet 4.6", providers: ["anthropic"], lifecycle: "ga", contextWindow: 1e6, maxOutputTokens: 128e3, vision: true, toolUse: true },
|
|
13089
|
+
{ id: "claude-sonnet-4-5-20250929", name: "Claude Sonnet 4.5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 2e5, vision: true, toolUse: true },
|
|
13090
|
+
{ id: "claude-haiku-4-5-20251001", name: "Claude Haiku 4.5", providers: ["anthropic"], lifecycle: "ga", contextWindow: 2e5, maxOutputTokens: 64e3, vision: true, toolUse: true },
|
|
13091
|
+
{ id: "gemini-3.7-flash", name: "Gemini 3.7 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13092
|
+
{ id: "gemini-3.6-flash", name: "Gemini 3.6 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13093
|
+
{ id: "gemini-3.5-flash", name: "Gemini 3.5 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13094
|
+
{ id: "gemini-3.5-flash-lite", name: "Gemini 3.5 Flash-Lite", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13095
|
+
{ id: "gemini-3.1-flash-lite", name: "Gemini 3.1 Flash-Lite", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13096
|
+
{ id: "gemini-3.1-pro-preview", name: "Gemini 3.1 Pro", providers: ["gemini"], lifecycle: "preview", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13097
|
+
{ id: "gemini-3-flash-preview", name: "Gemini 3 Flash", providers: ["gemini"], lifecycle: "preview", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13098
|
+
{ id: "gemini-2.5-pro", name: "Gemini 2.5 Pro", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13099
|
+
{ id: "gemini-2.5-flash", name: "Gemini 2.5 Flash", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true },
|
|
13100
|
+
{ id: "gemini-2.5-flash-lite", name: "Gemini 2.5 Flash-Lite", providers: ["gemini"], lifecycle: "ga", contextWindow: 1048576, maxOutputTokens: 65536, vision: true, toolUse: true }
|
|
13101
|
+
]
|
|
13102
|
+
};
|
|
13103
|
+
|
|
12826
13104
|
// src/providers/azure.ts
|
|
12827
13105
|
var DEFAULT_AZURE_API_VERSION = "2024-12-01-preview";
|
|
12828
|
-
var
|
|
12829
|
-
|
|
12830
|
-
|
|
12831
|
-
|
|
12832
|
-
[/
|
|
12833
|
-
|
|
12834
|
-
|
|
12835
|
-
|
|
12836
|
-
|
|
12837
|
-
|
|
12838
|
-
[/gpt-?4o-mini/, "gpt-4o-mini"],
|
|
12839
|
-
[/gpt-?4o/, "gpt-4o"]
|
|
12840
|
-
];
|
|
12841
|
-
var AZURE_BASE_MODELS = AZURE_BASE_MODEL_RULES.map(([, id]) => id);
|
|
13106
|
+
var CLOUD_MODELS = cloud_model_catalog_default.models;
|
|
13107
|
+
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));
|
|
13108
|
+
var AZURE_BASE_MODELS = AZURE_CATALOG.map((m) => m.id);
|
|
13109
|
+
function compactModelId(id) {
|
|
13110
|
+
return id.toLowerCase().replace(/[^a-z0-9.]/g, "");
|
|
13111
|
+
}
|
|
13112
|
+
function catalogEntry(id) {
|
|
13113
|
+
const n = id.toLowerCase();
|
|
13114
|
+
return CLOUD_MODELS.find((m) => m.id.toLowerCase() === n);
|
|
13115
|
+
}
|
|
12842
13116
|
function inferAzureBaseModel(deploymentName) {
|
|
12843
13117
|
const n = deploymentName.toLowerCase();
|
|
12844
|
-
|
|
13118
|
+
const compact = compactModelId(n);
|
|
13119
|
+
const hasBareGpt56Alias = compact.includes("gpt5.6");
|
|
13120
|
+
for (const model of AZURE_CATALOG) {
|
|
13121
|
+
if (hasBareGpt56Alias && model.id === "gpt-5") continue;
|
|
13122
|
+
if (n.includes(model.id.toLowerCase()) || compact.includes(compactModelId(model.id))) {
|
|
13123
|
+
return model.id;
|
|
13124
|
+
}
|
|
13125
|
+
}
|
|
13126
|
+
if (hasBareGpt56Alias) return "gpt-5.6-sol";
|
|
13127
|
+
const gpt5At = compact.indexOf("gpt5");
|
|
13128
|
+
if (gpt5At !== -1) {
|
|
13129
|
+
const suffix = compact.slice(gpt5At + "gpt5".length);
|
|
13130
|
+
if (suffix.includes("nano")) return "gpt-5-nano";
|
|
13131
|
+
if (suffix.includes("mini")) return "gpt-5-mini";
|
|
13132
|
+
return "gpt-5";
|
|
13133
|
+
}
|
|
13134
|
+
if (compact.includes("gpt4.1nano")) return "gpt-4.1-nano";
|
|
13135
|
+
if (compact.includes("gpt4.1mini")) return "gpt-4.1-mini";
|
|
13136
|
+
if (compact.includes("gpt4.1")) return "gpt-4.1";
|
|
13137
|
+
if (compact.includes("gpt4omini")) return "gpt-4o-mini";
|
|
13138
|
+
if (compact.includes("gpt4o")) return "gpt-4o";
|
|
12845
13139
|
return null;
|
|
12846
13140
|
}
|
|
13141
|
+
function catalogModelInfo(baseModelId) {
|
|
13142
|
+
const entry = catalogEntry(baseModelId);
|
|
13143
|
+
if (!entry?.contextWindow) return void 0;
|
|
13144
|
+
return {
|
|
13145
|
+
id: entry.id,
|
|
13146
|
+
name: entry.name,
|
|
13147
|
+
provider: "openai",
|
|
13148
|
+
contextWindow: entry.contextWindow,
|
|
13149
|
+
isVisionCapable: entry.vision ?? false,
|
|
13150
|
+
inputCostPer1kTokens: 0,
|
|
13151
|
+
outputCostPer1kTokens: 0,
|
|
13152
|
+
maxOutputTokens: entry.maxOutputTokens ?? 16e3,
|
|
13153
|
+
supportsStreaming: true,
|
|
13154
|
+
isLocal: false,
|
|
13155
|
+
supportsToolUse: entry.toolUse ?? true,
|
|
13156
|
+
pricingUnknown: true
|
|
13157
|
+
};
|
|
13158
|
+
}
|
|
12847
13159
|
function azureModelForDeployment(cfg) {
|
|
12848
13160
|
if (cfg.type !== "azure" || !cfg.deploymentName?.trim()) return null;
|
|
12849
13161
|
const id = cfg.deploymentName.trim();
|
|
12850
13162
|
const name = cfg.label?.trim() || id;
|
|
12851
13163
|
const baseModelId = cfg.model?.trim() || inferAzureBaseModel(id) || void 0;
|
|
12852
|
-
const base = baseModelId ? MODELS[baseModelId] : void 0;
|
|
12853
|
-
if (base) {
|
|
13164
|
+
const base = baseModelId ? MODELS[baseModelId] ?? catalogModelInfo(baseModelId) : void 0;
|
|
13165
|
+
if (base && baseModelId) {
|
|
12854
13166
|
const azurePrice = resolvePricing(
|
|
12855
13167
|
{ id, provider: "azure", isLocal: false, baseModelId },
|
|
12856
13168
|
{ region: cfg.region }
|
|
12857
13169
|
);
|
|
13170
|
+
const exactAzurePrice = !azurePrice.unknown && !azurePrice.estimatedFromProvider;
|
|
13171
|
+
const nativeToolsOnThisTransport = /^gpt-5\.6-(?:sol|terra|luna)$/i.test(baseModelId) ? false : base.supportsToolUse ?? true;
|
|
12858
13172
|
return {
|
|
12859
13173
|
...base,
|
|
12860
13174
|
id,
|
|
12861
|
-
// callable deployment name
|
|
12862
13175
|
name,
|
|
12863
13176
|
provider: "azure",
|
|
12864
13177
|
baseModelId,
|
|
12865
|
-
|
|
12866
|
-
|
|
12867
|
-
...azurePrice.unknown ? {} : {
|
|
13178
|
+
supportsToolUse: nativeToolsOnThisTransport,
|
|
13179
|
+
...!azurePrice.unknown ? {
|
|
12868
13180
|
inputCostPer1kTokens: azurePrice.input,
|
|
12869
13181
|
outputCostPer1kTokens: azurePrice.output,
|
|
12870
|
-
pricingUnknown:
|
|
12871
|
-
}
|
|
13182
|
+
pricingUnknown: !exactAzurePrice
|
|
13183
|
+
} : { pricingUnknown: true }
|
|
12872
13184
|
};
|
|
12873
13185
|
}
|
|
12874
13186
|
return {
|
|
@@ -12883,18 +13195,23 @@ function azureModelForDeployment(cfg) {
|
|
|
12883
13195
|
maxOutputTokens: 16e3,
|
|
12884
13196
|
supportsStreaming: true,
|
|
12885
13197
|
isLocal: false,
|
|
12886
|
-
supportsToolUse: true
|
|
13198
|
+
supportsToolUse: true,
|
|
13199
|
+
pricingUnknown: true
|
|
12887
13200
|
};
|
|
12888
13201
|
}
|
|
13202
|
+
function stripTrailingSlashes3(value) {
|
|
13203
|
+
let end = value.length;
|
|
13204
|
+
while (end > 0 && value.charCodeAt(end - 1) === 47) end--;
|
|
13205
|
+
return value.slice(0, end);
|
|
13206
|
+
}
|
|
12889
13207
|
var AzureOpenAIProvider = class extends OpenAIProvider {
|
|
12890
13208
|
constructor(config, model) {
|
|
12891
13209
|
const rawUrl = config.baseUrl ?? AZURE_BASE_URL_TEMPLATE.replace("{resource}", "YOUR_RESOURCE");
|
|
12892
|
-
const endpoint = rawUrl
|
|
13210
|
+
const endpoint = stripTrailingSlashes3(rawUrl);
|
|
12893
13211
|
super(
|
|
12894
13212
|
{
|
|
12895
13213
|
...config,
|
|
12896
13214
|
baseUrl: endpoint
|
|
12897
|
-
// Kept for superclass compatibility if it reads it
|
|
12898
13215
|
},
|
|
12899
13216
|
model
|
|
12900
13217
|
);
|
|
@@ -14637,7 +14954,7 @@ var Keystore = class {
|
|
|
14637
14954
|
const creds = await this.keytar.findCredentials(KEYTAR_SERVICE);
|
|
14638
14955
|
this.cache = Object.fromEntries(creds.map((c) => [c.account, c.password]));
|
|
14639
14956
|
this.backend = "keytar";
|
|
14640
|
-
if (password &&
|
|
14957
|
+
if (password && fs27.existsSync(this.storePath)) {
|
|
14641
14958
|
try {
|
|
14642
14959
|
const fileEntries = this.decryptFile(password);
|
|
14643
14960
|
for (const [k, v] of Object.entries(fileEntries)) {
|
|
@@ -14656,7 +14973,7 @@ var Keystore = class {
|
|
|
14656
14973
|
"Keystore unlock requires a password because the OS keychain (keytar) is not available on this system."
|
|
14657
14974
|
);
|
|
14658
14975
|
}
|
|
14659
|
-
if (!
|
|
14976
|
+
if (!fs27.existsSync(this.storePath)) {
|
|
14660
14977
|
const salt = crypto.randomBytes(SALT_LEN);
|
|
14661
14978
|
this.masterKey = this.deriveKey(password, salt);
|
|
14662
14979
|
this.writeWithSalt({}, salt);
|
|
@@ -14670,7 +14987,7 @@ var Keystore = class {
|
|
|
14670
14987
|
}
|
|
14671
14988
|
/** Synchronous legacy unlock kept for AES-only environments. */
|
|
14672
14989
|
unlockSync(password) {
|
|
14673
|
-
if (!
|
|
14990
|
+
if (!fs27.existsSync(this.storePath)) {
|
|
14674
14991
|
const salt = crypto.randomBytes(SALT_LEN);
|
|
14675
14992
|
this.masterKey = this.deriveKey(password, salt);
|
|
14676
14993
|
this.writeWithSalt({}, salt);
|
|
@@ -14728,7 +15045,7 @@ var Keystore = class {
|
|
|
14728
15045
|
}
|
|
14729
15046
|
}
|
|
14730
15047
|
decryptFile(password, knownSalt) {
|
|
14731
|
-
if (!
|
|
15048
|
+
if (!fs27.existsSync(this.storePath)) return {};
|
|
14732
15049
|
try {
|
|
14733
15050
|
const { salt, ciphertext, iv, tag } = this.readRaw();
|
|
14734
15051
|
const useSalt = knownSalt ?? salt;
|
|
@@ -14750,8 +15067,8 @@ var Keystore = class {
|
|
|
14750
15067
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
14751
15068
|
const tag = cipher.getAuthTag();
|
|
14752
15069
|
const out = Buffer.concat([raw.salt, iv, tag, ciphertext]);
|
|
14753
|
-
|
|
14754
|
-
|
|
15070
|
+
fs27.mkdirSync(path28.dirname(this.storePath), { recursive: true });
|
|
15071
|
+
fs27.writeFileSync(this.storePath, out, { mode: 384 });
|
|
14755
15072
|
}
|
|
14756
15073
|
writeWithSalt(data, salt) {
|
|
14757
15074
|
if (!this.masterKey) throw new Error("writeWithSalt called before masterKey was set");
|
|
@@ -14761,11 +15078,11 @@ var Keystore = class {
|
|
|
14761
15078
|
const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
|
|
14762
15079
|
const tag = cipher.getAuthTag();
|
|
14763
15080
|
const out = Buffer.concat([salt, iv, tag, ciphertext]);
|
|
14764
|
-
|
|
14765
|
-
|
|
15081
|
+
fs27.mkdirSync(path28.dirname(this.storePath), { recursive: true });
|
|
15082
|
+
fs27.writeFileSync(this.storePath, out, { mode: 384 });
|
|
14766
15083
|
}
|
|
14767
15084
|
readRaw() {
|
|
14768
|
-
const buf =
|
|
15085
|
+
const buf = fs27.readFileSync(this.storePath);
|
|
14769
15086
|
let offset = 0;
|
|
14770
15087
|
const salt = buf.subarray(offset, offset + SALT_LEN);
|
|
14771
15088
|
offset += SALT_LEN;
|
|
@@ -14798,7 +15115,7 @@ var CascadeIgnore = class {
|
|
|
14798
15115
|
]);
|
|
14799
15116
|
}
|
|
14800
15117
|
async load(workspacePath) {
|
|
14801
|
-
const filePath =
|
|
15118
|
+
const filePath = path28.join(workspacePath, ".cascadeignore");
|
|
14802
15119
|
try {
|
|
14803
15120
|
const content = await fs11.readFile(filePath, "utf-8");
|
|
14804
15121
|
const lines = content.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
|
|
@@ -14809,7 +15126,7 @@ var CascadeIgnore = class {
|
|
|
14809
15126
|
}
|
|
14810
15127
|
isIgnored(filePath, workspacePath) {
|
|
14811
15128
|
try {
|
|
14812
|
-
const relative = workspacePath ?
|
|
15129
|
+
const relative = workspacePath ? path28.relative(workspacePath, filePath) : filePath;
|
|
14813
15130
|
return this.ig.ignores(relative);
|
|
14814
15131
|
} catch {
|
|
14815
15132
|
return false;
|
|
@@ -14820,7 +15137,7 @@ var CascadeIgnore = class {
|
|
|
14820
15137
|
}
|
|
14821
15138
|
};
|
|
14822
15139
|
async function loadCascadeMd(workspacePath) {
|
|
14823
|
-
const filePath =
|
|
15140
|
+
const filePath = path28.join(workspacePath, "CASCADE.md");
|
|
14824
15141
|
try {
|
|
14825
15142
|
const raw = await fs11.readFile(filePath, "utf-8");
|
|
14826
15143
|
return parseCascadeMd(raw);
|
|
@@ -14851,7 +15168,7 @@ ${raw.trim()}`;
|
|
|
14851
15168
|
var MemoryStore = class _MemoryStore {
|
|
14852
15169
|
db;
|
|
14853
15170
|
constructor(dbPath) {
|
|
14854
|
-
|
|
15171
|
+
fs27.mkdirSync(path28.dirname(dbPath), { recursive: true });
|
|
14855
15172
|
try {
|
|
14856
15173
|
this.db = new Database2(dbPath, { timeout: 5e3 });
|
|
14857
15174
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -15749,13 +16066,38 @@ var WebSearchConfigSchema = z.object({
|
|
|
15749
16066
|
/** Tavily API key — get one at https://tavily.com */
|
|
15750
16067
|
tavilyApiKey: z.string().optional(),
|
|
15751
16068
|
/** Max results per search (default 5) */
|
|
15752
|
-
maxResults: z.number().default(5)
|
|
16069
|
+
maxResults: z.number().default(5),
|
|
16070
|
+
/**
|
|
16071
|
+
* Send `searxngUrl` through the SSRF guard. Off for a local run, where the
|
|
16072
|
+
* URL is the machine owner's own config and a self-hosted SearXNG is
|
|
16073
|
+
* routinely on a private address; on for a host that accepts it from a
|
|
16074
|
+
* request. See `src/tools/web-search.ts`.
|
|
16075
|
+
*
|
|
16076
|
+
* It has to be named HERE, not just on the TypeScript interfaces: the config
|
|
16077
|
+
* passes through `CascadeConfigSchema.parse()` on its way into a run, and
|
|
16078
|
+
* zod drops every key this object does not declare.
|
|
16079
|
+
*/
|
|
16080
|
+
guardSearxngUrl: z.boolean().optional()
|
|
15753
16081
|
});
|
|
15754
16082
|
var ToolsConfigSchema = z.object({
|
|
15755
16083
|
shellAllowlist: z.array(z.string()).default([]),
|
|
15756
16084
|
shellBlocklist: z.array(z.string()).default(["rm -rf", "sudo rm", "format", "mkfs"]),
|
|
15757
16085
|
requireApprovalFor: z.array(z.string()).default([]),
|
|
15758
16086
|
browserEnabled: z.boolean().default(false),
|
|
16087
|
+
/**
|
|
16088
|
+
* Let a run ACT on the page open in the desktop's built-in browser — click,
|
|
16089
|
+
* type, submit — not just read it.
|
|
16090
|
+
*
|
|
16091
|
+
* Distinct from `browserEnabled`, which turns on the headless Playwright
|
|
16092
|
+
* tool. That one drives a throwaway browser with an empty cookie jar; this
|
|
16093
|
+
* one drives the session the user is signed into, which is a different order
|
|
16094
|
+
* of consequence and so gets its own switch rather than riding on that one.
|
|
16095
|
+
*
|
|
16096
|
+
* Off by default. Turning it on does not hand anything over silently: the
|
|
16097
|
+
* tool still goes through the approval chain, and the browser panel carries a
|
|
16098
|
+
* stop control for the run that is acting.
|
|
16099
|
+
*/
|
|
16100
|
+
agentBrowserControl: z.boolean().default(false),
|
|
15759
16101
|
mcpServers: z.array(McpServerConfigSchema).optional(),
|
|
15760
16102
|
mcpTrusted: z.array(z.string()).optional(),
|
|
15761
16103
|
/** Web search backends — at least one should be configured for best results */
|
|
@@ -16153,7 +16495,7 @@ function credentialEndpointsConflict(type, rowEndpoint, credentialEndpoint) {
|
|
|
16153
16495
|
|
|
16154
16496
|
// src/config/global-credentials.ts
|
|
16155
16497
|
function credentialsPath(globalDir2) {
|
|
16156
|
-
return
|
|
16498
|
+
return path28.join(globalDir2, GLOBAL_CREDENTIALS_FILE);
|
|
16157
16499
|
}
|
|
16158
16500
|
function providerKey(p) {
|
|
16159
16501
|
return p.type;
|
|
@@ -16183,7 +16525,7 @@ function isPersistable(p) {
|
|
|
16183
16525
|
}
|
|
16184
16526
|
function loadGlobalCredentials(globalDir2) {
|
|
16185
16527
|
try {
|
|
16186
|
-
const raw =
|
|
16528
|
+
const raw = fs27.readFileSync(credentialsPath(globalDir2), "utf-8");
|
|
16187
16529
|
const parsed = JSON.parse(raw);
|
|
16188
16530
|
if (!Array.isArray(parsed.providers)) return [];
|
|
16189
16531
|
return parsed.providers.filter(
|
|
@@ -16195,13 +16537,13 @@ function loadGlobalCredentials(globalDir2) {
|
|
|
16195
16537
|
}
|
|
16196
16538
|
function saveGlobalCredentials(globalDir2, providers) {
|
|
16197
16539
|
const filePath = credentialsPath(globalDir2);
|
|
16198
|
-
|
|
16540
|
+
fs27.mkdirSync(globalDir2, { recursive: true, mode: 448 });
|
|
16199
16541
|
const body = { version: 1, providers: providers.filter(isPersistable) };
|
|
16200
16542
|
const tmp = `${filePath}.tmp`;
|
|
16201
|
-
|
|
16202
|
-
|
|
16543
|
+
fs27.writeFileSync(tmp, JSON.stringify(body, null, 2), { encoding: "utf-8", mode: 384 });
|
|
16544
|
+
fs27.renameSync(tmp, filePath);
|
|
16203
16545
|
try {
|
|
16204
|
-
|
|
16546
|
+
fs27.chmodSync(filePath, 384);
|
|
16205
16547
|
} catch {
|
|
16206
16548
|
}
|
|
16207
16549
|
}
|
|
@@ -16602,7 +16944,7 @@ var ConfigManager = class {
|
|
|
16602
16944
|
/** `globalDirOverride` exists for tests — never point it at the real home dir there. */
|
|
16603
16945
|
constructor(workspacePath = process.cwd(), globalDirOverride) {
|
|
16604
16946
|
this.workspacePath = workspacePath;
|
|
16605
|
-
this.globalDir = globalDirOverride ??
|
|
16947
|
+
this.globalDir = globalDirOverride ?? path28.join(os6.homedir(), GLOBAL_CONFIG_DIR);
|
|
16606
16948
|
}
|
|
16607
16949
|
async load() {
|
|
16608
16950
|
this.config = await this.loadConfig();
|
|
@@ -16616,8 +16958,8 @@ var ConfigManager = class {
|
|
|
16616
16958
|
this.ignore = new CascadeIgnore();
|
|
16617
16959
|
await this.ignore.load(this.workspacePath);
|
|
16618
16960
|
this.cascadeMd = await loadCascadeMd(this.workspacePath);
|
|
16619
|
-
this.keystore = new Keystore(
|
|
16620
|
-
this.store = new MemoryStore(
|
|
16961
|
+
this.keystore = new Keystore(path28.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
|
|
16962
|
+
this.store = new MemoryStore(path28.join(this.workspacePath, CASCADE_DB_FILE));
|
|
16621
16963
|
const globalRevoked = stripRevokedCredentials(loadGlobalCredentials(this.globalDir));
|
|
16622
16964
|
const globalCreds = filterRetiredCredentials(globalRevoked.kept);
|
|
16623
16965
|
if (globalRevoked.removed > 0) this.revokedCredentials += globalRevoked.removed;
|
|
@@ -16690,8 +17032,8 @@ var ConfigManager = class {
|
|
|
16690
17032
|
* never reached the disk (see `commitSettings`).
|
|
16691
17033
|
*/
|
|
16692
17034
|
async save(config = this.config) {
|
|
16693
|
-
const configPath =
|
|
16694
|
-
await fs11.mkdir(
|
|
17035
|
+
const configPath = path28.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
17036
|
+
await fs11.mkdir(path28.dirname(configPath), { recursive: true });
|
|
16695
17037
|
await fs11.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
16696
17038
|
try {
|
|
16697
17039
|
saveGlobalCredentials(this.globalDir, config.providers);
|
|
@@ -16741,7 +17083,7 @@ var ConfigManager = class {
|
|
|
16741
17083
|
this.revokedCredentials = 0;
|
|
16742
17084
|
this.revokedPins = [];
|
|
16743
17085
|
this.revokedNotice = void 0;
|
|
16744
|
-
const configPath =
|
|
17086
|
+
const configPath = path28.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
16745
17087
|
try {
|
|
16746
17088
|
const raw = await fs11.readFile(configPath, "utf-8");
|
|
16747
17089
|
const parsed = JSON.parse(raw);
|
|
@@ -16835,7 +17177,7 @@ var ConfigManager = class {
|
|
|
16835
17177
|
return onResource;
|
|
16836
17178
|
}
|
|
16837
17179
|
async persistClearedPins(tiers) {
|
|
16838
|
-
const configPath =
|
|
17180
|
+
const configPath = path28.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
16839
17181
|
try {
|
|
16840
17182
|
const raw = JSON.parse(await fs11.readFile(configPath, "utf-8"));
|
|
16841
17183
|
const models = raw["models"];
|
|
@@ -17429,7 +17771,7 @@ function computeDelegationSavings(stats, t1Model) {
|
|
|
17429
17771
|
var DEFAULT_SNAPSHOT_URL = "https://raw.githubusercontent.com/Varun-SV/Cascade-AI/main/src/core/router/benchmark-data.json";
|
|
17430
17772
|
var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
|
|
17431
17773
|
var FETCH_TIMEOUT_MS = 8e3;
|
|
17432
|
-
var DEFAULT_CACHE_FILE =
|
|
17774
|
+
var DEFAULT_CACHE_FILE = path28.join(os6.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
|
|
17433
17775
|
function normalizeModelId2(id) {
|
|
17434
17776
|
let s = id.toLowerCase();
|
|
17435
17777
|
const slash = s.lastIndexOf("/");
|
|
@@ -17576,7 +17918,7 @@ var LiveDataProvider = class {
|
|
|
17576
17918
|
}
|
|
17577
17919
|
async saveCache() {
|
|
17578
17920
|
try {
|
|
17579
|
-
await fs11.mkdir(
|
|
17921
|
+
await fs11.mkdir(path28.dirname(this.opts.cacheFile), { recursive: true });
|
|
17580
17922
|
const cache = {
|
|
17581
17923
|
fetchedAt: this.fetchedAt,
|
|
17582
17924
|
snapshot: this.snapshot ?? void 0,
|
|
@@ -17683,9 +18025,15 @@ var FAMILY_BENCHMARKS = {
|
|
|
17683
18025
|
"claude-opus": { code: 95, analysis: 92, creative: 90, data: 88 },
|
|
17684
18026
|
"claude-sonnet": { code: 93, analysis: 88, creative: 87, data: 85 },
|
|
17685
18027
|
"claude-haiku": { code: 80, analysis: 75, creative: 76, data: 72 },
|
|
17686
|
-
// OpenAI GPT-5 family
|
|
17687
|
-
//
|
|
17688
|
-
//
|
|
18028
|
+
// OpenAI GPT-5 family. GPT-5.6 is split by durable capability tier instead
|
|
18029
|
+
// of being folded into generic gpt-5: Sol, Terra and Luna have materially
|
|
18030
|
+
// different benchmark/cost envelopes and should compete independently.
|
|
18031
|
+
// Scores are the committed normalized snapshot; provenance is in
|
|
18032
|
+
// scripts/benchmarks/sources/openai-gpt-5.6-2026-08.json.
|
|
18033
|
+
"gpt-5.6-sol": { code: 100, analysis: 98, creative: 88, data: 88 },
|
|
18034
|
+
"gpt-5.6-terra": { code: 97, analysis: 92, creative: 82, data: 85 },
|
|
18035
|
+
"gpt-5.6-luna": { code: 93, analysis: 85, creative: 74, data: 60 },
|
|
18036
|
+
// Older GPT-5 point releases remain distinct where public data supports it.
|
|
17689
18037
|
"gpt-5.5": { code: 97, analysis: 96, creative: 94, data: 95 },
|
|
17690
18038
|
"gpt-5.4": { code: 94, analysis: 94, creative: 92, data: 92 },
|
|
17691
18039
|
"gpt-5.4-mini": { code: 86, analysis: 85, creative: 85, data: 83 },
|
|
@@ -17721,13 +18069,19 @@ var FAMILY_BENCHMARKS = {
|
|
|
17721
18069
|
"llama-small": { code: 55, analysis: 56, creative: 60, data: 54 }
|
|
17722
18070
|
};
|
|
17723
18071
|
var FAMILY_MATCHERS = [
|
|
18072
|
+
[/fable/i, "claude-opus"],
|
|
17724
18073
|
[/opus/i, "claude-opus"],
|
|
17725
18074
|
[/sonnet/i, "claude-sonnet"],
|
|
17726
18075
|
[/haiku/i, "claude-haiku"],
|
|
17727
|
-
// GPT-5 family — ordered most-specific first.
|
|
17728
|
-
//
|
|
17729
|
-
//
|
|
18076
|
+
// GPT-5 family — ordered most-specific first. GPT-5.6 durable tiers carry
|
|
18077
|
+
// their own profiles. 5.4 Nano intentionally maps to the nano class rather
|
|
18078
|
+
// than being swallowed by the generic 5.4 rule.
|
|
18079
|
+
[/gpt-?5\.6.*sol/i, "gpt-5.6-sol"],
|
|
18080
|
+
[/gpt-?5\.6.*terra/i, "gpt-5.6-terra"],
|
|
18081
|
+
[/gpt-?5\.6.*luna/i, "gpt-5.6-luna"],
|
|
18082
|
+
[/gpt-?5\.6(?:\s|$)/i, "gpt-5.6-sol"],
|
|
17730
18083
|
[/gpt-?5\.5/i, "gpt-5.5"],
|
|
18084
|
+
[/gpt-?5\.4.*nano/i, "gpt-5-nano"],
|
|
17731
18085
|
[/gpt-?5\.4.*mini/i, "gpt-5.4-mini"],
|
|
17732
18086
|
[/gpt-?5\.4/i, "gpt-5.4"],
|
|
17733
18087
|
[/gpt-?5.*nano/i, "gpt-5-nano"],
|
|
@@ -20528,6 +20882,23 @@ Now execute your subtask using this context where relevant.`
|
|
|
20528
20882
|
this.setStatus("FAILED", finalOutput);
|
|
20529
20883
|
this.peerBus?.publish(this.id, assignment.subtaskId, finalOutput, "FAILED");
|
|
20530
20884
|
return this.buildResult("ESCALATED", finalOutput, { checksRun, passed, failed }, issues, correctionAttempts);
|
|
20885
|
+
} finally {
|
|
20886
|
+
this.releaseBrowserControl();
|
|
20887
|
+
}
|
|
20888
|
+
}
|
|
20889
|
+
/**
|
|
20890
|
+
* End this worker's hold on the shared browser.
|
|
20891
|
+
*
|
|
20892
|
+
* Duck-typed rather than imported: `browser_control` is registered only by a
|
|
20893
|
+
* host that HAS a browser (the desktop), so in the CLI and hosted runs there
|
|
20894
|
+
* is no such tool and this is a no-op. Importing the class to instanceof-check
|
|
20895
|
+
* it would couple the worker to a tool that usually is not there.
|
|
20896
|
+
*/
|
|
20897
|
+
releaseBrowserControl() {
|
|
20898
|
+
try {
|
|
20899
|
+
const tool = this.toolRegistry.getTool?.("browser_control");
|
|
20900
|
+
tool?.releaseActor?.(this.id);
|
|
20901
|
+
} catch {
|
|
20531
20902
|
}
|
|
20532
20903
|
}
|
|
20533
20904
|
sendToPeer(toId, content) {
|
|
@@ -20786,8 +21157,8 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : "") + textToolSuffix,
|
|
|
20786
21157
|
// Media generation can run for a minute; without this a cancelled run
|
|
20787
21158
|
// still pays for an image nobody will see.
|
|
20788
21159
|
...this.signal ? { signal: this.signal } : {},
|
|
20789
|
-
saveSnapshot: async (
|
|
20790
|
-
this.store?.addFileSnapshot(this.taskId,
|
|
21160
|
+
saveSnapshot: async (path30, content) => {
|
|
21161
|
+
this.store?.addFileSnapshot(this.taskId, path30, content);
|
|
20791
21162
|
},
|
|
20792
21163
|
sendPeerSync: (to, syncType, content) => {
|
|
20793
21164
|
this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
|
|
@@ -20990,7 +21361,7 @@ ${assignment.expectedOutput}`;
|
|
|
20990
21361
|
const { promisify: promisify4 } = await import('util');
|
|
20991
21362
|
const execFileAsync3 = promisify4(execFile4);
|
|
20992
21363
|
for (const artifactPath of artifactPaths) {
|
|
20993
|
-
const absolutePath =
|
|
21364
|
+
const absolutePath = path28.resolve(this.artifactRoot(), artifactPath);
|
|
20994
21365
|
try {
|
|
20995
21366
|
const stat = await fs11.stat(absolutePath);
|
|
20996
21367
|
if (!stat.isFile()) {
|
|
@@ -21025,7 +21396,7 @@ ${assignment.expectedOutput}`;
|
|
|
21025
21396
|
issues.push(`PDF artifact looks too small to be valid: ${artifactPath}`);
|
|
21026
21397
|
continue;
|
|
21027
21398
|
}
|
|
21028
|
-
const ext =
|
|
21399
|
+
const ext = path28.extname(absolutePath).toLowerCase();
|
|
21029
21400
|
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;
|
|
21030
21401
|
if (check) {
|
|
21031
21402
|
try {
|
|
@@ -21117,7 +21488,7 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : ""),
|
|
|
21117
21488
|
async checkAcceptance(assignment) {
|
|
21118
21489
|
const criteria = assignment.acceptance ?? [];
|
|
21119
21490
|
if (!criteria.length) return [];
|
|
21120
|
-
const resolve2 = (target) =>
|
|
21491
|
+
const resolve2 = (target) => path28.resolve(this.artifactRoot(), target);
|
|
21121
21492
|
const workerCanWriteFiles = canProduceFiles(this.tools.map((t) => t.name));
|
|
21122
21493
|
return evaluateAcceptance(criteria, assignment.files ?? [], {
|
|
21123
21494
|
stat: async (target) => {
|
|
@@ -24421,16 +24792,16 @@ function resolveInWorkspace(workspaceRoot, input) {
|
|
|
24421
24792
|
if (typeof input !== "string" || input.length === 0) {
|
|
24422
24793
|
throw new WorkspaceSandboxError(String(input), workspaceRoot);
|
|
24423
24794
|
}
|
|
24424
|
-
const root =
|
|
24425
|
-
const abs =
|
|
24426
|
-
const rel =
|
|
24427
|
-
if (rel === "" || rel === ".") ; else if (rel.startsWith("..") ||
|
|
24795
|
+
const root = path28.resolve(workspaceRoot);
|
|
24796
|
+
const abs = path28.isAbsolute(input) ? path28.resolve(input) : path28.resolve(root, input);
|
|
24797
|
+
const rel = path28.relative(root, abs);
|
|
24798
|
+
if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path28.isAbsolute(rel)) {
|
|
24428
24799
|
throw new WorkspaceSandboxError(input, root);
|
|
24429
24800
|
}
|
|
24430
24801
|
try {
|
|
24431
|
-
const real =
|
|
24432
|
-
const realRel =
|
|
24433
|
-
if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") ||
|
|
24802
|
+
const real = fs27.realpathSync(abs);
|
|
24803
|
+
const realRel = path28.relative(root, real);
|
|
24804
|
+
if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path28.isAbsolute(realRel))) {
|
|
24434
24805
|
throw new WorkspaceSandboxError(input, root);
|
|
24435
24806
|
}
|
|
24436
24807
|
} catch (e) {
|
|
@@ -24979,9 +25350,9 @@ async function animatePptx(bytes, scheme) {
|
|
|
24979
25350
|
const zip = await import_jszip.default.loadAsync(bytes);
|
|
24980
25351
|
const slidePaths = Object.keys(zip.files).filter((n) => /^ppt\/slides\/slide\d+\.xml$/.test(n));
|
|
24981
25352
|
if (slidePaths.length === 0) return bytes;
|
|
24982
|
-
for (const
|
|
24983
|
-
const xml = await zip.file(
|
|
24984
|
-
zip.file(
|
|
25353
|
+
for (const path30 of slidePaths) {
|
|
25354
|
+
const xml = await zip.file(path30).async("string");
|
|
25355
|
+
zip.file(path30, animateSlideXml(xml, scheme));
|
|
24985
25356
|
}
|
|
24986
25357
|
return await zip.generateAsync({ type: "uint8array", compression: "DEFLATE" });
|
|
24987
25358
|
} catch {
|
|
@@ -25367,7 +25738,7 @@ var GenerateDocumentTool = class extends BaseTool {
|
|
|
25367
25738
|
}
|
|
25368
25739
|
}
|
|
25369
25740
|
const bytes = await renderDocument(ext, content, { name: filePath, loadImageBytes });
|
|
25370
|
-
await fs11.mkdir(
|
|
25741
|
+
await fs11.mkdir(path28.dirname(absPath), { recursive: true });
|
|
25371
25742
|
await fs11.writeFile(absPath, bytes);
|
|
25372
25743
|
const charts = countCharts(content);
|
|
25373
25744
|
return [
|
|
@@ -25585,7 +25956,7 @@ var FileWriteTool = class extends BaseTool {
|
|
|
25585
25956
|
} catch {
|
|
25586
25957
|
}
|
|
25587
25958
|
}
|
|
25588
|
-
await fs11.mkdir(
|
|
25959
|
+
await fs11.mkdir(path28.dirname(absPath), { recursive: true });
|
|
25589
25960
|
await fs11.writeFile(absPath, content, "utf-8");
|
|
25590
25961
|
return `Written ${content.length} characters to ${filePath}`;
|
|
25591
25962
|
}
|
|
@@ -25943,12 +26314,10 @@ ${data.description}`;
|
|
|
25943
26314
|
}
|
|
25944
26315
|
}
|
|
25945
26316
|
};
|
|
25946
|
-
|
|
25947
|
-
// src/tools/browser.ts
|
|
25948
26317
|
var BROWSER_LAUNCH_TIMEOUT_MS = 15e3;
|
|
25949
26318
|
var BrowserTool = class extends BaseTool {
|
|
25950
26319
|
name = "browser";
|
|
25951
|
-
description = "Control a browser: navigate to URLs, click elements, fill forms, take screenshots.
|
|
26320
|
+
description = "Control a browser: navigate to URLs, click elements, fill forms, take screenshots. Screenshots are saved to a file for image_analyze to read.";
|
|
25952
26321
|
inputSchema = {
|
|
25953
26322
|
type: "object",
|
|
25954
26323
|
properties: {
|
|
@@ -26015,7 +26384,14 @@ var BrowserTool = class extends BaseTool {
|
|
|
26015
26384
|
}
|
|
26016
26385
|
case "screenshot": {
|
|
26017
26386
|
const buf = await page2.screenshot({ type: "png" });
|
|
26018
|
-
|
|
26387
|
+
const rel = path28.join(".cascade", "screenshots", `screenshot-${Date.now()}-${randomUUID().slice(0, 8)}.png`);
|
|
26388
|
+
const abs = resolveInWorkspace(this.workspaceRoot, rel);
|
|
26389
|
+
await fs11.mkdir(path28.dirname(abs), { recursive: true });
|
|
26390
|
+
await fs11.writeFile(abs, buf);
|
|
26391
|
+
return [
|
|
26392
|
+
`Screenshot saved to ${abs} (${Math.round(buf.byteLength / 1024)} KB).`,
|
|
26393
|
+
`Call image_analyze with that path to see what is on it \u2014 that needs a vision-capable model.`
|
|
26394
|
+
].join(" ");
|
|
26019
26395
|
}
|
|
26020
26396
|
case "evaluate": {
|
|
26021
26397
|
const result = await page2.evaluate(input["script"]);
|
|
@@ -26083,7 +26459,7 @@ var ImageAnalyzeTool = class extends BaseTool {
|
|
|
26083
26459
|
};
|
|
26084
26460
|
async function fileToImageAttachment(filePath) {
|
|
26085
26461
|
const data = await fs11.readFile(filePath);
|
|
26086
|
-
const ext =
|
|
26462
|
+
const ext = path28.extname(filePath).toLowerCase();
|
|
26087
26463
|
const mimeMap = {
|
|
26088
26464
|
".jpg": "image/jpeg",
|
|
26089
26465
|
".jpeg": "image/jpeg",
|
|
@@ -26117,14 +26493,14 @@ var PDFCreateTool = class extends BaseTool {
|
|
|
26117
26493
|
const filePath = input["path"];
|
|
26118
26494
|
const content = input["content"];
|
|
26119
26495
|
const title = input["title"];
|
|
26120
|
-
const dir =
|
|
26121
|
-
if (!
|
|
26122
|
-
|
|
26496
|
+
const dir = path28.dirname(filePath);
|
|
26497
|
+
if (!fs27.existsSync(dir)) {
|
|
26498
|
+
fs27.mkdirSync(dir, { recursive: true });
|
|
26123
26499
|
}
|
|
26124
26500
|
return new Promise((resolve2, reject) => {
|
|
26125
26501
|
try {
|
|
26126
26502
|
const doc = new PDFDocument({ margin: 50 });
|
|
26127
|
-
const stream =
|
|
26503
|
+
const stream = fs27.createWriteStream(filePath);
|
|
26128
26504
|
doc.pipe(stream);
|
|
26129
26505
|
if (title) {
|
|
26130
26506
|
doc.info["Title"] = title;
|
|
@@ -26202,22 +26578,22 @@ var CodeInterpreterTool = class extends BaseTool {
|
|
|
26202
26578
|
}
|
|
26203
26579
|
cmdPrefix = NODE_CMD;
|
|
26204
26580
|
}
|
|
26205
|
-
const tmpDir =
|
|
26206
|
-
if (!
|
|
26207
|
-
|
|
26581
|
+
const tmpDir = path28.join(this.workspaceRoot, ".cascade", "tmp");
|
|
26582
|
+
if (!fs27.existsSync(tmpDir)) {
|
|
26583
|
+
fs27.mkdirSync(tmpDir, { recursive: true });
|
|
26208
26584
|
}
|
|
26209
26585
|
const extension = language === "python" ? "py" : "js";
|
|
26210
26586
|
const fileName = `intp_${randomUUID().slice(0, 8)}.${extension}`;
|
|
26211
|
-
const filePath =
|
|
26212
|
-
|
|
26587
|
+
const filePath = path28.join(tmpDir, fileName);
|
|
26588
|
+
fs27.writeFileSync(filePath, code, "utf-8");
|
|
26213
26589
|
const execArgs = [filePath, ...args];
|
|
26214
26590
|
return new Promise((resolve2) => {
|
|
26215
26591
|
const startMs = Date.now();
|
|
26216
26592
|
execFile(cmdPrefix, execArgs, { cwd: this.workspaceRoot, timeout: 3e4 }, (error, stdout, stderr) => {
|
|
26217
26593
|
const duration = Date.now() - startMs;
|
|
26218
26594
|
try {
|
|
26219
|
-
if (
|
|
26220
|
-
|
|
26595
|
+
if (fs27.existsSync(filePath)) {
|
|
26596
|
+
fs27.unlinkSync(filePath);
|
|
26221
26597
|
}
|
|
26222
26598
|
} catch (cleanupErr) {
|
|
26223
26599
|
console.error(`Failed to cleanup interpreter script ${filePath}:`, cleanupErr);
|
|
@@ -26297,18 +26673,178 @@ ${formatted}`;
|
|
|
26297
26673
|
return `Unknown action: ${action}`;
|
|
26298
26674
|
}
|
|
26299
26675
|
};
|
|
26676
|
+
var SsrfBlockedError = class extends Error {
|
|
26677
|
+
constructor(message) {
|
|
26678
|
+
super(message);
|
|
26679
|
+
this.name = "SsrfBlockedError";
|
|
26680
|
+
}
|
|
26681
|
+
};
|
|
26682
|
+
var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
26683
|
+
var MAX_REDIRECTS2 = 5;
|
|
26684
|
+
function allowLocal() {
|
|
26685
|
+
return process.env["CASCADE_ALLOW_LOCAL_FETCH"] === "1";
|
|
26686
|
+
}
|
|
26687
|
+
function isPrivateAddress(ip) {
|
|
26688
|
+
const type = net.isIP(ip);
|
|
26689
|
+
if (type === 4) return isPrivateIPv4(ip);
|
|
26690
|
+
if (type === 6) return isPrivateIPv6(ip);
|
|
26691
|
+
return true;
|
|
26692
|
+
}
|
|
26693
|
+
function isPrivateIPv4(ip) {
|
|
26694
|
+
const parts = ip.split(".").map((p) => Number(p));
|
|
26695
|
+
if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255)) return true;
|
|
26696
|
+
const [a, b] = parts;
|
|
26697
|
+
if (a === 0) return true;
|
|
26698
|
+
if (a === 10) return true;
|
|
26699
|
+
if (a === 127) return true;
|
|
26700
|
+
if (a === 169 && b === 254) return true;
|
|
26701
|
+
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
26702
|
+
if (a === 192 && b === 168) return true;
|
|
26703
|
+
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
26704
|
+
if (a >= 224) return true;
|
|
26705
|
+
return false;
|
|
26706
|
+
}
|
|
26707
|
+
function ipv6Groups(ip) {
|
|
26708
|
+
let s = ip.toLowerCase().replace(/^\[|\]$/g, "");
|
|
26709
|
+
const zone = s.indexOf("%");
|
|
26710
|
+
if (zone !== -1) s = s.slice(0, zone);
|
|
26711
|
+
const dotted = /^(.*:)(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(s);
|
|
26712
|
+
if (dotted) {
|
|
26713
|
+
const o = [dotted[2], dotted[3], dotted[4], dotted[5]].map((p) => Number(p));
|
|
26714
|
+
if (o.some((n) => !Number.isInteger(n) || n < 0 || n > 255)) return null;
|
|
26715
|
+
s = `${dotted[1]}${(o[0] << 8 | o[1]).toString(16)}:${(o[2] << 8 | o[3]).toString(16)}`;
|
|
26716
|
+
}
|
|
26717
|
+
const halves = s.split("::");
|
|
26718
|
+
if (halves.length > 2) return null;
|
|
26719
|
+
const split = (part) => part ? part.split(":") : [];
|
|
26720
|
+
const head = split(halves[0] ?? "");
|
|
26721
|
+
const tail = halves.length === 2 ? split(halves[1] ?? "") : [];
|
|
26722
|
+
const zeros = halves.length === 2 ? 8 - head.length - tail.length : 0;
|
|
26723
|
+
if (zeros < 0) return null;
|
|
26724
|
+
const groups = [...head, ...Array(zeros).fill("0"), ...tail];
|
|
26725
|
+
if (groups.length !== 8) return null;
|
|
26726
|
+
const out = [];
|
|
26727
|
+
for (const g of groups) {
|
|
26728
|
+
if (!/^[0-9a-f]{1,4}$/.test(g)) return null;
|
|
26729
|
+
out.push(parseInt(g, 16));
|
|
26730
|
+
}
|
|
26731
|
+
return out;
|
|
26732
|
+
}
|
|
26733
|
+
function embeddedIPv4(g) {
|
|
26734
|
+
const quad = (hi, lo) => `${hi >> 8}.${hi & 255}.${lo >> 8}.${lo & 255}`;
|
|
26735
|
+
const leadingZero = (n) => g.slice(0, n).every((x) => x === 0);
|
|
26736
|
+
if (leadingZero(5) && g[5] === 65535) return quad(g[6], g[7]);
|
|
26737
|
+
if (leadingZero(6)) return quad(g[6], g[7]);
|
|
26738
|
+
if (g[0] === 100 && g[1] === 65435) {
|
|
26739
|
+
if (g[2] === 1) {
|
|
26740
|
+
return quad(g[3], (g[4] & 255) << 8 | g[5] >> 8);
|
|
26741
|
+
}
|
|
26742
|
+
if (g[2] === 0 && g[3] === 0 && g[4] === 0 && g[5] === 0) return quad(g[6], g[7]);
|
|
26743
|
+
return null;
|
|
26744
|
+
}
|
|
26745
|
+
if (g[0] === 8194) return quad(g[1], g[2]);
|
|
26746
|
+
return null;
|
|
26747
|
+
}
|
|
26748
|
+
function isPrivateIPv6(ip) {
|
|
26749
|
+
const g = ipv6Groups(ip);
|
|
26750
|
+
if (!g) return true;
|
|
26751
|
+
const v4 = embeddedIPv4(g);
|
|
26752
|
+
if (v4) return isPrivateIPv4(v4);
|
|
26753
|
+
if (g[0] === 100 && g[1] === 65435) return true;
|
|
26754
|
+
if (g.every((x) => x === 0)) return true;
|
|
26755
|
+
if (leading7Zero(g) && g[7] === 1) return true;
|
|
26756
|
+
if ((g[0] & 65472) === 65152) return true;
|
|
26757
|
+
if ((g[0] & 65024) === 64512) return true;
|
|
26758
|
+
if ((g[0] & 65280) === 65280) return true;
|
|
26759
|
+
return false;
|
|
26760
|
+
}
|
|
26761
|
+
function leading7Zero(g) {
|
|
26762
|
+
return g.slice(0, 7).every((x) => x === 0);
|
|
26763
|
+
}
|
|
26764
|
+
async function assertPublicUrl(rawUrl) {
|
|
26765
|
+
let url;
|
|
26766
|
+
try {
|
|
26767
|
+
url = new URL(rawUrl);
|
|
26768
|
+
} catch {
|
|
26769
|
+
throw new SsrfBlockedError(`Invalid URL: ${rawUrl}`);
|
|
26770
|
+
}
|
|
26771
|
+
if (!ALLOWED_PROTOCOLS.has(url.protocol)) {
|
|
26772
|
+
throw new SsrfBlockedError(`Blocked URL scheme "${url.protocol}" \u2014 only http and https are allowed.`);
|
|
26773
|
+
}
|
|
26774
|
+
if (allowLocal()) return url;
|
|
26775
|
+
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
26776
|
+
if (net.isIP(host)) {
|
|
26777
|
+
if (isPrivateAddress(host)) {
|
|
26778
|
+
throw new SsrfBlockedError(`Blocked request to non-public address ${host}.`);
|
|
26779
|
+
}
|
|
26780
|
+
return url;
|
|
26781
|
+
}
|
|
26782
|
+
if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) {
|
|
26783
|
+
throw new SsrfBlockedError(`Blocked request to local hostname "${host}".`);
|
|
26784
|
+
}
|
|
26785
|
+
let addresses;
|
|
26786
|
+
try {
|
|
26787
|
+
const records = await dns2.lookup(host, { all: true });
|
|
26788
|
+
addresses = records.map((r) => r.address);
|
|
26789
|
+
} catch {
|
|
26790
|
+
throw new SsrfBlockedError(`Could not resolve host "${host}".`);
|
|
26791
|
+
}
|
|
26792
|
+
if (addresses.length === 0 || addresses.some((addr) => isPrivateAddress(addr))) {
|
|
26793
|
+
throw new SsrfBlockedError(`Blocked request to "${host}" \u2014 resolves to a non-public address.`);
|
|
26794
|
+
}
|
|
26795
|
+
return url;
|
|
26796
|
+
}
|
|
26797
|
+
var ssrfAgent = new Agent({
|
|
26798
|
+
connect: {
|
|
26799
|
+
lookup(hostname, options, callback) {
|
|
26800
|
+
if (allowLocal()) {
|
|
26801
|
+
dnsCallback.lookup(hostname, options, callback);
|
|
26802
|
+
return;
|
|
26803
|
+
}
|
|
26804
|
+
dnsCallback.lookup(hostname, { ...options, all: true }, (err, addresses) => {
|
|
26805
|
+
if (err) return callback(err, "", 0);
|
|
26806
|
+
const list = Array.isArray(addresses) ? addresses : [addresses];
|
|
26807
|
+
const bad = list.find((a) => isPrivateAddress(a.address));
|
|
26808
|
+
if (bad) {
|
|
26809
|
+
return callback(
|
|
26810
|
+
new SsrfBlockedError(`Blocked connection to "${hostname}" \u2014 resolved to non-public address ${bad.address}.`),
|
|
26811
|
+
"",
|
|
26812
|
+
0
|
|
26813
|
+
);
|
|
26814
|
+
}
|
|
26815
|
+
if (options.all) return callback(null, list);
|
|
26816
|
+
const first = list[0];
|
|
26817
|
+
return callback(null, first.address, first.family);
|
|
26818
|
+
});
|
|
26819
|
+
}
|
|
26820
|
+
}
|
|
26821
|
+
});
|
|
26822
|
+
async function safeFetch(rawUrl, init = {}) {
|
|
26823
|
+
let currentUrl = (await assertPublicUrl(rawUrl)).toString();
|
|
26824
|
+
for (let i = 0; i <= MAX_REDIRECTS2; i++) {
|
|
26825
|
+
const resp = await fetch(currentUrl, { ...init, redirect: "manual", dispatcher: ssrfAgent });
|
|
26826
|
+
if (resp.status < 300 || resp.status >= 400) return resp;
|
|
26827
|
+
const location = resp.headers.get("location");
|
|
26828
|
+
if (!location) return resp;
|
|
26829
|
+
const next = new URL(location, currentUrl);
|
|
26830
|
+
await assertPublicUrl(next.toString());
|
|
26831
|
+
currentUrl = next.toString();
|
|
26832
|
+
}
|
|
26833
|
+
throw new SsrfBlockedError(`Too many redirects (>${MAX_REDIRECTS2}).`);
|
|
26834
|
+
}
|
|
26300
26835
|
|
|
26301
26836
|
// src/tools/web-search.ts
|
|
26302
|
-
async function searchSearXNG(query, baseUrl, maxResults) {
|
|
26837
|
+
async function searchSearXNG(query, baseUrl, maxResults, guardUrl = false) {
|
|
26303
26838
|
const url = new URL("/search", baseUrl);
|
|
26304
26839
|
url.searchParams.set("q", query);
|
|
26305
26840
|
url.searchParams.set("format", "json");
|
|
26306
26841
|
url.searchParams.set("categories", "general");
|
|
26307
26842
|
url.searchParams.set("engines", "google,bing,duckduckgo");
|
|
26308
|
-
const
|
|
26843
|
+
const init = {
|
|
26309
26844
|
headers: { "User-Agent": "Cascade-AI/1.0 WebSearchTool" },
|
|
26310
26845
|
signal: AbortSignal.timeout(1e4)
|
|
26311
|
-
}
|
|
26846
|
+
};
|
|
26847
|
+
const resp = guardUrl ? await safeFetch(url.toString(), init) : await fetch(url.toString(), init);
|
|
26312
26848
|
if (!resp.ok) {
|
|
26313
26849
|
throw new Error(`SearXNG returned HTTP ${resp.status}`);
|
|
26314
26850
|
}
|
|
@@ -26437,7 +26973,11 @@ var WebSearchTool = class extends BaseTool {
|
|
|
26437
26973
|
searxngUrl: config.searxngUrl ?? process.env["SEARXNG_URL"],
|
|
26438
26974
|
braveApiKey: config.braveApiKey ?? process.env["BRAVE_SEARCH_API_KEY"],
|
|
26439
26975
|
tavilyApiKey: config.tavilyApiKey ?? process.env["TAVILY_API_KEY"],
|
|
26440
|
-
maxResults: config.maxResults ?? 5
|
|
26976
|
+
maxResults: config.maxResults ?? 5,
|
|
26977
|
+
// Note this reads only from `config`, never from the environment: the
|
|
26978
|
+
// guard tracks the trustworthiness of the URL the host passed in, and an
|
|
26979
|
+
// env var says nothing about that.
|
|
26980
|
+
guardSearxngUrl: config.guardSearxngUrl ?? false
|
|
26441
26981
|
};
|
|
26442
26982
|
}
|
|
26443
26983
|
async execute(input, _options) {
|
|
@@ -26452,7 +26992,7 @@ var WebSearchTool = class extends BaseTool {
|
|
|
26452
26992
|
let anyBackendReachable = false;
|
|
26453
26993
|
if (this.config.searxngUrl) {
|
|
26454
26994
|
try {
|
|
26455
|
-
results = await searchSearXNG(query, this.config.searxngUrl, maxResults);
|
|
26995
|
+
results = await searchSearXNG(query, this.config.searxngUrl, maxResults, this.config.guardSearxngUrl);
|
|
26456
26996
|
anyBackendReachable = true;
|
|
26457
26997
|
if (results.length > 0) return this.formatResults(query, results);
|
|
26458
26998
|
errors.push("SearXNG: returned 0 results");
|
|
@@ -26529,7 +27069,7 @@ var GlobTool = class extends BaseTool {
|
|
|
26529
27069
|
};
|
|
26530
27070
|
async execute(input, _options) {
|
|
26531
27071
|
const pattern = input["pattern"];
|
|
26532
|
-
const searchPath = input["path"] ?
|
|
27072
|
+
const searchPath = input["path"] ? path28.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
|
|
26533
27073
|
const matches = await glob(pattern, {
|
|
26534
27074
|
cwd: searchPath,
|
|
26535
27075
|
ignore: ["node_modules/**", ".git/**", "dist/**", "build/**"],
|
|
@@ -26542,7 +27082,7 @@ var GlobTool = class extends BaseTool {
|
|
|
26542
27082
|
const withMtime = await Promise.all(
|
|
26543
27083
|
matches.map(async (rel) => {
|
|
26544
27084
|
try {
|
|
26545
|
-
const stat = await fs11.stat(
|
|
27085
|
+
const stat = await fs11.stat(path28.join(searchPath, rel));
|
|
26546
27086
|
return { rel, mtime: stat.mtimeMs };
|
|
26547
27087
|
} catch {
|
|
26548
27088
|
return { rel, mtime: 0 };
|
|
@@ -26591,7 +27131,7 @@ var GrepTool = class extends BaseTool {
|
|
|
26591
27131
|
};
|
|
26592
27132
|
async execute(input, _options) {
|
|
26593
27133
|
const pattern = input["pattern"];
|
|
26594
|
-
const searchPath = input["path"] ?
|
|
27134
|
+
const searchPath = input["path"] ? path28.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
|
|
26595
27135
|
const globPattern = input["glob"];
|
|
26596
27136
|
const outputMode = input["output_mode"] ?? "content";
|
|
26597
27137
|
const context = input["context"] ?? 0;
|
|
@@ -26645,12 +27185,12 @@ var GrepTool = class extends BaseTool {
|
|
|
26645
27185
|
nodir: true
|
|
26646
27186
|
});
|
|
26647
27187
|
} catch {
|
|
26648
|
-
files = [
|
|
27188
|
+
files = [path28.relative(searchPath, searchPath) || "."];
|
|
26649
27189
|
}
|
|
26650
27190
|
const results = [];
|
|
26651
27191
|
let totalCount = 0;
|
|
26652
27192
|
for (const rel of files) {
|
|
26653
|
-
const abs =
|
|
27193
|
+
const abs = path28.join(searchPath, rel);
|
|
26654
27194
|
let content;
|
|
26655
27195
|
try {
|
|
26656
27196
|
content = await fs11.readFile(abs, "utf-8");
|
|
@@ -26691,92 +27231,6 @@ Total: ${totalCount} matches`);
|
|
|
26691
27231
|
return results.join("\n");
|
|
26692
27232
|
}
|
|
26693
27233
|
};
|
|
26694
|
-
var SsrfBlockedError = class extends Error {
|
|
26695
|
-
constructor(message) {
|
|
26696
|
-
super(message);
|
|
26697
|
-
this.name = "SsrfBlockedError";
|
|
26698
|
-
}
|
|
26699
|
-
};
|
|
26700
|
-
var ALLOWED_PROTOCOLS = /* @__PURE__ */ new Set(["http:", "https:"]);
|
|
26701
|
-
var MAX_REDIRECTS2 = 5;
|
|
26702
|
-
function allowLocal() {
|
|
26703
|
-
return process.env["CASCADE_ALLOW_LOCAL_FETCH"] === "1";
|
|
26704
|
-
}
|
|
26705
|
-
function isPrivateAddress(ip) {
|
|
26706
|
-
const type = net.isIP(ip);
|
|
26707
|
-
if (type === 4) return isPrivateIPv4(ip);
|
|
26708
|
-
if (type === 6) return isPrivateIPv6(ip);
|
|
26709
|
-
return true;
|
|
26710
|
-
}
|
|
26711
|
-
function isPrivateIPv4(ip) {
|
|
26712
|
-
const parts = ip.split(".").map((p) => Number(p));
|
|
26713
|
-
if (parts.length !== 4 || parts.some((p) => Number.isNaN(p) || p < 0 || p > 255)) return true;
|
|
26714
|
-
const [a, b] = parts;
|
|
26715
|
-
if (a === 0) return true;
|
|
26716
|
-
if (a === 10) return true;
|
|
26717
|
-
if (a === 127) return true;
|
|
26718
|
-
if (a === 169 && b === 254) return true;
|
|
26719
|
-
if (a === 172 && b >= 16 && b <= 31) return true;
|
|
26720
|
-
if (a === 192 && b === 168) return true;
|
|
26721
|
-
if (a === 100 && b >= 64 && b <= 127) return true;
|
|
26722
|
-
if (a >= 224) return true;
|
|
26723
|
-
return false;
|
|
26724
|
-
}
|
|
26725
|
-
function isPrivateIPv6(ip) {
|
|
26726
|
-
const lower = ip.toLowerCase().replace(/^\[|\]$/g, "");
|
|
26727
|
-
if (lower === "::1" || lower === "::") return true;
|
|
26728
|
-
if (lower.startsWith("fe80")) return true;
|
|
26729
|
-
if (lower.startsWith("fc") || lower.startsWith("fd")) return true;
|
|
26730
|
-
const mapped = /^::ffff:(\d+\.\d+\.\d+\.\d+)$/.exec(lower);
|
|
26731
|
-
if (mapped) return isPrivateIPv4(mapped[1]);
|
|
26732
|
-
return false;
|
|
26733
|
-
}
|
|
26734
|
-
async function assertPublicUrl(rawUrl) {
|
|
26735
|
-
let url;
|
|
26736
|
-
try {
|
|
26737
|
-
url = new URL(rawUrl);
|
|
26738
|
-
} catch {
|
|
26739
|
-
throw new SsrfBlockedError(`Invalid URL: ${rawUrl}`);
|
|
26740
|
-
}
|
|
26741
|
-
if (!ALLOWED_PROTOCOLS.has(url.protocol)) {
|
|
26742
|
-
throw new SsrfBlockedError(`Blocked URL scheme "${url.protocol}" \u2014 only http and https are allowed.`);
|
|
26743
|
-
}
|
|
26744
|
-
if (allowLocal()) return url;
|
|
26745
|
-
const host = url.hostname.replace(/^\[|\]$/g, "");
|
|
26746
|
-
if (net.isIP(host)) {
|
|
26747
|
-
if (isPrivateAddress(host)) {
|
|
26748
|
-
throw new SsrfBlockedError(`Blocked request to non-public address ${host}.`);
|
|
26749
|
-
}
|
|
26750
|
-
return url;
|
|
26751
|
-
}
|
|
26752
|
-
if (host === "localhost" || host.endsWith(".localhost") || host.endsWith(".local")) {
|
|
26753
|
-
throw new SsrfBlockedError(`Blocked request to local hostname "${host}".`);
|
|
26754
|
-
}
|
|
26755
|
-
let addresses;
|
|
26756
|
-
try {
|
|
26757
|
-
const records = await dns2.lookup(host, { all: true });
|
|
26758
|
-
addresses = records.map((r) => r.address);
|
|
26759
|
-
} catch {
|
|
26760
|
-
throw new SsrfBlockedError(`Could not resolve host "${host}".`);
|
|
26761
|
-
}
|
|
26762
|
-
if (addresses.length === 0 || addresses.some((addr) => isPrivateAddress(addr))) {
|
|
26763
|
-
throw new SsrfBlockedError(`Blocked request to "${host}" \u2014 resolves to a non-public address.`);
|
|
26764
|
-
}
|
|
26765
|
-
return url;
|
|
26766
|
-
}
|
|
26767
|
-
async function safeFetch(rawUrl, init = {}) {
|
|
26768
|
-
let currentUrl = (await assertPublicUrl(rawUrl)).toString();
|
|
26769
|
-
for (let i = 0; i <= MAX_REDIRECTS2; i++) {
|
|
26770
|
-
const resp = await fetch(currentUrl, { ...init, redirect: "manual" });
|
|
26771
|
-
if (resp.status < 300 || resp.status >= 400) return resp;
|
|
26772
|
-
const location = resp.headers.get("location");
|
|
26773
|
-
if (!location) return resp;
|
|
26774
|
-
const next = new URL(location, currentUrl);
|
|
26775
|
-
await assertPublicUrl(next.toString());
|
|
26776
|
-
currentUrl = next.toString();
|
|
26777
|
-
}
|
|
26778
|
-
throw new SsrfBlockedError(`Too many redirects (>${MAX_REDIRECTS2}).`);
|
|
26779
|
-
}
|
|
26780
27234
|
|
|
26781
27235
|
// src/tools/web-fetch.ts
|
|
26782
27236
|
var MAX_CHARS = 5e4;
|
|
@@ -27049,10 +27503,10 @@ var ToolRegistry = class extends EventEmitter {
|
|
|
27049
27503
|
}
|
|
27050
27504
|
isIgnored(filePath) {
|
|
27051
27505
|
if (!filePath) return false;
|
|
27052
|
-
const abs =
|
|
27053
|
-
const rel =
|
|
27054
|
-
if (!rel || rel.startsWith("..") ||
|
|
27055
|
-
const posixRel = rel.split(
|
|
27506
|
+
const abs = path28.resolve(this.workspaceRoot, filePath);
|
|
27507
|
+
const rel = path28.relative(this.workspaceRoot, abs);
|
|
27508
|
+
if (!rel || rel.startsWith("..") || path28.isAbsolute(rel)) return true;
|
|
27509
|
+
const posixRel = rel.split(path28.sep).join("/");
|
|
27056
27510
|
return this.ignoreMatcher.ignores(posixRel);
|
|
27057
27511
|
}
|
|
27058
27512
|
};
|
|
@@ -27286,22 +27740,22 @@ var FileMcpOAuthStore = class {
|
|
|
27286
27740
|
filePath;
|
|
27287
27741
|
load() {
|
|
27288
27742
|
try {
|
|
27289
|
-
return JSON.parse(
|
|
27743
|
+
return JSON.parse(fs27.readFileSync(this.filePath, "utf-8"));
|
|
27290
27744
|
} catch {
|
|
27291
27745
|
return void 0;
|
|
27292
27746
|
}
|
|
27293
27747
|
}
|
|
27294
27748
|
save(state) {
|
|
27295
|
-
|
|
27296
|
-
|
|
27749
|
+
fs27.mkdirSync(path28.dirname(this.filePath), { recursive: true, mode: 448 });
|
|
27750
|
+
fs27.writeFileSync(this.filePath, JSON.stringify(state, null, 2), { mode: 384 });
|
|
27297
27751
|
try {
|
|
27298
|
-
|
|
27752
|
+
fs27.chmodSync(this.filePath, 384);
|
|
27299
27753
|
} catch {
|
|
27300
27754
|
}
|
|
27301
27755
|
}
|
|
27302
27756
|
clear() {
|
|
27303
27757
|
try {
|
|
27304
|
-
|
|
27758
|
+
fs27.rmSync(this.filePath);
|
|
27305
27759
|
} catch {
|
|
27306
27760
|
}
|
|
27307
27761
|
}
|
|
@@ -28139,7 +28593,7 @@ var TaskAnalyzer = class {
|
|
|
28139
28593
|
analysisCache.clear();
|
|
28140
28594
|
}
|
|
28141
28595
|
};
|
|
28142
|
-
var DEFAULT_STATS_FILE =
|
|
28596
|
+
var DEFAULT_STATS_FILE = path28.join(os6.homedir(), ".cascade", "model-perf.json");
|
|
28143
28597
|
var ModelPerformanceTracker = class _ModelPerformanceTracker {
|
|
28144
28598
|
stats = /* @__PURE__ */ new Map();
|
|
28145
28599
|
featureStats = /* @__PURE__ */ new Map();
|
|
@@ -28179,7 +28633,7 @@ var ModelPerformanceTracker = class _ModelPerformanceTracker {
|
|
|
28179
28633
|
async save() {
|
|
28180
28634
|
if (this.readOnly) return;
|
|
28181
28635
|
try {
|
|
28182
|
-
await fs11.mkdir(
|
|
28636
|
+
await fs11.mkdir(path28.dirname(this.statsFile), { recursive: true });
|
|
28183
28637
|
const modelsObj = {};
|
|
28184
28638
|
const featuresObj = {};
|
|
28185
28639
|
for (const [key, stat] of this.stats) modelsObj[key] = stat;
|
|
@@ -28812,7 +29266,7 @@ Required capability: ${description.slice(0, 300)}`;
|
|
|
28812
29266
|
* any dangerous action, so a silently-reloaded tool can't act without approval. */
|
|
28813
29267
|
async loadPersistedTools() {
|
|
28814
29268
|
if (!this.workspacePath || !this.persistEnabled) return;
|
|
28815
|
-
const file =
|
|
29269
|
+
const file = path28.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
|
|
28816
29270
|
try {
|
|
28817
29271
|
const raw = await fs11.readFile(file, "utf-8");
|
|
28818
29272
|
const specs = JSON.parse(raw);
|
|
@@ -28836,8 +29290,8 @@ Required capability: ${description.slice(0, 300)}`;
|
|
|
28836
29290
|
}
|
|
28837
29291
|
async persist() {
|
|
28838
29292
|
if (!this.workspacePath || !this.persistEnabled) return;
|
|
28839
|
-
const dir =
|
|
28840
|
-
const file =
|
|
29293
|
+
const dir = path28.join(this.workspacePath, ".cascade");
|
|
29294
|
+
const file = path28.join(dir, DYNAMIC_TOOLS_FILE);
|
|
28841
29295
|
try {
|
|
28842
29296
|
await fs11.mkdir(dir, { recursive: true });
|
|
28843
29297
|
await fs11.writeFile(file, JSON.stringify(Array.from(this.specs.values()), null, 2), "utf-8");
|
|
@@ -28857,12 +29311,12 @@ var WorldStateDB = class {
|
|
|
28857
29311
|
constructor(workspacePath, debugMode = false) {
|
|
28858
29312
|
this.workspacePath = workspacePath;
|
|
28859
29313
|
this.debugMode = debugMode;
|
|
28860
|
-
const cascadeDir =
|
|
28861
|
-
if (!
|
|
28862
|
-
|
|
29314
|
+
const cascadeDir = path28.join(workspacePath, ".cascade");
|
|
29315
|
+
if (!fs27.existsSync(cascadeDir)) {
|
|
29316
|
+
fs27.mkdirSync(cascadeDir, { recursive: true });
|
|
28863
29317
|
}
|
|
28864
|
-
this.keyPath =
|
|
28865
|
-
this.dbPath =
|
|
29318
|
+
this.keyPath = path28.join(cascadeDir, "world_state.key");
|
|
29319
|
+
this.dbPath = path28.join(cascadeDir, "world_state.db");
|
|
28866
29320
|
this.initEncryptionKey();
|
|
28867
29321
|
this.db = new Database2(this.dbPath);
|
|
28868
29322
|
this.db.pragma("journal_mode = WAL");
|
|
@@ -28919,11 +29373,11 @@ var WorldStateDB = class {
|
|
|
28919
29373
|
).run(e, r, row.encrypted_value, row.source_worker, row.timestamp, validTo, change);
|
|
28920
29374
|
}
|
|
28921
29375
|
initEncryptionKey() {
|
|
28922
|
-
if (
|
|
28923
|
-
this.encryptionKey =
|
|
29376
|
+
if (fs27.existsSync(this.keyPath)) {
|
|
29377
|
+
this.encryptionKey = fs27.readFileSync(this.keyPath);
|
|
28924
29378
|
} else {
|
|
28925
29379
|
this.encryptionKey = crypto.randomBytes(32);
|
|
28926
|
-
|
|
29380
|
+
fs27.writeFileSync(this.keyPath, this.encryptionKey);
|
|
28927
29381
|
}
|
|
28928
29382
|
}
|
|
28929
29383
|
encrypt(text) {
|
|
@@ -29209,9 +29663,9 @@ var WorldStateDB = class {
|
|
|
29209
29663
|
dumpDebugIfNeeded() {
|
|
29210
29664
|
if (!this.debugMode) return;
|
|
29211
29665
|
try {
|
|
29212
|
-
const dumpPath =
|
|
29666
|
+
const dumpPath = path28.join(os6.tmpdir(), "cascade_world_state_debug.json");
|
|
29213
29667
|
const entries = this.getAllEntries();
|
|
29214
|
-
|
|
29668
|
+
fs27.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
|
|
29215
29669
|
} catch (err) {
|
|
29216
29670
|
console.error("Failed to dump debug world state", err);
|
|
29217
29671
|
}
|
|
@@ -29268,7 +29722,7 @@ var ResumeStore = class {
|
|
|
29268
29722
|
createdAt: checkpoint.createdAt ?? new Date(timestamp).toISOString()
|
|
29269
29723
|
};
|
|
29270
29724
|
await promises.mkdir(this.dir, { recursive: true });
|
|
29271
|
-
const finalPath =
|
|
29725
|
+
const finalPath = path28.join(this.dir, `${timestamp}-${safeId(record.taskId)}.json`);
|
|
29272
29726
|
const tempPath = `${finalPath}.${process.pid}.tmp`;
|
|
29273
29727
|
await promises.writeFile(tempPath, JSON.stringify(record, null, 2), "utf-8");
|
|
29274
29728
|
await promises.rename(tempPath, finalPath);
|
|
@@ -29291,7 +29745,7 @@ var ResumeStore = class {
|
|
|
29291
29745
|
const checkpoints = [];
|
|
29292
29746
|
for (const candidate of candidates) {
|
|
29293
29747
|
try {
|
|
29294
|
-
const raw = await promises.readFile(
|
|
29748
|
+
const raw = await promises.readFile(path28.join(this.dir, candidate.name), "utf-8");
|
|
29295
29749
|
const parsed = JSON.parse(raw);
|
|
29296
29750
|
if (isCheckpoint(parsed)) checkpoints.push(parsed);
|
|
29297
29751
|
} catch {
|
|
@@ -29330,7 +29784,7 @@ var ResumeStore = class {
|
|
|
29330
29784
|
const source = entries.find((name) => FILE_PATTERN.exec(name)?.[2] === wanted);
|
|
29331
29785
|
if (!source) return null;
|
|
29332
29786
|
const claimId = `${this.now()}-${safeId(target.taskId)}`;
|
|
29333
|
-
await promises.rename(
|
|
29787
|
+
await promises.rename(path28.join(this.dir, source), path28.join(this.dir, `${claimId}${CLAIM_SUFFIX}`));
|
|
29334
29788
|
return { checkpoint: target, claimId };
|
|
29335
29789
|
} catch {
|
|
29336
29790
|
return null;
|
|
@@ -29340,8 +29794,8 @@ var ResumeStore = class {
|
|
|
29340
29794
|
async release(claimId) {
|
|
29341
29795
|
try {
|
|
29342
29796
|
await promises.rename(
|
|
29343
|
-
|
|
29344
|
-
|
|
29797
|
+
path28.join(this.dir, `${claimId}${CLAIM_SUFFIX}`),
|
|
29798
|
+
path28.join(this.dir, `${claimId}.json`)
|
|
29345
29799
|
);
|
|
29346
29800
|
} catch {
|
|
29347
29801
|
}
|
|
@@ -29349,7 +29803,7 @@ var ResumeStore = class {
|
|
|
29349
29803
|
/** Discard a claim for good — the resumed run has taken ownership. */
|
|
29350
29804
|
async settle(claimId) {
|
|
29351
29805
|
try {
|
|
29352
|
-
await promises.rm(
|
|
29806
|
+
await promises.rm(path28.join(this.dir, `${claimId}${CLAIM_SUFFIX}`), { force: true });
|
|
29353
29807
|
} catch {
|
|
29354
29808
|
}
|
|
29355
29809
|
}
|
|
@@ -29378,8 +29832,8 @@ var ResumeStore = class {
|
|
|
29378
29832
|
const stamp2 = Number(/^(\d+)-/.exec(name)?.[1]);
|
|
29379
29833
|
if (!Number.isFinite(stamp2) || stamp2 >= cutoff) continue;
|
|
29380
29834
|
await promises.rename(
|
|
29381
|
-
|
|
29382
|
-
|
|
29835
|
+
path28.join(this.dir, name),
|
|
29836
|
+
path28.join(this.dir, `${name.slice(0, -CLAIM_SUFFIX.length)}.json`)
|
|
29383
29837
|
);
|
|
29384
29838
|
reclaimed++;
|
|
29385
29839
|
}
|
|
@@ -29395,7 +29849,7 @@ var ResumeStore = class {
|
|
|
29395
29849
|
await Promise.all(entries.map(async (name) => {
|
|
29396
29850
|
const match = FILE_PATTERN.exec(name);
|
|
29397
29851
|
if (match?.[2] === wanted) {
|
|
29398
|
-
await promises.rm(
|
|
29852
|
+
await promises.rm(path28.join(this.dir, name), { force: true });
|
|
29399
29853
|
}
|
|
29400
29854
|
}));
|
|
29401
29855
|
} catch {
|
|
@@ -29410,7 +29864,7 @@ var ResumeStore = class {
|
|
|
29410
29864
|
const doomed = files.filter((f, index) => f.timestamp < cutoff || index >= this.maxCheckpoints);
|
|
29411
29865
|
const staleTemps = entries.filter((name) => name.endsWith(".tmp")).filter((name) => (Number(FILE_PATTERN.exec(name.replace(/\.\d+\.tmp$/, ".json"))?.[1]) || 0) < cutoff);
|
|
29412
29866
|
await Promise.all([...doomed.map((f) => f.name), ...staleTemps].map(
|
|
29413
|
-
(name) => promises.rm(
|
|
29867
|
+
(name) => promises.rm(path28.join(this.dir, name), { force: true })
|
|
29414
29868
|
));
|
|
29415
29869
|
} catch {
|
|
29416
29870
|
}
|
|
@@ -29787,7 +30241,7 @@ var WorkspaceIndex = class {
|
|
|
29787
30241
|
isIgnored;
|
|
29788
30242
|
chunker;
|
|
29789
30243
|
constructor(opts) {
|
|
29790
|
-
this.root =
|
|
30244
|
+
this.root = path28.resolve(opts.root);
|
|
29791
30245
|
this.db = opts.db;
|
|
29792
30246
|
this.namespace = opts.namespace ?? "code";
|
|
29793
30247
|
this.extensions = new Set((opts.extensions ?? DEFAULT_EXTENSIONS).map((e) => e.replace(/^\./, "").toLowerCase()));
|
|
@@ -29819,14 +30273,14 @@ var WorkspaceIndex = class {
|
|
|
29819
30273
|
return;
|
|
29820
30274
|
}
|
|
29821
30275
|
for (const entry of entries) {
|
|
29822
|
-
const abs =
|
|
30276
|
+
const abs = path28.join(dir, entry.name);
|
|
29823
30277
|
if (entry.isDirectory()) {
|
|
29824
30278
|
if (HARD_SKIP_DIRS.has(entry.name) || this.isIgnored(abs)) continue;
|
|
29825
30279
|
await walk(abs);
|
|
29826
30280
|
} else if (entry.isFile()) {
|
|
29827
|
-
const ext =
|
|
30281
|
+
const ext = path28.extname(entry.name).slice(1).toLowerCase();
|
|
29828
30282
|
if (!this.extensions.has(ext) || this.isIgnored(abs)) continue;
|
|
29829
|
-
out.push(
|
|
30283
|
+
out.push(path28.relative(this.root, abs));
|
|
29830
30284
|
}
|
|
29831
30285
|
}
|
|
29832
30286
|
};
|
|
@@ -29856,7 +30310,7 @@ var WorkspaceIndex = class {
|
|
|
29856
30310
|
const entries = [];
|
|
29857
30311
|
for (const rel of rels) {
|
|
29858
30312
|
try {
|
|
29859
|
-
const abs =
|
|
30313
|
+
const abs = path28.join(this.root, rel);
|
|
29860
30314
|
const stat = await fs11.stat(abs);
|
|
29861
30315
|
if (stat.size > this.maxFileBytes) continue;
|
|
29862
30316
|
const buf = await fs11.readFile(abs);
|
|
@@ -30261,6 +30715,127 @@ var CurrentPageTool = class extends BaseTool {
|
|
|
30261
30715
|
}
|
|
30262
30716
|
};
|
|
30263
30717
|
|
|
30718
|
+
// src/tools/browser-control.ts
|
|
30719
|
+
var BrowserControlTool = class extends BaseTool {
|
|
30720
|
+
name = "browser_control";
|
|
30721
|
+
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.";
|
|
30722
|
+
inputSchema = {
|
|
30723
|
+
type: "object",
|
|
30724
|
+
properties: {
|
|
30725
|
+
action: {
|
|
30726
|
+
type: "string",
|
|
30727
|
+
enum: ["navigate", "click", "fill", "press", "wait_for", "extract_text"],
|
|
30728
|
+
description: "What to do."
|
|
30729
|
+
},
|
|
30730
|
+
url: { type: "string", description: "For navigate: the http(s) address to open." },
|
|
30731
|
+
selector: {
|
|
30732
|
+
type: "string",
|
|
30733
|
+
description: "CSS selector for click, fill and wait_for. Optional for extract_text (defaults to the whole page)."
|
|
30734
|
+
},
|
|
30735
|
+
value: { type: "string", description: "For fill: the text to type." },
|
|
30736
|
+
key: { type: "string", description: 'For press: a key name such as "Enter", "Tab" or "Escape".' },
|
|
30737
|
+
timeoutMs: { type: "number", description: "For wait_for: how long to wait before giving up (default 10000, max 30000)." }
|
|
30738
|
+
},
|
|
30739
|
+
required: ["action"]
|
|
30740
|
+
};
|
|
30741
|
+
controller;
|
|
30742
|
+
release;
|
|
30743
|
+
/** One-way. See `revoke`. */
|
|
30744
|
+
revoked = false;
|
|
30745
|
+
constructor(controller, release) {
|
|
30746
|
+
super();
|
|
30747
|
+
this.controller = controller;
|
|
30748
|
+
this.release = release;
|
|
30749
|
+
}
|
|
30750
|
+
/**
|
|
30751
|
+
* A worker has finished. Ends its hold on the browser.
|
|
30752
|
+
*
|
|
30753
|
+
* Called from the tier's terminal path rather than by the model, because a
|
|
30754
|
+
* model cannot be relied on to announce that it is done — which is why the
|
|
30755
|
+
* host cannot use idle time as the boundary instead.
|
|
30756
|
+
*/
|
|
30757
|
+
releaseActor(actorId) {
|
|
30758
|
+
this.release?.(actorId);
|
|
30759
|
+
}
|
|
30760
|
+
/**
|
|
30761
|
+
* Permanently disable this tool, whatever the registry still holds.
|
|
30762
|
+
*
|
|
30763
|
+
* Registration-time gates cannot express "this run turned out to be
|
|
30764
|
+
* unattended": a host that wires the controller and only then declares the
|
|
30765
|
+
* run unattended has already registered the tool, and a check that ran at
|
|
30766
|
+
* registration has no second chance. Enforcing at EXECUTION time makes the
|
|
30767
|
+
* guarantee independent of call order, which is the whole point of stating
|
|
30768
|
+
* the invariant rather than relying on the scheduler's current sequence.
|
|
30769
|
+
*
|
|
30770
|
+
* One-way on purpose. A safety gate that can be re-opened by a later call is
|
|
30771
|
+
* a gate that a refactor re-opens by accident.
|
|
30772
|
+
*/
|
|
30773
|
+
revoke() {
|
|
30774
|
+
this.revoked = true;
|
|
30775
|
+
}
|
|
30776
|
+
// Acting on a session the user is signed into is the highest-consequence
|
|
30777
|
+
// thing in the tool set — higher than shell, arguably, because the blast
|
|
30778
|
+
// radius is someone else's server rather than this machine.
|
|
30779
|
+
isDangerous() {
|
|
30780
|
+
return true;
|
|
30781
|
+
}
|
|
30782
|
+
async execute(input, options) {
|
|
30783
|
+
if (this.revoked) {
|
|
30784
|
+
return "Error: browser control is not available in this run. It requires a person watching who can stop it, and nobody is.";
|
|
30785
|
+
}
|
|
30786
|
+
const kind = input["action"];
|
|
30787
|
+
if (!kind) return "Error: action is required.";
|
|
30788
|
+
if (options?.signal?.aborted) return "Error: the run was cancelled before this action ran.";
|
|
30789
|
+
const missing = requiredFieldFor(kind, input);
|
|
30790
|
+
if (missing) return `Error: "${kind}" needs ${missing}.`;
|
|
30791
|
+
const action = {
|
|
30792
|
+
kind,
|
|
30793
|
+
...typeof input["url"] === "string" ? { url: input["url"] } : {},
|
|
30794
|
+
...typeof input["selector"] === "string" ? { selector: input["selector"] } : {},
|
|
30795
|
+
...typeof input["value"] === "string" ? { value: input["value"] } : {},
|
|
30796
|
+
...typeof input["key"] === "string" ? { key: input["key"] } : {},
|
|
30797
|
+
...typeof input["timeoutMs"] === "number" ? { timeoutMs: Math.min(Math.max(input["timeoutMs"], 0), 3e4) } : {}
|
|
30798
|
+
};
|
|
30799
|
+
let outcome;
|
|
30800
|
+
try {
|
|
30801
|
+
outcome = await this.controller(action, {
|
|
30802
|
+
// `sessionId` is the run. The host scopes both revocation and its
|
|
30803
|
+
// single-owner lease on it, so passing it is not telemetry — drop it
|
|
30804
|
+
// and a Stop in one run silently stops every later one.
|
|
30805
|
+
sessionId: options?.sessionId ?? "",
|
|
30806
|
+
actorId: options?.tierId ?? options?.sessionId ?? "",
|
|
30807
|
+
...options?.signal ? { signal: options.signal } : {}
|
|
30808
|
+
});
|
|
30809
|
+
} catch (err) {
|
|
30810
|
+
return `Error: the browser could not perform "${kind}" \u2014 ${err instanceof Error ? err.message : String(err)}`;
|
|
30811
|
+
}
|
|
30812
|
+
const where = outcome.url ? `
|
|
30813
|
+
Page: ${outcome.title ? `${outcome.title} \u2014 ` : ""}${outcome.url}` : "";
|
|
30814
|
+
return `${outcome.ok ? "" : "Failed: "}${outcome.detail}${where}`;
|
|
30815
|
+
}
|
|
30816
|
+
};
|
|
30817
|
+
function requiredFieldFor(kind, input) {
|
|
30818
|
+
const has = (k) => typeof input[k] === "string" && input[k].length > 0;
|
|
30819
|
+
const given = (k) => typeof input[k] === "string";
|
|
30820
|
+
switch (kind) {
|
|
30821
|
+
case "navigate":
|
|
30822
|
+
return has("url") ? null : "a url";
|
|
30823
|
+
case "click":
|
|
30824
|
+
return has("selector") ? null : "a selector";
|
|
30825
|
+
case "fill":
|
|
30826
|
+
return has("selector") ? given("value") ? null : "a value" : "a selector";
|
|
30827
|
+
case "press":
|
|
30828
|
+
return has("key") ? null : "a key";
|
|
30829
|
+
case "wait_for":
|
|
30830
|
+
return has("selector") ? null : "a selector";
|
|
30831
|
+
// extract_text works with or without a selector.
|
|
30832
|
+
case "extract_text":
|
|
30833
|
+
return null;
|
|
30834
|
+
default:
|
|
30835
|
+
return null;
|
|
30836
|
+
}
|
|
30837
|
+
}
|
|
30838
|
+
|
|
30264
30839
|
// src/core/cascade.ts
|
|
30265
30840
|
var ESCALATION_DECISION_TIMEOUT_MS = 5 * 6e4;
|
|
30266
30841
|
function buildContextualPrompt(prompt, history = []) {
|
|
@@ -30346,13 +30921,13 @@ var Cascade = class _Cascade extends EventEmitter {
|
|
|
30346
30921
|
this.toolRegistry = new ToolRegistry(this.config.tools, workspacePath);
|
|
30347
30922
|
try {
|
|
30348
30923
|
this.router.setDeadModelStore(new DeadModelStore(
|
|
30349
|
-
fileDeadModelPersistence(
|
|
30924
|
+
fileDeadModelPersistence(path28.join(workspacePath, ".cascade", "dead-models.json"))
|
|
30350
30925
|
));
|
|
30351
30926
|
} catch {
|
|
30352
30927
|
}
|
|
30353
30928
|
try {
|
|
30354
30929
|
this.resumeStore = new ResumeStore({
|
|
30355
|
-
dir:
|
|
30930
|
+
dir: path28.join(workspacePath, ".cascade", "resume")
|
|
30356
30931
|
});
|
|
30357
30932
|
} catch {
|
|
30358
30933
|
}
|
|
@@ -30440,8 +31015,8 @@ var Cascade = class _Cascade extends EventEmitter {
|
|
|
30440
31015
|
this.emit("log", { level: "warn", message: "Code index enabled but no embeddings-capable provider is configured \u2014 skipping." });
|
|
30441
31016
|
return;
|
|
30442
31017
|
}
|
|
30443
|
-
const dbPath = ci.dbPath ||
|
|
30444
|
-
|
|
31018
|
+
const dbPath = ci.dbPath || path28.join(this.workspacePath, ".cascade", "code-index.db");
|
|
31019
|
+
fs27.mkdirSync(path28.dirname(dbPath), { recursive: true });
|
|
30445
31020
|
const db = new Database2(dbPath);
|
|
30446
31021
|
db.pragma("journal_mode = WAL");
|
|
30447
31022
|
const complete = chatCompleterFromProviders(this.config.providers);
|
|
@@ -30753,6 +31328,49 @@ ${input}` }],
|
|
|
30753
31328
|
if ((this.config.tools?.disabledTools ?? []).includes("read_current_page")) return;
|
|
30754
31329
|
this.toolRegistry.register(new CurrentPageTool(provider));
|
|
30755
31330
|
}
|
|
31331
|
+
/**
|
|
31332
|
+
* True when nobody is watching this run — a scheduled/cron task, which
|
|
31333
|
+
* auto-approves every tool because there is no one to ask.
|
|
31334
|
+
*
|
|
31335
|
+
* That auto-approval is fine for ordinary tools and disqualifying for this
|
|
31336
|
+
* one: `browser_control` drives the signed-in browser, and "every run asks
|
|
31337
|
+
* first" is the promise the Settings copy makes. The property used to hold
|
|
31338
|
+
* only because `runScheduledTask` happens not to call `setBrowserController`
|
|
31339
|
+
* — a guarantee resting on an ABSENT call, which is exactly what a later
|
|
31340
|
+
* refactor adds without noticing. Stated and enforced instead.
|
|
31341
|
+
*/
|
|
31342
|
+
unattended = false;
|
|
31343
|
+
/**
|
|
31344
|
+
* Declare that no human is watching this run, so capabilities that depend on
|
|
31345
|
+
* a person being able to see and stop them are refused outright.
|
|
31346
|
+
*/
|
|
31347
|
+
setUnattended(on) {
|
|
31348
|
+
this.unattended = on;
|
|
31349
|
+
if (!on) return;
|
|
31350
|
+
const tool = this.toolRegistry.getTool("browser_control");
|
|
31351
|
+
tool?.revoke?.();
|
|
31352
|
+
}
|
|
31353
|
+
/**
|
|
31354
|
+
* Wire the host's browser so a run can ACT on the open page, not just read it.
|
|
31355
|
+
*
|
|
31356
|
+
* Two gates before this registers anything, and both matter:
|
|
31357
|
+
*
|
|
31358
|
+
* - `tools.agentBrowserControl` must be on. Unlike read_current_page, which
|
|
31359
|
+
* reaches nothing the user has not already opened themselves, this drives
|
|
31360
|
+
* the session they are signed into. That is opt-in, not a default.
|
|
31361
|
+
* - `disabledTools` still removes it, the same as every other tool.
|
|
31362
|
+
* - The run must not be UNATTENDED. See `setUnattended`.
|
|
31363
|
+
*
|
|
31364
|
+
* Host-supplied like the page provider: the tool exists only where there is a
|
|
31365
|
+
* real browser to drive, so it never has to be refused in the CLI or a hosted
|
|
31366
|
+
* run — it is simply not there.
|
|
31367
|
+
*/
|
|
31368
|
+
setBrowserController(controller, release) {
|
|
31369
|
+
if (this.unattended) return;
|
|
31370
|
+
if (!this.config.tools?.agentBrowserControl) return;
|
|
31371
|
+
if ((this.config.tools?.disabledTools ?? []).includes("browser_control")) return;
|
|
31372
|
+
this.toolRegistry.register(new BrowserControlTool(controller, release));
|
|
31373
|
+
}
|
|
30756
31374
|
registerMediaTools(workspacePath) {
|
|
30757
31375
|
const denied = new Set(this.config.tools?.disabledTools ?? []);
|
|
30758
31376
|
const permitted = (name) => !denied.has(name);
|
|
@@ -31358,6 +31976,7 @@ ${prompt}` : prompt;
|
|
|
31358
31976
|
this.router.setRunSignal(options.signal);
|
|
31359
31977
|
const startMs = Date.now();
|
|
31360
31978
|
const taskId = randomUUID();
|
|
31979
|
+
this.emit("run:started", { taskId });
|
|
31361
31980
|
this.decisionLog = [];
|
|
31362
31981
|
this.currentRunCompleted = [];
|
|
31363
31982
|
this.wroteCheckpointThisRun = false;
|
|
@@ -32123,6 +32742,7 @@ function applySettingsPayload(config, data) {
|
|
|
32123
32742
|
if (["auto", "T1", "T2", "T3"].includes(a["forceTier"])) config.routing = { ...config.routing ?? {}, forceTier: a["forceTier"] };
|
|
32124
32743
|
if (typeof a["benchmarksLive"] === "boolean") config.benchmarks = { ...config.benchmarks ?? {}, live: a["benchmarksLive"] };
|
|
32125
32744
|
if (["isolate", "worker", "auto"].includes(a["dynamicToolSandbox"])) config.tools = { ...config.tools ?? {}, dynamicToolSandbox: a["dynamicToolSandbox"] };
|
|
32745
|
+
if (typeof a["agentBrowserControl"] === "boolean") config.tools = { ...config.tools ?? {}, agentBrowserControl: a["agentBrowserControl"] };
|
|
32126
32746
|
if (typeof a["factsExtraction"] === "boolean") config.knowledge = { ...config.knowledge ?? {}, factsExtraction: a["factsExtraction"] };
|
|
32127
32747
|
if (typeof a["rememberSessions"] === "boolean") config.memory = { ...config.memory ?? {}, rememberSessions: a["rememberSessions"] };
|
|
32128
32748
|
if (typeof a["enableToolCreation"] === "boolean") config.enableToolCreation = a["enableToolCreation"];
|
|
@@ -32180,6 +32800,7 @@ function settingsSnapshot(config) {
|
|
|
32180
32800
|
forceTier: config.routing?.forceTier,
|
|
32181
32801
|
benchmarksLive: config.benchmarks?.live,
|
|
32182
32802
|
dynamicToolSandbox: config.tools?.dynamicToolSandbox,
|
|
32803
|
+
agentBrowserControl: config.tools?.agentBrowserControl,
|
|
32183
32804
|
factsExtraction: config.knowledge?.factsExtraction,
|
|
32184
32805
|
rememberSessions: config.memory?.rememberSessions,
|
|
32185
32806
|
enableToolCreation: config.enableToolCreation,
|
|
@@ -32215,8 +32836,8 @@ function replaceInPlace(target, source) {
|
|
|
32215
32836
|
}
|
|
32216
32837
|
function writeConfigFile(configPath, config) {
|
|
32217
32838
|
try {
|
|
32218
|
-
|
|
32219
|
-
|
|
32839
|
+
fs27.mkdirSync(path28.dirname(configPath), { recursive: true });
|
|
32840
|
+
fs27.writeFileSync(configPath, JSON.stringify(config, null, 2), "utf-8");
|
|
32220
32841
|
return { ok: true };
|
|
32221
32842
|
} catch (err) {
|
|
32222
32843
|
return { ok: false, error: err instanceof Error ? err.message : String(err) };
|
|
@@ -32626,7 +33247,7 @@ function aggregateCostStats(sessions, opts = {}) {
|
|
|
32626
33247
|
}
|
|
32627
33248
|
|
|
32628
33249
|
// src/dashboard/server.ts
|
|
32629
|
-
var __dirname$1 =
|
|
33250
|
+
var __dirname$1 = path28.dirname(fileURLToPath(import.meta.url));
|
|
32630
33251
|
var ORPHANED_ESCALATION_GRACE_MS = 3e4;
|
|
32631
33252
|
var MASK = "***";
|
|
32632
33253
|
function masked(key, value) {
|
|
@@ -32684,6 +33305,14 @@ var DashboardServer = class {
|
|
|
32684
33305
|
* always failing.
|
|
32685
33306
|
*/
|
|
32686
33307
|
currentPageProvider;
|
|
33308
|
+
/**
|
|
33309
|
+
* Supplied by the desktop shell so a run can ACT on that page — click, type,
|
|
33310
|
+
* submit — not just read it. Same reasoning as the provider above: absent in
|
|
33311
|
+
* the CLI, so the tool is not registered rather than present and failing.
|
|
33312
|
+
* Cascade applies the `tools.agentBrowserControl` gate on top of this.
|
|
33313
|
+
*/
|
|
33314
|
+
browserController;
|
|
33315
|
+
browserActorRelease;
|
|
32687
33316
|
activeControllers = /* @__PURE__ */ new Map();
|
|
32688
33317
|
/**
|
|
32689
33318
|
* Run taskIds per chat session — file snapshots are keyed by the run's
|
|
@@ -32745,6 +33374,7 @@ var DashboardServer = class {
|
|
|
32745
33374
|
});
|
|
32746
33375
|
this.socket.onConfigUpdate(async (data) => {
|
|
32747
33376
|
const result = await commitSettings(this.config, data, (committed) => this.persistConfig(committed));
|
|
33377
|
+
if (result.ok) this.settingsChangeHook?.(this.config);
|
|
32748
33378
|
const refused = result.refused.map((r) => ({
|
|
32749
33379
|
type: r.type,
|
|
32750
33380
|
reason: r.reason,
|
|
@@ -32768,6 +33398,7 @@ var DashboardServer = class {
|
|
|
32768
33398
|
const cascade = new Cascade(cfg, this.workspacePath, this.store);
|
|
32769
33399
|
this.activeSessions.set(sessionId, cascade);
|
|
32770
33400
|
if (this.currentPageProvider) cascade.setCurrentPageProvider(this.currentPageProvider);
|
|
33401
|
+
if (this.browserController) cascade.setBrowserController(this.browserController, this.browserActorRelease);
|
|
32771
33402
|
cascade.on("stream:token", (e) => {
|
|
32772
33403
|
this.emitToSessionClients(socketId, sessionId, "stream:token", { sessionId, tierId: e.tierId, text: e.text, primary: e.primary });
|
|
32773
33404
|
});
|
|
@@ -32791,13 +33422,16 @@ var DashboardServer = class {
|
|
|
32791
33422
|
if (ev.requestId) this.clearPendingEscalation(ev.requestId);
|
|
32792
33423
|
this.emitToSessionClients(socketId, sessionId, "escalation:timeout", { sessionId, ...e });
|
|
32793
33424
|
});
|
|
33425
|
+
cascade.on("run:started", (e) => {
|
|
33426
|
+
const id = e.taskId;
|
|
33427
|
+
if (id) this.recordSessionTask(sessionId, id);
|
|
33428
|
+
});
|
|
32794
33429
|
try {
|
|
32795
33430
|
const result = await cascade.run({
|
|
32796
33431
|
prompt: runPrompt,
|
|
32797
33432
|
signal: abortController.signal,
|
|
32798
33433
|
approvalCallback: this.makeApprovalCallback(sessionId)
|
|
32799
33434
|
});
|
|
32800
|
-
this.recordSessionTask(sessionId, result.taskId);
|
|
32801
33435
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED", result);
|
|
32802
33436
|
this.captureWhy(sessionId, cascade, result);
|
|
32803
33437
|
this.emitToSessionClients(socketId, sessionId, "session:complete", { sessionId, result });
|
|
@@ -32863,6 +33497,49 @@ var DashboardServer = class {
|
|
|
32863
33497
|
setCurrentPageProvider(provider) {
|
|
32864
33498
|
this.currentPageProvider = provider;
|
|
32865
33499
|
}
|
|
33500
|
+
/** Let runs act on that view, not just read it (desktop only). */
|
|
33501
|
+
setBrowserController(controller, release) {
|
|
33502
|
+
this.browserController = controller;
|
|
33503
|
+
this.browserActorRelease = release;
|
|
33504
|
+
}
|
|
33505
|
+
/**
|
|
33506
|
+
* Told whenever settings are committed through THIS server.
|
|
33507
|
+
*
|
|
33508
|
+
* Settings reach the live config by two routes — the desktop's Electron IPC
|
|
33509
|
+
* handler and this server's `config:update` socket — and both run the same
|
|
33510
|
+
* `commitSettings`. The desktop mirrors `tools.agentBrowserControl` into its
|
|
33511
|
+
* browser module after its own route, but had no way to learn about the
|
|
33512
|
+
* other, so a socket write left the module's copy stale: an enable the module
|
|
33513
|
+
* still refused, or worse a disable the module still honoured for a run
|
|
33514
|
+
* already holding the tool. This is the same sibling-writer drift
|
|
33515
|
+
* `settings-payload.ts` exists to prevent, one layer up.
|
|
33516
|
+
*/
|
|
33517
|
+
settingsChangeHook;
|
|
33518
|
+
onSettingsChanged(hook) {
|
|
33519
|
+
this.settingsChangeHook = hook;
|
|
33520
|
+
}
|
|
33521
|
+
/**
|
|
33522
|
+
* Told when a run ends, so a host holding run-scoped state can release it.
|
|
33523
|
+
*
|
|
33524
|
+
* The desktop uses this to retire a run's browser Stop control: that has to
|
|
33525
|
+
* outlive each individual action (the gap between two actions is when the
|
|
33526
|
+
* user most wants it) but must not outlive the run.
|
|
33527
|
+
*/
|
|
33528
|
+
/**
|
|
33529
|
+
* Fired when a run ends, carrying BOTH identifiers explicitly.
|
|
33530
|
+
*
|
|
33531
|
+
* They are different things and conflating them was a live bug: a chat
|
|
33532
|
+
* `sessionId` can contain many runs (hence `sessionTaskIds`), while
|
|
33533
|
+
* `Cascade.run()` mints a fresh random `taskId` per run — and it is the
|
|
33534
|
+
* taskId that tools see as `ToolExecuteOptions.sessionId`. Passing only the
|
|
33535
|
+
* chat id meant browser cleanup keyed on the task id matched nothing, so a
|
|
33536
|
+
* finished run kept its Stop banner armed and held its browser lease until
|
|
33537
|
+
* the deadlock ceiling.
|
|
33538
|
+
*/
|
|
33539
|
+
runEndedHook;
|
|
33540
|
+
onRunEnded(hook) {
|
|
33541
|
+
this.runEndedHook = hook;
|
|
33542
|
+
}
|
|
32866
33543
|
async start() {
|
|
32867
33544
|
const isLoopback = this.host === "127.0.0.1" || this.host === "::1" || this.host === "localhost";
|
|
32868
33545
|
if (!isLoopback) {
|
|
@@ -32945,7 +33622,7 @@ var DashboardServer = class {
|
|
|
32945
33622
|
* silently and still typecheck.
|
|
32946
33623
|
*/
|
|
32947
33624
|
persistConfig(config) {
|
|
32948
|
-
const result = writeConfigFile(
|
|
33625
|
+
const result = writeConfigFile(path28.join(this.workspacePath, CASCADE_CONFIG_FILE), config);
|
|
32949
33626
|
if (!result.ok) console.warn(`[dashboard] Failed to persist config: ${result.error}`);
|
|
32950
33627
|
return result;
|
|
32951
33628
|
}
|
|
@@ -32964,7 +33641,7 @@ var DashboardServer = class {
|
|
|
32964
33641
|
*/
|
|
32965
33642
|
syncGlobalCredentials() {
|
|
32966
33643
|
try {
|
|
32967
|
-
saveGlobalCredentials(
|
|
33644
|
+
saveGlobalCredentials(path28.join(os6.homedir(), GLOBAL_CONFIG_DIR), this.config.providers ?? []);
|
|
32968
33645
|
} catch (err) {
|
|
32969
33646
|
console.warn(`[dashboard] Failed to sync global credentials: ${err instanceof Error ? err.message : String(err)}`);
|
|
32970
33647
|
}
|
|
@@ -32979,15 +33656,15 @@ var DashboardServer = class {
|
|
|
32979
33656
|
resolveDashboardSecret() {
|
|
32980
33657
|
const fromConfig = this.config.dashboard.secret ?? process.env["CASCADE_DASHBOARD_SECRET"];
|
|
32981
33658
|
if (fromConfig) return fromConfig;
|
|
32982
|
-
const secretPath =
|
|
33659
|
+
const secretPath = path28.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
|
|
32983
33660
|
try {
|
|
32984
|
-
if (
|
|
32985
|
-
const existing =
|
|
33661
|
+
if (fs27.existsSync(secretPath)) {
|
|
33662
|
+
const existing = fs27.readFileSync(secretPath, "utf-8").trim();
|
|
32986
33663
|
if (existing.length >= 16) return existing;
|
|
32987
33664
|
}
|
|
32988
33665
|
const generated = randomUUID();
|
|
32989
|
-
|
|
32990
|
-
|
|
33666
|
+
fs27.mkdirSync(path28.dirname(secretPath), { recursive: true });
|
|
33667
|
+
fs27.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
|
|
32991
33668
|
if (this.config.dashboard.auth) {
|
|
32992
33669
|
console.warn(
|
|
32993
33670
|
`Dashboard auth enabled with no secret configured; persisted a generated secret to ${secretPath}. Set CASCADE_DASHBOARD_SECRET or config.dashboard.secret to override.`
|
|
@@ -33014,7 +33691,7 @@ var DashboardServer = class {
|
|
|
33014
33691
|
// ── Setup ─────────────────────────────────────
|
|
33015
33692
|
getGlobalStore() {
|
|
33016
33693
|
if (!this.globalStore) {
|
|
33017
|
-
const globalDbPath =
|
|
33694
|
+
const globalDbPath = path28.join(os6.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
33018
33695
|
this.globalStore = new MemoryStore(globalDbPath);
|
|
33019
33696
|
}
|
|
33020
33697
|
return this.globalStore;
|
|
@@ -33053,6 +33730,8 @@ var DashboardServer = class {
|
|
|
33053
33730
|
}
|
|
33054
33731
|
/** Record run end: the assistant reply (when there is one) and the runtime row's final status. */
|
|
33055
33732
|
persistRunEnd(sessionId, title, latestPrompt, reply, status, result) {
|
|
33733
|
+
const endedTaskId = result?.taskId ?? this.sessionTaskIds.get(sessionId)?.at(-1);
|
|
33734
|
+
this.runEndedHook?.({ sessionId, ...endedTaskId ? { taskId: endedTaskId } : {} });
|
|
33056
33735
|
try {
|
|
33057
33736
|
if (reply && reply.trim()) {
|
|
33058
33737
|
this.store.addMessage({ id: randomUUID(), sessionId, role: "assistant", content: reply, timestamp: (/* @__PURE__ */ new Date()).toISOString() });
|
|
@@ -33146,6 +33825,7 @@ var DashboardServer = class {
|
|
|
33146
33825
|
const prompt = task.prompt;
|
|
33147
33826
|
const title = this.persistRunStart(sessionId, `[${task.name}] ${prompt}`);
|
|
33148
33827
|
const cascade = new Cascade(this.config, task.workspacePath ?? this.workspacePath, this.store);
|
|
33828
|
+
cascade.setUnattended(true);
|
|
33149
33829
|
this.activeSessions.set(sessionId, cascade);
|
|
33150
33830
|
cascade.on("tier:status", (e) => {
|
|
33151
33831
|
this.socket.broadcast("tier:status", { sessionId, ...e });
|
|
@@ -33153,13 +33833,16 @@ var DashboardServer = class {
|
|
|
33153
33833
|
cascade.on("peer:message", (e) => {
|
|
33154
33834
|
this.socket.emitPeerMessage(e);
|
|
33155
33835
|
});
|
|
33836
|
+
cascade.on("run:started", (e) => {
|
|
33837
|
+
const id = e.taskId;
|
|
33838
|
+
if (id) this.recordSessionTask(sessionId, id);
|
|
33839
|
+
});
|
|
33156
33840
|
try {
|
|
33157
33841
|
const result = await cascade.run({
|
|
33158
33842
|
prompt,
|
|
33159
33843
|
identityId: task.identityId,
|
|
33160
33844
|
approvalCallback: async () => ({ approved: true, always: false })
|
|
33161
33845
|
});
|
|
33162
|
-
this.recordSessionTask(sessionId, result.taskId);
|
|
33163
33846
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED", result);
|
|
33164
33847
|
this.captureWhy(sessionId, cascade, result);
|
|
33165
33848
|
this.socket.broadcast("session:complete", { sessionId, result });
|
|
@@ -33394,12 +34077,12 @@ ${prompt}`;
|
|
|
33394
34077
|
}
|
|
33395
34078
|
}
|
|
33396
34079
|
watchRuntimeChanges() {
|
|
33397
|
-
const workspaceDbPath =
|
|
33398
|
-
const globalDbPath =
|
|
34080
|
+
const workspaceDbPath = path28.join(this.workspacePath, CASCADE_DB_FILE);
|
|
34081
|
+
const globalDbPath = path28.join(os6.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
33399
34082
|
const watchPaths = [workspaceDbPath, globalDbPath].filter((p, index, arr) => arr.indexOf(p) === index);
|
|
33400
34083
|
for (const watchPath of watchPaths) {
|
|
33401
|
-
if (!
|
|
33402
|
-
|
|
34084
|
+
if (!fs27.existsSync(watchPath)) continue;
|
|
34085
|
+
fs27.watchFile(watchPath, { interval: 3e3 }, () => {
|
|
33403
34086
|
this.throttledBroadcast(watchPath === globalDbPath ? "global" : "workspace");
|
|
33404
34087
|
});
|
|
33405
34088
|
}
|
|
@@ -33521,7 +34204,7 @@ ${prompt}`;
|
|
|
33521
34204
|
const sessionId = req.params.id;
|
|
33522
34205
|
this.store.deleteSession(sessionId);
|
|
33523
34206
|
this.store.deleteRuntimeSession(sessionId);
|
|
33524
|
-
const globalDbPath =
|
|
34207
|
+
const globalDbPath = path28.join(os6.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
33525
34208
|
const globalStore = new MemoryStore(globalDbPath);
|
|
33526
34209
|
try {
|
|
33527
34210
|
globalStore.deleteRuntimeSession(sessionId);
|
|
@@ -33614,7 +34297,7 @@ ${prompt}`;
|
|
|
33614
34297
|
});
|
|
33615
34298
|
this.app.delete("/api/sessions", auth2, (req, res) => {
|
|
33616
34299
|
const body = req.body;
|
|
33617
|
-
const globalDbPath =
|
|
34300
|
+
const globalDbPath = path28.join(os6.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
33618
34301
|
if (body?.ids && Array.isArray(body.ids) && body.ids.length > 0) {
|
|
33619
34302
|
const globalStore = new MemoryStore(globalDbPath);
|
|
33620
34303
|
try {
|
|
@@ -33637,7 +34320,7 @@ ${prompt}`;
|
|
|
33637
34320
|
});
|
|
33638
34321
|
this.app.delete("/api/runtime", auth2, (_req, res) => {
|
|
33639
34322
|
this.store.deleteAllRuntimeNodes();
|
|
33640
|
-
const globalDbPath =
|
|
34323
|
+
const globalDbPath = path28.join(os6.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
33641
34324
|
const globalStore = new MemoryStore(globalDbPath);
|
|
33642
34325
|
try {
|
|
33643
34326
|
globalStore.deleteAllRuntimeNodes();
|
|
@@ -33722,12 +34405,12 @@ ${prompt}`;
|
|
|
33722
34405
|
if (body["tierLimits"]) this.config.tierLimits = { ...this.config.tierLimits, ...body["tierLimits"] };
|
|
33723
34406
|
if (body["budget"]) this.config.budget = { ...this.config.budget, ...body["budget"] };
|
|
33724
34407
|
try {
|
|
33725
|
-
const configPath =
|
|
33726
|
-
const existing =
|
|
34408
|
+
const configPath = path28.join(this.workspacePath, CASCADE_CONFIG_FILE);
|
|
34409
|
+
const existing = fs27.existsSync(configPath) ? JSON.parse(fs27.readFileSync(configPath, "utf-8")) : {};
|
|
33727
34410
|
const updated = { ...existing, tierLimits: this.config.tierLimits, budget: this.config.budget };
|
|
33728
34411
|
const tmp = configPath + ".tmp";
|
|
33729
|
-
|
|
33730
|
-
|
|
34412
|
+
fs27.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
|
|
34413
|
+
fs27.renameSync(tmp, configPath);
|
|
33731
34414
|
res.json({ ok: true });
|
|
33732
34415
|
} catch (err) {
|
|
33733
34416
|
res.status(500).json({ error: `Failed to save config: ${err instanceof Error ? err.message : String(err)}` });
|
|
@@ -33755,7 +34438,7 @@ ${prompt}`;
|
|
|
33755
34438
|
this.app.get("/api/runtime", auth2, (req, res) => {
|
|
33756
34439
|
const scope = req.query["scope"] ?? "workspace";
|
|
33757
34440
|
if (scope === "global") {
|
|
33758
|
-
const globalDbPath =
|
|
34441
|
+
const globalDbPath = path28.join(os6.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
|
|
33759
34442
|
const globalStore = new MemoryStore(globalDbPath);
|
|
33760
34443
|
try {
|
|
33761
34444
|
res.json({
|
|
@@ -33792,6 +34475,7 @@ ${prompt}`;
|
|
|
33792
34475
|
const cascade = new Cascade(this.config, this.workspacePath, this.store);
|
|
33793
34476
|
this.activeSessions.set(sessionId, cascade);
|
|
33794
34477
|
if (this.currentPageProvider) cascade.setCurrentPageProvider(this.currentPageProvider);
|
|
34478
|
+
if (this.browserController) cascade.setBrowserController(this.browserController, this.browserActorRelease);
|
|
33795
34479
|
cascade.on("stream:token", (e) => {
|
|
33796
34480
|
this.socket.broadcastToRoom(`session:${sessionId}`, "stream:token", { sessionId, tierId: e.tierId, text: e.text, primary: e.primary });
|
|
33797
34481
|
});
|
|
@@ -33815,13 +34499,16 @@ ${prompt}`;
|
|
|
33815
34499
|
if (ev.requestId) this.clearPendingEscalation(ev.requestId);
|
|
33816
34500
|
this.socket.broadcastToRoom(`session:${sessionId}`, "escalation:timeout", { sessionId, ...e });
|
|
33817
34501
|
});
|
|
34502
|
+
cascade.on("run:started", (e) => {
|
|
34503
|
+
const id = e.taskId;
|
|
34504
|
+
if (id) this.recordSessionTask(sessionId, id);
|
|
34505
|
+
});
|
|
33818
34506
|
try {
|
|
33819
34507
|
const result = await cascade.run({
|
|
33820
34508
|
prompt: runPrompt,
|
|
33821
34509
|
identityId: body.identityId,
|
|
33822
34510
|
approvalCallback: this.makeApprovalCallback(sessionId)
|
|
33823
34511
|
});
|
|
33824
|
-
this.recordSessionTask(sessionId, result.taskId);
|
|
33825
34512
|
this.persistRunEnd(sessionId, title, prompt, result.output, "COMPLETED", result);
|
|
33826
34513
|
this.captureWhy(sessionId, cascade, result);
|
|
33827
34514
|
this.socket.broadcast("cost:update", {
|
|
@@ -34096,13 +34783,13 @@ ${prompt}`;
|
|
|
34096
34783
|
res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
|
|
34097
34784
|
}
|
|
34098
34785
|
});
|
|
34099
|
-
const prodPath =
|
|
34100
|
-
const devPath =
|
|
34101
|
-
const webDistPath =
|
|
34102
|
-
if (
|
|
34786
|
+
const prodPath = path28.resolve(__dirname$1, "../web/dist");
|
|
34787
|
+
const devPath = path28.resolve(__dirname$1, "../../web/dist");
|
|
34788
|
+
const webDistPath = fs27.existsSync(prodPath) ? prodPath : devPath;
|
|
34789
|
+
if (fs27.existsSync(webDistPath)) {
|
|
34103
34790
|
this.app.use(express.static(webDistPath));
|
|
34104
34791
|
this.app.get("*", (_req, res) => {
|
|
34105
|
-
res.sendFile(
|
|
34792
|
+
res.sendFile(path28.join(webDistPath, "index.html"));
|
|
34106
34793
|
});
|
|
34107
34794
|
} else {
|
|
34108
34795
|
this.app.get("/", (_req, res) => {
|
|
@@ -34289,14 +34976,14 @@ function planRetrieval(ctx) {
|
|
|
34289
34976
|
}
|
|
34290
34977
|
var CLOUD_SESSION_FILE = "cloud-session.json";
|
|
34291
34978
|
function globalDir(homeDir = os6.homedir()) {
|
|
34292
|
-
return
|
|
34979
|
+
return path28.join(homeDir, GLOBAL_CONFIG_DIR);
|
|
34293
34980
|
}
|
|
34294
34981
|
function cloudSessionPath(dir = globalDir()) {
|
|
34295
|
-
return
|
|
34982
|
+
return path28.join(dir, CLOUD_SESSION_FILE);
|
|
34296
34983
|
}
|
|
34297
34984
|
function loadCloudSession(dir = globalDir()) {
|
|
34298
34985
|
try {
|
|
34299
|
-
const raw =
|
|
34986
|
+
const raw = fs27.readFileSync(cloudSessionPath(dir), "utf-8");
|
|
34300
34987
|
const parsed = JSON.parse(raw);
|
|
34301
34988
|
if (!parsed?.refreshToken || !parsed?.serverUrl) return null;
|
|
34302
34989
|
return parsed;
|
|
@@ -34305,17 +34992,17 @@ function loadCloudSession(dir = globalDir()) {
|
|
|
34305
34992
|
}
|
|
34306
34993
|
}
|
|
34307
34994
|
function saveCloudSession(session, dir = globalDir()) {
|
|
34308
|
-
|
|
34995
|
+
fs27.mkdirSync(dir, { recursive: true, mode: 448 });
|
|
34309
34996
|
const file = cloudSessionPath(dir);
|
|
34310
|
-
|
|
34997
|
+
fs27.writeFileSync(file, JSON.stringify(session, null, 2), { mode: 384 });
|
|
34311
34998
|
try {
|
|
34312
|
-
|
|
34999
|
+
fs27.chmodSync(file, 384);
|
|
34313
35000
|
} catch {
|
|
34314
35001
|
}
|
|
34315
35002
|
}
|
|
34316
35003
|
function clearCloudSession(dir = globalDir()) {
|
|
34317
35004
|
try {
|
|
34318
|
-
|
|
35005
|
+
fs27.rmSync(cloudSessionPath(dir));
|
|
34319
35006
|
} catch {
|
|
34320
35007
|
}
|
|
34321
35008
|
}
|
|
@@ -34487,16 +35174,16 @@ var CloudClient = class _CloudClient {
|
|
|
34487
35174
|
return (await this.refresh()).accessToken;
|
|
34488
35175
|
}
|
|
34489
35176
|
/** Authenticated request that refreshes once on a 401 (e.g. server-side revoke). */
|
|
34490
|
-
async authed(
|
|
34491
|
-
const call = (token) => fetch(this.url(
|
|
35177
|
+
async authed(path30, init = {}) {
|
|
35178
|
+
const call = (token) => fetch(this.url(path30), { ...init, headers: { ...init.headers ?? {}, Authorization: `Bearer ${token}` } });
|
|
34492
35179
|
let res = await call(await this.accessToken());
|
|
34493
35180
|
if (res.status === 401) res = await call((await this.refresh()).accessToken);
|
|
34494
35181
|
if (!res.ok) throw new Error(`Request failed (${res.status}).`);
|
|
34495
35182
|
const text = await res.text();
|
|
34496
35183
|
return text ? JSON.parse(text) : {};
|
|
34497
35184
|
}
|
|
34498
|
-
authedGet(
|
|
34499
|
-
return this.authed(
|
|
35185
|
+
authedGet(path30) {
|
|
35186
|
+
return this.authed(path30);
|
|
34500
35187
|
}
|
|
34501
35188
|
async fetchMe(token) {
|
|
34502
35189
|
const res = await fetch(this.url("/api/me"), { headers: { Authorization: `Bearer ${token}` } });
|
|
@@ -34861,6 +35548,6 @@ function applySyncBundle(bundle, config, cleanup) {
|
|
|
34861
35548
|
return next;
|
|
34862
35549
|
}
|
|
34863
35550
|
|
|
34864
|
-
export { AZURE_BASE_MODELS, AZURE_BASE_URL_TEMPLATE, AuditLogger, CASCADE_AUDIT_FILE, CASCADE_CONFIG_DIR, CASCADE_CONFIG_FILE, CASCADE_DASHBOARD_SECRET_FILE, CASCADE_DB_FILE, CASCADE_IGNORE_FILE, CASCADE_KEYSTORE_FILE, CASCADE_MD_FILE, CASCADE_VERSION, CHARS_PER_TOKEN2 as CHARS_PER_TOKEN, COMPLEXITY_T2_COUNT, Cascade, CascadeCancelledError, CascadeIgnore, CascadeRouter, CascadeToolError, CloudClient, CodeSearchTool, ConfigManager, CurrentPageTool, DEFAULT_API_PORT, DEFAULT_APPROVAL_REQUIRED, DEFAULT_AUTO_SUMMARIZE_AT, DEFAULT_CLOUD_URL, DEFAULT_CONTEXT_LIMIT, DEFAULT_DASHBOARD_PORT, DEFAULT_MAX_SESSION_MESSAGES, DEFAULT_RETENTION_DAYS, DEFAULT_THEME, DOCUMENT_MIME, DashboardServer, DependencyScheduler, FileMcpOAuthStore, GLOBAL_CONFIG_DIR, GLOBAL_CREDENTIALS_FILE, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, GenerateDocumentTool, GraphRetriever, GraphSearchTool, HooksRunner, Keystore, LLMReranker, LM_STUDIO_BASE_URL, MAX_PAGE_TEXT_CHARS, MCP_TOOL_PREFIX, MODELS, McpClient, McpOAuthProvider, MemoryStore, OLLAMA_BASE_URL, OpenAICompatibleEmbedder, PROVIDER_DISPLAY_NAMES, RESUME_SCHEMA_VERSION, ResumeStore, Retriever, SqliteVectorStore, T1Administrator, T1_MODEL_PRIORITY, T2Manager, T2_MODEL_PRIORITY, T3Worker, T3_MODEL_PRIORITY, THEME_NAMES, TOOL_NAMES, TaskGraphValidationError, TaskScheduler, Telemetry, ToolRegistry, VISION_MODEL_PRIORITY, WorkspaceIndex, applyEndpointEdit, applyProviderApiKey, applyProviderCredential, applySettingsCredentials, applySettingsPayload, applySyncBundle, azureModelForDeployment, beginMcpOAuth, buildDocumentTools, buildManifest, buildSessionTranscript, bytesToBase64, cagCharBudget, chartKind, chartToTableRows, chatCompleterFromProviders, chunkCode, chunkText2 as chunkText, commitSettings, compileSectionGraph, compileSubtaskGraph, compileTaskGraph, completeMcpOAuth, connectMcpWithLoopbackOAuth, createCascade, credentialDispositionForEdit, credentialEndpointIdentity, decryptJSON as decryptSyncBlob, diffManifest, disambiguateMcpServerNames, discoverMcpAuthServer, discoverMcpTools, distillSessionFacts, embedderFromProviders, encryptJSON as encryptSyncBlob, endpointFromSettingsPayload, evaluateAcceptance, explainRefusal, extractCharts, failures, fileExt, fileOAuthProvider, gatherSyncBundle, hasDefaultEndpoint, hasProviderCredential, hasUsableProvider, hashContent, heuristicCodeChunker, inferAzureBaseModel, inlineRuns, isDocumentFormat, isMcpToolName, matchImageLine, mcpServerPrefix, mcpToolName, nodeHttpFetch, normalizeEndpoint, parseBlocks, parseChartSpec, parseDelimited, parseRankOrder, parseSlide, planRetrieval, preferIpv4Host, priorAzureRow, reciprocalRankFusion, refreshMcpToken, removeMcpServerDenials, renderDocument, renderDocx, renderPptx, renderXlsx, runCascade, sameAzureEndpoint, sameCredentialEndpoint, sameEndpoint, sessionWorthRemembering, settingsSnapshot, sniffImage, splitSlides, streamCascade, stripInline, summarizeCompleted, undecided, uniqueMcpServerName };
|
|
35551
|
+
export { AZURE_BASE_MODELS, AZURE_BASE_URL_TEMPLATE, AuditLogger, BrowserControlTool, CASCADE_AUDIT_FILE, CASCADE_CONFIG_DIR, CASCADE_CONFIG_FILE, CASCADE_DASHBOARD_SECRET_FILE, CASCADE_DB_FILE, CASCADE_IGNORE_FILE, CASCADE_KEYSTORE_FILE, CASCADE_MD_FILE, CASCADE_VERSION, CHARS_PER_TOKEN2 as CHARS_PER_TOKEN, COMPLEXITY_T2_COUNT, Cascade, CascadeCancelledError, CascadeConfigSchema, CascadeIgnore, CascadeRouter, CascadeToolError, CloudClient, CodeSearchTool, ConfigManager, CurrentPageTool, DEFAULT_API_PORT, DEFAULT_APPROVAL_REQUIRED, DEFAULT_AUTO_SUMMARIZE_AT, DEFAULT_CLOUD_URL, DEFAULT_CONTEXT_LIMIT, DEFAULT_DASHBOARD_PORT, DEFAULT_MAX_SESSION_MESSAGES, DEFAULT_RETENTION_DAYS, DEFAULT_THEME, DOCUMENT_MIME, DashboardServer, DependencyScheduler, FileMcpOAuthStore, GLOBAL_CONFIG_DIR, GLOBAL_CREDENTIALS_FILE, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, GenerateDocumentTool, GraphRetriever, GraphSearchTool, HooksRunner, Keystore, LLMReranker, LM_STUDIO_BASE_URL, MAX_PAGE_TEXT_CHARS, MCP_TOOL_PREFIX, MODELS, McpClient, McpOAuthProvider, MemoryStore, OLLAMA_BASE_URL, OpenAICompatibleEmbedder, PROVIDER_DISPLAY_NAMES, RESUME_SCHEMA_VERSION, ResumeStore, Retriever, SqliteVectorStore, T1Administrator, T1_MODEL_PRIORITY, T2Manager, T2_MODEL_PRIORITY, T3Worker, T3_MODEL_PRIORITY, THEME_NAMES, TOOL_NAMES, TaskGraphValidationError, TaskScheduler, Telemetry, ToolRegistry, VISION_MODEL_PRIORITY, WorkspaceIndex, applyEndpointEdit, applyProviderApiKey, applyProviderCredential, applySettingsCredentials, applySettingsPayload, applySyncBundle, azureModelForDeployment, beginMcpOAuth, buildDocumentTools, buildManifest, buildSessionTranscript, bytesToBase64, cagCharBudget, chartKind, chartToTableRows, chatCompleterFromProviders, chunkCode, chunkText2 as chunkText, commitSettings, compileSectionGraph, compileSubtaskGraph, compileTaskGraph, completeMcpOAuth, connectMcpWithLoopbackOAuth, createCascade, credentialDispositionForEdit, credentialEndpointIdentity, decryptJSON as decryptSyncBlob, diffManifest, disambiguateMcpServerNames, discoverMcpAuthServer, discoverMcpTools, distillSessionFacts, embedderFromProviders, encryptJSON as encryptSyncBlob, endpointFromSettingsPayload, evaluateAcceptance, explainRefusal, extractCharts, failures, fileExt, fileOAuthProvider, gatherSyncBundle, hasDefaultEndpoint, hasProviderCredential, hasUsableProvider, hashContent, heuristicCodeChunker, inferAzureBaseModel, inlineRuns, isDocumentFormat, isMcpToolName, matchImageLine, mcpServerPrefix, mcpToolName, nodeHttpFetch, normalizeEndpoint, parseBlocks, parseChartSpec, parseDelimited, parseRankOrder, parseSlide, planRetrieval, preferIpv4Host, priorAzureRow, reciprocalRankFusion, refreshMcpToken, removeMcpServerDenials, renderDocument, renderDocx, renderPptx, renderXlsx, runCascade, sameAzureEndpoint, sameCredentialEndpoint, sameEndpoint, sessionWorthRemembering, settingsSnapshot, sniffImage, splitSlides, streamCascade, stripInline, summarizeCompleted, undecided, uniqueMcpServerName };
|
|
34865
35552
|
//# sourceMappingURL=index.js.map
|
|
34866
35553
|
//# sourceMappingURL=index.js.map
|