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/index.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  'use strict';
2
2
 
3
3
  var Database = require('better-sqlite3');
4
- var path20 = require('path');
5
- var fs19 = require('fs');
4
+ var path21 = require('path');
5
+ var fs20 = require('fs');
6
6
  var os6 = require('os');
7
7
  var crypto3 = require('crypto');
8
8
  var EventEmitter = require('events');
@@ -58,8 +58,8 @@ function _interopNamespace(e) {
58
58
  }
59
59
 
60
60
  var Database__default = /*#__PURE__*/_interopDefault(Database);
61
- var path20__default = /*#__PURE__*/_interopDefault(path20);
62
- var fs19__default = /*#__PURE__*/_interopDefault(fs19);
61
+ var path21__default = /*#__PURE__*/_interopDefault(path21);
62
+ var fs20__default = /*#__PURE__*/_interopDefault(fs20);
63
63
  var os6__default = /*#__PURE__*/_interopDefault(os6);
64
64
  var crypto3__default = /*#__PURE__*/_interopDefault(crypto3);
65
65
  var EventEmitter__default = /*#__PURE__*/_interopDefault(EventEmitter);
@@ -104,12 +104,12 @@ var init_audit_logger = __esm({
104
104
  constructor(workspacePath, debugMode = false) {
105
105
  this.workspacePath = workspacePath;
106
106
  this.debugMode = debugMode;
107
- const cascadeDir = path20__default.default.join(workspacePath, ".cascade");
108
- if (!fs19__default.default.existsSync(cascadeDir)) {
109
- fs19__default.default.mkdirSync(cascadeDir, { recursive: true });
107
+ const cascadeDir = path21__default.default.join(workspacePath, ".cascade");
108
+ if (!fs20__default.default.existsSync(cascadeDir)) {
109
+ fs20__default.default.mkdirSync(cascadeDir, { recursive: true });
110
110
  }
111
- this.keyPath = path20__default.default.join(cascadeDir, "audit_log.key");
112
- this.dbPath = path20__default.default.join(cascadeDir, "audit_log.db");
111
+ this.keyPath = path21__default.default.join(cascadeDir, "audit_log.key");
112
+ this.dbPath = path21__default.default.join(cascadeDir, "audit_log.db");
113
113
  this.initEncryptionKey();
114
114
  this.db = new Database__default.default(this.dbPath);
115
115
  this.db.pragma("journal_mode = WAL");
@@ -139,11 +139,11 @@ var init_audit_logger = __esm({
139
139
  return crypto3__default.default.createHash("sha256").update(`${prevHash}|${timestamp}|${eventType}|${tierId}|${encryptedPayload}`).digest("hex");
140
140
  }
141
141
  initEncryptionKey() {
142
- if (fs19__default.default.existsSync(this.keyPath)) {
143
- this.encryptionKey = fs19__default.default.readFileSync(this.keyPath);
142
+ if (fs20__default.default.existsSync(this.keyPath)) {
143
+ this.encryptionKey = fs20__default.default.readFileSync(this.keyPath);
144
144
  } else {
145
145
  this.encryptionKey = crypto3__default.default.randomBytes(32);
146
- fs19__default.default.writeFileSync(this.keyPath, this.encryptionKey);
146
+ fs20__default.default.writeFileSync(this.keyPath, this.encryptionKey);
147
147
  }
148
148
  }
149
149
  encrypt(text) {
@@ -221,9 +221,9 @@ var init_audit_logger = __esm({
221
221
  dumpDebugIfNeeded() {
222
222
  if (!this.debugMode) return;
223
223
  try {
224
- const dumpPath = path20__default.default.join(os6__default.default.tmpdir(), "cascade_audit_logs_debug.json");
224
+ const dumpPath = path21__default.default.join(os6__default.default.tmpdir(), "cascade_audit_logs_debug.json");
225
225
  const entries = this.getAllLogs();
226
- fs19__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
226
+ fs20__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
227
227
  } catch (err) {
228
228
  console.error("Failed to dump debug audit logs", err);
229
229
  }
@@ -236,7 +236,7 @@ var init_audit_logger = __esm({
236
236
  });
237
237
 
238
238
  // src/constants.ts
239
- var CASCADE_VERSION = "0.17.0";
239
+ var CASCADE_VERSION = "0.18.0";
240
240
  var CASCADE_CONFIG_DIR = ".cascade";
241
241
  var CASCADE_MD_FILE = "CASCADE.md";
242
242
  var CASCADE_IGNORE_FILE = ".cascadeignore";
@@ -248,6 +248,7 @@ var CASCADE_DASHBOARD_SECRET_FILE = ".cascade/dashboard-secret";
248
248
  var GLOBAL_CONFIG_DIR = ".cascade-ai";
249
249
  var GLOBAL_DB_FILE = "memory.db";
250
250
  var GLOBAL_KEYSTORE_FILE = "keystore.enc";
251
+ var GLOBAL_CREDENTIALS_FILE = "credentials.json";
251
252
  var GLOBAL_RUNTIME_DB_FILE = "runtime.db";
252
253
  var DEFAULT_DASHBOARD_PORT = 4891;
253
254
  var DEFAULT_API_PORT = 4892;
@@ -2210,7 +2211,7 @@ function computeDelegationSavings(stats, t1Model) {
2210
2211
  var DEFAULT_SNAPSHOT_URL = "https://raw.githubusercontent.com/Varun-SV/Cascade-AI/main/src/core/router/benchmark-data.json";
2211
2212
  var OPENROUTER_MODELS_URL = "https://openrouter.ai/api/v1/models";
2212
2213
  var FETCH_TIMEOUT_MS = 8e3;
2213
- var DEFAULT_CACHE_FILE = path20__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
2214
+ var DEFAULT_CACHE_FILE = path21__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, "benchmarks-cache.json");
2214
2215
  function normalizeModelId(id) {
2215
2216
  let s = id.toLowerCase();
2216
2217
  const slash = s.lastIndexOf("/");
@@ -2355,7 +2356,7 @@ var LiveDataProvider = class {
2355
2356
  }
2356
2357
  async saveCache() {
2357
2358
  try {
2358
- await fs4__default.default.mkdir(path20__default.default.dirname(this.opts.cacheFile), { recursive: true });
2359
+ await fs4__default.default.mkdir(path21__default.default.dirname(this.opts.cacheFile), { recursive: true });
2359
2360
  const cache = {
2360
2361
  fetchedAt: this.fetchedAt,
2361
2362
  snapshot: this.snapshot ?? void 0,
@@ -4258,8 +4259,8 @@ HIERARCHY CONTEXT: ${this.hierarchyContext}` : "") + textToolSuffix,
4258
4259
  tierId: this.id,
4259
4260
  sessionId: this.taskId,
4260
4261
  requireApproval: false,
4261
- saveSnapshot: async (path21, content) => {
4262
- this.store?.addFileSnapshot(this.taskId, path21, content);
4262
+ saveSnapshot: async (path22, content) => {
4263
+ this.store?.addFileSnapshot(this.taskId, path22, content);
4263
4264
  },
4264
4265
  sendPeerSync: (to, syncType, content) => {
4265
4266
  this.peerBus?.send(this.id, to, syncType, this.assignment?.subtaskId ?? "", content);
@@ -4429,7 +4430,7 @@ ${assignment.expectedOutput}`;
4429
4430
  const { promisify: promisify4 } = await import('util');
4430
4431
  const execAsync2 = promisify4(exec2);
4431
4432
  for (const artifactPath of artifactPaths) {
4432
- const absolutePath = path20__default.default.resolve(process.cwd(), artifactPath);
4433
+ const absolutePath = path21__default.default.resolve(process.cwd(), artifactPath);
4433
4434
  try {
4434
4435
  const stat = await fs4__default.default.stat(absolutePath);
4435
4436
  if (!stat.isFile()) {
@@ -4450,7 +4451,7 @@ ${assignment.expectedOutput}`;
4450
4451
  issues.push(`PDF artifact looks too small to be valid: ${artifactPath}`);
4451
4452
  continue;
4452
4453
  }
4453
- const ext = path20__default.default.extname(absolutePath).toLowerCase();
4454
+ const ext = path21__default.default.extname(absolutePath).toLowerCase();
4454
4455
  try {
4455
4456
  if (ext === ".ts" || ext === ".tsx") {
4456
4457
  await execAsync2(`npx tsc --noEmit ${absolutePath}`, { timeout: 1e4 });
@@ -6532,16 +6533,16 @@ function resolveInWorkspace(workspaceRoot, input) {
6532
6533
  if (typeof input !== "string" || input.length === 0) {
6533
6534
  throw new WorkspaceSandboxError(String(input), workspaceRoot);
6534
6535
  }
6535
- const root = path20__default.default.resolve(workspaceRoot);
6536
- const abs = path20__default.default.isAbsolute(input) ? path20__default.default.resolve(input) : path20__default.default.resolve(root, input);
6537
- const rel = path20__default.default.relative(root, abs);
6538
- if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path20__default.default.isAbsolute(rel)) {
6536
+ const root = path21__default.default.resolve(workspaceRoot);
6537
+ const abs = path21__default.default.isAbsolute(input) ? path21__default.default.resolve(input) : path21__default.default.resolve(root, input);
6538
+ const rel = path21__default.default.relative(root, abs);
6539
+ if (rel === "" || rel === ".") ; else if (rel.startsWith("..") || path21__default.default.isAbsolute(rel)) {
6539
6540
  throw new WorkspaceSandboxError(input, root);
6540
6541
  }
6541
6542
  try {
6542
- const real = fs19__default.default.realpathSync(abs);
6543
- const realRel = path20__default.default.relative(root, real);
6544
- if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path20__default.default.isAbsolute(realRel))) {
6543
+ const real = fs20__default.default.realpathSync(abs);
6544
+ const realRel = path21__default.default.relative(root, real);
6545
+ if (realRel !== "" && realRel !== "." && (realRel.startsWith("..") || path21__default.default.isAbsolute(realRel))) {
6545
6546
  throw new WorkspaceSandboxError(input, root);
6546
6547
  }
6547
6548
  } catch (e) {
@@ -6602,7 +6603,7 @@ var FileWriteTool = class extends BaseTool {
6602
6603
  } catch {
6603
6604
  }
6604
6605
  }
6605
- await fs4__default.default.mkdir(path20__default.default.dirname(absPath), { recursive: true });
6606
+ await fs4__default.default.mkdir(path21__default.default.dirname(absPath), { recursive: true });
6606
6607
  await fs4__default.default.writeFile(absPath, content, "utf-8");
6607
6608
  return `Written ${content.length} characters to ${filePath}`;
6608
6609
  }
@@ -7100,7 +7101,7 @@ var ImageAnalyzeTool = class extends BaseTool {
7100
7101
  };
7101
7102
  async function fileToImageAttachment(filePath) {
7102
7103
  const data = await fs4__default.default.readFile(filePath);
7103
- const ext = path20__default.default.extname(filePath).toLowerCase();
7104
+ const ext = path21__default.default.extname(filePath).toLowerCase();
7104
7105
  const mimeMap = {
7105
7106
  ".jpg": "image/jpeg",
7106
7107
  ".jpeg": "image/jpeg",
@@ -7134,14 +7135,14 @@ var PDFCreateTool = class extends BaseTool {
7134
7135
  const filePath = input["path"];
7135
7136
  const content = input["content"];
7136
7137
  const title = input["title"];
7137
- const dir = path20__default.default.dirname(filePath);
7138
- if (!fs19__default.default.existsSync(dir)) {
7139
- fs19__default.default.mkdirSync(dir, { recursive: true });
7138
+ const dir = path21__default.default.dirname(filePath);
7139
+ if (!fs20__default.default.existsSync(dir)) {
7140
+ fs20__default.default.mkdirSync(dir, { recursive: true });
7140
7141
  }
7141
7142
  return new Promise((resolve, reject) => {
7142
7143
  try {
7143
7144
  const doc = new PDFDocument__default.default({ margin: 50 });
7144
- const stream = fs19__default.default.createWriteStream(filePath);
7145
+ const stream = fs20__default.default.createWriteStream(filePath);
7145
7146
  doc.pipe(stream);
7146
7147
  if (title) {
7147
7148
  doc.info["Title"] = title;
@@ -7219,22 +7220,22 @@ var CodeInterpreterTool = class extends BaseTool {
7219
7220
  }
7220
7221
  cmdPrefix = NODE_CMD;
7221
7222
  }
7222
- const tmpDir = path20__default.default.join(this.workspaceRoot, ".cascade", "tmp");
7223
- if (!fs19__default.default.existsSync(tmpDir)) {
7224
- fs19__default.default.mkdirSync(tmpDir, { recursive: true });
7223
+ const tmpDir = path21__default.default.join(this.workspaceRoot, ".cascade", "tmp");
7224
+ if (!fs20__default.default.existsSync(tmpDir)) {
7225
+ fs20__default.default.mkdirSync(tmpDir, { recursive: true });
7225
7226
  }
7226
7227
  const extension = language === "python" ? "py" : "js";
7227
7228
  const fileName = `intp_${crypto3.randomUUID().slice(0, 8)}.${extension}`;
7228
- const filePath = path20__default.default.join(tmpDir, fileName);
7229
- fs19__default.default.writeFileSync(filePath, code, "utf-8");
7229
+ const filePath = path21__default.default.join(tmpDir, fileName);
7230
+ fs20__default.default.writeFileSync(filePath, code, "utf-8");
7230
7231
  const execArgs = [filePath, ...args];
7231
7232
  return new Promise((resolve) => {
7232
7233
  const startMs = Date.now();
7233
7234
  child_process.execFile(cmdPrefix, execArgs, { cwd: this.workspaceRoot, timeout: 3e4 }, (error, stdout, stderr) => {
7234
7235
  const duration = Date.now() - startMs;
7235
7236
  try {
7236
- if (fs19__default.default.existsSync(filePath)) {
7237
- fs19__default.default.unlinkSync(filePath);
7237
+ if (fs20__default.default.existsSync(filePath)) {
7238
+ fs20__default.default.unlinkSync(filePath);
7238
7239
  }
7239
7240
  } catch (cleanupErr) {
7240
7241
  console.error(`Failed to cleanup interpreter script ${filePath}:`, cleanupErr);
@@ -7513,7 +7514,7 @@ var GlobTool = class extends BaseTool {
7513
7514
  };
7514
7515
  async execute(input, _options) {
7515
7516
  const pattern = input["pattern"];
7516
- const searchPath = input["path"] ? path20__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
7517
+ const searchPath = input["path"] ? path21__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
7517
7518
  const matches = await glob.glob(pattern, {
7518
7519
  cwd: searchPath,
7519
7520
  ignore: ["node_modules/**", ".git/**", "dist/**", "build/**"],
@@ -7526,7 +7527,7 @@ var GlobTool = class extends BaseTool {
7526
7527
  const withMtime = await Promise.all(
7527
7528
  matches.map(async (rel) => {
7528
7529
  try {
7529
- const stat = await fs4__default.default.stat(path20__default.default.join(searchPath, rel));
7530
+ const stat = await fs4__default.default.stat(path21__default.default.join(searchPath, rel));
7530
7531
  return { rel, mtime: stat.mtimeMs };
7531
7532
  } catch {
7532
7533
  return { rel, mtime: 0 };
@@ -7575,7 +7576,7 @@ var GrepTool = class extends BaseTool {
7575
7576
  };
7576
7577
  async execute(input, _options) {
7577
7578
  const pattern = input["pattern"];
7578
- const searchPath = input["path"] ? path20__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
7579
+ const searchPath = input["path"] ? path21__default.default.resolve(this.workspaceRoot, input["path"]) : this.workspaceRoot;
7579
7580
  const globPattern = input["glob"];
7580
7581
  const outputMode = input["output_mode"] ?? "content";
7581
7582
  const context = input["context"] ?? 0;
@@ -7629,12 +7630,12 @@ var GrepTool = class extends BaseTool {
7629
7630
  nodir: true
7630
7631
  });
7631
7632
  } catch {
7632
- files = [path20__default.default.relative(searchPath, searchPath) || "."];
7633
+ files = [path21__default.default.relative(searchPath, searchPath) || "."];
7633
7634
  }
7634
7635
  const results = [];
7635
7636
  let totalCount = 0;
7636
7637
  for (const rel of files) {
7637
- const abs = path20__default.default.join(searchPath, rel);
7638
+ const abs = path21__default.default.join(searchPath, rel);
7638
7639
  let content;
7639
7640
  try {
7640
7641
  content = await fs4__default.default.readFile(abs, "utf-8");
@@ -7996,10 +7997,10 @@ var ToolRegistry = class extends EventEmitter__default.default {
7996
7997
  }
7997
7998
  isIgnored(filePath) {
7998
7999
  if (!filePath) return false;
7999
- const abs = path20__default.default.resolve(this.workspaceRoot, filePath);
8000
- const rel = path20__default.default.relative(this.workspaceRoot, abs);
8001
- if (!rel || rel.startsWith("..") || path20__default.default.isAbsolute(rel)) return true;
8002
- const posixRel = rel.split(path20__default.default.sep).join("/");
8000
+ const abs = path21__default.default.resolve(this.workspaceRoot, filePath);
8001
+ const rel = path21__default.default.relative(this.workspaceRoot, abs);
8002
+ if (!rel || rel.startsWith("..") || path21__default.default.isAbsolute(rel)) return true;
8003
+ const posixRel = rel.split(path21__default.default.sep).join("/");
8003
8004
  return this.ignoreMatcher.ignores(posixRel);
8004
8005
  }
8005
8006
  };
@@ -8833,7 +8834,7 @@ var TaskAnalyzer = class {
8833
8834
  analysisCache.clear();
8834
8835
  }
8835
8836
  };
8836
- var DEFAULT_STATS_FILE = path20__default.default.join(os6__default.default.homedir(), ".cascade", "model-perf.json");
8837
+ var DEFAULT_STATS_FILE = path21__default.default.join(os6__default.default.homedir(), ".cascade", "model-perf.json");
8837
8838
  var ModelPerformanceTracker = class {
8838
8839
  stats = /* @__PURE__ */ new Map();
8839
8840
  featureStats = /* @__PURE__ */ new Map();
@@ -8865,7 +8866,7 @@ var ModelPerformanceTracker = class {
8865
8866
  }
8866
8867
  async save() {
8867
8868
  try {
8868
- await fs4__default.default.mkdir(path20__default.default.dirname(this.statsFile), { recursive: true });
8869
+ await fs4__default.default.mkdir(path21__default.default.dirname(this.statsFile), { recursive: true });
8869
8870
  const modelsObj = {};
8870
8871
  const featuresObj = {};
8871
8872
  for (const [key, stat] of this.stats) modelsObj[key] = stat;
@@ -9404,7 +9405,7 @@ Required capability: ${description.slice(0, 300)}`;
9404
9405
  * any dangerous action, so a silently-reloaded tool can't act without approval. */
9405
9406
  async loadPersistedTools() {
9406
9407
  if (!this.workspacePath || !this.persistEnabled) return;
9407
- const file = path20__default.default.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
9408
+ const file = path21__default.default.join(this.workspacePath, ".cascade", DYNAMIC_TOOLS_FILE);
9408
9409
  try {
9409
9410
  const raw = await fs4__default.default.readFile(file, "utf-8");
9410
9411
  const specs = JSON.parse(raw);
@@ -9428,8 +9429,8 @@ Required capability: ${description.slice(0, 300)}`;
9428
9429
  }
9429
9430
  async persist() {
9430
9431
  if (!this.workspacePath || !this.persistEnabled) return;
9431
- const dir = path20__default.default.join(this.workspacePath, ".cascade");
9432
- const file = path20__default.default.join(dir, DYNAMIC_TOOLS_FILE);
9432
+ const dir = path21__default.default.join(this.workspacePath, ".cascade");
9433
+ const file = path21__default.default.join(dir, DYNAMIC_TOOLS_FILE);
9433
9434
  try {
9434
9435
  await fs4__default.default.mkdir(dir, { recursive: true });
9435
9436
  await fs4__default.default.writeFile(file, JSON.stringify(Array.from(this.specs.values()), null, 2), "utf-8");
@@ -9449,12 +9450,12 @@ var WorldStateDB = class {
9449
9450
  constructor(workspacePath, debugMode = false) {
9450
9451
  this.workspacePath = workspacePath;
9451
9452
  this.debugMode = debugMode;
9452
- const cascadeDir = path20__default.default.join(workspacePath, ".cascade");
9453
- if (!fs19__default.default.existsSync(cascadeDir)) {
9454
- fs19__default.default.mkdirSync(cascadeDir, { recursive: true });
9453
+ const cascadeDir = path21__default.default.join(workspacePath, ".cascade");
9454
+ if (!fs20__default.default.existsSync(cascadeDir)) {
9455
+ fs20__default.default.mkdirSync(cascadeDir, { recursive: true });
9455
9456
  }
9456
- this.keyPath = path20__default.default.join(cascadeDir, "world_state.key");
9457
- this.dbPath = path20__default.default.join(cascadeDir, "world_state.db");
9457
+ this.keyPath = path21__default.default.join(cascadeDir, "world_state.key");
9458
+ this.dbPath = path21__default.default.join(cascadeDir, "world_state.db");
9458
9459
  this.initEncryptionKey();
9459
9460
  this.db = new Database__default.default(this.dbPath);
9460
9461
  this.db.pragma("journal_mode = WAL");
@@ -9484,11 +9485,11 @@ var WorldStateDB = class {
9484
9485
  dbPath;
9485
9486
  encryptionKey;
9486
9487
  initEncryptionKey() {
9487
- if (fs19__default.default.existsSync(this.keyPath)) {
9488
- this.encryptionKey = fs19__default.default.readFileSync(this.keyPath);
9488
+ if (fs20__default.default.existsSync(this.keyPath)) {
9489
+ this.encryptionKey = fs20__default.default.readFileSync(this.keyPath);
9489
9490
  } else {
9490
9491
  this.encryptionKey = crypto3__default.default.randomBytes(32);
9491
- fs19__default.default.writeFileSync(this.keyPath, this.encryptionKey);
9492
+ fs20__default.default.writeFileSync(this.keyPath, this.encryptionKey);
9492
9493
  }
9493
9494
  }
9494
9495
  encrypt(text) {
@@ -9629,6 +9630,22 @@ var WorldStateDB = class {
9629
9630
  }
9630
9631
  return selected.slice(0, limit).map((f) => `- ${f.entity} ${f.relation} ${f.value}`).join("\n");
9631
9632
  }
9633
+ /**
9634
+ * Delete one fact by its (normalized) entity + relation key. Returns whether
9635
+ * a row was actually removed. Powers the desktop Knowledge tab's per-fact
9636
+ * delete — users can prune what the planner remembers about their project.
9637
+ */
9638
+ deleteFact(entity, relation) {
9639
+ const e = normalizeKey(entity);
9640
+ const r = normalizeKey(relation);
9641
+ if (!e || !r) return false;
9642
+ const result = this.db.prepare("DELETE FROM facts WHERE entity = ? AND relation = ?").run(e, r);
9643
+ return result.changes > 0;
9644
+ }
9645
+ /** Delete every fact. Returns how many were removed. */
9646
+ clearFacts() {
9647
+ return this.db.prepare("DELETE FROM facts").run().changes;
9648
+ }
9632
9649
  // ── Export / Import ──────────────────────────
9633
9650
  //
9634
9651
  // Knowledge travels DECRYPTED in the export bundle (a portable plaintext
@@ -9677,9 +9694,9 @@ var WorldStateDB = class {
9677
9694
  dumpDebugIfNeeded() {
9678
9695
  if (!this.debugMode) return;
9679
9696
  try {
9680
- const dumpPath = path20__default.default.join(os6__default.default.tmpdir(), "cascade_world_state_debug.json");
9697
+ const dumpPath = path21__default.default.join(os6__default.default.tmpdir(), "cascade_world_state_debug.json");
9681
9698
  const entries = this.getAllEntries();
9682
- fs19__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
9699
+ fs20__default.default.writeFileSync(dumpPath, JSON.stringify(entries, null, 2), "utf-8");
9683
9700
  } catch (err) {
9684
9701
  console.error("Failed to dump debug world state", err);
9685
9702
  }
@@ -10589,7 +10606,7 @@ var Keystore = class {
10589
10606
  const creds = await this.keytar.findCredentials(KEYTAR_SERVICE);
10590
10607
  this.cache = Object.fromEntries(creds.map((c) => [c.account, c.password]));
10591
10608
  this.backend = "keytar";
10592
- if (password && fs19__default.default.existsSync(this.storePath)) {
10609
+ if (password && fs20__default.default.existsSync(this.storePath)) {
10593
10610
  try {
10594
10611
  const fileEntries = this.decryptFile(password);
10595
10612
  for (const [k, v] of Object.entries(fileEntries)) {
@@ -10608,7 +10625,7 @@ var Keystore = class {
10608
10625
  "Keystore unlock requires a password because the OS keychain (keytar) is not available on this system."
10609
10626
  );
10610
10627
  }
10611
- if (!fs19__default.default.existsSync(this.storePath)) {
10628
+ if (!fs20__default.default.existsSync(this.storePath)) {
10612
10629
  const salt = crypto3__default.default.randomBytes(SALT_LEN);
10613
10630
  this.masterKey = this.deriveKey(password, salt);
10614
10631
  this.writeWithSalt({}, salt);
@@ -10622,7 +10639,7 @@ var Keystore = class {
10622
10639
  }
10623
10640
  /** Synchronous legacy unlock kept for AES-only environments. */
10624
10641
  unlockSync(password) {
10625
- if (!fs19__default.default.existsSync(this.storePath)) {
10642
+ if (!fs20__default.default.existsSync(this.storePath)) {
10626
10643
  const salt = crypto3__default.default.randomBytes(SALT_LEN);
10627
10644
  this.masterKey = this.deriveKey(password, salt);
10628
10645
  this.writeWithSalt({}, salt);
@@ -10680,7 +10697,7 @@ var Keystore = class {
10680
10697
  }
10681
10698
  }
10682
10699
  decryptFile(password, knownSalt) {
10683
- if (!fs19__default.default.existsSync(this.storePath)) return {};
10700
+ if (!fs20__default.default.existsSync(this.storePath)) return {};
10684
10701
  try {
10685
10702
  const { salt, ciphertext, iv, tag } = this.readRaw();
10686
10703
  const useSalt = knownSalt ?? salt;
@@ -10702,8 +10719,8 @@ var Keystore = class {
10702
10719
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
10703
10720
  const tag = cipher.getAuthTag();
10704
10721
  const out = Buffer.concat([raw.salt, iv, tag, ciphertext]);
10705
- fs19__default.default.mkdirSync(path20__default.default.dirname(this.storePath), { recursive: true });
10706
- fs19__default.default.writeFileSync(this.storePath, out, { mode: 384 });
10722
+ fs20__default.default.mkdirSync(path21__default.default.dirname(this.storePath), { recursive: true });
10723
+ fs20__default.default.writeFileSync(this.storePath, out, { mode: 384 });
10707
10724
  }
10708
10725
  writeWithSalt(data, salt) {
10709
10726
  if (!this.masterKey) throw new Error("writeWithSalt called before masterKey was set");
@@ -10713,11 +10730,11 @@ var Keystore = class {
10713
10730
  const ciphertext = Buffer.concat([cipher.update(plaintext), cipher.final()]);
10714
10731
  const tag = cipher.getAuthTag();
10715
10732
  const out = Buffer.concat([salt, iv, tag, ciphertext]);
10716
- fs19__default.default.mkdirSync(path20__default.default.dirname(this.storePath), { recursive: true });
10717
- fs19__default.default.writeFileSync(this.storePath, out, { mode: 384 });
10733
+ fs20__default.default.mkdirSync(path21__default.default.dirname(this.storePath), { recursive: true });
10734
+ fs20__default.default.writeFileSync(this.storePath, out, { mode: 384 });
10718
10735
  }
10719
10736
  readRaw() {
10720
- const buf = fs19__default.default.readFileSync(this.storePath);
10737
+ const buf = fs20__default.default.readFileSync(this.storePath);
10721
10738
  let offset = 0;
10722
10739
  const salt = buf.subarray(offset, offset + SALT_LEN);
10723
10740
  offset += SALT_LEN;
@@ -10750,7 +10767,7 @@ var CascadeIgnore = class {
10750
10767
  ]);
10751
10768
  }
10752
10769
  async load(workspacePath) {
10753
- const filePath = path20__default.default.join(workspacePath, ".cascadeignore");
10770
+ const filePath = path21__default.default.join(workspacePath, ".cascadeignore");
10754
10771
  try {
10755
10772
  const content = await fs4__default.default.readFile(filePath, "utf-8");
10756
10773
  const lines = content.split("\n").filter((l) => l.trim() && !l.startsWith("#"));
@@ -10761,7 +10778,7 @@ var CascadeIgnore = class {
10761
10778
  }
10762
10779
  isIgnored(filePath, workspacePath) {
10763
10780
  try {
10764
- const relative = workspacePath ? path20__default.default.relative(workspacePath, filePath) : filePath;
10781
+ const relative = workspacePath ? path21__default.default.relative(workspacePath, filePath) : filePath;
10765
10782
  return this.ig.ignores(relative);
10766
10783
  } catch {
10767
10784
  return false;
@@ -10772,7 +10789,7 @@ var CascadeIgnore = class {
10772
10789
  }
10773
10790
  };
10774
10791
  async function loadCascadeMd(workspacePath) {
10775
- const filePath = path20__default.default.join(workspacePath, "CASCADE.md");
10792
+ const filePath = path21__default.default.join(workspacePath, "CASCADE.md");
10776
10793
  try {
10777
10794
  const raw = await fs4__default.default.readFile(filePath, "utf-8");
10778
10795
  return parseCascadeMd(raw);
@@ -10803,7 +10820,7 @@ ${raw.trim()}`;
10803
10820
  var MemoryStore = class _MemoryStore {
10804
10821
  db;
10805
10822
  constructor(dbPath) {
10806
- fs19__default.default.mkdirSync(path20__default.default.dirname(dbPath), { recursive: true });
10823
+ fs20__default.default.mkdirSync(path21__default.default.dirname(dbPath), { recursive: true });
10807
10824
  try {
10808
10825
  this.db = new Database__default.default(dbPath, { timeout: 5e3 });
10809
10826
  this.db.pragma("journal_mode = WAL");
@@ -11638,6 +11655,60 @@ Original error: ${err.message}`
11638
11655
  };
11639
11656
  }
11640
11657
  };
11658
+ function credentialsPath(globalDir) {
11659
+ return path21__default.default.join(globalDir, GLOBAL_CREDENTIALS_FILE);
11660
+ }
11661
+ function providerKey(p) {
11662
+ if (p.type === "azure") {
11663
+ return `azure:${p.deploymentName ?? p.baseUrl ?? p.label ?? ""}`;
11664
+ }
11665
+ return p.type;
11666
+ }
11667
+ function isPersistable(p) {
11668
+ return Boolean(p.apiKey || p.authToken || p.type === "azure" || p.baseUrl);
11669
+ }
11670
+ function loadGlobalCredentials(globalDir) {
11671
+ try {
11672
+ const raw = fs20__default.default.readFileSync(credentialsPath(globalDir), "utf-8");
11673
+ const parsed = JSON.parse(raw);
11674
+ if (!Array.isArray(parsed.providers)) return [];
11675
+ return parsed.providers.filter(
11676
+ (p) => Boolean(p) && typeof p.type === "string"
11677
+ );
11678
+ } catch {
11679
+ return [];
11680
+ }
11681
+ }
11682
+ function saveGlobalCredentials(globalDir, providers) {
11683
+ const filePath = credentialsPath(globalDir);
11684
+ fs20__default.default.mkdirSync(globalDir, { recursive: true, mode: 448 });
11685
+ const body = { version: 1, providers: providers.filter(isPersistable) };
11686
+ const tmp = `${filePath}.tmp`;
11687
+ fs20__default.default.writeFileSync(tmp, JSON.stringify(body, null, 2), { encoding: "utf-8", mode: 384 });
11688
+ fs20__default.default.renameSync(tmp, filePath);
11689
+ try {
11690
+ fs20__default.default.chmodSync(filePath, 384);
11691
+ } catch {
11692
+ }
11693
+ }
11694
+ function mergeGlobalCredentials(workspaceProviders, globalProviders) {
11695
+ const merged = [...workspaceProviders];
11696
+ const byKey = new Map(merged.map((p) => [providerKey(p), p]));
11697
+ for (const g of globalProviders) {
11698
+ const existing = byKey.get(providerKey(g));
11699
+ if (!existing) {
11700
+ merged.push({ ...g });
11701
+ byKey.set(providerKey(g), merged[merged.length - 1]);
11702
+ continue;
11703
+ }
11704
+ if (!existing.apiKey && g.apiKey) existing.apiKey = g.apiKey;
11705
+ if (!existing.authToken && g.authToken) existing.authToken = g.authToken;
11706
+ if (!existing.baseUrl && g.baseUrl) existing.baseUrl = g.baseUrl;
11707
+ if (!existing.apiVersion && g.apiVersion) existing.apiVersion = g.apiVersion;
11708
+ if (!existing.label && g.label) existing.label = g.label;
11709
+ }
11710
+ return merged;
11711
+ }
11641
11712
 
11642
11713
  // src/config/index.ts
11643
11714
  var ConfigManager = class {
@@ -11648,18 +11719,20 @@ var ConfigManager = class {
11648
11719
  cascadeMd = null;
11649
11720
  workspacePath;
11650
11721
  globalDir;
11651
- constructor(workspacePath = process.cwd()) {
11722
+ /** `globalDirOverride` exists for tests — never point it at the real home dir there. */
11723
+ constructor(workspacePath = process.cwd(), globalDirOverride) {
11652
11724
  this.workspacePath = workspacePath;
11653
- this.globalDir = path20__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR);
11725
+ this.globalDir = globalDirOverride ?? path21__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR);
11654
11726
  }
11655
11727
  async load() {
11656
11728
  this.config = await this.loadConfig();
11657
11729
  this.ignore = new CascadeIgnore();
11658
11730
  await this.ignore.load(this.workspacePath);
11659
11731
  this.cascadeMd = await loadCascadeMd(this.workspacePath);
11660
- this.keystore = new Keystore(path20__default.default.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
11661
- this.store = new MemoryStore(path20__default.default.join(this.workspacePath, CASCADE_DB_FILE));
11732
+ this.keystore = new Keystore(path21__default.default.join(this.globalDir, GLOBAL_KEYSTORE_FILE));
11733
+ this.store = new MemoryStore(path21__default.default.join(this.workspacePath, CASCADE_DB_FILE));
11662
11734
  await this.injectEnvKeys();
11735
+ this.config.providers = mergeGlobalCredentials(this.config.providers, loadGlobalCredentials(this.globalDir));
11663
11736
  await this.ensureDefaultIdentity();
11664
11737
  }
11665
11738
  getConfig() {
@@ -11681,9 +11754,14 @@ var ConfigManager = class {
11681
11754
  return this.workspacePath;
11682
11755
  }
11683
11756
  async save() {
11684
- const configPath = path20__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
11685
- await fs4__default.default.mkdir(path20__default.default.dirname(configPath), { recursive: true });
11757
+ const configPath = path21__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
11758
+ await fs4__default.default.mkdir(path21__default.default.dirname(configPath), { recursive: true });
11686
11759
  await fs4__default.default.writeFile(configPath, JSON.stringify(this.config, null, 2), "utf-8");
11760
+ try {
11761
+ saveGlobalCredentials(this.globalDir, this.config.providers);
11762
+ } catch (err) {
11763
+ console.warn(`Failed to sync credentials to global store: ${err instanceof Error ? err.message : String(err)}`);
11764
+ }
11687
11765
  }
11688
11766
  async updateConfig(updates) {
11689
11767
  this.config = validateConfig({ ...this.config, ...updates });
@@ -11706,7 +11784,7 @@ var ConfigManager = class {
11706
11784
  return configProvider?.apiKey;
11707
11785
  }
11708
11786
  async loadConfig() {
11709
- const configPath = path20__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
11787
+ const configPath = path21__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
11710
11788
  try {
11711
11789
  const raw = await fs4__default.default.readFile(configPath, "utf-8");
11712
11790
  return validateConfig(JSON.parse(raw));
@@ -11813,6 +11891,11 @@ function authMiddleware(secret, required = true) {
11813
11891
  }
11814
11892
  const user = verifyToken(token, secret);
11815
11893
  if (!user) {
11894
+ if (!required) {
11895
+ req.user = void 0;
11896
+ next();
11897
+ return;
11898
+ }
11816
11899
  res.status(401).json({ error: "Invalid or expired token" });
11817
11900
  return;
11818
11901
  }
@@ -12121,7 +12204,7 @@ function aggregateCostStats(sessions, opts = {}) {
12121
12204
  }
12122
12205
 
12123
12206
  // src/dashboard/server.ts
12124
- var __dirname$1 = path20__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
12207
+ var __dirname$1 = path21__default.default.dirname(url.fileURLToPath((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href))));
12125
12208
  var DashboardServer = class {
12126
12209
  app;
12127
12210
  httpServer;
@@ -12355,12 +12438,17 @@ var DashboardServer = class {
12355
12438
  */
12356
12439
  persistConfig() {
12357
12440
  try {
12358
- const configPath = path20__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
12359
- fs19__default.default.mkdirSync(path20__default.default.dirname(configPath), { recursive: true });
12360
- fs19__default.default.writeFileSync(configPath, JSON.stringify(this.config, null, 2), "utf-8");
12441
+ const configPath = path21__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
12442
+ fs20__default.default.mkdirSync(path21__default.default.dirname(configPath), { recursive: true });
12443
+ fs20__default.default.writeFileSync(configPath, JSON.stringify(this.config, null, 2), "utf-8");
12361
12444
  } catch (err) {
12362
12445
  console.warn(`[dashboard] Failed to persist config: ${err instanceof Error ? err.message : String(err)}`);
12363
12446
  }
12447
+ try {
12448
+ saveGlobalCredentials(path21__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR), this.config.providers ?? []);
12449
+ } catch (err) {
12450
+ console.warn(`[dashboard] Failed to sync global credentials: ${err instanceof Error ? err.message : String(err)}`);
12451
+ }
12364
12452
  }
12365
12453
  /**
12366
12454
  * Produce a stable dashboard JWT signing secret.
@@ -12372,15 +12460,15 @@ var DashboardServer = class {
12372
12460
  resolveDashboardSecret() {
12373
12461
  const fromConfig = this.config.dashboard.secret ?? process.env["CASCADE_DASHBOARD_SECRET"];
12374
12462
  if (fromConfig) return fromConfig;
12375
- const secretPath = path20__default.default.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
12463
+ const secretPath = path21__default.default.join(this.workspacePath, CASCADE_DASHBOARD_SECRET_FILE);
12376
12464
  try {
12377
- if (fs19__default.default.existsSync(secretPath)) {
12378
- const existing = fs19__default.default.readFileSync(secretPath, "utf-8").trim();
12465
+ if (fs20__default.default.existsSync(secretPath)) {
12466
+ const existing = fs20__default.default.readFileSync(secretPath, "utf-8").trim();
12379
12467
  if (existing.length >= 16) return existing;
12380
12468
  }
12381
12469
  const generated = crypto3.randomUUID();
12382
- fs19__default.default.mkdirSync(path20__default.default.dirname(secretPath), { recursive: true });
12383
- fs19__default.default.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
12470
+ fs20__default.default.mkdirSync(path21__default.default.dirname(secretPath), { recursive: true });
12471
+ fs20__default.default.writeFileSync(secretPath, generated, { encoding: "utf-8", mode: 384 });
12384
12472
  if (this.config.dashboard.auth) {
12385
12473
  console.warn(
12386
12474
  `Dashboard auth enabled with no secret configured; persisted a generated secret to ${secretPath}. Set CASCADE_DASHBOARD_SECRET or config.dashboard.secret to override.`
@@ -12407,7 +12495,7 @@ var DashboardServer = class {
12407
12495
  // ── Setup ─────────────────────────────────────
12408
12496
  getGlobalStore() {
12409
12497
  if (!this.globalStore) {
12410
- const globalDbPath = path20__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
12498
+ const globalDbPath = path21__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
12411
12499
  this.globalStore = new MemoryStore(globalDbPath);
12412
12500
  }
12413
12501
  return this.globalStore;
@@ -12676,12 +12764,12 @@ ${prompt}`;
12676
12764
  }
12677
12765
  }
12678
12766
  watchRuntimeChanges() {
12679
- const workspaceDbPath = path20__default.default.join(this.workspacePath, CASCADE_DB_FILE);
12680
- const globalDbPath = path20__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
12767
+ const workspaceDbPath = path21__default.default.join(this.workspacePath, CASCADE_DB_FILE);
12768
+ const globalDbPath = path21__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
12681
12769
  const watchPaths = [workspaceDbPath, globalDbPath].filter((p, index, arr) => arr.indexOf(p) === index);
12682
12770
  for (const watchPath of watchPaths) {
12683
- if (!fs19__default.default.existsSync(watchPath)) continue;
12684
- fs19__default.default.watchFile(watchPath, { interval: 3e3 }, () => {
12771
+ if (!fs20__default.default.existsSync(watchPath)) continue;
12772
+ fs20__default.default.watchFile(watchPath, { interval: 3e3 }, () => {
12685
12773
  this.throttledBroadcast(watchPath === globalDbPath ? "global" : "workspace");
12686
12774
  });
12687
12775
  }
@@ -12803,7 +12891,7 @@ ${prompt}`;
12803
12891
  const sessionId = req.params.id;
12804
12892
  this.store.deleteSession(sessionId);
12805
12893
  this.store.deleteRuntimeSession(sessionId);
12806
- const globalDbPath = path20__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
12894
+ const globalDbPath = path21__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
12807
12895
  const globalStore = new MemoryStore(globalDbPath);
12808
12896
  try {
12809
12897
  globalStore.deleteRuntimeSession(sessionId);
@@ -12896,7 +12984,7 @@ ${prompt}`;
12896
12984
  });
12897
12985
  this.app.delete("/api/sessions", auth, (req, res) => {
12898
12986
  const body = req.body;
12899
- const globalDbPath = path20__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
12987
+ const globalDbPath = path21__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
12900
12988
  if (body?.ids && Array.isArray(body.ids) && body.ids.length > 0) {
12901
12989
  const globalStore = new MemoryStore(globalDbPath);
12902
12990
  try {
@@ -12919,7 +13007,7 @@ ${prompt}`;
12919
13007
  });
12920
13008
  this.app.delete("/api/runtime", auth, (_req, res) => {
12921
13009
  this.store.deleteAllRuntimeNodes();
12922
- const globalDbPath = path20__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
13010
+ const globalDbPath = path21__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
12923
13011
  const globalStore = new MemoryStore(globalDbPath);
12924
13012
  try {
12925
13013
  globalStore.deleteAllRuntimeNodes();
@@ -13005,12 +13093,12 @@ ${prompt}`;
13005
13093
  if (body["tierLimits"]) this.config.tierLimits = { ...this.config.tierLimits, ...body["tierLimits"] };
13006
13094
  if (body["budget"]) this.config.budget = { ...this.config.budget, ...body["budget"] };
13007
13095
  try {
13008
- const configPath = path20__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
13009
- const existing = fs19__default.default.existsSync(configPath) ? JSON.parse(fs19__default.default.readFileSync(configPath, "utf-8")) : {};
13096
+ const configPath = path21__default.default.join(this.workspacePath, CASCADE_CONFIG_FILE);
13097
+ const existing = fs20__default.default.existsSync(configPath) ? JSON.parse(fs20__default.default.readFileSync(configPath, "utf-8")) : {};
13010
13098
  const updated = { ...existing, tierLimits: this.config.tierLimits, budget: this.config.budget };
13011
13099
  const tmp = configPath + ".tmp";
13012
- fs19__default.default.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
13013
- fs19__default.default.renameSync(tmp, configPath);
13100
+ fs20__default.default.writeFileSync(tmp, JSON.stringify(updated, null, 2), "utf-8");
13101
+ fs20__default.default.renameSync(tmp, configPath);
13014
13102
  res.json({ ok: true });
13015
13103
  } catch (err) {
13016
13104
  res.status(500).json({ error: `Failed to save config: ${err instanceof Error ? err.message : String(err)}` });
@@ -13038,7 +13126,7 @@ ${prompt}`;
13038
13126
  this.app.get("/api/runtime", auth, (req, res) => {
13039
13127
  const scope = req.query["scope"] ?? "workspace";
13040
13128
  if (scope === "global") {
13041
- const globalDbPath = path20__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
13129
+ const globalDbPath = path21__default.default.join(os6__default.default.homedir(), GLOBAL_CONFIG_DIR, GLOBAL_RUNTIME_DB_FILE);
13042
13130
  const globalStore = new MemoryStore(globalDbPath);
13043
13131
  try {
13044
13132
  res.json({
@@ -13273,6 +13361,48 @@ ${prompt}`;
13273
13361
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
13274
13362
  }
13275
13363
  });
13364
+ this.app.get("/api/knowledge", auth, (_req, res) => {
13365
+ try {
13366
+ const ws = new WorldStateDB(this.workspacePath);
13367
+ try {
13368
+ const facts = ws.getAllFacts();
13369
+ res.json({ total: facts.length, facts });
13370
+ } finally {
13371
+ ws.close();
13372
+ }
13373
+ } catch (err) {
13374
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
13375
+ }
13376
+ });
13377
+ this.app.delete("/api/knowledge/fact", auth, mutationLimiter, (req, res) => {
13378
+ const body = req.body;
13379
+ if (!body.entity || !body.relation) {
13380
+ res.status(400).json({ error: "entity and relation are required" });
13381
+ return;
13382
+ }
13383
+ try {
13384
+ const ws = new WorldStateDB(this.workspacePath);
13385
+ try {
13386
+ res.json({ ok: true, deleted: ws.deleteFact(body.entity, body.relation) });
13387
+ } finally {
13388
+ ws.close();
13389
+ }
13390
+ } catch (err) {
13391
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
13392
+ }
13393
+ });
13394
+ this.app.delete("/api/knowledge", auth, mutationLimiter, (_req, res) => {
13395
+ try {
13396
+ const ws = new WorldStateDB(this.workspacePath);
13397
+ try {
13398
+ res.json({ ok: true, deleted: ws.clearFacts() });
13399
+ } finally {
13400
+ ws.close();
13401
+ }
13402
+ } catch (err) {
13403
+ res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
13404
+ }
13405
+ });
13276
13406
  this.app.get("/api/audit-chain", auth, async (req, res) => {
13277
13407
  try {
13278
13408
  const limit = Math.min(500, Math.max(1, parseInt(req.query["limit"] || "200", 10) || 200));
@@ -13291,13 +13421,13 @@ ${prompt}`;
13291
13421
  res.status(500).json({ error: err instanceof Error ? err.message : String(err) });
13292
13422
  }
13293
13423
  });
13294
- const prodPath = path20__default.default.resolve(__dirname$1, "../web/dist");
13295
- const devPath = path20__default.default.resolve(__dirname$1, "../../web/dist");
13296
- const webDistPath = fs19__default.default.existsSync(prodPath) ? prodPath : devPath;
13297
- if (fs19__default.default.existsSync(webDistPath)) {
13424
+ const prodPath = path21__default.default.resolve(__dirname$1, "../web/dist");
13425
+ const devPath = path21__default.default.resolve(__dirname$1, "../../web/dist");
13426
+ const webDistPath = fs20__default.default.existsSync(prodPath) ? prodPath : devPath;
13427
+ if (fs20__default.default.existsSync(webDistPath)) {
13298
13428
  this.app.use(express__default.default.static(webDistPath));
13299
13429
  this.app.get("*", (_req, res) => {
13300
- res.sendFile(path20__default.default.join(webDistPath, "index.html"));
13430
+ res.sendFile(path21__default.default.join(webDistPath, "index.html"));
13301
13431
  });
13302
13432
  } else {
13303
13433
  this.app.get("/", (_req, res) => {
@@ -13398,6 +13528,7 @@ exports.DEFAULT_RETENTION_DAYS = DEFAULT_RETENTION_DAYS;
13398
13528
  exports.DEFAULT_THEME = DEFAULT_THEME;
13399
13529
  exports.DashboardServer = DashboardServer;
13400
13530
  exports.GLOBAL_CONFIG_DIR = GLOBAL_CONFIG_DIR;
13531
+ exports.GLOBAL_CREDENTIALS_FILE = GLOBAL_CREDENTIALS_FILE;
13401
13532
  exports.GLOBAL_DB_FILE = GLOBAL_DB_FILE;
13402
13533
  exports.GLOBAL_KEYSTORE_FILE = GLOBAL_KEYSTORE_FILE;
13403
13534
  exports.GLOBAL_RUNTIME_DB_FILE = GLOBAL_RUNTIME_DB_FILE;