@theokit/sdk-memory 0.1.0 → 0.2.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 (127) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/dist/index.cjs +297 -207
  3. package/dist/index.cjs.map +1 -1
  4. package/dist/index.d.cts +34 -33
  5. package/dist/index.d.ts +34 -33
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +300 -211
  8. package/dist/index.js.map +1 -1
  9. package/dist/internal/active-memory/active-memory-cache.d.ts.map +1 -0
  10. package/dist/internal/{active-memory-types.d.ts → active-memory/active-memory-types.d.ts} +1 -1
  11. package/dist/internal/active-memory/active-memory-types.d.ts.map +1 -0
  12. package/dist/internal/{active-memory.d.ts → active-memory/active-memory.d.ts} +2 -2
  13. package/dist/internal/active-memory/active-memory.d.ts.map +1 -0
  14. package/dist/internal/active-memory/composite-scorer.d.ts.map +1 -0
  15. package/dist/internal/active-memory/query-analyzer.d.ts.map +1 -0
  16. package/dist/internal/adapter-catalog.d.ts +6 -6
  17. package/dist/internal/categorized-memory.d.ts +56 -0
  18. package/dist/internal/categorized-memory.d.ts.map +1 -0
  19. package/dist/internal/dreaming/dreaming-diary.d.ts.map +1 -0
  20. package/dist/internal/{dreaming-phases.d.ts → dreaming/dreaming-phases.d.ts} +2 -2
  21. package/dist/internal/dreaming/dreaming-phases.d.ts.map +1 -0
  22. package/dist/internal/{dreaming-run.d.ts → dreaming/dreaming-run.d.ts} +1 -1
  23. package/dist/internal/dreaming/dreaming-run.d.ts.map +1 -0
  24. package/dist/internal/embedding/batch-encoder.d.ts.map +1 -0
  25. package/dist/internal/embedding/deepinfra-embedding.d.ts.map +1 -0
  26. package/dist/internal/embedding/embedding-adapter.d.ts.map +1 -0
  27. package/dist/internal/embedding/embedding-cache.d.ts.map +1 -0
  28. package/dist/internal/embedding/mistral-embedding.d.ts.map +1 -0
  29. package/dist/internal/embedding/ollama-embedding.d.ts.map +1 -0
  30. package/dist/internal/embedding/openai-compatible.d.ts.map +1 -0
  31. package/dist/internal/embedding/openai-embedding.d.ts.map +1 -0
  32. package/dist/internal/embedding/openrouter-embedding.d.ts.map +1 -0
  33. package/dist/internal/embedding/voyage-embedding.d.ts.map +1 -0
  34. package/dist/internal/index/index-db.d.ts.map +1 -0
  35. package/dist/internal/{index-manager-contract.d.ts → index/index-manager-contract.d.ts} +1 -1
  36. package/dist/internal/index/index-manager-contract.d.ts.map +1 -0
  37. package/dist/internal/index/index-manager-dispatch.d.ts.map +1 -0
  38. package/dist/internal/{index-manager.d.ts → index/index-manager.d.ts} +1 -1
  39. package/dist/internal/index/index-manager.d.ts.map +1 -0
  40. package/dist/internal/index/index-schema.d.ts.map +1 -0
  41. package/dist/internal/{lance-index.d.ts → index/lance-index.d.ts} +1 -1
  42. package/dist/internal/index/lance-index.d.ts.map +1 -0
  43. package/dist/internal/index/lance-memory-adapter.d.ts.map +1 -0
  44. package/dist/internal/index/memory-index.d.ts.map +1 -0
  45. package/dist/internal/index/migrate-sqlite-to-lance.d.ts.map +1 -0
  46. package/dist/internal/{migration.d.ts → index/migration.d.ts} +1 -1
  47. package/dist/internal/index/migration.d.ts.map +1 -0
  48. package/dist/internal/index/sqlite-vec-loader.d.ts.map +1 -0
  49. package/dist/internal/{vec-index.d.ts → index/vec-index.d.ts} +1 -1
  50. package/dist/internal/index/vec-index.d.ts.map +1 -0
  51. package/dist/internal/memory-types.d.ts +5 -0
  52. package/dist/internal/memory-types.d.ts.map +1 -1
  53. package/dist/internal/store/chunk-markdown.d.ts.map +1 -0
  54. package/dist/internal/{markdown-store.d.ts → store/markdown-store.d.ts} +1 -1
  55. package/dist/internal/store/markdown-store.d.ts.map +1 -0
  56. package/dist/internal/store/reader.d.ts.map +1 -0
  57. package/dist/internal/store/session-loader.d.ts.map +1 -0
  58. package/dist/internal/store/session-summary-writer.d.ts.map +1 -0
  59. package/dist/internal/store/transcript-store.d.ts.map +1 -0
  60. package/dist/internal/store/wiki-loader.d.ts.map +1 -0
  61. package/dist/internal/tools.d.ts +1 -1
  62. package/dist/internal/tools.d.ts.map +1 -1
  63. package/package.json +9 -9
  64. package/dist/internal/active-memory-cache.d.ts.map +0 -1
  65. package/dist/internal/active-memory-types.d.ts.map +0 -1
  66. package/dist/internal/active-memory.d.ts.map +0 -1
  67. package/dist/internal/batch-encoder.d.ts.map +0 -1
  68. package/dist/internal/chunk-markdown.d.ts.map +0 -1
  69. package/dist/internal/composite-scorer.d.ts.map +0 -1
  70. package/dist/internal/deepinfra-embedding.d.ts.map +0 -1
  71. package/dist/internal/dreaming-diary.d.ts.map +0 -1
  72. package/dist/internal/dreaming-phases.d.ts.map +0 -1
  73. package/dist/internal/dreaming-run.d.ts.map +0 -1
  74. package/dist/internal/embedding-adapter.d.ts.map +0 -1
  75. package/dist/internal/embedding-cache.d.ts.map +0 -1
  76. package/dist/internal/index-db.d.ts.map +0 -1
  77. package/dist/internal/index-manager-contract.d.ts.map +0 -1
  78. package/dist/internal/index-manager-dispatch.d.ts.map +0 -1
  79. package/dist/internal/index-manager.d.ts.map +0 -1
  80. package/dist/internal/index-schema.d.ts.map +0 -1
  81. package/dist/internal/lance-index.d.ts.map +0 -1
  82. package/dist/internal/lance-memory-adapter.d.ts.map +0 -1
  83. package/dist/internal/markdown-store.d.ts.map +0 -1
  84. package/dist/internal/memory-index.d.ts.map +0 -1
  85. package/dist/internal/migrate-sqlite-to-lance.d.ts.map +0 -1
  86. package/dist/internal/migration.d.ts.map +0 -1
  87. package/dist/internal/mistral-embedding.d.ts.map +0 -1
  88. package/dist/internal/ollama-embedding.d.ts.map +0 -1
  89. package/dist/internal/openai-compatible.d.ts.map +0 -1
  90. package/dist/internal/openai-embedding.d.ts.map +0 -1
  91. package/dist/internal/openrouter-embedding.d.ts.map +0 -1
  92. package/dist/internal/query-analyzer.d.ts.map +0 -1
  93. package/dist/internal/reader.d.ts.map +0 -1
  94. package/dist/internal/session-loader.d.ts.map +0 -1
  95. package/dist/internal/session-summary-writer.d.ts.map +0 -1
  96. package/dist/internal/sqlite-vec-loader.d.ts.map +0 -1
  97. package/dist/internal/transcript-store.d.ts.map +0 -1
  98. package/dist/internal/vec-index.d.ts.map +0 -1
  99. package/dist/internal/voyage-embedding.d.ts.map +0 -1
  100. package/dist/internal/wiki-loader.d.ts.map +0 -1
  101. /package/dist/internal/{active-memory-cache.d.ts → active-memory/active-memory-cache.d.ts} +0 -0
  102. /package/dist/internal/{composite-scorer.d.ts → active-memory/composite-scorer.d.ts} +0 -0
  103. /package/dist/internal/{query-analyzer.d.ts → active-memory/query-analyzer.d.ts} +0 -0
  104. /package/dist/internal/{dreaming-diary.d.ts → dreaming/dreaming-diary.d.ts} +0 -0
  105. /package/dist/internal/{batch-encoder.d.ts → embedding/batch-encoder.d.ts} +0 -0
  106. /package/dist/internal/{deepinfra-embedding.d.ts → embedding/deepinfra-embedding.d.ts} +0 -0
  107. /package/dist/internal/{embedding-adapter.d.ts → embedding/embedding-adapter.d.ts} +0 -0
  108. /package/dist/internal/{embedding-cache.d.ts → embedding/embedding-cache.d.ts} +0 -0
  109. /package/dist/internal/{mistral-embedding.d.ts → embedding/mistral-embedding.d.ts} +0 -0
  110. /package/dist/internal/{ollama-embedding.d.ts → embedding/ollama-embedding.d.ts} +0 -0
  111. /package/dist/internal/{openai-compatible.d.ts → embedding/openai-compatible.d.ts} +0 -0
  112. /package/dist/internal/{openai-embedding.d.ts → embedding/openai-embedding.d.ts} +0 -0
  113. /package/dist/internal/{openrouter-embedding.d.ts → embedding/openrouter-embedding.d.ts} +0 -0
  114. /package/dist/internal/{voyage-embedding.d.ts → embedding/voyage-embedding.d.ts} +0 -0
  115. /package/dist/internal/{index-db.d.ts → index/index-db.d.ts} +0 -0
  116. /package/dist/internal/{index-manager-dispatch.d.ts → index/index-manager-dispatch.d.ts} +0 -0
  117. /package/dist/internal/{index-schema.d.ts → index/index-schema.d.ts} +0 -0
  118. /package/dist/internal/{lance-memory-adapter.d.ts → index/lance-memory-adapter.d.ts} +0 -0
  119. /package/dist/internal/{memory-index.d.ts → index/memory-index.d.ts} +0 -0
  120. /package/dist/internal/{migrate-sqlite-to-lance.d.ts → index/migrate-sqlite-to-lance.d.ts} +0 -0
  121. /package/dist/internal/{sqlite-vec-loader.d.ts → index/sqlite-vec-loader.d.ts} +0 -0
  122. /package/dist/internal/{chunk-markdown.d.ts → store/chunk-markdown.d.ts} +0 -0
  123. /package/dist/internal/{reader.d.ts → store/reader.d.ts} +0 -0
  124. /package/dist/internal/{session-loader.d.ts → store/session-loader.d.ts} +0 -0
  125. /package/dist/internal/{session-summary-writer.d.ts → store/session-summary-writer.d.ts} +0 -0
  126. /package/dist/internal/{transcript-store.d.ts → store/transcript-store.d.ts} +0 -0
  127. /package/dist/internal/{wiki-loader.d.ts → store/wiki-loader.d.ts} +0 -0
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
- import { mkdir, readFile, readdir, rename, stat, access, unlink } from 'fs/promises';
2
- import { join, resolve, dirname, relative } from 'path';
1
+ import { mkdir, readFile, readdir, stat, access, unlink } from 'fs/promises';
2
+ import { join, resolve, relative } from 'path';
3
3
  import { Security } from '@theokit/sdk';
4
- import { replaceFileAtomic, withCwdMutex, atomicWriteJson, applyWalWithFallback, sanitizeFts5Query } from '@theokit/sdk/internal/persistence';
4
+ import { replaceFileAtomic, withCwdMutex, atomicWriteJson, openSqliteResilient, sanitizeFts5Query } from '@theokit/sdk/internal/persistence';
5
5
  import { sanitizeIdentifier, safePathJoin } from '@theokit/sdk/path-safety';
6
6
  import { createHash } from 'crypto';
7
7
  import { AuthenticationError, RateLimitError, ConfigurationError, NetworkError, UnknownAgentError } from '@theokit/sdk/errors';
@@ -249,7 +249,7 @@ function legacyMemoryJsonPath(cwd, config) {
249
249
  return safePathJoin(cwd, ".theokit", "memory", namespace, `${scope}-${userId}.json`);
250
250
  }
251
251
 
252
- // src/internal/markdown-store.ts
252
+ // src/internal/store/markdown-store.ts
253
253
  var MEMORY_MD_HEADER = "# Memory\n\n> Auto-managed by @theokit/sdk. Edit freely \u2014 the SDK reads from here.\n";
254
254
  var FACTS_HEADING = "## Facts";
255
255
  function memoryDir(cwd) {
@@ -340,7 +340,7 @@ async function appendFact(cwd, config, fact) {
340
340
  await appendFactToMarkdown(cwd, fact);
341
341
  }
342
342
 
343
- // src/internal/transcript-store.ts
343
+ // src/internal/store/transcript-store.ts
344
344
  async function persistActiveMemoryTranscript(cwd, transcript) {
345
345
  try {
346
346
  const dir = join(memoryDir(cwd), "transcripts", "active-memory");
@@ -353,7 +353,7 @@ async function persistActiveMemoryTranscript(cwd, transcript) {
353
353
  }
354
354
  }
355
355
 
356
- // src/internal/active-memory.ts
356
+ // src/internal/active-memory/active-memory.ts
357
357
  var SPAN_MEMORY_RECALL = "memory.recall";
358
358
  var HISTOGRAM_MEMORY_RECALL_DURATION_MS = "theokit_memory_recall_duration_ms";
359
359
  var DEFAULTS = {
@@ -702,7 +702,7 @@ function formatMessage(providerId, status, code) {
702
702
  return `${providerId} API error: ${code} (HTTP ${status})`;
703
703
  }
704
704
 
705
- // src/internal/embedding-cache.ts
705
+ // src/internal/embedding/embedding-cache.ts
706
706
  var LruEmbeddingCache = class {
707
707
  constructor(capacity = 5e3) {
708
708
  this.capacity = capacity;
@@ -729,7 +729,7 @@ var LruEmbeddingCache = class {
729
729
  }
730
730
  };
731
731
 
732
- // src/internal/openai-compatible.ts
732
+ // src/internal/embedding/openai-compatible.ts
733
733
  var MAX_BATCH = 100;
734
734
  var MAX_RETRIES = 2;
735
735
  async function createOpenAiCompatibleRuntime(cfg, options) {
@@ -900,7 +900,7 @@ function sleep(ms) {
900
900
  return new Promise((resolve) => setTimeout(resolve, ms));
901
901
  }
902
902
 
903
- // src/internal/deepinfra-embedding.ts
903
+ // src/internal/embedding/deepinfra-embedding.ts
904
904
  var DEFAULT_DEEPINFRA_EMBEDDING_MODEL = "BAAI/bge-large-en-v1.5";
905
905
  var DIMENSION_BY_MODEL = {
906
906
  "BAAI/bge-large-en-v1.5": 1024,
@@ -934,7 +934,7 @@ var deepinfraMemoryEmbeddingProviderAdapter = {
934
934
  )
935
935
  };
936
936
 
937
- // src/internal/mistral-embedding.ts
937
+ // src/internal/embedding/mistral-embedding.ts
938
938
  var DEFAULT_MISTRAL_EMBEDDING_MODEL = "mistral-embed";
939
939
  var DIMENSION_BY_MODEL2 = {
940
940
  "mistral-embed": 1024
@@ -958,7 +958,7 @@ var mistralMemoryEmbeddingProviderAdapter = {
958
958
  )
959
959
  };
960
960
 
961
- // src/internal/ollama-embedding.ts
961
+ // src/internal/embedding/ollama-embedding.ts
962
962
  var DEFAULT_OLLAMA_EMBEDDING_MODEL = "nomic-embed-text";
963
963
  var OLLAMA_LOCAL_SENTINEL_KEY = "ollama-local";
964
964
  var DIMENSION_BY_MODEL3 = {
@@ -1001,7 +1001,7 @@ var ollamaMemoryEmbeddingProviderAdapter = {
1001
1001
  }
1002
1002
  };
1003
1003
 
1004
- // src/internal/openai-embedding.ts
1004
+ // src/internal/embedding/openai-embedding.ts
1005
1005
  var DEFAULT_OPENAI_EMBEDDING_MODEL = "text-embedding-3-small";
1006
1006
  var DIMENSION_BY_MODEL4 = {
1007
1007
  "text-embedding-3-small": 1536,
@@ -1027,7 +1027,7 @@ var openAiMemoryEmbeddingProviderAdapter = {
1027
1027
  )
1028
1028
  };
1029
1029
 
1030
- // src/internal/openrouter-embedding.ts
1030
+ // src/internal/embedding/openrouter-embedding.ts
1031
1031
  var DEFAULT_OPENROUTER_EMBEDDING_MODEL = "openai/text-embedding-3-small";
1032
1032
  var DIMENSION_BY_MODEL5 = {
1033
1033
  "openai/text-embedding-3-small": 1536,
@@ -1054,7 +1054,7 @@ var openRouterMemoryEmbeddingProviderAdapter = {
1054
1054
  )
1055
1055
  };
1056
1056
 
1057
- // src/internal/voyage-embedding.ts
1057
+ // src/internal/embedding/voyage-embedding.ts
1058
1058
  var DEFAULT_VOYAGE_EMBEDDING_MODEL = "voyage-3-lite";
1059
1059
  var DIMENSION_BY_MODEL6 = {
1060
1060
  "voyage-3-lite": 512,
@@ -1091,80 +1091,131 @@ var MEMORY_EMBEDDING_ADAPTERS = {
1091
1091
  deepinfra: deepinfraMemoryEmbeddingProviderAdapter,
1092
1092
  ollama: ollamaMemoryEmbeddingProviderAdapter
1093
1093
  };
1094
- var HEADING_RE = /^(#{1,6})\s+(.+?)\s*$/;
1095
- function chunkMarkdown(text, options = {}) {
1096
- const maxChars = options.maxChars ?? 800;
1097
- if (text.length === 0) return [];
1098
- const lines = text.split("\n");
1099
- const acc = { chunks: [], lines: [], start: 1, heading: void 0 };
1100
- for (let i = 0; i < lines.length; i++) {
1101
- const line = lines[i] ?? "";
1102
- const lineNumber = i + 1;
1103
- processLine(acc, line, lineNumber, maxChars);
1094
+ var FACTS_HEADING2 = "## Facts";
1095
+ function createCategorizedMemory(options) {
1096
+ const { root, categories } = options;
1097
+ validateCategories(categories);
1098
+ const allowed = new Set(categories);
1099
+ function assertKnown(category) {
1100
+ if (!allowed.has(category)) {
1101
+ throw new ConfigurationError(
1102
+ `createCategorizedMemory: unknown category ${JSON.stringify(category)} (declared: ${categories.join(", ")})`,
1103
+ { code: "unknown_category" }
1104
+ );
1105
+ }
1104
1106
  }
1105
- flushAccumulator(acc, lines.length, maxChars);
1106
- return acc.chunks;
1107
- }
1108
- function processLine(acc, line, lineNumber, maxChars) {
1109
- const headingMatch = HEADING_RE.exec(line);
1110
- const isBlank = line.trim().length === 0;
1111
- if (headingMatch !== null && acc.lines.length > 0) {
1112
- flushAccumulator(acc, lineNumber - 1, maxChars);
1107
+ function categoryPath(category) {
1108
+ return safePathJoin(root, `${sanitizeIdentifier(category)}.md`);
1109
+ }
1110
+ async function add(category, text) {
1111
+ assertKnown(category);
1112
+ const encoded = encodeFact(redactSecrets(text));
1113
+ const sane = sanitizeIdentifier(category);
1114
+ await withCwdMutex(`catmem:${root}:${sane}`, async () => {
1115
+ const path = safePathJoin(root, `${sane}.md`);
1116
+ const raw = await readFileOrEmpty(path) || header(category);
1117
+ await mkdir(root, { recursive: true });
1118
+ await replaceFileAtomic(path, appendBullet(raw, encoded));
1119
+ });
1113
1120
  }
1114
- if (headingMatch !== null) acc.heading = headingMatch[2];
1115
- acc.lines.push(line);
1116
- if (isBlank && acc.lines.length > 1) {
1117
- const joined = acc.lines.join("\n");
1118
- if (joined.trim().length > 0) flushAccumulator(acc, lineNumber, maxChars);
1121
+ async function list(category) {
1122
+ if (category !== void 0) {
1123
+ assertKnown(category);
1124
+ return readCategory(categoryPath(category), category);
1125
+ }
1126
+ const out = [];
1127
+ for (const c of categories) {
1128
+ out.push(...await readCategory(categoryPath(c), c));
1129
+ }
1130
+ return out;
1119
1131
  }
1132
+ return { categories: [...categories], add, list };
1120
1133
  }
1121
- function flushAccumulator(acc, endLineExclusive, maxChars) {
1122
- if (acc.lines.length === 0) return;
1123
- const chunkText = acc.lines.join("\n");
1124
- if (chunkText.trim().length === 0) {
1125
- acc.lines = [];
1126
- acc.start = endLineExclusive + 1;
1127
- return;
1134
+ function validateCategories(categories) {
1135
+ if (categories.length === 0) {
1136
+ throw new ConfigurationError("createCategorizedMemory: categories must be non-empty", {
1137
+ code: "invalid_categories"
1138
+ });
1128
1139
  }
1129
- if (chunkText.length > maxChars) {
1130
- pushOversizedSlices(acc.chunks, chunkText, acc.start, acc.heading, maxChars);
1131
- } else {
1132
- acc.chunks.push(buildChunk(chunkText, acc.start, endLineExclusive, acc.heading));
1140
+ if (new Set(categories).size !== categories.length) {
1141
+ throw new ConfigurationError("createCategorizedMemory: categories must be unique", {
1142
+ code: "invalid_categories"
1143
+ });
1144
+ }
1145
+ const sanitized = categories.map((c) => trySanitize(c));
1146
+ if (new Set(sanitized).size !== categories.length) {
1147
+ throw new ConfigurationError(
1148
+ "createCategorizedMemory: categories must map to distinct filenames after sanitization",
1149
+ { code: "invalid_categories" }
1150
+ );
1133
1151
  }
1134
- acc.lines = [];
1135
- acc.start = endLineExclusive + 1;
1136
- }
1137
- function buildChunk(text, startLine, endLine, heading) {
1138
- return {
1139
- startLine,
1140
- endLine,
1141
- text,
1142
- hash: createHash("sha256").update(text).digest("hex"),
1143
- ...heading !== void 0 ? { heading } : {}
1144
- };
1145
1152
  }
1146
- function pushOversizedSlices(chunks, text, startLine, heading, maxChars) {
1147
- let remaining = text;
1148
- let lineCursor = startLine;
1149
- while (remaining.length > maxChars) {
1150
- const splitAt = findWordBoundarySplit(remaining, maxChars);
1151
- const slice = remaining.slice(0, splitAt);
1152
- const sliceLines = slice.split("\n").length;
1153
- chunks.push(buildChunk(slice, lineCursor, lineCursor + sliceLines - 1, heading));
1154
- remaining = remaining.slice(splitAt).replace(/^\s+/, "");
1155
- lineCursor += sliceLines - 1;
1153
+ function trySanitize(category) {
1154
+ try {
1155
+ return sanitizeIdentifier(category);
1156
+ } catch (cause) {
1157
+ throw new ConfigurationError(
1158
+ `createCategorizedMemory: category ${JSON.stringify(category)} is not a valid identifier`,
1159
+ { code: "invalid_categories", cause }
1160
+ );
1156
1161
  }
1157
- if (remaining.length > 0) {
1158
- const sliceLines = remaining.split("\n").length;
1159
- chunks.push(buildChunk(remaining, lineCursor, lineCursor + sliceLines - 1, heading));
1162
+ }
1163
+ function header(category) {
1164
+ return `---
1165
+ category: ${category}
1166
+ ---
1167
+
1168
+ ${FACTS_HEADING2}
1169
+ `;
1170
+ }
1171
+ async function readFileOrEmpty(path) {
1172
+ try {
1173
+ return await readFile(path, "utf8");
1174
+ } catch {
1175
+ return "";
1160
1176
  }
1161
1177
  }
1162
- function findWordBoundarySplit(text, maxChars) {
1163
- if (text.length <= maxChars) return text.length;
1164
- for (let i = maxChars; i > Math.max(0, maxChars - 200); i--) {
1165
- if (/\s/.test(text[i] ?? "x")) return i;
1178
+ async function readCategory(path, category) {
1179
+ const raw = await readFileOrEmpty(path);
1180
+ return parseFactBullets(raw).map((text) => ({ text: decodeFact(text), category }));
1181
+ }
1182
+ function encodeFact(text) {
1183
+ return text.replace(/\\/g, "\\\\").replace(/\r/g, "\\r").replace(/\n/g, "\\n");
1184
+ }
1185
+ function decodeFact(text) {
1186
+ return text.replace(
1187
+ /\\(.)/g,
1188
+ (_match, char) => char === "n" ? "\n" : char === "r" ? "\r" : char
1189
+ );
1190
+ }
1191
+ function parseFactBullets(raw) {
1192
+ const idx = raw.indexOf(FACTS_HEADING2);
1193
+ if (idx === -1) return [];
1194
+ const tail = raw.slice(idx + FACTS_HEADING2.length);
1195
+ const nextHeading = tail.search(/\n#{1,2}\s/);
1196
+ const block = nextHeading === -1 ? tail : tail.slice(0, nextHeading);
1197
+ return block.split("\n").map((line) => line.trim()).filter((line) => line.startsWith("- ")).map((line) => line.slice(2).trim());
1198
+ }
1199
+ function appendBullet(raw, text) {
1200
+ const bullet = `- ${text}`;
1201
+ const idx = raw.indexOf(FACTS_HEADING2);
1202
+ if (idx === -1) {
1203
+ const sep = raw.endsWith("\n") ? "" : "\n";
1204
+ return `${raw}${sep}${FACTS_HEADING2}
1205
+
1206
+ ${bullet}
1207
+ `;
1166
1208
  }
1167
- return maxChars;
1209
+ const after = idx + FACTS_HEADING2.length;
1210
+ const nextHeading = raw.slice(after).search(/\n#{1,2}\s/);
1211
+ if (nextHeading === -1) {
1212
+ const trailing = raw.endsWith("\n") ? "" : "\n";
1213
+ return `${raw}${trailing}${bullet}
1214
+ `;
1215
+ }
1216
+ const insertAt = after + nextHeading;
1217
+ return `${raw.slice(0, insertAt)}
1218
+ ${bullet}${raw.slice(insertAt)}`;
1168
1219
  }
1169
1220
  function diaryPath(cwd) {
1170
1221
  return join(memoryDir(cwd), "dream-diary.md");
@@ -1208,7 +1259,7 @@ function entryHash(entry) {
1208
1259
  ).digest("hex");
1209
1260
  }
1210
1261
 
1211
- // src/internal/dreaming-phases.ts
1262
+ // src/internal/dreaming/dreaming-phases.ts
1212
1263
  var DEFAULT_DEDUP_THRESHOLD = 0.95;
1213
1264
  var DEFAULT_CLUSTER_THRESHOLD = 0.75;
1214
1265
  async function lightPhase(facts, embedding, threshold = DEFAULT_DEDUP_THRESHOLD) {
@@ -1366,7 +1417,7 @@ function emptyResult(status) {
1366
1417
  };
1367
1418
  }
1368
1419
 
1369
- // src/internal/index-schema.ts
1420
+ // src/internal/index/index-schema.ts
1370
1421
  var SCHEMA_STATEMENTS = [
1371
1422
  `CREATE TABLE IF NOT EXISTS files (
1372
1423
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -1412,57 +1463,180 @@ var PRAGMA_STATEMENTS = [
1412
1463
  "PRAGMA foreign_keys=ON"
1413
1464
  ];
1414
1465
 
1415
- // src/internal/index-db.ts
1466
+ // src/internal/index/index-db.ts
1416
1467
  async function openMemoryDb(opts) {
1417
- await mkdir(dirname(opts.filePath), { recursive: true });
1418
- try {
1419
- return await openConcrete(opts.filePath);
1420
- } catch (cause) {
1421
- if (opts.recoverCorrupt !== false && isCorruptionError(cause)) {
1422
- await renameAside(opts.filePath);
1423
- return await openConcrete(opts.filePath);
1468
+ return openSqliteResilient({
1469
+ filePath: opts.filePath,
1470
+ label: "memory-index",
1471
+ recoverCorrupt: opts.recoverCorrupt,
1472
+ onOpen: (db) => {
1473
+ for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
1474
+ for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
1424
1475
  }
1425
- throw cause;
1476
+ });
1477
+ }
1478
+ function defaultIndexPath(cwd) {
1479
+ return join(cwd, ".theokit", "memory", ".index", "memory.sqlite");
1480
+ }
1481
+ var HEADING_RE = /^(#{1,6})\s+(.+?)\s*$/;
1482
+ function chunkMarkdown(text, options = {}) {
1483
+ const maxChars = options.maxChars ?? 800;
1484
+ if (text.length === 0) return [];
1485
+ const lines = text.split("\n");
1486
+ const acc = { chunks: [], lines: [], start: 1, heading: void 0 };
1487
+ for (let i = 0; i < lines.length; i++) {
1488
+ const line = lines[i] ?? "";
1489
+ const lineNumber = i + 1;
1490
+ processLine(acc, line, lineNumber, maxChars);
1426
1491
  }
1492
+ flushAccumulator(acc, lines.length, maxChars);
1493
+ return acc.chunks;
1427
1494
  }
1428
- async function openConcrete(filePath) {
1429
- const db = await loadDriver(filePath);
1430
- applyWalWithFallback(db, "memory-index");
1431
- for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
1432
- for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
1433
- return db;
1495
+ function processLine(acc, line, lineNumber, maxChars) {
1496
+ const headingMatch = HEADING_RE.exec(line);
1497
+ const isBlank = line.trim().length === 0;
1498
+ if (headingMatch !== null && acc.lines.length > 0) {
1499
+ flushAccumulator(acc, lineNumber - 1, maxChars);
1500
+ }
1501
+ if (headingMatch !== null) acc.heading = headingMatch[2];
1502
+ acc.lines.push(line);
1503
+ if (isBlank && acc.lines.length > 1) {
1504
+ const joined = acc.lines.join("\n");
1505
+ if (joined.trim().length > 0) flushAccumulator(acc, lineNumber, maxChars);
1506
+ }
1507
+ }
1508
+ function flushAccumulator(acc, endLineExclusive, maxChars) {
1509
+ if (acc.lines.length === 0) return;
1510
+ const chunkText = acc.lines.join("\n");
1511
+ if (chunkText.trim().length === 0) {
1512
+ acc.lines = [];
1513
+ acc.start = endLineExclusive + 1;
1514
+ return;
1515
+ }
1516
+ if (chunkText.length > maxChars) {
1517
+ pushOversizedSlices(acc.chunks, chunkText, acc.start, acc.heading, maxChars);
1518
+ } else {
1519
+ acc.chunks.push(buildChunk(chunkText, acc.start, endLineExclusive, acc.heading));
1520
+ }
1521
+ acc.lines = [];
1522
+ acc.start = endLineExclusive + 1;
1523
+ }
1524
+ function buildChunk(text, startLine, endLine, heading) {
1525
+ return {
1526
+ startLine,
1527
+ endLine,
1528
+ text,
1529
+ hash: createHash("sha256").update(text).digest("hex"),
1530
+ ...heading !== void 0 ? { heading } : {}
1531
+ };
1434
1532
  }
1435
- async function loadDriver(filePath) {
1533
+ function pushOversizedSlices(chunks, text, startLine, heading, maxChars) {
1534
+ let remaining = text;
1535
+ let lineCursor = startLine;
1536
+ while (remaining.length > maxChars) {
1537
+ const splitAt = findWordBoundarySplit(remaining, maxChars);
1538
+ const slice = remaining.slice(0, splitAt);
1539
+ const sliceLines = slice.split("\n").length;
1540
+ chunks.push(buildChunk(slice, lineCursor, lineCursor + sliceLines - 1, heading));
1541
+ remaining = remaining.slice(splitAt).replace(/^\s+/, "");
1542
+ lineCursor += sliceLines - 1;
1543
+ }
1544
+ if (remaining.length > 0) {
1545
+ const sliceLines = remaining.split("\n").length;
1546
+ chunks.push(buildChunk(remaining, lineCursor, lineCursor + sliceLines - 1, heading));
1547
+ }
1548
+ }
1549
+ function findWordBoundarySplit(text, maxChars) {
1550
+ if (text.length <= maxChars) return text.length;
1551
+ for (let i = maxChars; i > Math.max(0, maxChars - 200); i--) {
1552
+ if (/\s/.test(text[i] ?? "x")) return i;
1553
+ }
1554
+ return maxChars;
1555
+ }
1556
+ var MAX_TURN_CHARS = 2e3;
1557
+ function sessionsDir(cwd) {
1558
+ return join(memoryDir(cwd), "sessions");
1559
+ }
1560
+ function sessionSummaryPath(cwd, runId) {
1561
+ return join(sessionsDir(cwd), `${sanitizeRunId2(runId)}.md`);
1562
+ }
1563
+ function sanitizeRunId2(runId) {
1564
+ return runId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 128);
1565
+ }
1566
+ function truncate2(text) {
1567
+ if (text.length <= MAX_TURN_CHARS) return text;
1568
+ return `${text.slice(0, MAX_TURN_CHARS)}\u2026`;
1569
+ }
1570
+ async function writeSessionSummary(input) {
1571
+ if (input.status !== "finished") return;
1572
+ const path = sessionSummaryPath(input.cwd, input.runId);
1573
+ await mkdir(sessionsDir(input.cwd), { recursive: true });
1574
+ const safeUser = redactSecrets(truncate2(input.userText));
1575
+ const safeAssistant = redactSecrets(truncate2(input.assistantText));
1576
+ const iso = new Date(input.at).toISOString();
1577
+ const body = [
1578
+ "---",
1579
+ `runId: ${input.runId}`,
1580
+ `agentId: ${input.agentId}`,
1581
+ `at: ${iso}`,
1582
+ `status: ${input.status}`,
1583
+ "---",
1584
+ "",
1585
+ "## User",
1586
+ "",
1587
+ safeUser,
1588
+ "",
1589
+ "## Assistant",
1590
+ "",
1591
+ safeAssistant,
1592
+ ""
1593
+ ].join("\n");
1594
+ await replaceFileAtomic(path, body);
1595
+ }
1596
+
1597
+ // src/internal/store/session-loader.ts
1598
+ async function discoverSessionFiles(cwd) {
1599
+ let entries;
1436
1600
  try {
1437
- const mod = await import('better-sqlite3');
1438
- const Ctor = mod.default ?? mod;
1439
- const db = new Ctor(filePath);
1440
- return db;
1441
- } catch (cause) {
1442
- const message = cause instanceof Error ? cause.message : String(cause);
1443
- throw new ConfigurationError(
1444
- `Failed to load SQLite driver. Install \`better-sqlite3\` or run on Node 22.5+ for built-in \`node:sqlite\`. Cause: ${message}`,
1445
- { code: "sqlite_driver_unavailable", cause }
1446
- );
1601
+ entries = await readdir(sessionsDir(cwd));
1602
+ } catch {
1603
+ return [];
1447
1604
  }
1605
+ const root = memoryDir(cwd);
1606
+ return entries.filter((entry) => entry.endsWith(".md")).map((entry) => {
1607
+ const absolutePath = join(sessionsDir(cwd), entry);
1608
+ return {
1609
+ absolutePath,
1610
+ relPath: relativeToRoot(root, absolutePath)
1611
+ };
1612
+ });
1448
1613
  }
1449
- function isCorruptionError(cause) {
1450
- if (!(cause instanceof Error)) return false;
1451
- const msg = cause.message.toLowerCase();
1452
- return msg.includes("malformed") || msg.includes("not a database") || msg.includes("encrypted") || msg.includes("disk image is malformed");
1614
+ function relativeToRoot(root, absolutePath) {
1615
+ if (absolutePath.startsWith(`${root}/`)) return absolutePath.slice(root.length + 1);
1616
+ return absolutePath;
1453
1617
  }
1454
- async function renameAside(filePath) {
1455
- const asidePath = `${filePath}.corrupt-${Date.now()}`;
1456
- await rename(filePath, asidePath).catch(() => void 0);
1457
- await rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => void 0);
1458
- await rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => void 0);
1459
- process.stderr.write(
1460
- `[theokit-sdk] memory index corrupt; renamed aside to ${asidePath} and rebuilt schema
1461
- `
1462
- );
1618
+ function wikiDir(cwd) {
1619
+ return join(memoryDir(cwd), "wiki");
1463
1620
  }
1464
- function defaultIndexPath(cwd) {
1465
- return join(cwd, ".theokit", "memory", ".index", "memory.sqlite");
1621
+ async function discoverWikiFiles(cwd) {
1622
+ let entries;
1623
+ try {
1624
+ entries = await readdir(wikiDir(cwd));
1625
+ } catch {
1626
+ return [];
1627
+ }
1628
+ const root = memoryDir(cwd);
1629
+ return entries.filter((entry) => entry.endsWith(".md")).map((entry) => ({
1630
+ absolutePath: join(wikiDir(cwd), entry),
1631
+ relPath: join("wiki", entry)
1632
+ })).map((file) => ({
1633
+ absolutePath: file.absolutePath,
1634
+ relPath: relativeToRoot2(root, file.absolutePath)
1635
+ }));
1636
+ }
1637
+ function relativeToRoot2(root, absolutePath) {
1638
+ if (absolutePath.startsWith(`${root}/`)) return absolutePath.slice(root.length + 1);
1639
+ return absolutePath;
1466
1640
  }
1467
1641
  function requireLance() {
1468
1642
  try {
@@ -1592,7 +1766,7 @@ function lanceStoragePath(cwd) {
1592
1766
  return join(cwd, ".theokit", "memory", "lance");
1593
1767
  }
1594
1768
 
1595
- // src/internal/memory-index.ts
1769
+ // src/internal/index/memory-index.ts
1596
1770
  function parseSearchOptions(options = {}) {
1597
1771
  return {
1598
1772
  maxResults: Math.max(1, options.maxResults ?? 10),
@@ -1600,7 +1774,7 @@ function parseSearchOptions(options = {}) {
1600
1774
  };
1601
1775
  }
1602
1776
 
1603
- // src/internal/lance-memory-adapter.ts
1777
+ // src/internal/index/lance-memory-adapter.ts
1604
1778
  var DEFAULT_NAMESPACE = "default";
1605
1779
  var EMPTY_SYNC_RESULT = Object.freeze({
1606
1780
  filesScanned: 0,
@@ -1665,7 +1839,7 @@ function translateLanceHit(hit) {
1665
1839
  };
1666
1840
  }
1667
1841
 
1668
- // src/internal/index-manager-dispatch.ts
1842
+ // src/internal/index/index-manager-dispatch.ts
1669
1843
  var VALID_BACKENDS = ["sqlite-vec", "lance"];
1670
1844
  function assertValidBackend(backend) {
1671
1845
  if (!VALID_BACKENDS.includes(backend)) {
@@ -1689,68 +1863,6 @@ async function openLanceIndex(opts) {
1689
1863
  });
1690
1864
  return new LanceMemoryAdapter(lance);
1691
1865
  }
1692
- var MAX_TURN_CHARS = 2e3;
1693
- function sessionsDir(cwd) {
1694
- return join(memoryDir(cwd), "sessions");
1695
- }
1696
- function sessionSummaryPath(cwd, runId) {
1697
- return join(sessionsDir(cwd), `${sanitizeRunId2(runId)}.md`);
1698
- }
1699
- function sanitizeRunId2(runId) {
1700
- return runId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 128);
1701
- }
1702
- function truncate2(text) {
1703
- if (text.length <= MAX_TURN_CHARS) return text;
1704
- return `${text.slice(0, MAX_TURN_CHARS)}\u2026`;
1705
- }
1706
- async function writeSessionSummary(input) {
1707
- if (input.status !== "finished") return;
1708
- const path = sessionSummaryPath(input.cwd, input.runId);
1709
- await mkdir(sessionsDir(input.cwd), { recursive: true });
1710
- const safeUser = redactSecrets(truncate2(input.userText));
1711
- const safeAssistant = redactSecrets(truncate2(input.assistantText));
1712
- const iso = new Date(input.at).toISOString();
1713
- const body = [
1714
- "---",
1715
- `runId: ${input.runId}`,
1716
- `agentId: ${input.agentId}`,
1717
- `at: ${iso}`,
1718
- `status: ${input.status}`,
1719
- "---",
1720
- "",
1721
- "## User",
1722
- "",
1723
- safeUser,
1724
- "",
1725
- "## Assistant",
1726
- "",
1727
- safeAssistant,
1728
- ""
1729
- ].join("\n");
1730
- await replaceFileAtomic(path, body);
1731
- }
1732
-
1733
- // src/internal/session-loader.ts
1734
- async function discoverSessionFiles(cwd) {
1735
- let entries;
1736
- try {
1737
- entries = await readdir(sessionsDir(cwd));
1738
- } catch {
1739
- return [];
1740
- }
1741
- const root = memoryDir(cwd);
1742
- return entries.filter((entry) => entry.endsWith(".md")).map((entry) => {
1743
- const absolutePath = join(sessionsDir(cwd), entry);
1744
- return {
1745
- absolutePath,
1746
- relPath: relativeToRoot(root, absolutePath)
1747
- };
1748
- });
1749
- }
1750
- function relativeToRoot(root, absolutePath) {
1751
- if (absolutePath.startsWith(`${root}/`)) return absolutePath.slice(root.length + 1);
1752
- return absolutePath;
1753
- }
1754
1866
  async function loadSqliteVecExtension(db) {
1755
1867
  try {
1756
1868
  const mod = await import('sqlite-vec');
@@ -1776,7 +1888,7 @@ function isSqliteVecLoaded(db) {
1776
1888
  }
1777
1889
  }
1778
1890
 
1779
- // src/internal/vec-index.ts
1891
+ // src/internal/index/vec-index.ts
1780
1892
  var META_KEY_PROVIDER_ID = "embedding.providerId";
1781
1893
  var META_KEY_MODEL = "embedding.model";
1782
1894
  var META_KEY_DIMENSION = "embedding.dimension";
@@ -1849,31 +1961,8 @@ async function embedMissingChunks(args) {
1849
1961
  }
1850
1962
  return normalized.length;
1851
1963
  }
1852
- function wikiDir(cwd) {
1853
- return join(memoryDir(cwd), "wiki");
1854
- }
1855
- async function discoverWikiFiles(cwd) {
1856
- let entries;
1857
- try {
1858
- entries = await readdir(wikiDir(cwd));
1859
- } catch {
1860
- return [];
1861
- }
1862
- const root = memoryDir(cwd);
1863
- return entries.filter((entry) => entry.endsWith(".md")).map((entry) => ({
1864
- absolutePath: join(wikiDir(cwd), entry),
1865
- relPath: join("wiki", entry)
1866
- })).map((file) => ({
1867
- absolutePath: file.absolutePath,
1868
- relPath: relativeToRoot2(root, file.absolutePath)
1869
- }));
1870
- }
1871
- function relativeToRoot2(root, absolutePath) {
1872
- if (absolutePath.startsWith(`${root}/`)) return absolutePath.slice(root.length + 1);
1873
- return absolutePath;
1874
- }
1875
1964
 
1876
- // src/internal/index-manager.ts
1965
+ // src/internal/index/index-manager.ts
1877
1966
  var IndexManager = class _IndexManager {
1878
1967
  constructor(cwd, db, embedding) {
1879
1968
  this.cwd = cwd;
@@ -2495,6 +2584,6 @@ function isPathInside(root, candidate) {
2495
2584
  return candidate === root || candidate.startsWith(normalizedRoot);
2496
2585
  }
2497
2586
 
2498
- export { ActiveMemoryCache, CircuitBreaker, DEFAULT_DEEPINFRA_EMBEDDING_MODEL, DEFAULT_MEMORY_READ_LINES, DEFAULT_MISTRAL_EMBEDDING_MODEL, DEFAULT_OLLAMA_EMBEDDING_MODEL, DEFAULT_OPENAI_EMBEDDING_MODEL, DEFAULT_OPENROUTER_EMBEDDING_MODEL, DEFAULT_VOYAGE_EMBEDDING_MODEL, IndexManager, LanceIndex, LanceMemoryAdapter, MEMORY_EMBEDDING_ADAPTERS, META_KEY_DIMENSION, META_KEY_MODEL, META_KEY_PROVIDER_ID, MIGRATION_V2_STATEMENTS, PRAGMA_STATEMENTS, SCHEMA_STATEMENTS, VALID_BACKENDS, appendDiaryEntry, appendFact, appendFactToMarkdown, assertValidBackend, buildErrorMetadata, chunkMarkdown, createInMemoryMarkdownProvider, createMemoryGetTool, createMemorySearchTool, createOpenAiCompatibleRuntime, createVectorIndex, deepPhase, deepinfraMemoryEmbeddingProviderAdapter, defaultIndexPath, diaryPath, discoverSessionFiles, discoverWikiFiles, dropVectorIndex, embedMissingChunks, entryHash, identityMatches, isLanceAvailable, isSqliteVecLoaded, lanceStoragePath, legacyMemoryJsonPath, lightPhase, listNotes, loadSqliteVecExtension, mapOpenAICompatibleError, memoryDir, memoryMdPath, migrateLegacyJson, migrateSqliteToLance, mistralMemoryEmbeddingProviderAdapter, notesDir, ollamaMemoryEmbeddingProviderAdapter, openAiMemoryEmbeddingProviderAdapter, openLanceIndex, openMemoryDb, openRouterMemoryEmbeddingProviderAdapter, packVector, parseRetryAfter, parseSearchOptions, persistActiveMemoryTranscript, readEmbeddingIdentity, readFacts, readFactsFromMarkdown, readMemoryFileBounded, redactSecrets, remPhase, renderDiaryEntry, resetMigrationStateForTests, runActiveMemory, runDreamingSweep, sessionSummaryPath, sessionsDir, truncateRaw, upsertEmbedding, vectorSearch, voyageMemoryEmbeddingProviderAdapter, wikiDir, writeEmbeddingIdentity, writeSessionSummary };
2587
+ export { ActiveMemoryCache, CircuitBreaker, DEFAULT_DEEPINFRA_EMBEDDING_MODEL, DEFAULT_MEMORY_READ_LINES, DEFAULT_MISTRAL_EMBEDDING_MODEL, DEFAULT_OLLAMA_EMBEDDING_MODEL, DEFAULT_OPENAI_EMBEDDING_MODEL, DEFAULT_OPENROUTER_EMBEDDING_MODEL, DEFAULT_VOYAGE_EMBEDDING_MODEL, IndexManager, LanceIndex, LanceMemoryAdapter, MEMORY_EMBEDDING_ADAPTERS, META_KEY_DIMENSION, META_KEY_MODEL, META_KEY_PROVIDER_ID, MIGRATION_V2_STATEMENTS, PRAGMA_STATEMENTS, SCHEMA_STATEMENTS, VALID_BACKENDS, appendDiaryEntry, appendFact, appendFactToMarkdown, assertValidBackend, buildErrorMetadata, chunkMarkdown, createCategorizedMemory, createInMemoryMarkdownProvider, createMemoryGetTool, createMemorySearchTool, createOpenAiCompatibleRuntime, createVectorIndex, deepPhase, deepinfraMemoryEmbeddingProviderAdapter, defaultIndexPath, diaryPath, discoverSessionFiles, discoverWikiFiles, dropVectorIndex, embedMissingChunks, entryHash, identityMatches, isLanceAvailable, isSqliteVecLoaded, lanceStoragePath, legacyMemoryJsonPath, lightPhase, listNotes, loadSqliteVecExtension, mapOpenAICompatibleError, memoryDir, memoryMdPath, migrateLegacyJson, migrateSqliteToLance, mistralMemoryEmbeddingProviderAdapter, notesDir, ollamaMemoryEmbeddingProviderAdapter, openAiMemoryEmbeddingProviderAdapter, openLanceIndex, openMemoryDb, openRouterMemoryEmbeddingProviderAdapter, packVector, parseRetryAfter, parseSearchOptions, persistActiveMemoryTranscript, readEmbeddingIdentity, readFacts, readFactsFromMarkdown, readMemoryFileBounded, redactSecrets, remPhase, renderDiaryEntry, resetMigrationStateForTests, runActiveMemory, runDreamingSweep, sessionSummaryPath, sessionsDir, truncateRaw, upsertEmbedding, vectorSearch, voyageMemoryEmbeddingProviderAdapter, wikiDir, writeEmbeddingIdentity, writeSessionSummary };
2499
2588
  //# sourceMappingURL=index.js.map
2500
2589
  //# sourceMappingURL=index.js.map