cascade-ai 0.17.0 → 0.18.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.cjs CHANGED
@@ -9,8 +9,8 @@ var https = require('https');
9
9
  var zlib = require('zlib');
10
10
  var stream = require('stream');
11
11
  var Database2 = require('better-sqlite3');
12
- var path25 = require('path');
13
- var fs23 = require('fs');
12
+ var path26 = require('path');
13
+ var fs24 = require('fs');
14
14
  var os8 = require('os');
15
15
  var crypto = require('crypto');
16
16
  var ink = require('ink');
@@ -19,7 +19,7 @@ var React2 = require('react');
19
19
  var chalk9 = require('chalk');
20
20
  var dotenv = require('dotenv');
21
21
  var url = require('url');
22
- var fs9 = require('fs/promises');
22
+ var fs10 = require('fs/promises');
23
23
  var _ignoreModule = require('ignore');
24
24
  var zod = require('zod');
25
25
  var child_process = require('child_process');
@@ -73,14 +73,14 @@ var http__default = /*#__PURE__*/_interopDefault(http);
73
73
  var https__default = /*#__PURE__*/_interopDefault(https);
74
74
  var zlib__default = /*#__PURE__*/_interopDefault(zlib);
75
75
  var Database2__default = /*#__PURE__*/_interopDefault(Database2);
76
- var path25__default = /*#__PURE__*/_interopDefault(path25);
77
- var fs23__default = /*#__PURE__*/_interopDefault(fs23);
76
+ var path26__default = /*#__PURE__*/_interopDefault(path26);
77
+ var fs24__default = /*#__PURE__*/_interopDefault(fs24);
78
78
  var os8__default = /*#__PURE__*/_interopDefault(os8);
79
79
  var crypto__default = /*#__PURE__*/_interopDefault(crypto);
80
80
  var React2__default = /*#__PURE__*/_interopDefault(React2);
81
81
  var chalk9__default = /*#__PURE__*/_interopDefault(chalk9);
82
82
  var dotenv__default = /*#__PURE__*/_interopDefault(dotenv);
83
- var fs9__default = /*#__PURE__*/_interopDefault(fs9);
83
+ var fs10__default = /*#__PURE__*/_interopDefault(fs10);
84
84
  var _ignoreModule__namespace = /*#__PURE__*/_interopNamespace(_ignoreModule);
85
85
  var EventEmitter__default = /*#__PURE__*/_interopDefault(EventEmitter);
86
86
  var PDFDocument__default = /*#__PURE__*/_interopDefault(PDFDocument);
@@ -108,16 +108,17 @@ var __export = (target, all) => {
108
108
  };
109
109
 
110
110
  // src/constants.ts
111
- var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SECRET_FILE, GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_RUNTIME_DB_FILE, DEFAULT_DASHBOARD_PORT, DEFAULT_CONTEXT_LIMIT, DEFAULT_AUTO_SUMMARIZE_AT, MODELS, T1_MODEL_PRIORITY, T2_MODEL_PRIORITY, T3_MODEL_PRIORITY, VISION_MODEL_PRIORITY, COMPLEXITY_T2_COUNT, THEME_NAMES, DEFAULT_THEME, OLLAMA_BASE_URL, LM_STUDIO_BASE_URL, AZURE_BASE_URL_TEMPLATE, TOOL_NAMES, DEFAULT_APPROVAL_REQUIRED;
111
+ var CASCADE_VERSION, CASCADE_CONFIG_FILE, CASCADE_DB_FILE, CASCADE_DASHBOARD_SECRET_FILE, GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE, GLOBAL_KEYSTORE_FILE, GLOBAL_CREDENTIALS_FILE, GLOBAL_RUNTIME_DB_FILE, DEFAULT_DASHBOARD_PORT, DEFAULT_CONTEXT_LIMIT, DEFAULT_AUTO_SUMMARIZE_AT, MODELS, T1_MODEL_PRIORITY, T2_MODEL_PRIORITY, T3_MODEL_PRIORITY, VISION_MODEL_PRIORITY, COMPLEXITY_T2_COUNT, THEME_NAMES, DEFAULT_THEME, OLLAMA_BASE_URL, LM_STUDIO_BASE_URL, AZURE_BASE_URL_TEMPLATE, TOOL_NAMES, DEFAULT_APPROVAL_REQUIRED;
112
112
  var init_constants = __esm({
113
113
  "src/constants.ts"() {
114
- CASCADE_VERSION = "0.17.0";
114
+ CASCADE_VERSION = "0.18.0";
115
115
  CASCADE_CONFIG_FILE = ".cascade/config.json";
116
116
  CASCADE_DB_FILE = ".cascade/memory.db";
117
117
  CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
118
118
  GLOBAL_CONFIG_DIR = ".cascade-ai";
119
119
  GLOBAL_DB_FILE = "memory.db";
120
120
  GLOBAL_KEYSTORE_FILE = "keystore.enc";
121
+ GLOBAL_CREDENTIALS_FILE = "credentials.json";
121
122
  GLOBAL_RUNTIME_DB_FILE = "runtime.db";
122
123
  DEFAULT_DASHBOARD_PORT = 4891;
123
124
  DEFAULT_CONTEXT_LIMIT = 2e5;
@@ -1632,12 +1633,12 @@ var init_audit_logger = __esm({
1632
1633
  constructor(workspacePath, debugMode = false) {
1633
1634
  this.workspacePath = workspacePath;
1634
1635
  this.debugMode = debugMode;
1635
- const cascadeDir = path25__default.default.join(workspacePath, ".cascade");
1636
- if (!fs23__default.default.existsSync(cascadeDir)) {
1637
- fs23__default.default.mkdirSync(cascadeDir, { recursive: true });
1636
+ const cascadeDir = path26__default.default.join(workspacePath, ".cascade");
1637
+ if (!fs24__default.default.existsSync(cascadeDir)) {
1638
+ fs24__default.default.mkdirSync(cascadeDir, { recursive: true });
1638
1639
  }
1639
- this.keyPath = path25__default.default.join(cascadeDir, "audit_log.key");
1640
- this.dbPath = path25__default.default.join(cascadeDir, "audit_log.db");
1640
+ this.keyPath = path26__default.default.join(cascadeDir, "audit_log.key");
1641
+ this.dbPath = path26__default.default.join(cascadeDir, "audit_log.db");
1641
1642
  this.initEncryptionKey();
1642
1643
  this.db = new Database2__default.default(this.dbPath);
1643
1644
  this.db.pragma("journal_mode = WAL");
@@ -1667,11 +1668,11 @@ var init_audit_logger = __esm({
1667
1668
  return crypto__default.default.createHash("sha256").update(`${prevHash}|${timestamp}|${eventType}|${tierId}|${encryptedPayload}`).digest("hex");
1668
1669
  }
1669
1670
  initEncryptionKey() {
1670
- if (fs23__default.default.existsSync(this.keyPath)) {
1671
- this.encryptionKey = fs23__default.default.readFileSync(this.keyPath);
1671
+ if (fs24__default.default.existsSync(this.keyPath)) {
1672
+ this.encryptionKey = fs24__default.default.readFileSync(this.keyPath);
1672
1673
  } else {
1673
1674
  this.encryptionKey = crypto__default.default.randomBytes(32);
1674
- fs23__default.default.writeFileSync(this.keyPath, this.encryptionKey);
1675
+ fs24__default.default.writeFileSync(this.keyPath, this.encryptionKey);
1675
1676
  }
1676
1677
  }
1677
1678
  encrypt(text) {
@@ -1749,9 +1750,9 @@ var init_audit_logger = __esm({
1749
1750
  dumpDebugIfNeeded() {
1750
1751
  if (!this.debugMode) return;
1751
1752
  try {
1752
- const dumpPath = path25__default.default.join(os8__default.default.tmpdir(), "cascade_audit_logs_debug.json");
1753
+ const dumpPath = path26__default.default.join(os8__default.default.tmpdir(), "cascade_audit_logs_debug.json");
1753
1754
  const entries = this.getAllLogs();
1754
- fs23__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
1755
+ fs24__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
1755
1756
  } catch (err) {
1756
1757
  console.error("Failed to dump debug audit logs", err);
1757
1758
  }
@@ -1806,7 +1807,7 @@ var Keystore = class {
1806
1807
  const creds = await this.keytar.findCredentials(KEYTAR_SERVICE);
1807
1808
  this.cache = Object.fromEntries(creds.map((c) => [c.account, c.password]));
1808
1809
  this.backend = "keytar";
1809
- if (password && fs23__default.default.existsSync(this.storePath)) {
1810
+ if (password && fs24__default.default.existsSync(this.storePath)) {
1810
1811
  try {
1811
1812
  const fileEntries = this.decryptFile(password);
1812
1813
  for (const [k, v] of Object.entries(fileEntries)) {
@@ -1825,7 +1826,7 @@ var Keystore = class {
1825
1826
  "Keystore unlock requires a password because the OS keychain (keytar) is not available on this system."
1826
1827
  );
1827
1828
  }
1828
- if (!fs23__default.default.existsSync(this.storePath)) {
1829
+ if (!fs24__default.default.existsSync(this.storePath)) {
1829
1830
  const salt = crypto__default.default.randomBytes(SALT_LEN);
1830
1831
  this.masterKey = this.deriveKey(password, salt);
1831
1832
  this.writeWithSalt({}, salt);
@@ -1839,7 +1840,7 @@ var Keystore = class {
1839
1840
  }
1840
1841
  /** Synchronous legacy unlock kept for AES-only environments. */
1841
1842
  unlockSync(password) {
1842
- if (!fs23__default.default.existsSync(this.storePath)) {
1843
+ if (!fs24__default.default.existsSync(this.storePath)) {
1843
1844
  const salt = crypto__default.default.randomBytes(SALT_LEN);
1844
1845
  this.masterKey = this.deriveKey(password, salt);
1845
1846
  this.writeWithSalt({}, salt);
@@ -1897,7 +1898,7 @@ var Keystore = class {
1897
1898
  }
1898
1899
  }
1899
1900
  decryptFile(password, knownSalt) {
1900
- if (!fs23__default.default.existsSync(this.storePath)) return {};
1901
+ if (!fs24__default.default.existsSync(this.storePath)) return {};
1901
1902
  try {
1902
1903
  const { salt, ciphertext, iv, tag } = this.readRaw();
1903
1904
  const useSalt = knownSalt ?? salt;
@@ -1919,8 +1920,8 @@ var Keystore = class {
1919
1920
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
1920
1921
  const tag = cipher.getAuthTag();
1921
1922
  const out = Buffer.concat([raw.salt, iv, tag, ciphertext]);
1922
- fs23__default.default.mkdirSync(path25__default.default.dirname(this.storePath), { recursive: true });
1923
- fs23__default.default.writeFileSync(this.storePath, out, { mode: 384 });
1923
+ fs24__default.default.mkdirSync(path26__default.default.dirname(this.storePath), { recursive: true });
1924
+ fs24__default.default.writeFileSync(this.storePath, out, { mode: 384 });
1924
1925
  }
1925
1926
  writeWithSalt(data, salt) {
1926
1927
  if (!this.masterKey) throw new Error("writeWithSalt called before masterKey was set");
@@ -1930,11 +1931,11 @@ var Keystore = class {
1930
1931
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
1931
1932
  const tag = cipher.getAuthTag();
1932
1933
  const out = Buffer.concat([salt, iv, tag, ciphertext]);
1933
- fs23__default.default.mkdirSync(path25__default.default.dirname(this.storePath), { recursive: true });
1934
- fs23__default.default.writeFileSync(this.storePath, out, { mode: 384 });
1934
+ fs24__default.default.mkdirSync(path26__default.default.dirname(this.storePath), { recursive: true });
1935
+ fs24__default.default.writeFileSync(this.storePath, out, { mode: 384 });
1935
1936
  }
1936
1937
  readRaw() {
1937
- const buf = fs23__default.default.readFileSync(this.storePath);
1938
+ const buf = fs24__default.default.readFileSync(this.storePath);
1938
1939
  let offset = 0;
1939
1940
  const salt = buf.subarray(offset, offset + SALT_LEN);
1940
1941
  offset += SALT_LEN;
@@ -1967,9 +1968,9 @@ var CascadeIgnore = class {
1967
1968
  ]);
1968
1969
  }
1969
1970
  async load(workspacePath) {
1970
- const filePath = path25__default.default.join(workspacePath, ".cascadeignore");
1971
+ const filePath = path26__default.default.join(workspacePath, ".cascadeignore");
1971
1972
  try {
1972
- const content = await fs9__default.default.readFile(filePath, "utf-8");
1973
+ const content = await fs10__default.default.readFile(filePath, "utf-8");
1973
1974
  const lines = content.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
1974
1975
  this.ig.add(lines);
1975
1976
  this.loaded = true;
@@ -1978,7 +1979,7 @@ var CascadeIgnore = class {
1978
1979
  }
1979
1980
  isIgnored(filePath, workspacePath) {
1980
1981
  try {
1981
- const relative = workspacePath ? path25__default.default.relative(workspacePath, filePath) : filePath;
1982
+ const relative = workspacePath ? path26__default.default.relative(workspacePath, filePath) : filePath;
1982
1983
  return this.ig.ignores(relative);
1983
1984
  } catch {
1984
1985
  return false;
@@ -1989,7 +1990,7 @@ var CascadeIgnore = class {
1989
1990
  }
1990
1991
  };
1991
1992
  async function createDefaultIgnoreFile(workspacePath) {
1992
- const filePath = path25__default.default.join(workspacePath, ".cascadeignore");
1993
+ const filePath = path26__default.default.join(workspacePath, ".cascadeignore");
1993
1994
  const content = `# .cascadeignore \u2014 Files Cascade agents cannot read or modify
1994
1995
  # Syntax identical to .gitignore
1995
1996
 
@@ -2016,12 +2017,12 @@ build/
2016
2017
  .DS_Store
2017
2018
  Thumbs.db
2018
2019
  `;
2019
- await fs9__default.default.writeFile(filePath, content, "utf-8");
2020
+ await fs10__default.default.writeFile(filePath, content, "utf-8");
2020
2021
  }
2021
2022
  async function loadCascadeMd(workspacePath) {
2022
- const filePath = path25__default.default.join(workspacePath, "CASCADE.md");
2023
+ const filePath = path26__default.default.join(workspacePath, "CASCADE.md");
2023
2024
  try {
2024
- const raw = await fs9__default.default.readFile(filePath, "utf-8");
2025
+ const raw = await fs10__default.default.readFile(filePath, "utf-8");
2025
2026
  return parseCascadeMd(raw);
2026
2027
  } catch {
2027
2028
  return null;
@@ -2048,7 +2049,7 @@ ${raw.trim()}`;
2048
2049
  return { raw, sections, systemPrompt };
2049
2050
  }
2050
2051
  async function createDefaultCascadeMd(workspacePath) {
2051
- const filePath = path25__default.default.join(workspacePath, "CASCADE.md");
2052
+ const filePath = path26__default.default.join(workspacePath, "CASCADE.md");
2052
2053
  const content = `# Cascade Project Instructions
2053
2054
 
2054
2055
  This file contains project-specific instructions for the Cascade AI agent.
@@ -2078,12 +2079,12 @@ All tools are allowed unless specified otherwise.
2078
2079
 
2079
2080
  List any areas the agent should not touch.
2080
2081
  `;
2081
- await fs9__default.default.writeFile(filePath, content, "utf-8");
2082
+ await fs10__default.default.writeFile(filePath, content, "utf-8");
2082
2083
  }
2083
2084
  var MemoryStore = class _MemoryStore {
2084
2085
  db;
2085
2086
  constructor(dbPath) {
2086
- fs23__default.default.mkdirSync(path25__default.default.dirname(dbPath), { recursive: true });
2087
+ fs24__default.default.mkdirSync(path26__default.default.dirname(dbPath), { recursive: true });
2087
2088
  try {
2088
2089
  this.db = new Database2__default.default(dbPath, { timeout: 5e3 });
2089
2090
  this.db.pragma("journal_mode = WAL");
@@ -3230,6 +3231,63 @@ function validateConfig(raw) {
3230
3231
  return result.data;
3231
3232
  }
3232
3233
 
3234
+ // src/config/global-credentials.ts
3235
+ init_constants();
3236
+ function credentialsPath(globalDir) {
3237
+ return path26__default.default.join(globalDir, GLOBAL_CREDENTIALS_FILE);
3238
+ }
3239
+ function providerKey(p) {
3240
+ if (p.type === "azure") {
3241
+ return `azure:${p.deploymentName ?? p.baseUrl ?? p.label ?? ""}`;
3242
+ }
3243
+ return p.type;
3244
+ }
3245
+ function isPersistable(p) {
3246
+ return Boolean(p.apiKey || p.authToken || p.type === "azure" || p.baseUrl);
3247
+ }
3248
+ function loadGlobalCredentials(globalDir) {
3249
+ try {
3250
+ const raw = fs24__default.default.readFileSync(credentialsPath(globalDir), "utf-8");
3251
+ const parsed = JSON.parse(raw);
3252
+ if (!Array.isArray(parsed.providers)) return [];
3253
+ return parsed.providers.filter(
3254
+ (p) => Boolean(p) && typeof p.type === "string"
3255
+ );
3256
+ } catch {
3257
+ return [];
3258
+ }
3259
+ }
3260
+ function saveGlobalCredentials(globalDir, providers) {
3261
+ const filePath = credentialsPath(globalDir);
3262
+ fs24__default.default.mkdirSync(globalDir, { recursive: true, mode: 448 });
3263
+ const body = { version: 1, providers: providers.filter(isPersistable) };
3264
+ const tmp = `${filePath}.tmp`;
3265
+ fs24__default.default.writeFileSync(tmp, JSON.stringify(body, null, 2), { encoding: "utf-8", mode: 384 });
3266
+ fs24__default.default.renameSync(tmp, filePath);
3267
+ try {
3268
+ fs24__default.default.chmodSync(filePath, 384);
3269
+ } catch {
3270
+ }
3271
+ }
3272
+ function mergeGlobalCredentials(workspaceProviders, globalProviders) {
3273
+ const merged = [...workspaceProviders];
3274
+ const byKey = new Map(merged.map((p) => [providerKey(p), p]));
3275
+ for (const g of globalProviders) {
3276
+ const existing = byKey.get(providerKey(g));
3277
+ if (!existing) {
3278
+ merged.push({ ...g });
3279
+ byKey.set(providerKey(g), merged[merged.length - 1]);
3280
+ continue;
3281
+ }
3282
+ if (!existing.apiKey && g.apiKey) existing.apiKey = g.apiKey;
3283
+ if (!existing.authToken && g.authToken) existing.authToken = g.authToken;
3284
+ if (!existing.baseUrl && g.baseUrl) existing.baseUrl = g.baseUrl;
3285
+ if (!existing.apiVersion && g.apiVersion) existing.apiVersion = g.apiVersion;
3286
+ if (!existing.label && g.label) existing.label = g.label;
3287
+ }
3288
+ return merged;
3289
+ }
3290
+
3233
3291
  // src/config/index.ts
3234
3292
  init_constants();
3235
3293
  var KEY_OPTIONAL_PROVIDER_TYPES = /* @__PURE__ */ new Set(["ollama", "openai-compatible"]);
@@ -3245,18 +3303,20 @@ var ConfigManager = class {
3245
3303
  cascadeMd = null;
3246
3304
  workspacePath;
3247
3305
  globalDir;
3248
- constructor(workspacePath = process.cwd()) {
3306
+ /** `globalDirOverride` exists for tests — never point it at the real home dir there. */
3307
+ constructor(workspacePath = process.cwd(), globalDirOverride) {
3249
3308
  this.workspacePath = workspacePath;
3250
- this.globalDir = path25__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR);
3309
+ this.globalDir = globalDirOverride ?? path26__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR);
3251
3310
  }
3252
3311
  async load() {
3253
3312
  this.config = await this.loadConfig();
3254
3313
  this.ignore = new CascadeIgnore();
3255
3314
  await this.ignore.load(this.workspacePath);
3256
3315
  this.cascadeMd = await loadCascadeMd(this.workspacePath);
3257
- this.keystore = new Keystore(path25__default.default.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
3258
- this.store = new MemoryStore(path25__default.default.join(this.workspacePath, CASCADE_DB_FILE));
3316
+ this.keystore = new Keystore(path26__default.default.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
3317
+ this.store = new MemoryStore(path26__default.default.join(this.workspacePath, CASCADE_DB_FILE));
3259
3318
  await this.injectEnvKeys();
3319
+ this.config.providers = mergeGlobalCredentials(this.config.providers, loadGlobalCredentials(this.globalDir));
3260
3320
  await this.ensureDefaultIdentity();
3261
3321
  }
3262
3322
  getConfig() {
@@ -3278,9 +3338,14 @@ var ConfigManager = class {
3278
3338
  return this.workspacePath;
3279
3339
  }
3280
3340
  async save() {
3281
- const configPath = path25__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
3282
- await fs9__default.default.mkdir(path25__default.default.dirname(configPath), { recursive: true });
3283
- await fs9__default.default.writeFile(configPath, JSON.stringify(this.config, null, 2), "utf-8");
3341
+ const configPath = path26__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
3342
+ await fs10__default.default.mkdir(path26__default.default.dirname(configPath), { recursive: true });
3343
+ await fs10__default.default.writeFile(configPath, JSON.stringify(this.config, null, 2), "utf-8");
3344
+ try {
3345
+ saveGlobalCredentials(this.globalDir, this.config.providers);
3346
+ } catch (err) {
3347
+ console.warn(`Failed to sync credentials to global store: ${err instanceof Error ? err.message : String(err)}`);
3348
+ }
3284
3349
  }
3285
3350
  async updateConfig(updates) {
3286
3351
  this.config = validateConfig({ ...this.config, ...updates });
@@ -3303,9 +3368,9 @@ var ConfigManager = class {
3303
3368
  return configProvider?.apiKey;
3304
3369
  }
3305
3370
  async loadConfig() {
3306
- const configPath = path25__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
3371
+ const configPath = path26__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
3307
3372
  try {
3308
- const raw = await fs9__default.default.readFile(configPath, "utf-8");
3373
+ const raw = await fs10__default.default.readFile(configPath, "utf-8");
3309
3374
  return validateConfig(JSON.parse(raw));
3310
3375
  } catch (err) {
3311
3376
  if (err.code === "ENOENT") {
@@ -4182,7 +4247,7 @@ init_constants();
4182
4247
  var DEFAULT_SNAPSHOT_URL = "https://raw.githubusercontent.com/Varun-SV/Cascade-AI/main/src/core/router/benchmark-data.json";
4183
4248
  var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
4184
4249
  var FETCH_TIMEOUT_MS = 8e3;
4185
- var DEFAULT_CACHE_FILE = path25__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
4250
+ var DEFAULT_CACHE_FILE = path26__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
4186
4251
  function normalizeModelId(id) {
4187
4252
  let s = id.toLowerCase();
4188
4253
  const slash = s.lastIndexOf("/");
@@ -4215,7 +4280,7 @@ var LiveDataProvider = class {
4215
4280
  if (this.loaded) return;
4216
4281
  this.loaded = true;
4217
4282
  try {
4218
- const raw = await fs9__default.default.readFile(this.opts.cacheFile, "utf-8");
4283
+ const raw = await fs10__default.default.readFile(this.opts.cacheFile, "utf-8");
4219
4284
  const cache = JSON.parse(raw);
4220
4285
  if (cache.snapshot?.families) {
4221
4286
  this.snapshot = cache.snapshot;
@@ -4327,14 +4392,14 @@ var LiveDataProvider = class {
4327
4392
  }
4328
4393
  async saveCache() {
4329
4394
  try {
4330
- await fs9__default.default.mkdir(path25__default.default.dirname(this.opts.cacheFile), { recursive: true });
4395
+ await fs10__default.default.mkdir(path26__default.default.dirname(this.opts.cacheFile), { recursive: true });
4331
4396
  const cache = {
4332
4397
  fetchedAt: this.fetchedAt,
4333
4398
  snapshot: this.snapshot ?? void 0,
4334
4399
  prices: Object.fromEntries(this.prices),
4335
4400
  capabilities: Object.fromEntries(this.capabilities)
4336
4401
  };
4337
- await fs9__default.default.writeFile(this.opts.cacheFile, JSON.stringify(cache, null, 2), "utf-8");
4402
+ await fs10__default.default.writeFile(this.opts.cacheFile, JSON.stringify(cache, null, 2), "utf-8");
4338
4403
  } catch {
4339
4404
  }
4340
4405
  }
@@ -6231,8 +6296,8 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : "") + textToolSuffix,
6231
6296
  tierId: this.id,
6232
6297
  sessionId: this.taskId,
6233
6298
  requireApproval: false,
6234
- saveSnapshot: async (path30, content) => {
6235
- this.store?.addFileSnapshot(this.taskId, path30, content);
6299
+ saveSnapshot: async (path31, content) => {
6300
+ this.store?.addFileSnapshot(this.taskId, path31, content);
6236
6301
  },
6237
6302
  sendPeerSync: (to, syncType, content) => {
6238
6303
  this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
@@ -6402,9 +6467,9 @@ ${assignment.expectedOutput}`;
6402
6467
  const { promisify: promisify4 } = await import('util');
6403
6468
  const execAsync3 = promisify4(exec3);
6404
6469
  for (const artifactPath of artifactPaths) {
6405
- const absolutePath = path25__default.default.resolve(process.cwd(), artifactPath);
6470
+ const absolutePath = path26__default.default.resolve(process.cwd(), artifactPath);
6406
6471
  try {
6407
- const stat = await fs9__default.default.stat(absolutePath);
6472
+ const stat = await fs10__default.default.stat(absolutePath);
6408
6473
  if (!stat.isFile()) {
6409
6474
  issues.push(`Expected artifact is not a file: ${artifactPath}`);
6410
6475
  continue;
@@ -6414,7 +6479,7 @@ ${assignment.expectedOutput}`;
6414
6479
  continue;
6415
6480
  }
6416
6481
  if (!/\.pdf$/i.test(artifactPath)) {
6417
- const content = await fs9__default.default.readFile(absolutePath, "utf-8");
6482
+ const content = await fs10__default.default.readFile(absolutePath, "utf-8");
6418
6483
  if (!content.trim()) {
6419
6484
  issues.push(`Artifact content is empty: ${artifactPath}`);
6420
6485
  continue;
@@ -6423,7 +6488,7 @@ ${assignment.expectedOutput}`;
6423
6488
  issues.push(`PDF artifact looks too small to be valid: ${artifactPath}`);
6424
6489
  continue;
6425
6490
  }
6426
- const ext = path25__default.default.extname(absolutePath).toLowerCase();
6491
+ const ext = path26__default.default.extname(absolutePath).toLowerCase();
6427
6492
  try {
6428
6493
  if (ext === ".ts" || ext === ".tsx") {
6429
6494
  await execAsync3(`npx tsc --noEmit ${absolutePath}`, { timeout: 1e4 });
@@ -8514,16 +8579,16 @@ function resolveInWorkspace(workspaceRoot, input) {
8514
8579
  if (typeof input !== "string" || input.length === 0) {
8515
8580
  throw new WorkspaceSandboxError(String(input), workspaceRoot);
8516
8581
  }
8517
- const root = path25__default.default.resolve(workspaceRoot);
8518
- const abs = path25__default.default.isAbsolute(input) ? path25__default.default.resolve(input) : path25__default.default.resolve(root, input);
8519
- const rel = path25__default.default.relative(root, abs);
8520
- if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path25__default.default.isAbsolute(rel)) {
8582
+ const root = path26__default.default.resolve(workspaceRoot);
8583
+ const abs = path26__default.default.isAbsolute(input) ? path26__default.default.resolve(input) : path26__default.default.resolve(root, input);
8584
+ const rel = path26__default.default.relative(root, abs);
8585
+ if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path26__default.default.isAbsolute(rel)) {
8521
8586
  throw new WorkspaceSandboxError(input, root);
8522
8587
  }
8523
8588
  try {
8524
- const real = fs23__default.default.realpathSync(abs);
8525
- const realRel = path25__default.default.relative(root, real);
8526
- if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path25__default.default.isAbsolute(realRel))) {
8589
+ const real = fs24__default.default.realpathSync(abs);
8590
+ const realRel = path26__default.default.relative(root, real);
8591
+ if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path26__default.default.isAbsolute(realRel))) {
8527
8592
  throw new WorkspaceSandboxError(input, root);
8528
8593
  }
8529
8594
  } catch (e) {
@@ -8550,7 +8615,7 @@ var FileReadTool = class extends BaseTool {
8550
8615
  const absPath = resolveInWorkspace(this.workspaceRoot, filePath);
8551
8616
  const offset = input["offset"] ?? 1;
8552
8617
  const limit = input["limit"];
8553
- const content = await fs9__default.default.readFile(absPath, "utf-8");
8618
+ const content = await fs10__default.default.readFile(absPath, "utf-8");
8554
8619
  const lines = content.split("\n");
8555
8620
  const start = Math.max(0, offset - 1);
8556
8621
  const end = limit ? start + limit : lines.length;
@@ -8579,13 +8644,13 @@ var FileWriteTool = class extends BaseTool {
8579
8644
  const content = input["content"];
8580
8645
  if (options.saveSnapshot) {
8581
8646
  try {
8582
- const oldContent = await fs9__default.default.readFile(absPath, "utf-8");
8647
+ const oldContent = await fs10__default.default.readFile(absPath, "utf-8");
8583
8648
  await options.saveSnapshot(absPath, oldContent);
8584
8649
  } catch {
8585
8650
  }
8586
8651
  }
8587
- await fs9__default.default.mkdir(path25__default.default.dirname(absPath), { recursive: true });
8588
- await fs9__default.default.writeFile(absPath, content, "utf-8");
8652
+ await fs10__default.default.mkdir(path26__default.default.dirname(absPath), { recursive: true });
8653
+ await fs10__default.default.writeFile(absPath, content, "utf-8");
8589
8654
  return `Written ${content.length} characters to ${filePath}`;
8590
8655
  }
8591
8656
  };
@@ -8611,7 +8676,7 @@ var FileEditTool = class extends BaseTool {
8611
8676
  const oldString = input["old_string"];
8612
8677
  const newString = input["new_string"];
8613
8678
  const replaceAll = input["replace_all"] ?? false;
8614
- const rawContent = await fs9__default.default.readFile(absPath, "utf-8");
8679
+ const rawContent = await fs10__default.default.readFile(absPath, "utf-8");
8615
8680
  if (options.saveSnapshot) {
8616
8681
  await options.saveSnapshot(absPath, rawContent);
8617
8682
  }
@@ -8623,7 +8688,7 @@ var FileEditTool = class extends BaseTool {
8623
8688
  );
8624
8689
  }
8625
8690
  const updated = replaceAll ? content.split(normalizedOld).join(newString) : content.replace(normalizedOld, newString);
8626
- await fs9__default.default.writeFile(absPath, updated, "utf-8");
8691
+ await fs10__default.default.writeFile(absPath, updated, "utf-8");
8627
8692
  const count = replaceAll ? content.split(normalizedOld).length - 1 : 1;
8628
8693
  return `Replaced ${count} occurrence(s) in ${filePath}`;
8629
8694
  }
@@ -8646,12 +8711,12 @@ var FileDeleteTool = class extends BaseTool {
8646
8711
  const absPath = resolveInWorkspace(this.workspaceRoot, filePath);
8647
8712
  if (options.saveSnapshot) {
8648
8713
  try {
8649
- const oldContent = await fs9__default.default.readFile(absPath, "utf-8");
8714
+ const oldContent = await fs10__default.default.readFile(absPath, "utf-8");
8650
8715
  await options.saveSnapshot(absPath, oldContent);
8651
8716
  } catch {
8652
8717
  }
8653
8718
  }
8654
- await fs9__default.default.rm(absPath, { recursive: false });
8719
+ await fs10__default.default.rm(absPath, { recursive: false });
8655
8720
  return `Deleted ${filePath}`;
8656
8721
  }
8657
8722
  };
@@ -8668,7 +8733,7 @@ var FileListTool = class extends BaseTool {
8668
8733
  async execute(input, _options) {
8669
8734
  const inputPath = input["path"] || ".";
8670
8735
  const absPath = resolveInWorkspace(this.workspaceRoot, inputPath);
8671
- const entries = await fs9__default.default.readdir(absPath, { withFileTypes: true });
8736
+ const entries = await fs10__default.default.readdir(absPath, { withFileTypes: true });
8672
8737
  return entries.map((e) => `${e.isDirectory() ? "[DIR] " : " "}${e.name}`).join("\n") || "(empty directory)";
8673
8738
  }
8674
8739
  };
@@ -9081,8 +9146,8 @@ var ImageAnalyzeTool = class extends BaseTool {
9081
9146
  }
9082
9147
  };
9083
9148
  async function fileToImageAttachment(filePath) {
9084
- const data = await fs9__default.default.readFile(filePath);
9085
- const ext = path25__default.default.extname(filePath).toLowerCase();
9149
+ const data = await fs10__default.default.readFile(filePath);
9150
+ const ext = path26__default.default.extname(filePath).toLowerCase();
9086
9151
  const mimeMap = {
9087
9152
  ".jpg": "image/jpeg",
9088
9153
  ".jpeg": "image/jpeg",
@@ -9116,14 +9181,14 @@ var PDFCreateTool = class extends BaseTool {
9116
9181
  const filePath = input["path"];
9117
9182
  const content = input["content"];
9118
9183
  const title = input["title"];
9119
- const dir = path25__default.default.dirname(filePath);
9120
- if (!fs23__default.default.existsSync(dir)) {
9121
- fs23__default.default.mkdirSync(dir, { recursive: true });
9184
+ const dir = path26__default.default.dirname(filePath);
9185
+ if (!fs24__default.default.existsSync(dir)) {
9186
+ fs24__default.default.mkdirSync(dir, { recursive: true });
9122
9187
  }
9123
9188
  return new Promise((resolve, reject) => {
9124
9189
  try {
9125
9190
  const doc = new PDFDocument__default.default({ margin: 50 });
9126
- const stream = fs23__default.default.createWriteStream(filePath);
9191
+ const stream = fs24__default.default.createWriteStream(filePath);
9127
9192
  doc.pipe(stream);
9128
9193
  if (title) {
9129
9194
  doc.info["Title"] = title;
@@ -9201,22 +9266,22 @@ var CodeInterpreterTool = class extends BaseTool {
9201
9266
  }
9202
9267
  cmdPrefix = NODE_CMD;
9203
9268
  }
9204
- const tmpDir = path25__default.default.join(this.workspaceRoot, ".cascade", "tmp");
9205
- if (!fs23__default.default.existsSync(tmpDir)) {
9206
- fs23__default.default.mkdirSync(tmpDir, { recursive: true });
9269
+ const tmpDir = path26__default.default.join(this.workspaceRoot, ".cascade", "tmp");
9270
+ if (!fs24__default.default.existsSync(tmpDir)) {
9271
+ fs24__default.default.mkdirSync(tmpDir, { recursive: true });
9207
9272
  }
9208
9273
  const extension = language === "python" ? "py" : "js";
9209
9274
  const fileName = `intp_${crypto.randomUUID().slice(0, 8)}.${extension}`;
9210
- const filePath = path25__default.default.join(tmpDir, fileName);
9211
- fs23__default.default.writeFileSync(filePath, code, "utf-8");
9275
+ const filePath = path26__default.default.join(tmpDir, fileName);
9276
+ fs24__default.default.writeFileSync(filePath, code, "utf-8");
9212
9277
  const execArgs = [filePath, ...args];
9213
9278
  return new Promise((resolve) => {
9214
9279
  const startMs = Date.now();
9215
9280
  child_process.execFile(cmdPrefix, execArgs, { cwd: this.workspaceRoot, timeout: 3e4 }, (error, stdout, stderr) => {
9216
9281
  const duration = Date.now() - startMs;
9217
9282
  try {
9218
- if (fs23__default.default.existsSync(filePath)) {
9219
- fs23__default.default.unlinkSync(filePath);
9283
+ if (fs24__default.default.existsSync(filePath)) {
9284
+ fs24__default.default.unlinkSync(filePath);
9220
9285
  }
9221
9286
  } catch (cleanupErr) {
9222
9287
  console.error(`Failed to cleanup interpreter script ${filePath}:`, cleanupErr);
@@ -9495,7 +9560,7 @@ var GlobTool = class extends BaseTool {
9495
9560
  };
9496
9561
  async execute(input, _options) {
9497
9562
  const pattern = input["pattern"];
9498
- const searchPath = input["path"] ? path25__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
9563
+ const searchPath = input["path"] ? path26__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
9499
9564
  const matches = await glob.glob(pattern, {
9500
9565
  cwd: searchPath,
9501
9566
  ignore: ["node_modules/**", ".git/**", "dist/**", "build/**"],
@@ -9508,7 +9573,7 @@ var GlobTool = class extends BaseTool {
9508
9573
  const withMtime = await Promise.all(
9509
9574
  matches.map(async (rel) => {
9510
9575
  try {
9511
- const stat = await fs9__default.default.stat(path25__default.default.join(searchPath, rel));
9576
+ const stat = await fs10__default.default.stat(path26__default.default.join(searchPath, rel));
9512
9577
  return { rel, mtime: stat.mtimeMs };
9513
9578
  } catch {
9514
9579
  return { rel, mtime: 0 };
@@ -9557,7 +9622,7 @@ var GrepTool = class extends BaseTool {
9557
9622
  };
9558
9623
  async execute(input, _options) {
9559
9624
  const pattern = input["pattern"];
9560
- const searchPath = input["path"] ? path25__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
9625
+ const searchPath = input["path"] ? path26__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
9561
9626
  const globPattern = input["glob"];
9562
9627
  const outputMode = input["output_mode"] ?? "content";
9563
9628
  const context = input["context"] ?? 0;
@@ -9611,15 +9676,15 @@ var GrepTool = class extends BaseTool {
9611
9676
  nodir: true
9612
9677
  });
9613
9678
  } catch {
9614
- files = [path25__default.default.relative(searchPath, searchPath) || "."];
9679
+ files = [path26__default.default.relative(searchPath, searchPath) || "."];
9615
9680
  }
9616
9681
  const results = [];
9617
9682
  let totalCount = 0;
9618
9683
  for (const rel of files) {
9619
- const abs = path25__default.default.join(searchPath, rel);
9684
+ const abs = path26__default.default.join(searchPath, rel);
9620
9685
  let content;
9621
9686
  try {
9622
- content = await fs9__default.default.readFile(abs, "utf-8");
9687
+ content = await fs10__default.default.readFile(abs, "utf-8");
9623
9688
  } catch {
9624
9689
  continue;
9625
9690
  }
@@ -9978,10 +10043,10 @@ var ToolRegistry = class extends EventEmitter__default.default {
9978
10043
  }
9979
10044
  isIgnored(filePath) {
9980
10045
  if (!filePath) return false;
9981
- const abs = path25__default.default.resolve(this.workspaceRoot, filePath);
9982
- const rel = path25__default.default.relative(this.workspaceRoot, abs);
9983
- if (!rel || rel.startsWith("..") || path25__default.default.isAbsolute(rel)) return true;
9984
- const posixRel = rel.split(path25__default.default.sep).join("/");
10046
+ const abs = path26__default.default.resolve(this.workspaceRoot, filePath);
10047
+ const rel = path26__default.default.relative(this.workspaceRoot, abs);
10048
+ if (!rel || rel.startsWith("..") || path26__default.default.isAbsolute(rel)) return true;
10049
+ const posixRel = rel.split(path26__default.default.sep).join("/");
9985
10050
  return this.ignoreMatcher.ignores(posixRel);
9986
10051
  }
9987
10052
  };
@@ -10504,7 +10569,7 @@ var TaskAnalyzer = class {
10504
10569
  analysisCache.clear();
10505
10570
  }
10506
10571
  };
10507
- var DEFAULT_STATS_FILE = path25__default.default.join(os8__default.default.homedir(), ".cascade", "model-perf.json");
10572
+ var DEFAULT_STATS_FILE = path26__default.default.join(os8__default.default.homedir(), ".cascade", "model-perf.json");
10508
10573
  var ModelPerformanceTracker = class {
10509
10574
  stats = /* @__PURE__ */ new Map();
10510
10575
  featureStats = /* @__PURE__ */ new Map();
@@ -10517,7 +10582,7 @@ var ModelPerformanceTracker = class {
10517
10582
  if (this.loaded) return;
10518
10583
  this.loaded = true;
10519
10584
  try {
10520
- const raw = await fs9__default.default.readFile(this.statsFile, "utf-8");
10585
+ const raw = await fs10__default.default.readFile(this.statsFile, "utf-8");
10521
10586
  const parsed = JSON.parse(raw);
10522
10587
  if (parsed.models) {
10523
10588
  for (const [key, stat] of Object.entries(parsed.models)) this.stats.set(key, stat);
@@ -10536,12 +10601,12 @@ var ModelPerformanceTracker = class {
10536
10601
  }
10537
10602
  async save() {
10538
10603
  try {
10539
- await fs9__default.default.mkdir(path25__default.default.dirname(this.statsFile), { recursive: true });
10604
+ await fs10__default.default.mkdir(path26__default.default.dirname(this.statsFile), { recursive: true });
10540
10605
  const modelsObj = {};
10541
10606
  const featuresObj = {};
10542
10607
  for (const [key, stat] of this.stats) modelsObj[key] = stat;
10543
10608
  for (const [key, stat] of this.featureStats) featuresObj[key] = stat;
10544
- await fs9__default.default.writeFile(this.statsFile, JSON.stringify({ models: modelsObj, features: featuresObj }, null, 2), "utf-8");
10609
+ await fs10__default.default.writeFile(this.statsFile, JSON.stringify({ models: modelsObj, features: featuresObj }, null, 2), "utf-8");
10545
10610
  } catch {
10546
10611
  }
10547
10612
  }
@@ -11075,9 +11140,9 @@ Required capability: ${description.slice(0, 300)}`;
11075
11140
  * any dangerous action, so a silently-reloaded tool can't act without approval. */
11076
11141
  async loadPersistedTools() {
11077
11142
  if (!this.workspacePath || !this.persistEnabled) return;
11078
- const file = path25__default.default.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
11143
+ const file = path26__default.default.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
11079
11144
  try {
11080
- const raw = await fs9__default.default.readFile(file, "utf-8");
11145
+ const raw = await fs10__default.default.readFile(file, "utf-8");
11081
11146
  const specs = JSON.parse(raw);
11082
11147
  if (!Array.isArray(specs)) return;
11083
11148
  let loaded = 0;
@@ -11099,11 +11164,11 @@ Required capability: ${description.slice(0, 300)}`;
11099
11164
  }
11100
11165
  async persist() {
11101
11166
  if (!this.workspacePath || !this.persistEnabled) return;
11102
- const dir = path25__default.default.join(this.workspacePath, ".cascade");
11103
- const file = path25__default.default.join(dir, DYNAMIC_TOOLS_FILE);
11167
+ const dir = path26__default.default.join(this.workspacePath, ".cascade");
11168
+ const file = path26__default.default.join(dir, DYNAMIC_TOOLS_FILE);
11104
11169
  try {
11105
- await fs9__default.default.mkdir(dir, { recursive: true });
11106
- await fs9__default.default.writeFile(file, JSON.stringify(Array.from(this.specs.values()), null, 2), "utf-8");
11170
+ await fs10__default.default.mkdir(dir, { recursive: true });
11171
+ await fs10__default.default.writeFile(file, JSON.stringify(Array.from(this.specs.values()), null, 2), "utf-8");
11107
11172
  } catch (err) {
11108
11173
  this.log(`[tool-creator] Failed to persist tools: ${err instanceof Error ? err.message : String(err)}`);
11109
11174
  }
@@ -11120,12 +11185,12 @@ var WorldStateDB = class {
11120
11185
  constructor(workspacePath, debugMode = false) {
11121
11186
  this.workspacePath = workspacePath;
11122
11187
  this.debugMode = debugMode;
11123
- const cascadeDir = path25__default.default.join(workspacePath, ".cascade");
11124
- if (!fs23__default.default.existsSync(cascadeDir)) {
11125
- fs23__default.default.mkdirSync(cascadeDir, { recursive: true });
11188
+ const cascadeDir = path26__default.default.join(workspacePath, ".cascade");
11189
+ if (!fs24__default.default.existsSync(cascadeDir)) {
11190
+ fs24__default.default.mkdirSync(cascadeDir, { recursive: true });
11126
11191
  }
11127
- this.keyPath = path25__default.default.join(cascadeDir, "world_state.key");
11128
- this.dbPath = path25__default.default.join(cascadeDir, "world_state.db");
11192
+ this.keyPath = path26__default.default.join(cascadeDir, "world_state.key");
11193
+ this.dbPath = path26__default.default.join(cascadeDir, "world_state.db");
11129
11194
  this.initEncryptionKey();
11130
11195
  this.db = new Database2__default.default(this.dbPath);
11131
11196
  this.db.pragma("journal_mode = WAL");
@@ -11155,11 +11220,11 @@ var WorldStateDB = class {
11155
11220
  dbPath;
11156
11221
  encryptionKey;
11157
11222
  initEncryptionKey() {
11158
- if (fs23__default.default.existsSync(this.keyPath)) {
11159
- this.encryptionKey = fs23__default.default.readFileSync(this.keyPath);
11223
+ if (fs24__default.default.existsSync(this.keyPath)) {
11224
+ this.encryptionKey = fs24__default.default.readFileSync(this.keyPath);
11160
11225
  } else {
11161
11226
  this.encryptionKey = crypto__default.default.randomBytes(32);
11162
- fs23__default.default.writeFileSync(this.keyPath, this.encryptionKey);
11227
+ fs24__default.default.writeFileSync(this.keyPath, this.encryptionKey);
11163
11228
  }
11164
11229
  }
11165
11230
  encrypt(text) {
@@ -11300,6 +11365,22 @@ var WorldStateDB = class {
11300
11365
  }
11301
11366
  return selected.slice(0, limit).map((f) => `- ${f.entity} ${f.relation} ${f.value}`).join("\n");
11302
11367
  }
11368
+ /**
11369
+ * Delete one fact by its (normalized) entity + relation key. Returns whether
11370
+ * a row was actually removed. Powers the desktop Knowledge tab's per-fact
11371
+ * delete — users can prune what the planner remembers about their project.
11372
+ */
11373
+ deleteFact(entity, relation) {
11374
+ const e = normalizeKey(entity);
11375
+ const r = normalizeKey(relation);
11376
+ if (!e || !r) return false;
11377
+ const result = this.db.prepare("DELETE FROM facts WHERE entity = ? AND relation = ?").run(e, r);
11378
+ return result.changes > 0;
11379
+ }
11380
+ /** Delete every fact. Returns how many were removed. */
11381
+ clearFacts() {
11382
+ return this.db.prepare("DELETE FROM facts").run().changes;
11383
+ }
11303
11384
  // ── Export / Import ──────────────────────────
11304
11385
  //
11305
11386
  // Knowledge travels DECRYPTED in the export bundle (a portable plaintext
@@ -11348,9 +11429,9 @@ var WorldStateDB = class {
11348
11429
  dumpDebugIfNeeded() {
11349
11430
  if (!this.debugMode) return;
11350
11431
  try {
11351
- const dumpPath = path25__default.default.join(os8__default.default.tmpdir(), "cascade_world_state_debug.json");
11432
+ const dumpPath = path26__default.default.join(os8__default.default.tmpdir(), "cascade_world_state_debug.json");
11352
11433
  const entries = this.getAllEntries();
11353
- fs23__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
11434
+ fs24__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
11354
11435
  } catch (err) {
11355
11436
  console.error("Failed to dump debug world state", err);
11356
11437
  }
@@ -13768,10 +13849,10 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, a
13768
13849
  }
13769
13850
  } catch {
13770
13851
  }
13771
- const configPath = path25__default.default.join(workspacePath, ".cascade", "config.json");
13852
+ const configPath = path26__default.default.join(workspacePath, ".cascade", "config.json");
13772
13853
  try {
13773
- await fs9__default.default.mkdir(path25__default.default.dirname(configPath), { recursive: true });
13774
- await fs9__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
13854
+ await fs10__default.default.mkdir(path26__default.default.dirname(configPath), { recursive: true });
13855
+ await fs10__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
13775
13856
  } catch (err) {
13776
13857
  const msg = err instanceof Error ? err.message : String(err);
13777
13858
  dispatch({
@@ -13822,9 +13903,9 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, a
13822
13903
  if (msg.includes("non-text parts")) return;
13823
13904
  originalLog(...args);
13824
13905
  };
13825
- const store = new MemoryStore(path25__default.default.join(workspacePath, CASCADE_DB_FILE));
13906
+ const store = new MemoryStore(path26__default.default.join(workspacePath, CASCADE_DB_FILE));
13826
13907
  storeRef.current = store;
13827
- globalStoreRef.current = new MemoryStore(path25__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE));
13908
+ globalStoreRef.current = new MemoryStore(path26__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE));
13828
13909
  const identityRows = store.listIdentities().map((i) => ({ id: i.id, name: i.name, isDefault: i.isDefault }));
13829
13910
  setIdentities(identityRows);
13830
13911
  let initialIdentityId = config.defaultIdentityId ?? identityRows.find((i) => i.isDefault)?.id ?? identityRows[0]?.id;
@@ -13887,14 +13968,14 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, a
13887
13968
  onThemeChange: (name) => setTheme(getTheme(name)),
13888
13969
  onExport: async (fmt) => {
13889
13970
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
13890
- const exportPath = path25__default.default.join(workspacePath, `cascade-export-${stamp}.${fmt === "json" ? "json" : "md"}`);
13971
+ const exportPath = path26__default.default.join(workspacePath, `cascade-export-${stamp}.${fmt === "json" ? "json" : "md"}`);
13891
13972
  if (fmt === "json") {
13892
- await fs9__default.default.writeFile(exportPath, JSON.stringify({ sessionId: sessionIdRef.current, messages: state.messages }, null, 2), "utf-8");
13973
+ await fs10__default.default.writeFile(exportPath, JSON.stringify({ sessionId: sessionIdRef.current, messages: state.messages }, null, 2), "utf-8");
13893
13974
  } else {
13894
13975
  const markdown = state.messages.map((msg) => `## ${msg.role.toUpperCase()} \u2014 ${msg.timestamp}
13895
13976
 
13896
13977
  ${msg.content}`).join("\n\n");
13897
- await fs9__default.default.writeFile(exportPath, markdown, "utf-8");
13978
+ await fs10__default.default.writeFile(exportPath, markdown, "utf-8");
13898
13979
  }
13899
13980
  },
13900
13981
  onRollback: async () => {
@@ -13904,7 +13985,7 @@ ${msg.content}`).join("\n\n");
13904
13985
  if (!snapshots.length) return "No file snapshots found for this session.";
13905
13986
  for (const { filePath, content } of snapshots) {
13906
13987
  try {
13907
- await fs9__default.default.writeFile(filePath, content, "utf-8");
13988
+ await fs10__default.default.writeFile(filePath, content, "utf-8");
13908
13989
  } catch (err) {
13909
13990
  console.error(`Restore failed: ${filePath}`, err);
13910
13991
  }
@@ -14783,9 +14864,9 @@ function stringifySlashOutput(val) {
14783
14864
  }
14784
14865
  async function searchSessionsAndMessages(query, workspacePath) {
14785
14866
  if (!query) return "Usage: /search <query>";
14786
- const dbPath = path25__default.default.join(workspacePath, CASCADE_DB_FILE);
14867
+ const dbPath = path26__default.default.join(workspacePath, CASCADE_DB_FILE);
14787
14868
  try {
14788
- await fs9__default.default.access(dbPath);
14869
+ await fs10__default.default.access(dbPath);
14789
14870
  } catch {
14790
14871
  return "No database found. Start a conversation first.";
14791
14872
  }
@@ -14817,7 +14898,7 @@ async function searchSessionsAndMessages(query, workspacePath) {
14817
14898
  async function diagnoseRuntime(config, workspacePath) {
14818
14899
  const providers = config.providers.map((p) => `${p.type}${p.apiKey ? " (key set)" : " (no key)"}`).join("\n");
14819
14900
  const models = [`T1: ${config.models.t1 ?? "default"}`, `T2: ${config.models.t2 ?? "default"}`, `T3: ${config.models.t3 ?? "default"}`].join("\n");
14820
- const store = new MemoryStore(path25__default.default.join(workspacePath, CASCADE_DB_FILE));
14901
+ const store = new MemoryStore(path26__default.default.join(workspacePath, CASCADE_DB_FILE));
14821
14902
  try {
14822
14903
  const sessions = store.listSessions(void 0, 3);
14823
14904
  return [
@@ -14836,7 +14917,7 @@ async function diagnoseRuntime(config, workspacePath) {
14836
14917
  }
14837
14918
  async function showRecentLogs(args, workspacePath) {
14838
14919
  const limit = Number.parseInt(args[0] ?? "10", 10) || 10;
14839
- const store = new MemoryStore(path25__default.default.join(workspacePath, CASCADE_DB_FILE));
14920
+ const store = new MemoryStore(path26__default.default.join(workspacePath, CASCADE_DB_FILE));
14840
14921
  try {
14841
14922
  const logs = store.listRuntimeNodeLogs(void 0, void 0, limit);
14842
14923
  if (!logs.length) return "No recent runtime logs.";
@@ -14848,7 +14929,7 @@ async function showRecentLogs(args, workspacePath) {
14848
14929
  async function loadSessionSnapshot(args, workspacePath) {
14849
14930
  const sessionId = args[0];
14850
14931
  if (!sessionId) return "Usage: /resume <sessionId>";
14851
- const store = new MemoryStore(path25__default.default.join(workspacePath, CASCADE_DB_FILE));
14932
+ const store = new MemoryStore(path26__default.default.join(workspacePath, CASCADE_DB_FILE));
14852
14933
  try {
14853
14934
  const session = store.getSession(sessionId);
14854
14935
  if (!session) return `Session not found: ${sessionId}`;
@@ -15268,10 +15349,10 @@ function SetupWizard({ workspacePath, onComplete }) {
15268
15349
  ...anyAuto ? { cascadeAuto: true } : {}
15269
15350
  };
15270
15351
  const config = CascadeConfigSchema.parse(rawConfig);
15271
- const configDir = path25__default.default.join(workspacePath, ".cascade");
15272
- await fs9__default.default.mkdir(configDir, { recursive: true });
15273
- const configPath = path25__default.default.join(workspacePath, CASCADE_CONFIG_FILE);
15274
- await fs9__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
15352
+ const configDir = path26__default.default.join(workspacePath, ".cascade");
15353
+ await fs10__default.default.mkdir(configDir, { recursive: true });
15354
+ const configPath = path26__default.default.join(workspacePath, CASCADE_CONFIG_FILE);
15355
+ await fs10__default.default.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
15275
15356
  savedConfigRef.current = config;
15276
15357
  dispatchRef.current({ type: "GO_DONE" });
15277
15358
  } catch (err) {
@@ -15611,14 +15692,14 @@ function printTelemetryBanner() {
15611
15692
  async function initCommand(workspacePath = process.cwd()) {
15612
15693
  const spin = ora__default.default({ text: "Initializing Cascade project\u2026", color: "magenta" }).start();
15613
15694
  try {
15614
- const configDir = path25__default.default.join(workspacePath, ".cascade");
15615
- await fs9__default.default.mkdir(configDir, { recursive: true });
15616
- const mdPath = path25__default.default.join(workspacePath, "CASCADE.md");
15695
+ const configDir = path26__default.default.join(workspacePath, ".cascade");
15696
+ await fs10__default.default.mkdir(configDir, { recursive: true });
15697
+ const mdPath = path26__default.default.join(workspacePath, "CASCADE.md");
15617
15698
  if (!await fileExists(mdPath)) {
15618
15699
  await createDefaultCascadeMd(workspacePath);
15619
15700
  spin.succeed(chalk9__default.default.green("Created CASCADE.md"));
15620
15701
  }
15621
- const ignorePath = path25__default.default.join(workspacePath, ".cascadeignore");
15702
+ const ignorePath = path26__default.default.join(workspacePath, ".cascadeignore");
15622
15703
  if (!await fileExists(ignorePath)) {
15623
15704
  await createDefaultIgnoreFile(workspacePath);
15624
15705
  spin.succeed(chalk9__default.default.green("Created .cascadeignore"));
@@ -15627,7 +15708,7 @@ async function initCommand(workspacePath = process.cwd()) {
15627
15708
  console.log();
15628
15709
  console.log(chalk9__default.default.magenta(" \u25C8 Cascade AI \u2014 Project initialized"));
15629
15710
  console.log();
15630
- const configPath = path25__default.default.join(workspacePath, CASCADE_CONFIG_FILE);
15711
+ const configPath = path26__default.default.join(workspacePath, CASCADE_CONFIG_FILE);
15631
15712
  if (await fileExists(configPath)) {
15632
15713
  console.log(chalk9__default.default.yellow(" .cascade/config.json already exists \u2014 launching wizard to reconfigure."));
15633
15714
  console.log();
@@ -15646,7 +15727,7 @@ async function initCommand(workspacePath = process.cwd()) {
15646
15727
  }
15647
15728
  async function fileExists(p) {
15648
15729
  try {
15649
- await fs9__default.default.access(p);
15730
+ await fs10__default.default.access(p);
15650
15731
  return true;
15651
15732
  } catch {
15652
15733
  return false;
@@ -15658,7 +15739,7 @@ init_constants();
15658
15739
  var TOS_WARNING = "Reusing this subscription token outside its own CLI may violate the vendor\u2019s terms of service.";
15659
15740
  async function readJson(file) {
15660
15741
  try {
15661
- const raw = await fs9__default.default.readFile(file, "utf-8");
15742
+ const raw = await fs10__default.default.readFile(file, "utf-8");
15662
15743
  return JSON.parse(raw);
15663
15744
  } catch {
15664
15745
  return null;
@@ -15685,7 +15766,7 @@ function fromEnv(env) {
15685
15766
  return out;
15686
15767
  }
15687
15768
  async function fromClaudeCode(home) {
15688
- const file = path25__default.default.join(home, ".claude", ".credentials.json");
15769
+ const file = path26__default.default.join(home, ".claude", ".credentials.json");
15689
15770
  const data = await readJson(file);
15690
15771
  if (!data) return [];
15691
15772
  const oauth = data["claudeAiOauth"];
@@ -15708,7 +15789,7 @@ async function fromClaudeCode(home) {
15708
15789
  return [];
15709
15790
  }
15710
15791
  async function fromCodex(home) {
15711
- const file = path25__default.default.join(home, ".codex", "auth.json");
15792
+ const file = path26__default.default.join(home, ".codex", "auth.json");
15712
15793
  const data = await readJson(file);
15713
15794
  if (!data) return [];
15714
15795
  const apiKey = str(data["OPENAI_API_KEY"]);
@@ -15731,7 +15812,7 @@ async function fromCodex(home) {
15731
15812
  return [];
15732
15813
  }
15733
15814
  async function fromGemini(home) {
15734
- const file = path25__default.default.join(home, ".gemini", "oauth_creds.json");
15815
+ const file = path26__default.default.join(home, ".gemini", "oauth_creds.json");
15735
15816
  const data = await readJson(file);
15736
15817
  const accessToken = str(data?.["access_token"]);
15737
15818
  if (!accessToken) return [];
@@ -15747,7 +15828,7 @@ async function fromGemini(home) {
15747
15828
  }
15748
15829
  async function fromCopilot(home) {
15749
15830
  for (const name of ["apps.json", "hosts.json"]) {
15750
- const file = path25__default.default.join(home, ".config", "github-copilot", name);
15831
+ const file = path26__default.default.join(home, ".config", "github-copilot", name);
15751
15832
  const data = await readJson(file);
15752
15833
  if (!data) continue;
15753
15834
  for (const value of Object.values(data)) {
@@ -15801,7 +15882,7 @@ async function doctorCommand() {
15801
15882
  checks.push({
15802
15883
  label: "Cascade config",
15803
15884
  ok: true,
15804
- detail: `Loaded ${path25__default.default.join(process.cwd(), CASCADE_CONFIG_FILE)}`
15885
+ detail: `Loaded ${path26__default.default.join(process.cwd(), CASCADE_CONFIG_FILE)}`
15805
15886
  });
15806
15887
  const providers = [
15807
15888
  { type: "anthropic", name: "Anthropic" },
@@ -15957,6 +16038,11 @@ function authMiddleware(secret, required = true) {
15957
16038
  }
15958
16039
  const user = verifyToken(token, secret);
15959
16040
  if (!user) {
16041
+ if (!required) {
16042
+ req.user = void 0;
16043
+ next();
16044
+ return;
16045
+ }
15960
16046
  res.status(401).json({ error: "Invalid or expired token" });
15961
16047
  return;
15962
16048
  }
@@ -16268,7 +16354,7 @@ function aggregateCostStats(sessions, opts = {}) {
16268
16354
  }
16269
16355
 
16270
16356
  // src/dashboard/server.ts
16271
- var __dirname$1 = path25__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))));
16357
+ var __dirname$1 = path26__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))));
16272
16358
  var DashboardServer = class {
16273
16359
  app;
16274
16360
  httpServer;
@@ -16502,12 +16588,17 @@ var DashboardServer = class {
16502
16588
  */
16503
16589
  persistConfig() {
16504
16590
  try {
16505
- const configPath = path25__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
16506
- fs23__default.default.mkdirSync(path25__default.default.dirname(configPath), { recursive: true });
16507
- fs23__default.default.writeFileSync(configPath, JSON.stringify(this.config, null, 2), "utf-8");
16591
+ const configPath = path26__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
16592
+ fs24__default.default.mkdirSync(path26__default.default.dirname(configPath), { recursive: true });
16593
+ fs24__default.default.writeFileSync(configPath, JSON.stringify(this.config, null, 2), "utf-8");
16508
16594
  } catch (err) {
16509
16595
  console.warn(`[dashboard] Failed to persist config: ${err instanceof Error ? err.message : String(err)}`);
16510
16596
  }
16597
+ try {
16598
+ saveGlobalCredentials(path26__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR), this.config.providers ?? []);
16599
+ } catch (err) {
16600
+ console.warn(`[dashboard] Failed to sync global credentials: ${err instanceof Error ? err.message : String(err)}`);
16601
+ }
16511
16602
  }
16512
16603
  /**
16513
16604
  * Produce a stable dashboard JWT signing secret.
@@ -16519,15 +16610,15 @@ var DashboardServer = class {
16519
16610
  resolveDashboardSecret() {
16520
16611
  const fromConfig = this.config.dashboard.secret ?? process.env["CASCADE_DASHBOARD_SECRET"];
16521
16612
  if (fromConfig) return fromConfig;
16522
- const secretPath = path25__default.default.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
16613
+ const secretPath = path26__default.default.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
16523
16614
  try {
16524
- if (fs23__default.default.existsSync(secretPath)) {
16525
- const existing = fs23__default.default.readFileSync(secretPath, "utf-8").trim();
16615
+ if (fs24__default.default.existsSync(secretPath)) {
16616
+ const existing = fs24__default.default.readFileSync(secretPath, "utf-8").trim();
16526
16617
  if (existing.length >= 16) return existing;
16527
16618
  }
16528
16619
  const generated = crypto.randomUUID();
16529
- fs23__default.default.mkdirSync(path25__default.default.dirname(secretPath), { recursive: true });
16530
- fs23__default.default.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
16620
+ fs24__default.default.mkdirSync(path26__default.default.dirname(secretPath), { recursive: true });
16621
+ fs24__default.default.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
16531
16622
  if (this.config.dashboard.auth) {
16532
16623
  console.warn(
16533
16624
  `Dashboard auth enabled with no secret configured; persisted a generated secret to ${secretPath}. Set CASCADE_DASHBOARD_SECRET or config.dashboard.secret to override.`
@@ -16554,7 +16645,7 @@ var DashboardServer = class {
16554
16645
  // ── Setup ─────────────────────────────────────
16555
16646
  getGlobalStore() {
16556
16647
  if (!this.globalStore) {
16557
- const globalDbPath = path25__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
16648
+ const globalDbPath = path26__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
16558
16649
  this.globalStore = new MemoryStore(globalDbPath);
16559
16650
  }
16560
16651
  return this.globalStore;
@@ -16823,12 +16914,12 @@ ${prompt}`;
16823
16914
  }
16824
16915
  }
16825
16916
  watchRuntimeChanges() {
16826
- const workspaceDbPath = path25__default.default.join(this.workspacePath, CASCADE_DB_FILE);
16827
- const globalDbPath = path25__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
16917
+ const workspaceDbPath = path26__default.default.join(this.workspacePath, CASCADE_DB_FILE);
16918
+ const globalDbPath = path26__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
16828
16919
  const watchPaths = [workspaceDbPath, globalDbPath].filter((p, index, arr) => arr.indexOf(p) === index);
16829
16920
  for (const watchPath of watchPaths) {
16830
- if (!fs23__default.default.existsSync(watchPath)) continue;
16831
- fs23__default.default.watchFile(watchPath, { interval: 3e3 }, () => {
16921
+ if (!fs24__default.default.existsSync(watchPath)) continue;
16922
+ fs24__default.default.watchFile(watchPath, { interval: 3e3 }, () => {
16832
16923
  this.throttledBroadcast(watchPath === globalDbPath ? "global" : "workspace");
16833
16924
  });
16834
16925
  }
@@ -16950,7 +17041,7 @@ ${prompt}`;
16950
17041
  const sessionId = req.params.id;
16951
17042
  this.store.deleteSession(sessionId);
16952
17043
  this.store.deleteRuntimeSession(sessionId);
16953
- const globalDbPath = path25__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
17044
+ const globalDbPath = path26__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
16954
17045
  const globalStore = new MemoryStore(globalDbPath);
16955
17046
  try {
16956
17047
  globalStore.deleteRuntimeSession(sessionId);
@@ -17043,7 +17134,7 @@ ${prompt}`;
17043
17134
  });
17044
17135
  this.app.delete("/api/sessions", auth, (req, res) => {
17045
17136
  const body = req.body;
17046
- const globalDbPath = path25__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
17137
+ const globalDbPath = path26__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
17047
17138
  if (body?.ids && Array.isArray(body.ids) && body.ids.length > 0) {
17048
17139
  const globalStore = new MemoryStore(globalDbPath);
17049
17140
  try {
@@ -17066,7 +17157,7 @@ ${prompt}`;
17066
17157
  });
17067
17158
  this.app.delete("/api/runtime", auth, (_req, res) => {
17068
17159
  this.store.deleteAllRuntimeNodes();
17069
- const globalDbPath = path25__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
17160
+ const globalDbPath = path26__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
17070
17161
  const globalStore = new MemoryStore(globalDbPath);
17071
17162
  try {
17072
17163
  globalStore.deleteAllRuntimeNodes();
@@ -17152,12 +17243,12 @@ ${prompt}`;
17152
17243
  if (body["tierLimits"]) this.config.tierLimits = { ...this.config.tierLimits, ...body["tierLimits"] };
17153
17244
  if (body["budget"]) this.config.budget = { ...this.config.budget, ...body["budget"] };
17154
17245
  try {
17155
- const configPath = path25__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
17156
- const existing = fs23__default.default.existsSync(configPath) ? JSON.parse(fs23__default.default.readFileSync(configPath, "utf-8")) : {};
17246
+ const configPath = path26__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
17247
+ const existing = fs24__default.default.existsSync(configPath) ? JSON.parse(fs24__default.default.readFileSync(configPath, "utf-8")) : {};
17157
17248
  const updated = { ...existing, tierLimits: this.config.tierLimits, budget: this.config.budget };
17158
17249
  const tmp = configPath + ".tmp";
17159
- fs23__default.default.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
17160
- fs23__default.default.renameSync(tmp, configPath);
17250
+ fs24__default.default.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
17251
+ fs24__default.default.renameSync(tmp, configPath);
17161
17252
  res.json({ ok: true });
17162
17253
  } catch (err) {
17163
17254
  res.status(500).json({ error: `Failed to save config: ${err instanceof Error ? err.message : String(err)}` });
@@ -17185,7 +17276,7 @@ ${prompt}`;
17185
17276
  this.app.get("/api/runtime", auth, (req, res) => {
17186
17277
  const scope = req.query["scope"] ?? "workspace";
17187
17278
  if (scope === "global") {
17188
- const globalDbPath = path25__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
17279
+ const globalDbPath = path26__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
17189
17280
  const globalStore = new MemoryStore(globalDbPath);
17190
17281
  try {
17191
17282
  res.json({
@@ -17420,6 +17511,48 @@ ${prompt}`;
17420
17511
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
17421
17512
  }
17422
17513
  });
17514
+ this.app.get("/api/knowledge", auth, (_req, res) => {
17515
+ try {
17516
+ const ws = new WorldStateDB(this.workspacePath);
17517
+ try {
17518
+ const facts = ws.getAllFacts();
17519
+ res.json({ total: facts.length, facts });
17520
+ } finally {
17521
+ ws.close();
17522
+ }
17523
+ } catch (err) {
17524
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
17525
+ }
17526
+ });
17527
+ this.app.delete("/api/knowledge/fact", auth, mutationLimiter, (req, res) => {
17528
+ const body = req.body;
17529
+ if (!body.entity || !body.relation) {
17530
+ res.status(400).json({ error: "entity and relation are required" });
17531
+ return;
17532
+ }
17533
+ try {
17534
+ const ws = new WorldStateDB(this.workspacePath);
17535
+ try {
17536
+ res.json({ ok: true, deleted: ws.deleteFact(body.entity, body.relation) });
17537
+ } finally {
17538
+ ws.close();
17539
+ }
17540
+ } catch (err) {
17541
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
17542
+ }
17543
+ });
17544
+ this.app.delete("/api/knowledge", auth, mutationLimiter, (_req, res) => {
17545
+ try {
17546
+ const ws = new WorldStateDB(this.workspacePath);
17547
+ try {
17548
+ res.json({ ok: true, deleted: ws.clearFacts() });
17549
+ } finally {
17550
+ ws.close();
17551
+ }
17552
+ } catch (err) {
17553
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
17554
+ }
17555
+ });
17423
17556
  this.app.get("/api/audit-chain", auth, async (req, res) => {
17424
17557
  try {
17425
17558
  const limit = Math.min(500, Math.max(1, parseInt(req.query["limit"] || "200", 10) || 200));
@@ -17438,13 +17571,13 @@ ${prompt}`;
17438
17571
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
17439
17572
  }
17440
17573
  });
17441
- const prodPath = path25__default.default.resolve(__dirname$1, "../web/dist");
17442
- const devPath = path25__default.default.resolve(__dirname$1, "../../web/dist");
17443
- const webDistPath = fs23__default.default.existsSync(prodPath) ? prodPath : devPath;
17444
- if (fs23__default.default.existsSync(webDistPath)) {
17574
+ const prodPath = path26__default.default.resolve(__dirname$1, "../web/dist");
17575
+ const devPath = path26__default.default.resolve(__dirname$1, "../../web/dist");
17576
+ const webDistPath = fs24__default.default.existsSync(prodPath) ? prodPath : devPath;
17577
+ if (fs24__default.default.existsSync(webDistPath)) {
17445
17578
  this.app.use(express__default.default.static(webDistPath));
17446
17579
  this.app.get("*", (_req, res) => {
17447
- res.sendFile(path25__default.default.join(webDistPath, "index.html"));
17580
+ res.sendFile(path26__default.default.join(webDistPath, "index.html"));
17448
17581
  });
17449
17582
  } else {
17450
17583
  this.app.get("/", (_req, res) => {
@@ -17465,7 +17598,7 @@ init_constants();
17465
17598
  async function dashboardCommand(config, workspacePath = process.cwd()) {
17466
17599
  const port = config.dashboard.port ?? DEFAULT_DASHBOARD_PORT;
17467
17600
  const spin = ora__default.default({ text: `Starting dashboard on port ${port}\u2026`, color: "magenta" }).start();
17468
- const store = new MemoryStore(path25__default.default.join(workspacePath, CASCADE_DB_FILE));
17601
+ const store = new MemoryStore(path26__default.default.join(workspacePath, CASCADE_DB_FILE));
17469
17602
  const server = new DashboardServer(config, store, workspacePath);
17470
17603
  server.watchRuntimeChanges();
17471
17604
  const gThis = globalThis;
@@ -17497,7 +17630,7 @@ init_constants();
17497
17630
  function makeIdentityCommand(workspacePath = process.cwd()) {
17498
17631
  const identity = new commander.Command("identity").alias("id").description("Manage Cascade identities");
17499
17632
  identity.command("list").description("List all available identities").action(() => {
17500
- const store = new MemoryStore(path25__default.default.join(workspacePath, CASCADE_DB_FILE));
17633
+ const store = new MemoryStore(path26__default.default.join(workspacePath, CASCADE_DB_FILE));
17501
17634
  try {
17502
17635
  const identities = store.listIdentities();
17503
17636
  console.log(chalk9__default.default.bold("\n Identities:"));
@@ -17517,7 +17650,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
17517
17650
  }
17518
17651
  });
17519
17652
  identity.command("create <name>").description("Create a new identity").option("-d, --desc <text>", "Description of the identity").option("-s, --system <text>", "System prompt").option("--default", "Set as default identity").action((name, options) => {
17520
- const store = new MemoryStore(path25__default.default.join(workspacePath, CASCADE_DB_FILE));
17653
+ const store = new MemoryStore(path26__default.default.join(workspacePath, CASCADE_DB_FILE));
17521
17654
  try {
17522
17655
  if (options.default) {
17523
17656
  const existingDefault = store.getDefaultIdentity();
@@ -17543,7 +17676,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
17543
17676
  }
17544
17677
  });
17545
17678
  identity.command("set-default <name>").description("Set an identity as default by name or ID").action((query) => {
17546
- const store = new MemoryStore(path25__default.default.join(workspacePath, CASCADE_DB_FILE));
17679
+ const store = new MemoryStore(path26__default.default.join(workspacePath, CASCADE_DB_FILE));
17547
17680
  try {
17548
17681
  const identities = store.listIdentities();
17549
17682
  const match = identities.find((i) => i.id === query || i.name.toLowerCase() === query.toLowerCase());
@@ -17671,11 +17804,11 @@ async function exportCommand(options = {}) {
17671
17804
  let store;
17672
17805
  try {
17673
17806
  const workspacePath = options.workspacePath ?? process.cwd();
17674
- const workspaceDbPath = path25__default.default.join(workspacePath, CASCADE_DB_FILE);
17675
- const globalDbPath = path25__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE);
17807
+ const workspaceDbPath = path26__default.default.join(workspacePath, CASCADE_DB_FILE);
17808
+ const globalDbPath = path26__default.default.join(os8__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE);
17676
17809
  let dbPath = globalDbPath;
17677
17810
  try {
17678
- await fs9__default.default.access(workspaceDbPath);
17811
+ await fs10__default.default.access(workspaceDbPath);
17679
17812
  dbPath = workspaceDbPath;
17680
17813
  } catch {
17681
17814
  }
@@ -17714,8 +17847,8 @@ async function exportCommand(options = {}) {
17714
17847
  const ext = format === "json" ? ".json" : ".md";
17715
17848
  const safeName = session.title.replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").slice(0, 60);
17716
17849
  const defaultFile = `cascade-export-${safeName}${ext}`;
17717
- const outPath = options.output ? path25__default.default.resolve(options.output) : path25__default.default.join(process.cwd(), defaultFile);
17718
- await fs9__default.default.writeFile(outPath, content, "utf-8");
17850
+ const outPath = options.output ? path26__default.default.resolve(options.output) : path26__default.default.join(process.cwd(), defaultFile);
17851
+ await fs10__default.default.writeFile(outPath, content, "utf-8");
17719
17852
  spin.succeed(chalk9__default.default.green(`Exported to ${chalk9__default.default.white(outPath)}`));
17720
17853
  const messageCount = Array.isArray(session.messages) ? session.messages.length : 0;
17721
17854
  console.log();
@@ -17942,11 +18075,11 @@ async function statsCommand() {
17942
18075
  dotenv__default.default.config();
17943
18076
  function warnIfBuildIsStale() {
17944
18077
  try {
17945
- const here = path25__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))));
18078
+ const here = path26__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('cli.cjs', document.baseURI).href))));
17946
18079
  for (const rel of ["..", "../.."]) {
17947
- const pkgPath = path25__default.default.join(here, rel, "package.json");
17948
- if (!fs23__default.default.existsSync(pkgPath)) continue;
17949
- const pkg = JSON.parse(fs23__default.default.readFileSync(pkgPath, "utf-8"));
18080
+ const pkgPath = path26__default.default.join(here, rel, "package.json");
18081
+ if (!fs24__default.default.existsSync(pkgPath)) continue;
18082
+ const pkg = JSON.parse(fs24__default.default.readFileSync(pkgPath, "utf-8"));
17950
18083
  if (pkg.name !== "cascade-ai") continue;
17951
18084
  if (pkg.version && pkg.version !== CASCADE_VERSION) {
17952
18085
  console.error(