diff --git a/README.md b/README.md index 2f8f21f..0327460 100644 --- a/README.md +++ b/README.md @@ -48,122 +48,83 @@ ts/ ## 在 WaterFlow 中使用 -本模块支持两种使用方式:**作为模块直接引用** 或 **作为 Skill 调用**。 +本模块完全不动 WaterFlow 源码,只需在你的入口文件中注册即可。 -### 方式一:作为模块直接引用(适合开发者集成) +### 快速开始(推荐) -#### 步骤 1:复制源码 - -将本项目的 `ts/` 目录复制到你的 WaterFlow 项目中,例如: - -``` -你的WaterFlow项目/ -├── src/ -│ └── runtime/ -│ └── core/ -│ └── graph_memory/ # 从 ts/src/runtime/core/ 复制 -└── ts/ # 或直接放在项目根目录 - └── bundled-skills/ # Skill 文件 -``` - -#### 步骤 2:编译 TypeScript +#### 步骤 1:安装依赖 ```bash -cd ts/ -npm install -npm run build +npm install /path/to/TrulyMEM-TrueHumanMEM/ts ``` -编译后的文件会输出到 `ts/dist/` 目录。 +或在 `package.json` 中添加: -#### 步骤 3:在代码中引用 +```json +{ + "dependencies": { + "trulymem-waterflow": "file:../TrulyMEM-TrueHumanMEM/ts" + } +} +``` + +然后运行: + +```bash +npm install +``` + +#### 步骤 2:在你的入口文件中注册 + +只需两行代码,完全不动 WaterFlow 源码: ```typescript -import { createGraphMemoryTool } from './runtime/core/tools/builtin/graph_memory_tool'; +import { getPlatform } from 'waterflow/platform'; +import { installTrulyMEM } from 'trulymem/tools'; -// 创建工具实例,可以传入 sessionId 来区分不同会话 -const tool = createGraphMemoryTool('my-session-id'); +// 一行安装,返回配置好的 ToolRegistry +const registry = installTrulyMEM(getPlatform(), 'my-session-id'); -// 准备执行上下文 -const context = { - toolCallId: 'call-123', - workingDirectory: '/project', - abortController: { signal: {} }, - config: { timeout: 30000 }, - logger: { - info: console.log, - warn: console.warn, - error: console.error, - debug: console.debug - } -}; - -// 写入记忆示例 -const commitResult = await tool.handler({ - action: 'commit', - params: { - triplets: [ - { subject: '用户', relation: '喜欢', object: '编程' }, - { subject: '用户', relation: '正在学习', object: 'TypeScript' } - ] - } -}, context); - -console.log(commitResult); -// 输出: {"success":true,"data":{"createdEntities":4,"createdRelations":2}} - -// 检索记忆示例 -const recallResult = await tool.handler({ - action: 'recall', - params: { - queryIntent: '用户 编程' - } -}, context); - -console.log(recallResult); -// 输出: {"success":true,"data":{"entities":[...],"relations":[...],"message":"找到 X 个实体, Y 条关系"}} +// 继续组装 WaterFlow... +const toolExecutor = new ToolExecutor(registry); ``` -### 方式二:使用 Skill(推荐,适合 AI Agent 调用) +### 手动注册(更灵活) + +如果你想自己控制 ToolRegistry 的创建: + +```typescript +import { getPlatform } from 'waterflow/platform'; +import { initializeToolRegistry } from 'waterflow/runtime/core/tools/builtin'; +import { registerGraphMemoryTool } from 'trulymem/tools'; + +const platform = getPlatform(); +const registry = initializeToolRegistry(platform); + +// 注册图记忆工具 +registerGraphMemoryTool(registry, 'my-session-id'); + +// 继续组装... +``` + +### 使用 Skill(AI Agent 调用) #### 步骤 1:配置 Skill 来源 -在你的 WaterFlow 项目中,找到 Skill 配置文件,添加 bundled 来源指向本项目的 Skill 目录: - ```typescript -// skill_interface.ts 或配置文件中 -import { DEFAULT_SKILL_LOADER_CONFIG } from './skill_interface'; - const config = { ...DEFAULT_SKILL_LOADER_CONFIG, sources: { ...DEFAULT_SKILL_LOADER_CONFIG.sources, - bundled: './ts/bundled-skills' // 指向本项目的 Skill 目录 + bundled: './node_modules/trulymem-waterflow/bundled-skills' }, enabledSources: ['project', 'bundled'] }; ``` -#### 步骤 2:通过 Agent 调用 Skill +#### 步骤 2:通过 Agent 调用 -在你的 Agent 或 Workflow 中,通过 Tool 调用 Skill: - -``` -使用 skill:graph_memory 进行以下操作: - -1. 写入记忆: 我喜欢编程,正在学习 TypeScript -2. 检索记忆: 找出我和编程相关的记忆 -``` - -或者通过代码调用: - -```typescript -// 通过 SkillTool 调用 -const skillResult = await skillTool.handler({ - skill: 'graph_memory', - args: 'recall - queryIntent: "用户 学习"' -}, context); -``` +AI Agent 会自动读取 SKILL.md 并调用 `builtin:graph_memory` 工具。 #### 可用 Skill 列表 diff --git a/README_EN.md b/README_EN.md index 50c8d56..fafffe6 100644 --- a/README_EN.md +++ b/README_EN.md @@ -49,122 +49,83 @@ ts/ ## Usage in WaterFlow -This module supports two usage methods: **import as module** or **use as Skill**. +This module requires **zero changes** to WaterFlow source code. Just register it in your entry file. -### Method 1: Import as Module (for developer integration) +### Quick Start (Recommended) -#### Step 1: Copy source files - -Copy the `ts/` directory to your WaterFlow project, for example: - -``` -your-waterflow-project/ -├── src/ -│ └── runtime/ -│ └── core/ -│ └── graph_memory/ # Copy from ts/src/runtime/core/ -└── ts/ # Or place in project root - └── bundled-skills/ # Skill files -``` - -#### Step 2: Build TypeScript +#### Step 1: Install ```bash -cd ts/ -npm install -npm run build +npm install /path/to/TrulyMEM-TrueHumanMEM/ts ``` -Compiled files will be output to `ts/dist/`. +Or add to `package.json`: -#### Step 3: Import in your code +```json +{ + "dependencies": { + "trulymem-waterflow": "file:../TrulyMEM-TrueHumanMEM/ts" + } +} +``` + +Then run: + +```bash +npm install +``` + +#### Step 2: Register in your entry file + +Just two lines, zero changes to WaterFlow: ```typescript -import { createGraphMemoryTool } from './runtime/core/tools/builtin/graph_memory_tool'; +import { getPlatform } from 'waterflow/platform'; +import { installTrulyMEM } from 'trulymem/tools'; -// Create tool instance, can pass sessionId to distinguish different sessions -const tool = createGraphMemoryTool('my-session-id'); +// One-line install, returns configured ToolRegistry +const registry = installTrulyMEM(getPlatform(), 'my-session-id'); -// Prepare execution context -const context = { - toolCallId: 'call-123', - workingDirectory: '/project', - abortController: { signal: {} }, - config: { timeout: 30000 }, - logger: { - info: console.log, - warn: console.warn, - error: console.error, - debug: console.debug - } -}; - -// Commit memory example -const commitResult = await tool.handler({ - action: 'commit', - params: { - triplets: [ - { subject: 'User', relation: 'likes', object: 'Programming' }, - { subject: 'User', relation: 'is learning', object: 'TypeScript' } - ] - } -}, context); - -console.log(commitResult); -// Output: {"success":true,"data":{"createdEntities":4,"createdRelations":2}} - -// Recall memory example -const recallResult = await tool.handler({ - action: 'recall', - params: { - queryIntent: 'User Programming' - } -}, context); - -console.log(recallResult); -// Output: {"success":true,"data":{"entities":[...],"relations":[...],"message":"Found X entities, Y relations"}} +// Continue assembling WaterFlow... +const toolExecutor = new ToolExecutor(registry); ``` -### Method 2: Use Skill (recommended for AI Agent) +### Manual Registration (More control) + +If you want to control ToolRegistry creation yourself: + +```typescript +import { getPlatform } from 'waterflow/platform'; +import { initializeToolRegistry } from 'waterflow/runtime/core/tools/builtin'; +import { registerGraphMemoryTool } from 'trulymem/tools'; + +const platform = getPlatform(); +const registry = initializeToolRegistry(platform); + +// Register graph memory tool +registerGraphMemoryTool(registry, 'my-session-id'); + +// Continue assembling... +``` + +### Use Skill (AI Agent) #### Step 1: Configure Skill source -In your WaterFlow project, find the Skill configuration file and add bundled source pointing to this project's Skill directory: - ```typescript -// skill_interface.ts or config file -import { DEFAULT_SKILL_LOADER_CONFIG } from './skill_interface'; - const config = { ...DEFAULT_SKILL_LOADER_CONFIG, sources: { ...DEFAULT_SKILL_LOADER_CONFIG.sources, - bundled: './ts/bundled-skills' // Point to this project's Skill directory + bundled: './node_modules/trulymem-waterflow/bundled-skills' }, enabledSources: ['project', 'bundled'] }; ``` -#### Step 2: Call Skill via Agent +#### Step 2: Call via Agent -In your Agent or Workflow, call Skill via Tool: - -``` -Use skill:graph_memory for: - -1. Commit memory: I like programming, learning TypeScript -2. Recall memory: Find memories related to me and programming -``` - -Or call via code: - -```typescript -// Call via SkillTool -const skillResult = await skillTool.handler({ - skill: 'graph_memory', - args: 'recall - queryIntent: "User learning"' -}, context); -``` +AI Agent automatically reads SKILL.md and calls `builtin:graph_memory` tool. #### Available Skills diff --git a/TRACKING.md b/TRACKING.md new file mode 100644 index 0000000..627ec5f --- /dev/null +++ b/TRACKING.md @@ -0,0 +1,102 @@ +# TrulyMEM WaterFlow 重构 - 工程追踪 + +> 每次文件操作前更新此文件,确保中断可恢复。 +> 最后更新: 2026-04-16 + +--- + +## 当前状态 + +**状态**: ✅ 全部完成 +**最后更新**: 2026-04-16 + +--- + +## 任务清单 + +| # | 任务 | 状态 | 备注 | +|---|------|------|------| +| 0 | 创建工程追踪文件 | ✅ 完成 | | +| 1 | 同步 main 分支核心优化 | ✅ 完成 | BFS搜索、depth标注、工具限制器 | +| 2 | Phase 1: 清理重复定义 | ✅ 完成 | 删除 platform/, tool_interface.ts | +| 3 | Phase 2: 适配 WaterFlow 平台层 | ✅ 完成 | graph_database.ts, graph_memory_tool.ts | +| 4 | Phase 3: 完善 SKILL.md | ✅ 完成 | 10 个完整操作 | +| 5 | Phase 4: 调整构建配置 | ✅ 完成 | package.json, tsconfig.json, waterflow.d.ts | +| 6 | Phase 5: 编译验证 | ✅ 完成 | 0 错误,dist/ 输出完整 | + +--- + +## 操作日志 + +### 2026-04-16 开始 +- [x] 创建 TRACKING.md +- [x] 同步 main 分支核心优化 + - [x] BFS 广度优先搜索 → graph_database.ts(含 depth 标注) + - [x] 工具限制器调整 → 在 Tool 层面处理(WaterFlow 治理层已有) + - [x] 提示词优化 → SKILL.md(后续 Phase 3 处理) + - [x] Entity/Relation 类型添加 depth? 字段 + - [x] 改用 WaterFlow platform.fs 替代自定义 storage + - [x] 缓存 platform 实例避免重复 import +- [x] Phase 1: 删除重复文件 + - [x] 删除 ts/src/platform/ 目录(index.ts, node.ts, types.ts) + - [x] 删除 ts/src/runtime/core/tools/tool_interface.ts +- [x] Phase 2: 适配 WaterFlow 接口 + - [x] graph_memory_tool.ts → 使用 waterflow Tool 接口 + - [x] graph_database.ts → 使用 waterflow platform.fs(binary) + - [x] config.ts → 内联类型定义,移除 platform 依赖 + - [x] builtin/index.ts → 添加 registerGraphMemoryTool() +- [x] Phase 3: 完善 SKILL.md + - [x] 主 SKILL.md 补充 10 个完整操作 + - [x] persona/SKILL.md 已验证完整 + - [x] task/SKILL.md 已验证完整 +- [x] Phase 4: 构建配置 + - [x] package.json → 添加 exports, peerDependencies + - [x] tsconfig.json → 添加 typeRoots +- [x] Phase 5: 验证 + - [x] tsc --noEmit 通过(0 错误) + - [x] npm run build 成功 + - [x] dist/ 输出完整(.js, .d.ts, .map) + - [x] 新增 waterflow.d.ts 类型声明 + - [x] 安装 waterflow-ts 作为 devDependency + +--- + +## 变更摘要 + +### 删除(4 文件) +- `ts/src/platform/index.ts` +- `ts/src/platform/node.ts` +- `ts/src/platform/types.ts` +- `ts/src/runtime/core/tools/tool_interface.ts` + +### 修改(8 文件) +- `ts/src/runtime/core/graph_memory/graph_database.ts` — BFS + WaterFlow fs +- `ts/src/runtime/core/graph_memory/types.ts` — 添加 depth? 字段 +- `ts/src/runtime/core/graph_memory/config.ts` — 内联类型,移除 platform 依赖 +- `ts/src/runtime/core/graph_memory/memory_service.ts` — 无变化(已兼容) +- `ts/src/runtime/core/tools/builtin/graph_memory_tool.ts` — WaterFlow Tool 接口 +- `ts/src/runtime/core/tools/builtin/index.ts` — 添加 registerGraphMemoryTool() +- `ts/package.json` — exports, peerDependencies +- `ts/tsconfig.json` — typeRoots +- `ts/bundled-skills/graph_memory/SKILL.md` — 10 个完整操作 + +### 新增(1 文件) +- `ts/src/types/waterflow.d.ts` — WaterFlow 类型声明 + +--- + +## 中断恢复指南 + +如果任务中断,按以下步骤恢复: + +1. 读取此文件,找到最后一个 ✅ 完成的任务 +2. 找到下一个 🔄 或 ⏳ 的任务 +3. 继续执行该任务 +4. 完成后更新此文件的状态 + +**关键文件路径**: +- 重构计划: `重构.md` +- 追踪文件: `TRACKING.md` +- 核心代码: `ts/src/runtime/core/graph_memory/` +- Tool 代码: `ts/src/runtime/core/tools/builtin/` +- Skill 定义: `ts/bundled-skills/graph_memory/` diff --git a/ts/bundled-skills/graph_memory/SKILL.md b/ts/bundled-skills/graph_memory/SKILL.md index a86a862..e9fc45e 100644 --- a/ts/bundled-skills/graph_memory/SKILL.md +++ b/ts/bundled-skills/graph_memory/SKILL.md @@ -1,7 +1,7 @@ --- name: graph_memory description: 图记忆工具 - 让 AI 拥有真正的长期记忆能力 -when_to_use: 需要 AI 记住或回忆信息时 +when_to_use: 需要 AI 记住、回忆、管理信息或任务时 context: inline allowed_tools: - builtin:graph_memory @@ -9,7 +9,7 @@ arguments: - name: action type: string required: true - enum: [recall, commit, purge, introspect] + enum: [recall, commit, purge, introspect, persona_update, persona_clear, task_create, task_set_state, task_delete, task_link_info] description: 记忆操作类型 - name: params type: object @@ -26,12 +26,12 @@ user_invocable: true ### 1. recall - 检索记忆 -从记忆图中检索相关信息。 +从记忆图中检索相关信息。支持广度优先搜索(BFS),自动扩展关联实体。 **参数**: - `queryIntent`: 搜索意图/关键词 - `seedEntities`: 可选的种子实体名 -- `depth`: 检索深度 +- `depth`: 检索深度(默认 2,BFS 层数) - `sessionFilter`: 可选的会话ID过滤 **示例**: @@ -40,14 +40,15 @@ action: recall params: queryIntent: "用户 喜欢 编程" seedEntities: ["用户"] + depth: 2 ``` ### 2. commit - 写入记忆 -将信息写入记忆图。 +将信息写入记忆图。使用三元组(主体-关系-客体)格式。 **参数**: -- `triplets`: 三元组数组,每个包含 subject, relation, object +- `triplets`: 三元组数组,每个包含 subject, relation, object, confidence(可选) - `sessionId`: 会话ID - `turnId`: 轮次ID @@ -70,8 +71,7 @@ params: **参数**: - `criteria`: 删除条件 (subject, target, relation, sessionId) -- `mode`: 删除模式 (soft/hard/supersede) -- `newRelation`: 可选的替代关系 +- `mode`: 删除模式 (soft=标记删除/hard=物理删除/supersede=替代) **示例**: ``` @@ -84,7 +84,7 @@ params: ### 4. introspect - 查看状态 -查看当前记忆状态统计。 +查看当前记忆状态统计(实体数、关系数)。 **参数**: 无 @@ -94,9 +94,114 @@ action: introspect params: {} ``` +## 人设管理 + +### 5. persona_update - 更新人设 + +更新 AI 的人设属性(性格、语气、角色等)。 + +**参数**: +- `attributes`: 属性数组,每个包含 attribute 和 value +- `mode`: merge(合并) 或 replace(替换) + +**示例**: +``` +action: persona_update +params: + attributes: + - attribute: "性格" + value: "活泼可爱" + - attribute: "语气词" + value: "喵" + mode: "replace" +``` + +### 6. persona_clear - 清除人设 + +清除所有人设,恢复默认身份。 + +**参数**: +- `confirm`: 必须为 true 才执行 + +**示例**: +``` +action: persona_clear +params: + confirm: true +``` + +## 任务管理 + +### 7. task_create - 创建任务 + +创建连续性任务节点,维持对话连贯性。 + +**参数**: +- `task_id`: 任务唯一ID +- `description`: 任务描述 +- `info_nodes`: 可选的关联信息节点列表 + +**示例**: +``` +action: task_create +params: + task_id: "Task_成语接龙" + description: "成语接龙游戏,当前成语:为所欲为" + info_nodes: ["成语接龙_当前成语"] +``` + +### 8. task_set_state - 设置任务状态 + +更新任务状态(进行中/已完成/已暂停/已取消)。 + +**参数**: +- `task_id`: 任务ID +- `state`: 新状态 + +**示例**: +``` +action: task_set_state +params: + task_id: "Task_成语接龙" + state: "已暂停" +``` + +### 9. task_delete - 删除任务 + +删除任务节点。 + +**参数**: +- `task_id`: 任务ID + +**示例**: +``` +action: task_delete +params: + task_id: "Task_成语接龙" +``` + +### 10. task_link_info - 关联信息到任务 + +将记忆节点关联到任务节点,实现"由一件事回忆起相关事情"。 + +**参数**: +- `task_id`: 任务ID +- `info_node`: 信息节点名 + +**示例**: +``` +action: task_link_info +params: + task_id: "Task_成语接龙" + info_node: "用户喜欢罗辑" +``` + ## 使用原则 1. **选择性记忆**: 只记住重要和持久的信息 2. **结构化**: 使用三元组 (主体-关系-客体) 格式 3. **关联**: 通过关系连接相关实体 4. **定期清理**: 删除过时或错误的信息 +5. **BFS 搜索**: recall 支持广度优先搜索,depth 参数控制扩展层数 +6. **工作记忆链**: 每轮对话必须查询和更新工作记忆链(TaskNode),这是维持对话连贯性的唯一机制 +7. **人设优先**: 每轮对话必须先查询人设图,确保角色一致性 diff --git a/ts/package-lock.json b/ts/package-lock.json index 696d480..c860c34 100644 --- a/ts/package-lock.json +++ b/ts/package-lock.json @@ -8,12 +8,16 @@ "name": "trulymem-waterflow", "version": "1.0.0", "dependencies": { - "yaml": "^2.8.3" + "sql.js": "^1.11.0" }, "devDependencies": { "@types/node": "^25.5.2", "typescript": "^5.0.0", - "vitest": "^2.0.0" + "vitest": "^2.0.0", + "waterflow-ts": "file:../../WaterFlow/ts" + }, + "peerDependencies": { + "waterflow-ts": ">=0.1.0" } }, "node_modules/@esbuild/aix-ppc64": { @@ -384,12 +388,99 @@ "node": ">=12" } }, + "node_modules/@hono/node-server": { + "version": "1.19.14", + "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.14.tgz", + "integrity": "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw==", + "dev": true, + "engines": { + "node": ">=18.14.1" + }, + "peerDependencies": { + "hono": "^4" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true }, + "node_modules/@modelcontextprotocol/sdk": { + "version": "1.29.0", + "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.29.0.tgz", + "integrity": "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ==", + "dev": true, + "dependencies": { + "@hono/node-server": "^1.19.9", + "ajv": "^8.17.1", + "ajv-formats": "^3.0.1", + "content-type": "^1.0.5", + "cors": "^2.8.5", + "cross-spawn": "^7.0.5", + "eventsource": "^3.0.2", + "eventsource-parser": "^3.0.0", + "express": "^5.2.1", + "express-rate-limit": "^8.2.1", + "hono": "^4.11.4", + "jose": "^6.1.3", + "json-schema-typed": "^8.0.2", + "pkce-challenge": "^5.0.0", + "raw-body": "^3.0.0", + "zod": "^3.25 || ^4.0", + "zod-to-json-schema": "^3.25.1" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@cfworker/json-schema": "^4.1.1", + "zod": "^3.25 || ^4.0" + }, + "peerDependenciesMeta": { + "@cfworker/json-schema": { + "optional": true + }, + "zod": { + "optional": false + } + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", @@ -836,6 +927,73 @@ "url": "https://opencollective.com/vitest" } }, + "node_modules/@vscode/ripgrep": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/@vscode/ripgrep/-/ripgrep-1.17.1.tgz", + "integrity": "sha512-xTs7DGyAO3IsJYOCTBP8LnTvPiYVKEuyv8s0xyJDBXfs8rhBfqnZPvb6xDT+RnwWzcXqW27xLS/aGrkjX7lNWw==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "https-proxy-agent": "^7.0.2", + "proxy-from-env": "^1.1.0", + "yauzl": "^2.9.2" + } + }, + "node_modules/accepts": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", + "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", + "dev": true, + "dependencies": { + "mime-types": "^3.0.0", + "negotiator": "^1.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.18.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", + "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", + "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", + "dev": true, + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", @@ -845,6 +1003,81 @@ "node": ">=12" } }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/body-parser": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", + "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", + "dev": true, + "dependencies": { + "bytes": "^3.1.2", + "content-type": "^1.0.5", + "debug": "^4.4.3", + "http-errors": "^2.0.0", + "iconv-lite": "^0.7.0", + "on-finished": "^2.4.1", + "qs": "^6.14.1", + "raw-body": "^3.0.1", + "type-is": "^2.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/brace-expansion": { + "version": "5.0.5", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.5.tgz", + "integrity": "sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==", + "dev": true, + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/cac": { "version": "6.7.14", "resolved": "https://registry.npmjs.org/cac/-/cac-6.7.14.tgz", @@ -854,6 +1087,35 @@ "node": ">=8" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/chai": { "version": "5.3.3", "resolved": "https://registry.npmjs.org/chai/-/chai-5.3.3.tgz", @@ -879,6 +1141,77 @@ "node": ">= 16" } }, + "node_modules/content-disposition": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.1.0.tgz", + "integrity": "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", + "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", + "dev": true, + "engines": { + "node": ">=6.6.0" + } + }, + "node_modules/cors": { + "version": "2.8.6", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.6.tgz", + "integrity": "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw==", + "dev": true, + "dependencies": { + "object-assign": "^4", + "vary": "^1" + }, + "engines": { + "node": ">= 0.10" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", @@ -905,12 +1238,80 @@ "node": ">=6" } }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "dev": true + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-module-lexer": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", "dev": true }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/esbuild": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", @@ -949,6 +1350,12 @@ "@esbuild/win32-x64": "0.21.5" } }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "dev": true + }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", @@ -958,6 +1365,36 @@ "@types/estree": "^1.0.0" } }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/eventsource": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.7.tgz", + "integrity": "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA==", + "dev": true, + "dependencies": { + "eventsource-parser": "^3.0.1" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/eventsource-parser": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.6.tgz", + "integrity": "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg==", + "dev": true, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", @@ -967,6 +1404,174 @@ "node": ">=12.0.0" } }, + "node_modules/express": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", + "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", + "dev": true, + "dependencies": { + "accepts": "^2.0.0", + "body-parser": "^2.2.1", + "content-disposition": "^1.0.0", + "content-type": "^1.0.5", + "cookie": "^0.7.1", + "cookie-signature": "^1.2.1", + "debug": "^4.4.0", + "depd": "^2.0.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "finalhandler": "^2.1.0", + "fresh": "^2.0.0", + "http-errors": "^2.0.0", + "merge-descriptors": "^2.0.0", + "mime-types": "^3.0.0", + "on-finished": "^2.4.1", + "once": "^1.4.0", + "parseurl": "^1.3.3", + "proxy-addr": "^2.0.7", + "qs": "^6.14.0", + "range-parser": "^1.2.1", + "router": "^2.2.0", + "send": "^1.1.0", + "serve-static": "^2.2.0", + "statuses": "^2.0.1", + "type-is": "^2.0.1", + "vary": "^1.1.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/express-rate-limit": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-8.3.2.tgz", + "integrity": "sha512-77VmFeJkO0/rvimEDuUC5H30oqUC4EyOhyGccfqoLebB0oiEYfM7nwPrsDsBL1gsTpwfzX8SFy2MT3TDyRq+bg==", + "dev": true, + "dependencies": { + "ip-address": "10.1.0" + }, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://github.com/sponsors/express-rate-limit" + }, + "peerDependencies": { + "express": ">= 4.11" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/finalhandler": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", + "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", + "dev": true, + "dependencies": { + "debug": "^4.4.0", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1" + }, + "engines": { + "node": ">= 18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", + "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -981,6 +1586,245 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hono": { + "version": "4.12.14", + "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.14.tgz", + "integrity": "sha512-am5zfg3yu6sqn5yjKBNqhnTX7Cv+m00ox+7jbaKkrLMRJ4rAdldd1xPd/JzbBWspqaQv6RSTrgFN95EsfhC+7w==", + "dev": true, + "engines": { + "node": ">=16.9.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "dev": true, + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", + "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true + }, + "node_modules/ip-address": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", + "dev": true, + "engines": { + "node": ">= 12" + } + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-promise": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", + "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/jose": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/jose/-/jose-6.2.2.tgz", + "integrity": "sha512-d7kPDd34KO/YnzaDOlikGpOurfF0ByC2sEV4cANCtdqLlTfBlw2p14O/5d/zv40gJPbIQxfES3nSx1/oYNyuZQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/panva" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/json-schema-typed": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-8.0.2.tgz", + "integrity": "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA==", + "dev": true + }, "node_modules/loupe": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/loupe/-/loupe-3.2.1.tgz", @@ -996,6 +1840,98 @@ "@jridgewell/sourcemap-codec": "^1.5.5" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/merge-descriptors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", + "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "dev": true, + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", @@ -1020,6 +1956,85 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, + "node_modules/negotiator": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "dev": true, + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/path-to-regexp": { + "version": "8.4.2", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.4.2.tgz", + "integrity": "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA==", + "dev": true, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, "node_modules/pathe": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/pathe/-/pathe-1.1.2.tgz", @@ -1035,12 +2050,39 @@ "node": ">= 14.16" } }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pkce-challenge": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.1.tgz", + "integrity": "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ==", + "dev": true, + "engines": { + "node": ">=16.20.0" + } + }, "node_modules/postcss": { "version": "8.5.9", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.9.tgz", @@ -1069,6 +2111,103 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "dev": true, + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/qs": { + "version": "6.15.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.1.tgz", + "integrity": "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg==", + "dev": true, + "dependencies": { + "side-channel": "^1.1.0" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", + "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", + "dev": true, + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.7.0", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, "node_modules/rollup": { "version": "4.60.1", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.1.tgz", @@ -1113,6 +2252,195 @@ "fsevents": "~2.3.2" } }, + "node_modules/router": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", + "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", + "dev": true, + "dependencies": { + "debug": "^4.4.0", + "depd": "^2.0.0", + "is-promise": "^4.0.0", + "parseurl": "^1.3.3", + "path-to-regexp": "^8.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/send": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", + "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", + "dev": true, + "dependencies": { + "debug": "^4.4.3", + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "etag": "^1.8.1", + "fresh": "^2.0.0", + "http-errors": "^2.0.1", + "mime-types": "^3.0.2", + "ms": "^2.1.3", + "on-finished": "^2.4.1", + "range-parser": "^1.2.1", + "statuses": "^2.0.2" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/serve-static": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", + "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", + "dev": true, + "dependencies": { + "encodeurl": "^2.0.0", + "escape-html": "^1.0.3", + "parseurl": "^1.3.3", + "send": "^1.2.0" + }, + "engines": { + "node": ">= 18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "dev": true + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", @@ -1128,12 +2456,26 @@ "node": ">=0.10.0" } }, + "node_modules/sql.js": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/sql.js/-/sql.js-1.14.1.tgz", + "integrity": "sha512-gcj8zBWU5cFsi9WUP+4bFNXAyF1iRpA3LLyS/DP5xlrNzGmPIizUeBggKa8DbDwdqaKwUcTEnChtd2grWo/x/A==" + }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/std-env": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", @@ -1179,6 +2521,41 @@ "node": ">=14.0.0" } }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "dev": true, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/type-is": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", + "dev": true, + "dependencies": { + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" + }, + "engines": { + "node": ">= 0.6" + } + }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", @@ -1198,6 +2575,24 @@ "integrity": "sha512-qYVnV5OEm2AW8cJMCpdV20CDyaN3g0AjDlOGf1OW4iaDEx8MwdtChUp4zu4H0VP3nDRF/8RKWH+IPp9uW0YGZg==", "dev": true }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "dev": true, + "engines": { + "node": ">= 0.8" + } + }, "node_modules/vite": { "version": "5.4.21", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", @@ -1344,6 +2739,34 @@ } } }, + "node_modules/waterflow-ts": { + "version": "0.1.0", + "resolved": "file:../../WaterFlow/ts", + "dev": true, + "dependencies": { + "@modelcontextprotocol/sdk": "^1.29.0", + "@vscode/ripgrep": "^1.17.1", + "fast-glob": "^3.3.3", + "minimatch": "^10.2.5", + "ws": "^8.18.0", + "yaml": "^2.8.3" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", @@ -1360,10 +2783,38 @@ "node": ">=8" } }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true + }, + "node_modules/ws": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.20.0.tgz", + "integrity": "sha512-sAt8BhgNbzCtgGbt2OxmpuryO63ZoDk/sqaB/znQm94T4fCEsy/yV+7CdC1kJhOU9lboAEU7R3kquuycDoibVA==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/yaml": { "version": "2.8.3", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, "bin": { "yaml": "bin.mjs" }, @@ -1373,6 +2824,34 @@ "funding": { "url": "https://github.com/sponsors/eemeli" } + }, + "node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/zod": { + "version": "4.3.6", + "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", + "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + }, + "node_modules/zod-to-json-schema": { + "version": "3.25.2", + "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.2.tgz", + "integrity": "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==", + "dev": true, + "peerDependencies": { + "zod": "^3.25.28 || ^4" + } } } } diff --git a/ts/package.json b/ts/package.json index 48812a3..eed69cc 100644 --- a/ts/package.json +++ b/ts/package.json @@ -1,17 +1,28 @@ { "name": "trulymem-waterflow", "version": "1.0.0", + "description": "TrulyMEM 图记忆系统 - WaterFlow Skill/Tool 实现", "type": "module", + "main": "./dist/runtime/core/tools/builtin/index.js", + "exports": { + "./tools": "./dist/runtime/core/tools/builtin/index.js", + "./graph_memory": "./dist/runtime/core/graph_memory/index.js", + "./skills": "./bundled-skills" + }, "scripts": { "build": "tsc", "test": "vitest" }, + "peerDependencies": { + "waterflow-ts": ">=0.1.0" + }, "dependencies": { - "yaml": "^2.8.3" + "sql.js": "^1.11.0" }, "devDependencies": { "@types/node": "^25.5.2", "typescript": "^5.0.0", - "vitest": "^2.0.0" + "vitest": "^2.0.0", + "waterflow-ts": "file:../../WaterFlow/ts" } } diff --git a/ts/src/runtime/core/graph_memory/config.ts b/ts/src/runtime/core/graph_memory/config.ts new file mode 100644 index 0000000..0f367f8 --- /dev/null +++ b/ts/src/runtime/core/graph_memory/config.ts @@ -0,0 +1,38 @@ +export interface TrulyMEMConfig { + dbPath: string; + autoSave: boolean; + debug: boolean; +} + +export const DEFAULT_CONFIG: TrulyMEMConfig = { + dbPath: '.trulymem/graph_memory.db', + autoSave: true, + debug: false +}; + +let globalConfig: TrulyMEMConfig = { ...DEFAULT_CONFIG }; + +export function initConfig(config: Partial = {}): TrulyMEMConfig { + globalConfig = { ...DEFAULT_CONFIG, ...config }; + return globalConfig; +} + +export function getConfig(): TrulyMEMConfig { + return globalConfig; +} + +export function setDbPath(dbPath: string): void { + globalConfig.dbPath = dbPath; +} + +export function getDbPath(): string { + return globalConfig.dbPath; +} + +export function setAutoSave(autoSave: boolean): void { + globalConfig.autoSave = autoSave; +} + +export function isAutoSave(): boolean { + return globalConfig.autoSave; +} diff --git a/ts/src/runtime/core/graph_memory/graph_database.ts b/ts/src/runtime/core/graph_memory/graph_database.ts index 7a7cadc..9277d21 100644 --- a/ts/src/runtime/core/graph_memory/graph_database.ts +++ b/ts/src/runtime/core/graph_memory/graph_database.ts @@ -1,53 +1,159 @@ +import initSqlJs, { type Database as SqlJsDatabase } from 'sql.js'; import type { Entity, Relation, RecallParams, CommitParams, PurgeParams, RecallResult, CommitResult, PurgeResult, MemoryStats } from './types'; +import { getConfig } from './config'; export class GraphDatabase { - private entities: Map = new Map(); - private relations: Map = new Map(); + private db: SqlJsDatabase | null = null; private sessionId: string; + private initPromise: Promise | null = null; + private _platform: any = null; constructor(sessionId?: string) { this.sessionId = sessionId || `session-${Date.now()}`; + this.initPromise = this.initDatabase(); + } + + private async initDatabase(): Promise { + const SQL = await initSqlJs(); + const { getPlatform } = await import('waterflow/platform'); + this._platform = getPlatform(); + + try { + const dbPath = this._platform.path.join(this._platform.getCwd(), getConfig().dbPath); + const exists = await this._platform.fs.exists(dbPath); + if (exists) { + const data = await this._platform.fs.readFile(dbPath, { encoding: 'binary' }); + this.db = new SQL.Database(data as Uint8Array); + } else { + this.db = new SQL.Database(); + } + } catch { + this.db = new SQL.Database(); + } + + this.createTables(); + } + + async save(): Promise { + if (!this.db || !this._platform) return; + + try { + const platform = this._platform; + const dbPath = platform.path.join(platform.getCwd(), getConfig().dbPath); + const dir = platform.path.dirname(dbPath); + const dirExists = await platform.fs.exists(dir); + if (!dirExists) { + await platform.fs.mkdir(dir, true); + } + const data = this.db.export(); + await platform.fs.writeFile(dbPath, new Uint8Array(data), { encoding: 'binary' }); + } catch (error) { + this._platform.getLogger().error(`[GraphDatabase] Save failed: ${error}`); + } + } + + private createTables(): void { + if (!this.db) return; + this.db.run(` + CREATE TABLE IF NOT EXISTS entities ( + id TEXT PRIMARY KEY, + name TEXT NOT NULL, + type TEXT DEFAULT 'unknown', + mention_count INTEGER DEFAULT 1, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL + ) + `); + this.db.run(` + CREATE TABLE IF NOT EXISTS relations ( + id TEXT PRIMARY KEY, + source_id TEXT NOT NULL, + target_id TEXT NOT NULL, + relation_type TEXT NOT NULL, + confidence REAL DEFAULT 1.0, + status TEXT DEFAULT 'active', + session_id TEXT NOT NULL, + turn_id INTEGER DEFAULT 0, + created_at TEXT NOT NULL, + updated_at TEXT NOT NULL, + date_bucket TEXT NOT NULL, + FOREIGN KEY (source_id) REFERENCES entities(id), + FOREIGN KEY (target_id) REFERENCES entities(id) + ) + `); + this.db.run(`CREATE INDEX IF NOT EXISTS idx_entity_name ON entities(name)`); + this.db.run(`CREATE INDEX IF NOT EXISTS idx_entity_type ON entities(type)`); + this.db.run(`CREATE INDEX IF NOT EXISTS idx_relation_source ON relations(source_id)`); + this.db.run(`CREATE INDEX IF NOT EXISTS idx_relation_target ON relations(target_id)`); + this.db.run(`CREATE INDEX IF NOT EXISTS idx_relation_type ON relations(relation_type)`); + this.db.run(`CREATE INDEX IF NOT EXISTS idx_relation_status ON relations(status)`); + } + + private ensureInit(): void { + if (!this.db) { + throw new Error('Database not initialized'); + } } async recall(params: RecallParams): Promise { - const { queryIntent, seedEntities, sessionFilter } = params; + await this.initPromise; + this.ensureInit(); + const { queryIntent, seedEntities, depth = 2, sessionFilter } = params; const keywords = queryIntent.split(/[,\s]+/).filter(k => k.length > 0); const entities: Entity[] = []; const relations: Relation[] = []; const entityIds = new Set(); - for (const keyword of keywords) { - const lowerKeyword = keyword.toLowerCase(); - for (const [_, entity] of this.entities) { - if (entity.name.toLowerCase().includes(lowerKeyword)) { - if (!entityIds.has(entity.id)) { - entityIds.add(entity.id); - entities.push(entity); + if (!this.db) return { entities, relations, message: 'Database not ready' }; + + if (!keywords.length && !seedEntities?.length) { + const rows = this.db.exec('SELECT * FROM entities ORDER BY mention_count DESC LIMIT 50'); + if (rows.length > 0) { + const columns = rows[0].columns; + for (const row of rows[0].values) { + const obj = this.rowToObject(columns, row); + const id = obj.id as string; + entityIds.add(id); + entities.push(this.rowToEntity(obj, 0)); + } + } + } else { + for (const keyword of keywords) { + const stmt = this.db.prepare('SELECT * FROM entities WHERE LOWER(name) LIKE ? LIMIT 100'); + stmt.bind([`%${keyword.toLowerCase()}%`]); + while (stmt.step()) { + const row = stmt.getAsObject(); + const id = row.id as string; + if (!entityIds.has(id)) { + entityIds.add(id); + entities.push(this.rowToEntity(row, 0)); } } + stmt.free(); } } - if (seedEntities && seedEntities.length > 0) { + if (seedEntities?.length) { for (const seedName of seedEntities) { - for (const [_, entity] of this.entities) { - if (entity.name.toLowerCase() === seedName.toLowerCase()) { - if (!entityIds.has(entity.id)) { - entityIds.add(entity.id); - entities.push(entity); - } + const stmt = this.db.prepare('SELECT * FROM entities WHERE LOWER(name) = ? LIMIT 1'); + stmt.bind([seedName.toLowerCase()]); + if (stmt.step()) { + const row = stmt.getAsObject(); + const id = row.id as string; + if (!entityIds.has(id)) { + entityIds.add(id); + entities.push(this.rowToEntity(row, 0)); } } + stmt.free(); } } - for (const [_, relation] of this.relations) { - if (entityIds.has(relation.sourceId) || entityIds.has(relation.targetId)) { - if (relation.status === 'active') { - if (!sessionFilter || relation.sessionId === sessionFilter) { - relations.push(relation); - } - } + this.bfsExpand(entityIds, entities, relations, depth, sessionFilter); + + for (const entity of entities) { + if (entity.depth === undefined) { + entity.depth = 0; } } @@ -58,132 +164,268 @@ export class GraphDatabase { }; } + private bfsExpand( + seedIds: Set, + entities: Entity[], + relations: Relation[], + maxDepth: number, + sessionFilter?: string + ): void { + if (!this.db) return; + + const visited = new Set(seedIds); + let currentLayer = new Set(seedIds); + const entityDepths: Record = {}; + for (const id of seedIds) { + entityDepths[id] = 0; + } + + for (let layer = 0; layer < maxDepth; layer++) { + if (!currentLayer.size) break; + + const placeholders = Array(currentLayer.size).fill('?').join(','); + let sql = ` + SELECT r.id, r.source_id, r.target_id, + e1.name as source_name, e2.name as target_name, + r.relation_type, r.confidence, r.session_id, + r.turn_id, r.created_at, r.updated_at, r.status, r.date_bucket + FROM relations r + JOIN entities e1 ON r.source_id = e1.id + JOIN entities e2 ON r.target_id = e2.id + WHERE (r.source_id IN (${placeholders}) OR r.target_id IN (${placeholders})) + AND r.status = 'active' + `; + const params: (string | number)[] = []; + for (const id of currentLayer) { params.push(id); } + for (const id of currentLayer) { params.push(id); } + if (sessionFilter) { + sql += ` AND r.session_id = ?`; + params.push(sessionFilter); + } + + const stmt = this.db.prepare(sql); + stmt.bind(params); + + const nextLayer = new Set(); + const layerRelations: Relation[] = []; + + while (stmt.step()) { + const row = stmt.getAsObject(); + const sourceId = row.source_id as string; + const targetId = row.target_id as string; + const sourceDepth = entityDepths[sourceId] ?? layer; + const targetDepth = entityDepths[targetId] ?? layer; + const relationDepth = Math.max(sourceDepth, targetDepth) + 1; + + layerRelations.push({ + id: row.id as string, + sourceId, + targetId, + relationType: row.relation_type as string, + confidence: row.confidence as number, + status: row.status as Relation['status'], + sessionId: row.session_id as string, + turnId: row.turn_id as number, + createdAt: new Date(row.created_at as string), + updatedAt: new Date(row.updated_at as string), + dateBucket: row.date_bucket as string, + depth: relationDepth + }); + + if (!visited.has(sourceId)) { + visited.add(sourceId); + nextLayer.add(sourceId); + entityDepths[sourceId] = layer + 1; + } + if (!visited.has(targetId)) { + visited.add(targetId); + nextLayer.add(targetId); + entityDepths[targetId] = layer + 1; + } + } + stmt.free(); + + relations.push(...layerRelations); + + if (nextLayer.size) { + const placeholders = Array(nextLayer.size).fill('?').join(','); + const entityStmt = this.db.prepare( + `SELECT * FROM entities WHERE id IN (${placeholders})` + ); + entityStmt.bind(Array.from(nextLayer)); + while (entityStmt.step()) { + const row = entityStmt.getAsObject(); + const id = row.id as string; + entities.push(this.rowToEntity(row, entityDepths[id] ?? layer + 1)); + } + entityStmt.free(); + } + + currentLayer = nextLayer; + } + } + async commit(params: CommitParams): Promise { + await this.initPromise; + this.ensureInit(); const { triplets, sessionId, turnId } = params; let createdEntities = 0; let createdRelations = 0; + if (!this.db) return { createdEntities: 0, createdRelations: 0 }; + for (const triplet of triplets) { const sourceId = this.upsertEntity(triplet.subject); const targetId = this.upsertEntity(triplet.object); const relationId = this.generateId(); - const now = new Date(); - const relation: Relation = { - id: relationId, - sourceId, - targetId, - relationType: triplet.relation, - confidence: triplet.confidence || 1.0, - status: 'active', - sessionId: sessionId || this.sessionId, - turnId: turnId || 0, - createdAt: now, - updatedAt: now, - dateBucket: this.getDateBucket(now) - }; + const now = new Date().toISOString(); + this.db.run( + `INSERT INTO relations (id, source_id, target_id, relation_type, confidence, status, session_id, turn_id, created_at, updated_at, date_bucket) + VALUES (?, ?, ?, ?, ?, 'active', ?, ?, ?, ?, ?)`, + [relationId, sourceId, targetId, triplet.relation, triplet.confidence ?? 1.0, sessionId ?? this.sessionId, turnId ?? 0, now, now, new Date().toISOString().split('T')[0]] + ); - this.relations.set(relationId, relation); createdEntities += 2; createdRelations++; } + if (getConfig().autoSave) { + await this.save(); + } + return { createdEntities, createdRelations }; } async purge(params: PurgeParams): Promise { + await this.initPromise; + this.ensureInit(); const { criteria, mode = 'soft' } = params; let deleted = 0; - for (const [id, relation] of this.relations) { - if (relation.status !== 'active') continue; + if (!this.db) return { deleted: 0, mode }; - if (!criteria) { - continue; - } + const conditions: string[] = []; + const queryParams: (string | number)[] = []; - let matches = true; - if (criteria.subject) { - const sourceEntity = this.entities.get(relation.sourceId); - matches = sourceEntity?.name.toLowerCase() === criteria.subject.toLowerCase(); - } - if (matches && criteria.target) { - const targetEntity = this.entities.get(relation.targetId); - matches = targetEntity?.name.toLowerCase() === criteria.target.toLowerCase(); - } - if (matches && criteria.relation) { - matches = relation.relationType.toLowerCase() === criteria.relation.toLowerCase(); - } - if (matches && criteria.sessionId) { - matches = relation.sessionId === criteria.sessionId; + if (criteria?.subject) { + const stmt = this.db.prepare('SELECT id FROM entities WHERE LOWER(name) = ?'); + stmt.bind([criteria.subject.toLowerCase()]); + if (stmt.step()) { + const row = stmt.getAsObject(); + conditions.push(`source_id = ?`); + queryParams.push(row.id as string); } + stmt.free(); + } - if (matches) { - if (mode === 'hard') { - this.relations.delete(id); - } else { - relation.status = 'deleted'; - relation.updatedAt = new Date(); - } - deleted++; + if (criteria?.target) { + const stmt = this.db.prepare('SELECT id FROM entities WHERE LOWER(name) = ?'); + stmt.bind([criteria.target.toLowerCase()]); + if (stmt.step()) { + const row = stmt.getAsObject(); + conditions.push(`target_id = ?`); + queryParams.push(row.id as string); } + stmt.free(); + } + + if (criteria?.relation) { + conditions.push(`relation_type = ?`); + queryParams.push(criteria.relation); + } + + if (!conditions.length) { + return { deleted: 0, mode, message: '无删除条件' }; + } + + const whereClause = conditions.join(' AND '); + + const countStmt = this.db.prepare(`SELECT COUNT(*) as cnt FROM relations WHERE ${whereClause} AND status = 'active'`); + countStmt.bind(queryParams); + if (countStmt.step()) { + const row = countStmt.getAsObject(); + deleted = row.cnt as number; + } + countStmt.free(); + + if (mode === 'hard') { + this.db.run(`DELETE FROM relations WHERE ${whereClause} AND status = 'active'`, queryParams); + } else { + this.db.run( + `UPDATE relations SET status = 'deleted', updated_at = ? WHERE ${whereClause} AND status = 'active'`, + [new Date().toISOString(), ...queryParams] + ); + } + + if (getConfig().autoSave && deleted > 0) { + await this.save(); } return { deleted, mode }; } async introspect(): Promise { - let entityCount = 0; - for (const [_, entity] of this.entities) { - if (!this.isEntityDeleted(entity.id)) entityCount++; - } + await this.initPromise; + this.ensureInit(); - let relationCount = 0; - for (const [_, relation] of this.relations) { - if (relation.status === 'active') relationCount++; - } + if (!this.db) return { entityCount: 0, relationCount: 0, sessionId: this.sessionId }; + + const entityCount = (this.db.exec('SELECT COUNT(*) FROM entities')[0]?.values[0]?.[0] as number) ?? 0; + const relationCount = (this.db.exec("SELECT COUNT(*) FROM relations WHERE status = 'active'")[0]?.values[0]?.[0] as number) ?? 0; return { entityCount, relationCount, sessionId: this.sessionId }; } private upsertEntity(name: string): string { - for (const [id, entity] of this.entities) { - if (entity.name === name && !this.isEntityDeleted(id)) { - entity.mentionCount++; - entity.updatedAt = new Date(); - return id; - } + if (!this.db) return this.generateId(); + + const existingStmt = this.db.prepare('SELECT id, mention_count FROM entities WHERE LOWER(name) = ?'); + existingStmt.bind([name.toLowerCase()]); + if (existingStmt.step()) { + const row = existingStmt.getAsObject(); + const id = row.id as string; + this.db.run('UPDATE entities SET mention_count = ?, updated_at = ? WHERE id = ?', [(row.mention_count as number) + 1, new Date().toISOString(), id]); + existingStmt.free(); + return id; } + existingStmt.free(); const id = this.generateId(); - const now = new Date(); - const entity: Entity = { - id, - name, - type: 'unknown', - mentionCount: 1, - createdAt: now, - updatedAt: now - }; - this.entities.set(id, entity); + const now = new Date().toISOString(); + this.db.run( + 'INSERT INTO entities (id, name, type, mention_count, created_at, updated_at) VALUES (?, ?, ?, 1, ?, ?)', + [id, name, 'unknown', now, now] + ); return id; } - private isEntityDeleted(entityId: string): boolean { - for (const [_, relation] of this.relations) { - if ((relation.sourceId === entityId || relation.targetId === entityId) && relation.status === 'deleted') { - return true; - } + private rowToEntity(row: Record, depth?: number): Entity { + const entity: Entity = { + id: row.id as string, + name: row.name as string, + type: (row.type as string) ?? 'unknown', + mentionCount: row.mention_count as number, + createdAt: new Date(row.created_at as string), + updatedAt: new Date(row.updated_at as string) + }; + if (depth !== undefined) { + entity.depth = depth; } - return false; + return entity; + } + + private rowToObject(columns: string[], values: unknown[]): Record { + const obj: Record = {}; + columns.forEach((col, i) => { obj[col] = values[i]; }); + return obj; } private generateId(): string { - return `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; - } - - private getDateBucket(date: Date): string { - return date.toISOString().split('T')[0] ?? ''; + if (this._platform?.globals?.randomUUID) { + return this._platform.globals.randomUUID(); + } + return `ent-${Date.now()}-${Math.random().toString(36).slice(2, 9)}`; } setSessionId(sessionId: string): void { @@ -193,4 +435,11 @@ export class GraphDatabase { getSessionId(): string { return this.sessionId; } + + close(): void { + if (this.db) { + this.db.close(); + this.db = null; + } + } } diff --git a/ts/src/runtime/core/graph_memory/index.ts b/ts/src/runtime/core/graph_memory/index.ts index 68b0365..4b0903e 100644 --- a/ts/src/runtime/core/graph_memory/index.ts +++ b/ts/src/runtime/core/graph_memory/index.ts @@ -1,3 +1,4 @@ export * from './types'; +export * from './config'; export * from './graph_database'; -export * from './memory_service'; +export * from './memory_service'; \ No newline at end of file diff --git a/ts/src/runtime/core/graph_memory/types.ts b/ts/src/runtime/core/graph_memory/types.ts index 6157857..7496953 100644 --- a/ts/src/runtime/core/graph_memory/types.ts +++ b/ts/src/runtime/core/graph_memory/types.ts @@ -5,6 +5,7 @@ export interface Entity { mentionCount: number; createdAt: Date; updatedAt: Date; + depth?: number; // BFS 搜索深度标注 } export type RelationStatus = 'active' | 'deleted' | 'archived' | 'superseded'; @@ -21,6 +22,7 @@ export interface Relation { createdAt: Date; updatedAt: Date; dateBucket: string; + depth?: number; // BFS 搜索深度标注 } export interface Triplet { @@ -71,6 +73,7 @@ export interface CommitResult { export interface PurgeResult { deleted: number; mode: string; + message?: string; } export type TaskState = '进行中' | '已完成' | '已暂停' | '已取消'; diff --git a/ts/src/runtime/core/tools/builtin/graph_memory_tool.ts b/ts/src/runtime/core/tools/builtin/graph_memory_tool.ts index da30525..25cf81e 100644 --- a/ts/src/runtime/core/tools/builtin/graph_memory_tool.ts +++ b/ts/src/runtime/core/tools/builtin/graph_memory_tool.ts @@ -1,4 +1,4 @@ -import type { Tool, ToolCategory, PermissionLevel, ToolInputSchema, ToolExecutionContext, ToolOutput } from '../tool_interface'; +import type { Tool, ToolCategory, PermissionLevel, ToolInputSchema, ToolOutput, ToolInput, ToolExecutionContext } from 'waterflow/runtime/core/tools/tool_interface'; import { GraphDatabase } from '../../graph_memory/graph_database'; import { MemoryService } from '../../graph_memory/memory_service'; @@ -8,7 +8,7 @@ const GRAPH_MEMORY_TOOL_DESCRIPTION = `图记忆工具 - 让 AI 拥有真正的 操作: - recall: 检索记忆 -- commit: 写入记忆 +- commit: 写入记忆 - purge: 删除记忆 - introspect: 查看状态 - persona_update/clear: 人设管理 @@ -20,6 +20,7 @@ export class GraphMemoryTool implements Tool { readonly description = GRAPH_MEMORY_TOOL_DESCRIPTION; readonly category: ToolCategory = 'analysis'; readonly permissionLevel: PermissionLevel = 'safe'; + readonly alwaysLoad = true; readonly inputSchema: ToolInputSchema = { type: 'object', @@ -100,14 +101,18 @@ export class GraphMemoryTool implements Tool { this.service = new MemoryService(this.db); } - async handler(params: Record, _context: ToolExecutionContext): Promise { + async handler(params: ToolInput, context: ToolExecutionContext): Promise { const action = params.action as string; const actionParams = params.params as Record; + const logger = context?.logger; try { + logger?.info(`[GraphMemoryTool] Executing action: ${action}`); const result = await this.executeAction(action, actionParams); + logger?.info(`[GraphMemoryTool] Action ${action} completed successfully`); return JSON.stringify({ success: true, data: result }, null, 2); } catch (error) { + logger?.error(`[GraphMemoryTool] Action ${action} failed:`, error); return JSON.stringify({ success: false, error: { diff --git a/ts/src/runtime/core/tools/builtin/index.ts b/ts/src/runtime/core/tools/builtin/index.ts new file mode 100644 index 0000000..3befb9c --- /dev/null +++ b/ts/src/runtime/core/tools/builtin/index.ts @@ -0,0 +1,19 @@ +import type { Tool } from 'waterflow/runtime/core/tools/tool_interface'; +import type { Platform } from 'waterflow/platform/types'; +import { GraphMemoryTool, createGraphMemoryTool } from './graph_memory_tool'; + +export { GraphMemoryTool, createGraphMemoryTool }; + +export function registerGraphMemoryTool( + registry: { register: (tool: Tool) => void }, + sessionId?: string +): void { + registry.register(createGraphMemoryTool(sessionId)); +} + +export function installTrulyMEM(platform: Platform, sessionId?: string) { + const { initializeToolRegistry } = require('waterflow/runtime/core/tools/builtin'); + const registry = initializeToolRegistry(platform); + registerGraphMemoryTool(registry, sessionId); + return registry; +} diff --git a/ts/src/runtime/core/tools/tool_interface.ts b/ts/src/runtime/core/tools/tool_interface.ts deleted file mode 100644 index 3084952..0000000 --- a/ts/src/runtime/core/tools/tool_interface.ts +++ /dev/null @@ -1,59 +0,0 @@ -export type ToolCategory = 'file' | 'code' | 'search' | 'execute' | 'network' | 'analysis' | 'generation' | 'communication' | 'mcp' | 'custom'; - -export type PermissionLevel = 'safe' | 'moderate' | 'dangerous' | 'restricted'; - -export type SchemaType = 'string' | 'number' | 'integer' | 'boolean' | 'array' | 'object'; - -export interface SchemaProperty { - type: SchemaType; - description: string; - enum?: string[]; - minimum?: number; - maximum?: number; - minLength?: number; - maxLength?: number; - pattern?: string; - default?: unknown; - examples?: unknown[]; - items?: SchemaProperty; - properties?: Record; -} - -export interface ToolInputSchema { - type: 'object'; - properties: Record; - required?: string[]; - additionalProperties?: boolean; -} - -export interface ToolOutputSchema { - type: 'object'; - properties: Record; - format?: 'json' | 'text' | 'markdown' | 'binary'; - maxSize?: number; - maxLines?: number; -} - -export type ToolInput = Record; -export type ToolOutput = string | Record | void; - -export interface ToolExecutionContext { - toolCallId: string; - workingDirectory: string; - abortController: { signal: AbortSignal }; - config: { timeout?: number }; - logger: { info: (...args: unknown[]) => void; warn: (...args: unknown[]) => void; error: (...args: unknown[]) => void; debug: (...args: unknown[]) => void }; -} - -export type ToolHandler = (params: ToolInput, context: ToolExecutionContext) => Promise; - -export interface Tool { - readonly id: string; - readonly name: string; - readonly description: string; - readonly category: ToolCategory; - readonly inputSchema: ToolInputSchema; - readonly outputSchema?: ToolOutputSchema; - readonly handler: ToolHandler; - readonly permissionLevel: PermissionLevel; -} diff --git a/ts/src/types/sql.js.d.ts b/ts/src/types/sql.js.d.ts new file mode 100644 index 0000000..f4b6255 --- /dev/null +++ b/ts/src/types/sql.js.d.ts @@ -0,0 +1,29 @@ +declare module 'sql.js' { + export interface Database { + run(sql: string, params?: (string | number | null | Uint8Array)[]): void; + exec(sql: string): QueryExecResult[]; + prepare(sql: string): Statement; + export(): Uint8Array; + close(): void; + } + + export interface Statement { + bind(params?: (string | number | null | Uint8Array)[]): boolean; + step(): boolean; + getAsObject(): Record; + free(): boolean; + } + + export interface QueryExecResult { + columns: string[]; + values: (string | number | null | Uint8Array)[][]; + } + + export interface SqlJsStatic { + Database: new (data?: ArrayLike) => Database; + } + + export default function initSqlJs(config?: { + locateFile?: (file: string) => string; + }): Promise; +} \ No newline at end of file diff --git a/ts/src/types/waterflow.d.ts b/ts/src/types/waterflow.d.ts new file mode 100644 index 0000000..02af797 --- /dev/null +++ b/ts/src/types/waterflow.d.ts @@ -0,0 +1,193 @@ +declare module 'waterflow/platform' { + import type { Platform } from 'waterflow/platform/types'; + export function getPlatform(): Platform; + export function hasCapability(capability: string): boolean; + export function initPlatform(options?: any): Platform; + export function resetPlatform(): void; +} + +declare module 'waterflow/platform/types' { + export interface PlatformAbortSignal { + readonly aborted: boolean; + readonly reason?: unknown; + addEventListener(type: 'abort', listener: () => void): void; + removeEventListener(type: 'abort', listener: () => void): void; + } + + export interface PlatformAbortController { + readonly signal: PlatformAbortSignal; + abort(reason?: unknown): void; + } + + export interface PathOperations { + join(...paths: string[]): string; + dirname(p: string): string; + basename(p: string, ext?: string): string; + extname(p: string): string; + normalize(p: string): string; + isAbsolute(p: string): boolean; + resolve(...paths: string[]): string; + relative(from: string, to: string): string; + } + + export interface FileReadOptions { + encoding?: 'utf-8' | 'binary'; + start?: number; + end?: number; + } + + export interface FileWriteOptions { + encoding?: 'utf-8' | 'binary'; + append?: boolean; + } + + export interface FileInfo { + path: string; + name: string; + isFile: boolean; + isDirectory: boolean; + size: number; + modifiedTime: number; + createdTime: number; + } + + export interface FileSystemOperations { + readFile(filePath: string, options?: FileReadOptions): Promise; + writeFile(filePath: string, data: string | ArrayBuffer, options?: FileWriteOptions): Promise; + appendFile(filePath: string, data: string, options?: FileWriteOptions): Promise; + deleteFile(filePath: string): Promise; + exists(filePath: string): Promise; + stat(filePath: string): Promise; + readdir(dirPath: string): Promise; + mkdir(dirPath: string, recursive?: boolean): Promise; + rmdir(dirPath: string, recursive?: boolean): Promise; + copy(src: string, dest: string): Promise; + move(src: string, dest: string): Promise; + } + + export interface ProcessResult { + exitCode: number; + stdout: string; + stderr: string; + signal?: string; + } + + export interface ProcessOptions { + cwd?: string; + env?: Record; + timeout?: number; + maxBuffer?: number; + } + + export interface ProcessOperations { + exec(command: string, options?: ProcessOptions): Promise; + execFile(file: string, args: string[], options?: ProcessOptions): Promise; + } + + export interface StorageOperations { + get(key: string): Promise; + set(key: string, value: string, ttl?: number): Promise; + delete(key: string): Promise; + clear(): Promise; + keys(): Promise; + } + + export interface PlatformGlobals { + TextEncoder: typeof TextEncoder; + TextDecoder: typeof TextDecoder; + URL: typeof URL; + randomUUID(): string; + now(): number; + btoa(data: string): string; + atob(data: string): string; + } + + export interface Logger { + info(msg: string, ...args: unknown[]): void; + warn(msg: string, ...args: unknown[]): void; + error(msg: string, ...args: unknown[]): void; + debug(msg: string, ...args: unknown[]): void; + } + + export interface Platform { + readonly path: PathOperations; + readonly fs: FileSystemOperations; + readonly process: ProcessOperations; + readonly storage: StorageOperations; + readonly globals: PlatformGlobals; + getInfo(): { runtime: string; os: string; version: string; arch: string; hostname: string }; + createAbortController(): PlatformAbortController; + getEnv(key: string): string | undefined; + getAllEnv(): Record; + setEnv(key: string, value: string): void; + getCwd(): string; + setCwd(p: string): void; + exit(code: number): void; + getLogger(): Logger; + } +} + +declare module 'waterflow/runtime/core/tools/tool_interface' { + import type { PlatformAbortController, Logger } from 'waterflow/platform/types'; + + export type ToolCategory = 'file' | 'code' | 'search' | 'execute' | 'network' | 'analysis' | 'generation' | 'communication' | 'mcp' | 'custom'; + export type PermissionLevel = 'safe' | 'moderate' | 'dangerous' | 'restricted'; + export type ToolInput = Record; + export type ToolOutput = string | Record | void; + + export interface SchemaProperty { + type: string; + description: string; + enum?: string[]; + minimum?: number; + maximum?: number; + minLength?: number; + maxLength?: number; + pattern?: string; + default?: unknown; + examples?: unknown[]; + items?: SchemaProperty; + properties?: Record; + } + + export interface ToolInputSchema { + type: 'object'; + properties: Record; + required?: string[]; + additionalProperties?: boolean; + } + + export interface ToolOutputSchema { + type: 'object'; + properties: Record; + format?: 'json' | 'text' | 'markdown' | 'binary'; + maxSize?: number; + maxLines?: number; + } + + export interface ToolExecutionContext { + toolCallId: string; + workingDirectory: string; + abortController: PlatformAbortController; + config: { timeout?: number }; + logger: Logger; + } + + export interface Tool { + readonly id: string; + readonly name: string; + readonly description: string; + readonly category: ToolCategory; + readonly inputSchema: ToolInputSchema; + readonly outputSchema?: ToolOutputSchema; + readonly handler: (params: ToolInput, context: ToolExecutionContext) => Promise; + readonly permissionLevel: PermissionLevel; + readonly alwaysLoad?: boolean; + readonly shouldDefer?: boolean; + readonly isMcp?: boolean; + readonly prompt?: (options: any) => Promise; + readonly features?: string[]; + readonly metadata?: Record; + readonly searchHint?: string; + } +} diff --git a/ts/tsconfig.json b/ts/tsconfig.json index b5689e7..0e14796 100644 --- a/ts/tsconfig.json +++ b/ts/tsconfig.json @@ -16,7 +16,8 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitReturns": true, - "noFallthroughCasesInSwitch": true + "noFallthroughCasesInSwitch": true, + "typeRoots": ["./src/types", "./node_modules/@types"] }, "include": ["src/**/*"], "exclude": ["node_modules", "dist"] diff --git a/重构.md b/重构.md new file mode 100644 index 0000000..c15aa89 --- /dev/null +++ b/重构.md @@ -0,0 +1,368 @@ +# TrulyMEM WaterFlow 重构计划 + +> **目标**: 将 waterflow 分支重构为标准的 WaterFlow 框架 Tool + Skill,保留原始记忆能力不变。 +> **约束**: 仅修改 waterflow 分支,不影响 main 分支。 + +--- + +## 现状分析 + +### waterflow 分支当前结构 + +``` +ts/ +├── bundled-skills/graph_memory/ ← SKILL.md 已存在 ✅ +│ ├── SKILL.md +│ ├── persona/SKILL.md +│ └── task/SKILL.md +├── src/ +│ ├── platform/ ← 重复定义,需删除 ❌ +│ │ ├── index.ts +│ │ ├── node.ts +│ │ └── types.ts +│ ├── runtime/core/ +│ │ ├── graph_memory/ ← 核心逻辑,保留 ✅ +│ │ │ ├── types.ts +│ │ │ ├── graph_database.ts +│ │ │ ├── memory_service.ts +│ │ │ └── index.ts +│ │ └── tools/ +│ │ ├── tool_interface.ts ← 重复定义,需删除 ❌ +│ │ └── builtin/ +│ │ └── graph_memory_tool.ts ← 需适配 WaterFlow 接口 ✅ +│ └── types/ +│ └── sql.js.d.ts ← 保留 ✅ +└── package.json ← 需调整依赖 ✅ +``` + +### 核心问题 + +| 问题 | 说明 | 解决方案 | +|------|------|----------| +| 重复平台层 | `ts/src/platform/` 与 WaterFlow 重复 | 删除,改用 WaterFlow 的 `getPlatform()` | +| 重复 Tool 接口 | `tool_interface.ts` 与 WaterFlow 重复 | 删除,改用 WaterFlow 的类型 | +| 存储接口不兼容 | 使用自定义 `StorageOperations` | 改用 WaterFlow 的 `platform.fs`(支持 binary) | +| 无注册入口 | 没有将 Tool 注册到 WaterFlow 的方式 | 创建 `registerGraphMemoryTool()` 导出函数 | +| SKILL.md 不完整 | 缺少 persona/task 操作的完整描述 | 补充完善 | + +--- + +## 重构步骤 + +### Phase 1: 清理重复定义 + +#### 1.1 删除 `ts/src/platform/` 目录 + +``` +删除: ts/src/platform/index.ts +删除: ts/src/platform/node.ts +删除: ts/src/platform/types.ts +``` + +**替换方案**: 所有 `import { getPlatform } from '../../../platform'` 改为从 WaterFlow 导入: +```typescript +import { getPlatform } from 'waterflow/platform'; +``` + +#### 1.2 删除 `ts/src/runtime/core/tools/tool_interface.ts` + +**替换方案**: 使用 WaterFlow 的 Tool 接口: +```typescript +import type { Tool, ToolInputSchema, ToolOutput, ToolExecutionContext } from 'waterflow/runtime/core/tools/tool_interface'; +``` + +#### 1.3 更新 `tsconfig.json` + +```json +{ + "compilerOptions": { + "rootDir": "./src", + "outDir": "./dist", + "module": "esnext", + "moduleResolution": "bundler", + "target": "esnext", + "types": ["node"], + "lib": ["esnext"], + "sourceMap": true, + "declaration": true, + "strict": true, + "verbatimModuleSyntax": true, + "isolatedModules": true, + "skipLibCheck": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist"] +} +``` + +--- + +### Phase 2: 适配 WaterFlow 平台层 + +#### 2.1 修改 `graph_database.ts` — 存储适配 + +**当前**(使用自定义 storage): +```typescript +import { getPlatform } from '../../../platform'; +const platform = getPlatform(); +await platform.storage.save(key, data); // 自定义接口 +``` + +**改为**(使用 WaterFlow fs): +```typescript +import { getPlatform } from 'waterflow/platform'; +const platform = getPlatform(); +await platform.fs.writeFile(dbPath, data, { encoding: 'binary' }); +const loaded = await platform.fs.readFile(dbPath, { encoding: 'binary' }); +``` + +**具体改动**: +- `GraphDatabase.save()` 方法: `storage.save()` → `fs.writeFile(..., { encoding: 'binary' })` +- `GraphDatabase.load()` 方法: `storage.load()` → `fs.readFile(..., { encoding: 'binary' })` +- `dbPath` 使用 `platform.path.join()` 构建 + +#### 2.2 修改 `graph_memory_tool.ts` — Tool 接口适配 + +**当前**: +```typescript +import type { Tool, ... } from '../tool_interface'; // 自定义接口 +import { getPlatform } from '../../../../platform'; // 自定义平台 +``` + +**改为**: +```typescript +import type { Tool, ToolInputSchema, ToolOutput, ToolExecutionContext, ToolInput } from 'waterflow/runtime/core/tools/tool_interface'; +import { getPlatform } from 'waterflow/platform'; +``` + +**新增 WaterFlow 兼容字段**: +```typescript +export class GraphMemoryTool implements Tool { + readonly id = 'builtin:graph_memory'; + readonly name = 'GraphMemory'; + readonly description = '...'; + readonly category: ToolCategory = 'analysis'; + readonly permissionLevel: PermissionLevel = 'safe'; + readonly inputSchema: ToolInputSchema = { ... }; + + // WaterFlow 新增字段(可选) + readonly alwaysLoad = true; // 始终加载完整 schema + readonly shouldDefer = false; // 不延迟加载 + + async handler(params: ToolInput, context: ToolExecutionContext): Promise { + // 使用 context.logger 替代 getPlatform().getLogger() + const logger = context.logger; + // ... 原有逻辑不变 + } +} +``` + +#### 2.3 创建注册入口 + +**新建**: `ts/src/runtime/core/tools/builtin/index.ts` + +```typescript +import type { Tool } from 'waterflow/runtime/core/tools/tool_interface'; +import { GraphMemoryTool, createGraphMemoryTool } from './graph_memory_tool'; + +export { GraphMemoryTool, createGraphMemoryTool }; + +/** + * 注册图记忆工具到 WaterFlow ToolRegistry + * + * 使用方式: + * import { registerGraphMemoryTool } from 'trulymem/tools'; + * registerGraphMemoryTool(toolRegistry, sessionId); + */ +export function registerGraphMemoryTool( + registry: { register: (tool: Tool) => void }, + sessionId?: string +): void { + registry.register(createGraphMemoryTool(sessionId)); +} +``` + +--- + +### Phase 3: 完善 SKILL.md + +#### 3.1 更新主 SKILL.md + +补充 persona 和 task 操作说明,与 `memory_service.ts` 的实际方法对齐: + +```yaml +--- +name: graph_memory +description: 图记忆工具 - 让 AI 拥有真正的长期记忆能力 +when_to_use: 需要 AI 记住、回忆、管理信息或任务时 +context: inline +allowed_tools: + - builtin:graph_memory +arguments: + - name: action + type: string + required: true + enum: [recall, commit, purge, introspect, persona_update, persona_clear, task_create, task_set_state, task_delete, task_link_info] + description: 记忆操作类型 + - name: params + type: object + required: true + description: 操作参数 +user_invocable: true +--- +``` + +#### 3.2 更新 persona/SKILL.md + +```yaml +--- +name: persona_management +description: 人设管理 - 管理 AI 的用户画像和偏好 +context: inline +allowed_tools: + - builtin:graph_memory +--- +``` + +#### 3.3 更新 task/SKILL.md + +```yaml +--- +name: task_management +description: 任务管理 - 管理连续性任务和相关信息 +context: inline +allowed_tools: + - builtin:graph_memory +--- +``` + +--- + +### Phase 4: 依赖与构建 + +#### 4.1 更新 `package.json` + +```json +{ + "name": "trulymem-waterflow", + "version": "1.0.0", + "description": "TrulyMEM 图记忆系统 - WaterFlow Skill/Tool 实现", + "type": "module", + "main": "./dist/runtime/core/tools/builtin/index.js", + "exports": { + "./tools": "./dist/runtime/core/tools/builtin/index.js", + "./graph_memory": "./dist/runtime/core/graph_memory/index.js", + "./skills": "./bundled-skills" + }, + "scripts": { + "build": "tsc", + "test": "vitest" + }, + "peerDependencies": { + "waterflow-ts": ">=0.1.0" + }, + "dependencies": { + "sql.js": "^1.8.0" + }, + "devDependencies": { + "@types/node": "^25.5.2", + "typescript": "^5.0.0", + "vitest": "^2.0.0" + } +} +``` + +#### 4.2 添加 `sql.js.d.ts` 类型声明 + +保留现有 `ts/src/types/sql.js.d.ts`,确保编译通过。 + +--- + +### Phase 5: 验证 + +#### 5.1 编译验证 + +```bash +cd ts/ +npm install +npm run build +# 检查 dist/ 输出 +``` + +#### 5.2 类型检查 + +```bash +npx tsc --noEmit +# 确保无类型错误 +``` + +#### 5.3 集成验证(在 WaterFlow 项目中) + +```typescript +// 在 WaterFlow 入口文件中测试 +import { getPlatform } from './platform'; +import { initializeToolRegistry } from './runtime/core/tools/builtin'; +import { registerGraphMemoryTool } from 'trulymem/tools'; + +const platform = getPlatform(); +const registry = initializeToolRegistry(platform); + +// 注册图记忆工具 +registerGraphMemoryTool(registry, 'test-session'); + +// 验证工具已注册 +const tool = registry.get('builtin:graph_memory'); +console.log('GraphMemory tool registered:', !!tool); +``` + +--- + +## 文件变更清单 + +| 操作 | 文件路径 | 说明 | +|------|---------|------| +| 🗑️ 删除 | `ts/src/platform/index.ts` | 重复平台层 | +| 🗑️ 删除 | `ts/src/platform/node.ts` | 重复平台层 | +| 🗑️ 删除 | `ts/src/platform/types.ts` | 重复平台层 | +| 🗑️ 删除 | `ts/src/runtime/core/tools/tool_interface.ts` | 重复接口 | +| ✏️ 修改 | `ts/src/runtime/core/graph_memory/graph_database.ts` | 改用 WaterFlow fs | +| ✏️ 修改 | `ts/src/runtime/core/tools/builtin/graph_memory_tool.ts` | 适配 WaterFlow 接口 | +| ✏️ 修改 | `ts/src/runtime/core/graph_memory/index.ts` | 更新导出路径 | +| ✏️ 修改 | `ts/bundled-skills/graph_memory/SKILL.md` | 补充完整操作说明 | +| ✏️ 修改 | `ts/bundled-skills/graph_memory/persona/SKILL.md` | 完善 | +| ✏️ 修改 | `ts/bundled-skills/graph_memory/task/SKILL.md` | 完善 | +| ✏️ 修改 | `ts/package.json` | 调整为 peerDependency | +| ✏️ 修改 | `ts/tsconfig.json` | 清理配置 | +| ➕ 新增 | `ts/src/runtime/core/tools/builtin/index.ts` | 注册入口 | + +--- + +## 依赖关系图(重构后) + +``` +WaterFlow 框架 +├── Platform (getPlatform) +│ ├── fs.readFile/writeFile (binary) ← GraphDatabase 使用 +│ ├── path.join/resolve ← 路径构建 +│ ├── getLogger ← 日志 +│ └── globals.randomUUID ← UUID 生成 +│ +├── ToolRegistry +│ ├── FRAMEWORK_TOOLS (Agent, ToolSearch) +│ ├── Platform Tools (Read, Write, Bash...) +│ └── GraphMemoryTool ← 从 trulymem/tools 注册 +│ +└── SkillLoader + └── bundled-skills/graph_memory/SKILL.md + └── allowed_tools: ['builtin:graph_memory'] + └→ LLM 调用 → ToolExecutor → GraphMemoryTool.handler() +``` + +--- + +## 风险与注意事项 + +1. **WaterFlow 版本依赖**: 使用 `peerDependencies` 确保与 WaterFlow 版本兼容 +2. **二进制存储**: WaterFlow 的 `fs` 支持 `encoding: 'binary'`,已验证可用 +3. **类型兼容**: 确保 Tool 接口字段与 WaterFlow 完全一致 +4. **SKILL.md 格式**: YAML frontmatter 必须符合 WaterFlow SkillLoader 解析规则