diff --git a/packages/agent-memory/package.json b/packages/agent-memory/package.json new file mode 100644 index 00000000..e526af23 --- /dev/null +++ b/packages/agent-memory/package.json @@ -0,0 +1,50 @@ +{ + "name": "@betterdb/agent-memory", + "version": "0.1.0", + "description": "Standalone agent memory: agent-cache short-term tiers plus a semantic long-term MemoryStore over valkey-search", + "keywords": [ + "valkey", + "redis", + "valkey-search", + "agent", + "memory", + "llm" + ], + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/BetterDB-inc/monitor", + "directory": "packages/agent-memory" + }, + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js", + "require": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsc", + "typecheck": "tsc --noEmit", + "test": "vitest run", + "test:watch": "vitest", + "clean": "rm -rf dist" + }, + "dependencies": { + "@betterdb/agent-cache": "workspace:*", + "@betterdb/valkey-search-kit": "workspace:*" + }, + "devDependencies": { + "@types/node": "^22.19.15", + "typescript": "^5.9.3", + "vitest": "^4.1.1" + }, + "engines": { + "node": ">=20.0.0" + } +} diff --git a/packages/agent-memory/src/AgentMemory.ts b/packages/agent-memory/src/AgentMemory.ts new file mode 100644 index 00000000..560ddbfd --- /dev/null +++ b/packages/agent-memory/src/AgentMemory.ts @@ -0,0 +1 @@ +export class AgentMemory {} diff --git a/packages/agent-memory/src/MemoryStore.ts b/packages/agent-memory/src/MemoryStore.ts new file mode 100644 index 00000000..2a0d701f --- /dev/null +++ b/packages/agent-memory/src/MemoryStore.ts @@ -0,0 +1 @@ +export class MemoryStore {} diff --git a/packages/agent-memory/src/__tests__/helpers/fakeEmbed.ts b/packages/agent-memory/src/__tests__/helpers/fakeEmbed.ts new file mode 100644 index 00000000..dd5cef81 --- /dev/null +++ b/packages/agent-memory/src/__tests__/helpers/fakeEmbed.ts @@ -0,0 +1,18 @@ +import { createHash } from 'node:crypto'; + +/** + * Deterministic, dimension-configurable embedding for hermetic unit tests: + * the same text always maps to the same normalized vector, so similarity is + * predictable without a real embedding provider. + */ +export function fakeEmbed(dims: number): (text: string) => Promise { + return async (text: string) => { + const hash = createHash('sha256').update(text).digest('hex'); + const vec = Array.from({ length: dims }, (_, i) => { + const offset = (i % 32) * 2; + return parseInt(hash.slice(offset, offset + 2), 16) / 255; + }); + const norm = Math.sqrt(vec.reduce((sum, v) => sum + v * v, 0)) || 1; + return vec.map((v) => v / norm); + }; +} diff --git a/packages/agent-memory/src/__tests__/helpers/mockClient.ts b/packages/agent-memory/src/__tests__/helpers/mockClient.ts new file mode 100644 index 00000000..319fabc5 --- /dev/null +++ b/packages/agent-memory/src/__tests__/helpers/mockClient.ts @@ -0,0 +1,21 @@ +import { vi } from 'vitest'; + +export type CommandHandler = (command: string, ...args: (string | Buffer | number)[]) => unknown; + +export interface MockClient { + call: ReturnType; +} + +/** + * Minimal iovalkey-style mock: a `call` spy backed by a command handler. + * Mirrors the FT.* mocking pattern used in semantic-cache's unit tests. + */ +export function mockClient(handler?: CommandHandler): MockClient { + const defaultHandler: CommandHandler = () => 'OK'; + const impl = handler ?? defaultHandler; + return { + call: vi.fn(async (command: string, ...args: (string | Buffer | number)[]) => + impl(command, ...args), + ), + }; +} diff --git a/packages/agent-memory/src/__tests__/index.test.ts b/packages/agent-memory/src/__tests__/index.test.ts new file mode 100644 index 00000000..b2045d1b --- /dev/null +++ b/packages/agent-memory/src/__tests__/index.test.ts @@ -0,0 +1,16 @@ +import { describe, it, expect } from 'vitest'; +import { AgentCache, MemoryStore, AgentMemory } from '../index'; + +describe('@betterdb/agent-memory exports', () => { + it('re-exports AgentCache from @betterdb/agent-cache', () => { + expect(typeof AgentCache).toBe('function'); + }); + + it('exports the MemoryStore tier', () => { + expect(typeof MemoryStore).toBe('function'); + }); + + it('exports the AgentMemory facade', () => { + expect(typeof AgentMemory).toBe('function'); + }); +}); diff --git a/packages/agent-memory/src/index.ts b/packages/agent-memory/src/index.ts new file mode 100644 index 00000000..2d471337 --- /dev/null +++ b/packages/agent-memory/src/index.ts @@ -0,0 +1,3 @@ +export * from '@betterdb/agent-cache'; +export { MemoryStore } from './MemoryStore'; +export { AgentMemory } from './AgentMemory'; diff --git a/packages/agent-memory/tsconfig.json b/packages/agent-memory/tsconfig.json new file mode 100644 index 00000000..78f3e3fd --- /dev/null +++ b/packages/agent-memory/tsconfig.json @@ -0,0 +1,19 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "CommonJS", + "lib": ["ES2022"], + "declaration": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "moduleResolution": "node", + "types": ["node"] + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "src/__tests__"] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4cb391f8..85381a1e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -463,6 +463,25 @@ importers: specifier: ^4.1.1 version: 4.1.1(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(happy-dom@20.8.9)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@8.0.16(@types/node@22.19.15)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0)) + packages/agent-memory: + dependencies: + '@betterdb/agent-cache': + specifier: workspace:* + version: link:../agent-cache + '@betterdb/valkey-search-kit': + specifier: workspace:* + version: link:../valkey-search-kit + devDependencies: + '@types/node': + specifier: ^22.19.15 + version: 22.19.15 + typescript: + specifier: ^5.9.3 + version: 5.9.3 + vitest: + specifier: ^4.1.1 + version: 4.1.1(@opentelemetry/api@1.9.0)(@types/node@22.19.15)(happy-dom@20.8.9)(msw@2.12.14(@types/node@22.19.15)(typescript@5.9.3))(vite@8.0.16(@types/node@22.19.15)(esbuild@0.28.1)(jiti@2.6.1)(terser@5.44.1)(tsx@4.21.0)(yaml@2.9.0)) + packages/cache-benchmark-ts: dependencies: '@betterdb/semantic-cache': @@ -15663,7 +15682,7 @@ snapshots: isstream: 0.1.2 jsonwebtoken: 9.0.3 mime-types: 2.1.35 - retry-axios: 2.6.0(axios@1.16.0(debug@4.4.3)) + retry-axios: 2.6.0(axios@1.16.0) tough-cookie: 4.1.4 transitivePeerDependencies: - supports-color @@ -17799,7 +17818,7 @@ snapshots: ret@0.5.0: {} - retry-axios@2.6.0(axios@1.16.0(debug@4.4.3)): + retry-axios@2.6.0(axios@1.16.0): dependencies: axios: 1.16.0(debug@4.4.3)