@lydia-agent/core 0.1.0 → 0.1.1
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 +129 -118
- package/dist/index.js +129 -118
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -590,17 +590,20 @@ var StrategyRegistry = class {
|
|
|
590
590
|
|
|
591
591
|
// src/strategy/approval-service.ts
|
|
592
592
|
import * as os2 from "os";
|
|
593
|
-
import * as
|
|
593
|
+
import * as path3 from "path";
|
|
594
594
|
|
|
595
595
|
// src/memory/manager.ts
|
|
596
596
|
import Database from "better-sqlite3";
|
|
597
597
|
import { EventEmitter } from "events";
|
|
598
|
+
import * as fs2 from "fs";
|
|
599
|
+
import * as path2 from "path";
|
|
598
600
|
var MemoryManager = class extends EventEmitter {
|
|
599
601
|
db;
|
|
600
602
|
checkpointTtlMs;
|
|
601
603
|
observationFrameTtlMs;
|
|
602
604
|
constructor(dbPath, options = {}) {
|
|
603
605
|
super();
|
|
606
|
+
fs2.mkdirSync(path2.dirname(dbPath), { recursive: true });
|
|
604
607
|
this.db = new Database(dbPath);
|
|
605
608
|
this.checkpointTtlMs = options.checkpointTtlMs ?? 24 * 60 * 60 * 1e3;
|
|
606
609
|
this.observationFrameTtlMs = options.observationFrameTtlMs ?? 24 * 7 * 60 * 60 * 1e3;
|
|
@@ -1484,7 +1487,7 @@ var StrategyApprovalService = class {
|
|
|
1484
1487
|
if (memoryManager) {
|
|
1485
1488
|
this.memory = memoryManager;
|
|
1486
1489
|
} else {
|
|
1487
|
-
const dbPath =
|
|
1490
|
+
const dbPath = path3.join(os2.homedir(), ".lydia", "memory.db");
|
|
1488
1491
|
this.memory = new MemoryManager(dbPath);
|
|
1489
1492
|
}
|
|
1490
1493
|
this.configLoader = configLoader || new ConfigLoader();
|
|
@@ -1545,7 +1548,7 @@ var StrategyApprovalService = class {
|
|
|
1545
1548
|
};
|
|
1546
1549
|
|
|
1547
1550
|
// src/strategy/shadow-router.ts
|
|
1548
|
-
import * as
|
|
1551
|
+
import * as path4 from "path";
|
|
1549
1552
|
import * as os3 from "os";
|
|
1550
1553
|
var ShadowRouter = class {
|
|
1551
1554
|
memory;
|
|
@@ -1555,7 +1558,7 @@ var ShadowRouter = class {
|
|
|
1555
1558
|
if (memoryManager) {
|
|
1556
1559
|
this.memory = memoryManager;
|
|
1557
1560
|
} else {
|
|
1558
|
-
const dbPath =
|
|
1561
|
+
const dbPath = path4.join(os3.homedir(), ".lydia", "memory.db");
|
|
1559
1562
|
this.memory = new MemoryManager(dbPath);
|
|
1560
1563
|
}
|
|
1561
1564
|
this.registry = strategyRegistry || new StrategyRegistry();
|
|
@@ -1826,39 +1829,39 @@ var StrategyReviewer = class {
|
|
|
1826
1829
|
};
|
|
1827
1830
|
|
|
1828
1831
|
// src/strategy/branch-manager.ts
|
|
1829
|
-
import * as
|
|
1830
|
-
import * as
|
|
1832
|
+
import * as fs3 from "fs/promises";
|
|
1833
|
+
import * as path5 from "path";
|
|
1831
1834
|
import * as os4 from "os";
|
|
1832
1835
|
import { parse as parseYaml2, stringify as stringifyYaml2 } from "yaml";
|
|
1833
1836
|
var StrategyBranchManager = class {
|
|
1834
1837
|
baseDir;
|
|
1835
1838
|
constructor(baseDir) {
|
|
1836
|
-
this.baseDir = baseDir ||
|
|
1839
|
+
this.baseDir = baseDir || path5.join(os4.homedir(), ".lydia", "strategies");
|
|
1837
1840
|
}
|
|
1838
1841
|
async init() {
|
|
1839
|
-
await
|
|
1840
|
-
await
|
|
1841
|
-
await
|
|
1842
|
+
await fs3.mkdir(this.baseDir, { recursive: true });
|
|
1843
|
+
await fs3.mkdir(path5.join(this.baseDir, "branches"), { recursive: true });
|
|
1844
|
+
await fs3.mkdir(path5.join(this.baseDir, "archive"), { recursive: true });
|
|
1842
1845
|
}
|
|
1843
1846
|
async listBranches() {
|
|
1844
|
-
const branchesDir =
|
|
1847
|
+
const branchesDir = path5.join(this.baseDir, "branches");
|
|
1845
1848
|
try {
|
|
1846
|
-
const entries = await
|
|
1849
|
+
const entries = await fs3.readdir(branchesDir, { withFileTypes: true });
|
|
1847
1850
|
const branches = [];
|
|
1848
1851
|
for (const entry of entries) {
|
|
1849
1852
|
if (!entry.isDirectory()) continue;
|
|
1850
1853
|
}
|
|
1851
|
-
const files = await
|
|
1854
|
+
const files = await fs3.readdir(branchesDir);
|
|
1852
1855
|
for (const file of files) {
|
|
1853
1856
|
if (!file.endsWith(".yml")) continue;
|
|
1854
|
-
const content = await
|
|
1857
|
+
const content = await fs3.readFile(path5.join(branchesDir, file), "utf-8");
|
|
1855
1858
|
try {
|
|
1856
1859
|
const strategy = StrategySchema.parse(parseYaml2(content));
|
|
1857
1860
|
branches.push({
|
|
1858
1861
|
name: strategy.metadata.id,
|
|
1859
1862
|
// ID is used as branch name effectively
|
|
1860
1863
|
version: strategy.metadata.version,
|
|
1861
|
-
path:
|
|
1864
|
+
path: path5.join(branchesDir, file),
|
|
1862
1865
|
parent: strategy.metadata.inheritFrom,
|
|
1863
1866
|
createdAt: Date.now()
|
|
1864
1867
|
// We might want to store this in metadata later
|
|
@@ -1874,8 +1877,8 @@ var StrategyBranchManager = class {
|
|
|
1874
1877
|
}
|
|
1875
1878
|
}
|
|
1876
1879
|
async createBranch(sourceStrategy, newBranchName, modifications) {
|
|
1877
|
-
const targetDir =
|
|
1878
|
-
await
|
|
1880
|
+
const targetDir = path5.join(this.baseDir, "branches");
|
|
1881
|
+
await fs3.mkdir(targetDir, { recursive: true });
|
|
1879
1882
|
const newStrategy = {
|
|
1880
1883
|
...sourceStrategy,
|
|
1881
1884
|
...modifications,
|
|
@@ -1889,14 +1892,14 @@ var StrategyBranchManager = class {
|
|
|
1889
1892
|
...modifications.metadata
|
|
1890
1893
|
}
|
|
1891
1894
|
};
|
|
1892
|
-
const filePath =
|
|
1893
|
-
await
|
|
1895
|
+
const filePath = path5.join(targetDir, `${newBranchName}.yml`);
|
|
1896
|
+
await fs3.writeFile(filePath, stringifyYaml2(newStrategy), "utf-8");
|
|
1894
1897
|
return newStrategy;
|
|
1895
1898
|
}
|
|
1896
1899
|
async getBranch(branchName) {
|
|
1897
|
-
const filePath =
|
|
1900
|
+
const filePath = path5.join(this.baseDir, "branches", `${branchName}.yml`);
|
|
1898
1901
|
try {
|
|
1899
|
-
const content = await
|
|
1902
|
+
const content = await fs3.readFile(filePath, "utf-8");
|
|
1900
1903
|
return StrategySchema.parse(parseYaml2(content));
|
|
1901
1904
|
} catch {
|
|
1902
1905
|
return null;
|
|
@@ -1907,12 +1910,12 @@ var StrategyBranchManager = class {
|
|
|
1907
1910
|
if (!branch) throw new Error(`Branch ${branchName} not found`);
|
|
1908
1911
|
const parentId = branch.metadata.inheritFrom;
|
|
1909
1912
|
if (!parentId) throw new Error("Branch has no parent to merge into");
|
|
1910
|
-
const files = await
|
|
1913
|
+
const files = await fs3.readdir(this.baseDir);
|
|
1911
1914
|
let parentFile = null;
|
|
1912
1915
|
for (const file of files) {
|
|
1913
1916
|
if (file === "branches" || file === "archive") continue;
|
|
1914
1917
|
try {
|
|
1915
|
-
const content = await
|
|
1918
|
+
const content = await fs3.readFile(path5.join(this.baseDir, file), "utf-8");
|
|
1916
1919
|
const data = parseYaml2(content);
|
|
1917
1920
|
if (data.metadata?.id === parentId) {
|
|
1918
1921
|
parentFile = file;
|
|
@@ -1924,18 +1927,18 @@ var StrategyBranchManager = class {
|
|
|
1924
1927
|
if (!parentFile) {
|
|
1925
1928
|
throw new Error(`Parent strategy ${parentId} not found in ${this.baseDir}`);
|
|
1926
1929
|
}
|
|
1927
|
-
const parentPath =
|
|
1928
|
-
await
|
|
1930
|
+
const parentPath = path5.join(this.baseDir, parentFile);
|
|
1931
|
+
await fs3.copyFile(parentPath, path5.join(this.baseDir, "archive", `${parentId}-${Date.now()}.bak.yml`));
|
|
1929
1932
|
const start = Date.now();
|
|
1930
|
-
const branchContent = await
|
|
1931
|
-
await
|
|
1933
|
+
const branchContent = await fs3.readFile(path5.join(this.baseDir, "branches", `${branchName}.yml`), "utf-8");
|
|
1934
|
+
await fs3.writeFile(parentPath, branchContent, "utf-8");
|
|
1932
1935
|
await this.archiveBranch(branchName);
|
|
1933
1936
|
}
|
|
1934
1937
|
async archiveBranch(branchName) {
|
|
1935
|
-
const srcPath =
|
|
1936
|
-
const destPath =
|
|
1938
|
+
const srcPath = path5.join(this.baseDir, "branches", `${branchName}.yml`);
|
|
1939
|
+
const destPath = path5.join(this.baseDir, "archive", `${branchName}-${Date.now()}.yml`);
|
|
1937
1940
|
try {
|
|
1938
|
-
await
|
|
1941
|
+
await fs3.rename(srcPath, destPath);
|
|
1939
1942
|
} catch (e) {
|
|
1940
1943
|
console.error(`Failed to archive branch ${branchName}:`, e);
|
|
1941
1944
|
throw e;
|
|
@@ -2198,11 +2201,11 @@ var SkillRegistry = class {
|
|
|
2198
2201
|
};
|
|
2199
2202
|
|
|
2200
2203
|
// src/skills/loader.ts
|
|
2201
|
-
import * as
|
|
2202
|
-
import * as
|
|
2204
|
+
import * as fs4 from "fs/promises";
|
|
2205
|
+
import * as path6 from "path";
|
|
2203
2206
|
import * as os5 from "os";
|
|
2204
2207
|
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
2205
|
-
var __dirname3 =
|
|
2208
|
+
var __dirname3 = path6.dirname(fileURLToPath2(import.meta.url));
|
|
2206
2209
|
var SkillLoader = class {
|
|
2207
2210
|
registry;
|
|
2208
2211
|
/** Ordered list of directories to scan for skills */
|
|
@@ -2217,13 +2220,13 @@ var SkillLoader = class {
|
|
|
2217
2220
|
getDirectories(extraDirs = []) {
|
|
2218
2221
|
return [
|
|
2219
2222
|
// 1. Built-in skills (relative to this file in dist/skills)
|
|
2220
|
-
|
|
2223
|
+
path6.resolve(__dirname3, "../../skills"),
|
|
2221
2224
|
// 1.5 Built-in skills in source tree (dev mode)
|
|
2222
|
-
|
|
2225
|
+
path6.resolve(__dirname3, "../skills"),
|
|
2223
2226
|
// 2. User global skills
|
|
2224
|
-
|
|
2227
|
+
path6.join(os5.homedir(), ".lydia", "skills"),
|
|
2225
2228
|
// 3. Project local skills
|
|
2226
|
-
|
|
2229
|
+
path6.join(process.cwd(), ".lydia", "skills"),
|
|
2227
2230
|
// 4. Extra directories from config
|
|
2228
2231
|
...extraDirs
|
|
2229
2232
|
];
|
|
@@ -2244,11 +2247,11 @@ var SkillLoader = class {
|
|
|
2244
2247
|
*/
|
|
2245
2248
|
async loadMetadataFromDirectory(dirPath) {
|
|
2246
2249
|
try {
|
|
2247
|
-
const stats = await
|
|
2250
|
+
const stats = await fs4.stat(dirPath);
|
|
2248
2251
|
if (!stats.isDirectory()) return;
|
|
2249
|
-
const entries = await
|
|
2252
|
+
const entries = await fs4.readdir(dirPath, { withFileTypes: true });
|
|
2250
2253
|
for (const entry of entries) {
|
|
2251
|
-
const fullPath =
|
|
2254
|
+
const fullPath = path6.join(dirPath, entry.name);
|
|
2252
2255
|
if (entry.isDirectory()) {
|
|
2253
2256
|
await this.loadMetadataFromDirectory(fullPath);
|
|
2254
2257
|
} else if (entry.isFile() && entry.name.endsWith(".md")) {
|
|
@@ -2264,7 +2267,7 @@ var SkillLoader = class {
|
|
|
2264
2267
|
*/
|
|
2265
2268
|
async loadSkillMeta(filePath) {
|
|
2266
2269
|
try {
|
|
2267
|
-
const content = await
|
|
2270
|
+
const content = await fs4.readFile(filePath, "utf-8");
|
|
2268
2271
|
if (content.startsWith("---")) {
|
|
2269
2272
|
const meta = SkillParser.parseMeta(content, filePath);
|
|
2270
2273
|
this.registry.register(meta);
|
|
@@ -2285,7 +2288,7 @@ var SkillLoader = class {
|
|
|
2285
2288
|
const meta = this.registry.get(name);
|
|
2286
2289
|
if (!meta?.path) return null;
|
|
2287
2290
|
try {
|
|
2288
|
-
const fileContent = await
|
|
2291
|
+
const fileContent = await fs4.readFile(meta.path, "utf-8");
|
|
2289
2292
|
const skill = SkillParser.parse(fileContent, meta.path);
|
|
2290
2293
|
return skill.content;
|
|
2291
2294
|
} catch (error) {
|
|
@@ -2301,7 +2304,7 @@ var SkillLoader = class {
|
|
|
2301
2304
|
const meta = this.registry.get(name);
|
|
2302
2305
|
if (!meta?.path) return null;
|
|
2303
2306
|
try {
|
|
2304
|
-
const fileContent = await
|
|
2307
|
+
const fileContent = await fs4.readFile(meta.path, "utf-8");
|
|
2305
2308
|
return SkillParser.parse(fileContent, meta.path);
|
|
2306
2309
|
} catch (error) {
|
|
2307
2310
|
console.warn(`Failed to load full skill "${name}":`, error);
|
|
@@ -2314,7 +2317,7 @@ var SkillLoader = class {
|
|
|
2314
2317
|
*/
|
|
2315
2318
|
async reloadSkillMeta(filePath) {
|
|
2316
2319
|
try {
|
|
2317
|
-
const content = await
|
|
2320
|
+
const content = await fs4.readFile(filePath, "utf-8");
|
|
2318
2321
|
if (content.startsWith("---")) {
|
|
2319
2322
|
const meta = SkillParser.parseMeta(content, filePath);
|
|
2320
2323
|
this.registry.register(meta);
|
|
@@ -2329,9 +2332,9 @@ var SkillLoader = class {
|
|
|
2329
2332
|
|
|
2330
2333
|
// src/skills/watcher.ts
|
|
2331
2334
|
import { EventEmitter as EventEmitter2 } from "events";
|
|
2332
|
-
import * as
|
|
2335
|
+
import * as fs5 from "fs";
|
|
2333
2336
|
import * as fsPromises from "fs/promises";
|
|
2334
|
-
import * as
|
|
2337
|
+
import * as path7 from "path";
|
|
2335
2338
|
var SkillWatcher = class extends EventEmitter2 {
|
|
2336
2339
|
constructor(directories, registry, loader, options) {
|
|
2337
2340
|
super();
|
|
@@ -2341,7 +2344,7 @@ var SkillWatcher = class extends EventEmitter2 {
|
|
|
2341
2344
|
this.debounceMs = options?.debounceMs ?? 300;
|
|
2342
2345
|
for (const skill of this.registry.list()) {
|
|
2343
2346
|
if (skill.path) {
|
|
2344
|
-
this.pathToName.set(
|
|
2347
|
+
this.pathToName.set(path7.resolve(skill.path), skill.name);
|
|
2345
2348
|
}
|
|
2346
2349
|
}
|
|
2347
2350
|
}
|
|
@@ -2380,12 +2383,12 @@ var SkillWatcher = class extends EventEmitter2 {
|
|
|
2380
2383
|
}
|
|
2381
2384
|
watchDirectory(dirPath) {
|
|
2382
2385
|
try {
|
|
2383
|
-
if (!
|
|
2386
|
+
if (!fs5.existsSync(dirPath) || !fs5.statSync(dirPath).isDirectory()) {
|
|
2384
2387
|
return;
|
|
2385
2388
|
}
|
|
2386
|
-
const watcher =
|
|
2389
|
+
const watcher = fs5.watch(dirPath, { recursive: true }, (eventType, filename) => {
|
|
2387
2390
|
if (!filename) return;
|
|
2388
|
-
const fullPath =
|
|
2391
|
+
const fullPath = path7.resolve(dirPath, filename);
|
|
2389
2392
|
if (!filename.endsWith(".md")) return;
|
|
2390
2393
|
this.handleFileChange(fullPath, eventType);
|
|
2391
2394
|
});
|
|
@@ -2397,7 +2400,7 @@ var SkillWatcher = class extends EventEmitter2 {
|
|
|
2397
2400
|
}
|
|
2398
2401
|
}
|
|
2399
2402
|
handleFileChange(filePath, _eventType) {
|
|
2400
|
-
const resolvedPath =
|
|
2403
|
+
const resolvedPath = path7.resolve(filePath);
|
|
2401
2404
|
const existingTimer = this.debounceTimers.get(resolvedPath);
|
|
2402
2405
|
if (existingTimer) {
|
|
2403
2406
|
clearTimeout(existingTimer);
|
|
@@ -2454,7 +2457,7 @@ var ShellServer = class {
|
|
|
2454
2457
|
this.server = new Server(
|
|
2455
2458
|
{
|
|
2456
2459
|
name: "internal-shell",
|
|
2457
|
-
version: "0.1.
|
|
2460
|
+
version: "0.1.1"
|
|
2458
2461
|
},
|
|
2459
2462
|
{
|
|
2460
2463
|
capabilities: {
|
|
@@ -2521,18 +2524,18 @@ ${error.stderr || ""}`
|
|
|
2521
2524
|
// src/mcp/servers/filesystem.ts
|
|
2522
2525
|
import { Server as Server2 } from "@modelcontextprotocol/sdk/server/index.js";
|
|
2523
2526
|
import { CallToolRequestSchema as CallToolRequestSchema2, ListToolsRequestSchema as ListToolsRequestSchema2 } from "@modelcontextprotocol/sdk/types.js";
|
|
2524
|
-
import * as
|
|
2525
|
-
import * as
|
|
2527
|
+
import * as fs6 from "fs/promises";
|
|
2528
|
+
import * as path8 from "path";
|
|
2526
2529
|
import { gunzipSync, gzipSync } from "zlib";
|
|
2527
2530
|
var FileSystemServer = class {
|
|
2528
2531
|
server;
|
|
2529
2532
|
allowedRootDir;
|
|
2530
2533
|
constructor(allowedRootDir = process.cwd()) {
|
|
2531
|
-
this.allowedRootDir =
|
|
2534
|
+
this.allowedRootDir = path8.resolve(allowedRootDir);
|
|
2532
2535
|
this.server = new Server2(
|
|
2533
2536
|
{
|
|
2534
2537
|
name: "internal-fs",
|
|
2535
|
-
version: "0.1.
|
|
2538
|
+
version: "0.1.1"
|
|
2536
2539
|
},
|
|
2537
2540
|
{
|
|
2538
2541
|
capabilities: {
|
|
@@ -2543,7 +2546,7 @@ var FileSystemServer = class {
|
|
|
2543
2546
|
this.setupHandlers();
|
|
2544
2547
|
}
|
|
2545
2548
|
validatePath(requestedPath) {
|
|
2546
|
-
const resolvedPath =
|
|
2549
|
+
const resolvedPath = path8.resolve(this.allowedRootDir, requestedPath);
|
|
2547
2550
|
const root = process.platform === "win32" ? this.allowedRootDir.toLowerCase() : this.allowedRootDir;
|
|
2548
2551
|
const target = process.platform === "win32" ? resolvedPath.toLowerCase() : resolvedPath;
|
|
2549
2552
|
if (!target.startsWith(root)) {
|
|
@@ -2665,22 +2668,22 @@ var FileSystemServer = class {
|
|
|
2665
2668
|
switch (request.params.name) {
|
|
2666
2669
|
case "fs_read_file": {
|
|
2667
2670
|
const filePath = this.validatePath(args.path);
|
|
2668
|
-
const content = await
|
|
2671
|
+
const content = await fs6.readFile(filePath, "utf-8");
|
|
2669
2672
|
return {
|
|
2670
2673
|
content: [{ type: "text", text: content }]
|
|
2671
2674
|
};
|
|
2672
2675
|
}
|
|
2673
2676
|
case "fs_write_file": {
|
|
2674
2677
|
const filePath = this.validatePath(args.path);
|
|
2675
|
-
await
|
|
2676
|
-
await
|
|
2678
|
+
await fs6.mkdir(path8.dirname(filePath), { recursive: true });
|
|
2679
|
+
await fs6.writeFile(filePath, args.content, "utf-8");
|
|
2677
2680
|
return {
|
|
2678
2681
|
content: [{ type: "text", text: `Successfully wrote to ${filePath}` }]
|
|
2679
2682
|
};
|
|
2680
2683
|
}
|
|
2681
2684
|
case "fs_list_directory": {
|
|
2682
2685
|
const dirPath = this.validatePath(args.path);
|
|
2683
|
-
const entries = await
|
|
2686
|
+
const entries = await fs6.readdir(dirPath, { withFileTypes: true });
|
|
2684
2687
|
const list = entries.map((e) => `${e.isDirectory() ? "[DIR]" : "[FILE]"} ${e.name}`).join("\n");
|
|
2685
2688
|
return {
|
|
2686
2689
|
content: [{ type: "text", text: list }]
|
|
@@ -2691,8 +2694,8 @@ var FileSystemServer = class {
|
|
|
2691
2694
|
const toPath = this.validatePath(args.to);
|
|
2692
2695
|
const overwrite = Boolean(args.overwrite);
|
|
2693
2696
|
await this.ensureDestWritable(toPath, overwrite);
|
|
2694
|
-
await
|
|
2695
|
-
await
|
|
2697
|
+
await fs6.mkdir(path8.dirname(toPath), { recursive: true });
|
|
2698
|
+
await fs6.copyFile(fromPath, toPath);
|
|
2696
2699
|
return {
|
|
2697
2700
|
content: [{ type: "text", text: `Successfully copied ${fromPath} -> ${toPath}` }]
|
|
2698
2701
|
};
|
|
@@ -2702,7 +2705,7 @@ var FileSystemServer = class {
|
|
|
2702
2705
|
const toPath = this.validatePath(args.to);
|
|
2703
2706
|
const overwrite = Boolean(args.overwrite);
|
|
2704
2707
|
await this.ensureDestWritable(toPath, overwrite);
|
|
2705
|
-
await
|
|
2708
|
+
await fs6.mkdir(path8.dirname(toPath), { recursive: true });
|
|
2706
2709
|
await this.moveFile(fromPath, toPath);
|
|
2707
2710
|
return {
|
|
2708
2711
|
content: [{ type: "text", text: `Successfully moved ${fromPath} -> ${toPath}` }]
|
|
@@ -2724,9 +2727,9 @@ var FileSystemServer = class {
|
|
|
2724
2727
|
const overwrite = Boolean(args.overwrite);
|
|
2725
2728
|
const maxBytes = Math.max(1024, Number(args.maxBytes) || 20 * 1024 * 1024);
|
|
2726
2729
|
await this.ensureDestWritable(outputPath, overwrite);
|
|
2727
|
-
await
|
|
2730
|
+
await fs6.mkdir(path8.dirname(outputPath), { recursive: true });
|
|
2728
2731
|
const bundle = await this.createArchiveBundle(sourcePath, maxBytes);
|
|
2729
|
-
await
|
|
2732
|
+
await fs6.writeFile(outputPath, bundle.buffer);
|
|
2730
2733
|
return {
|
|
2731
2734
|
content: [{ type: "text", text: `Successfully archived ${bundle.fileCount} file(s) to ${outputPath}` }]
|
|
2732
2735
|
};
|
|
@@ -2736,7 +2739,7 @@ var FileSystemServer = class {
|
|
|
2736
2739
|
const outputDir = this.validatePath(args.outputDir);
|
|
2737
2740
|
const overwrite = Boolean(args.overwrite);
|
|
2738
2741
|
const maxBytes = Math.max(1024, Number(args.maxBytes) || 20 * 1024 * 1024);
|
|
2739
|
-
const archiveBuffer = await
|
|
2742
|
+
const archiveBuffer = await fs6.readFile(archivePath);
|
|
2740
2743
|
const bundle = this.parseArchiveBundle(archiveBuffer);
|
|
2741
2744
|
const written = await this.extractArchiveBundle(bundle, outputDir, overwrite, maxBytes);
|
|
2742
2745
|
return {
|
|
@@ -2756,11 +2759,11 @@ var FileSystemServer = class {
|
|
|
2756
2759
|
}
|
|
2757
2760
|
async ensureDestWritable(destPath, overwrite) {
|
|
2758
2761
|
try {
|
|
2759
|
-
await
|
|
2762
|
+
await fs6.access(destPath);
|
|
2760
2763
|
if (!overwrite) {
|
|
2761
2764
|
throw new Error(`Destination already exists: ${destPath}. Pass overwrite=true to replace.`);
|
|
2762
2765
|
}
|
|
2763
|
-
await
|
|
2766
|
+
await fs6.rm(destPath, { recursive: true, force: true });
|
|
2764
2767
|
} catch (error) {
|
|
2765
2768
|
if (error?.code === "ENOENT") return;
|
|
2766
2769
|
throw error;
|
|
@@ -2768,11 +2771,11 @@ var FileSystemServer = class {
|
|
|
2768
2771
|
}
|
|
2769
2772
|
async moveFile(fromPath, toPath) {
|
|
2770
2773
|
try {
|
|
2771
|
-
await
|
|
2774
|
+
await fs6.rename(fromPath, toPath);
|
|
2772
2775
|
} catch (error) {
|
|
2773
2776
|
if (error?.code !== "EXDEV") throw error;
|
|
2774
|
-
await
|
|
2775
|
-
await
|
|
2777
|
+
await fs6.copyFile(fromPath, toPath);
|
|
2778
|
+
await fs6.unlink(fromPath);
|
|
2776
2779
|
}
|
|
2777
2780
|
}
|
|
2778
2781
|
async searchByName(basePath, pattern, maxResults) {
|
|
@@ -2782,10 +2785,10 @@ var FileSystemServer = class {
|
|
|
2782
2785
|
while (queue.length > 0 && results.length < maxResults) {
|
|
2783
2786
|
const current = queue.shift();
|
|
2784
2787
|
if (!current) break;
|
|
2785
|
-
const entries = await
|
|
2788
|
+
const entries = await fs6.readdir(current, { withFileTypes: true });
|
|
2786
2789
|
for (const entry of entries) {
|
|
2787
|
-
const fullPath =
|
|
2788
|
-
const relative3 =
|
|
2790
|
+
const fullPath = path8.join(current, entry.name);
|
|
2791
|
+
const relative3 = path8.relative(this.allowedRootDir, fullPath) || ".";
|
|
2789
2792
|
if (matcher(entry.name)) {
|
|
2790
2793
|
results.push(`${entry.isDirectory() ? "[DIR]" : "[FILE]"} ${relative3}`);
|
|
2791
2794
|
if (results.length >= maxResults) break;
|
|
@@ -2809,11 +2812,11 @@ var FileSystemServer = class {
|
|
|
2809
2812
|
return (name) => name.toLowerCase().includes(lowered);
|
|
2810
2813
|
}
|
|
2811
2814
|
async createArchiveBundle(sourcePath, maxBytes) {
|
|
2812
|
-
const stat3 = await
|
|
2815
|
+
const stat3 = await fs6.stat(sourcePath);
|
|
2813
2816
|
const files = [];
|
|
2814
2817
|
let totalBytes = 0;
|
|
2815
2818
|
const readAndAppend = async (absolutePath, relativePath) => {
|
|
2816
|
-
const data = await
|
|
2819
|
+
const data = await fs6.readFile(absolutePath);
|
|
2817
2820
|
totalBytes += data.length;
|
|
2818
2821
|
if (totalBytes > maxBytes) {
|
|
2819
2822
|
throw new Error(`Archive exceeds maxBytes limit (${maxBytes}).`);
|
|
@@ -2826,28 +2829,28 @@ var FileSystemServer = class {
|
|
|
2826
2829
|
});
|
|
2827
2830
|
};
|
|
2828
2831
|
if (stat3.isFile()) {
|
|
2829
|
-
await readAndAppend(sourcePath,
|
|
2832
|
+
await readAndAppend(sourcePath, path8.basename(sourcePath));
|
|
2830
2833
|
} else if (stat3.isDirectory()) {
|
|
2831
2834
|
const queue = [sourcePath];
|
|
2832
2835
|
while (queue.length > 0) {
|
|
2833
2836
|
const current = queue.shift();
|
|
2834
2837
|
if (!current) break;
|
|
2835
|
-
const entries = await
|
|
2838
|
+
const entries = await fs6.readdir(current, { withFileTypes: true });
|
|
2836
2839
|
for (const entry of entries) {
|
|
2837
|
-
const fullPath =
|
|
2840
|
+
const fullPath = path8.join(current, entry.name);
|
|
2838
2841
|
if (entry.isDirectory()) {
|
|
2839
2842
|
queue.push(fullPath);
|
|
2840
2843
|
continue;
|
|
2841
2844
|
}
|
|
2842
2845
|
if (!entry.isFile()) continue;
|
|
2843
|
-
const relativePath =
|
|
2846
|
+
const relativePath = path8.relative(sourcePath, fullPath) || entry.name;
|
|
2844
2847
|
await readAndAppend(fullPath, relativePath.replace(/\\/g, "/"));
|
|
2845
2848
|
}
|
|
2846
2849
|
}
|
|
2847
2850
|
} else {
|
|
2848
2851
|
throw new Error("Only files and directories can be archived.");
|
|
2849
2852
|
}
|
|
2850
|
-
const sourceRelative =
|
|
2853
|
+
const sourceRelative = path8.relative(this.allowedRootDir, sourcePath).replace(/\\/g, "/");
|
|
2851
2854
|
const bundle = {
|
|
2852
2855
|
format: "lydia-archive-v1",
|
|
2853
2856
|
source: sourceRelative || ".",
|
|
@@ -2880,13 +2883,13 @@ var FileSystemServer = class {
|
|
|
2880
2883
|
let count = 0;
|
|
2881
2884
|
for (const file of bundle.files) {
|
|
2882
2885
|
const relPath = String(file.path || "").replace(/\\/g, "/");
|
|
2883
|
-
if (!relPath ||
|
|
2886
|
+
if (!relPath || path8.isAbsolute(relPath) || relPath.includes("..")) {
|
|
2884
2887
|
throw new Error(`Unsafe archive entry path: ${relPath || "<empty>"}`);
|
|
2885
2888
|
}
|
|
2886
|
-
const targetPath =
|
|
2889
|
+
const targetPath = path8.resolve(outputDir, relPath);
|
|
2887
2890
|
const safeOutputDir = process.platform === "win32" ? outputDir.toLowerCase() : outputDir;
|
|
2888
2891
|
const safeTarget = process.platform === "win32" ? targetPath.toLowerCase() : targetPath;
|
|
2889
|
-
if (!safeTarget.startsWith(safeOutputDir +
|
|
2892
|
+
if (!safeTarget.startsWith(safeOutputDir + path8.sep) && safeTarget !== safeOutputDir) {
|
|
2890
2893
|
throw new Error(`Archive entry escapes destination: ${relPath}`);
|
|
2891
2894
|
}
|
|
2892
2895
|
const data = this.decodeArchiveEntry(file);
|
|
@@ -2894,8 +2897,8 @@ var FileSystemServer = class {
|
|
|
2894
2897
|
if (totalBytes > maxBytes) {
|
|
2895
2898
|
throw new Error(`Unarchive exceeds maxBytes limit (${maxBytes}).`);
|
|
2896
2899
|
}
|
|
2897
|
-
await
|
|
2898
|
-
await
|
|
2900
|
+
await fs6.mkdir(path8.dirname(targetPath), { recursive: true });
|
|
2901
|
+
await fs6.writeFile(targetPath, data);
|
|
2899
2902
|
count += 1;
|
|
2900
2903
|
}
|
|
2901
2904
|
return count;
|
|
@@ -2915,22 +2918,22 @@ var FileSystemServer = class {
|
|
|
2915
2918
|
}
|
|
2916
2919
|
async prepareOutputDirectory(outputDir, overwrite) {
|
|
2917
2920
|
try {
|
|
2918
|
-
const stat3 = await
|
|
2921
|
+
const stat3 = await fs6.stat(outputDir);
|
|
2919
2922
|
if (!stat3.isDirectory()) {
|
|
2920
2923
|
throw new Error(`Output path is not a directory: ${outputDir}`);
|
|
2921
2924
|
}
|
|
2922
2925
|
if (!overwrite) {
|
|
2923
|
-
const entries = await
|
|
2926
|
+
const entries = await fs6.readdir(outputDir);
|
|
2924
2927
|
if (entries.length > 0) {
|
|
2925
2928
|
throw new Error(`Output directory is not empty: ${outputDir}. Pass overwrite=true to replace.`);
|
|
2926
2929
|
}
|
|
2927
2930
|
} else {
|
|
2928
|
-
await
|
|
2929
|
-
await
|
|
2931
|
+
await fs6.rm(outputDir, { recursive: true, force: true });
|
|
2932
|
+
await fs6.mkdir(outputDir, { recursive: true });
|
|
2930
2933
|
}
|
|
2931
2934
|
} catch (error) {
|
|
2932
2935
|
if (error?.code === "ENOENT") {
|
|
2933
|
-
await
|
|
2936
|
+
await fs6.mkdir(outputDir, { recursive: true });
|
|
2934
2937
|
return;
|
|
2935
2938
|
}
|
|
2936
2939
|
throw error;
|
|
@@ -2950,7 +2953,7 @@ var GitServer = class {
|
|
|
2950
2953
|
this.server = new Server3(
|
|
2951
2954
|
{
|
|
2952
2955
|
name: "internal-git",
|
|
2953
|
-
version: "0.1.
|
|
2956
|
+
version: "0.1.1"
|
|
2954
2957
|
},
|
|
2955
2958
|
{
|
|
2956
2959
|
capabilities: {
|
|
@@ -3127,7 +3130,7 @@ var MemoryServer = class {
|
|
|
3127
3130
|
this.server = new Server4(
|
|
3128
3131
|
{
|
|
3129
3132
|
name: "internal-memory",
|
|
3130
|
-
version: "0.1.
|
|
3133
|
+
version: "0.1.1"
|
|
3131
3134
|
},
|
|
3132
3135
|
{
|
|
3133
3136
|
capabilities: {
|
|
@@ -3253,7 +3256,7 @@ var InteractionServer = class extends EventEmitter3 {
|
|
|
3253
3256
|
constructor() {
|
|
3254
3257
|
super();
|
|
3255
3258
|
this.server = new Server5(
|
|
3256
|
-
{ name: "internal-interaction", version: "0.1.
|
|
3259
|
+
{ name: "internal-interaction", version: "0.1.1" },
|
|
3257
3260
|
{ capabilities: { tools: {} } }
|
|
3258
3261
|
);
|
|
3259
3262
|
this.setupHandlers();
|
|
@@ -3634,7 +3637,7 @@ var McpClientManager = class {
|
|
|
3634
3637
|
const client = new Client(
|
|
3635
3638
|
{
|
|
3636
3639
|
name: "lydia-client",
|
|
3637
|
-
version: "0.1.
|
|
3640
|
+
version: "0.1.1"
|
|
3638
3641
|
},
|
|
3639
3642
|
{
|
|
3640
3643
|
capabilities: {
|
|
@@ -3742,7 +3745,7 @@ var McpClientManager = class {
|
|
|
3742
3745
|
};
|
|
3743
3746
|
|
|
3744
3747
|
// src/gate/risk.ts
|
|
3745
|
-
import * as
|
|
3748
|
+
import * as path9 from "path";
|
|
3746
3749
|
import * as os6 from "os";
|
|
3747
3750
|
var DEFAULT_USER_DATA_DIRS = [
|
|
3748
3751
|
"~/.lydia",
|
|
@@ -3772,13 +3775,13 @@ var COMPUTER_USE_HIGH_RISK_ACTIONS = /* @__PURE__ */ new Set([
|
|
|
3772
3775
|
]);
|
|
3773
3776
|
function expandHome(p) {
|
|
3774
3777
|
if (p.startsWith("~/") || p === "~") {
|
|
3775
|
-
return
|
|
3778
|
+
return path9.join(os6.homedir(), p.slice(2));
|
|
3776
3779
|
}
|
|
3777
3780
|
return p;
|
|
3778
3781
|
}
|
|
3779
3782
|
function normalizePath(p) {
|
|
3780
3783
|
const expanded = expandHome(p);
|
|
3781
|
-
const normalized =
|
|
3784
|
+
const normalized = path9.resolve(expanded);
|
|
3782
3785
|
return process.platform === "win32" ? normalized.toLowerCase() : normalized;
|
|
3783
3786
|
}
|
|
3784
3787
|
function buildProtectedDirs(config) {
|
|
@@ -3791,7 +3794,7 @@ function buildProtectedDirs(config) {
|
|
|
3791
3794
|
}
|
|
3792
3795
|
function isInProtectedDir(targetPath, protectedDirs) {
|
|
3793
3796
|
const normalizedTarget = normalizePath(targetPath);
|
|
3794
|
-
return protectedDirs.some((dir) => normalizedTarget === dir || normalizedTarget.startsWith(dir +
|
|
3797
|
+
return protectedDirs.some((dir) => normalizedTarget === dir || normalizedTarget.startsWith(dir + path9.sep));
|
|
3795
3798
|
}
|
|
3796
3799
|
function extractPathsFromCommand(command) {
|
|
3797
3800
|
const paths = [];
|
|
@@ -3813,7 +3816,7 @@ function hasRelativePathTraversal(input) {
|
|
|
3813
3816
|
return /(^|[\\\/])\.\.([\\\/]|$)/.test(input);
|
|
3814
3817
|
}
|
|
3815
3818
|
function isRelativePath(input) {
|
|
3816
|
-
return !
|
|
3819
|
+
return !path9.isAbsolute(input);
|
|
3817
3820
|
}
|
|
3818
3821
|
function resolveCanonicalComputerUseAction(toolName) {
|
|
3819
3822
|
const direct = resolveCanonicalComputerUseToolName(toolName);
|
|
@@ -3878,7 +3881,7 @@ function assessRisk(toolName, args, mcp, config) {
|
|
|
3878
3881
|
return {
|
|
3879
3882
|
level: "high",
|
|
3880
3883
|
reason: `${opLabel} with relative path traversal`,
|
|
3881
|
-
signature: `fs_write_rel:${normalizePath(
|
|
3884
|
+
signature: `fs_write_rel:${normalizePath(path9.resolve(targetPath))}`,
|
|
3882
3885
|
details: targetPath
|
|
3883
3886
|
};
|
|
3884
3887
|
}
|
|
@@ -3909,6 +3912,14 @@ function assessRisk(toolName, args, mcp, config) {
|
|
|
3909
3912
|
if (toolName === "shell_execute") {
|
|
3910
3913
|
const command = typeof args?.command === "string" ? args.command : "";
|
|
3911
3914
|
if (command && (isDestructiveShellCommand(command) || isPermissionChangeCommand(command))) {
|
|
3915
|
+
if (isPermissionChangeCommand(command)) {
|
|
3916
|
+
return {
|
|
3917
|
+
level: "high",
|
|
3918
|
+
reason: "Permission change shell command",
|
|
3919
|
+
signature: `shell_permission:${command.toLowerCase().slice(0, 80)}`,
|
|
3920
|
+
details: command
|
|
3921
|
+
};
|
|
3922
|
+
}
|
|
3912
3923
|
const targets = extractPathsFromCommand(command);
|
|
3913
3924
|
const hasTraversal = hasRelativePathTraversal(command);
|
|
3914
3925
|
if (targets.length === 0 || hasTraversal) {
|
|
@@ -4147,7 +4158,7 @@ var StrategyUpdateGate = class {
|
|
|
4147
4158
|
};
|
|
4148
4159
|
|
|
4149
4160
|
// src/gate/review-manager.ts
|
|
4150
|
-
import * as
|
|
4161
|
+
import * as path10 from "path";
|
|
4151
4162
|
import * as os7 from "os";
|
|
4152
4163
|
var ReviewManager = class {
|
|
4153
4164
|
memory;
|
|
@@ -4156,7 +4167,7 @@ var ReviewManager = class {
|
|
|
4156
4167
|
this.memory = memoryManager;
|
|
4157
4168
|
return;
|
|
4158
4169
|
}
|
|
4159
|
-
const dbPath =
|
|
4170
|
+
const dbPath = path10.join(os7.homedir(), ".lydia", "memory.db");
|
|
4160
4171
|
this.memory = new MemoryManager(dbPath);
|
|
4161
4172
|
}
|
|
4162
4173
|
async init() {
|
|
@@ -4335,7 +4346,7 @@ ${this.originalPlan}
|
|
|
4335
4346
|
};
|
|
4336
4347
|
|
|
4337
4348
|
// src/replay/mcp.ts
|
|
4338
|
-
import * as
|
|
4349
|
+
import * as path11 from "path";
|
|
4339
4350
|
var ReplayMcpClientManager = class extends McpClientManager {
|
|
4340
4351
|
traces;
|
|
4341
4352
|
callIndex = 0;
|
|
@@ -4490,12 +4501,12 @@ var ReplayMcpClientManager = class extends McpClientManager {
|
|
|
4490
4501
|
const loweredPattern = rawPattern.toLowerCase();
|
|
4491
4502
|
const names = /* @__PURE__ */ new Set();
|
|
4492
4503
|
for (const storedPath of this.virtualFiles.keys()) {
|
|
4493
|
-
const rel =
|
|
4504
|
+
const rel = path11.relative(normalized, storedPath);
|
|
4494
4505
|
if (rel.startsWith("..")) continue;
|
|
4495
4506
|
const parts = rel.split("/").filter(Boolean);
|
|
4496
4507
|
for (const part of parts) {
|
|
4497
4508
|
if (part.toLowerCase().includes(loweredPattern)) {
|
|
4498
|
-
names.add(`[FILE] ${
|
|
4509
|
+
names.add(`[FILE] ${path11.posix.join(path11.posix.relative(this.virtualRoot, normalized), rel)}`);
|
|
4499
4510
|
break;
|
|
4500
4511
|
}
|
|
4501
4512
|
}
|
|
@@ -4523,12 +4534,12 @@ var ReplayMcpClientManager = class extends McpClientManager {
|
|
|
4523
4534
|
createdAt: 0,
|
|
4524
4535
|
totalBytes: sourceFile ? sourceFile.length : sourceEntries.reduce((acc, [, text]) => acc + text.length, 0),
|
|
4525
4536
|
files: sourceFile ? [{
|
|
4526
|
-
path:
|
|
4537
|
+
path: path11.posix.basename(source),
|
|
4527
4538
|
size: sourceFile.length,
|
|
4528
4539
|
encoding: "base64",
|
|
4529
4540
|
data: Buffer.from(sourceFile, "utf-8").toString("base64")
|
|
4530
4541
|
}] : sourceEntries.map(([filePath, text]) => ({
|
|
4531
|
-
path:
|
|
4542
|
+
path: path11.posix.relative(source, filePath),
|
|
4532
4543
|
size: text.length,
|
|
4533
4544
|
encoding: "base64",
|
|
4534
4545
|
data: Buffer.from(text, "utf-8").toString("base64")
|
|
@@ -4583,7 +4594,7 @@ var ReplayMcpClientManager = class extends McpClientManager {
|
|
|
4583
4594
|
for (const item of parsed.files) {
|
|
4584
4595
|
const rel = typeof item?.path === "string" ? item.path.replace(/\\/g, "/") : "";
|
|
4585
4596
|
if (!rel || rel.includes("..") || rel.startsWith("/")) continue;
|
|
4586
|
-
const target = this.normalizeFsPath(
|
|
4597
|
+
const target = this.normalizeFsPath(path11.posix.join(outputDir, rel));
|
|
4587
4598
|
const base64 = typeof item?.data === "string" ? item.data : "";
|
|
4588
4599
|
const text = base64 ? Buffer.from(base64, "base64").toString("utf-8") : "";
|
|
4589
4600
|
this.virtualFiles.set(target, text);
|
|
@@ -4876,12 +4887,12 @@ var ReplayMcpClientManager = class extends McpClientManager {
|
|
|
4876
4887
|
if (driveMatch) {
|
|
4877
4888
|
const drive = driveMatch[1].toLowerCase();
|
|
4878
4889
|
const rest = driveMatch[2] || "/";
|
|
4879
|
-
return
|
|
4890
|
+
return path11.posix.normalize(`/${drive}${rest}`);
|
|
4880
4891
|
}
|
|
4881
4892
|
if (source.startsWith("/")) {
|
|
4882
|
-
return
|
|
4893
|
+
return path11.posix.normalize(source);
|
|
4883
4894
|
}
|
|
4884
|
-
return
|
|
4895
|
+
return path11.posix.normalize(path11.posix.join(this.virtualRoot, source));
|
|
4885
4896
|
}
|
|
4886
4897
|
listDirectoryEntries(directory) {
|
|
4887
4898
|
const normalizedDir = this.normalizeFsPath(directory);
|
|
@@ -5072,7 +5083,7 @@ var StrategyEvaluator = class {
|
|
|
5072
5083
|
};
|
|
5073
5084
|
|
|
5074
5085
|
// src/replay/manager.ts
|
|
5075
|
-
import * as
|
|
5086
|
+
import * as path12 from "path";
|
|
5076
5087
|
import * as os8 from "os";
|
|
5077
5088
|
var ReplayManager = class {
|
|
5078
5089
|
memoryManager;
|
|
@@ -5081,7 +5092,7 @@ var ReplayManager = class {
|
|
|
5081
5092
|
if (memoryManager) {
|
|
5082
5093
|
this.memoryManager = memoryManager;
|
|
5083
5094
|
} else {
|
|
5084
|
-
const dbPath =
|
|
5095
|
+
const dbPath = path12.join(os8.homedir(), ".lydia", "memory.db");
|
|
5085
5096
|
this.memoryManager = new MemoryManager(dbPath);
|
|
5086
5097
|
}
|
|
5087
5098
|
this.evaluator = new StrategyEvaluator();
|
|
@@ -5101,7 +5112,7 @@ var ReplayManager = class {
|
|
|
5101
5112
|
const agent = new Agent(mockLLM);
|
|
5102
5113
|
agent.mcpClientManager = mockMcp;
|
|
5103
5114
|
agent.isInitialized = true;
|
|
5104
|
-
const tempDb =
|
|
5115
|
+
const tempDb = path12.join(os8.tmpdir(), `lydia-replay-${Date.now()}-${episodeId}.db`);
|
|
5105
5116
|
agent.memoryManager = new MemoryManager(tempDb);
|
|
5106
5117
|
try {
|
|
5107
5118
|
const config = await agent.configLoader.load();
|
|
@@ -5421,7 +5432,7 @@ var FeedbackCollector = class {
|
|
|
5421
5432
|
};
|
|
5422
5433
|
|
|
5423
5434
|
// src/execution/agent.ts
|
|
5424
|
-
import * as
|
|
5435
|
+
import * as path13 from "path";
|
|
5425
5436
|
import * as os9 from "os";
|
|
5426
5437
|
var Agent = class extends EventEmitter5 {
|
|
5427
5438
|
llm;
|
|
@@ -5518,7 +5529,7 @@ var Agent = class extends EventEmitter5 {
|
|
|
5518
5529
|
await this.reviewManager.init();
|
|
5519
5530
|
const config = await this.configLoader.load();
|
|
5520
5531
|
this.config = config;
|
|
5521
|
-
const dbPath =
|
|
5532
|
+
const dbPath = path13.join(os9.homedir(), ".lydia", "memory.db");
|
|
5522
5533
|
this.memoryManager = new MemoryManager(dbPath, {
|
|
5523
5534
|
checkpointTtlMs: Math.max(1, config.memory?.checkpointTtlHours ?? 24) * 60 * 60 * 1e3,
|
|
5524
5535
|
observationFrameTtlMs: Math.max(1, config.memory?.observationFrameTtlHours ?? 24 * 7) * 60 * 60 * 1e3
|