nova-terminal-assistant 0.1.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.

Potentially problematic release.


This version of nova-terminal-assistant might be problematic. Click here for more details.

Files changed (192) hide show
  1. package/README.md +358 -0
  2. package/bin/nova +38 -0
  3. package/bin/nova.js +12 -0
  4. package/package.json +67 -0
  5. package/src/cli/commands/SmartCompletion.ts +458 -0
  6. package/src/cli/index.ts +5 -0
  7. package/src/cli/startup/IFlowRepl.ts +212 -0
  8. package/src/cli/startup/InkBasedRepl.ts +1056 -0
  9. package/src/cli/startup/InteractiveRepl.ts +2833 -0
  10. package/src/cli/startup/NovaApp.ts +1861 -0
  11. package/src/cli/startup/index.ts +4 -0
  12. package/src/cli/startup/parseArgs.ts +293 -0
  13. package/src/cli/test-modules.ts +27 -0
  14. package/src/cli/ui/IFlowDropdown.ts +425 -0
  15. package/src/cli/ui/ModernReplUI.ts +276 -0
  16. package/src/cli/ui/SimpleSelector2.ts +215 -0
  17. package/src/cli/ui/components/ConfirmDialog.ts +176 -0
  18. package/src/cli/ui/components/ErrorPanel.ts +364 -0
  19. package/src/cli/ui/components/InkAppRunner.tsx +67 -0
  20. package/src/cli/ui/components/InkComponents.tsx +613 -0
  21. package/src/cli/ui/components/NovaInkApp.tsx +312 -0
  22. package/src/cli/ui/components/ProgressBar.ts +177 -0
  23. package/src/cli/ui/components/ProgressIndicator.ts +298 -0
  24. package/src/cli/ui/components/QuickActions.ts +396 -0
  25. package/src/cli/ui/components/SimpleErrorPanel.ts +231 -0
  26. package/src/cli/ui/components/StatusBar.ts +194 -0
  27. package/src/cli/ui/components/ThinkingBlockRenderer.ts +401 -0
  28. package/src/cli/ui/components/index.ts +27 -0
  29. package/src/cli/ui/ink-prototype.tsx +347 -0
  30. package/src/cli/utils/CliUI.ts +336 -0
  31. package/src/cli/utils/CompletionHelper.ts +388 -0
  32. package/src/cli/utils/EnhancedCompleter.test.ts +226 -0
  33. package/src/cli/utils/EnhancedCompleter.ts +513 -0
  34. package/src/cli/utils/ErrorEnhancer.ts +429 -0
  35. package/src/cli/utils/OutputFormatter.ts +193 -0
  36. package/src/cli/utils/index.ts +9 -0
  37. package/src/core/agents/AgentOrchestrator.ts +515 -0
  38. package/src/core/agents/index.ts +17 -0
  39. package/src/core/audit/AuditLogger.ts +509 -0
  40. package/src/core/audit/index.ts +11 -0
  41. package/src/core/auth/AuthManager.d.ts.map +1 -0
  42. package/src/core/auth/AuthManager.ts +138 -0
  43. package/src/core/auth/index.d.ts.map +1 -0
  44. package/src/core/auth/index.ts +2 -0
  45. package/src/core/config/ConfigManager.d.ts.map +1 -0
  46. package/src/core/config/ConfigManager.test.ts +183 -0
  47. package/src/core/config/ConfigManager.ts +1219 -0
  48. package/src/core/config/index.d.ts.map +1 -0
  49. package/src/core/config/index.ts +1 -0
  50. package/src/core/context/ContextBuilder.d.ts.map +1 -0
  51. package/src/core/context/ContextBuilder.ts +171 -0
  52. package/src/core/context/ContextCompressor.d.ts.map +1 -0
  53. package/src/core/context/ContextCompressor.ts +642 -0
  54. package/src/core/context/LayeredMemoryManager.ts +657 -0
  55. package/src/core/context/MemoryDiscovery.d.ts.map +1 -0
  56. package/src/core/context/MemoryDiscovery.ts +175 -0
  57. package/src/core/context/defaultSystemPrompt.d.ts.map +1 -0
  58. package/src/core/context/defaultSystemPrompt.ts +35 -0
  59. package/src/core/context/index.d.ts.map +1 -0
  60. package/src/core/context/index.ts +22 -0
  61. package/src/core/extensions/SkillGenerator.ts +421 -0
  62. package/src/core/extensions/SkillInstaller.d.ts.map +1 -0
  63. package/src/core/extensions/SkillInstaller.ts +257 -0
  64. package/src/core/extensions/SkillRegistry.d.ts.map +1 -0
  65. package/src/core/extensions/SkillRegistry.ts +361 -0
  66. package/src/core/extensions/SkillValidator.ts +525 -0
  67. package/src/core/extensions/index.ts +15 -0
  68. package/src/core/index.d.ts.map +1 -0
  69. package/src/core/index.ts +42 -0
  70. package/src/core/mcp/McpManager.d.ts.map +1 -0
  71. package/src/core/mcp/McpManager.ts +632 -0
  72. package/src/core/mcp/index.d.ts.map +1 -0
  73. package/src/core/mcp/index.ts +2 -0
  74. package/src/core/model/ModelClient.d.ts.map +1 -0
  75. package/src/core/model/ModelClient.ts +217 -0
  76. package/src/core/model/ModelConnectionTester.ts +363 -0
  77. package/src/core/model/ModelValidator.ts +348 -0
  78. package/src/core/model/index.d.ts.map +1 -0
  79. package/src/core/model/index.ts +6 -0
  80. package/src/core/model/providers/AnthropicProvider.d.ts.map +1 -0
  81. package/src/core/model/providers/AnthropicProvider.ts +279 -0
  82. package/src/core/model/providers/CodingPlanProvider.d.ts.map +1 -0
  83. package/src/core/model/providers/CodingPlanProvider.ts +210 -0
  84. package/src/core/model/providers/OllamaCloudProvider.d.ts.map +1 -0
  85. package/src/core/model/providers/OllamaCloudProvider.ts +405 -0
  86. package/src/core/model/providers/OllamaManager.d.ts.map +1 -0
  87. package/src/core/model/providers/OllamaManager.ts +201 -0
  88. package/src/core/model/providers/OllamaProvider.d.ts.map +1 -0
  89. package/src/core/model/providers/OllamaProvider.ts +73 -0
  90. package/src/core/model/providers/OpenAICompatibleProvider.d.ts.map +1 -0
  91. package/src/core/model/providers/OpenAICompatibleProvider.ts +327 -0
  92. package/src/core/model/providers/OpenAIProvider.d.ts.map +1 -0
  93. package/src/core/model/providers/OpenAIProvider.ts +29 -0
  94. package/src/core/model/providers/index.d.ts.map +1 -0
  95. package/src/core/model/providers/index.ts +12 -0
  96. package/src/core/model/types.d.ts.map +1 -0
  97. package/src/core/model/types.ts +77 -0
  98. package/src/core/security/ApprovalManager.d.ts.map +1 -0
  99. package/src/core/security/ApprovalManager.ts +174 -0
  100. package/src/core/security/FileFilter.d.ts.map +1 -0
  101. package/src/core/security/FileFilter.ts +141 -0
  102. package/src/core/security/HookExecutor.d.ts.map +1 -0
  103. package/src/core/security/HookExecutor.ts +178 -0
  104. package/src/core/security/SandboxExecutor.ts +447 -0
  105. package/src/core/security/index.d.ts.map +1 -0
  106. package/src/core/security/index.ts +8 -0
  107. package/src/core/session/AgentLoop.d.ts.map +1 -0
  108. package/src/core/session/AgentLoop.ts +501 -0
  109. package/src/core/session/SessionManager.d.ts.map +1 -0
  110. package/src/core/session/SessionManager.test.ts +183 -0
  111. package/src/core/session/SessionManager.ts +460 -0
  112. package/src/core/session/index.d.ts.map +1 -0
  113. package/src/core/session/index.ts +3 -0
  114. package/src/core/telemetry/Telemetry.d.ts.map +1 -0
  115. package/src/core/telemetry/Telemetry.ts +90 -0
  116. package/src/core/telemetry/TelemetryService.ts +531 -0
  117. package/src/core/telemetry/index.d.ts.map +1 -0
  118. package/src/core/telemetry/index.ts +12 -0
  119. package/src/core/testing/AutoFixer.ts +385 -0
  120. package/src/core/testing/ErrorAnalyzer.ts +499 -0
  121. package/src/core/testing/TestRunner.ts +265 -0
  122. package/src/core/testing/agent-cli-tests.ts +538 -0
  123. package/src/core/testing/index.ts +11 -0
  124. package/src/core/tools/ToolRegistry.d.ts.map +1 -0
  125. package/src/core/tools/ToolRegistry.test.ts +206 -0
  126. package/src/core/tools/ToolRegistry.ts +260 -0
  127. package/src/core/tools/impl/EditFileTool.d.ts.map +1 -0
  128. package/src/core/tools/impl/EditFileTool.ts +97 -0
  129. package/src/core/tools/impl/ListDirectoryTool.d.ts.map +1 -0
  130. package/src/core/tools/impl/ListDirectoryTool.ts +142 -0
  131. package/src/core/tools/impl/MemoryTool.d.ts.map +1 -0
  132. package/src/core/tools/impl/MemoryTool.ts +102 -0
  133. package/src/core/tools/impl/ReadFileTool.d.ts.map +1 -0
  134. package/src/core/tools/impl/ReadFileTool.ts +58 -0
  135. package/src/core/tools/impl/SearchContentTool.d.ts.map +1 -0
  136. package/src/core/tools/impl/SearchContentTool.ts +94 -0
  137. package/src/core/tools/impl/SearchFileTool.d.ts.map +1 -0
  138. package/src/core/tools/impl/SearchFileTool.ts +61 -0
  139. package/src/core/tools/impl/ShellTool.d.ts.map +1 -0
  140. package/src/core/tools/impl/ShellTool.ts +118 -0
  141. package/src/core/tools/impl/TaskTool.d.ts.map +1 -0
  142. package/src/core/tools/impl/TaskTool.ts +207 -0
  143. package/src/core/tools/impl/TodoTool.d.ts.map +1 -0
  144. package/src/core/tools/impl/TodoTool.ts +122 -0
  145. package/src/core/tools/impl/WebFetchTool.d.ts.map +1 -0
  146. package/src/core/tools/impl/WebFetchTool.ts +103 -0
  147. package/src/core/tools/impl/WebSearchTool.d.ts.map +1 -0
  148. package/src/core/tools/impl/WebSearchTool.ts +89 -0
  149. package/src/core/tools/impl/WriteFileTool.d.ts.map +1 -0
  150. package/src/core/tools/impl/WriteFileTool.ts +49 -0
  151. package/src/core/tools/impl/index.d.ts.map +1 -0
  152. package/src/core/tools/impl/index.ts +16 -0
  153. package/src/core/tools/index.d.ts.map +1 -0
  154. package/src/core/tools/index.ts +7 -0
  155. package/src/core/tools/schemas/execution.d.ts.map +1 -0
  156. package/src/core/tools/schemas/execution.ts +42 -0
  157. package/src/core/tools/schemas/file.d.ts.map +1 -0
  158. package/src/core/tools/schemas/file.ts +119 -0
  159. package/src/core/tools/schemas/index.d.ts.map +1 -0
  160. package/src/core/tools/schemas/index.ts +11 -0
  161. package/src/core/tools/schemas/memory.d.ts.map +1 -0
  162. package/src/core/tools/schemas/memory.ts +52 -0
  163. package/src/core/tools/schemas/orchestration.d.ts.map +1 -0
  164. package/src/core/tools/schemas/orchestration.ts +44 -0
  165. package/src/core/tools/schemas/search.d.ts.map +1 -0
  166. package/src/core/tools/schemas/search.ts +112 -0
  167. package/src/core/tools/schemas/todo.d.ts.map +1 -0
  168. package/src/core/tools/schemas/todo.ts +32 -0
  169. package/src/core/tools/schemas/web.d.ts.map +1 -0
  170. package/src/core/tools/schemas/web.ts +86 -0
  171. package/src/core/types/config.d.ts.map +1 -0
  172. package/src/core/types/config.ts +200 -0
  173. package/src/core/types/errors.d.ts.map +1 -0
  174. package/src/core/types/errors.ts +204 -0
  175. package/src/core/types/index.d.ts.map +1 -0
  176. package/src/core/types/index.ts +8 -0
  177. package/src/core/types/session.d.ts.map +1 -0
  178. package/src/core/types/session.ts +216 -0
  179. package/src/core/types/tools.d.ts.map +1 -0
  180. package/src/core/types/tools.ts +157 -0
  181. package/src/core/utils/CheckpointManager.d.ts.map +1 -0
  182. package/src/core/utils/CheckpointManager.ts +327 -0
  183. package/src/core/utils/Logger.d.ts.map +1 -0
  184. package/src/core/utils/Logger.ts +98 -0
  185. package/src/core/utils/RetryManager.ts +471 -0
  186. package/src/core/utils/TokenCounter.d.ts.map +1 -0
  187. package/src/core/utils/TokenCounter.ts +414 -0
  188. package/src/core/utils/VectorMemoryStore.ts +440 -0
  189. package/src/core/utils/helpers.d.ts.map +1 -0
  190. package/src/core/utils/helpers.ts +89 -0
  191. package/src/core/utils/index.d.ts.map +1 -0
  192. package/src/core/utils/index.ts +19 -0
@@ -0,0 +1,440 @@
1
+ // ============================================================================
2
+ // VectorMemoryStore - Vector-based semantic memory storage
3
+ // ============================================================================
4
+
5
+ import * as fs from 'node:fs';
6
+ import * as path from 'node:path';
7
+ import { createLogger } from './Logger.js';
8
+ import { generateId } from './helpers.js';
9
+
10
+ const logger = createLogger('VectorMemoryStore');
11
+
12
+ // ============================================================================
13
+ // Types
14
+ // ============================================================================
15
+
16
+ /**
17
+ * Memory entry with vector embedding
18
+ */
19
+ export interface VectorMemoryEntry {
20
+ id: string;
21
+ content: string;
22
+ embedding: number[];
23
+ metadata: {
24
+ session: string;
25
+ timestamp: Date;
26
+ type: 'decision' | 'code' | 'error' | 'insight' | 'context' | 'preference';
27
+ importance: number; // 0-1 score
28
+ tags: string[];
29
+ source?: string; // File or URL source
30
+ };
31
+ }
32
+
33
+ /**
34
+ * Search result with similarity score
35
+ */
36
+ export interface MemoryMatch {
37
+ entry: VectorMemoryEntry;
38
+ score: number; // Cosine similarity 0-1
39
+ }
40
+
41
+ /**
42
+ * Memory store configuration
43
+ */
44
+ export interface VectorMemoryConfig {
45
+ /** Storage directory for persistence */
46
+ storageDir: string;
47
+ /** Maximum entries to keep */
48
+ maxEntries?: number;
49
+ /** Minimum similarity score for search results */
50
+ minSimilarity?: number;
51
+ /** Embedding dimension (default 384 for small models) */
52
+ embeddingDimension?: number;
53
+ }
54
+
55
+ // ============================================================================
56
+ // Embedding Functions
57
+ // ============================================================================
58
+
59
+ /**
60
+ * Simple embedding function using character n-grams
61
+ * This is a lightweight alternative to neural embeddings
62
+ */
63
+ function simpleEmbedding(text: string, dimension: number = 384): number[] {
64
+ const embedding = new Array(dimension).fill(0);
65
+
66
+ // Normalize text
67
+ const normalized = text.toLowerCase().replace(/[^a-z0-9\u4e00-\u9fff\s]/g, ' ');
68
+ const words = normalized.split(/\s+/).filter(w => w.length > 0);
69
+
70
+ // Hash-based embedding
71
+ for (const word of words) {
72
+ // Simple hash function
73
+ let hash = 0;
74
+ for (let i = 0; i < word.length; i++) {
75
+ const char = word.charCodeAt(i);
76
+ hash = ((hash << 5) - hash) + char;
77
+ hash = hash & hash; // Convert to 32-bit integer
78
+ }
79
+
80
+ // Map hash to embedding dimensions
81
+ const pos = Math.abs(hash) % dimension;
82
+ const sign = hash > 0 ? 1 : -1;
83
+ embedding[pos] += sign * (1 / words.length);
84
+
85
+ // Add n-gram features
86
+ for (let i = 0; i < word.length - 1; i++) {
87
+ const bigram = word.slice(i, i + 2);
88
+ let bigramHash = 0;
89
+ for (let j = 0; j < bigram.length; j++) {
90
+ bigramHash = ((bigramHash << 5) - bigramHash) + bigram.charCodeAt(j);
91
+ bigramHash = bigramHash & bigramHash;
92
+ }
93
+ const bigramPos = Math.abs(bigramHash) % dimension;
94
+ embedding[bigramPos] += 0.1 / words.length;
95
+ }
96
+ }
97
+
98
+ // Normalize embedding
99
+ const norm = Math.sqrt(embedding.reduce((sum, v) => sum + v * v, 0));
100
+ if (norm > 0) {
101
+ for (let i = 0; i < embedding.length; i++) {
102
+ embedding[i] /= norm;
103
+ }
104
+ }
105
+
106
+ return embedding;
107
+ }
108
+
109
+ /**
110
+ * Calculate cosine similarity between two vectors
111
+ */
112
+ function cosineSimilarity(a: number[], b: number[]): number {
113
+ if (a.length !== b.length) return 0;
114
+
115
+ let dotProduct = 0;
116
+ let normA = 0;
117
+ let normB = 0;
118
+
119
+ for (let i = 0; i < a.length; i++) {
120
+ const aVal = a[i] ?? 0;
121
+ const bVal = b[i] ?? 0;
122
+ dotProduct += aVal * bVal;
123
+ normA += aVal * aVal;
124
+ normB += bVal * bVal;
125
+ }
126
+
127
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
128
+ return denom > 0 ? dotProduct / denom : 0;
129
+ }
130
+
131
+ // ============================================================================
132
+ // VectorMemoryStore
133
+ // ============================================================================
134
+
135
+ /**
136
+ * Vector-based memory store for semantic search
137
+ */
138
+ export class VectorMemoryStore {
139
+ private entries: VectorMemoryEntry[] = [];
140
+ private config: Required<VectorMemoryConfig>;
141
+ private storageFile: string;
142
+ private isDirty = false;
143
+
144
+ constructor(config: VectorMemoryConfig) {
145
+ this.config = {
146
+ maxEntries: config.maxEntries ?? 1000,
147
+ minSimilarity: config.minSimilarity ?? 0.5,
148
+ embeddingDimension: config.embeddingDimension ?? 384,
149
+ ...config,
150
+ };
151
+
152
+ this.storageFile = path.join(this.config.storageDir, 'vector-memory.json');
153
+ this.load();
154
+
155
+ // Auto-save on process exit
156
+ process.on('beforeExit', () => this.save());
157
+ }
158
+
159
+ // -----------------------------------------------------------------------
160
+ // Core Operations
161
+ // -----------------------------------------------------------------------
162
+
163
+ /**
164
+ * Add a memory entry
165
+ */
166
+ async addMemory(
167
+ content: string,
168
+ metadata: VectorMemoryEntry['metadata']
169
+ ): Promise<VectorMemoryEntry> {
170
+ // Generate embedding
171
+ const embedding = simpleEmbedding(content, this.config.embeddingDimension);
172
+
173
+ const entry: VectorMemoryEntry = {
174
+ id: generateId(),
175
+ content,
176
+ embedding,
177
+ metadata: {
178
+ ...metadata,
179
+ timestamp: metadata.timestamp ?? new Date(),
180
+ },
181
+ };
182
+
183
+ this.entries.push(entry);
184
+ this.isDirty = true;
185
+
186
+ // Prune if over limit
187
+ if (this.entries.length > this.config.maxEntries) {
188
+ this.prune();
189
+ }
190
+
191
+ logger.debug(`Added memory entry: ${entry.id}`, { type: metadata.type });
192
+ return entry;
193
+ }
194
+
195
+ /**
196
+ * Search for similar memories
197
+ */
198
+ async searchSimilar(
199
+ query: string,
200
+ options: {
201
+ topK?: number;
202
+ minSimilarity?: number;
203
+ type?: VectorMemoryEntry['metadata']['type'];
204
+ tags?: string[];
205
+ } = {}
206
+ ): Promise<MemoryMatch[]> {
207
+ const { topK = 5, minSimilarity, type, tags } = options;
208
+ const threshold = minSimilarity ?? this.config.minSimilarity;
209
+
210
+ // Generate query embedding
211
+ const queryEmbedding = simpleEmbedding(query, this.config.embeddingDimension);
212
+
213
+ // Calculate similarities
214
+ const matches: MemoryMatch[] = [];
215
+
216
+ for (const entry of this.entries) {
217
+ // Filter by type
218
+ if (type && entry.metadata.type !== type) continue;
219
+
220
+ // Filter by tags
221
+ if (tags && tags.length > 0) {
222
+ const hasTag = tags.some(t => entry.metadata.tags.includes(t));
223
+ if (!hasTag) continue;
224
+ }
225
+
226
+ const score = cosineSimilarity(queryEmbedding, entry.embedding);
227
+
228
+ if (score >= threshold) {
229
+ matches.push({ entry, score });
230
+ }
231
+ }
232
+
233
+ // Sort by score and return top K
234
+ matches.sort((a, b) => b.score - a.score);
235
+ return matches.slice(0, topK);
236
+ }
237
+
238
+ /**
239
+ * Get entry by ID
240
+ */
241
+ getEntry(id: string): VectorMemoryEntry | undefined {
242
+ return this.entries.find(e => e.id === id);
243
+ }
244
+
245
+ /**
246
+ * Delete entry by ID
247
+ */
248
+ deleteEntry(id: string): boolean {
249
+ const index = this.entries.findIndex(e => e.id === id);
250
+ if (index >= 0) {
251
+ this.entries.splice(index, 1);
252
+ this.isDirty = true;
253
+ return true;
254
+ }
255
+ return false;
256
+ }
257
+
258
+ /**
259
+ * Get all entries of a specific type
260
+ */
261
+ getEntriesByType(type: VectorMemoryEntry['metadata']['type']): VectorMemoryEntry[] {
262
+ return this.entries.filter(e => e.metadata.type === type);
263
+ }
264
+
265
+ /**
266
+ * Get entries by tags
267
+ */
268
+ getEntriesByTags(tags: string[]): VectorMemoryEntry[] {
269
+ return this.entries.filter(e =>
270
+ tags.some(t => e.metadata.tags.includes(t))
271
+ );
272
+ }
273
+
274
+ /**
275
+ * Get entries for a session
276
+ */
277
+ getEntriesBySession(sessionId: string): VectorMemoryEntry[] {
278
+ return this.entries.filter(e => e.metadata.session === sessionId);
279
+ }
280
+
281
+ // -----------------------------------------------------------------------
282
+ // Persistence
283
+ // -----------------------------------------------------------------------
284
+
285
+ /**
286
+ * Save to disk
287
+ */
288
+ save(): void {
289
+ if (!this.isDirty) return;
290
+
291
+ try {
292
+ // Ensure directory exists
293
+ const dir = path.dirname(this.storageFile);
294
+ if (!fs.existsSync(dir)) {
295
+ fs.mkdirSync(dir, { recursive: true });
296
+ }
297
+
298
+ // Serialize entries
299
+ const data = {
300
+ version: 1,
301
+ entries: this.entries.map(e => ({
302
+ ...e,
303
+ metadata: {
304
+ ...e.metadata,
305
+ timestamp: e.metadata.timestamp.toISOString(),
306
+ },
307
+ })),
308
+ };
309
+
310
+ fs.writeFileSync(this.storageFile, JSON.stringify(data, null, 2), 'utf-8');
311
+ this.isDirty = false;
312
+
313
+ logger.debug(`Saved ${this.entries.length} memory entries`);
314
+ } catch (error) {
315
+ logger.error('Failed to save memory store', { error });
316
+ }
317
+ }
318
+
319
+ /**
320
+ * Load from disk
321
+ */
322
+ load(): void {
323
+ try {
324
+ if (!fs.existsSync(this.storageFile)) {
325
+ logger.debug('No existing memory store found, starting fresh');
326
+ return;
327
+ }
328
+
329
+ const data = JSON.parse(fs.readFileSync(this.storageFile, 'utf-8'));
330
+
331
+ if (data.version === 1 && Array.isArray(data.entries)) {
332
+ this.entries = data.entries.map((e: any) => ({
333
+ ...e,
334
+ metadata: {
335
+ ...e.metadata,
336
+ timestamp: new Date(e.metadata.timestamp),
337
+ },
338
+ }));
339
+
340
+ logger.debug(`Loaded ${this.entries.length} memory entries`);
341
+ }
342
+ } catch (error) {
343
+ logger.error('Failed to load memory store', { error });
344
+ this.entries = [];
345
+ }
346
+ }
347
+
348
+ // -----------------------------------------------------------------------
349
+ // Maintenance
350
+ // -----------------------------------------------------------------------
351
+
352
+ /**
353
+ * Prune entries based on importance and age
354
+ */
355
+ private prune(): void {
356
+ if (this.entries.length <= this.config.maxEntries) return;
357
+
358
+ // Calculate age scores (newer is better)
359
+ const now = Date.now();
360
+ const maxAge = 30 * 24 * 60 * 60 * 1000; // 30 days
361
+
362
+ const scoredEntries = this.entries.map(entry => {
363
+ const age = now - entry.metadata.timestamp.getTime();
364
+ const ageScore = Math.max(0, 1 - age / maxAge);
365
+
366
+ // Combined score: importance * 0.7 + ageScore * 0.3
367
+ const score = entry.metadata.importance * 0.7 + ageScore * 0.3;
368
+
369
+ return { entry, score };
370
+ });
371
+
372
+ // Sort by score and keep top entries
373
+ scoredEntries.sort((a, b) => b.score - a.score);
374
+ this.entries = scoredEntries
375
+ .slice(0, this.config.maxEntries)
376
+ .map(s => s.entry);
377
+
378
+ this.isDirty = true;
379
+ logger.debug(`Pruned memory to ${this.entries.length} entries`);
380
+ }
381
+
382
+ /**
383
+ * Get statistics
384
+ */
385
+ getStats(): {
386
+ totalEntries: number;
387
+ byType: Record<string, number>;
388
+ oldestEntry: Date | null;
389
+ newestEntry: Date | null;
390
+ avgImportance: number;
391
+ } {
392
+ const byType: Record<string, number> = {};
393
+ let oldest: Date | null = null;
394
+ let newest: Date | null = null;
395
+ let totalImportance = 0;
396
+
397
+ for (const entry of this.entries) {
398
+ byType[entry.metadata.type] = (byType[entry.metadata.type] ?? 0) + 1;
399
+
400
+ if (!oldest || entry.metadata.timestamp < oldest) {
401
+ oldest = entry.metadata.timestamp;
402
+ }
403
+ if (!newest || entry.metadata.timestamp > newest) {
404
+ newest = entry.metadata.timestamp;
405
+ }
406
+
407
+ totalImportance += entry.metadata.importance;
408
+ }
409
+
410
+ return {
411
+ totalEntries: this.entries.length,
412
+ byType,
413
+ oldestEntry: oldest,
414
+ newestEntry: newest,
415
+ avgImportance: this.entries.length > 0 ? totalImportance / this.entries.length : 0,
416
+ };
417
+ }
418
+
419
+ /**
420
+ * Clear all entries
421
+ */
422
+ clear(): void {
423
+ this.entries = [];
424
+ this.isDirty = true;
425
+ }
426
+ }
427
+
428
+ // ============================================================================
429
+ // Factory function
430
+ // ============================================================================
431
+
432
+ /**
433
+ * Create a vector memory store with default configuration
434
+ */
435
+ export function createVectorMemoryStore(
436
+ storageDir?: string
437
+ ): VectorMemoryStore {
438
+ const defaultDir = storageDir ?? path.join(process.env.HOME ?? '~', '.nova', 'memory');
439
+ return new VectorMemoryStore({ storageDir: defaultDir });
440
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["helpers.ts"],"names":[],"mappings":"AAMA,2BAA2B;AAC3B,wBAAgB,UAAU,CAAC,MAAM,SAAK,GAAG,MAAM,CAG9C;AAED,qCAAqC;AACrC,wBAAgB,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE/C;AAED,gDAAgD;AAChD,wBAAsB,KAAK,CAAC,CAAC,EAC3B,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EACpB,OAAO,GAAE;IACP,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC;CACpC,GACL,OAAO,CAAC,CAAC,CAAC,CAiBZ;AAED,wCAAwC;AACxC,wBAAgB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,MAAM,SAAQ,GAAG,MAAM,CAGhF;AAED,4CAA4C;AAC5C,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CASjD;AAED,+CAA+C;AAC/C,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAKjD;AAED,2BAA2B;AAC3B,wBAAgB,SAAS,CAAC,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAEtC;AAED,0BAA0B;AAC1B,wBAAgB,QAAQ,CAAC,CAAC,SAAS,CAAC,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,IAAI,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,GAAG,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,KAAK,IAAI,CAMvH;AAED,uCAAuC;AACvC,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAErE"}
@@ -0,0 +1,89 @@
1
+ // ============================================================================
2
+ // Utility Helpers
3
+ // ============================================================================
4
+
5
+ import { randomBytes } from 'node:crypto';
6
+
7
+ /** Generate a random ID */
8
+ export function generateId(prefix = ''): string {
9
+ const bytes = randomBytes(8).toString('hex');
10
+ return prefix ? `${prefix}_${bytes}` : bytes;
11
+ }
12
+
13
+ /** Sleep for a specified duration */
14
+ export function sleep(ms: number): Promise<void> {
15
+ return new Promise((resolve) => setTimeout(resolve, ms));
16
+ }
17
+
18
+ /** Retry a function with exponential backoff */
19
+ export async function retry<T>(
20
+ fn: () => Promise<T>,
21
+ options: {
22
+ maxAttempts?: number;
23
+ baseDelay?: number;
24
+ maxDelay?: number;
25
+ shouldRetry?: (error: Error) => boolean;
26
+ } = {}
27
+ ): Promise<T> {
28
+ const { maxAttempts = 3, baseDelay = 1000, maxDelay = 30000, shouldRetry } = options;
29
+ let lastError: Error | undefined;
30
+
31
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
32
+ try {
33
+ return await fn();
34
+ } catch (err) {
35
+ lastError = err as Error;
36
+ if (attempt === maxAttempts) break;
37
+ if (shouldRetry && !shouldRetry(lastError)) break;
38
+ const delay = Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
39
+ await sleep(delay);
40
+ }
41
+ }
42
+
43
+ throw lastError;
44
+ }
45
+
46
+ /** Truncate text to a maximum length */
47
+ export function truncate(text: string, maxLength: number, suffix = '...'): string {
48
+ if (text.length <= maxLength) return text;
49
+ return text.slice(0, maxLength - suffix.length) + suffix;
50
+ }
51
+
52
+ /** Format bytes as human-readable string */
53
+ export function formatBytes(bytes: number): string {
54
+ const units = ['B', 'KB', 'MB', 'GB', 'TB'];
55
+ let size = bytes;
56
+ let unitIndex = 0;
57
+ while (size >= 1024 && unitIndex < units.length - 1) {
58
+ size /= 1024;
59
+ unitIndex++;
60
+ }
61
+ return `${size.toFixed(unitIndex === 0 ? 0 : 1)} ${units[unitIndex]}`;
62
+ }
63
+
64
+ /** Format duration as human-readable string */
65
+ export function formatDuration(ms: number): string {
66
+ if (ms < 1000) return `${ms}ms`;
67
+ if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`;
68
+ if (ms < 3600000) return `${Math.floor(ms / 60000)}m ${Math.floor((ms % 60000) / 1000)}s`;
69
+ return `${Math.floor(ms / 3600000)}h ${Math.floor((ms % 3600000) / 60000)}m`;
70
+ }
71
+
72
+ /** Deep clone an object */
73
+ export function deepClone<T>(obj: T): T {
74
+ return JSON.parse(JSON.stringify(obj));
75
+ }
76
+
77
+ /** Debounce a function */
78
+ export function debounce<T extends (...args: unknown[]) => void>(fn: T, delay: number): (...args: Parameters<T>) => void {
79
+ let timer: ReturnType<typeof setTimeout>;
80
+ return (...args: Parameters<T>) => {
81
+ clearTimeout(timer);
82
+ timer = setTimeout(() => fn(...args), delay);
83
+ };
84
+ }
85
+
86
+ /** Ensure a value is within a range */
87
+ export function clamp(value: number, min: number, max: number): number {
88
+ return Math.max(min, Math.min(max, value));
89
+ }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AACnD,YAAY,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,cAAc,EAAE,SAAS,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,cAAc,CAAC"}
@@ -0,0 +1,19 @@
1
+ export { Logger, createLogger } from './Logger.js';
2
+ export type { LogEntry } from './Logger.js';
3
+ export { CheckpointManager } from './CheckpointManager.js';
4
+ export type { Checkpoint, CheckpointFile, CheckpointStats } from './CheckpointManager.js';
5
+ export { generateId, sleep, retry, truncate, formatBytes, formatDuration, deepClone, debounce, clamp } from './helpers.js';
6
+ export { TokenCounter, tokenCounter, countTokens, countMessagesTokens } from './TokenCounter.js';
7
+ export {
8
+ withRetry,
9
+ createRetryWrapper,
10
+ RateLimiter,
11
+ withRateLimit,
12
+ ConcurrencyLimiter,
13
+ createResilientFunction,
14
+ createDefaultApiRateLimiter,
15
+ createDefaultConcurrencyLimiter,
16
+ } from './RetryManager.js';
17
+ export type { RetryConfig, RateLimiterConfig } from './RetryManager.js';
18
+ export { VectorMemoryStore, createVectorMemoryStore } from './VectorMemoryStore.js';
19
+ export type { VectorMemoryEntry, MemoryMatch, VectorMemoryConfig } from './VectorMemoryStore.js';