om-memory-system 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +587 -0
  3. package/dist/adapters/opencode/auto-capture-host.d.ts +3 -0
  4. package/dist/adapters/opencode/auto-capture-host.js +29 -0
  5. package/dist/adapters/opencode/auto-capture-summary.d.ts +4 -0
  6. package/dist/adapters/opencode/auto-capture-summary.js +147 -0
  7. package/dist/adapters/opencode/conversation.d.ts +2 -0
  8. package/dist/adapters/opencode/conversation.js +50 -0
  9. package/dist/adapters/opencode/user-prompt.d.ts +9 -0
  10. package/dist/adapters/opencode/user-prompt.js +26 -0
  11. package/dist/adapters/pi/capture.d.ts +41 -0
  12. package/dist/adapters/pi/capture.js +91 -0
  13. package/dist/adapters/pi/conversation.d.ts +38 -0
  14. package/dist/adapters/pi/conversation.js +118 -0
  15. package/dist/adapters/pi/extension.d.ts +18 -0
  16. package/dist/adapters/pi/extension.js +234 -0
  17. package/dist/adapters/pi/import-command.d.ts +29 -0
  18. package/dist/adapters/pi/import-command.js +232 -0
  19. package/dist/adapters/pi/profile.d.ts +39 -0
  20. package/dist/adapters/pi/profile.js +148 -0
  21. package/dist/adapters/pi/provider.d.ts +50 -0
  22. package/dist/adapters/pi/provider.js +77 -0
  23. package/dist/config.d.ts +290 -0
  24. package/dist/config.js +742 -0
  25. package/dist/core/capture-context.d.ts +4 -0
  26. package/dist/core/capture-context.js +132 -0
  27. package/dist/core/capture.d.ts +15 -0
  28. package/dist/core/capture.js +68 -0
  29. package/dist/core/extraction.d.ts +80 -0
  30. package/dist/core/extraction.js +140 -0
  31. package/dist/core/host.d.ts +52 -0
  32. package/dist/core/host.js +1 -0
  33. package/dist/core/memory-operations.d.ts +27 -0
  34. package/dist/core/memory-operations.js +247 -0
  35. package/dist/core/retrieval.d.ts +12 -0
  36. package/dist/core/retrieval.js +72 -0
  37. package/dist/importer/discovery.d.ts +31 -0
  38. package/dist/importer/discovery.js +88 -0
  39. package/dist/importer/importer.d.ts +87 -0
  40. package/dist/importer/importer.js +260 -0
  41. package/dist/importer/ledger.d.ts +44 -0
  42. package/dist/importer/ledger.js +137 -0
  43. package/dist/importer/session-loader.d.ts +19 -0
  44. package/dist/importer/session-loader.js +12 -0
  45. package/dist/index.d.ts +24 -0
  46. package/dist/index.js +601 -0
  47. package/dist/plugin.d.ts +9 -0
  48. package/dist/plugin.js +11 -0
  49. package/dist/services/ai/ai-provider-factory.d.ts +7 -0
  50. package/dist/services/ai/ai-provider-factory.js +41 -0
  51. package/dist/services/ai/api-key-placeholder.d.ts +1 -0
  52. package/dist/services/ai/api-key-placeholder.js +14 -0
  53. package/dist/services/ai/internal-capture-sessions.d.ts +6 -0
  54. package/dist/services/ai/internal-capture-sessions.js +33 -0
  55. package/dist/services/ai/opencode-diagnostics.d.ts +7 -0
  56. package/dist/services/ai/opencode-diagnostics.js +36 -0
  57. package/dist/services/ai/opencode-host-config.d.ts +10 -0
  58. package/dist/services/ai/opencode-host-config.js +51 -0
  59. package/dist/services/ai/opencode-provider-loader.d.ts +3 -0
  60. package/dist/services/ai/opencode-provider-loader.js +4 -0
  61. package/dist/services/ai/opencode-provider.d.ts +92 -0
  62. package/dist/services/ai/opencode-provider.js +419 -0
  63. package/dist/services/ai/opencode-sdk-client.d.ts +6 -0
  64. package/dist/services/ai/opencode-sdk-client.js +38 -0
  65. package/dist/services/ai/profile-llm-client.d.ts +2 -0
  66. package/dist/services/ai/profile-llm-client.js +27 -0
  67. package/dist/services/ai/provider-config.d.ts +17 -0
  68. package/dist/services/ai/provider-config.js +30 -0
  69. package/dist/services/ai/providers/anthropic-messages.d.ts +34 -0
  70. package/dist/services/ai/providers/anthropic-messages.js +220 -0
  71. package/dist/services/ai/providers/base-provider.d.ts +24 -0
  72. package/dist/services/ai/providers/base-provider.js +24 -0
  73. package/dist/services/ai/providers/google-gemini.d.ts +15 -0
  74. package/dist/services/ai/providers/google-gemini.js +228 -0
  75. package/dist/services/ai/providers/minimax.d.ts +35 -0
  76. package/dist/services/ai/providers/minimax.js +55 -0
  77. package/dist/services/ai/providers/openai-chat-completion.d.ts +22 -0
  78. package/dist/services/ai/providers/openai-chat-completion.js +387 -0
  79. package/dist/services/ai/providers/openai-responses.d.ts +13 -0
  80. package/dist/services/ai/providers/openai-responses.js +182 -0
  81. package/dist/services/ai/providers/orcarouter.d.ts +51 -0
  82. package/dist/services/ai/providers/orcarouter.js +66 -0
  83. package/dist/services/ai/session/ai-session-manager.d.ts +24 -0
  84. package/dist/services/ai/session/ai-session-manager.js +211 -0
  85. package/dist/services/ai/session/session-types.d.ts +42 -0
  86. package/dist/services/ai/session/session-types.js +1 -0
  87. package/dist/services/ai/tools/tool-schema.d.ts +40 -0
  88. package/dist/services/ai/tools/tool-schema.js +24 -0
  89. package/dist/services/ai/validators/user-profile-validator.d.ts +12 -0
  90. package/dist/services/ai/validators/user-profile-validator.js +114 -0
  91. package/dist/services/api-handlers.d.ts +186 -0
  92. package/dist/services/api-handlers.js +1284 -0
  93. package/dist/services/auth-token.d.ts +14 -0
  94. package/dist/services/auth-token.js +59 -0
  95. package/dist/services/auto-capture.d.ts +3 -0
  96. package/dist/services/auto-capture.js +127 -0
  97. package/dist/services/cleanup-service.d.ts +22 -0
  98. package/dist/services/cleanup-service.js +109 -0
  99. package/dist/services/client.d.ts +134 -0
  100. package/dist/services/client.js +327 -0
  101. package/dist/services/context.d.ts +10 -0
  102. package/dist/services/context.js +27 -0
  103. package/dist/services/cors.d.ts +12 -0
  104. package/dist/services/cors.js +52 -0
  105. package/dist/services/deduplication-service.d.ts +28 -0
  106. package/dist/services/deduplication-service.js +111 -0
  107. package/dist/services/embedding.d.ts +36 -0
  108. package/dist/services/embedding.js +200 -0
  109. package/dist/services/jsonc.d.ts +6 -0
  110. package/dist/services/jsonc.js +76 -0
  111. package/dist/services/language-detector.d.ts +2 -0
  112. package/dist/services/language-detector.js +33 -0
  113. package/dist/services/legacy-migration.d.ts +77 -0
  114. package/dist/services/legacy-migration.js +343 -0
  115. package/dist/services/logger.d.ts +1 -0
  116. package/dist/services/logger.js +119 -0
  117. package/dist/services/memory-portability-service.d.ts +37 -0
  118. package/dist/services/memory-portability-service.js +340 -0
  119. package/dist/services/memory-scope.d.ts +16 -0
  120. package/dist/services/memory-scope.js +43 -0
  121. package/dist/services/migration-service.d.ts +42 -0
  122. package/dist/services/migration-service.js +307 -0
  123. package/dist/services/onnxruntime-resolve.d.ts +24 -0
  124. package/dist/services/onnxruntime-resolve.js +174 -0
  125. package/dist/services/privacy.d.ts +26 -0
  126. package/dist/services/privacy.js +69 -0
  127. package/dist/services/request-timeouts.d.ts +9 -0
  128. package/dist/services/request-timeouts.js +11 -0
  129. package/dist/services/runtime-require.d.ts +17 -0
  130. package/dist/services/runtime-require.js +75 -0
  131. package/dist/services/secret-resolver.d.ts +1 -0
  132. package/dist/services/secret-resolver.js +65 -0
  133. package/dist/services/shard-inventory-service.d.ts +69 -0
  134. package/dist/services/shard-inventory-service.js +315 -0
  135. package/dist/services/shard-path-migration-service.d.ts +31 -0
  136. package/dist/services/shard-path-migration-service.js +367 -0
  137. package/dist/services/tag-prefix-migration.d.ts +60 -0
  138. package/dist/services/tag-prefix-migration.js +439 -0
  139. package/dist/services/tag-prefix-scan.d.ts +26 -0
  140. package/dist/services/tag-prefix-scan.js +91 -0
  141. package/dist/services/tags.d.ts +29 -0
  142. package/dist/services/tags.js +251 -0
  143. package/dist/services/turso/connection-manager.d.ts +15 -0
  144. package/dist/services/turso/connection-manager.js +163 -0
  145. package/dist/services/turso/cross-process-write-lock.d.ts +1 -0
  146. package/dist/services/turso/cross-process-write-lock.js +89 -0
  147. package/dist/services/turso/legacy-migrator.d.ts +1 -0
  148. package/dist/services/turso/legacy-migrator.js +540 -0
  149. package/dist/services/turso/lifecycle.d.ts +1 -0
  150. package/dist/services/turso/lifecycle.js +14 -0
  151. package/dist/services/turso/operation-lock.d.ts +2 -0
  152. package/dist/services/turso/operation-lock.js +61 -0
  153. package/dist/services/turso/ready.d.ts +2 -0
  154. package/dist/services/turso/ready.js +36 -0
  155. package/dist/services/turso/shard-manager.d.ts +38 -0
  156. package/dist/services/turso/shard-manager.js +496 -0
  157. package/dist/services/turso/sqlite-handle-release.d.ts +16 -0
  158. package/dist/services/turso/sqlite-handle-release.js +56 -0
  159. package/dist/services/turso/turso-db.d.ts +18 -0
  160. package/dist/services/turso/turso-db.js +53 -0
  161. package/dist/services/turso/types.d.ts +42 -0
  162. package/dist/services/turso/types.js +1 -0
  163. package/dist/services/turso/vector-search.d.ts +41 -0
  164. package/dist/services/turso/vector-search.js +384 -0
  165. package/dist/services/turso/vector-utils.d.ts +7 -0
  166. package/dist/services/turso/vector-utils.js +67 -0
  167. package/dist/services/user-memory-learning.d.ts +97 -0
  168. package/dist/services/user-memory-learning.js +673 -0
  169. package/dist/services/user-profile/ai-cleanup.d.ts +58 -0
  170. package/dist/services/user-profile/ai-cleanup.js +486 -0
  171. package/dist/services/user-profile/profile-context.d.ts +1 -0
  172. package/dist/services/user-profile/profile-context.js +105 -0
  173. package/dist/services/user-profile/profile-utils.d.ts +2 -0
  174. package/dist/services/user-profile/profile-utils.js +45 -0
  175. package/dist/services/user-profile/types.d.ts +92 -0
  176. package/dist/services/user-profile/types.js +1 -0
  177. package/dist/services/user-profile/user-profile-manager.d.ts +65 -0
  178. package/dist/services/user-profile/user-profile-manager.js +1660 -0
  179. package/dist/services/user-prompt/user-prompt-manager.d.ts +53 -0
  180. package/dist/services/user-prompt/user-prompt-manager.js +303 -0
  181. package/dist/services/web-api-auth.d.ts +3 -0
  182. package/dist/services/web-api-auth.js +25 -0
  183. package/dist/services/web-auth.d.ts +46 -0
  184. package/dist/services/web-auth.js +105 -0
  185. package/dist/services/web-server.d.ts +57 -0
  186. package/dist/services/web-server.js +659 -0
  187. package/dist/shared/api/index.d.ts +4 -0
  188. package/dist/shared/api/index.js +2 -0
  189. package/dist/shared/api/portability-schemas.d.ts +56 -0
  190. package/dist/shared/api/portability-schemas.js +39 -0
  191. package/dist/shared/api/schemas.d.ts +295 -0
  192. package/dist/shared/api/schemas.js +134 -0
  193. package/dist/shared/api/types.d.ts +28 -0
  194. package/dist/shared/api/types.js +1 -0
  195. package/dist/types/index.d.ts +27 -0
  196. package/dist/types/index.js +1 -0
  197. package/dist/utils/context-limit.d.ts +10 -0
  198. package/dist/utils/context-limit.js +43 -0
  199. package/dist/utils/math.d.ts +7 -0
  200. package/dist/utils/math.js +48 -0
  201. package/dist/utils/profile.d.ts +2 -0
  202. package/dist/utils/profile.js +15 -0
  203. package/dist/v2/adapter.d.ts +5 -0
  204. package/dist/v2/adapter.js +218 -0
  205. package/dist/v2/legacy-client.d.ts +15 -0
  206. package/dist/v2/legacy-client.js +257 -0
  207. package/dist/v2/memory-bridge.d.ts +18 -0
  208. package/dist/v2/memory-bridge.js +29 -0
  209. package/dist/v2/plugin.d.ts +3 -0
  210. package/dist/v2/plugin.js +24 -0
  211. package/dist/web/assets/index-DPjEpD1h.css +2 -0
  212. package/dist/web/assets/index-do26c4JJ.js +122 -0
  213. package/dist/web/assets/jetbrains-mono-cyrillic-wght-normal-D73BlboJ.woff2 +0 -0
  214. package/dist/web/assets/jetbrains-mono-greek-wght-normal-Bw9x6K1M.woff2 +0 -0
  215. package/dist/web/assets/jetbrains-mono-latin-ext-wght-normal-DBQx-q_a.woff2 +0 -0
  216. package/dist/web/assets/jetbrains-mono-latin-wght-normal-B9CIFXIH.woff2 +0 -0
  217. package/dist/web/assets/jetbrains-mono-vietnamese-wght-normal-Bt-aOZkq.woff2 +0 -0
  218. package/dist/web/favicon.ico +0 -0
  219. package/dist/web/index.html +14 -0
  220. package/dist/web/omms-icon.png +0 -0
  221. package/package.json +138 -0
@@ -0,0 +1,42 @@
1
+ export interface ShardInfo {
2
+ id: number;
3
+ scope: "user" | "project";
4
+ scopeHash: string;
5
+ shardIndex: number;
6
+ dbPath: string;
7
+ vectorCount: number;
8
+ isActive: boolean;
9
+ createdAt: number;
10
+ }
11
+ export interface MemoryRecord {
12
+ id: string;
13
+ content: string;
14
+ vector: Float32Array;
15
+ tagsVector?: Float32Array;
16
+ containerTag: string;
17
+ tags?: string;
18
+ type?: string;
19
+ createdAt: number;
20
+ updatedAt: number;
21
+ metadata?: string;
22
+ displayName?: string;
23
+ userName?: string;
24
+ userEmail?: string;
25
+ projectPath?: string;
26
+ projectName?: string;
27
+ gitRepoUrl?: string;
28
+ }
29
+ export interface SearchResult {
30
+ id: string;
31
+ memory: string;
32
+ similarity: number;
33
+ createdAt?: number;
34
+ tags?: string[];
35
+ metadata?: Record<string, unknown>;
36
+ displayName?: string;
37
+ userName?: string;
38
+ userEmail?: string;
39
+ projectPath?: string;
40
+ projectName?: string;
41
+ gitRepoUrl?: string;
42
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,41 @@
1
+ import type { MemoryRecord, SearchResult, ShardInfo } from "./types.js";
2
+ import type { TursoDb } from "./turso-db.js";
3
+ import type { Transaction } from "@libsql/client";
4
+ export declare class TursoVectorSearch {
5
+ insertVectorInTransaction(tx: Transaction, record: MemoryRecord): Promise<void>;
6
+ insertVector(db: TursoDb, record: MemoryRecord): Promise<void>;
7
+ searchInShard(shard: ShardInfo, queryVector: Float32Array, containerTag: string, limit: number, queryText?: string): Promise<SearchResult[]>;
8
+ private searchKind;
9
+ private exactScanKind;
10
+ searchAcrossShards(shards: ShardInfo[], queryVector: Float32Array, containerTag: string, limit: number, similarityThreshold: number, queryText?: string): Promise<{
11
+ results: SearchResult[];
12
+ warnings: string[];
13
+ }>;
14
+ deleteVector(db: TursoDb, memoryId: string): Promise<void>;
15
+ updateVector(db: TursoDb, memoryId: string, vector: Float32Array, tagsVector?: Float32Array): Promise<void>;
16
+ listMemories(db: TursoDb, containerTag: string, limit: number): Promise<Record<string, unknown>[]>;
17
+ getAllMemories(db: TursoDb): Promise<Record<string, unknown>[]>;
18
+ getAllMemoriesWithExtractedVectors(db: TursoDb): Promise<Array<Record<string, unknown> & {
19
+ vector_json: string | null;
20
+ }>>;
21
+ getMemoryById(db: TursoDb, memoryId: string): Promise<Record<string, unknown> | null>;
22
+ getMemoriesBySessionID(db: TursoDb, sessionID: string): Promise<Record<string, unknown>[]>;
23
+ countVectors(db: TursoDb, containerTag: string): Promise<number>;
24
+ countAllVectors(db: TursoDb): Promise<number>;
25
+ getDistinctTags(db: TursoDb): Promise<Record<string, unknown>[]>;
26
+ getProjectPathCounts(db: TursoDb): Promise<Array<{
27
+ projectPath: string;
28
+ count: number;
29
+ containerTag: string | null;
30
+ }>>;
31
+ updateProjectAssociation(db: TursoDb, oldContainerTag: string, update: {
32
+ containerTag: string;
33
+ projectPath?: string;
34
+ projectName?: string;
35
+ displayName?: string;
36
+ gitRepoUrl?: string | null;
37
+ }): Promise<number>;
38
+ pinMemory(db: TursoDb, memoryId: string): Promise<void>;
39
+ unpinMemory(db: TursoDb, memoryId: string): Promise<void>;
40
+ }
41
+ export declare const tursoVectorSearch: TursoVectorSearch;
@@ -0,0 +1,384 @@
1
+ import { tursoConnectionManager } from "./connection-manager.js";
2
+ import { log } from "../logger.js";
3
+ import { distanceToSimilarity, vectorToJson } from "./vector-utils.js";
4
+ function parseMetadata(value) {
5
+ if (!value || typeof value !== "string")
6
+ return undefined;
7
+ try {
8
+ return JSON.parse(value);
9
+ }
10
+ catch {
11
+ return undefined;
12
+ }
13
+ }
14
+ export class TursoVectorSearch {
15
+ async insertVectorInTransaction(tx, record) {
16
+ const contentVector = vectorToJson(record.vector);
17
+ const commonArgs = [
18
+ record.containerTag,
19
+ record.tags || null,
20
+ record.type || null,
21
+ record.createdAt,
22
+ record.updatedAt,
23
+ record.metadata || null,
24
+ record.displayName || null,
25
+ record.userName || null,
26
+ record.userEmail || null,
27
+ record.projectPath || null,
28
+ record.projectName || null,
29
+ record.gitRepoUrl || null,
30
+ ];
31
+ if (record.tagsVector) {
32
+ await tx.execute({
33
+ sql: `
34
+ INSERT INTO memories (
35
+ id, content, vector, tags_vector, container_tag, tags, type, created_at, updated_at,
36
+ metadata, display_name, user_name, user_email, project_path, project_name, git_repo_url
37
+ ) VALUES (?, ?, vector32(?), vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
38
+ `,
39
+ args: [
40
+ record.id,
41
+ record.content,
42
+ contentVector,
43
+ vectorToJson(record.tagsVector),
44
+ ...commonArgs,
45
+ ],
46
+ });
47
+ return;
48
+ }
49
+ await tx.execute({
50
+ sql: `
51
+ INSERT INTO memories (
52
+ id, content, vector, tags_vector, container_tag, tags, type, created_at, updated_at,
53
+ metadata, display_name, user_name, user_email, project_path, project_name, git_repo_url
54
+ ) VALUES (?, ?, vector32(?), NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
55
+ `,
56
+ args: [record.id, record.content, contentVector, ...commonArgs],
57
+ });
58
+ }
59
+ async insertVector(db, record) {
60
+ const contentVector = vectorToJson(record.vector);
61
+ const commonArgs = [
62
+ record.containerTag,
63
+ record.tags || null,
64
+ record.type || null,
65
+ record.createdAt,
66
+ record.updatedAt,
67
+ record.metadata || null,
68
+ record.displayName || null,
69
+ record.userName || null,
70
+ record.userEmail || null,
71
+ record.projectPath || null,
72
+ record.projectName || null,
73
+ record.gitRepoUrl || null,
74
+ ];
75
+ if (record.tagsVector) {
76
+ await db.execute(`
77
+ INSERT INTO memories (
78
+ id, content, vector, tags_vector, container_tag, tags, type, created_at, updated_at,
79
+ metadata, display_name, user_name, user_email, project_path, project_name, git_repo_url
80
+ ) VALUES (?, ?, vector32(?), vector32(?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
81
+ `, [record.id, record.content, contentVector, vectorToJson(record.tagsVector), ...commonArgs]);
82
+ return;
83
+ }
84
+ await db.execute(`
85
+ INSERT INTO memories (
86
+ id, content, vector, tags_vector, container_tag, tags, type, created_at, updated_at,
87
+ metadata, display_name, user_name, user_email, project_path, project_name, git_repo_url
88
+ ) VALUES (?, ?, vector32(?), NULL, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
89
+ `, [record.id, record.content, contentVector, ...commonArgs]);
90
+ }
91
+ async searchInShard(shard, queryVector, containerTag, limit, queryText) {
92
+ const db = await tursoConnectionManager.getConnection(shard.dbPath);
93
+ const queryJson = vectorToJson(queryVector);
94
+ // Over-fetch aggressively when filtering by container_tag after ANN,
95
+ // because post-filtering can discard many DiskANN neighbors.
96
+ const k = containerTag === "" ? Math.max(limit * 4, 32) : Math.max(limit * 16, 128);
97
+ const contentResults = await this.searchKind(db, queryJson, k, containerTag, "memories_vec_idx", "vector");
98
+ const tagsResults = await this.searchKind(db, queryJson, k, containerTag, "memories_tags_vec_idx", "tags_vector");
99
+ const candidateIds = new Set();
100
+ for (const result of contentResults)
101
+ candidateIds.add(result.id);
102
+ for (const result of tagsResults)
103
+ candidateIds.add(result.id);
104
+ const ids = Array.from(candidateIds);
105
+ if (ids.length === 0)
106
+ return [];
107
+ const placeholders = ids.map(() => "?").join(",");
108
+ // Recompute exact distances for hydrated rows so tag-only / content-only ANN
109
+ // hits still get a full hybrid score (missing ANN side is not forced to 0).
110
+ const rows = await db.all(containerTag === ""
111
+ ? `
112
+ SELECT id, content, tags, created_at, metadata, container_tag,
113
+ display_name, user_name, user_email, project_path, project_name,
114
+ git_repo_url, is_pinned,
115
+ vector_distance_cos(vector, vector32(?)) AS content_dist,
116
+ CASE WHEN tags_vector IS NOT NULL
117
+ THEN vector_distance_cos(tags_vector, vector32(?))
118
+ ELSE NULL END AS tags_dist
119
+ FROM memories
120
+ WHERE id IN (${placeholders})
121
+ `
122
+ : `
123
+ SELECT id, content, tags, created_at, metadata, container_tag,
124
+ display_name, user_name, user_email, project_path, project_name,
125
+ git_repo_url, is_pinned,
126
+ vector_distance_cos(vector, vector32(?)) AS content_dist,
127
+ CASE WHEN tags_vector IS NOT NULL
128
+ THEN vector_distance_cos(tags_vector, vector32(?))
129
+ ELSE NULL END AS tags_dist
130
+ FROM memories
131
+ WHERE id IN (${placeholders}) AND container_tag = ?
132
+ `, containerTag === ""
133
+ ? [queryJson, queryJson, ...ids]
134
+ : [queryJson, queryJson, ...ids, containerTag]);
135
+ const queryWords = queryText
136
+ ? queryText
137
+ .toLowerCase()
138
+ .split(/[\s,]+/)
139
+ .filter((word) => word.length > 1)
140
+ : [];
141
+ const hydratedResults = rows.map((row) => {
142
+ const contentSim = distanceToSimilarity(Number(row.content_dist));
143
+ const tagsSim = row.tags_dist == null || row.tags_dist === undefined
144
+ ? 0
145
+ : distanceToSimilarity(Number(row.tags_dist));
146
+ const memoryTagsStr = String(row.tags || "");
147
+ const memoryTags = memoryTagsStr.split(",").map((tag) => tag.trim().toLowerCase());
148
+ let exactMatchBoost = 0;
149
+ if (queryWords.length > 0 && memoryTags.length > 0) {
150
+ const matches = queryWords.filter((word) => memoryTags.some((tag) => tag.includes(word) || word.includes(tag))).length;
151
+ exactMatchBoost = matches / Math.max(queryWords.length, 1);
152
+ }
153
+ const finalTagsSim = Math.max(tagsSim, exactMatchBoost);
154
+ const similarity = contentSim * 0.6 + finalTagsSim * 0.4;
155
+ return {
156
+ id: String(row.id),
157
+ memory: String(row.content),
158
+ similarity,
159
+ createdAt: Number(row.created_at),
160
+ tags: memoryTagsStr ? memoryTagsStr.split(",") : [],
161
+ metadata: parseMetadata(row.metadata),
162
+ containerTag: String(row.container_tag),
163
+ displayName: row.display_name ? String(row.display_name) : undefined,
164
+ userName: row.user_name ? String(row.user_name) : undefined,
165
+ userEmail: row.user_email ? String(row.user_email) : undefined,
166
+ projectPath: row.project_path ? String(row.project_path) : undefined,
167
+ projectName: row.project_name ? String(row.project_name) : undefined,
168
+ gitRepoUrl: row.git_repo_url ? String(row.git_repo_url) : undefined,
169
+ isPinned: row.is_pinned,
170
+ };
171
+ });
172
+ hydratedResults.sort((a, b) => b.similarity - a.similarity);
173
+ return hydratedResults.slice(0, Math.max(0, limit));
174
+ }
175
+ async searchKind(db, queryJson, k, containerTag, indexName, columnName) {
176
+ try {
177
+ const rows = await db.all(containerTag === ""
178
+ ? `
179
+ SELECT m.id AS id, vector_distance_cos(m.${columnName}, vector32(?)) AS dist
180
+ FROM vector_top_k('${indexName}', vector32(?), ?) AS v
181
+ CROSS JOIN memories m ON m.rowid = v.id
182
+ WHERE m.${columnName} IS NOT NULL
183
+ `
184
+ : `
185
+ SELECT m.id AS id, vector_distance_cos(m.${columnName}, vector32(?)) AS dist
186
+ FROM vector_top_k('${indexName}', vector32(?), ?) AS v
187
+ CROSS JOIN memories m ON m.rowid = v.id
188
+ WHERE m.${columnName} IS NOT NULL AND m.container_tag = ?
189
+ `, containerTag === "" ? [queryJson, queryJson, k] : [queryJson, queryJson, k, containerTag]);
190
+ return rows.map((row) => ({
191
+ id: String(row.id),
192
+ similarity: distanceToSimilarity(Number(row.dist)),
193
+ }));
194
+ }
195
+ catch (error) {
196
+ log("Turso vector_top_k failed; falling back to exact scan", {
197
+ indexName,
198
+ error: String(error),
199
+ });
200
+ return this.exactScanKind(db, queryJson, k, containerTag, columnName);
201
+ }
202
+ }
203
+ async exactScanKind(db, queryJson, k, containerTag, columnName) {
204
+ const rows = await db.all(containerTag === ""
205
+ ? `
206
+ SELECT m.id AS id, vector_distance_cos(m.${columnName}, vector32(?)) AS dist
207
+ FROM memories m
208
+ WHERE m.${columnName} IS NOT NULL
209
+ ORDER BY dist ASC
210
+ LIMIT ?
211
+ `
212
+ : `
213
+ SELECT m.id AS id, vector_distance_cos(m.${columnName}, vector32(?)) AS dist
214
+ FROM memories m
215
+ WHERE m.${columnName} IS NOT NULL AND m.container_tag = ?
216
+ ORDER BY dist ASC
217
+ LIMIT ?
218
+ `, containerTag === "" ? [queryJson, k] : [queryJson, containerTag, k]);
219
+ return rows.map((row) => ({
220
+ id: String(row.id),
221
+ similarity: distanceToSimilarity(Number(row.dist)),
222
+ }));
223
+ }
224
+ async searchAcrossShards(shards, queryVector, containerTag, limit, similarityThreshold, queryText) {
225
+ const shardErrors = [];
226
+ const shardPromises = shards.map(async (shard) => {
227
+ try {
228
+ return await this.searchInShard(shard, queryVector, containerTag, limit, queryText);
229
+ }
230
+ catch (error) {
231
+ const message = error instanceof Error ? error.message : String(error);
232
+ log("Shard search error", { shardId: shard.id, error: message });
233
+ shardErrors.push({ shardId: shard.id, error: message });
234
+ return [];
235
+ }
236
+ });
237
+ const resultsArray = await Promise.all(shardPromises);
238
+ if (shardErrors.length > 0 && shardErrors.length === shards.length) {
239
+ throw new Error(`Vector search failed on all shards: ${shardErrors.map((entry) => `${entry.shardId}: ${entry.error}`).join("; ")}`);
240
+ }
241
+ const warnings = shardErrors.length > 0
242
+ ? [
243
+ `Vector search completed with partial shard failures (${shardErrors.length}/${shards.length}): ${shardErrors
244
+ .map((entry) => `${entry.shardId}: ${entry.error}`)
245
+ .join("; ")}`,
246
+ ]
247
+ : [];
248
+ if (warnings.length > 0) {
249
+ log("Vector search completed with partial shard failures", {
250
+ failedShards: shardErrors,
251
+ totalShards: shards.length,
252
+ });
253
+ }
254
+ const allResults = resultsArray.flat();
255
+ allResults.sort((a, b) => b.similarity - a.similarity);
256
+ return {
257
+ results: allResults
258
+ .filter((result) => result.similarity >= similarityThreshold)
259
+ .slice(0, limit),
260
+ warnings,
261
+ };
262
+ }
263
+ async deleteVector(db, memoryId) {
264
+ await db.run(`DELETE FROM memories WHERE id = ?`, [memoryId]);
265
+ }
266
+ async updateVector(db, memoryId, vector, tagsVector) {
267
+ const contentVector = vectorToJson(vector);
268
+ if (tagsVector) {
269
+ await db.execute(`UPDATE memories SET vector = vector32(?), tags_vector = vector32(?) WHERE id = ?`, [contentVector, vectorToJson(tagsVector), memoryId]);
270
+ }
271
+ else {
272
+ await db.execute(`UPDATE memories SET vector = vector32(?), tags_vector = NULL WHERE id = ?`, [contentVector, memoryId]);
273
+ }
274
+ }
275
+ async listMemories(db, containerTag, limit) {
276
+ return containerTag === ""
277
+ ? db.all(`
278
+ SELECT * FROM memories
279
+ ORDER BY created_at DESC
280
+ LIMIT ?
281
+ `, [limit])
282
+ : db.all(`
283
+ SELECT * FROM memories
284
+ WHERE container_tag = ?
285
+ ORDER BY created_at DESC
286
+ LIMIT ?
287
+ `, [containerTag, limit]);
288
+ }
289
+ async getAllMemories(db) {
290
+ return db.all(`SELECT * FROM memories ORDER BY created_at DESC`);
291
+ }
292
+ async getAllMemoriesWithExtractedVectors(db) {
293
+ return db.all(`
294
+ SELECT
295
+ id,
296
+ content,
297
+ container_tag,
298
+ created_at,
299
+ vector_extract(vector) AS vector_json
300
+ FROM memories
301
+ ORDER BY created_at DESC
302
+ `);
303
+ }
304
+ async getMemoryById(db, memoryId) {
305
+ return db.get(`SELECT * FROM memories WHERE id = ?`, [memoryId]);
306
+ }
307
+ async getMemoriesBySessionID(db, sessionID) {
308
+ const rows = await db.all(`
309
+ SELECT * FROM memories
310
+ WHERE metadata LIKE ?
311
+ ORDER BY created_at DESC
312
+ `, [`%"sessionID":"${sessionID}"%`]);
313
+ return rows.map((row) => ({
314
+ ...row,
315
+ tags: row.tags ? String(row.tags).split(",") : [],
316
+ metadata: row.metadata ? (parseMetadata(String(row.metadata)) ?? {}) : {},
317
+ }));
318
+ }
319
+ async countVectors(db, containerTag) {
320
+ const row = await db.get(`SELECT COUNT(*) as count FROM memories WHERE container_tag = ?`, [
321
+ containerTag,
322
+ ]);
323
+ return Number(row?.count ?? 0);
324
+ }
325
+ async countAllVectors(db) {
326
+ const row = await db.get(`SELECT COUNT(*) as count FROM memories`);
327
+ return Number(row?.count ?? 0);
328
+ }
329
+ async getDistinctTags(db) {
330
+ return db.all(`
331
+ SELECT DISTINCT
332
+ container_tag,
333
+ display_name,
334
+ user_name,
335
+ user_email,
336
+ project_path,
337
+ project_name,
338
+ git_repo_url
339
+ FROM memories
340
+ `);
341
+ }
342
+ async getProjectPathCounts(db) {
343
+ const rows = await db.all(`
344
+ SELECT
345
+ project_path AS project_path,
346
+ container_tag AS container_tag,
347
+ COUNT(*) AS cnt
348
+ FROM memories
349
+ WHERE project_path IS NOT NULL AND project_path != ''
350
+ GROUP BY project_path, container_tag
351
+ ORDER BY cnt DESC, project_path ASC
352
+ `);
353
+ return rows.map((row) => ({
354
+ projectPath: String(row.project_path),
355
+ count: Number(row.cnt ?? 0),
356
+ containerTag: row.container_tag ? String(row.container_tag) : null,
357
+ }));
358
+ }
359
+ async updateProjectAssociation(db, oldContainerTag, update) {
360
+ return db.run(`
361
+ UPDATE memories SET
362
+ container_tag = ?,
363
+ project_path = ?,
364
+ project_name = ?,
365
+ display_name = ?,
366
+ git_repo_url = ?
367
+ WHERE container_tag = ?
368
+ `, [
369
+ update.containerTag,
370
+ update.projectPath ?? null,
371
+ update.projectName ?? null,
372
+ update.displayName ?? null,
373
+ update.gitRepoUrl ?? null,
374
+ oldContainerTag,
375
+ ]);
376
+ }
377
+ async pinMemory(db, memoryId) {
378
+ await db.run(`UPDATE memories SET is_pinned = 1 WHERE id = ?`, [memoryId]);
379
+ }
380
+ async unpinMemory(db, memoryId) {
381
+ await db.run(`UPDATE memories SET is_pinned = 0 WHERE id = ?`, [memoryId]);
382
+ }
383
+ }
384
+ export const tursoVectorSearch = new TursoVectorSearch();
@@ -0,0 +1,7 @@
1
+ export declare function vectorToJson(vector: Float32Array): string;
2
+ export declare function blobToFloat32Array(value: unknown): Float32Array | null;
3
+ /** Parse libSQL `vector_extract()` JSON output (preferred over raw F32_BLOB bytes). */
4
+ export declare function parseExtractedVector(value: unknown): Float32Array | null;
5
+ export declare function distanceToSimilarity(distance: number): number;
6
+ /** Canonical text used for tag-vector embeddings (must stay consistent across write paths). */
7
+ export declare function formatTagsForEmbedding(tags: string[]): string;
@@ -0,0 +1,67 @@
1
+ export function vectorToJson(vector) {
2
+ return JSON.stringify(Array.from(vector));
3
+ }
4
+ export function blobToFloat32Array(value) {
5
+ if (value == null)
6
+ return null;
7
+ try {
8
+ if (value instanceof Float32Array) {
9
+ return value;
10
+ }
11
+ if (value instanceof ArrayBuffer) {
12
+ if (value.byteLength === 0 || value.byteLength % 4 !== 0) {
13
+ return null;
14
+ }
15
+ return new Float32Array(value);
16
+ }
17
+ if (ArrayBuffer.isView(value)) {
18
+ const view = value;
19
+ const byteLength = view.byteLength;
20
+ if (byteLength === 0 || byteLength % 4 !== 0) {
21
+ return null;
22
+ }
23
+ return new Float32Array(view.buffer.slice(view.byteOffset, view.byteOffset + view.byteLength));
24
+ }
25
+ if (typeof value === "string") {
26
+ try {
27
+ const parsed = JSON.parse(value);
28
+ return new Float32Array(parsed);
29
+ }
30
+ catch {
31
+ return null;
32
+ }
33
+ }
34
+ }
35
+ catch {
36
+ return null;
37
+ }
38
+ return null;
39
+ }
40
+ /** Parse libSQL `vector_extract()` JSON output (preferred over raw F32_BLOB bytes). */
41
+ export function parseExtractedVector(value) {
42
+ if (value == null)
43
+ return null;
44
+ if (typeof value === "string") {
45
+ try {
46
+ const parsed = JSON.parse(value);
47
+ if (!Array.isArray(parsed) || parsed.length === 0)
48
+ return null;
49
+ return new Float32Array(parsed);
50
+ }
51
+ catch {
52
+ return null;
53
+ }
54
+ }
55
+ return blobToFloat32Array(value);
56
+ }
57
+ export function distanceToSimilarity(distance) {
58
+ const similarity = 1 - Number(distance);
59
+ if (!Number.isFinite(similarity))
60
+ return 0;
61
+ // Clamp float artifacts (docs: cos-distance can be slightly negative near exact matches).
62
+ return Math.max(0, Math.min(1, similarity));
63
+ }
64
+ /** Canonical text used for tag-vector embeddings (must stay consistent across write paths). */
65
+ export function formatTagsForEmbedding(tags) {
66
+ return `Topics: ${tags.join(", ")}`;
67
+ }
@@ -0,0 +1,97 @@
1
+ import type { PluginInput } from "@opencode-ai/plugin";
2
+ export declare function shouldRunAutomaticProfileCleanup(previousPromptCount: number, addedPromptCount: number, interval?: number): boolean;
3
+ export declare function performUserProfileLearning(ctx: PluginInput, directory: string): Promise<void>;
4
+ import { createUserProfileAnalysisSchema, USER_PROFILE_LLM_CONFIDENCE_MAX } from "../core/extraction.js";
5
+ export { createUserProfileAnalysisSchema, USER_PROFILE_LLM_CONFIDENCE_MAX };
6
+ export declare function createUserProfileToolSchema(existingProfile: boolean): {
7
+ type: "function";
8
+ function: {
9
+ name: string;
10
+ description: string;
11
+ parameters: {
12
+ type: string;
13
+ properties: {
14
+ preferences: {
15
+ type: string;
16
+ items: {
17
+ type: string;
18
+ properties: {
19
+ category: {
20
+ type: string;
21
+ };
22
+ description: {
23
+ type: string;
24
+ };
25
+ confidence: {
26
+ type: string;
27
+ minimum: number;
28
+ maximum: number;
29
+ };
30
+ evidence: {
31
+ type: string;
32
+ items: {
33
+ type: string;
34
+ };
35
+ maxItems: number;
36
+ };
37
+ };
38
+ required: string[];
39
+ };
40
+ };
41
+ patterns: {
42
+ type: string;
43
+ items: {
44
+ type: string;
45
+ properties: {
46
+ category: {
47
+ type: string;
48
+ };
49
+ description: {
50
+ type: string;
51
+ };
52
+ };
53
+ required: string[];
54
+ };
55
+ };
56
+ workflows: {
57
+ type: string;
58
+ items: {
59
+ type: string;
60
+ properties: {
61
+ description: {
62
+ type: string;
63
+ };
64
+ steps: {
65
+ type: string;
66
+ items: {
67
+ type: string;
68
+ };
69
+ };
70
+ };
71
+ required: string[];
72
+ };
73
+ };
74
+ validations: {
75
+ type: string;
76
+ items: {
77
+ type: string;
78
+ properties: {
79
+ index: {
80
+ type: string;
81
+ };
82
+ verdict: {
83
+ type: string;
84
+ enum: string[];
85
+ };
86
+ reason: {
87
+ type: string;
88
+ };
89
+ };
90
+ required: string[];
91
+ };
92
+ };
93
+ };
94
+ required: string[];
95
+ };
96
+ };
97
+ };