@waynesutton/agent-memory 0.0.1-alpha.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.
Files changed (200) hide show
  1. package/.claude/settings.json +9 -0
  2. package/.claude/settings.local.json +7 -0
  3. package/AGENTS.md +113 -0
  4. package/CLAUDE.md +79 -0
  5. package/README.md +1003 -0
  6. package/dist/cli/index.d.ts +3 -0
  7. package/dist/cli/index.d.ts.map +1 -0
  8. package/dist/cli/index.js +192 -0
  9. package/dist/cli/index.js.map +1 -0
  10. package/dist/cli/parsers/claude-code.d.ts +3 -0
  11. package/dist/cli/parsers/claude-code.d.ts.map +1 -0
  12. package/dist/cli/parsers/claude-code.js +75 -0
  13. package/dist/cli/parsers/claude-code.js.map +1 -0
  14. package/dist/cli/parsers/codex.d.ts +3 -0
  15. package/dist/cli/parsers/codex.d.ts.map +1 -0
  16. package/dist/cli/parsers/codex.js +42 -0
  17. package/dist/cli/parsers/codex.js.map +1 -0
  18. package/dist/cli/parsers/conductor.d.ts +3 -0
  19. package/dist/cli/parsers/conductor.d.ts.map +1 -0
  20. package/dist/cli/parsers/conductor.js +43 -0
  21. package/dist/cli/parsers/conductor.js.map +1 -0
  22. package/dist/cli/parsers/cursor.d.ts +3 -0
  23. package/dist/cli/parsers/cursor.d.ts.map +1 -0
  24. package/dist/cli/parsers/cursor.js +50 -0
  25. package/dist/cli/parsers/cursor.js.map +1 -0
  26. package/dist/cli/parsers/index.d.ts +12 -0
  27. package/dist/cli/parsers/index.d.ts.map +1 -0
  28. package/dist/cli/parsers/index.js +27 -0
  29. package/dist/cli/parsers/index.js.map +1 -0
  30. package/dist/cli/parsers/opencode.d.ts +3 -0
  31. package/dist/cli/parsers/opencode.d.ts.map +1 -0
  32. package/dist/cli/parsers/opencode.js +72 -0
  33. package/dist/cli/parsers/opencode.js.map +1 -0
  34. package/dist/cli/parsers/parsers.test.d.ts +2 -0
  35. package/dist/cli/parsers/parsers.test.d.ts.map +1 -0
  36. package/dist/cli/parsers/parsers.test.js +151 -0
  37. package/dist/cli/parsers/parsers.test.js.map +1 -0
  38. package/dist/cli/parsers/pi.d.ts +3 -0
  39. package/dist/cli/parsers/pi.d.ts.map +1 -0
  40. package/dist/cli/parsers/pi.js +43 -0
  41. package/dist/cli/parsers/pi.js.map +1 -0
  42. package/dist/cli/parsers/types.d.ts +25 -0
  43. package/dist/cli/parsers/types.d.ts.map +1 -0
  44. package/dist/cli/parsers/types.js +2 -0
  45. package/dist/cli/parsers/types.js.map +1 -0
  46. package/dist/cli/parsers/vscode-copilot.d.ts +3 -0
  47. package/dist/cli/parsers/vscode-copilot.d.ts.map +1 -0
  48. package/dist/cli/parsers/vscode-copilot.js +69 -0
  49. package/dist/cli/parsers/vscode-copilot.js.map +1 -0
  50. package/dist/cli/parsers/zed.d.ts +3 -0
  51. package/dist/cli/parsers/zed.d.ts.map +1 -0
  52. package/dist/cli/parsers/zed.js +43 -0
  53. package/dist/cli/parsers/zed.js.map +1 -0
  54. package/dist/cli/sync.d.ts +21 -0
  55. package/dist/cli/sync.d.ts.map +1 -0
  56. package/dist/cli/sync.js +78 -0
  57. package/dist/cli/sync.js.map +1 -0
  58. package/dist/cli/type-extractor.d.ts +25 -0
  59. package/dist/cli/type-extractor.d.ts.map +1 -0
  60. package/dist/cli/type-extractor.js +254 -0
  61. package/dist/cli/type-extractor.js.map +1 -0
  62. package/dist/cli/type-extractor.test.d.ts +2 -0
  63. package/dist/cli/type-extractor.test.d.ts.map +1 -0
  64. package/dist/cli/type-extractor.test.js +173 -0
  65. package/dist/cli/type-extractor.test.js.map +1 -0
  66. package/dist/client/http.d.ts +44 -0
  67. package/dist/client/http.d.ts.map +1 -0
  68. package/dist/client/http.js +311 -0
  69. package/dist/client/http.js.map +1 -0
  70. package/dist/client/index.d.ts +158 -0
  71. package/dist/client/index.d.ts.map +1 -0
  72. package/dist/client/index.js +256 -0
  73. package/dist/client/index.js.map +1 -0
  74. package/dist/component/_generated/api.d.ts +12 -0
  75. package/dist/component/_generated/api.d.ts.map +1 -0
  76. package/dist/component/_generated/api.js +13 -0
  77. package/dist/component/_generated/api.js.map +1 -0
  78. package/dist/component/_generated/dataModel.d.ts +18 -0
  79. package/dist/component/_generated/dataModel.d.ts.map +1 -0
  80. package/dist/component/_generated/dataModel.js +11 -0
  81. package/dist/component/_generated/dataModel.js.map +1 -0
  82. package/dist/component/_generated/server.d.ts +42 -0
  83. package/dist/component/_generated/server.d.ts.map +1 -0
  84. package/dist/component/_generated/server.js +39 -0
  85. package/dist/component/_generated/server.js.map +1 -0
  86. package/dist/component/actions.d.ts +42 -0
  87. package/dist/component/actions.d.ts.map +1 -0
  88. package/dist/component/actions.js +405 -0
  89. package/dist/component/actions.js.map +1 -0
  90. package/dist/component/apiKeyMutations.d.ts +29 -0
  91. package/dist/component/apiKeyMutations.d.ts.map +1 -0
  92. package/dist/component/apiKeyMutations.js +149 -0
  93. package/dist/component/apiKeyMutations.js.map +1 -0
  94. package/dist/component/apiKeyQueries.d.ts +37 -0
  95. package/dist/component/apiKeyQueries.d.ts.map +1 -0
  96. package/dist/component/apiKeyQueries.js +127 -0
  97. package/dist/component/apiKeyQueries.js.map +1 -0
  98. package/dist/component/checksum.d.ts +6 -0
  99. package/dist/component/checksum.d.ts.map +1 -0
  100. package/dist/component/checksum.js +14 -0
  101. package/dist/component/checksum.js.map +1 -0
  102. package/dist/component/checksum.test.d.ts +2 -0
  103. package/dist/component/checksum.test.d.ts.map +1 -0
  104. package/dist/component/checksum.test.js +27 -0
  105. package/dist/component/checksum.test.js.map +1 -0
  106. package/dist/component/convex.config.d.ts +3 -0
  107. package/dist/component/convex.config.d.ts.map +1 -0
  108. package/dist/component/convex.config.js +4 -0
  109. package/dist/component/convex.config.js.map +1 -0
  110. package/dist/component/cronActions.d.ts +3 -0
  111. package/dist/component/cronActions.d.ts.map +1 -0
  112. package/dist/component/cronActions.js +38 -0
  113. package/dist/component/cronActions.js.map +1 -0
  114. package/dist/component/cronQueries.d.ts +6 -0
  115. package/dist/component/cronQueries.d.ts.map +1 -0
  116. package/dist/component/cronQueries.js +38 -0
  117. package/dist/component/cronQueries.js.map +1 -0
  118. package/dist/component/crons.d.ts +3 -0
  119. package/dist/component/crons.d.ts.map +1 -0
  120. package/dist/component/crons.js +18 -0
  121. package/dist/component/crons.js.map +1 -0
  122. package/dist/component/format.d.ts +11 -0
  123. package/dist/component/format.d.ts.map +1 -0
  124. package/dist/component/format.js +175 -0
  125. package/dist/component/format.js.map +1 -0
  126. package/dist/component/format.test.d.ts +2 -0
  127. package/dist/component/format.test.d.ts.map +1 -0
  128. package/dist/component/format.test.js +118 -0
  129. package/dist/component/format.test.js.map +1 -0
  130. package/dist/component/mutations.d.ts +158 -0
  131. package/dist/component/mutations.d.ts.map +1 -0
  132. package/dist/component/mutations.js +745 -0
  133. package/dist/component/mutations.js.map +1 -0
  134. package/dist/component/queries.d.ts +94 -0
  135. package/dist/component/queries.d.ts.map +1 -0
  136. package/dist/component/queries.js +574 -0
  137. package/dist/component/queries.js.map +1 -0
  138. package/dist/component/schema.d.ts +278 -0
  139. package/dist/component/schema.d.ts.map +1 -0
  140. package/dist/component/schema.js +161 -0
  141. package/dist/component/schema.js.map +1 -0
  142. package/dist/mcp/server.d.ts +11 -0
  143. package/dist/mcp/server.d.ts.map +1 -0
  144. package/dist/mcp/server.js +571 -0
  145. package/dist/mcp/server.js.map +1 -0
  146. package/dist/shared.d.ts +126 -0
  147. package/dist/shared.d.ts.map +1 -0
  148. package/dist/shared.js +67 -0
  149. package/dist/shared.js.map +1 -0
  150. package/dist/test.d.ts +23 -0
  151. package/dist/test.d.ts.map +1 -0
  152. package/dist/test.js +21 -0
  153. package/dist/test.js.map +1 -0
  154. package/eslint.config.js +15 -0
  155. package/example/convex/convex.config.ts +7 -0
  156. package/example/convex/memory.ts +129 -0
  157. package/llms.md +175 -0
  158. package/llms.txt +126 -0
  159. package/package.json +80 -0
  160. package/prds/API-REFERENCE.md +935 -0
  161. package/prds/SETUP.md +682 -0
  162. package/src/cli/index.ts +254 -0
  163. package/src/cli/parsers/claude-code.ts +80 -0
  164. package/src/cli/parsers/codex.ts +45 -0
  165. package/src/cli/parsers/conductor.ts +47 -0
  166. package/src/cli/parsers/cursor.ts +55 -0
  167. package/src/cli/parsers/index.ts +30 -0
  168. package/src/cli/parsers/opencode.ts +84 -0
  169. package/src/cli/parsers/parsers.test.ts +201 -0
  170. package/src/cli/parsers/pi.ts +47 -0
  171. package/src/cli/parsers/types.ts +26 -0
  172. package/src/cli/parsers/vscode-copilot.ts +78 -0
  173. package/src/cli/parsers/zed.ts +47 -0
  174. package/src/cli/sync.ts +110 -0
  175. package/src/cli/type-extractor.test.ts +241 -0
  176. package/src/cli/type-extractor.ts +331 -0
  177. package/src/client/http.ts +415 -0
  178. package/src/client/index.ts +519 -0
  179. package/src/component/_generated/api.ts +14 -0
  180. package/src/component/_generated/dataModel.ts +20 -0
  181. package/src/component/_generated/server.ts +64 -0
  182. package/src/component/actions.ts +558 -0
  183. package/src/component/apiKeyMutations.ts +175 -0
  184. package/src/component/apiKeyQueries.ts +156 -0
  185. package/src/component/checksum.test.ts +31 -0
  186. package/src/component/checksum.ts +13 -0
  187. package/src/component/convex.config.ts +5 -0
  188. package/src/component/cronActions.ts +52 -0
  189. package/src/component/cronQueries.ts +42 -0
  190. package/src/component/crons.ts +34 -0
  191. package/src/component/format.test.ts +133 -0
  192. package/src/component/format.ts +232 -0
  193. package/src/component/mutations.ts +824 -0
  194. package/src/component/queries.ts +684 -0
  195. package/src/component/schema.ts +207 -0
  196. package/src/mcp/server.ts +695 -0
  197. package/src/shared.ts +251 -0
  198. package/src/test.ts +32 -0
  199. package/tsconfig.json +21 -0
  200. package/vitest.config.ts +8 -0
@@ -0,0 +1,745 @@
1
+ import { mutation, internalMutation } from "./_generated/server.js";
2
+ import { v } from "convex/values";
3
+ import { memoryTypeValidator, scopeValidator, syncDirectionValidator, feedbackSentimentValidator, } from "./schema.js";
4
+ import { computeChecksum } from "./checksum.js";
5
+ // ── create ──────────────────────────────────────────────────────────
6
+ export const create = mutation({
7
+ args: {
8
+ projectId: v.string(),
9
+ scope: scopeValidator,
10
+ userId: v.optional(v.string()),
11
+ agentId: v.optional(v.string()),
12
+ sessionId: v.optional(v.string()),
13
+ title: v.string(),
14
+ content: v.string(),
15
+ memoryType: memoryTypeValidator,
16
+ tags: v.optional(v.array(v.string())),
17
+ paths: v.optional(v.array(v.string())),
18
+ priority: v.optional(v.float64()),
19
+ source: v.optional(v.string()),
20
+ },
21
+ returns: v.string(),
22
+ handler: async (ctx, args) => {
23
+ const checksum = computeChecksum(args.content);
24
+ const now = Date.now();
25
+ const id = await ctx.db.insert("memories", {
26
+ projectId: args.projectId,
27
+ scope: args.scope,
28
+ userId: args.userId,
29
+ agentId: args.agentId,
30
+ sessionId: args.sessionId,
31
+ title: args.title,
32
+ content: args.content,
33
+ memoryType: args.memoryType,
34
+ tags: args.tags ?? [],
35
+ paths: args.paths,
36
+ priority: args.priority,
37
+ source: args.source,
38
+ checksum,
39
+ archived: false,
40
+ accessCount: 0,
41
+ lastAccessedAt: now,
42
+ positiveCount: 0,
43
+ negativeCount: 0,
44
+ });
45
+ // Record history
46
+ await ctx.db.insert("memoryHistory", {
47
+ memoryId: id,
48
+ projectId: args.projectId,
49
+ newContent: args.content,
50
+ newTitle: args.title,
51
+ event: "created",
52
+ actor: args.source ?? args.agentId ?? args.userId ?? "unknown",
53
+ timestamp: now,
54
+ });
55
+ return id;
56
+ },
57
+ });
58
+ // ── update ──────────────────────────────────────────────────────────
59
+ export const update = mutation({
60
+ args: {
61
+ memoryId: v.string(),
62
+ content: v.optional(v.string()),
63
+ title: v.optional(v.string()),
64
+ tags: v.optional(v.array(v.string())),
65
+ paths: v.optional(v.array(v.string())),
66
+ priority: v.optional(v.float64()),
67
+ memoryType: v.optional(memoryTypeValidator),
68
+ actor: v.optional(v.string()),
69
+ },
70
+ returns: v.null(),
71
+ handler: async (ctx, args) => {
72
+ const id = ctx.db.normalizeId("memories", args.memoryId);
73
+ if (!id)
74
+ throw new Error(`Invalid memory ID: ${args.memoryId}`);
75
+ const existing = await ctx.db.get(id);
76
+ if (!existing)
77
+ throw new Error(`Memory not found: ${args.memoryId}`);
78
+ const patch = {};
79
+ if (args.content !== undefined) {
80
+ patch.content = args.content;
81
+ patch.checksum = computeChecksum(args.content);
82
+ }
83
+ if (args.title !== undefined)
84
+ patch.title = args.title;
85
+ if (args.tags !== undefined)
86
+ patch.tags = args.tags;
87
+ if (args.paths !== undefined)
88
+ patch.paths = args.paths;
89
+ if (args.priority !== undefined)
90
+ patch.priority = args.priority;
91
+ if (args.memoryType !== undefined)
92
+ patch.memoryType = args.memoryType;
93
+ await ctx.db.patch(id, patch);
94
+ // Record history if content or title changed
95
+ if (args.content !== undefined || args.title !== undefined) {
96
+ await ctx.db.insert("memoryHistory", {
97
+ memoryId: id,
98
+ projectId: existing.projectId,
99
+ previousContent: args.content !== undefined ? existing.content : undefined,
100
+ newContent: args.content,
101
+ previousTitle: args.title !== undefined ? existing.title : undefined,
102
+ newTitle: args.title,
103
+ event: "updated",
104
+ actor: args.actor ?? "unknown",
105
+ timestamp: Date.now(),
106
+ });
107
+ }
108
+ return null;
109
+ },
110
+ });
111
+ // ── archive (soft-delete) ───────────────────────────────────────────
112
+ export const archive = mutation({
113
+ args: {
114
+ memoryId: v.string(),
115
+ actor: v.optional(v.string()),
116
+ },
117
+ returns: v.null(),
118
+ handler: async (ctx, args) => {
119
+ const id = ctx.db.normalizeId("memories", args.memoryId);
120
+ if (!id)
121
+ throw new Error(`Invalid memory ID: ${args.memoryId}`);
122
+ const existing = await ctx.db.get(id);
123
+ if (!existing)
124
+ throw new Error(`Memory not found: ${args.memoryId}`);
125
+ await ctx.db.patch(id, { archived: true });
126
+ await ctx.db.insert("memoryHistory", {
127
+ memoryId: id,
128
+ projectId: existing.projectId,
129
+ previousContent: existing.content,
130
+ previousTitle: existing.title,
131
+ event: "archived",
132
+ actor: args.actor ?? "unknown",
133
+ timestamp: Date.now(),
134
+ });
135
+ return null;
136
+ },
137
+ });
138
+ // ── restore (un-archive) ───────────────────────────────────────────
139
+ export const restore = mutation({
140
+ args: {
141
+ memoryId: v.string(),
142
+ actor: v.optional(v.string()),
143
+ },
144
+ returns: v.null(),
145
+ handler: async (ctx, args) => {
146
+ const id = ctx.db.normalizeId("memories", args.memoryId);
147
+ if (!id)
148
+ throw new Error(`Invalid memory ID: ${args.memoryId}`);
149
+ const existing = await ctx.db.get(id);
150
+ if (!existing)
151
+ throw new Error(`Memory not found: ${args.memoryId}`);
152
+ await ctx.db.patch(id, { archived: false });
153
+ await ctx.db.insert("memoryHistory", {
154
+ memoryId: id,
155
+ projectId: existing.projectId,
156
+ event: "restored",
157
+ actor: args.actor ?? "unknown",
158
+ timestamp: Date.now(),
159
+ });
160
+ return null;
161
+ },
162
+ });
163
+ // ── batchArchive ────────────────────────────────────────────────────
164
+ export const batchArchive = mutation({
165
+ args: {
166
+ memoryIds: v.array(v.string()),
167
+ actor: v.optional(v.string()),
168
+ },
169
+ returns: v.object({
170
+ archived: v.float64(),
171
+ failed: v.float64(),
172
+ }),
173
+ handler: async (ctx, args) => {
174
+ let archived = 0;
175
+ let failed = 0;
176
+ const now = Date.now();
177
+ for (const memoryId of args.memoryIds) {
178
+ const id = ctx.db.normalizeId("memories", memoryId);
179
+ if (!id) {
180
+ failed++;
181
+ continue;
182
+ }
183
+ const existing = await ctx.db.get(id);
184
+ if (!existing) {
185
+ failed++;
186
+ continue;
187
+ }
188
+ await ctx.db.patch(id, { archived: true });
189
+ await ctx.db.insert("memoryHistory", {
190
+ memoryId: id,
191
+ projectId: existing.projectId,
192
+ previousContent: existing.content,
193
+ previousTitle: existing.title,
194
+ event: "archived",
195
+ actor: args.actor ?? "unknown",
196
+ timestamp: now,
197
+ });
198
+ archived++;
199
+ }
200
+ return { archived, failed };
201
+ },
202
+ });
203
+ // ── batchUpdate ─────────────────────────────────────────────────────
204
+ export const batchUpdate = mutation({
205
+ args: {
206
+ updates: v.array(v.object({
207
+ memoryId: v.string(),
208
+ content: v.optional(v.string()),
209
+ title: v.optional(v.string()),
210
+ tags: v.optional(v.array(v.string())),
211
+ paths: v.optional(v.array(v.string())),
212
+ priority: v.optional(v.float64()),
213
+ memoryType: v.optional(memoryTypeValidator),
214
+ })),
215
+ actor: v.optional(v.string()),
216
+ },
217
+ returns: v.object({
218
+ updated: v.float64(),
219
+ failed: v.float64(),
220
+ }),
221
+ handler: async (ctx, args) => {
222
+ let updated = 0;
223
+ let failed = 0;
224
+ const now = Date.now();
225
+ for (const upd of args.updates) {
226
+ const id = ctx.db.normalizeId("memories", upd.memoryId);
227
+ if (!id) {
228
+ failed++;
229
+ continue;
230
+ }
231
+ const existing = await ctx.db.get(id);
232
+ if (!existing) {
233
+ failed++;
234
+ continue;
235
+ }
236
+ const patch = {};
237
+ if (upd.content !== undefined) {
238
+ patch.content = upd.content;
239
+ patch.checksum = computeChecksum(upd.content);
240
+ }
241
+ if (upd.title !== undefined)
242
+ patch.title = upd.title;
243
+ if (upd.tags !== undefined)
244
+ patch.tags = upd.tags;
245
+ if (upd.paths !== undefined)
246
+ patch.paths = upd.paths;
247
+ if (upd.priority !== undefined)
248
+ patch.priority = upd.priority;
249
+ if (upd.memoryType !== undefined)
250
+ patch.memoryType = upd.memoryType;
251
+ await ctx.db.patch(id, patch);
252
+ if (upd.content !== undefined || upd.title !== undefined) {
253
+ await ctx.db.insert("memoryHistory", {
254
+ memoryId: id,
255
+ projectId: existing.projectId,
256
+ previousContent: upd.content !== undefined ? existing.content : undefined,
257
+ newContent: upd.content,
258
+ previousTitle: upd.title !== undefined ? existing.title : undefined,
259
+ newTitle: upd.title,
260
+ event: "updated",
261
+ actor: args.actor ?? "unknown",
262
+ timestamp: now,
263
+ });
264
+ }
265
+ updated++;
266
+ }
267
+ return { updated, failed };
268
+ },
269
+ });
270
+ // ── recordAccess (track memory reads for relevance) ─────────────────
271
+ export const recordAccess = mutation({
272
+ args: {
273
+ memoryIds: v.array(v.string()),
274
+ },
275
+ returns: v.null(),
276
+ handler: async (ctx, args) => {
277
+ const now = Date.now();
278
+ for (const memoryId of args.memoryIds) {
279
+ const id = ctx.db.normalizeId("memories", memoryId);
280
+ if (!id)
281
+ continue;
282
+ const existing = await ctx.db.get(id);
283
+ if (!existing)
284
+ continue;
285
+ await ctx.db.patch(id, {
286
+ accessCount: (existing.accessCount ?? 0) + 1,
287
+ lastAccessedAt: now,
288
+ });
289
+ }
290
+ return null;
291
+ },
292
+ });
293
+ // ── addFeedback ─────────────────────────────────────────────────────
294
+ export const addFeedback = mutation({
295
+ args: {
296
+ memoryId: v.string(),
297
+ sentiment: feedbackSentimentValidator,
298
+ comment: v.optional(v.string()),
299
+ actor: v.string(),
300
+ },
301
+ returns: v.null(),
302
+ handler: async (ctx, args) => {
303
+ const id = ctx.db.normalizeId("memories", args.memoryId);
304
+ if (!id)
305
+ throw new Error(`Invalid memory ID: ${args.memoryId}`);
306
+ const existing = await ctx.db.get(id);
307
+ if (!existing)
308
+ throw new Error(`Memory not found: ${args.memoryId}`);
309
+ // Record the feedback entry
310
+ await ctx.db.insert("memoryFeedback", {
311
+ memoryId: id,
312
+ projectId: existing.projectId,
313
+ sentiment: args.sentiment,
314
+ comment: args.comment,
315
+ actor: args.actor,
316
+ timestamp: Date.now(),
317
+ });
318
+ // Update aggregated counts on the memory
319
+ if (args.sentiment === "positive") {
320
+ await ctx.db.patch(id, {
321
+ positiveCount: (existing.positiveCount ?? 0) + 1,
322
+ });
323
+ }
324
+ else {
325
+ await ctx.db.patch(id, {
326
+ negativeCount: (existing.negativeCount ?? 0) + 1,
327
+ });
328
+ }
329
+ return null;
330
+ },
331
+ });
332
+ // ── addRelation ─────────────────────────────────────────────────────
333
+ export const addRelation = mutation({
334
+ args: {
335
+ projectId: v.string(),
336
+ fromMemoryId: v.string(),
337
+ toMemoryId: v.string(),
338
+ relationship: v.string(),
339
+ metadata: v.optional(v.object({
340
+ confidence: v.optional(v.float64()),
341
+ createdBy: v.optional(v.string()),
342
+ })),
343
+ },
344
+ returns: v.string(),
345
+ handler: async (ctx, args) => {
346
+ const fromId = ctx.db.normalizeId("memories", args.fromMemoryId);
347
+ const toId = ctx.db.normalizeId("memories", args.toMemoryId);
348
+ if (!fromId)
349
+ throw new Error(`Invalid from memory ID: ${args.fromMemoryId}`);
350
+ if (!toId)
351
+ throw new Error(`Invalid to memory ID: ${args.toMemoryId}`);
352
+ const id = await ctx.db.insert("memoryRelations", {
353
+ projectId: args.projectId,
354
+ fromMemoryId: fromId,
355
+ toMemoryId: toId,
356
+ relationship: args.relationship,
357
+ metadata: args.metadata,
358
+ timestamp: Date.now(),
359
+ });
360
+ return id;
361
+ },
362
+ });
363
+ // ── removeRelation ──────────────────────────────────────────────────
364
+ export const removeRelation = mutation({
365
+ args: {
366
+ relationId: v.string(),
367
+ },
368
+ returns: v.null(),
369
+ handler: async (ctx, args) => {
370
+ const id = ctx.db.normalizeId("memoryRelations", args.relationId);
371
+ if (!id)
372
+ throw new Error(`Invalid relation ID: ${args.relationId}`);
373
+ await ctx.db.delete(id);
374
+ return null;
375
+ },
376
+ });
377
+ // ── importFromLocal (bulk upsert) ───────────────────────────────────
378
+ const importMemoryValidator = v.object({
379
+ title: v.string(),
380
+ content: v.string(),
381
+ memoryType: memoryTypeValidator,
382
+ scope: scopeValidator,
383
+ tags: v.array(v.string()),
384
+ paths: v.optional(v.array(v.string())),
385
+ priority: v.optional(v.float64()),
386
+ source: v.string(),
387
+ checksum: v.string(),
388
+ });
389
+ export const importFromLocal = mutation({
390
+ args: {
391
+ projectId: v.string(),
392
+ userId: v.optional(v.string()),
393
+ memories: v.array(importMemoryValidator),
394
+ },
395
+ returns: v.object({
396
+ created: v.float64(),
397
+ updated: v.float64(),
398
+ unchanged: v.float64(),
399
+ }),
400
+ handler: async (ctx, args) => {
401
+ let created = 0;
402
+ let updated = 0;
403
+ let unchanged = 0;
404
+ const now = Date.now();
405
+ for (const mem of args.memories) {
406
+ const existing = await ctx.db
407
+ .query("memories")
408
+ .withIndex("by_project_title", (q) => q.eq("projectId", args.projectId).eq("title", mem.title))
409
+ .first();
410
+ if (!existing) {
411
+ const id = await ctx.db.insert("memories", {
412
+ projectId: args.projectId,
413
+ userId: args.userId,
414
+ title: mem.title,
415
+ content: mem.content,
416
+ memoryType: mem.memoryType,
417
+ scope: mem.scope,
418
+ tags: mem.tags,
419
+ paths: mem.paths,
420
+ priority: mem.priority,
421
+ source: mem.source,
422
+ checksum: mem.checksum,
423
+ archived: false,
424
+ accessCount: 0,
425
+ lastAccessedAt: now,
426
+ positiveCount: 0,
427
+ negativeCount: 0,
428
+ });
429
+ await ctx.db.insert("memoryHistory", {
430
+ memoryId: id,
431
+ projectId: args.projectId,
432
+ newContent: mem.content,
433
+ newTitle: mem.title,
434
+ event: "created",
435
+ actor: mem.source,
436
+ timestamp: now,
437
+ });
438
+ created++;
439
+ }
440
+ else if (existing.checksum !== mem.checksum) {
441
+ await ctx.db.patch(existing._id, {
442
+ content: mem.content,
443
+ memoryType: mem.memoryType,
444
+ tags: mem.tags,
445
+ paths: mem.paths,
446
+ priority: mem.priority,
447
+ source: mem.source,
448
+ checksum: mem.checksum,
449
+ });
450
+ await ctx.db.insert("memoryHistory", {
451
+ memoryId: existing._id,
452
+ projectId: args.projectId,
453
+ previousContent: existing.content,
454
+ newContent: mem.content,
455
+ previousTitle: existing.title,
456
+ newTitle: mem.title,
457
+ event: "updated",
458
+ actor: mem.source,
459
+ timestamp: now,
460
+ });
461
+ updated++;
462
+ }
463
+ else {
464
+ unchanged++;
465
+ }
466
+ }
467
+ return { created, updated, unchanged };
468
+ },
469
+ });
470
+ // ── upsertProject ───────────────────────────────────────────────────
471
+ export const upsertProject = mutation({
472
+ args: {
473
+ projectId: v.string(),
474
+ name: v.string(),
475
+ description: v.optional(v.string()),
476
+ settings: v.optional(v.object({
477
+ autoSync: v.boolean(),
478
+ syncFormats: v.array(v.string()),
479
+ embeddingModel: v.optional(v.string()),
480
+ embeddingDimensions: v.optional(v.float64()),
481
+ factExtractionPrompt: v.optional(v.string()),
482
+ updateDecisionPrompt: v.optional(v.string()),
483
+ decayEnabled: v.optional(v.boolean()),
484
+ decayHalfLifeDays: v.optional(v.float64()),
485
+ })),
486
+ },
487
+ returns: v.string(),
488
+ handler: async (ctx, args) => {
489
+ const existing = await ctx.db
490
+ .query("projects")
491
+ .withIndex("by_projectId", (q) => q.eq("projectId", args.projectId))
492
+ .first();
493
+ const settings = args.settings ?? {
494
+ autoSync: false,
495
+ syncFormats: [],
496
+ };
497
+ if (existing) {
498
+ await ctx.db.patch(existing._id, {
499
+ name: args.name,
500
+ description: args.description,
501
+ settings,
502
+ });
503
+ return existing._id;
504
+ }
505
+ const id = await ctx.db.insert("projects", {
506
+ projectId: args.projectId,
507
+ name: args.name,
508
+ description: args.description,
509
+ settings,
510
+ });
511
+ return id;
512
+ },
513
+ });
514
+ // ── recordSync ──────────────────────────────────────────────────────
515
+ export const recordSync = mutation({
516
+ args: {
517
+ projectId: v.string(),
518
+ userId: v.optional(v.string()),
519
+ memoryId: v.string(),
520
+ targetFormat: v.string(),
521
+ targetPath: v.string(),
522
+ checksum: v.string(),
523
+ direction: syncDirectionValidator,
524
+ },
525
+ returns: v.null(),
526
+ handler: async (ctx, args) => {
527
+ const memId = ctx.db.normalizeId("memories", args.memoryId);
528
+ if (!memId)
529
+ throw new Error(`Invalid memory ID: ${args.memoryId}`);
530
+ const now = Date.now();
531
+ await ctx.db.insert("syncLog", {
532
+ projectId: args.projectId,
533
+ userId: args.userId,
534
+ memoryId: memId,
535
+ targetFormat: args.targetFormat,
536
+ targetPath: args.targetPath,
537
+ syncedAt: now,
538
+ checksum: args.checksum,
539
+ direction: args.direction,
540
+ });
541
+ await ctx.db.patch(memId, { lastSyncedAt: now });
542
+ return null;
543
+ },
544
+ });
545
+ // ── storeEmbedding ──────────────────────────────────────────────────
546
+ export const storeEmbedding = mutation({
547
+ args: {
548
+ memoryId: v.string(),
549
+ embedding: v.array(v.float64()),
550
+ model: v.string(),
551
+ dimensions: v.float64(),
552
+ },
553
+ returns: v.null(),
554
+ handler: async (ctx, args) => {
555
+ const memId = ctx.db.normalizeId("memories", args.memoryId);
556
+ if (!memId)
557
+ throw new Error(`Invalid memory ID: ${args.memoryId}`);
558
+ const existing = await ctx.db
559
+ .query("embeddings")
560
+ .withIndex("by_memory", (q) => q.eq("memoryId", memId))
561
+ .first();
562
+ if (existing) {
563
+ await ctx.db.patch(existing._id, {
564
+ embedding: args.embedding,
565
+ model: args.model,
566
+ dimensions: args.dimensions,
567
+ });
568
+ await ctx.db.patch(memId, { embeddingId: existing._id });
569
+ }
570
+ else {
571
+ const embeddingId = await ctx.db.insert("embeddings", {
572
+ memoryId: memId,
573
+ embedding: args.embedding,
574
+ model: args.model,
575
+ dimensions: args.dimensions,
576
+ });
577
+ await ctx.db.patch(memId, { embeddingId });
578
+ }
579
+ return null;
580
+ },
581
+ });
582
+ // ── Internal: applyDecay (called by cron) ───────────────────────────
583
+ export const applyDecay = internalMutation({
584
+ args: {
585
+ projectId: v.string(),
586
+ halfLifeDays: v.float64(),
587
+ },
588
+ returns: v.object({
589
+ processed: v.float64(),
590
+ decayed: v.float64(),
591
+ }),
592
+ handler: async (ctx, args) => {
593
+ const now = Date.now();
594
+ const halfLifeMs = args.halfLifeDays * 24 * 60 * 60 * 1000;
595
+ const memories = await ctx.db
596
+ .query("memories")
597
+ .withIndex("by_project", (q) => q.eq("projectId", args.projectId).eq("archived", false))
598
+ .take(500);
599
+ let processed = 0;
600
+ let decayed = 0;
601
+ for (const m of memories) {
602
+ if ((m.priority ?? 0) >= 0.8)
603
+ continue; // don't decay pinned memories
604
+ processed++;
605
+ const lastAccess = m.lastAccessedAt ?? m._creationTime;
606
+ const timeSinceAccess = now - lastAccess;
607
+ // Exponential decay: score = base * 0.5^(t/halfLife)
608
+ const decayFactor = Math.pow(0.5, timeSinceAccess / halfLifeMs);
609
+ // If very low access and old, reduce priority
610
+ if (decayFactor < 0.1 && (m.accessCount ?? 0) < 3) {
611
+ const currentPriority = m.priority ?? 0.5;
612
+ const newPriority = Math.max(0.01, currentPriority * decayFactor);
613
+ if (Math.abs(newPriority - currentPriority) > 0.01) {
614
+ await ctx.db.patch(m._id, { priority: newPriority });
615
+ decayed++;
616
+ }
617
+ }
618
+ }
619
+ return { processed, decayed };
620
+ },
621
+ });
622
+ // ── Internal: cleanupOldHistory ─────────────────────────────────────
623
+ export const cleanupOldHistory = internalMutation({
624
+ args: {
625
+ projectId: v.string(),
626
+ olderThanMs: v.float64(),
627
+ },
628
+ returns: v.object({ deleted: v.float64() }),
629
+ handler: async (ctx, args) => {
630
+ const cutoff = Date.now() - args.olderThanMs;
631
+ const old = await ctx.db
632
+ .query("memoryHistory")
633
+ .withIndex("by_project", (q) => q.eq("projectId", args.projectId))
634
+ .take(500);
635
+ let deleted = 0;
636
+ for (const entry of old) {
637
+ if (entry.timestamp < cutoff) {
638
+ await ctx.db.delete(entry._id);
639
+ deleted++;
640
+ }
641
+ }
642
+ return { deleted };
643
+ },
644
+ });
645
+ // ── Internal: store ingest result (used by ingest action) ──────────
646
+ export const ingestCreateMemory = internalMutation({
647
+ args: {
648
+ projectId: v.string(),
649
+ scope: scopeValidator,
650
+ userId: v.optional(v.string()),
651
+ agentId: v.optional(v.string()),
652
+ sessionId: v.optional(v.string()),
653
+ title: v.string(),
654
+ content: v.string(),
655
+ memoryType: memoryTypeValidator,
656
+ tags: v.array(v.string()),
657
+ source: v.string(),
658
+ },
659
+ returns: v.string(),
660
+ handler: async (ctx, args) => {
661
+ const checksum = computeChecksum(args.content);
662
+ const now = Date.now();
663
+ const id = await ctx.db.insert("memories", {
664
+ projectId: args.projectId,
665
+ scope: args.scope,
666
+ userId: args.userId,
667
+ agentId: args.agentId,
668
+ sessionId: args.sessionId,
669
+ title: args.title,
670
+ content: args.content,
671
+ memoryType: args.memoryType,
672
+ tags: args.tags,
673
+ checksum,
674
+ archived: false,
675
+ accessCount: 0,
676
+ lastAccessedAt: now,
677
+ positiveCount: 0,
678
+ negativeCount: 0,
679
+ source: args.source,
680
+ });
681
+ await ctx.db.insert("memoryHistory", {
682
+ memoryId: id,
683
+ projectId: args.projectId,
684
+ newContent: args.content,
685
+ newTitle: args.title,
686
+ event: "created",
687
+ actor: "ingest",
688
+ timestamp: now,
689
+ });
690
+ return id;
691
+ },
692
+ });
693
+ export const ingestUpdateMemory = internalMutation({
694
+ args: {
695
+ memoryId: v.string(),
696
+ content: v.string(),
697
+ },
698
+ returns: v.null(),
699
+ handler: async (ctx, args) => {
700
+ const id = ctx.db.normalizeId("memories", args.memoryId);
701
+ if (!id)
702
+ throw new Error(`Invalid memory ID: ${args.memoryId}`);
703
+ const existing = await ctx.db.get(id);
704
+ if (!existing)
705
+ throw new Error(`Memory not found: ${args.memoryId}`);
706
+ const checksum = computeChecksum(args.content);
707
+ await ctx.db.patch(id, { content: args.content, checksum });
708
+ await ctx.db.insert("memoryHistory", {
709
+ memoryId: id,
710
+ projectId: existing.projectId,
711
+ previousContent: existing.content,
712
+ newContent: args.content,
713
+ event: "merged",
714
+ actor: "ingest",
715
+ timestamp: Date.now(),
716
+ });
717
+ return null;
718
+ },
719
+ });
720
+ export const ingestDeleteMemory = internalMutation({
721
+ args: {
722
+ memoryId: v.string(),
723
+ },
724
+ returns: v.null(),
725
+ handler: async (ctx, args) => {
726
+ const id = ctx.db.normalizeId("memories", args.memoryId);
727
+ if (!id)
728
+ throw new Error(`Invalid memory ID: ${args.memoryId}`);
729
+ const existing = await ctx.db.get(id);
730
+ if (!existing)
731
+ throw new Error(`Memory not found: ${args.memoryId}`);
732
+ await ctx.db.patch(id, { archived: true });
733
+ await ctx.db.insert("memoryHistory", {
734
+ memoryId: id,
735
+ projectId: existing.projectId,
736
+ previousContent: existing.content,
737
+ previousTitle: existing.title,
738
+ event: "archived",
739
+ actor: "ingest",
740
+ timestamp: Date.now(),
741
+ });
742
+ return null;
743
+ },
744
+ });
745
+ //# sourceMappingURL=mutations.js.map