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.js CHANGED
@@ -7,8 +7,8 @@ import https from 'https';
7
7
  import zlib from 'zlib';
8
8
  import { Readable } from 'stream';
9
9
  import Database2 from 'better-sqlite3';
10
- import path25 from 'path';
11
- import fs23 from 'fs';
10
+ import path26 from 'path';
11
+ import fs24 from 'fs';
12
12
  import os8 from 'os';
13
13
  import crypto, { randomUUID, timingSafeEqual } from 'crypto';
14
14
  import { render, Box, Text, useStdout, useApp, useInput, Static } from 'ink';
@@ -17,7 +17,7 @@ import React2, { useState, useReducer, useRef, useCallback, useEffect, useMemo }
17
17
  import chalk9 from 'chalk';
18
18
  import dotenv from 'dotenv';
19
19
  import { fileURLToPath } from 'url';
20
- import fs9 from 'fs/promises';
20
+ import fs10 from 'fs/promises';
21
21
  import * as _ignoreModule from 'ignore';
22
22
  import _ignoreModule__default from 'ignore';
23
23
  import { z } from 'zod';
@@ -56,16 +56,17 @@ var __export = (target, all) => {
56
56
  };
57
57
 
58
58
  // src/constants.ts
59
- 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;
59
+ 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;
60
60
  var init_constants = __esm({
61
61
  "src/constants.ts"() {
62
- CASCADE_VERSION = "0.17.0";
62
+ CASCADE_VERSION = "0.18.0";
63
63
  CASCADE_CONFIG_FILE = ".cascade/config.json";
64
64
  CASCADE_DB_FILE = ".cascade/memory.db";
65
65
  CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
66
66
  GLOBAL_CONFIG_DIR = ".cascade-ai";
67
67
  GLOBAL_DB_FILE = "memory.db";
68
68
  GLOBAL_KEYSTORE_FILE = "keystore.enc";
69
+ GLOBAL_CREDENTIALS_FILE = "credentials.json";
69
70
  GLOBAL_RUNTIME_DB_FILE = "runtime.db";
70
71
  DEFAULT_DASHBOARD_PORT = 4891;
71
72
  DEFAULT_CONTEXT_LIMIT = 2e5;
@@ -1580,12 +1581,12 @@ var init_audit_logger = __esm({
1580
1581
  constructor(workspacePath, debugMode = false) {
1581
1582
  this.workspacePath = workspacePath;
1582
1583
  this.debugMode = debugMode;
1583
- const cascadeDir = path25.join(workspacePath, ".cascade");
1584
- if (!fs23.existsSync(cascadeDir)) {
1585
- fs23.mkdirSync(cascadeDir, { recursive: true });
1584
+ const cascadeDir = path26.join(workspacePath, ".cascade");
1585
+ if (!fs24.existsSync(cascadeDir)) {
1586
+ fs24.mkdirSync(cascadeDir, { recursive: true });
1586
1587
  }
1587
- this.keyPath = path25.join(cascadeDir, "audit_log.key");
1588
- this.dbPath = path25.join(cascadeDir, "audit_log.db");
1588
+ this.keyPath = path26.join(cascadeDir, "audit_log.key");
1589
+ this.dbPath = path26.join(cascadeDir, "audit_log.db");
1589
1590
  this.initEncryptionKey();
1590
1591
  this.db = new Database2(this.dbPath);
1591
1592
  this.db.pragma("journal_mode = WAL");
@@ -1615,11 +1616,11 @@ var init_audit_logger = __esm({
1615
1616
  return crypto.createHash("sha256").update(`${prevHash}|${timestamp}|${eventType}|${tierId}|${encryptedPayload}`).digest("hex");
1616
1617
  }
1617
1618
  initEncryptionKey() {
1618
- if (fs23.existsSync(this.keyPath)) {
1619
- this.encryptionKey = fs23.readFileSync(this.keyPath);
1619
+ if (fs24.existsSync(this.keyPath)) {
1620
+ this.encryptionKey = fs24.readFileSync(this.keyPath);
1620
1621
  } else {
1621
1622
  this.encryptionKey = crypto.randomBytes(32);
1622
- fs23.writeFileSync(this.keyPath, this.encryptionKey);
1623
+ fs24.writeFileSync(this.keyPath, this.encryptionKey);
1623
1624
  }
1624
1625
  }
1625
1626
  encrypt(text) {
@@ -1697,9 +1698,9 @@ var init_audit_logger = __esm({
1697
1698
  dumpDebugIfNeeded() {
1698
1699
  if (!this.debugMode) return;
1699
1700
  try {
1700
- const dumpPath = path25.join(os8.tmpdir(), "cascade_audit_logs_debug.json");
1701
+ const dumpPath = path26.join(os8.tmpdir(), "cascade_audit_logs_debug.json");
1701
1702
  const entries = this.getAllLogs();
1702
- fs23.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
1703
+ fs24.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
1703
1704
  } catch (err) {
1704
1705
  console.error("Failed to dump debug audit logs", err);
1705
1706
  }
@@ -1754,7 +1755,7 @@ var Keystore = class {
1754
1755
  const creds = await this.keytar.findCredentials(KEYTAR_SERVICE);
1755
1756
  this.cache = Object.fromEntries(creds.map((c) => [c.account, c.password]));
1756
1757
  this.backend = "keytar";
1757
- if (password && fs23.existsSync(this.storePath)) {
1758
+ if (password && fs24.existsSync(this.storePath)) {
1758
1759
  try {
1759
1760
  const fileEntries = this.decryptFile(password);
1760
1761
  for (const [k, v] of Object.entries(fileEntries)) {
@@ -1773,7 +1774,7 @@ var Keystore = class {
1773
1774
  "Keystore unlock requires a password because the OS keychain (keytar) is not available on this system."
1774
1775
  );
1775
1776
  }
1776
- if (!fs23.existsSync(this.storePath)) {
1777
+ if (!fs24.existsSync(this.storePath)) {
1777
1778
  const salt = crypto.randomBytes(SALT_LEN);
1778
1779
  this.masterKey = this.deriveKey(password, salt);
1779
1780
  this.writeWithSalt({}, salt);
@@ -1787,7 +1788,7 @@ var Keystore = class {
1787
1788
  }
1788
1789
  /** Synchronous legacy unlock kept for AES-only environments. */
1789
1790
  unlockSync(password) {
1790
- if (!fs23.existsSync(this.storePath)) {
1791
+ if (!fs24.existsSync(this.storePath)) {
1791
1792
  const salt = crypto.randomBytes(SALT_LEN);
1792
1793
  this.masterKey = this.deriveKey(password, salt);
1793
1794
  this.writeWithSalt({}, salt);
@@ -1845,7 +1846,7 @@ var Keystore = class {
1845
1846
  }
1846
1847
  }
1847
1848
  decryptFile(password, knownSalt) {
1848
- if (!fs23.existsSync(this.storePath)) return {};
1849
+ if (!fs24.existsSync(this.storePath)) return {};
1849
1850
  try {
1850
1851
  const { salt, ciphertext, iv, tag } = this.readRaw();
1851
1852
  const useSalt = knownSalt ?? salt;
@@ -1867,8 +1868,8 @@ var Keystore = class {
1867
1868
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
1868
1869
  const tag = cipher.getAuthTag();
1869
1870
  const out = Buffer.concat([raw.salt, iv, tag, ciphertext]);
1870
- fs23.mkdirSync(path25.dirname(this.storePath), { recursive: true });
1871
- fs23.writeFileSync(this.storePath, out, { mode: 384 });
1871
+ fs24.mkdirSync(path26.dirname(this.storePath), { recursive: true });
1872
+ fs24.writeFileSync(this.storePath, out, { mode: 384 });
1872
1873
  }
1873
1874
  writeWithSalt(data, salt) {
1874
1875
  if (!this.masterKey) throw new Error("writeWithSalt called before masterKey was set");
@@ -1878,11 +1879,11 @@ var Keystore = class {
1878
1879
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
1879
1880
  const tag = cipher.getAuthTag();
1880
1881
  const out = Buffer.concat([salt, iv, tag, ciphertext]);
1881
- fs23.mkdirSync(path25.dirname(this.storePath), { recursive: true });
1882
- fs23.writeFileSync(this.storePath, out, { mode: 384 });
1882
+ fs24.mkdirSync(path26.dirname(this.storePath), { recursive: true });
1883
+ fs24.writeFileSync(this.storePath, out, { mode: 384 });
1883
1884
  }
1884
1885
  readRaw() {
1885
- const buf = fs23.readFileSync(this.storePath);
1886
+ const buf = fs24.readFileSync(this.storePath);
1886
1887
  let offset = 0;
1887
1888
  const salt = buf.subarray(offset, offset + SALT_LEN);
1888
1889
  offset += SALT_LEN;
@@ -1915,9 +1916,9 @@ var CascadeIgnore = class {
1915
1916
  ]);
1916
1917
  }
1917
1918
  async load(workspacePath) {
1918
- const filePath = path25.join(workspacePath, ".cascadeignore");
1919
+ const filePath = path26.join(workspacePath, ".cascadeignore");
1919
1920
  try {
1920
- const content = await fs9.readFile(filePath, "utf-8");
1921
+ const content = await fs10.readFile(filePath, "utf-8");
1921
1922
  const lines = content.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
1922
1923
  this.ig.add(lines);
1923
1924
  this.loaded = true;
@@ -1926,7 +1927,7 @@ var CascadeIgnore = class {
1926
1927
  }
1927
1928
  isIgnored(filePath, workspacePath) {
1928
1929
  try {
1929
- const relative = workspacePath ? path25.relative(workspacePath, filePath) : filePath;
1930
+ const relative = workspacePath ? path26.relative(workspacePath, filePath) : filePath;
1930
1931
  return this.ig.ignores(relative);
1931
1932
  } catch {
1932
1933
  return false;
@@ -1937,7 +1938,7 @@ var CascadeIgnore = class {
1937
1938
  }
1938
1939
  };
1939
1940
  async function createDefaultIgnoreFile(workspacePath) {
1940
- const filePath = path25.join(workspacePath, ".cascadeignore");
1941
+ const filePath = path26.join(workspacePath, ".cascadeignore");
1941
1942
  const content = `# .cascadeignore \u2014 Files Cascade agents cannot read or modify
1942
1943
  # Syntax identical to .gitignore
1943
1944
 
@@ -1964,12 +1965,12 @@ build/
1964
1965
  .DS_Store
1965
1966
  Thumbs.db
1966
1967
  `;
1967
- await fs9.writeFile(filePath, content, "utf-8");
1968
+ await fs10.writeFile(filePath, content, "utf-8");
1968
1969
  }
1969
1970
  async function loadCascadeMd(workspacePath) {
1970
- const filePath = path25.join(workspacePath, "CASCADE.md");
1971
+ const filePath = path26.join(workspacePath, "CASCADE.md");
1971
1972
  try {
1972
- const raw = await fs9.readFile(filePath, "utf-8");
1973
+ const raw = await fs10.readFile(filePath, "utf-8");
1973
1974
  return parseCascadeMd(raw);
1974
1975
  } catch {
1975
1976
  return null;
@@ -1996,7 +1997,7 @@ ${raw.trim()}`;
1996
1997
  return { raw, sections, systemPrompt };
1997
1998
  }
1998
1999
  async function createDefaultCascadeMd(workspacePath) {
1999
- const filePath = path25.join(workspacePath, "CASCADE.md");
2000
+ const filePath = path26.join(workspacePath, "CASCADE.md");
2000
2001
  const content = `# Cascade Project Instructions
2001
2002
 
2002
2003
  This file contains project-specific instructions for the Cascade AI agent.
@@ -2026,12 +2027,12 @@ All tools are allowed unless specified otherwise.
2026
2027
 
2027
2028
  List any areas the agent should not touch.
2028
2029
  `;
2029
- await fs9.writeFile(filePath, content, "utf-8");
2030
+ await fs10.writeFile(filePath, content, "utf-8");
2030
2031
  }
2031
2032
  var MemoryStore = class _MemoryStore {
2032
2033
  db;
2033
2034
  constructor(dbPath) {
2034
- fs23.mkdirSync(path25.dirname(dbPath), { recursive: true });
2035
+ fs24.mkdirSync(path26.dirname(dbPath), { recursive: true });
2035
2036
  try {
2036
2037
  this.db = new Database2(dbPath, { timeout: 5e3 });
2037
2038
  this.db.pragma("journal_mode = WAL");
@@ -3178,6 +3179,63 @@ function validateConfig(raw) {
3178
3179
  return result.data;
3179
3180
  }
3180
3181
 
3182
+ // src/config/global-credentials.ts
3183
+ init_constants();
3184
+ function credentialsPath(globalDir) {
3185
+ return path26.join(globalDir, GLOBAL_CREDENTIALS_FILE);
3186
+ }
3187
+ function providerKey(p) {
3188
+ if (p.type === "azure") {
3189
+ return `azure:${p.deploymentName ?? p.baseUrl ?? p.label ?? ""}`;
3190
+ }
3191
+ return p.type;
3192
+ }
3193
+ function isPersistable(p) {
3194
+ return Boolean(p.apiKey || p.authToken || p.type === "azure" || p.baseUrl);
3195
+ }
3196
+ function loadGlobalCredentials(globalDir) {
3197
+ try {
3198
+ const raw = fs24.readFileSync(credentialsPath(globalDir), "utf-8");
3199
+ const parsed = JSON.parse(raw);
3200
+ if (!Array.isArray(parsed.providers)) return [];
3201
+ return parsed.providers.filter(
3202
+ (p) => Boolean(p) && typeof p.type === "string"
3203
+ );
3204
+ } catch {
3205
+ return [];
3206
+ }
3207
+ }
3208
+ function saveGlobalCredentials(globalDir, providers) {
3209
+ const filePath = credentialsPath(globalDir);
3210
+ fs24.mkdirSync(globalDir, { recursive: true, mode: 448 });
3211
+ const body = { version: 1, providers: providers.filter(isPersistable) };
3212
+ const tmp = `${filePath}.tmp`;
3213
+ fs24.writeFileSync(tmp, JSON.stringify(body, null, 2), { encoding: "utf-8", mode: 384 });
3214
+ fs24.renameSync(tmp, filePath);
3215
+ try {
3216
+ fs24.chmodSync(filePath, 384);
3217
+ } catch {
3218
+ }
3219
+ }
3220
+ function mergeGlobalCredentials(workspaceProviders, globalProviders) {
3221
+ const merged = [...workspaceProviders];
3222
+ const byKey = new Map(merged.map((p) => [providerKey(p), p]));
3223
+ for (const g of globalProviders) {
3224
+ const existing = byKey.get(providerKey(g));
3225
+ if (!existing) {
3226
+ merged.push({ ...g });
3227
+ byKey.set(providerKey(g), merged[merged.length - 1]);
3228
+ continue;
3229
+ }
3230
+ if (!existing.apiKey && g.apiKey) existing.apiKey = g.apiKey;
3231
+ if (!existing.authToken && g.authToken) existing.authToken = g.authToken;
3232
+ if (!existing.baseUrl && g.baseUrl) existing.baseUrl = g.baseUrl;
3233
+ if (!existing.apiVersion && g.apiVersion) existing.apiVersion = g.apiVersion;
3234
+ if (!existing.label && g.label) existing.label = g.label;
3235
+ }
3236
+ return merged;
3237
+ }
3238
+
3181
3239
  // src/config/index.ts
3182
3240
  init_constants();
3183
3241
  var KEY_OPTIONAL_PROVIDER_TYPES = /* @__PURE__ */ new Set(["ollama", "openai-compatible"]);
@@ -3193,18 +3251,20 @@ var ConfigManager = class {
3193
3251
  cascadeMd = null;
3194
3252
  workspacePath;
3195
3253
  globalDir;
3196
- constructor(workspacePath = process.cwd()) {
3254
+ /** `globalDirOverride` exists for tests — never point it at the real home dir there. */
3255
+ constructor(workspacePath = process.cwd(), globalDirOverride) {
3197
3256
  this.workspacePath = workspacePath;
3198
- this.globalDir = path25.join(os8.homedir(), GLOBAL_CONFIG_DIR);
3257
+ this.globalDir = globalDirOverride ?? path26.join(os8.homedir(), GLOBAL_CONFIG_DIR);
3199
3258
  }
3200
3259
  async load() {
3201
3260
  this.config = await this.loadConfig();
3202
3261
  this.ignore = new CascadeIgnore();
3203
3262
  await this.ignore.load(this.workspacePath);
3204
3263
  this.cascadeMd = await loadCascadeMd(this.workspacePath);
3205
- this.keystore = new Keystore(path25.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
3206
- this.store = new MemoryStore(path25.join(this.workspacePath, CASCADE_DB_FILE));
3264
+ this.keystore = new Keystore(path26.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
3265
+ this.store = new MemoryStore(path26.join(this.workspacePath, CASCADE_DB_FILE));
3207
3266
  await this.injectEnvKeys();
3267
+ this.config.providers = mergeGlobalCredentials(this.config.providers, loadGlobalCredentials(this.globalDir));
3208
3268
  await this.ensureDefaultIdentity();
3209
3269
  }
3210
3270
  getConfig() {
@@ -3226,9 +3286,14 @@ var ConfigManager = class {
3226
3286
  return this.workspacePath;
3227
3287
  }
3228
3288
  async save() {
3229
- const configPath = path25.join(this.workspacePath, CASCADE_CONFIG_FILE);
3230
- await fs9.mkdir(path25.dirname(configPath), { recursive: true });
3231
- await fs9.writeFile(configPath, JSON.stringify(this.config, null, 2), "utf-8");
3289
+ const configPath = path26.join(this.workspacePath, CASCADE_CONFIG_FILE);
3290
+ await fs10.mkdir(path26.dirname(configPath), { recursive: true });
3291
+ await fs10.writeFile(configPath, JSON.stringify(this.config, null, 2), "utf-8");
3292
+ try {
3293
+ saveGlobalCredentials(this.globalDir, this.config.providers);
3294
+ } catch (err) {
3295
+ console.warn(`Failed to sync credentials to global store: ${err instanceof Error ? err.message : String(err)}`);
3296
+ }
3232
3297
  }
3233
3298
  async updateConfig(updates) {
3234
3299
  this.config = validateConfig({ ...this.config, ...updates });
@@ -3251,9 +3316,9 @@ var ConfigManager = class {
3251
3316
  return configProvider?.apiKey;
3252
3317
  }
3253
3318
  async loadConfig() {
3254
- const configPath = path25.join(this.workspacePath, CASCADE_CONFIG_FILE);
3319
+ const configPath = path26.join(this.workspacePath, CASCADE_CONFIG_FILE);
3255
3320
  try {
3256
- const raw = await fs9.readFile(configPath, "utf-8");
3321
+ const raw = await fs10.readFile(configPath, "utf-8");
3257
3322
  return validateConfig(JSON.parse(raw));
3258
3323
  } catch (err) {
3259
3324
  if (err.code === "ENOENT") {
@@ -4130,7 +4195,7 @@ init_constants();
4130
4195
  var DEFAULT_SNAPSHOT_URL = "https://raw.githubusercontent.com/Varun-SV/Cascade-AI/main/src/core/router/benchmark-data.json";
4131
4196
  var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
4132
4197
  var FETCH_TIMEOUT_MS = 8e3;
4133
- var DEFAULT_CACHE_FILE = path25.join(os8.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
4198
+ var DEFAULT_CACHE_FILE = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
4134
4199
  function normalizeModelId(id) {
4135
4200
  let s = id.toLowerCase();
4136
4201
  const slash = s.lastIndexOf("/");
@@ -4163,7 +4228,7 @@ var LiveDataProvider = class {
4163
4228
  if (this.loaded) return;
4164
4229
  this.loaded = true;
4165
4230
  try {
4166
- const raw = await fs9.readFile(this.opts.cacheFile, "utf-8");
4231
+ const raw = await fs10.readFile(this.opts.cacheFile, "utf-8");
4167
4232
  const cache = JSON.parse(raw);
4168
4233
  if (cache.snapshot?.families) {
4169
4234
  this.snapshot = cache.snapshot;
@@ -4275,14 +4340,14 @@ var LiveDataProvider = class {
4275
4340
  }
4276
4341
  async saveCache() {
4277
4342
  try {
4278
- await fs9.mkdir(path25.dirname(this.opts.cacheFile), { recursive: true });
4343
+ await fs10.mkdir(path26.dirname(this.opts.cacheFile), { recursive: true });
4279
4344
  const cache = {
4280
4345
  fetchedAt: this.fetchedAt,
4281
4346
  snapshot: this.snapshot ?? void 0,
4282
4347
  prices: Object.fromEntries(this.prices),
4283
4348
  capabilities: Object.fromEntries(this.capabilities)
4284
4349
  };
4285
- await fs9.writeFile(this.opts.cacheFile, JSON.stringify(cache, null, 2), "utf-8");
4350
+ await fs10.writeFile(this.opts.cacheFile, JSON.stringify(cache, null, 2), "utf-8");
4286
4351
  } catch {
4287
4352
  }
4288
4353
  }
@@ -6179,8 +6244,8 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : "") + textToolSuffix,
6179
6244
  tierId: this.id,
6180
6245
  sessionId: this.taskId,
6181
6246
  requireApproval: false,
6182
- saveSnapshot: async (path30, content) => {
6183
- this.store?.addFileSnapshot(this.taskId, path30, content);
6247
+ saveSnapshot: async (path31, content) => {
6248
+ this.store?.addFileSnapshot(this.taskId, path31, content);
6184
6249
  },
6185
6250
  sendPeerSync: (to, syncType, content) => {
6186
6251
  this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
@@ -6350,9 +6415,9 @@ ${assignment.expectedOutput}`;
6350
6415
  const { promisify: promisify4 } = await import('util');
6351
6416
  const execAsync3 = promisify4(exec3);
6352
6417
  for (const artifactPath of artifactPaths) {
6353
- const absolutePath = path25.resolve(process.cwd(), artifactPath);
6418
+ const absolutePath = path26.resolve(process.cwd(), artifactPath);
6354
6419
  try {
6355
- const stat = await fs9.stat(absolutePath);
6420
+ const stat = await fs10.stat(absolutePath);
6356
6421
  if (!stat.isFile()) {
6357
6422
  issues.push(`Expected artifact is not a file: ${artifactPath}`);
6358
6423
  continue;
@@ -6362,7 +6427,7 @@ ${assignment.expectedOutput}`;
6362
6427
  continue;
6363
6428
  }
6364
6429
  if (!/\.pdf$/i.test(artifactPath)) {
6365
- const content = await fs9.readFile(absolutePath, "utf-8");
6430
+ const content = await fs10.readFile(absolutePath, "utf-8");
6366
6431
  if (!content.trim()) {
6367
6432
  issues.push(`Artifact content is empty: ${artifactPath}`);
6368
6433
  continue;
@@ -6371,7 +6436,7 @@ ${assignment.expectedOutput}`;
6371
6436
  issues.push(`PDF artifact looks too small to be valid: ${artifactPath}`);
6372
6437
  continue;
6373
6438
  }
6374
- const ext = path25.extname(absolutePath).toLowerCase();
6439
+ const ext = path26.extname(absolutePath).toLowerCase();
6375
6440
  try {
6376
6441
  if (ext === ".ts" || ext === ".tsx") {
6377
6442
  await execAsync3(`npx tsc --noEmit ${absolutePath}`, { timeout: 1e4 });
@@ -8462,16 +8527,16 @@ function resolveInWorkspace(workspaceRoot, input) {
8462
8527
  if (typeof input !== "string" || input.length === 0) {
8463
8528
  throw new WorkspaceSandboxError(String(input), workspaceRoot);
8464
8529
  }
8465
- const root = path25.resolve(workspaceRoot);
8466
- const abs = path25.isAbsolute(input) ? path25.resolve(input) : path25.resolve(root, input);
8467
- const rel = path25.relative(root, abs);
8468
- if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path25.isAbsolute(rel)) {
8530
+ const root = path26.resolve(workspaceRoot);
8531
+ const abs = path26.isAbsolute(input) ? path26.resolve(input) : path26.resolve(root, input);
8532
+ const rel = path26.relative(root, abs);
8533
+ if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path26.isAbsolute(rel)) {
8469
8534
  throw new WorkspaceSandboxError(input, root);
8470
8535
  }
8471
8536
  try {
8472
- const real = fs23.realpathSync(abs);
8473
- const realRel = path25.relative(root, real);
8474
- if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path25.isAbsolute(realRel))) {
8537
+ const real = fs24.realpathSync(abs);
8538
+ const realRel = path26.relative(root, real);
8539
+ if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path26.isAbsolute(realRel))) {
8475
8540
  throw new WorkspaceSandboxError(input, root);
8476
8541
  }
8477
8542
  } catch (e) {
@@ -8498,7 +8563,7 @@ var FileReadTool = class extends BaseTool {
8498
8563
  const absPath = resolveInWorkspace(this.workspaceRoot, filePath);
8499
8564
  const offset = input["offset"] ?? 1;
8500
8565
  const limit = input["limit"];
8501
- const content = await fs9.readFile(absPath, "utf-8");
8566
+ const content = await fs10.readFile(absPath, "utf-8");
8502
8567
  const lines = content.split("\n");
8503
8568
  const start = Math.max(0, offset - 1);
8504
8569
  const end = limit ? start + limit : lines.length;
@@ -8527,13 +8592,13 @@ var FileWriteTool = class extends BaseTool {
8527
8592
  const content = input["content"];
8528
8593
  if (options.saveSnapshot) {
8529
8594
  try {
8530
- const oldContent = await fs9.readFile(absPath, "utf-8");
8595
+ const oldContent = await fs10.readFile(absPath, "utf-8");
8531
8596
  await options.saveSnapshot(absPath, oldContent);
8532
8597
  } catch {
8533
8598
  }
8534
8599
  }
8535
- await fs9.mkdir(path25.dirname(absPath), { recursive: true });
8536
- await fs9.writeFile(absPath, content, "utf-8");
8600
+ await fs10.mkdir(path26.dirname(absPath), { recursive: true });
8601
+ await fs10.writeFile(absPath, content, "utf-8");
8537
8602
  return `Written ${content.length} characters to ${filePath}`;
8538
8603
  }
8539
8604
  };
@@ -8559,7 +8624,7 @@ var FileEditTool = class extends BaseTool {
8559
8624
  const oldString = input["old_string"];
8560
8625
  const newString = input["new_string"];
8561
8626
  const replaceAll = input["replace_all"] ?? false;
8562
- const rawContent = await fs9.readFile(absPath, "utf-8");
8627
+ const rawContent = await fs10.readFile(absPath, "utf-8");
8563
8628
  if (options.saveSnapshot) {
8564
8629
  await options.saveSnapshot(absPath, rawContent);
8565
8630
  }
@@ -8571,7 +8636,7 @@ var FileEditTool = class extends BaseTool {
8571
8636
  );
8572
8637
  }
8573
8638
  const updated = replaceAll ? content.split(normalizedOld).join(newString) : content.replace(normalizedOld, newString);
8574
- await fs9.writeFile(absPath, updated, "utf-8");
8639
+ await fs10.writeFile(absPath, updated, "utf-8");
8575
8640
  const count = replaceAll ? content.split(normalizedOld).length - 1 : 1;
8576
8641
  return `Replaced ${count} occurrence(s) in ${filePath}`;
8577
8642
  }
@@ -8594,12 +8659,12 @@ var FileDeleteTool = class extends BaseTool {
8594
8659
  const absPath = resolveInWorkspace(this.workspaceRoot, filePath);
8595
8660
  if (options.saveSnapshot) {
8596
8661
  try {
8597
- const oldContent = await fs9.readFile(absPath, "utf-8");
8662
+ const oldContent = await fs10.readFile(absPath, "utf-8");
8598
8663
  await options.saveSnapshot(absPath, oldContent);
8599
8664
  } catch {
8600
8665
  }
8601
8666
  }
8602
- await fs9.rm(absPath, { recursive: false });
8667
+ await fs10.rm(absPath, { recursive: false });
8603
8668
  return `Deleted ${filePath}`;
8604
8669
  }
8605
8670
  };
@@ -8616,7 +8681,7 @@ var FileListTool = class extends BaseTool {
8616
8681
  async execute(input, _options) {
8617
8682
  const inputPath = input["path"] || ".";
8618
8683
  const absPath = resolveInWorkspace(this.workspaceRoot, inputPath);
8619
- const entries = await fs9.readdir(absPath, { withFileTypes: true });
8684
+ const entries = await fs10.readdir(absPath, { withFileTypes: true });
8620
8685
  return entries.map((e) => `${e.isDirectory() ? "[DIR] " : " "}${e.name}`).join("\n") || "(empty directory)";
8621
8686
  }
8622
8687
  };
@@ -9029,8 +9094,8 @@ var ImageAnalyzeTool = class extends BaseTool {
9029
9094
  }
9030
9095
  };
9031
9096
  async function fileToImageAttachment(filePath) {
9032
- const data = await fs9.readFile(filePath);
9033
- const ext = path25.extname(filePath).toLowerCase();
9097
+ const data = await fs10.readFile(filePath);
9098
+ const ext = path26.extname(filePath).toLowerCase();
9034
9099
  const mimeMap = {
9035
9100
  ".jpg": "image/jpeg",
9036
9101
  ".jpeg": "image/jpeg",
@@ -9064,14 +9129,14 @@ var PDFCreateTool = class extends BaseTool {
9064
9129
  const filePath = input["path"];
9065
9130
  const content = input["content"];
9066
9131
  const title = input["title"];
9067
- const dir = path25.dirname(filePath);
9068
- if (!fs23.existsSync(dir)) {
9069
- fs23.mkdirSync(dir, { recursive: true });
9132
+ const dir = path26.dirname(filePath);
9133
+ if (!fs24.existsSync(dir)) {
9134
+ fs24.mkdirSync(dir, { recursive: true });
9070
9135
  }
9071
9136
  return new Promise((resolve, reject) => {
9072
9137
  try {
9073
9138
  const doc = new PDFDocument({ margin: 50 });
9074
- const stream = fs23.createWriteStream(filePath);
9139
+ const stream = fs24.createWriteStream(filePath);
9075
9140
  doc.pipe(stream);
9076
9141
  if (title) {
9077
9142
  doc.info["Title"] = title;
@@ -9149,22 +9214,22 @@ var CodeInterpreterTool = class extends BaseTool {
9149
9214
  }
9150
9215
  cmdPrefix = NODE_CMD;
9151
9216
  }
9152
- const tmpDir = path25.join(this.workspaceRoot, ".cascade", "tmp");
9153
- if (!fs23.existsSync(tmpDir)) {
9154
- fs23.mkdirSync(tmpDir, { recursive: true });
9217
+ const tmpDir = path26.join(this.workspaceRoot, ".cascade", "tmp");
9218
+ if (!fs24.existsSync(tmpDir)) {
9219
+ fs24.mkdirSync(tmpDir, { recursive: true });
9155
9220
  }
9156
9221
  const extension = language === "python" ? "py" : "js";
9157
9222
  const fileName = `intp_${randomUUID().slice(0, 8)}.${extension}`;
9158
- const filePath = path25.join(tmpDir, fileName);
9159
- fs23.writeFileSync(filePath, code, "utf-8");
9223
+ const filePath = path26.join(tmpDir, fileName);
9224
+ fs24.writeFileSync(filePath, code, "utf-8");
9160
9225
  const execArgs = [filePath, ...args];
9161
9226
  return new Promise((resolve) => {
9162
9227
  const startMs = Date.now();
9163
9228
  execFile(cmdPrefix, execArgs, { cwd: this.workspaceRoot, timeout: 3e4 }, (error, stdout, stderr) => {
9164
9229
  const duration = Date.now() - startMs;
9165
9230
  try {
9166
- if (fs23.existsSync(filePath)) {
9167
- fs23.unlinkSync(filePath);
9231
+ if (fs24.existsSync(filePath)) {
9232
+ fs24.unlinkSync(filePath);
9168
9233
  }
9169
9234
  } catch (cleanupErr) {
9170
9235
  console.error(`Failed to cleanup interpreter script ${filePath}:`, cleanupErr);
@@ -9443,7 +9508,7 @@ var GlobTool = class extends BaseTool {
9443
9508
  };
9444
9509
  async execute(input, _options) {
9445
9510
  const pattern = input["pattern"];
9446
- const searchPath = input["path"] ? path25.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
9511
+ const searchPath = input["path"] ? path26.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
9447
9512
  const matches = await glob(pattern, {
9448
9513
  cwd: searchPath,
9449
9514
  ignore: ["node_modules/**", ".git/**", "dist/**", "build/**"],
@@ -9456,7 +9521,7 @@ var GlobTool = class extends BaseTool {
9456
9521
  const withMtime = await Promise.all(
9457
9522
  matches.map(async (rel) => {
9458
9523
  try {
9459
- const stat = await fs9.stat(path25.join(searchPath, rel));
9524
+ const stat = await fs10.stat(path26.join(searchPath, rel));
9460
9525
  return { rel, mtime: stat.mtimeMs };
9461
9526
  } catch {
9462
9527
  return { rel, mtime: 0 };
@@ -9505,7 +9570,7 @@ var GrepTool = class extends BaseTool {
9505
9570
  };
9506
9571
  async execute(input, _options) {
9507
9572
  const pattern = input["pattern"];
9508
- const searchPath = input["path"] ? path25.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
9573
+ const searchPath = input["path"] ? path26.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
9509
9574
  const globPattern = input["glob"];
9510
9575
  const outputMode = input["output_mode"] ?? "content";
9511
9576
  const context = input["context"] ?? 0;
@@ -9559,15 +9624,15 @@ var GrepTool = class extends BaseTool {
9559
9624
  nodir: true
9560
9625
  });
9561
9626
  } catch {
9562
- files = [path25.relative(searchPath, searchPath) || "."];
9627
+ files = [path26.relative(searchPath, searchPath) || "."];
9563
9628
  }
9564
9629
  const results = [];
9565
9630
  let totalCount = 0;
9566
9631
  for (const rel of files) {
9567
- const abs = path25.join(searchPath, rel);
9632
+ const abs = path26.join(searchPath, rel);
9568
9633
  let content;
9569
9634
  try {
9570
- content = await fs9.readFile(abs, "utf-8");
9635
+ content = await fs10.readFile(abs, "utf-8");
9571
9636
  } catch {
9572
9637
  continue;
9573
9638
  }
@@ -9926,10 +9991,10 @@ var ToolRegistry = class extends EventEmitter {
9926
9991
  }
9927
9992
  isIgnored(filePath) {
9928
9993
  if (!filePath) return false;
9929
- const abs = path25.resolve(this.workspaceRoot, filePath);
9930
- const rel = path25.relative(this.workspaceRoot, abs);
9931
- if (!rel || rel.startsWith("..") || path25.isAbsolute(rel)) return true;
9932
- const posixRel = rel.split(path25.sep).join("/");
9994
+ const abs = path26.resolve(this.workspaceRoot, filePath);
9995
+ const rel = path26.relative(this.workspaceRoot, abs);
9996
+ if (!rel || rel.startsWith("..") || path26.isAbsolute(rel)) return true;
9997
+ const posixRel = rel.split(path26.sep).join("/");
9933
9998
  return this.ignoreMatcher.ignores(posixRel);
9934
9999
  }
9935
10000
  };
@@ -10452,7 +10517,7 @@ var TaskAnalyzer = class {
10452
10517
  analysisCache.clear();
10453
10518
  }
10454
10519
  };
10455
- var DEFAULT_STATS_FILE = path25.join(os8.homedir(), ".cascade", "model-perf.json");
10520
+ var DEFAULT_STATS_FILE = path26.join(os8.homedir(), ".cascade", "model-perf.json");
10456
10521
  var ModelPerformanceTracker = class {
10457
10522
  stats = /* @__PURE__ */ new Map();
10458
10523
  featureStats = /* @__PURE__ */ new Map();
@@ -10465,7 +10530,7 @@ var ModelPerformanceTracker = class {
10465
10530
  if (this.loaded) return;
10466
10531
  this.loaded = true;
10467
10532
  try {
10468
- const raw = await fs9.readFile(this.statsFile, "utf-8");
10533
+ const raw = await fs10.readFile(this.statsFile, "utf-8");
10469
10534
  const parsed = JSON.parse(raw);
10470
10535
  if (parsed.models) {
10471
10536
  for (const [key, stat] of Object.entries(parsed.models)) this.stats.set(key, stat);
@@ -10484,12 +10549,12 @@ var ModelPerformanceTracker = class {
10484
10549
  }
10485
10550
  async save() {
10486
10551
  try {
10487
- await fs9.mkdir(path25.dirname(this.statsFile), { recursive: true });
10552
+ await fs10.mkdir(path26.dirname(this.statsFile), { recursive: true });
10488
10553
  const modelsObj = {};
10489
10554
  const featuresObj = {};
10490
10555
  for (const [key, stat] of this.stats) modelsObj[key] = stat;
10491
10556
  for (const [key, stat] of this.featureStats) featuresObj[key] = stat;
10492
- await fs9.writeFile(this.statsFile, JSON.stringify({ models: modelsObj, features: featuresObj }, null, 2), "utf-8");
10557
+ await fs10.writeFile(this.statsFile, JSON.stringify({ models: modelsObj, features: featuresObj }, null, 2), "utf-8");
10493
10558
  } catch {
10494
10559
  }
10495
10560
  }
@@ -11023,9 +11088,9 @@ Required capability: ${description.slice(0, 300)}`;
11023
11088
  * any dangerous action, so a silently-reloaded tool can't act without approval. */
11024
11089
  async loadPersistedTools() {
11025
11090
  if (!this.workspacePath || !this.persistEnabled) return;
11026
- const file = path25.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
11091
+ const file = path26.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
11027
11092
  try {
11028
- const raw = await fs9.readFile(file, "utf-8");
11093
+ const raw = await fs10.readFile(file, "utf-8");
11029
11094
  const specs = JSON.parse(raw);
11030
11095
  if (!Array.isArray(specs)) return;
11031
11096
  let loaded = 0;
@@ -11047,11 +11112,11 @@ Required capability: ${description.slice(0, 300)}`;
11047
11112
  }
11048
11113
  async persist() {
11049
11114
  if (!this.workspacePath || !this.persistEnabled) return;
11050
- const dir = path25.join(this.workspacePath, ".cascade");
11051
- const file = path25.join(dir, DYNAMIC_TOOLS_FILE);
11115
+ const dir = path26.join(this.workspacePath, ".cascade");
11116
+ const file = path26.join(dir, DYNAMIC_TOOLS_FILE);
11052
11117
  try {
11053
- await fs9.mkdir(dir, { recursive: true });
11054
- await fs9.writeFile(file, JSON.stringify(Array.from(this.specs.values()), null, 2), "utf-8");
11118
+ await fs10.mkdir(dir, { recursive: true });
11119
+ await fs10.writeFile(file, JSON.stringify(Array.from(this.specs.values()), null, 2), "utf-8");
11055
11120
  } catch (err) {
11056
11121
  this.log(`[tool-creator] Failed to persist tools: ${err instanceof Error ? err.message : String(err)}`);
11057
11122
  }
@@ -11068,12 +11133,12 @@ var WorldStateDB = class {
11068
11133
  constructor(workspacePath, debugMode = false) {
11069
11134
  this.workspacePath = workspacePath;
11070
11135
  this.debugMode = debugMode;
11071
- const cascadeDir = path25.join(workspacePath, ".cascade");
11072
- if (!fs23.existsSync(cascadeDir)) {
11073
- fs23.mkdirSync(cascadeDir, { recursive: true });
11136
+ const cascadeDir = path26.join(workspacePath, ".cascade");
11137
+ if (!fs24.existsSync(cascadeDir)) {
11138
+ fs24.mkdirSync(cascadeDir, { recursive: true });
11074
11139
  }
11075
- this.keyPath = path25.join(cascadeDir, "world_state.key");
11076
- this.dbPath = path25.join(cascadeDir, "world_state.db");
11140
+ this.keyPath = path26.join(cascadeDir, "world_state.key");
11141
+ this.dbPath = path26.join(cascadeDir, "world_state.db");
11077
11142
  this.initEncryptionKey();
11078
11143
  this.db = new Database2(this.dbPath);
11079
11144
  this.db.pragma("journal_mode = WAL");
@@ -11103,11 +11168,11 @@ var WorldStateDB = class {
11103
11168
  dbPath;
11104
11169
  encryptionKey;
11105
11170
  initEncryptionKey() {
11106
- if (fs23.existsSync(this.keyPath)) {
11107
- this.encryptionKey = fs23.readFileSync(this.keyPath);
11171
+ if (fs24.existsSync(this.keyPath)) {
11172
+ this.encryptionKey = fs24.readFileSync(this.keyPath);
11108
11173
  } else {
11109
11174
  this.encryptionKey = crypto.randomBytes(32);
11110
- fs23.writeFileSync(this.keyPath, this.encryptionKey);
11175
+ fs24.writeFileSync(this.keyPath, this.encryptionKey);
11111
11176
  }
11112
11177
  }
11113
11178
  encrypt(text) {
@@ -11248,6 +11313,22 @@ var WorldStateDB = class {
11248
11313
  }
11249
11314
  return selected.slice(0, limit).map((f) => `- ${f.entity} ${f.relation} ${f.value}`).join("\n");
11250
11315
  }
11316
+ /**
11317
+ * Delete one fact by its (normalized) entity + relation key. Returns whether
11318
+ * a row was actually removed. Powers the desktop Knowledge tab's per-fact
11319
+ * delete — users can prune what the planner remembers about their project.
11320
+ */
11321
+ deleteFact(entity, relation) {
11322
+ const e = normalizeKey(entity);
11323
+ const r = normalizeKey(relation);
11324
+ if (!e || !r) return false;
11325
+ const result = this.db.prepare("DELETE FROM facts WHERE entity = ? AND relation = ?").run(e, r);
11326
+ return result.changes > 0;
11327
+ }
11328
+ /** Delete every fact. Returns how many were removed. */
11329
+ clearFacts() {
11330
+ return this.db.prepare("DELETE FROM facts").run().changes;
11331
+ }
11251
11332
  // ── Export / Import ──────────────────────────
11252
11333
  //
11253
11334
  // Knowledge travels DECRYPTED in the export bundle (a portable plaintext
@@ -11296,9 +11377,9 @@ var WorldStateDB = class {
11296
11377
  dumpDebugIfNeeded() {
11297
11378
  if (!this.debugMode) return;
11298
11379
  try {
11299
- const dumpPath = path25.join(os8.tmpdir(), "cascade_world_state_debug.json");
11380
+ const dumpPath = path26.join(os8.tmpdir(), "cascade_world_state_debug.json");
11300
11381
  const entries = this.getAllEntries();
11301
- fs23.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
11382
+ fs24.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
11302
11383
  } catch (err) {
11303
11384
  console.error("Failed to dump debug world state", err);
11304
11385
  }
@@ -13716,10 +13797,10 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, a
13716
13797
  }
13717
13798
  } catch {
13718
13799
  }
13719
- const configPath = path25.join(workspacePath, ".cascade", "config.json");
13800
+ const configPath = path26.join(workspacePath, ".cascade", "config.json");
13720
13801
  try {
13721
- await fs9.mkdir(path25.dirname(configPath), { recursive: true });
13722
- await fs9.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
13802
+ await fs10.mkdir(path26.dirname(configPath), { recursive: true });
13803
+ await fs10.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
13723
13804
  } catch (err) {
13724
13805
  const msg = err instanceof Error ? err.message : String(err);
13725
13806
  dispatch({
@@ -13770,9 +13851,9 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, a
13770
13851
  if (msg.includes("non-text parts")) return;
13771
13852
  originalLog(...args);
13772
13853
  };
13773
- const store = new MemoryStore(path25.join(workspacePath, CASCADE_DB_FILE));
13854
+ const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
13774
13855
  storeRef.current = store;
13775
- globalStoreRef.current = new MemoryStore(path25.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE));
13856
+ globalStoreRef.current = new MemoryStore(path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE));
13776
13857
  const identityRows = store.listIdentities().map((i) => ({ id: i.id, name: i.name, isDefault: i.isDefault }));
13777
13858
  setIdentities(identityRows);
13778
13859
  let initialIdentityId = config.defaultIdentityId ?? identityRows.find((i) => i.isDefault)?.id ?? identityRows[0]?.id;
@@ -13835,14 +13916,14 @@ function Repl({ config, workspacePath, themeName, initialPrompt, identityName, a
13835
13916
  onThemeChange: (name) => setTheme(getTheme(name)),
13836
13917
  onExport: async (fmt) => {
13837
13918
  const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
13838
- const exportPath = path25.join(workspacePath, `cascade-export-${stamp}.${fmt === "json" ? "json" : "md"}`);
13919
+ const exportPath = path26.join(workspacePath, `cascade-export-${stamp}.${fmt === "json" ? "json" : "md"}`);
13839
13920
  if (fmt === "json") {
13840
- await fs9.writeFile(exportPath, JSON.stringify({ sessionId: sessionIdRef.current, messages: state.messages }, null, 2), "utf-8");
13921
+ await fs10.writeFile(exportPath, JSON.stringify({ sessionId: sessionIdRef.current, messages: state.messages }, null, 2), "utf-8");
13841
13922
  } else {
13842
13923
  const markdown = state.messages.map((msg) => `## ${msg.role.toUpperCase()} \u2014 ${msg.timestamp}
13843
13924
 
13844
13925
  ${msg.content}`).join("\n\n");
13845
- await fs9.writeFile(exportPath, markdown, "utf-8");
13926
+ await fs10.writeFile(exportPath, markdown, "utf-8");
13846
13927
  }
13847
13928
  },
13848
13929
  onRollback: async () => {
@@ -13852,7 +13933,7 @@ ${msg.content}`).join("\n\n");
13852
13933
  if (!snapshots.length) return "No file snapshots found for this session.";
13853
13934
  for (const { filePath, content } of snapshots) {
13854
13935
  try {
13855
- await fs9.writeFile(filePath, content, "utf-8");
13936
+ await fs10.writeFile(filePath, content, "utf-8");
13856
13937
  } catch (err) {
13857
13938
  console.error(`Restore failed: ${filePath}`, err);
13858
13939
  }
@@ -14731,9 +14812,9 @@ function stringifySlashOutput(val) {
14731
14812
  }
14732
14813
  async function searchSessionsAndMessages(query, workspacePath) {
14733
14814
  if (!query) return "Usage: /search <query>";
14734
- const dbPath = path25.join(workspacePath, CASCADE_DB_FILE);
14815
+ const dbPath = path26.join(workspacePath, CASCADE_DB_FILE);
14735
14816
  try {
14736
- await fs9.access(dbPath);
14817
+ await fs10.access(dbPath);
14737
14818
  } catch {
14738
14819
  return "No database found. Start a conversation first.";
14739
14820
  }
@@ -14765,7 +14846,7 @@ async function searchSessionsAndMessages(query, workspacePath) {
14765
14846
  async function diagnoseRuntime(config, workspacePath) {
14766
14847
  const providers = config.providers.map((p) => `${p.type}${p.apiKey ? " (key set)" : " (no key)"}`).join("\n");
14767
14848
  const models = [`T1: ${config.models.t1 ?? "default"}`, `T2: ${config.models.t2 ?? "default"}`, `T3: ${config.models.t3 ?? "default"}`].join("\n");
14768
- const store = new MemoryStore(path25.join(workspacePath, CASCADE_DB_FILE));
14849
+ const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
14769
14850
  try {
14770
14851
  const sessions = store.listSessions(void 0, 3);
14771
14852
  return [
@@ -14784,7 +14865,7 @@ async function diagnoseRuntime(config, workspacePath) {
14784
14865
  }
14785
14866
  async function showRecentLogs(args, workspacePath) {
14786
14867
  const limit = Number.parseInt(args[0] ?? "10", 10) || 10;
14787
- const store = new MemoryStore(path25.join(workspacePath, CASCADE_DB_FILE));
14868
+ const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
14788
14869
  try {
14789
14870
  const logs = store.listRuntimeNodeLogs(void 0, void 0, limit);
14790
14871
  if (!logs.length) return "No recent runtime logs.";
@@ -14796,7 +14877,7 @@ async function showRecentLogs(args, workspacePath) {
14796
14877
  async function loadSessionSnapshot(args, workspacePath) {
14797
14878
  const sessionId = args[0];
14798
14879
  if (!sessionId) return "Usage: /resume <sessionId>";
14799
- const store = new MemoryStore(path25.join(workspacePath, CASCADE_DB_FILE));
14880
+ const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
14800
14881
  try {
14801
14882
  const session = store.getSession(sessionId);
14802
14883
  if (!session) return `Session not found: ${sessionId}`;
@@ -15216,10 +15297,10 @@ function SetupWizard({ workspacePath, onComplete }) {
15216
15297
  ...anyAuto ? { cascadeAuto: true } : {}
15217
15298
  };
15218
15299
  const config = CascadeConfigSchema.parse(rawConfig);
15219
- const configDir = path25.join(workspacePath, ".cascade");
15220
- await fs9.mkdir(configDir, { recursive: true });
15221
- const configPath = path25.join(workspacePath, CASCADE_CONFIG_FILE);
15222
- await fs9.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
15300
+ const configDir = path26.join(workspacePath, ".cascade");
15301
+ await fs10.mkdir(configDir, { recursive: true });
15302
+ const configPath = path26.join(workspacePath, CASCADE_CONFIG_FILE);
15303
+ await fs10.writeFile(configPath, JSON.stringify(config, null, 2), "utf-8");
15223
15304
  savedConfigRef.current = config;
15224
15305
  dispatchRef.current({ type: "GO_DONE" });
15225
15306
  } catch (err) {
@@ -15559,14 +15640,14 @@ function printTelemetryBanner() {
15559
15640
  async function initCommand(workspacePath = process.cwd()) {
15560
15641
  const spin = ora({ text: "Initializing Cascade project\u2026", color: "magenta" }).start();
15561
15642
  try {
15562
- const configDir = path25.join(workspacePath, ".cascade");
15563
- await fs9.mkdir(configDir, { recursive: true });
15564
- const mdPath = path25.join(workspacePath, "CASCADE.md");
15643
+ const configDir = path26.join(workspacePath, ".cascade");
15644
+ await fs10.mkdir(configDir, { recursive: true });
15645
+ const mdPath = path26.join(workspacePath, "CASCADE.md");
15565
15646
  if (!await fileExists(mdPath)) {
15566
15647
  await createDefaultCascadeMd(workspacePath);
15567
15648
  spin.succeed(chalk9.green("Created CASCADE.md"));
15568
15649
  }
15569
- const ignorePath = path25.join(workspacePath, ".cascadeignore");
15650
+ const ignorePath = path26.join(workspacePath, ".cascadeignore");
15570
15651
  if (!await fileExists(ignorePath)) {
15571
15652
  await createDefaultIgnoreFile(workspacePath);
15572
15653
  spin.succeed(chalk9.green("Created .cascadeignore"));
@@ -15575,7 +15656,7 @@ async function initCommand(workspacePath = process.cwd()) {
15575
15656
  console.log();
15576
15657
  console.log(chalk9.magenta(" \u25C8 Cascade AI \u2014 Project initialized"));
15577
15658
  console.log();
15578
- const configPath = path25.join(workspacePath, CASCADE_CONFIG_FILE);
15659
+ const configPath = path26.join(workspacePath, CASCADE_CONFIG_FILE);
15579
15660
  if (await fileExists(configPath)) {
15580
15661
  console.log(chalk9.yellow(" .cascade/config.json already exists \u2014 launching wizard to reconfigure."));
15581
15662
  console.log();
@@ -15594,7 +15675,7 @@ async function initCommand(workspacePath = process.cwd()) {
15594
15675
  }
15595
15676
  async function fileExists(p) {
15596
15677
  try {
15597
- await fs9.access(p);
15678
+ await fs10.access(p);
15598
15679
  return true;
15599
15680
  } catch {
15600
15681
  return false;
@@ -15606,7 +15687,7 @@ init_constants();
15606
15687
  var TOS_WARNING = "Reusing this subscription token outside its own CLI may violate the vendor\u2019s terms of service.";
15607
15688
  async function readJson(file) {
15608
15689
  try {
15609
- const raw = await fs9.readFile(file, "utf-8");
15690
+ const raw = await fs10.readFile(file, "utf-8");
15610
15691
  return JSON.parse(raw);
15611
15692
  } catch {
15612
15693
  return null;
@@ -15633,7 +15714,7 @@ function fromEnv(env) {
15633
15714
  return out;
15634
15715
  }
15635
15716
  async function fromClaudeCode(home) {
15636
- const file = path25.join(home, ".claude", ".credentials.json");
15717
+ const file = path26.join(home, ".claude", ".credentials.json");
15637
15718
  const data = await readJson(file);
15638
15719
  if (!data) return [];
15639
15720
  const oauth = data["claudeAiOauth"];
@@ -15656,7 +15737,7 @@ async function fromClaudeCode(home) {
15656
15737
  return [];
15657
15738
  }
15658
15739
  async function fromCodex(home) {
15659
- const file = path25.join(home, ".codex", "auth.json");
15740
+ const file = path26.join(home, ".codex", "auth.json");
15660
15741
  const data = await readJson(file);
15661
15742
  if (!data) return [];
15662
15743
  const apiKey = str(data["OPENAI_API_KEY"]);
@@ -15679,7 +15760,7 @@ async function fromCodex(home) {
15679
15760
  return [];
15680
15761
  }
15681
15762
  async function fromGemini(home) {
15682
- const file = path25.join(home, ".gemini", "oauth_creds.json");
15763
+ const file = path26.join(home, ".gemini", "oauth_creds.json");
15683
15764
  const data = await readJson(file);
15684
15765
  const accessToken = str(data?.["access_token"]);
15685
15766
  if (!accessToken) return [];
@@ -15695,7 +15776,7 @@ async function fromGemini(home) {
15695
15776
  }
15696
15777
  async function fromCopilot(home) {
15697
15778
  for (const name of ["apps.json", "hosts.json"]) {
15698
- const file = path25.join(home, ".config", "github-copilot", name);
15779
+ const file = path26.join(home, ".config", "github-copilot", name);
15699
15780
  const data = await readJson(file);
15700
15781
  if (!data) continue;
15701
15782
  for (const value of Object.values(data)) {
@@ -15749,7 +15830,7 @@ async function doctorCommand() {
15749
15830
  checks.push({
15750
15831
  label: "Cascade config",
15751
15832
  ok: true,
15752
- detail: `Loaded ${path25.join(process.cwd(), CASCADE_CONFIG_FILE)}`
15833
+ detail: `Loaded ${path26.join(process.cwd(), CASCADE_CONFIG_FILE)}`
15753
15834
  });
15754
15835
  const providers = [
15755
15836
  { type: "anthropic", name: "Anthropic" },
@@ -15905,6 +15986,11 @@ function authMiddleware(secret, required = true) {
15905
15986
  }
15906
15987
  const user = verifyToken(token, secret);
15907
15988
  if (!user) {
15989
+ if (!required) {
15990
+ req.user = void 0;
15991
+ next();
15992
+ return;
15993
+ }
15908
15994
  res.status(401).json({ error: "Invalid or expired token" });
15909
15995
  return;
15910
15996
  }
@@ -16216,7 +16302,7 @@ function aggregateCostStats(sessions, opts = {}) {
16216
16302
  }
16217
16303
 
16218
16304
  // src/dashboard/server.ts
16219
- var __dirname$1 = path25.dirname(fileURLToPath(import.meta.url));
16305
+ var __dirname$1 = path26.dirname(fileURLToPath(import.meta.url));
16220
16306
  var DashboardServer = class {
16221
16307
  app;
16222
16308
  httpServer;
@@ -16450,12 +16536,17 @@ var DashboardServer = class {
16450
16536
  */
16451
16537
  persistConfig() {
16452
16538
  try {
16453
- const configPath = path25.join(this.workspacePath, CASCADE_CONFIG_FILE);
16454
- fs23.mkdirSync(path25.dirname(configPath), { recursive: true });
16455
- fs23.writeFileSync(configPath, JSON.stringify(this.config, null, 2), "utf-8");
16539
+ const configPath = path26.join(this.workspacePath, CASCADE_CONFIG_FILE);
16540
+ fs24.mkdirSync(path26.dirname(configPath), { recursive: true });
16541
+ fs24.writeFileSync(configPath, JSON.stringify(this.config, null, 2), "utf-8");
16456
16542
  } catch (err) {
16457
16543
  console.warn(`[dashboard] Failed to persist config: ${err instanceof Error ? err.message : String(err)}`);
16458
16544
  }
16545
+ try {
16546
+ saveGlobalCredentials(path26.join(os8.homedir(), GLOBAL_CONFIG_DIR), this.config.providers ?? []);
16547
+ } catch (err) {
16548
+ console.warn(`[dashboard] Failed to sync global credentials: ${err instanceof Error ? err.message : String(err)}`);
16549
+ }
16459
16550
  }
16460
16551
  /**
16461
16552
  * Produce a stable dashboard JWT signing secret.
@@ -16467,15 +16558,15 @@ var DashboardServer = class {
16467
16558
  resolveDashboardSecret() {
16468
16559
  const fromConfig = this.config.dashboard.secret ?? process.env["CASCADE_DASHBOARD_SECRET"];
16469
16560
  if (fromConfig) return fromConfig;
16470
- const secretPath = path25.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
16561
+ const secretPath = path26.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
16471
16562
  try {
16472
- if (fs23.existsSync(secretPath)) {
16473
- const existing = fs23.readFileSync(secretPath, "utf-8").trim();
16563
+ if (fs24.existsSync(secretPath)) {
16564
+ const existing = fs24.readFileSync(secretPath, "utf-8").trim();
16474
16565
  if (existing.length >= 16) return existing;
16475
16566
  }
16476
16567
  const generated = randomUUID();
16477
- fs23.mkdirSync(path25.dirname(secretPath), { recursive: true });
16478
- fs23.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
16568
+ fs24.mkdirSync(path26.dirname(secretPath), { recursive: true });
16569
+ fs24.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
16479
16570
  if (this.config.dashboard.auth) {
16480
16571
  console.warn(
16481
16572
  `Dashboard auth enabled with no secret configured; persisted a generated secret to ${secretPath}. Set CASCADE_DASHBOARD_SECRET or config.dashboard.secret to override.`
@@ -16502,7 +16593,7 @@ var DashboardServer = class {
16502
16593
  // ── Setup ─────────────────────────────────────
16503
16594
  getGlobalStore() {
16504
16595
  if (!this.globalStore) {
16505
- const globalDbPath = path25.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
16596
+ const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
16506
16597
  this.globalStore = new MemoryStore(globalDbPath);
16507
16598
  }
16508
16599
  return this.globalStore;
@@ -16771,12 +16862,12 @@ ${prompt}`;
16771
16862
  }
16772
16863
  }
16773
16864
  watchRuntimeChanges() {
16774
- const workspaceDbPath = path25.join(this.workspacePath, CASCADE_DB_FILE);
16775
- const globalDbPath = path25.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
16865
+ const workspaceDbPath = path26.join(this.workspacePath, CASCADE_DB_FILE);
16866
+ const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
16776
16867
  const watchPaths = [workspaceDbPath, globalDbPath].filter((p, index, arr) => arr.indexOf(p) === index);
16777
16868
  for (const watchPath of watchPaths) {
16778
- if (!fs23.existsSync(watchPath)) continue;
16779
- fs23.watchFile(watchPath, { interval: 3e3 }, () => {
16869
+ if (!fs24.existsSync(watchPath)) continue;
16870
+ fs24.watchFile(watchPath, { interval: 3e3 }, () => {
16780
16871
  this.throttledBroadcast(watchPath === globalDbPath ? "global" : "workspace");
16781
16872
  });
16782
16873
  }
@@ -16898,7 +16989,7 @@ ${prompt}`;
16898
16989
  const sessionId = req.params.id;
16899
16990
  this.store.deleteSession(sessionId);
16900
16991
  this.store.deleteRuntimeSession(sessionId);
16901
- const globalDbPath = path25.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
16992
+ const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
16902
16993
  const globalStore = new MemoryStore(globalDbPath);
16903
16994
  try {
16904
16995
  globalStore.deleteRuntimeSession(sessionId);
@@ -16991,7 +17082,7 @@ ${prompt}`;
16991
17082
  });
16992
17083
  this.app.delete("/api/sessions", auth, (req, res) => {
16993
17084
  const body = req.body;
16994
- const globalDbPath = path25.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
17085
+ const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
16995
17086
  if (body?.ids && Array.isArray(body.ids) && body.ids.length > 0) {
16996
17087
  const globalStore = new MemoryStore(globalDbPath);
16997
17088
  try {
@@ -17014,7 +17105,7 @@ ${prompt}`;
17014
17105
  });
17015
17106
  this.app.delete("/api/runtime", auth, (_req, res) => {
17016
17107
  this.store.deleteAllRuntimeNodes();
17017
- const globalDbPath = path25.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
17108
+ const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
17018
17109
  const globalStore = new MemoryStore(globalDbPath);
17019
17110
  try {
17020
17111
  globalStore.deleteAllRuntimeNodes();
@@ -17100,12 +17191,12 @@ ${prompt}`;
17100
17191
  if (body["tierLimits"]) this.config.tierLimits = { ...this.config.tierLimits, ...body["tierLimits"] };
17101
17192
  if (body["budget"]) this.config.budget = { ...this.config.budget, ...body["budget"] };
17102
17193
  try {
17103
- const configPath = path25.join(this.workspacePath, CASCADE_CONFIG_FILE);
17104
- const existing = fs23.existsSync(configPath) ? JSON.parse(fs23.readFileSync(configPath, "utf-8")) : {};
17194
+ const configPath = path26.join(this.workspacePath, CASCADE_CONFIG_FILE);
17195
+ const existing = fs24.existsSync(configPath) ? JSON.parse(fs24.readFileSync(configPath, "utf-8")) : {};
17105
17196
  const updated = { ...existing, tierLimits: this.config.tierLimits, budget: this.config.budget };
17106
17197
  const tmp = configPath + ".tmp";
17107
- fs23.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
17108
- fs23.renameSync(tmp, configPath);
17198
+ fs24.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
17199
+ fs24.renameSync(tmp, configPath);
17109
17200
  res.json({ ok: true });
17110
17201
  } catch (err) {
17111
17202
  res.status(500).json({ error: `Failed to save config: ${err instanceof Error ? err.message : String(err)}` });
@@ -17133,7 +17224,7 @@ ${prompt}`;
17133
17224
  this.app.get("/api/runtime", auth, (req, res) => {
17134
17225
  const scope = req.query["scope"] ?? "workspace";
17135
17226
  if (scope === "global") {
17136
- const globalDbPath = path25.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
17227
+ const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
17137
17228
  const globalStore = new MemoryStore(globalDbPath);
17138
17229
  try {
17139
17230
  res.json({
@@ -17368,6 +17459,48 @@ ${prompt}`;
17368
17459
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
17369
17460
  }
17370
17461
  });
17462
+ this.app.get("/api/knowledge", auth, (_req, res) => {
17463
+ try {
17464
+ const ws = new WorldStateDB(this.workspacePath);
17465
+ try {
17466
+ const facts = ws.getAllFacts();
17467
+ res.json({ total: facts.length, facts });
17468
+ } finally {
17469
+ ws.close();
17470
+ }
17471
+ } catch (err) {
17472
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
17473
+ }
17474
+ });
17475
+ this.app.delete("/api/knowledge/fact", auth, mutationLimiter, (req, res) => {
17476
+ const body = req.body;
17477
+ if (!body.entity || !body.relation) {
17478
+ res.status(400).json({ error: "entity and relation are required" });
17479
+ return;
17480
+ }
17481
+ try {
17482
+ const ws = new WorldStateDB(this.workspacePath);
17483
+ try {
17484
+ res.json({ ok: true, deleted: ws.deleteFact(body.entity, body.relation) });
17485
+ } finally {
17486
+ ws.close();
17487
+ }
17488
+ } catch (err) {
17489
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
17490
+ }
17491
+ });
17492
+ this.app.delete("/api/knowledge", auth, mutationLimiter, (_req, res) => {
17493
+ try {
17494
+ const ws = new WorldStateDB(this.workspacePath);
17495
+ try {
17496
+ res.json({ ok: true, deleted: ws.clearFacts() });
17497
+ } finally {
17498
+ ws.close();
17499
+ }
17500
+ } catch (err) {
17501
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
17502
+ }
17503
+ });
17371
17504
  this.app.get("/api/audit-chain", auth, async (req, res) => {
17372
17505
  try {
17373
17506
  const limit = Math.min(500, Math.max(1, parseInt(req.query["limit"] || "200", 10) || 200));
@@ -17386,13 +17519,13 @@ ${prompt}`;
17386
17519
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
17387
17520
  }
17388
17521
  });
17389
- const prodPath = path25.resolve(__dirname$1, "../web/dist");
17390
- const devPath = path25.resolve(__dirname$1, "../../web/dist");
17391
- const webDistPath = fs23.existsSync(prodPath) ? prodPath : devPath;
17392
- if (fs23.existsSync(webDistPath)) {
17522
+ const prodPath = path26.resolve(__dirname$1, "../web/dist");
17523
+ const devPath = path26.resolve(__dirname$1, "../../web/dist");
17524
+ const webDistPath = fs24.existsSync(prodPath) ? prodPath : devPath;
17525
+ if (fs24.existsSync(webDistPath)) {
17393
17526
  this.app.use(express.static(webDistPath));
17394
17527
  this.app.get("*", (_req, res) => {
17395
- res.sendFile(path25.join(webDistPath, "index.html"));
17528
+ res.sendFile(path26.join(webDistPath, "index.html"));
17396
17529
  });
17397
17530
  } else {
17398
17531
  this.app.get("/", (_req, res) => {
@@ -17413,7 +17546,7 @@ init_constants();
17413
17546
  async function dashboardCommand(config, workspacePath = process.cwd()) {
17414
17547
  const port = config.dashboard.port ?? DEFAULT_DASHBOARD_PORT;
17415
17548
  const spin = ora({ text: `Starting dashboard on port ${port}\u2026`, color: "magenta" }).start();
17416
- const store = new MemoryStore(path25.join(workspacePath, CASCADE_DB_FILE));
17549
+ const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
17417
17550
  const server = new DashboardServer(config, store, workspacePath);
17418
17551
  server.watchRuntimeChanges();
17419
17552
  const gThis = globalThis;
@@ -17445,7 +17578,7 @@ init_constants();
17445
17578
  function makeIdentityCommand(workspacePath = process.cwd()) {
17446
17579
  const identity = new Command("identity").alias("id").description("Manage Cascade identities");
17447
17580
  identity.command("list").description("List all available identities").action(() => {
17448
- const store = new MemoryStore(path25.join(workspacePath, CASCADE_DB_FILE));
17581
+ const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
17449
17582
  try {
17450
17583
  const identities = store.listIdentities();
17451
17584
  console.log(chalk9.bold("\n Identities:"));
@@ -17465,7 +17598,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
17465
17598
  }
17466
17599
  });
17467
17600
  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) => {
17468
- const store = new MemoryStore(path25.join(workspacePath, CASCADE_DB_FILE));
17601
+ const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
17469
17602
  try {
17470
17603
  if (options.default) {
17471
17604
  const existingDefault = store.getDefaultIdentity();
@@ -17491,7 +17624,7 @@ function makeIdentityCommand(workspacePath = process.cwd()) {
17491
17624
  }
17492
17625
  });
17493
17626
  identity.command("set-default <name>").description("Set an identity as default by name or ID").action((query) => {
17494
- const store = new MemoryStore(path25.join(workspacePath, CASCADE_DB_FILE));
17627
+ const store = new MemoryStore(path26.join(workspacePath, CASCADE_DB_FILE));
17495
17628
  try {
17496
17629
  const identities = store.listIdentities();
17497
17630
  const match = identities.find((i) => i.id === query || i.name.toLowerCase() === query.toLowerCase());
@@ -17619,11 +17752,11 @@ async function exportCommand(options = {}) {
17619
17752
  let store;
17620
17753
  try {
17621
17754
  const workspacePath = options.workspacePath ?? process.cwd();
17622
- const workspaceDbPath = path25.join(workspacePath, CASCADE_DB_FILE);
17623
- const globalDbPath = path25.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE);
17755
+ const workspaceDbPath = path26.join(workspacePath, CASCADE_DB_FILE);
17756
+ const globalDbPath = path26.join(os8.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_DB_FILE);
17624
17757
  let dbPath = globalDbPath;
17625
17758
  try {
17626
- await fs9.access(workspaceDbPath);
17759
+ await fs10.access(workspaceDbPath);
17627
17760
  dbPath = workspaceDbPath;
17628
17761
  } catch {
17629
17762
  }
@@ -17662,8 +17795,8 @@ async function exportCommand(options = {}) {
17662
17795
  const ext = format === "json" ? ".json" : ".md";
17663
17796
  const safeName = session.title.replace(/[^\w\s-]/g, "").replace(/\s+/g, "-").slice(0, 60);
17664
17797
  const defaultFile = `cascade-export-${safeName}${ext}`;
17665
- const outPath = options.output ? path25.resolve(options.output) : path25.join(process.cwd(), defaultFile);
17666
- await fs9.writeFile(outPath, content, "utf-8");
17798
+ const outPath = options.output ? path26.resolve(options.output) : path26.join(process.cwd(), defaultFile);
17799
+ await fs10.writeFile(outPath, content, "utf-8");
17667
17800
  spin.succeed(chalk9.green(`Exported to ${chalk9.white(outPath)}`));
17668
17801
  const messageCount = Array.isArray(session.messages) ? session.messages.length : 0;
17669
17802
  console.log();
@@ -17890,11 +18023,11 @@ async function statsCommand() {
17890
18023
  dotenv.config();
17891
18024
  function warnIfBuildIsStale() {
17892
18025
  try {
17893
- const here = path25.dirname(fileURLToPath(import.meta.url));
18026
+ const here = path26.dirname(fileURLToPath(import.meta.url));
17894
18027
  for (const rel of ["..", "../.."]) {
17895
- const pkgPath = path25.join(here, rel, "package.json");
17896
- if (!fs23.existsSync(pkgPath)) continue;
17897
- const pkg = JSON.parse(fs23.readFileSync(pkgPath, "utf-8"));
18028
+ const pkgPath = path26.join(here, rel, "package.json");
18029
+ if (!fs24.existsSync(pkgPath)) continue;
18030
+ const pkg = JSON.parse(fs24.readFileSync(pkgPath, "utf-8"));
17898
18031
  if (pkg.name !== "cascade-ai") continue;
17899
18032
  if (pkg.version && pkg.version !== CASCADE_VERSION) {
17900
18033
  console.error(