@aeriondyseti/vector-memory-mcp 0.5.0 → 0.9.0-dev.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  import { randomUUID } from "crypto";
2
2
  import type { Memory } from "../types/memory.js";
3
- import { DELETED_TOMBSTONE, isSuperseded } from "../types/memory.js";
3
+ import { isDeleted } from "../types/memory.js";
4
4
  import type { MemoryRepository } from "../db/memory.repository.js";
5
5
  import type { EmbeddingsService } from "./embeddings.service.js";
6
6
 
@@ -42,33 +42,64 @@ export class MemoryService {
42
42
  return await this.repository.markDeleted(id);
43
43
  }
44
44
 
45
- async search(query: string, limit: number = 10): Promise<Memory[]> {
45
+
46
+ async update(
47
+ id: string,
48
+ updates: {
49
+ content?: string;
50
+ embeddingText?: string;
51
+ metadata?: Record<string, unknown>;
52
+ }
53
+ ): Promise<Memory | null> {
54
+ const existing = await this.repository.findById(id);
55
+ if (!existing) {
56
+ return null;
57
+ }
58
+
59
+ const newContent = updates.content ?? existing.content;
60
+ const newMetadata = updates.metadata ?? existing.metadata;
61
+
62
+ // Regenerate embedding if content or embeddingText changed
63
+ let newEmbedding = existing.embedding;
64
+ if (updates.content !== undefined || updates.embeddingText !== undefined) {
65
+ const textToEmbed = updates.embeddingText ?? newContent;
66
+ newEmbedding = await this.embeddings.embed(textToEmbed);
67
+ }
68
+
69
+ const updatedMemory: Memory = {
70
+ ...existing,
71
+ content: newContent,
72
+ embedding: newEmbedding,
73
+ metadata: newMetadata,
74
+ updatedAt: new Date(),
75
+ };
76
+
77
+ await this.repository.upsert(updatedMemory);
78
+ return updatedMemory;
79
+ }
80
+
81
+ async search(
82
+ query: string,
83
+ limit: number = 10,
84
+ includeDeleted: boolean = false
85
+ ): Promise<Memory[]> {
46
86
  const queryEmbedding = await this.embeddings.embed(query);
47
87
  const fetchLimit = limit * 3;
48
88
 
49
89
  const rows = await this.repository.findSimilar(queryEmbedding, fetchLimit);
50
90
 
51
91
  const results: Memory[] = [];
52
- const seenIds = new Set<string>();
53
92
 
54
93
  for (const row of rows) {
55
- let memory = await this.repository.findById(row.id);
94
+ const memory = await this.repository.findById(row.id);
56
95
 
57
96
  if (!memory) {
58
97
  continue;
59
98
  }
60
99
 
61
- if (isSuperseded(memory)) {
62
- memory = await this.followSupersessionChain(row.id);
63
- if (!memory) {
64
- continue;
65
- }
66
- }
67
-
68
- if (seenIds.has(memory.id)) {
100
+ if (!includeDeleted && isDeleted(memory)) {
69
101
  continue;
70
102
  }
71
- seenIds.add(memory.id);
72
103
 
73
104
  results.push(memory);
74
105
  if (results.length >= limit) {
@@ -79,29 +110,76 @@ export class MemoryService {
79
110
  return results;
80
111
  }
81
112
 
82
- private async followSupersessionChain(memoryId: string): Promise<Memory | null> {
83
- const visited = new Set<string>();
84
- let currentId: string | null = memoryId;
85
-
86
- while (currentId && !visited.has(currentId)) {
87
- visited.add(currentId);
88
- const memory = await this.repository.findById(currentId);
113
+ private static readonly UUID_ZERO =
114
+ "00000000-0000-0000-0000-000000000000";
115
+
116
+ async storeHandoff(args: {
117
+ project: string;
118
+ branch?: string;
119
+ summary: string;
120
+ completed?: string[];
121
+ in_progress_blocked?: string[];
122
+ key_decisions?: string[];
123
+ next_steps?: string[];
124
+ memory_ids?: string[];
125
+ metadata?: Record<string, unknown>;
126
+ }): Promise<Memory> {
127
+ const now = new Date();
128
+ const date = now.toISOString().slice(0, 10);
129
+ const time = now.toISOString().slice(11, 16);
89
130
 
90
- if (!memory) {
91
- return null;
131
+ const list = (items: string[] | undefined) => {
132
+ if (!items || items.length === 0) {
133
+ return "- (none)";
92
134
  }
135
+ return items.map((i) => `- ${i}`).join("\n");
136
+ };
93
137
 
94
- if (memory.supersededBy === null) {
95
- return memory;
96
- }
138
+ const content = `# Handoff - ${args.project}
139
+ **Date:** ${date} ${time} | **Branch:** ${args.branch ?? "unknown"}
97
140
 
98
- if (memory.supersededBy === DELETED_TOMBSTONE) {
99
- return null;
100
- }
141
+ ## Summary
142
+ ${args.summary}
101
143
 
102
- currentId = memory.supersededBy;
103
- }
144
+ ## Completed
145
+ ${list(args.completed)}
146
+
147
+ ## In Progress / Blocked
148
+ ${list(args.in_progress_blocked)}
149
+
150
+ ## Key Decisions
151
+ ${list(args.key_decisions)}
152
+
153
+ ## Next Steps
154
+ ${list(args.next_steps)}
155
+
156
+ ## Memory IDs
157
+ ${list(args.memory_ids)}`;
158
+
159
+ const metadata: Record<string, unknown> = {
160
+ ...(args.metadata ?? {}),
161
+ type: "handoff",
162
+ project: args.project,
163
+ date,
164
+ branch: args.branch ?? "unknown",
165
+ memory_ids: args.memory_ids ?? [],
166
+ };
167
+
168
+ const memory: Memory = {
169
+ id: MemoryService.UUID_ZERO,
170
+ content,
171
+ embedding: new Array(this.embeddings.dimension).fill(0),
172
+ metadata,
173
+ createdAt: now,
174
+ updatedAt: now,
175
+ supersededBy: null,
176
+ };
177
+
178
+ await this.repository.upsert(memory);
179
+ return memory;
180
+ }
104
181
 
105
- return null;
182
+ async getLatestHandoff(): Promise<Memory | null> {
183
+ return await this.get(MemoryService.UUID_ZERO);
106
184
  }
107
185
  }
@@ -19,10 +19,6 @@ export function isDeleted(memory: Memory): boolean {
19
19
  return memory.supersededBy === DELETED_TOMBSTONE;
20
20
  }
21
21
 
22
- export function isSuperseded(memory: Memory): boolean {
23
- return memory.supersededBy !== null;
24
- }
25
-
26
22
  export function memoryToDict(memory: Memory): Record<string, unknown> {
27
23
  return {
28
24
  id: memory.id,