@zooique/memora 4.0.0 → 5.0.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/CHANGELOG.md +165 -1743
- package/README.en.md +43 -36
- package/README.md +308 -303
- package/dist/agent/agent.d.ts +46 -82
- package/dist/agent/agent.d.ts.map +1 -1
- package/dist/agent/agent.js +153 -220
- package/dist/agent/agent.js.map +1 -1
- package/dist/agent/assembler.d.ts +3 -16
- package/dist/agent/assembler.d.ts.map +1 -1
- package/dist/agent/assembler.js +46 -35
- package/dist/agent/assembler.js.map +1 -1
- package/dist/agent/backgroundTasks.d.ts +8 -1
- package/dist/agent/backgroundTasks.d.ts.map +1 -1
- package/dist/agent/backgroundTasks.js +8 -1
- package/dist/agent/backgroundTasks.js.map +1 -1
- package/dist/agent/budget.d.ts +23 -5
- package/dist/agent/budget.d.ts.map +1 -1
- package/dist/agent/budget.js +8 -2
- package/dist/agent/budget.js.map +1 -1
- package/dist/agent/builtinToolHandlers.d.ts +27 -8
- package/dist/agent/builtinToolHandlers.d.ts.map +1 -1
- package/dist/agent/builtinToolHandlers.js +45 -9
- package/dist/agent/builtinToolHandlers.js.map +1 -1
- package/dist/agent/builtinTools.d.ts.map +1 -1
- package/dist/agent/builtinTools.js +4 -3
- package/dist/agent/builtinTools.js.map +1 -1
- package/dist/agent/constants.d.ts +0 -6
- package/dist/agent/constants.d.ts.map +1 -1
- package/dist/agent/constants.js +7 -8
- package/dist/agent/constants.js.map +1 -1
- package/dist/agent/contextManager.d.ts +8 -0
- package/dist/agent/contextManager.d.ts.map +1 -1
- package/dist/agent/contextManager.js +45 -3
- package/dist/agent/contextManager.js.map +1 -1
- package/dist/agent/contextPreparer.d.ts +1 -1
- package/dist/agent/contextPreparer.d.ts.map +1 -1
- package/dist/agent/contextPreparer.js +15 -10
- package/dist/agent/contextPreparer.js.map +1 -1
- package/dist/agent/guardRail.d.ts +1 -1
- package/dist/agent/guardRail.d.ts.map +1 -1
- package/dist/agent/guardRail.js +1 -1
- package/dist/agent/guardRail.js.map +1 -1
- package/dist/agent/loop.d.ts +46 -13
- package/dist/agent/loop.d.ts.map +1 -1
- package/dist/agent/loop.js +128 -25
- package/dist/agent/loop.js.map +1 -1
- package/dist/agent/managers/archiveCoordinator.d.ts +15 -3
- package/dist/agent/managers/archiveCoordinator.d.ts.map +1 -1
- package/dist/agent/managers/archiveCoordinator.js +13 -7
- package/dist/agent/managers/archiveCoordinator.js.map +1 -1
- package/dist/agent/managers/chatLockManager.d.ts +3 -9
- package/dist/agent/managers/chatLockManager.d.ts.map +1 -1
- package/dist/agent/managers/chatLockManager.js +7 -25
- package/dist/agent/managers/chatLockManager.js.map +1 -1
- package/dist/agent/managers/llmCaller.d.ts.map +1 -1
- package/dist/agent/managers/llmCaller.js +27 -1
- package/dist/agent/managers/llmCaller.js.map +1 -1
- package/dist/agent/managers/memoryInspector.d.ts +12 -7
- package/dist/agent/managers/memoryInspector.d.ts.map +1 -1
- package/dist/agent/managers/memoryInspector.js +32 -19
- package/dist/agent/managers/memoryInspector.js.map +1 -1
- package/dist/agent/managers/roundSummaryGenerator.d.ts +3 -1
- package/dist/agent/managers/roundSummaryGenerator.d.ts.map +1 -1
- package/dist/agent/managers/roundSummaryGenerator.js +14 -4
- package/dist/agent/managers/roundSummaryGenerator.js.map +1 -1
- package/dist/agent/managers/sessionArchiver.d.ts +20 -6
- package/dist/agent/managers/sessionArchiver.d.ts.map +1 -1
- package/dist/agent/managers/sessionArchiver.js +19 -5
- package/dist/agent/managers/sessionArchiver.js.map +1 -1
- package/dist/agent/managers/sessionManager.d.ts +21 -88
- package/dist/agent/managers/sessionManager.d.ts.map +1 -1
- package/dist/agent/managers/sessionManager.js +77 -278
- package/dist/agent/managers/sessionManager.js.map +1 -1
- package/dist/agent/seed/orchestrator.d.ts.map +1 -1
- package/dist/agent/seed/orchestrator.js +3 -3
- package/dist/agent/seed/orchestrator.js.map +1 -1
- package/dist/agent/seed/prepare.d.ts.map +1 -1
- package/dist/agent/seed/prepare.js +9 -4
- package/dist/agent/seed/prepare.js.map +1 -1
- package/dist/agent/seed/types.d.ts +11 -4
- package/dist/agent/seed/types.d.ts.map +1 -1
- package/dist/agent/seed/types.js +2 -2
- package/dist/agent/toolExecutor.d.ts +51 -6
- package/dist/agent/toolExecutor.d.ts.map +1 -1
- package/dist/agent/toolExecutor.js +74 -9
- package/dist/agent/toolExecutor.js.map +1 -1
- package/dist/agent/toolResultCache.js +1 -1
- package/dist/agent/toolRunner.d.ts +4 -2
- package/dist/agent/toolRunner.d.ts.map +1 -1
- package/dist/agent/toolRunner.js +7 -4
- package/dist/agent/toolRunner.js.map +1 -1
- package/dist/agent/types.d.ts +8 -25
- package/dist/agent/types.d.ts.map +1 -1
- package/dist/index.d.ts +9 -20
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +35 -32
- package/dist/index.js.map +1 -1
- package/dist/logging/logger.d.ts +4 -1
- package/dist/logging/logger.d.ts.map +1 -1
- package/dist/logging/logger.js +5 -2
- package/dist/logging/logger.js.map +1 -1
- package/dist/memory/gcService.d.ts.map +1 -1
- package/dist/memory/gcService.js +3 -5
- package/dist/memory/gcService.js.map +1 -1
- package/dist/memory/inMemorySessionStore.d.ts +2 -2
- package/dist/memory/inMemorySessionStore.js +2 -2
- package/dist/memory/inMemoryStorage.d.ts +4 -3
- package/dist/memory/inMemoryStorage.d.ts.map +1 -1
- package/dist/memory/inMemoryStorage.js +2 -2
- package/dist/memory/inMemoryStorage.js.map +1 -1
- package/dist/memory/nodeSqliteStorage.d.ts +17 -0
- package/dist/memory/nodeSqliteStorage.d.ts.map +1 -0
- package/dist/memory/nodeSqliteStorage.js +393 -0
- package/dist/memory/nodeSqliteStorage.js.map +1 -0
- package/dist/memory/projectManager.d.ts +10 -20
- package/dist/memory/projectManager.d.ts.map +1 -1
- package/dist/memory/projectManager.js +27 -41
- package/dist/memory/projectManager.js.map +1 -1
- package/dist/memory/roundStore.d.ts +76 -1
- package/dist/memory/roundStore.d.ts.map +1 -1
- package/dist/memory/roundStore.js.map +1 -1
- package/dist/memory/roundSummaryLookup.d.ts +26 -0
- package/dist/memory/roundSummaryLookup.d.ts.map +1 -0
- package/dist/memory/roundSummaryLookup.js +19 -0
- package/dist/memory/roundSummaryLookup.js.map +1 -0
- package/dist/memory/sessionStore.d.ts +19 -2
- package/dist/memory/sessionStore.d.ts.map +1 -1
- package/dist/memory/sessionStore.js.map +1 -1
- package/dist/memory/sourceValidation.d.ts +2 -13
- package/dist/memory/sourceValidation.d.ts.map +1 -1
- package/dist/memory/sourceValidation.js +5 -16
- package/dist/memory/sourceValidation.js.map +1 -1
- package/dist/memory/storageInterface.d.ts +26 -16
- package/dist/memory/storageInterface.d.ts.map +1 -1
- package/dist/memory/supersedeResolve.d.ts +39 -0
- package/dist/memory/supersedeResolve.d.ts.map +1 -0
- package/dist/memory/supersedeResolve.js +16 -0
- package/dist/memory/supersedeResolve.js.map +1 -0
- package/dist/memory/types.d.ts +3 -2
- package/dist/memory/types.d.ts.map +1 -1
- package/dist/memory/types.js +2 -1
- package/dist/memory/types.js.map +1 -1
- package/dist/role-pack/rolePackManager.d.ts +33 -24
- package/dist/role-pack/rolePackManager.d.ts.map +1 -1
- package/dist/role-pack/rolePackManager.js +48 -42
- package/dist/role-pack/rolePackManager.js.map +1 -1
- package/dist/role-pack/strategyKeys.d.ts +8 -0
- package/dist/role-pack/strategyKeys.d.ts.map +1 -1
- package/dist/role-pack/strategyKeys.js +10 -0
- package/dist/role-pack/strategyKeys.js.map +1 -1
- package/dist/role-pack/strategyResolver.d.ts +6 -1
- package/dist/role-pack/strategyResolver.d.ts.map +1 -1
- package/dist/role-pack/strategyResolver.js +20 -2
- package/dist/role-pack/strategyResolver.js.map +1 -1
- package/dist/role-pack/types.d.ts +5 -1
- package/dist/role-pack/types.d.ts.map +1 -1
- package/dist/security/pathGuard.d.ts +1 -1
- package/dist/security/pathGuard.d.ts.map +1 -1
- package/dist/security/pathGuard.js +1 -1
- package/dist/security/pathGuard.js.map +1 -1
- package/dist/utils/backgroundTask.d.ts +56 -34
- package/dist/utils/backgroundTask.d.ts.map +1 -1
- package/dist/utils/backgroundTask.js +130 -99
- package/dist/utils/backgroundTask.js.map +1 -1
- package/dist/utils/errors.d.ts +3 -2
- package/dist/utils/errors.d.ts.map +1 -1
- package/dist/utils/errors.js +3 -2
- package/dist/utils/errors.js.map +1 -1
- package/dist/utils/eventEmitter.d.ts +20 -22
- package/dist/utils/eventEmitter.d.ts.map +1 -1
- package/dist/utils/eventEmitter.js +0 -3
- package/dist/utils/eventEmitter.js.map +1 -1
- package/dist/utils/time.d.ts +3 -3
- package/dist/utils/time.d.ts.map +1 -1
- package/dist/utils/time.js +3 -3
- package/dist/utils/time.js.map +1 -1
- package/dist/utils/toError.d.ts +1 -1
- package/dist/utils/toError.js +1 -1
- package/dist/web-fetch/loginWall.d.ts +39 -0
- package/dist/web-fetch/loginWall.d.ts.map +1 -0
- package/dist/web-fetch/loginWall.js +78 -0
- package/dist/web-fetch/loginWall.js.map +1 -0
- package/dist/web-fetch/types.d.ts +8 -0
- package/dist/web-fetch/types.d.ts.map +1 -1
- package/dist/web-fetch/webFetchProvider.d.ts.map +1 -1
- package/dist/web-fetch/webFetchProvider.js +3 -1
- package/dist/web-fetch/webFetchProvider.js.map +1 -1
- package/dist/web-search/fetchWebSearchProvider.js +28 -23
- package/dist/web-search/fetchWebSearchProvider.js.map +1 -1
- package/docs/architecture/role-pack-authoring-guide.md +3 -1
- package/docs/architecture/role-pack-spec.md +2 -1
- package/docs/memora-api-reference.md +1349 -1276
- package/docs/memora-/345/215/207/347/272/247/346/214/207/345/215/227-4.x-/345/210/260-5.0.0.md +385 -0
- package/docs/memora-/346/216/245/345/205/245/346/214/207/345/215/227.md +87 -49
- package/docs/role-pack-/345/274/200/346/224/276/351/224/256/346/214/207/345/215/227.md +148 -148
- package/package.json +9 -4
- package/role-packs/README.md +41 -40
- package/role-packs/memora/345/212/251/346/211/213/rules.md +4 -4
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/manifest.json +1 -0
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/persona.md +46 -46
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/rules.md +16 -16
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/emotion-wave.md +46 -46
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/foreshadow.md +29 -29
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/outline-expand.md +73 -48
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/project-init/scripts/init-novel-project.mjs +445 -441
- package/role-packs//345/205/261/351/270/243/345/260/217/350/257/264/345/256/266/skills/story-bible.md +62 -62
- package/role-packs//347/231/275/350/257/235/346/226/271/346/241/210/350/256/276/350/256/241/345/270/210/manifest.json +1 -0
- package/dist/agent/managers/dedupManager.d.ts +0 -85
- package/dist/agent/managers/dedupManager.d.ts.map +0 -1
- package/dist/agent/managers/dedupManager.js +0 -248
- package/dist/agent/managers/dedupManager.js.map +0 -1
- package/dist/agent/managers/goalConsistencyChecker.d.ts +0 -42
- package/dist/agent/managers/goalConsistencyChecker.d.ts.map +0 -1
- package/dist/agent/managers/goalConsistencyChecker.js +0 -178
- package/dist/agent/managers/goalConsistencyChecker.js.map +0 -1
- package/dist/agent/managers/llmJudgeHelper.d.ts +0 -21
- package/dist/agent/managers/llmJudgeHelper.d.ts.map +0 -1
- package/dist/agent/managers/llmJudgeHelper.js +0 -32
- package/dist/agent/managers/llmJudgeHelper.js.map +0 -1
- package/dist/agent/managers/memoryAdvisor.d.ts +0 -105
- package/dist/agent/managers/memoryAdvisor.d.ts.map +0 -1
- package/dist/agent/managers/memoryAdvisor.js +0 -322
- package/dist/agent/managers/memoryAdvisor.js.map +0 -1
- package/dist/agent/managers/memoryGovernance.d.ts +0 -24
- package/dist/agent/managers/memoryGovernance.d.ts.map +0 -1
- package/dist/agent/managers/memoryGovernance.js +0 -46
- package/dist/agent/managers/memoryGovernance.js.map +0 -1
- package/dist/memory/governance.d.ts +0 -17
- package/dist/memory/governance.d.ts.map +0 -1
- package/dist/memory/governance.js +0 -17
- package/dist/memory/governance.js.map +0 -1
- package/dist/memory/projectRegistry.d.ts +0 -47
- package/dist/memory/projectRegistry.d.ts.map +0 -1
- package/dist/memory/projectRegistry.js +0 -151
- package/dist/memory/projectRegistry.js.map +0 -1
- package/dist/utils/recallDefaults.d.ts +0 -16
- package/dist/utils/recallDefaults.d.ts.map +0 -1
- package/dist/utils/recallDefaults.js +0 -16
- package/dist/utils/recallDefaults.js.map +0 -1
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Node 内置 SQLite 存储实现 —— `IMemoryStorage` 的落盘版(`node:sqlite` 内建模块,零第三方运行时依赖)
|
|
3
|
+
*
|
|
4
|
+
* ## 边界(勿越)
|
|
5
|
+
* - **只出工厂、不出实现类**:宿主经 `createNodeSqliteStorage()` 消费;实现类随签名结构可用,但不挂公共面
|
|
6
|
+
* (避免宿主绕过工厂校验自建实例——基址/只读/PRAGMA 全在工厂收口)。
|
|
7
|
+
* - **文件路径语义留在宿主侧**:工厂收 `dbPath` 全长路径,内核不拼 `.memora/`、不定义记忆文件名常量
|
|
8
|
+
* (对齐 ADR-002 定位:内核不感知数据目录布局)。
|
|
9
|
+
* - **不接管 IO 装载与迁移**:JSON 读取与一次性迁移是宿主的组合调用,内核不实现。
|
|
10
|
+
*
|
|
11
|
+
* ## 为什么用 `process.getBuiltinModule` 而非 `createRequire(import.meta.url)`
|
|
12
|
+
* ① `import.meta.url` 在本仓宿主 esbuild bundle 场景**有事故史**(bundle 后路径漂移,见宿主 assemble.ts /
|
|
13
|
+
* extension.ts 相关注释);
|
|
14
|
+
* ② `process.getBuiltinModule('node:xxx')` 对**不存在的模块返回 `undefined` 而非抛异常**(Node 22.22.2 /
|
|
15
|
+
* 24.12.0 实测)⇒「环境不支持」是显式判据;若改用 `createRequire` 的 catch-all,bundle 造成的
|
|
16
|
+
* `TypeError` 会被吞成「老 Node 环境」= 真故障伪装成环境问题(违背「不带伤」);
|
|
17
|
+
* ③ 版本代价 ≥22.3 被 `node:sqlite` 自身的 ≥22.13 门槛完全吸收,不额外抬门槛。
|
|
18
|
+
* Node ≤22.2 无 `getBuiltinModule` 方法 → 走同一条「不可用」判据。
|
|
19
|
+
*
|
|
20
|
+
* ## 可用性判据唯一来源(禁判据散点)
|
|
21
|
+
* 「当前环境能否用 SQLite」的**唯一判据** = 调用本工厂的结果(成功 / 抛 `NOT_AVAILABLE` 专属错误)。
|
|
22
|
+
* 宿主**禁止**自行解析 `process.versions.node` 推断——版本号与实际加载结果可能不一致。
|
|
23
|
+
*
|
|
24
|
+
* ## 只读视图
|
|
25
|
+
* `{ readOnly: true }` 重载返回 `ReadOnlyMemoryStorage`(仅读方法):连接以 SQLite 只读模式打开,且返回对象
|
|
26
|
+
* **结构上不含写方法**(不是「约定不乱调」)。供只读宿主(如 memora-mcp)零并列复用本实现,避免各宿主自写
|
|
27
|
+
* 第二份 SQLite 语义实现(三份并列 = 语义漂移)。**只读模式不建库**:库不存在时抛
|
|
28
|
+
* `MemoraError{ errorCode: FILE_NOT_FOUND }`,宿主据此区分「未初始化」与「真故障」。
|
|
29
|
+
*
|
|
30
|
+
* ## API 子集约束(防「因错误的原因通过」)
|
|
31
|
+
* 根 `@types/node` 版本高于运行时门槛(22.13)⇒ `tsc` 绿**不等于**运行时有该 API。本文件只使用 ≥22.13
|
|
32
|
+
* 已存在的 API:`DatabaseSync` / `prepare` / `run` / `get` / `all` / `exec` / `close` + 选项 `readOnly`
|
|
33
|
+
* (`@since v22.12.0`)。**刻意不使用** `timeout` 选项(`@since v24.0.0`,高于门槛)——忙等待改经
|
|
34
|
+
* `PRAGMA busy_timeout` 设置(全版本可用)。
|
|
35
|
+
*
|
|
36
|
+
* ## 并发
|
|
37
|
+
* 写锁为整库级,多进程(vscode / mcp / dsh)同写须排队(SQLite 默认 `busy_timeout = 0` = 立即报
|
|
38
|
+
* `SQLITE_BUSY`)⇒ 构造时设忙等待;写模式另设 `journal_mode = WAL`(读写不互斥,多进程互见)。
|
|
39
|
+
*/
|
|
40
|
+
import { existsSync } from 'node:fs';
|
|
41
|
+
import { parseMemory } from '../memory/types.js';
|
|
42
|
+
import { escapeLike, validateSource } from '../memory/sourceValidation.js';
|
|
43
|
+
import { segmentLower } from '../utils/segmenter.js';
|
|
44
|
+
import { logger } from '../logging/logger.js';
|
|
45
|
+
import { MemoraError, ToolErrorCode, configError } from '../utils/errors.js';
|
|
46
|
+
import { nowIso } from '../utils/time.js';
|
|
47
|
+
/** 忙等待超时(毫秒):写锁整库级,多进程同写须排队;0(SQLite 默认)会让并发写立即失败 */
|
|
48
|
+
const DEFAULT_BUSY_TIMEOUT_MS = 3000;
|
|
49
|
+
/** 建表语句:列名 snake_case 对齐 SQL 习惯,字段集与 `Memory` 白名单逐一对应 */
|
|
50
|
+
const SCHEMA_SQL = `
|
|
51
|
+
CREATE TABLE IF NOT EXISTS memories (
|
|
52
|
+
id TEXT PRIMARY KEY,
|
|
53
|
+
content TEXT NOT NULL,
|
|
54
|
+
source TEXT NOT NULL,
|
|
55
|
+
name TEXT NOT NULL,
|
|
56
|
+
created_at TEXT NOT NULL,
|
|
57
|
+
accessed_at TEXT NOT NULL,
|
|
58
|
+
deleted_at TEXT,
|
|
59
|
+
metadata TEXT,
|
|
60
|
+
summary_type TEXT,
|
|
61
|
+
session_name TEXT,
|
|
62
|
+
round_id TEXT,
|
|
63
|
+
is_modified INTEGER,
|
|
64
|
+
superseded_by TEXT,
|
|
65
|
+
material_fingerprint TEXT
|
|
66
|
+
)`;
|
|
67
|
+
/** 列清单(显式列出而非 `SELECT *`:列序稳定且与 rowToMemory 对齐,加列时不静默错位) */
|
|
68
|
+
const COLUMNS = [
|
|
69
|
+
'id',
|
|
70
|
+
'content',
|
|
71
|
+
'source',
|
|
72
|
+
'name',
|
|
73
|
+
'created_at',
|
|
74
|
+
'accessed_at',
|
|
75
|
+
'deleted_at',
|
|
76
|
+
'metadata',
|
|
77
|
+
'summary_type',
|
|
78
|
+
'session_name',
|
|
79
|
+
'round_id',
|
|
80
|
+
'is_modified',
|
|
81
|
+
'superseded_by',
|
|
82
|
+
'material_fingerprint',
|
|
83
|
+
].join(', ');
|
|
84
|
+
/** upsert 语句:整对象覆盖(契约「无 CAS 乐观锁」;`ON CONFLICT DO UPDATE` 语义等价于 REPLACE) */
|
|
85
|
+
const UPSERT_SQL = `
|
|
86
|
+
INSERT INTO memories (${COLUMNS})
|
|
87
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
88
|
+
ON CONFLICT(id) DO UPDATE SET
|
|
89
|
+
content = excluded.content,
|
|
90
|
+
source = excluded.source,
|
|
91
|
+
name = excluded.name,
|
|
92
|
+
created_at = excluded.created_at,
|
|
93
|
+
accessed_at = excluded.accessed_at,
|
|
94
|
+
deleted_at = excluded.deleted_at,
|
|
95
|
+
metadata = excluded.metadata,
|
|
96
|
+
summary_type = excluded.summary_type,
|
|
97
|
+
session_name = excluded.session_name,
|
|
98
|
+
round_id = excluded.round_id,
|
|
99
|
+
is_modified = excluded.is_modified,
|
|
100
|
+
superseded_by = excluded.superseded_by,
|
|
101
|
+
material_fingerprint = excluded.material_fingerprint`;
|
|
102
|
+
/**
|
|
103
|
+
* 取 Node 内建 sqlite 模块。
|
|
104
|
+
* 判据显式:模块缺失 → 返回 `undefined`(**不是抛异常**);Node ≤22.2 无 `getBuiltinModule` 方法,走同一判据。
|
|
105
|
+
*/
|
|
106
|
+
function loadSqliteModule() {
|
|
107
|
+
if (typeof process.getBuiltinModule !== 'function')
|
|
108
|
+
return undefined;
|
|
109
|
+
return process.getBuiltinModule('node:sqlite');
|
|
110
|
+
}
|
|
111
|
+
/**
|
|
112
|
+
* 专属错误:「当前运行时无 node:sqlite 实现方」。
|
|
113
|
+
* 复用 `NOT_AVAILABLE`(能力缺席、不可重试)——与「provider 未注入」同语义类,避免为存储另开枚举码
|
|
114
|
+
* 造成单码单义分裂;宿主判据 = `err instanceof MemoraError && err.errorCode === ToolErrorCode.NOT_AVAILABLE`。
|
|
115
|
+
*/
|
|
116
|
+
function sqliteUnavailableError() {
|
|
117
|
+
return new MemoraError({
|
|
118
|
+
title: 'SQLite 存储不可用',
|
|
119
|
+
detail: '当前 Node 运行时未提供 node:sqlite 内建模块(需 Node ≥22.13)',
|
|
120
|
+
suggestions: [
|
|
121
|
+
'升级 Node 至 ≥22.13 后重试',
|
|
122
|
+
'或改用 JSON / 内存存储(宿主按本工厂的抛出结果降级)',
|
|
123
|
+
],
|
|
124
|
+
category: 'config',
|
|
125
|
+
errorCode: ToolErrorCode.NOT_AVAILABLE,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* 专属错误:「只读打开时库文件不存在」。
|
|
130
|
+
*
|
|
131
|
+
* 为什么不把驱动的裸 `ERR_SQLITE_ERROR: unable to open database file` 抛出去:只读宿主
|
|
132
|
+
* (memora-mcp / dsh 等)必须能用**错误码**区分「本工作区还没建库」与「真故障」,
|
|
133
|
+
* 否则每个宿主都得自己 `existsSync`(判据散点),用户看到的也是不可操作的驱动原文。
|
|
134
|
+
*
|
|
135
|
+
* 复用 `FILE_NOT_FOUND`:语义就是「文件不存在」,不为存储另开枚举码。
|
|
136
|
+
*/
|
|
137
|
+
function memoryDbNotFoundError(dbPath) {
|
|
138
|
+
return new MemoraError({
|
|
139
|
+
title: '记忆库文件不存在',
|
|
140
|
+
detail: `${dbPath} 尚未创建(只读模式不建库)`,
|
|
141
|
+
suggestions: [
|
|
142
|
+
'先用写宿主(如 Memora VS Code 插件)打开该工作区一次,以创建并迁移记忆库',
|
|
143
|
+
'确认传入的是工作区根目录,而不是它的子目录',
|
|
144
|
+
],
|
|
145
|
+
category: 'config',
|
|
146
|
+
errorCode: ToolErrorCode.FILE_NOT_FOUND,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* 行 → Memory:字段白名单校验**单一来源** = `parseMemory`(禁在本文件复写字段类型/取值校验)。
|
|
151
|
+
*/
|
|
152
|
+
function rowToMemory(row) {
|
|
153
|
+
return parseMemory({
|
|
154
|
+
id: row.id,
|
|
155
|
+
content: row.content,
|
|
156
|
+
source: row.source,
|
|
157
|
+
name: row.name,
|
|
158
|
+
createdAt: row.created_at,
|
|
159
|
+
accessedAt: row.accessed_at,
|
|
160
|
+
// SQL NULL 交 parseMemory 归一(其已把 null 视为未软删除)——字符串则为其原始软删除时间
|
|
161
|
+
deletedAt: row.deleted_at,
|
|
162
|
+
metadata: row.metadata === null || row.metadata === undefined ? undefined : parseMetadata(row.metadata),
|
|
163
|
+
// 以下可空列须显式把 null 归一为 undefined:parseMemory 对「非 undefined」做类型校验,null 会被判非法
|
|
164
|
+
summaryType: row.summary_type ?? undefined,
|
|
165
|
+
sessionName: row.session_name ?? undefined,
|
|
166
|
+
roundId: row.round_id ?? undefined,
|
|
167
|
+
isModified: row.is_modified === null || row.is_modified === undefined
|
|
168
|
+
? undefined
|
|
169
|
+
: Number(row.is_modified) !== 0,
|
|
170
|
+
supersededBy: row.superseded_by ?? undefined,
|
|
171
|
+
materialFingerprint: row.material_fingerprint ?? undefined,
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
/** 解析 metadata 列的 JSON 文本;非法 JSON 抛错(响亮失败,不静默丢字段) */
|
|
175
|
+
function parseMetadata(raw) {
|
|
176
|
+
try {
|
|
177
|
+
return JSON.parse(String(raw));
|
|
178
|
+
}
|
|
179
|
+
catch (err) {
|
|
180
|
+
throw configError('SQLite 记忆行损坏', `metadata 列不是合法 JSON:${String(raw)}`, ['检查该行是否被外部工具写坏', '或用备份重新迁移'], err instanceof Error ? err : undefined);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/** Memory → 绑定参数(顺序严格对齐 COLUMNS) */
|
|
184
|
+
function memoryToParams(m) {
|
|
185
|
+
return [
|
|
186
|
+
m.id,
|
|
187
|
+
m.content,
|
|
188
|
+
m.source,
|
|
189
|
+
m.name,
|
|
190
|
+
m.createdAt,
|
|
191
|
+
m.accessedAt,
|
|
192
|
+
m.deletedAt ?? null,
|
|
193
|
+
m.metadata === undefined ? null : JSON.stringify(m.metadata),
|
|
194
|
+
m.summaryType ?? null,
|
|
195
|
+
m.sessionName ?? null,
|
|
196
|
+
m.roundId ?? null,
|
|
197
|
+
m.isModified === undefined ? null : m.isModified ? 1 : 0,
|
|
198
|
+
m.supersededBy ?? null,
|
|
199
|
+
m.materialFingerprint ?? null,
|
|
200
|
+
];
|
|
201
|
+
}
|
|
202
|
+
/** 从行里取计数值(`COUNT(*)` 结果列) */
|
|
203
|
+
function rowCount(row) {
|
|
204
|
+
return row === undefined ? 0 : Number(row.c);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 内部实现类(不挂公共面)。所有方法同步,语义逐位对齐 `InMemoryStorage`(契约对拍测试兜底)。
|
|
208
|
+
*/
|
|
209
|
+
class NodeSqliteStorage {
|
|
210
|
+
db;
|
|
211
|
+
/** 写模式才建表 + 设 WAL;只读模式连表都不建(宿主只读打开既有库) */
|
|
212
|
+
stmtUpsert;
|
|
213
|
+
stmtSoftDelete;
|
|
214
|
+
stmtRestore;
|
|
215
|
+
stmtPurge;
|
|
216
|
+
stmtListDeleted;
|
|
217
|
+
stmtListDeletedLimit;
|
|
218
|
+
stmtGetDeletedById;
|
|
219
|
+
stmtPurgeExpired;
|
|
220
|
+
stmtGetActiveById;
|
|
221
|
+
stmtGetActiveBySource;
|
|
222
|
+
stmtActiveByAccessedDesc;
|
|
223
|
+
stmtGetDeletedAt;
|
|
224
|
+
stmtCount;
|
|
225
|
+
stmtCountBySource;
|
|
226
|
+
stmtTouch;
|
|
227
|
+
stmtAllSources;
|
|
228
|
+
/** search 语句按 token 数缓存(每 token 两个 LIKE 占位),避免每次调用重新 prepare */
|
|
229
|
+
searchStmtCache = new Map();
|
|
230
|
+
closed = false;
|
|
231
|
+
constructor(db, writeMode) {
|
|
232
|
+
this.db = db;
|
|
233
|
+
// 忙等待:多进程同写排队,而非立即 SQLITE_BUSY(用 PRAGMA 而非 DatabaseSync 的 timeout 选项——
|
|
234
|
+
// 后者 @since v24.0.0,高于本实现的 22.13 门槛)
|
|
235
|
+
this.db.exec(`PRAGMA busy_timeout = ${DEFAULT_BUSY_TIMEOUT_MS}`);
|
|
236
|
+
if (writeMode) {
|
|
237
|
+
// WAL:读写不互斥,多进程互见(这是本次换 SQLite 的核心收益)
|
|
238
|
+
this.db.exec('PRAGMA journal_mode = WAL');
|
|
239
|
+
this.db.exec(SCHEMA_SQL);
|
|
240
|
+
}
|
|
241
|
+
this.stmtUpsert = this.prepare(UPSERT_SQL);
|
|
242
|
+
this.stmtSoftDelete = this.prepare('UPDATE memories SET deleted_at = ? WHERE id = ? AND deleted_at IS NULL');
|
|
243
|
+
this.stmtRestore = this.prepare('UPDATE memories SET deleted_at = NULL WHERE id = ? AND deleted_at IS NOT NULL');
|
|
244
|
+
this.stmtPurge = this.prepare('DELETE FROM memories WHERE id = ?');
|
|
245
|
+
this.stmtListDeleted = this.prepare(`SELECT ${COLUMNS} FROM memories WHERE deleted_at IS NOT NULL ORDER BY deleted_at DESC`);
|
|
246
|
+
this.stmtListDeletedLimit = this.prepare(`SELECT ${COLUMNS} FROM memories WHERE deleted_at IS NOT NULL ORDER BY deleted_at DESC LIMIT ?`);
|
|
247
|
+
this.stmtGetDeletedById = this.prepare(`SELECT ${COLUMNS} FROM memories WHERE id = ? AND deleted_at IS NOT NULL`);
|
|
248
|
+
this.stmtPurgeExpired = this.prepare('DELETE FROM memories WHERE deleted_at IS NOT NULL AND deleted_at < ?');
|
|
249
|
+
this.stmtGetActiveById = this.prepare(`SELECT ${COLUMNS} FROM memories WHERE id = ? AND deleted_at IS NULL`);
|
|
250
|
+
this.stmtGetActiveBySource = this.prepare(`SELECT ${COLUMNS} FROM memories WHERE source = ? AND deleted_at IS NULL ORDER BY accessed_at DESC`);
|
|
251
|
+
this.stmtActiveByAccessedDesc = this.prepare(`SELECT ${COLUMNS} FROM memories WHERE deleted_at IS NULL ORDER BY accessed_at DESC LIMIT ?`);
|
|
252
|
+
this.stmtGetDeletedAt = this.prepare('SELECT deleted_at FROM memories WHERE id = ?');
|
|
253
|
+
this.stmtCount = this.prepare('SELECT COUNT(*) AS c FROM memories WHERE deleted_at IS NULL');
|
|
254
|
+
this.stmtCountBySource = this.prepare('SELECT COUNT(*) AS c FROM memories WHERE source = ? AND deleted_at IS NULL');
|
|
255
|
+
this.stmtTouch = this.prepare('UPDATE memories SET accessed_at = ? WHERE id = ? AND deleted_at IS NULL');
|
|
256
|
+
this.stmtAllSources = this.prepare('SELECT source AS s, COUNT(*) AS c FROM memories WHERE deleted_at IS NULL GROUP BY source');
|
|
257
|
+
}
|
|
258
|
+
/** prepare 包装(集中类型收口点,免每处重复断言) */
|
|
259
|
+
prepare(sql) {
|
|
260
|
+
return this.db.prepare(sql);
|
|
261
|
+
}
|
|
262
|
+
upsert(memory) {
|
|
263
|
+
const result = validateSource(memory.source);
|
|
264
|
+
if (result.severity === 'block') {
|
|
265
|
+
throw configError('source 校验失败,拒绝写入', result.warning, [
|
|
266
|
+
'请检查 source 字段是否拼写正确',
|
|
267
|
+
'参考 source 开放字符串规范',
|
|
268
|
+
]);
|
|
269
|
+
}
|
|
270
|
+
if (result.severity === 'warn' && result.warning) {
|
|
271
|
+
logger.warn({ id: memory.id, source: memory.source, warning: result.warning }, 'source 校验警告');
|
|
272
|
+
}
|
|
273
|
+
// 软删除复活拦截:与 InMemoryStorage 同判据(须先显式 restore,或新对象自带 deletedAt)
|
|
274
|
+
const existing = this.stmtGetDeletedAt.get(memory.id);
|
|
275
|
+
if (existing && existing.deleted_at !== null && memory.deletedAt === undefined) {
|
|
276
|
+
throw configError('不允许通过 upsert 复活软删除记忆', `id=${memory.id} 已被软删除(deletedAt=${String(existing.deleted_at)})`, [
|
|
277
|
+
'若需恢复:先调用 restore(id),再 upsert',
|
|
278
|
+
'若需覆盖软删除态:在 newMemory 中显式传入 deletedAt 字段',
|
|
279
|
+
'参考软删除机制设计',
|
|
280
|
+
]);
|
|
281
|
+
}
|
|
282
|
+
this.stmtUpsert.run(...memoryToParams(memory));
|
|
283
|
+
}
|
|
284
|
+
delete(id) {
|
|
285
|
+
this.stmtSoftDelete.run(nowIso(), id);
|
|
286
|
+
}
|
|
287
|
+
restore(id) {
|
|
288
|
+
this.stmtRestore.run(id);
|
|
289
|
+
}
|
|
290
|
+
purge(id) {
|
|
291
|
+
this.stmtPurge.run(id);
|
|
292
|
+
}
|
|
293
|
+
listDeleted(limit) {
|
|
294
|
+
const rows = limit !== undefined && limit > 0
|
|
295
|
+
? this.stmtListDeletedLimit.all(limit)
|
|
296
|
+
: this.stmtListDeleted.all();
|
|
297
|
+
return rows.map(rowToMemory);
|
|
298
|
+
}
|
|
299
|
+
getDeletedById(id) {
|
|
300
|
+
const row = this.stmtGetDeletedById.get(id);
|
|
301
|
+
return row === undefined ? null : rowToMemory(row);
|
|
302
|
+
}
|
|
303
|
+
purgeExpired(before) {
|
|
304
|
+
const result = this.stmtPurgeExpired.run(before.toISOString());
|
|
305
|
+
return Number(result.changes);
|
|
306
|
+
}
|
|
307
|
+
getById(id) {
|
|
308
|
+
const row = this.stmtGetActiveById.get(id);
|
|
309
|
+
return row === undefined ? null : rowToMemory(row);
|
|
310
|
+
}
|
|
311
|
+
getBySource(source) {
|
|
312
|
+
return this.stmtGetActiveBySource.all(source).map(rowToMemory);
|
|
313
|
+
}
|
|
314
|
+
search(query, limit = 10) {
|
|
315
|
+
// 空查询 / 分词零 token:退化为「按 accessedAt 降序取前 N」(与 InMemoryStorage 同语义)
|
|
316
|
+
const tokens = query.trim() ? segmentLower(query) : [];
|
|
317
|
+
if (tokens.length === 0) {
|
|
318
|
+
return this.stmtActiveByAccessedDesc.all(limit).map(rowToMemory);
|
|
319
|
+
}
|
|
320
|
+
const stmt = this.searchStatement(tokens.length);
|
|
321
|
+
// 每 token 两个 LIKE(content / name);转义后必须配 ESCAPE '\',否则含 % / _ 的字面值零召回
|
|
322
|
+
const params = [];
|
|
323
|
+
for (const token of tokens) {
|
|
324
|
+
const pattern = `%${escapeLike(token)}%`;
|
|
325
|
+
params.push(pattern, pattern);
|
|
326
|
+
}
|
|
327
|
+
params.push(limit);
|
|
328
|
+
return stmt.all(...params).map(rowToMemory);
|
|
329
|
+
}
|
|
330
|
+
/** 按 token 数取(必要时建)search 语句:任一 token 命中 content/name 即入选 */
|
|
331
|
+
searchStatement(tokenCount) {
|
|
332
|
+
const cached = this.searchStmtCache.get(tokenCount);
|
|
333
|
+
if (cached)
|
|
334
|
+
return cached;
|
|
335
|
+
const clauses = Array.from({ length: tokenCount }, () => `(content LIKE ? ESCAPE '\\' OR name LIKE ? ESCAPE '\\')`).join(' OR ');
|
|
336
|
+
const stmt = this.prepare(`SELECT ${COLUMNS} FROM memories WHERE deleted_at IS NULL AND (${clauses}) ORDER BY accessed_at DESC LIMIT ?`);
|
|
337
|
+
this.searchStmtCache.set(tokenCount, stmt);
|
|
338
|
+
return stmt;
|
|
339
|
+
}
|
|
340
|
+
count() {
|
|
341
|
+
return rowCount(this.stmtCount.get());
|
|
342
|
+
}
|
|
343
|
+
countBySource(source) {
|
|
344
|
+
return rowCount(this.stmtCountBySource.get(source));
|
|
345
|
+
}
|
|
346
|
+
touch(id, now) {
|
|
347
|
+
return Number(this.stmtTouch.run(now, id).changes) > 0;
|
|
348
|
+
}
|
|
349
|
+
getAllSources() {
|
|
350
|
+
const out = new Map();
|
|
351
|
+
for (const row of this.stmtAllSources.all()) {
|
|
352
|
+
out.set(String(row.s), Number(row.c));
|
|
353
|
+
}
|
|
354
|
+
return out;
|
|
355
|
+
}
|
|
356
|
+
close() {
|
|
357
|
+
if (this.closed)
|
|
358
|
+
return;
|
|
359
|
+
this.closed = true;
|
|
360
|
+
this.db.close();
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
/** 构建只读视图:仅含读方法 + close(结构上拿不到写方法),供只读宿主复用 */
|
|
364
|
+
function toReadOnlyView(impl) {
|
|
365
|
+
return Object.freeze({
|
|
366
|
+
getById: (id) => impl.getById(id),
|
|
367
|
+
getBySource: (source) => impl.getBySource(source),
|
|
368
|
+
search: (query, limit) => impl.search(query, limit),
|
|
369
|
+
count: () => impl.count(),
|
|
370
|
+
countBySource: (source) => impl.countBySource(source),
|
|
371
|
+
getAllSources: () => impl.getAllSources(),
|
|
372
|
+
listDeleted: (limit) => impl.listDeleted(limit),
|
|
373
|
+
getDeletedById: (id) => impl.getDeletedById(id),
|
|
374
|
+
close: () => impl.close?.(),
|
|
375
|
+
});
|
|
376
|
+
}
|
|
377
|
+
export function createNodeSqliteStorage(dbPath, options) {
|
|
378
|
+
const mod = loadSqliteModule();
|
|
379
|
+
if (!mod)
|
|
380
|
+
throw sqliteUnavailableError();
|
|
381
|
+
const readOnly = options?.readOnly === true;
|
|
382
|
+
// 只读模式不建库:先判存在,把「未初始化」收成专属错误码(见 memoryDbNotFoundError)。
|
|
383
|
+
// 判存在与打开之间存在 TOCTOU 窗口,但文件被删时 DatabaseSync 会抛裸错——真故障原样暴露,不吞。
|
|
384
|
+
if (readOnly && !existsSync(dbPath))
|
|
385
|
+
throw memoryDbNotFoundError(dbPath);
|
|
386
|
+
// 注意:不能写 `new DatabaseSync(path, undefined)`——第二个实参一旦出现,Node 参数校验即要求它是对象
|
|
387
|
+
const db = readOnly
|
|
388
|
+
? new mod.DatabaseSync(dbPath, { readOnly: true })
|
|
389
|
+
: new mod.DatabaseSync(dbPath);
|
|
390
|
+
const impl = new NodeSqliteStorage(db, !readOnly);
|
|
391
|
+
return readOnly ? toReadOnlyView(impl) : impl;
|
|
392
|
+
}
|
|
393
|
+
//# sourceMappingURL=nodeSqliteStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nodeSqliteStorage.js","sourceRoot":"","sources":["../../src/memory/nodeSqliteStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import type { IMemoryStorage } from '../memory/storageInterface.js';
|
|
2
2
|
import type { SecurityGuard } from '../security/pathGuard.js';
|
|
3
3
|
import { type Memory } from '../memory/types.js';
|
|
4
|
-
import { type ProjectEntry } from '../memory/projectRegistry.js';
|
|
5
4
|
/** 项目上下文:打开一个项目后产出的一组组件(含项目级信息) */
|
|
6
5
|
export interface ProjectContext {
|
|
7
6
|
/** 项目根目录的绝对路径 */
|
|
8
7
|
projectPath: string;
|
|
9
|
-
/**
|
|
8
|
+
/** 项目名称(显式传入优先,否则取目录名;内核不读任何注册表) */
|
|
10
9
|
projectName: string;
|
|
11
10
|
/** .memora/ 目录的绝对路径 */
|
|
12
11
|
memoraDir: string;
|
|
@@ -23,22 +22,21 @@ export interface ProjectManagerOptions {
|
|
|
23
22
|
dataDir: string;
|
|
24
23
|
/** 外部注入存储实例(可选,不传则内部 InMemoryStorage 兜底) */
|
|
25
24
|
storage?: IMemoryStorage;
|
|
26
|
-
/** 注册表目录(可选,缺省同 dataDir;跨项目按名解析需要多项目共用一份注册表) */
|
|
27
|
-
registryDir?: string;
|
|
28
25
|
/**
|
|
29
26
|
* SecurityGuard 工厂函数(Agent 层注入,解除 memory→security 反向依赖)。
|
|
30
|
-
*
|
|
27
|
+
* 在 initProject 时调用一次;不提供则 ProjectContext.security 为 null。
|
|
31
28
|
*/
|
|
32
29
|
createSecurityGuard?: (projectPath: string, memoraDir: string, configDir?: string, agentDataDir?: string) => SecurityGuard;
|
|
33
30
|
}
|
|
34
31
|
/**
|
|
35
|
-
*
|
|
36
|
-
*
|
|
32
|
+
* 项目管理器:**单个**项目的生命周期编排——初始化(加锁 → 确保存储 → 构建上下文)、
|
|
33
|
+
* 关闭与锁释放。一个 Agent 绑定一个项目(2026-10-18 形态定案),因此本类**不持跨项目状态**:
|
|
34
|
+
* 无切换入口、无注册表、无项目名单。多项目由宿主各建一个 Agent 实例。
|
|
37
35
|
*/
|
|
38
36
|
export declare class ProjectManager {
|
|
39
37
|
/** Agent 级数据目录(宿主传入;内核不假设其下的文件形态) */
|
|
40
38
|
private readonly agentDataDir;
|
|
41
|
-
/** Agent
|
|
39
|
+
/** Agent 级存储实例(实例内唯一,init 后不再重建) */
|
|
42
40
|
private agentIndex;
|
|
43
41
|
/** 当前打开的项目路径 */
|
|
44
42
|
private currentProjectPath;
|
|
@@ -46,35 +44,27 @@ export declare class ProjectManager {
|
|
|
46
44
|
private externalStorage;
|
|
47
45
|
/** SecurityGuard 工厂函数(Agent 层注入) */
|
|
48
46
|
private readonly createSecurityGuard?;
|
|
49
|
-
/** 项目注册表(专职 projects.json) */
|
|
50
|
-
private readonly registry;
|
|
51
47
|
/** 锁文件管理器(专职 .memora/.lock) */
|
|
52
48
|
private readonly lockManager;
|
|
53
49
|
constructor(options: ProjectManagerOptions);
|
|
54
50
|
/**
|
|
55
|
-
* 确保 Agent
|
|
51
|
+
* 确保 Agent 级资源(存储实例)已初始化,Agent 实例生命周期内保持同一实例。
|
|
56
52
|
* 构造注入的外部实例直接用,否则内部 InMemoryStorage(非持久化,仅开发/测试)。
|
|
57
53
|
*/
|
|
58
54
|
private ensureAgentResources;
|
|
59
55
|
/**
|
|
60
|
-
*
|
|
56
|
+
* 初始化项目:防重入释放旧锁→取项目级锁→确保存储→构建上下文。
|
|
61
57
|
* 任一步失败释放锁并重置状态,防锁文件残留导致下次启动检测失败。
|
|
62
58
|
*/
|
|
63
59
|
initProject(projectPath: string, projectName?: string, configDir?: string): Promise<ProjectContext>;
|
|
64
60
|
/**
|
|
65
|
-
* 构建项目上下文:bootstrap 过滤(恒为空,设定记忆归角色包层)+ 安全守卫创建 +
|
|
61
|
+
* 构建项目上下文:bootstrap 过滤(恒为空,设定记忆归角色包层)+ 安全守卫创建 + 名称推断 + 日志。
|
|
66
62
|
*/
|
|
67
63
|
private buildProjectContext;
|
|
68
64
|
/** 关闭当前项目:释放锁/清状态,但不关 Agent 级 DB(共享,Agent 生命周期内持久存在) */
|
|
69
65
|
closeProject(): Promise<void>;
|
|
70
|
-
/** 完全关闭:释放项目锁 + 关闭 Agent
|
|
66
|
+
/** 完全关闭:释放项目锁 + 关闭 Agent 级数据库(由 `Agent.close()` 调用) */
|
|
71
67
|
shutdown(): Promise<void>;
|
|
72
|
-
/** 列出已注册项目(委托 ProjectRegistry) */
|
|
73
|
-
get list(): ProjectEntry[];
|
|
74
|
-
/** 注册项目(委托 ProjectRegistry) */
|
|
75
|
-
registerProject(projectPath: string, name: string): void;
|
|
76
|
-
/** 移除项目(委托 ProjectRegistry) */
|
|
77
|
-
unregisterProject(projectPath: string): void;
|
|
78
68
|
/** 当前项目路径 */
|
|
79
69
|
get currentProject(): string | null;
|
|
80
70
|
/** 解析项目 .memora/ 目录(项目根目录下,区别于用户级 dataDir) */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"projectManager.d.ts","sourceRoot":"","sources":["../../src/memory/projectManager.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAG7D,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"projectManager.d.ts","sourceRoot":"","sources":["../../src/memory/projectManager.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACnE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAC;AAG7D,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAehD,mCAAmC;AACnC,MAAM,WAAW,cAAc;IAC7B,iBAAiB;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,oCAAoC;IACpC,WAAW,EAAE,MAAM,CAAC;IACpB,uBAAuB;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,kCAAkC;IAClC,KAAK,EAAE,cAAc,CAAC;IACtB,+BAA+B;IAC/B,QAAQ,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,kCAAkC;IAClC,iBAAiB,EAAE,MAAM,EAAE,CAAC;CAC7B;AAED,0BAA0B;AAC1B,MAAM,WAAW,qBAAqB;IACpC,qCAAqC;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,4CAA4C;IAC5C,OAAO,CAAC,EAAE,cAAc,CAAC;IACzB;;;OAGG;IACH,mBAAmB,CAAC,EAAE,CACpB,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,EACjB,SAAS,CAAC,EAAE,MAAM,EAClB,YAAY,CAAC,EAAE,MAAM,KAClB,aAAa,CAAC;CACpB;AAID;;;;GAIG;AACH,qBAAa,cAAc;IACzB,qCAAqC;IACrC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,oCAAoC;IACpC,OAAO,CAAC,UAAU,CAA+B;IACjD,gBAAgB;IAChB,OAAO,CAAC,kBAAkB,CAAuB;IACjD,yCAAyC;IACzC,OAAO,CAAC,eAAe,CAAwB;IAC/C,oCAAoC;IACpC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAC,CAKlB;IACnB,+BAA+B;IAC/B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAc;gBAE9B,OAAO,EAAE,qBAAqB;IAQ1C;;;OAGG;YACW,oBAAoB;IAiBlC;;;OAGG;IACG,WAAW,CACf,WAAW,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,EACpB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,cAAc,CAAC;IA0B1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IAqC3B,wDAAwD;IAClD,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC;IAKnC,uDAAuD;IACjD,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;IAc/B,aAAa;IACb,IAAI,cAAc,IAAI,MAAM,GAAG,IAAI,CAElC;IAED,8CAA8C;IAC9C,OAAO,CAAC,gBAAgB;CAGzB"}
|
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* 项目管理器 —
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
2
|
+
* 项目管理器 — 单个项目的生命周期编排(单 Agent 模型)。
|
|
3
|
+
* 职责:项目初始化编排(加锁 → 确保 Agent 级存储 → 构建上下文)、关闭与锁释放。
|
|
4
|
+
* 边界(2026-10-18 定案):一个 Agent 绑定一个项目;**多项目由宿主创建多个 Agent 实例**,
|
|
5
|
+
* 内核既不提供单实例内切换、也不维护跨项目名单(历史 `switchProject` / `projects.json`
|
|
6
|
+
* 注册表整链已随 PR-SWITCH-1 删除)。项目名只从路径推断,内核不落任何跨项目状态。
|
|
7
7
|
*/
|
|
8
|
-
import { resolve
|
|
8
|
+
import { resolve } from 'node:path';
|
|
9
9
|
import { mkdir } from 'node:fs/promises';
|
|
10
10
|
import { InMemoryStorage } from '../memory/inMemoryStorage.js';
|
|
11
11
|
import { logger } from '../logging/logger.js';
|
|
12
12
|
import { expandHome } from '../utils/path.js';
|
|
13
|
-
import { ProjectRegistry } from '../memory/projectRegistry.js';
|
|
14
13
|
import { LockManager } from '../memory/lockManager.js';
|
|
14
|
+
/**
|
|
15
|
+
* 从路径推断项目名称(取最后一段目录名,无法推断返回 'unnamed')。
|
|
16
|
+
* 原为 ProjectRegistry 静态方法,注册表整链删除后摘为本模块级纯函数——项目名是
|
|
17
|
+
* `ProjectContext.projectName` 的唯一来源,推断逻辑不该随注册表一起消失。
|
|
18
|
+
*/
|
|
19
|
+
function inferProjectName(projectPath) {
|
|
20
|
+
const parts = projectPath.replace(/[/\\]+$/, '').split(/[/\\]/);
|
|
21
|
+
return parts[parts.length - 1] || 'unnamed';
|
|
22
|
+
}
|
|
15
23
|
// ─── 类 ──────────────────────────────────────────────────
|
|
16
24
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
25
|
+
* 项目管理器:**单个**项目的生命周期编排——初始化(加锁 → 确保存储 → 构建上下文)、
|
|
26
|
+
* 关闭与锁释放。一个 Agent 绑定一个项目(2026-10-18 形态定案),因此本类**不持跨项目状态**:
|
|
27
|
+
* 无切换入口、无注册表、无项目名单。多项目由宿主各建一个 Agent 实例。
|
|
19
28
|
*/
|
|
20
29
|
export class ProjectManager {
|
|
21
30
|
/** Agent 级数据目录(宿主传入;内核不假设其下的文件形态) */
|
|
22
31
|
agentDataDir;
|
|
23
|
-
/** Agent
|
|
32
|
+
/** Agent 级存储实例(实例内唯一,init 后不再重建) */
|
|
24
33
|
agentIndex = null;
|
|
25
34
|
/** 当前打开的项目路径 */
|
|
26
35
|
currentProjectPath = null;
|
|
@@ -28,26 +37,17 @@ export class ProjectManager {
|
|
|
28
37
|
externalStorage;
|
|
29
38
|
/** SecurityGuard 工厂函数(Agent 层注入) */
|
|
30
39
|
createSecurityGuard;
|
|
31
|
-
/** 项目注册表(专职 projects.json) */
|
|
32
|
-
registry;
|
|
33
40
|
/** 锁文件管理器(专职 .memora/.lock) */
|
|
34
41
|
lockManager;
|
|
35
42
|
constructor(options) {
|
|
36
|
-
const { dataDir, storage,
|
|
37
|
-
|
|
38
|
-
this.agentDataDir = memoraHome;
|
|
39
|
-
// 注册表目录:显式指定时用之(典型为用户级路径,使注册表跨项目共享);
|
|
40
|
-
// 缺省随 dataDir——此时注册表能否跨项目取决于调用方传的 dataDir 是共享还是项目级,
|
|
41
|
-
// 内核不做假设:项目级 dataDir 下每个项目只注册自身,按名切换不成立(switchProject 已加守卫)。
|
|
42
|
-
const registryHome = registryDir ? resolve(expandHome(registryDir)) : memoraHome;
|
|
43
|
+
const { dataDir, storage, createSecurityGuard } = options;
|
|
44
|
+
this.agentDataDir = resolve(expandHome(dataDir));
|
|
43
45
|
this.externalStorage = storage ?? null;
|
|
44
46
|
this.createSecurityGuard = createSecurityGuard;
|
|
45
|
-
// 注册表/锁文件委托专职模块
|
|
46
|
-
this.registry = new ProjectRegistry(join(registryHome, 'projects.json'));
|
|
47
47
|
this.lockManager = new LockManager();
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
|
-
* 确保 Agent
|
|
50
|
+
* 确保 Agent 级资源(存储实例)已初始化,Agent 实例生命周期内保持同一实例。
|
|
51
51
|
* 构造注入的外部实例直接用,否则内部 InMemoryStorage(非持久化,仅开发/测试)。
|
|
52
52
|
*/
|
|
53
53
|
async ensureAgentResources() {
|
|
@@ -64,11 +64,11 @@ export class ProjectManager {
|
|
|
64
64
|
return { index: this.agentIndex };
|
|
65
65
|
}
|
|
66
66
|
/**
|
|
67
|
-
*
|
|
67
|
+
* 初始化项目:防重入释放旧锁→取项目级锁→确保存储→构建上下文。
|
|
68
68
|
* 任一步失败释放锁并重置状态,防锁文件残留导致下次启动检测失败。
|
|
69
69
|
*/
|
|
70
70
|
async initProject(projectPath, projectName, configDir) {
|
|
71
|
-
// 1)
|
|
71
|
+
// 1) 防重入:本实例已打开过项目则先释放旧锁(不关 Agent 级 DB)
|
|
72
72
|
if (this.currentProjectPath) {
|
|
73
73
|
await this.closeProject();
|
|
74
74
|
}
|
|
@@ -93,7 +93,7 @@ export class ProjectManager {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
/**
|
|
96
|
-
* 构建项目上下文:bootstrap 过滤(恒为空,设定记忆归角色包层)+ 安全守卫创建 +
|
|
96
|
+
* 构建项目上下文:bootstrap 过滤(恒为空,设定记忆归角色包层)+ 安全守卫创建 + 名称推断 + 日志。
|
|
97
97
|
*/
|
|
98
98
|
buildProjectContext(projectPath, projectName, memoraDir, index, configDir) {
|
|
99
99
|
// 设定记忆唯一归角色包内容层,由 assembler 角色包路径接管,恒为空数组
|
|
@@ -102,9 +102,7 @@ export class ProjectManager {
|
|
|
102
102
|
const security = this.createSecurityGuard
|
|
103
103
|
? this.createSecurityGuard(projectPath, memoraDir, configDir, this.agentDataDir)
|
|
104
104
|
: null;
|
|
105
|
-
|
|
106
|
-
const name = projectName || ProjectRegistry.inferProjectName(projectPath);
|
|
107
|
-
this.registry.register(projectPath, name);
|
|
105
|
+
const name = projectName || inferProjectName(projectPath);
|
|
108
106
|
logger.info({
|
|
109
107
|
projectPath,
|
|
110
108
|
projectName: name,
|
|
@@ -125,7 +123,7 @@ export class ProjectManager {
|
|
|
125
123
|
await this.lockManager.release();
|
|
126
124
|
this.currentProjectPath = null;
|
|
127
125
|
}
|
|
128
|
-
/** 完全关闭:释放项目锁 + 关闭 Agent
|
|
126
|
+
/** 完全关闭:释放项目锁 + 关闭 Agent 级数据库(由 `Agent.close()` 调用) */
|
|
129
127
|
async shutdown() {
|
|
130
128
|
if (this.currentProjectPath) {
|
|
131
129
|
await this.closeProject();
|
|
@@ -140,18 +138,6 @@ export class ProjectManager {
|
|
|
140
138
|
this.agentIndex = null;
|
|
141
139
|
}
|
|
142
140
|
}
|
|
143
|
-
/** 列出已注册项目(委托 ProjectRegistry) */
|
|
144
|
-
get list() {
|
|
145
|
-
return this.registry.list;
|
|
146
|
-
}
|
|
147
|
-
/** 注册项目(委托 ProjectRegistry) */
|
|
148
|
-
registerProject(projectPath, name) {
|
|
149
|
-
this.registry.register(projectPath, name);
|
|
150
|
-
}
|
|
151
|
-
/** 移除项目(委托 ProjectRegistry) */
|
|
152
|
-
unregisterProject(projectPath) {
|
|
153
|
-
this.registry.unregister(projectPath);
|
|
154
|
-
}
|
|
155
141
|
/** 当前项目路径 */
|
|
156
142
|
get currentProject() {
|
|
157
143
|
return this.currentProjectPath;
|