@bniladridas/cursor 0.1.7

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 (100) hide show
  1. package/.clang-tidy +28 -0
  2. package/.dockerignore +56 -0
  3. package/.env.example +29 -0
  4. package/.github/CODEOWNERS +2 -0
  5. package/.github/ISSUE_TEMPLATE/blank.md +27 -0
  6. package/.github/ISSUE_TEMPLATE/bug_report.md +33 -0
  7. package/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  8. package/.github/SECURITY.md +24 -0
  9. package/.github/codeql/codeql-config.yml +8 -0
  10. package/.github/dependabot.yml +14 -0
  11. package/.github/labeler.yml +50 -0
  12. package/.github/packaging/brand-cursor.png +0 -0
  13. package/.github/packaging/database/init.sql +48 -0
  14. package/.github/packaging/docker/Dockerfile +111 -0
  15. package/.github/packaging/docker/docker-compose.yml +56 -0
  16. package/.github/packaging/scripts/preflight.sh +413 -0
  17. package/.github/packaging/scripts/prepare-release.sh +141 -0
  18. package/.github/packaging/scripts/release.sh +22 -0
  19. package/.github/packaging/scripts/setup-git-hooks.sh +73 -0
  20. package/.github/pull_request_template.md +31 -0
  21. package/.github/signed.json +9 -0
  22. package/.github/workflows/README.md +23 -0
  23. package/.github/workflows/ci.yml +181 -0
  24. package/.github/workflows/cla.yml +33 -0
  25. package/.github/workflows/formula-sha.yml +63 -0
  26. package/.github/workflows/issue-response.yml +44 -0
  27. package/.github/workflows/labeler.yml +42 -0
  28. package/.github/workflows/pr-body.yml +49 -0
  29. package/.github/workflows/release.yml +176 -0
  30. package/.github/workflows/security.yml +94 -0
  31. package/.github/workflows/stale.yml +38 -0
  32. package/AGENTS.md +49 -0
  33. package/CHANGELOG.md +3 -0
  34. package/CMakeLists.txt +646 -0
  35. package/Formula/cursor.rb +46 -0
  36. package/LICENSE +201 -0
  37. package/Makefile +28 -0
  38. package/README.md +46 -0
  39. package/cli.js +16 -0
  40. package/include/agent.h +86 -0
  41. package/include/agent_mode.h +17 -0
  42. package/include/memory_manager.h +102 -0
  43. package/include/services/ai_service.h +31 -0
  44. package/include/services/auth_service.h +87 -0
  45. package/include/services/checkpoint_service.h +69 -0
  46. package/include/services/codebase_service.h +38 -0
  47. package/include/services/command_service.h +23 -0
  48. package/include/services/context_service.h +74 -0
  49. package/include/services/database_service.h +56 -0
  50. package/include/services/error_service.h +106 -0
  51. package/include/services/file_service.h +51 -0
  52. package/include/services/git_service.h +29 -0
  53. package/include/services/github_service.h +85 -0
  54. package/include/services/mcp_service.h +85 -0
  55. package/include/services/multi_file_service.h +93 -0
  56. package/include/services/sandbox_service.h +96 -0
  57. package/include/services/theme_service.h +67 -0
  58. package/include/services/web_service.h +52 -0
  59. package/include/utils/config.h +68 -0
  60. package/include/utils/memory_utils.h +79 -0
  61. package/include/utils/platform.h +56 -0
  62. package/include/utils/ui.h +43 -0
  63. package/include/utils/validation.h +63 -0
  64. package/include/utils/version.h.in +17 -0
  65. package/install.js +49 -0
  66. package/package.json +16 -0
  67. package/release/checksums.txt +3 -0
  68. package/release/cursor-linux/cursor_v0.1.7_linux_amd64.tar.gz +0 -0
  69. package/release/cursor-macos/cursor_v0.1.7_darwin_arm64.tar.gz +0 -0
  70. package/release/cursor-windows/cursor__windows_amd64.zip +0 -0
  71. package/src/agent.cpp +2026 -0
  72. package/src/main.cpp +97 -0
  73. package/src/memory_manager.cpp +814 -0
  74. package/src/services/ai_service.cpp +366 -0
  75. package/src/services/auth_service.cpp +779 -0
  76. package/src/services/checkpoint_service.cpp +465 -0
  77. package/src/services/codebase_service.cpp +233 -0
  78. package/src/services/command_service.cpp +82 -0
  79. package/src/services/context_service.cpp +348 -0
  80. package/src/services/database_service.cpp +148 -0
  81. package/src/services/error_service.cpp +438 -0
  82. package/src/services/file_service.cpp +349 -0
  83. package/src/services/git_service.cpp +148 -0
  84. package/src/services/github_service.cpp +435 -0
  85. package/src/services/mcp_service.cpp +481 -0
  86. package/src/services/multi_file_service.cpp +591 -0
  87. package/src/services/sandbox_service.cpp +678 -0
  88. package/src/services/theme_service.cpp +429 -0
  89. package/src/services/web_service.cpp +532 -0
  90. package/src/utils/config.cpp +77 -0
  91. package/src/utils/memory_utils.cpp +93 -0
  92. package/src/utils/ui.cpp +307 -0
  93. package/src/utils/validation.cpp +306 -0
  94. package/src/utils/version.cpp +175 -0
  95. package/tests/e2e/docker-compose.yml +195 -0
  96. package/tests/e2e/run_e2e_tests.sh +70 -0
  97. package/tests/e2e/run_tests_in_docker.sh +115 -0
  98. package/tests/main_test.cpp +16 -0
  99. package/tests/mocks/mock_ollama.py +98 -0
  100. package/tests/mocks/start_nginx.sh +64 -0
@@ -0,0 +1,814 @@
1
+ #include "memory_manager.h"
2
+ #include "services/file_service.h"
3
+ #include <filesystem>
4
+ #include <fstream>
5
+ #include <stdexcept>
6
+
7
+ #include <algorithm>
8
+ #include <chrono>
9
+ #include <cstdlib>
10
+ #include <iomanip>
11
+ #include <iostream>
12
+ #include <regex>
13
+ #include <sstream>
14
+ #include <unordered_set>
15
+
16
+ namespace Data {
17
+
18
+ MemoryManager::MemoryManager(const std::string &filename)
19
+ : memory_file_(filename),
20
+ last_file_check_(std::chrono::steady_clock::now()), cached_file_size_(0) {
21
+ ensure_memory_directory();
22
+ global_memory_file_ = get_global_memory_path();
23
+
24
+ // Initialize cache
25
+ entry_cache_.reserve(lru_cache_size);
26
+ }
27
+
28
+ void MemoryManager::ensure_memory_directory() const {
29
+ try {
30
+ // Ensure directory exists if a path is provided
31
+ std::filesystem::path file_path(memory_file_);
32
+ if (file_path.has_parent_path()) {
33
+ std::filesystem::create_directories(file_path.parent_path());
34
+ }
35
+
36
+ // Ensure global memory directory exists
37
+ std::filesystem::path global_path(get_global_memory_path());
38
+ if (global_path.has_parent_path()) {
39
+ std::filesystem::create_directories(global_path.parent_path());
40
+ }
41
+ } catch (const std::filesystem::filesystem_error &e) {
42
+ throw std::runtime_error("Failed to create memory directory: " +
43
+ std::string(e.what()));
44
+ }
45
+ }
46
+
47
+ std::string MemoryManager::get_timestamp() const {
48
+ auto now = std::chrono::system_clock::now();
49
+ auto time_t = std::chrono::system_clock::to_time_t(now);
50
+ std::stringstream ss;
51
+ ss << std::put_time(std::localtime(&time_t), "%Y-%m-%d %H:%M:%S");
52
+ return ss.str();
53
+ }
54
+
55
+ std::string MemoryManager::get_global_memory_path() const {
56
+ // Get home directory
57
+ const char *home = std::getenv("HOME");
58
+ if (!home) {
59
+ home = std::getenv("USERPROFILE"); // Windows
60
+ }
61
+ if (!home) {
62
+ return "global_memory.md"; // Fallback to current directory
63
+ }
64
+
65
+ std::filesystem::path global_path =
66
+ std::filesystem::path(home) / ".cursor" / "CURSOR.md";
67
+ return global_path.string();
68
+ }
69
+
70
+ std::vector<std::string> MemoryManager::load_memory() const {
71
+ // Legacy method - convert from optimized load_recent_entries_only()
72
+ std::vector<MemoryEntry> entries = load_recent_entries_only();
73
+ std::vector<std::string> memory;
74
+ memory.reserve(entries.size());
75
+
76
+ for (const auto &entry : entries) {
77
+ memory.push_back(entry.content);
78
+ }
79
+
80
+ return memory;
81
+ }
82
+
83
+ void MemoryManager::save_interaction(const std::string &user_input,
84
+ const std::string &response) {
85
+ std::ofstream file(memory_file_, std::ios::out | std::ios::app);
86
+ if (!file.is_open()) {
87
+ throw std::runtime_error("Unable to open memory file for writing: " +
88
+ memory_file_);
89
+ }
90
+
91
+ file << "User: " << user_input << "\n";
92
+ file << "Assistant: " << response << "\n";
93
+ file << "---\n"; // Separator for better readability
94
+ }
95
+
96
+ void MemoryManager::clear_memory() {
97
+ std::ofstream file(memory_file_, std::ios::out | std::ios::trunc);
98
+ if (!file.is_open()) {
99
+ throw std::runtime_error("Unable to open memory file for clearing: " +
100
+ memory_file_);
101
+ }
102
+ // File is now empty
103
+ }
104
+
105
+ std::string MemoryManager::get_context_string() const {
106
+ // Use optimized recent entries loading with StringBuilder
107
+ std::vector<MemoryEntry> recent_entries = load_recent_entries_only();
108
+
109
+ // Use StringBuilder for efficient string concatenation
110
+ Utils::Memory::StringBuilder context_builder(4096);
111
+
112
+ // Limit context to last 20 interactions
113
+ constexpr size_t max_interactions = 20;
114
+ size_t interaction_count = 0;
115
+
116
+ // Process entries in reverse order to get most recent first
117
+ for (auto it = recent_entries.rbegin();
118
+ it != recent_entries.rend() && interaction_count < max_interactions;
119
+ ++it) {
120
+ if (it->type == "interaction") {
121
+ context_builder.append(it->content).append("\n");
122
+ interaction_count++;
123
+ }
124
+ }
125
+
126
+ // Add global context efficiently
127
+ std::string global_context = get_global_context();
128
+ if (!global_context.empty()) {
129
+ std::string context_content = std::move(context_builder).build();
130
+ Utils::Memory::StringBuilder final_builder(global_context.size() +
131
+ context_content.size() + 50);
132
+ final_builder.append(global_context)
133
+ .append("\n\nRecent interactions:\n")
134
+ .append(context_content);
135
+ return std::move(final_builder).build();
136
+ }
137
+
138
+ return std::move(context_builder).build();
139
+ }
140
+
141
+ size_t MemoryManager::get_memory_size() const {
142
+ // Use cached file size instead of loading entire file
143
+ if (!Services::FileService::file_exists(memory_file_)) {
144
+ return 0;
145
+ }
146
+
147
+ try {
148
+ // Update cached size if needed
149
+ auto now = std::chrono::steady_clock::now();
150
+ if (now - last_file_check_ > std::chrono::seconds(5)) {
151
+ cached_file_size_ = std::filesystem::file_size(memory_file_);
152
+ last_file_check_ = now;
153
+ }
154
+
155
+ // Approximate line count based on average line size (80 chars)
156
+ return cached_file_size_ / 80;
157
+ } catch (const std::exception &) {
158
+ return load_recent_entries_only().size(); // Fallback
159
+ }
160
+ }
161
+
162
+ void MemoryManager::save_fact(const std::string &fact) {
163
+ std::ofstream file(memory_file_, std::ios::out | std::ios::app);
164
+ if (!file.is_open()) {
165
+ throw std::runtime_error("Unable to open memory file for writing: " +
166
+ memory_file_);
167
+ }
168
+
169
+ file << "Fact [" << get_timestamp() << "]: " << fact << "\n";
170
+ }
171
+
172
+ void MemoryManager::save_preference(const std::string &preference) {
173
+ std::ofstream file(memory_file_, std::ios::out | std::ios::app);
174
+ if (!file.is_open()) {
175
+ throw std::runtime_error("Unable to open memory file for writing: " +
176
+ memory_file_);
177
+ }
178
+
179
+ file << "Preference [" << get_timestamp() << "]: " << preference << "\n";
180
+ }
181
+
182
+ std::vector<MemoryEntry> MemoryManager::load_structured_memory() const {
183
+ // Use optimized recent entries loading - already parsed
184
+ return load_recent_entries_only();
185
+ }
186
+
187
+ std::string MemoryManager::get_facts_context() const {
188
+ std::vector<MemoryEntry> entries = load_structured_memory();
189
+ std::string facts_context;
190
+
191
+ for (const auto &entry : entries) {
192
+ if (entry.type == "fact") {
193
+ facts_context += "- " + entry.content + "\n";
194
+ }
195
+ }
196
+
197
+ return facts_context.empty() ? "" : "Known facts:\n" + facts_context;
198
+ }
199
+
200
+ std::string MemoryManager::get_preferences_context() const {
201
+ std::vector<MemoryEntry> entries = load_structured_memory();
202
+ std::string prefs_context;
203
+
204
+ for (const auto &entry : entries) {
205
+ if (entry.type == "preference") {
206
+ prefs_context += "- " + entry.content + "\n";
207
+ }
208
+ }
209
+
210
+ return prefs_context.empty() ? "" : "User preferences:\n" + prefs_context;
211
+ }
212
+
213
+ void MemoryManager::save_global_fact(const std::string &fact) {
214
+ try {
215
+ std::string content;
216
+
217
+ // Read existing content
218
+ if (Services::FileService::file_exists(global_memory_file_)) {
219
+ std::ifstream file(global_memory_file_);
220
+ if (file.is_open()) {
221
+ std::ostringstream buffer;
222
+ buffer << file.rdbuf();
223
+ content = buffer.str();
224
+ }
225
+ }
226
+
227
+ // Find or create the memories section
228
+ const std::string header = "## Cursor Added Memories";
229
+ size_t header_pos = content.find(header);
230
+
231
+ if (header_pos == std::string::npos) {
232
+ // Add header and fact
233
+ if (!content.empty() && content.back() != '\n') {
234
+ content += "\n";
235
+ }
236
+ content += "\n" + header + "\n- " + fact + "\n";
237
+ } else {
238
+ // Find insertion point after header
239
+ size_t insert_pos = header_pos + header.length();
240
+ size_t next_header = content.find("\n## ", insert_pos);
241
+
242
+ if (next_header == std::string::npos) {
243
+ // Insert at end
244
+ content += "\n- " + fact;
245
+ } else {
246
+ // Insert before next header
247
+ content.insert(next_header, "\n- " + fact);
248
+ }
249
+ }
250
+
251
+ // Write back to file
252
+ std::ofstream file(global_memory_file_);
253
+ if (file.is_open()) {
254
+ file << content;
255
+ }
256
+
257
+ } catch (const std::exception &) {
258
+ throw std::runtime_error("Failed to save global fact");
259
+ }
260
+ }
261
+
262
+ std::string MemoryManager::get_global_context() const {
263
+ if (!Services::FileService::file_exists(global_memory_file_)) {
264
+ return "";
265
+ }
266
+
267
+ try {
268
+ std::ifstream file(global_memory_file_);
269
+ if (!file.is_open()) {
270
+ return "";
271
+ }
272
+
273
+ std::ostringstream buffer;
274
+ buffer << file.rdbuf();
275
+ std::string content = buffer.str();
276
+
277
+ // Extract memories section
278
+ const std::string header = "## Cursor Added Memories";
279
+ size_t header_pos = content.find(header);
280
+
281
+ if (header_pos == std::string::npos) {
282
+ return "";
283
+ }
284
+
285
+ size_t start_pos = header_pos + header.length();
286
+ size_t end_pos = content.find("\n## ", start_pos);
287
+
288
+ if (end_pos == std::string::npos) {
289
+ end_pos = content.length();
290
+ }
291
+
292
+ std::string memories = content.substr(start_pos, end_pos - start_pos);
293
+ return memories.empty() ? "" : "Global memories:\n" + memories;
294
+
295
+ } catch (const std::exception &) {
296
+ return "";
297
+ }
298
+ }
299
+
300
+ void MemoryManager::clear_global_memory() {
301
+ try {
302
+ if (Services::FileService::file_exists(global_memory_file_)) {
303
+ std::filesystem::remove(global_memory_file_);
304
+ }
305
+ } catch (const std::exception &) {
306
+ throw std::runtime_error("Failed to clear global memory");
307
+ }
308
+ }
309
+
310
+ std::vector<MemoryEntry>
311
+ MemoryManager::search_memory(const std::string &query) const {
312
+ rebuild_index();
313
+
314
+ std::vector<MemoryEntry> results;
315
+ std::unordered_set<size_t> result_indices;
316
+
317
+ // Convert query to lowercase for case-insensitive search
318
+ std::string lower_query(query);
319
+ std::transform(lower_query.begin(), lower_query.end(), lower_query.begin(),
320
+ ::tolower);
321
+
322
+ // Tokenize query and search index (O(1) lookup per word)
323
+ std::istringstream iss(lower_query);
324
+ std::string word;
325
+ while (iss >> word) {
326
+ // Remove punctuation
327
+ word.erase(std::remove_if(word.begin(), word.end(), ::ispunct), word.end());
328
+
329
+ if (word.length() > 2) {
330
+ auto it = content_index_.find(word);
331
+ if (it != content_index_.end()) {
332
+ for (size_t idx : it->second) {
333
+ result_indices.insert(idx);
334
+ }
335
+ }
336
+ }
337
+ }
338
+
339
+ // If no indexed results, fall back to linear search on recent entries
340
+ if (result_indices.empty()) {
341
+ std::vector<MemoryEntry> recent_entries = load_recent_entries_only();
342
+ for (const auto &entry : recent_entries) {
343
+ std::string lower_content = entry.content;
344
+ std::transform(lower_content.begin(), lower_content.end(),
345
+ lower_content.begin(), ::tolower);
346
+
347
+ if (lower_content.find(lower_query) != std::string::npos) {
348
+ results.push_back(entry);
349
+ }
350
+ }
351
+ } else {
352
+ // Convert indices to entries
353
+ std::vector<MemoryEntry> recent_entries = load_recent_entries_only();
354
+ results.reserve(result_indices.size());
355
+
356
+ for (size_t idx : result_indices) {
357
+ if (idx < recent_entries.size()) {
358
+ results.push_back(recent_entries[idx]);
359
+ }
360
+ }
361
+ }
362
+
363
+ return results;
364
+ }
365
+
366
+ void MemoryManager::export_memory(const std::string &filename) const {
367
+ std::vector<MemoryEntry> entries = load_structured_memory();
368
+
369
+ std::ofstream file(filename);
370
+ if (!file.is_open()) {
371
+ throw std::runtime_error("Unable to open export file: " + filename);
372
+ }
373
+
374
+ file << "# Cursor Memory Export\n\n";
375
+ file << "Export Date: " << get_timestamp() << "\n\n";
376
+
377
+ for (const auto &entry : entries) {
378
+ file << "## " << entry.type << " [" << entry.timestamp << "]\n";
379
+ file << entry.content << "\n\n";
380
+ }
381
+ }
382
+
383
+ bool MemoryManager::import_memory(const std::string &filename) {
384
+ if (!Services::FileService::file_exists(filename)) {
385
+ return false;
386
+ }
387
+
388
+ try {
389
+ std::ifstream file(filename);
390
+ if (!file.is_open()) {
391
+ return false;
392
+ }
393
+
394
+ std::string line;
395
+ while (std::getline(file, line)) {
396
+ if (!line.empty() && line != "# Cursor Memory Export" &&
397
+ line.find("Export Date:") == std::string::npos) {
398
+
399
+ std::ofstream memory_file(memory_file_, std::ios::out | std::ios::app);
400
+ if (memory_file.is_open()) {
401
+ memory_file << line << "\n";
402
+ }
403
+ }
404
+ }
405
+
406
+ return true;
407
+ } catch (const std::exception &) {
408
+ return false;
409
+ }
410
+ }
411
+
412
+ void MemoryManager::save_conversation_state(const std::string &tag) {
413
+ try {
414
+ // Create conversations directory
415
+ std::filesystem::path conv_dir =
416
+ std::filesystem::path(memory_file_).parent_path() / "conversations";
417
+ std::filesystem::create_directories(conv_dir);
418
+
419
+ // Save current memory to tagged file
420
+ std::filesystem::path conv_file = conv_dir / (tag + ".txt");
421
+ std::filesystem::copy_file(
422
+ memory_file_, conv_file,
423
+ std::filesystem::copy_options::overwrite_existing);
424
+
425
+ } catch (const std::exception &) {
426
+ throw std::runtime_error("Failed to save conversation state");
427
+ }
428
+ }
429
+
430
+ bool MemoryManager::resume_conversation_state(const std::string &tag) {
431
+ try {
432
+ std::filesystem::path conv_dir =
433
+ std::filesystem::path(memory_file_).parent_path() / "conversations";
434
+ std::filesystem::path conv_file = conv_dir / (tag + ".txt");
435
+
436
+ if (!std::filesystem::exists(conv_file)) {
437
+ return false;
438
+ }
439
+
440
+ // Replace current memory with saved state
441
+ std::filesystem::copy_file(
442
+ conv_file, memory_file_,
443
+ std::filesystem::copy_options::overwrite_existing);
444
+ return true;
445
+
446
+ } catch (const std::exception &) {
447
+ return false;
448
+ }
449
+ }
450
+
451
+ std::vector<std::string> MemoryManager::list_conversation_states() const {
452
+ std::vector<std::string> conversations;
453
+
454
+ try {
455
+ std::filesystem::path conv_dir =
456
+ std::filesystem::path(memory_file_).parent_path() / "conversations";
457
+
458
+ if (!std::filesystem::exists(conv_dir)) {
459
+ return conversations;
460
+ }
461
+
462
+ for (const auto &entry : std::filesystem::directory_iterator(conv_dir)) {
463
+ if (entry.is_regular_file() && entry.path().extension() == ".txt") {
464
+ conversations.push_back(entry.path().stem().string());
465
+ }
466
+ }
467
+
468
+ std::sort(conversations.begin(), conversations.end());
469
+
470
+ } catch (const std::exception &) {
471
+ // Return empty vector on error
472
+ }
473
+
474
+ return conversations;
475
+ }
476
+
477
+ void MemoryManager::delete_conversation_state(const std::string &tag) {
478
+ try {
479
+ std::filesystem::path conv_dir =
480
+ std::filesystem::path(memory_file_).parent_path() / "conversations";
481
+ std::filesystem::path conv_file = conv_dir / (tag + ".txt");
482
+
483
+ if (std::filesystem::exists(conv_file)) {
484
+ std::filesystem::remove(conv_file);
485
+ }
486
+
487
+ } catch (const std::exception &) {
488
+ throw std::runtime_error("Failed to delete conversation state");
489
+ }
490
+ }
491
+
492
+ std::string MemoryManager::get_compressible_context() const {
493
+ std::vector<MemoryEntry> entries = load_structured_memory();
494
+
495
+ // Only compress interaction entries, preserve facts and preferences
496
+ std::string compressible_content;
497
+ int interaction_count = 0;
498
+
499
+ for (const auto &entry : entries) {
500
+ if (entry.type == "interaction") {
501
+ compressible_content +=
502
+ "[" + entry.timestamp + "] " + entry.content + "\n";
503
+ interaction_count++;
504
+ }
505
+ }
506
+
507
+ if (interaction_count < 5) {
508
+ return ""; // Not enough content to compress
509
+ }
510
+
511
+ return compressible_content;
512
+ }
513
+
514
+ void MemoryManager::compress_memory(const std::string &compressed_summary) {
515
+ try {
516
+ // Load current memory entries
517
+ std::vector<MemoryEntry> entries = load_structured_memory();
518
+
519
+ // Create backup before compression
520
+ std::string backup_file = memory_file_ + ".backup." + get_timestamp();
521
+ std::filesystem::copy_file(memory_file_, backup_file);
522
+
523
+ // Clear current memory file
524
+ std::ofstream file(memory_file_, std::ios::trunc);
525
+ if (!file.is_open()) {
526
+ throw std::runtime_error("Could not open memory file for compression");
527
+ }
528
+
529
+ // Write compressed summary as a single entry
530
+ file << "[" << get_timestamp()
531
+ << "] COMPRESSED_SUMMARY: " << compressed_summary << std::endl;
532
+
533
+ // Preserve recent interactions (last 3), facts, and preferences
534
+ int recent_interactions = 0;
535
+ for (auto it = entries.rbegin(); it != entries.rend(); ++it) {
536
+ const auto &entry = *it;
537
+
538
+ if (entry.type == "interaction" && recent_interactions < 3) {
539
+ file << "[" << entry.timestamp << "] " << entry.content << std::endl;
540
+ recent_interactions++;
541
+ } else if (entry.type == "fact" || entry.type == "preference") {
542
+ file << "[" << entry.timestamp << "] " << entry.type << ": "
543
+ << entry.content << std::endl;
544
+ }
545
+ }
546
+
547
+ file.close();
548
+
549
+ // Clear cache after compression
550
+ entry_cache_.clear();
551
+ lru_order_.clear();
552
+ content_index_.clear();
553
+ index_dirty_ = true;
554
+ current_memory_usage_ = 0;
555
+
556
+ } catch (const std::exception &) {
557
+ throw std::runtime_error("Failed to compress memory");
558
+ }
559
+ }
560
+
561
+ // ============================================================================
562
+ // MEMORY OPTIMIZATION IMPLEMENTATIONS
563
+ // ============================================================================
564
+
565
+ bool MemoryManager::is_cache_valid() const {
566
+ if (!Services::FileService::file_exists(memory_file_)) {
567
+ return entry_cache_.empty();
568
+ }
569
+
570
+ // Check if file has been modified since last cache
571
+ auto now = std::chrono::steady_clock::now();
572
+ if (now - last_file_check_ >
573
+ std::chrono::seconds(5)) { // Check every 5 seconds
574
+ try {
575
+ size_t current_size = std::filesystem::file_size(memory_file_);
576
+ last_file_check_ = now;
577
+
578
+ if (current_size != cached_file_size_) {
579
+ cached_file_size_ = current_size;
580
+ return false; // Cache invalid
581
+ }
582
+ } catch (const std::exception &) {
583
+ return false;
584
+ }
585
+ }
586
+
587
+ return true;
588
+ }
589
+
590
+ void MemoryManager::update_lru_access(size_t entry_id) const {
591
+ // Remove from current position
592
+ auto it = std::find(lru_order_.begin(), lru_order_.end(), entry_id);
593
+ if (it != lru_order_.end()) {
594
+ lru_order_.erase(it);
595
+ }
596
+
597
+ // Add to front (most recently used)
598
+ lru_order_.push_front(entry_id);
599
+ }
600
+
601
+ void MemoryManager::evict_lru_entries() const {
602
+ while (entry_cache_.size() > lru_cache_size && !lru_order_.empty()) {
603
+ size_t lru_id = lru_order_.back();
604
+ lru_order_.pop_back();
605
+
606
+ auto it = entry_cache_.find(lru_id);
607
+ if (it != entry_cache_.end()) {
608
+ current_memory_usage_ -= calculate_entry_size(it->second);
609
+ entry_cache_.erase(it);
610
+ }
611
+ }
612
+ }
613
+
614
+ MemoryEntry
615
+ MemoryManager::parse_entry_from_line(std::string_view line,
616
+ size_t /* line_number */) const {
617
+ MemoryEntry entry;
618
+ entry.content =
619
+ std::string(line); // Convert string_view to string only when needed
620
+ entry.timestamp = get_timestamp(); // Default timestamp
621
+ entry.type = "interaction"; // Default type
622
+
623
+ // Use string_view for efficient parsing - avoid substr() copies
624
+ if (line.starts_with("Fact [")) {
625
+ entry.type = "fact";
626
+ size_t bracket_end = line.find("]: ");
627
+ if (bracket_end != std::string_view::npos) {
628
+ entry.content = std::string(line.substr(bracket_end + 3));
629
+ // Extract timestamp using string_view
630
+ size_t ts_start = 5; // After "Fact ["
631
+ size_t ts_end = line.find(']');
632
+ if (ts_end != std::string_view::npos && ts_end > ts_start) {
633
+ entry.timestamp = std::string(line.substr(ts_start, ts_end - ts_start));
634
+ }
635
+ }
636
+ } else if (line.starts_with("Preference [")) {
637
+ entry.type = "preference";
638
+ size_t bracket_end = line.find("]: ");
639
+ if (bracket_end != std::string_view::npos) {
640
+ entry.content = std::string(line.substr(bracket_end + 3));
641
+ // Extract timestamp using string_view
642
+ size_t ts_start = 11; // After "Preference ["
643
+ size_t ts_end = line.find(']');
644
+ if (ts_end != std::string_view::npos && ts_end > ts_start) {
645
+ entry.timestamp = std::string(line.substr(ts_start, ts_end - ts_start));
646
+ }
647
+ }
648
+ }
649
+
650
+ return entry;
651
+ }
652
+
653
+ std::vector<MemoryEntry> MemoryManager::load_recent_entries_only() const {
654
+ std::vector<MemoryEntry> entries;
655
+
656
+ if (!Services::FileService::file_exists(memory_file_)) {
657
+ return entries;
658
+ }
659
+
660
+ // Check cache validity first
661
+ if (!is_cache_valid()) {
662
+ entry_cache_.clear();
663
+ lru_order_.clear();
664
+ index_dirty_ = true;
665
+ current_memory_usage_ = 0;
666
+ }
667
+
668
+ std::ifstream file(memory_file_, std::ios::in);
669
+ if (!file.is_open()) {
670
+ return entries;
671
+ }
672
+
673
+ std::string line;
674
+ size_t line_number = 0;
675
+ size_t total_lines = 0;
676
+
677
+ // First pass: count total lines (for recent entries calculation)
678
+ while (std::getline(file, line)) {
679
+ if (!line.empty()) {
680
+ total_lines++;
681
+ }
682
+ }
683
+
684
+ // Reset file stream
685
+ file.clear();
686
+ file.seekg(0, std::ios::beg);
687
+
688
+ // Calculate start position for recent entries
689
+ size_t start_line = total_lines > recent_entries_limit
690
+ ? total_lines - recent_entries_limit
691
+ : 0;
692
+
693
+ entries.reserve(std::min(total_lines, recent_entries_limit));
694
+
695
+ // Second pass: load only recent entries
696
+ line_number = 0;
697
+ while (std::getline(file, line)) {
698
+ if (line.empty())
699
+ continue;
700
+
701
+ if (line_number >= start_line) {
702
+ // Check cache first
703
+ auto cache_it = entry_cache_.find(line_number);
704
+ if (cache_it != entry_cache_.end()) {
705
+ entries.push_back(cache_it->second);
706
+ update_lru_access(line_number);
707
+ } else {
708
+ // Parse new entry using string_view for efficiency
709
+ MemoryEntry entry = parse_entry_from_line(line, line_number);
710
+ entries.push_back(entry);
711
+
712
+ // Add to cache if within limits
713
+ if (entry_cache_.size() < lru_cache_size) {
714
+ entry_cache_[line_number] = entry;
715
+ update_lru_access(line_number);
716
+ current_memory_usage_ += calculate_entry_size(entry);
717
+ }
718
+ }
719
+ }
720
+ line_number++;
721
+ }
722
+
723
+ // Evict old entries if cache is full
724
+ evict_lru_entries();
725
+
726
+ return entries;
727
+ }
728
+
729
+ void MemoryManager::rebuild_index() const {
730
+ if (!index_dirty_)
731
+ return;
732
+
733
+ content_index_.clear();
734
+
735
+ // Load recent entries and build index
736
+ std::vector<MemoryEntry> entries = load_recent_entries_only();
737
+
738
+ for (size_t i = 0; i < entries.size(); ++i) {
739
+ const auto &entry = entries[i];
740
+
741
+ // Tokenize content for indexing (simple word-based)
742
+ std::istringstream iss(entry.content);
743
+ std::string word;
744
+ while (iss >> word) {
745
+ // Convert to lowercase for case-insensitive search
746
+ std::transform(word.begin(), word.end(), word.begin(), ::tolower);
747
+ // Remove punctuation
748
+ word.erase(std::remove_if(word.begin(), word.end(), ::ispunct),
749
+ word.end());
750
+
751
+ if (word.length() > 2) { // Index words longer than 2 characters
752
+ content_index_[word].push_back(i);
753
+ }
754
+ }
755
+
756
+ // Also index by type
757
+ content_index_[entry.type].push_back(i);
758
+ }
759
+
760
+ index_dirty_ = false;
761
+ }
762
+
763
+ std::vector<MemoryEntry>
764
+ MemoryManager::search_memory_by_type(const std::string &type) const {
765
+ rebuild_index();
766
+
767
+ std::vector<MemoryEntry> results;
768
+ auto it = content_index_.find(type);
769
+ if (it != content_index_.end()) {
770
+ std::vector<MemoryEntry> recent_entries = load_recent_entries_only();
771
+ results.reserve(it->second.size());
772
+
773
+ for (size_t idx : it->second) {
774
+ if (idx < recent_entries.size()) {
775
+ results.push_back(recent_entries[idx]);
776
+ }
777
+ }
778
+ }
779
+
780
+ return results;
781
+ }
782
+
783
+ size_t MemoryManager::get_cache_hit_ratio() const {
784
+ if (lru_order_.empty())
785
+ return 0;
786
+
787
+ // Simple approximation: cache hits vs total accesses
788
+ return (entry_cache_.size() * 100) / std::max(size_t(1), lru_order_.size());
789
+ }
790
+
791
+ void MemoryManager::print_memory_stats() const {
792
+ std::cout << "=== Memory Manager Statistics ===" << std::endl;
793
+ std::cout << "Cache entries: " << entry_cache_.size() << "/" << lru_cache_size
794
+ << std::endl;
795
+ std::cout << "Memory usage: " << (current_memory_usage_ / 1024) << " KB"
796
+ << std::endl;
797
+ std::cout << "Cache hit ratio: " << get_cache_hit_ratio() << "%" << std::endl;
798
+ std::cout << "Index entries: " << content_index_.size() << std::endl;
799
+ std::cout << "File size: " << (cached_file_size_ / 1024) << " KB"
800
+ << std::endl;
801
+ }
802
+
803
+ // Helper methods implementation
804
+ void MemoryManager::evict_old_entries() const {
805
+ // This method can be used for automatic memory management
806
+ // Currently handled by evict_lru_entries()
807
+ }
808
+
809
+ size_t MemoryManager::calculate_entry_size(const MemoryEntry &entry) const {
810
+ return entry.content.size() + entry.timestamp.size() + entry.type.size() +
811
+ sizeof(MemoryEntry); // Approximate size including object overhead
812
+ }
813
+
814
+ } // namespace Data