@theokit/sdk-memory 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.
Files changed (99) hide show
  1. package/CHANGELOG.md +112 -0
  2. package/LICENSE +201 -0
  3. package/README.md +86 -0
  4. package/dist/in-memory-provider.d.ts +38 -0
  5. package/dist/in-memory-provider.d.ts.map +1 -0
  6. package/dist/index.cjs +2585 -0
  7. package/dist/index.cjs.map +1 -0
  8. package/dist/index.d.cts +40 -0
  9. package/dist/index.d.ts +40 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +2500 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/internal/active-memory-cache.d.ts +49 -0
  14. package/dist/internal/active-memory-cache.d.ts.map +1 -0
  15. package/dist/internal/active-memory-types.d.ts +30 -0
  16. package/dist/internal/active-memory-types.d.ts.map +1 -0
  17. package/dist/internal/active-memory.d.ts +108 -0
  18. package/dist/internal/active-memory.d.ts.map +1 -0
  19. package/dist/internal/adapter-catalog.d.ts +82 -0
  20. package/dist/internal/adapter-catalog.d.ts.map +1 -0
  21. package/dist/internal/adapter-http-error.d.ts +63 -0
  22. package/dist/internal/adapter-http-error.d.ts.map +1 -0
  23. package/dist/internal/batch-encoder.d.ts +30 -0
  24. package/dist/internal/batch-encoder.d.ts.map +1 -0
  25. package/dist/internal/chunk-markdown.d.ts +66 -0
  26. package/dist/internal/chunk-markdown.d.ts.map +1 -0
  27. package/dist/internal/circuit-breaker.d.ts +36 -0
  28. package/dist/internal/circuit-breaker.d.ts.map +1 -0
  29. package/dist/internal/composite-scorer.d.ts +19 -0
  30. package/dist/internal/composite-scorer.d.ts.map +1 -0
  31. package/dist/internal/deepinfra-embedding.d.ts +25 -0
  32. package/dist/internal/deepinfra-embedding.d.ts.map +1 -0
  33. package/dist/internal/dreaming-diary.d.ts +38 -0
  34. package/dist/internal/dreaming-diary.d.ts.map +1 -0
  35. package/dist/internal/dreaming-phases.d.ts +43 -0
  36. package/dist/internal/dreaming-phases.d.ts.map +1 -0
  37. package/dist/internal/dreaming-run.d.ts +49 -0
  38. package/dist/internal/dreaming-run.d.ts.map +1 -0
  39. package/dist/internal/embedding-adapter.d.ts +57 -0
  40. package/dist/internal/embedding-adapter.d.ts.map +1 -0
  41. package/dist/internal/embedding-cache.d.ts +16 -0
  42. package/dist/internal/embedding-cache.d.ts.map +1 -0
  43. package/dist/internal/index-db.d.ts +50 -0
  44. package/dist/internal/index-db.d.ts.map +1 -0
  45. package/dist/internal/index-manager-contract.d.ts +70 -0
  46. package/dist/internal/index-manager-contract.d.ts.map +1 -0
  47. package/dist/internal/index-manager-dispatch.d.ts +36 -0
  48. package/dist/internal/index-manager-dispatch.d.ts.map +1 -0
  49. package/dist/internal/index-manager.d.ts +94 -0
  50. package/dist/internal/index-manager.d.ts.map +1 -0
  51. package/dist/internal/index-schema.d.ts +35 -0
  52. package/dist/internal/index-schema.d.ts.map +1 -0
  53. package/dist/internal/lance-index.d.ts +70 -0
  54. package/dist/internal/lance-index.d.ts.map +1 -0
  55. package/dist/internal/lance-memory-adapter.d.ts +102 -0
  56. package/dist/internal/lance-memory-adapter.d.ts.map +1 -0
  57. package/dist/internal/markdown-store.d.ts +17 -0
  58. package/dist/internal/markdown-store.d.ts.map +1 -0
  59. package/dist/internal/memory-index.d.ts +68 -0
  60. package/dist/internal/memory-index.d.ts.map +1 -0
  61. package/dist/internal/memory-scope.d.ts +21 -0
  62. package/dist/internal/memory-scope.d.ts.map +1 -0
  63. package/dist/internal/memory-types.d.ts +95 -0
  64. package/dist/internal/memory-types.d.ts.map +1 -0
  65. package/dist/internal/migrate-sqlite-to-lance.d.ts +49 -0
  66. package/dist/internal/migrate-sqlite-to-lance.d.ts.map +1 -0
  67. package/dist/internal/migration.d.ts +10 -0
  68. package/dist/internal/migration.d.ts.map +1 -0
  69. package/dist/internal/mistral-embedding.d.ts +23 -0
  70. package/dist/internal/mistral-embedding.d.ts.map +1 -0
  71. package/dist/internal/ollama-embedding.d.ts +48 -0
  72. package/dist/internal/ollama-embedding.d.ts.map +1 -0
  73. package/dist/internal/openai-compatible.d.ts +24 -0
  74. package/dist/internal/openai-compatible.d.ts.map +1 -0
  75. package/dist/internal/openai-embedding.d.ts +34 -0
  76. package/dist/internal/openai-embedding.d.ts.map +1 -0
  77. package/dist/internal/openrouter-embedding.d.ts +25 -0
  78. package/dist/internal/openrouter-embedding.d.ts.map +1 -0
  79. package/dist/internal/query-analyzer.d.ts +16 -0
  80. package/dist/internal/query-analyzer.d.ts.map +1 -0
  81. package/dist/internal/reader.d.ts +64 -0
  82. package/dist/internal/reader.d.ts.map +1 -0
  83. package/dist/internal/session-loader.d.ts +25 -0
  84. package/dist/internal/session-loader.d.ts.map +1 -0
  85. package/dist/internal/session-summary-writer.d.ts +43 -0
  86. package/dist/internal/session-summary-writer.d.ts.map +1 -0
  87. package/dist/internal/sqlite-vec-loader.d.ts +62 -0
  88. package/dist/internal/sqlite-vec-loader.d.ts.map +1 -0
  89. package/dist/internal/tools.d.ts +44 -0
  90. package/dist/internal/tools.d.ts.map +1 -0
  91. package/dist/internal/transcript-store.d.ts +36 -0
  92. package/dist/internal/transcript-store.d.ts.map +1 -0
  93. package/dist/internal/vec-index.d.ts +81 -0
  94. package/dist/internal/vec-index.d.ts.map +1 -0
  95. package/dist/internal/voyage-embedding.d.ts +23 -0
  96. package/dist/internal/voyage-embedding.d.ts.map +1 -0
  97. package/dist/internal/wiki-loader.d.ts +24 -0
  98. package/dist/internal/wiki-loader.d.ts.map +1 -0
  99. package/package.json +74 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,2585 @@
1
+ 'use strict';
2
+
3
+ var promises = require('fs/promises');
4
+ var path = require('path');
5
+ var sdk = require('@theokit/sdk');
6
+ var persistence = require('@theokit/sdk/internal/persistence');
7
+ var pathSafety = require('@theokit/sdk/path-safety');
8
+ var crypto = require('crypto');
9
+ var errors = require('@theokit/sdk/errors');
10
+ var fs = require('fs');
11
+ var module$1 = require('module');
12
+
13
+ var _documentCurrentScript = typeof document !== 'undefined' ? document.currentScript : null;
14
+ // src/in-memory-provider.ts
15
+ var ADAPTER_ID = "in-memory-md";
16
+ var INTERNAL_STATE = /* @__PURE__ */ Symbol("sdk-memory.in-memory.state");
17
+ function buildCapabilities() {
18
+ return {
19
+ history: false,
20
+ sessions: false,
21
+ tenancy: false,
22
+ reasoning: false,
23
+ toolSchemas: false,
24
+ prefetch: false
25
+ };
26
+ }
27
+ function buildAdapter(state) {
28
+ return {
29
+ id: ADAPTER_ID,
30
+ capabilities: buildCapabilities(),
31
+ isAvailable() {
32
+ return true;
33
+ },
34
+ async write(content, _ctx) {
35
+ const text = typeof content === "string" ? content : content.map((m) => m.content).join("\n");
36
+ const safeText = sdk.Security.redact(text);
37
+ const id = `${ADAPTER_ID}:${state.counter.next++}`;
38
+ state.facts.set(id, {
39
+ id,
40
+ content: safeText,
41
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
42
+ });
43
+ return id;
44
+ },
45
+ async recall(_query, _ctx, k) {
46
+ const all = Array.from(state.facts.values());
47
+ const limit = k !== void 0 && k >= 0 ? k : all.length;
48
+ return all.slice(0, limit);
49
+ },
50
+ async delete(id) {
51
+ if (!String(id).startsWith(`${ADAPTER_ID}:`)) {
52
+ throw new Error(
53
+ `MemoryAdapter.delete: id "${String(id)}" was not minted by adapter "${ADAPTER_ID}"`
54
+ );
55
+ }
56
+ state.facts.delete(id);
57
+ }
58
+ };
59
+ }
60
+ var MAX_SUMMARY_TURN_CHARS = 2e3;
61
+ function truncate(text) {
62
+ if (text.length <= MAX_SUMMARY_TURN_CHARS) return text;
63
+ return `${text.slice(0, MAX_SUMMARY_TURN_CHARS)}\u2026`;
64
+ }
65
+ function sanitizeRunId(runId) {
66
+ return runId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 128);
67
+ }
68
+ function renderSessionSummaryMarkdown(args) {
69
+ const iso = new Date(args.at).toISOString();
70
+ const safeUser = sdk.Security.redact(truncate(args.userText));
71
+ const safeAssistant = sdk.Security.redact(truncate(args.assistantText));
72
+ return [
73
+ "---",
74
+ `runId: ${args.runId}`,
75
+ `agentId: ${args.agentId}`,
76
+ `at: ${iso}`,
77
+ `status: ${args.status}`,
78
+ "---",
79
+ "",
80
+ "## User",
81
+ "",
82
+ safeUser,
83
+ "",
84
+ "## Assistant",
85
+ "",
86
+ safeAssistant,
87
+ ""
88
+ ].join("\n");
89
+ }
90
+ function extractAgentIdFromFrontmatter(content) {
91
+ const match = content.match(/^agentId:\s*(.+)$/m);
92
+ if (match === null || match[1] === void 0) return void 0;
93
+ return match[1].trim();
94
+ }
95
+ async function recallSessionSummaries(cwd, userMessage, agentIdFilter) {
96
+ const RECALL_CAP = 5;
97
+ const sessionsDir2 = path.join(cwd, ".theokit", "memory", "sessions");
98
+ let files;
99
+ try {
100
+ files = await promises.readdir(sessionsDir2);
101
+ } catch {
102
+ return [];
103
+ }
104
+ const query = userMessage.toLowerCase().trim();
105
+ if (query.length === 0) return [];
106
+ const hits = [];
107
+ for (const file of files) {
108
+ if (!file.endsWith(".md")) continue;
109
+ if (hits.length >= RECALL_CAP) break;
110
+ const filePath = path.join(sessionsDir2, file);
111
+ let content;
112
+ try {
113
+ content = await promises.readFile(filePath, "utf-8");
114
+ } catch {
115
+ continue;
116
+ }
117
+ if (agentIdFilter !== void 0) {
118
+ const fileAgentId = extractAgentIdFromFrontmatter(content);
119
+ if (fileAgentId !== agentIdFilter) continue;
120
+ }
121
+ if (content.toLowerCase().includes(query)) {
122
+ const userIdx = content.indexOf("## User");
123
+ const snippet = userIdx >= 0 ? content.slice(userIdx, userIdx + 200).replace(/\s+/g, " ") : file;
124
+ const id = `${ADAPTER_ID}:session:${file}`;
125
+ hits.push({
126
+ id,
127
+ content: snippet,
128
+ score: 0.5
129
+ // substring-match has no real score; placeholder
130
+ });
131
+ }
132
+ }
133
+ return hits;
134
+ }
135
+ function buildMemorySearchTool(state) {
136
+ return {
137
+ name: "memory_search",
138
+ description: "Search past session summaries for content related to a query. Use when the user asks about something they mentioned in a previous conversation. Returns up to 5 short snippets.",
139
+ inputSchema: {
140
+ type: "object",
141
+ properties: {
142
+ query: {
143
+ type: "string",
144
+ description: "What to search for (a topic, fact, or keyword)."
145
+ }
146
+ },
147
+ required: ["query"],
148
+ additionalProperties: false
149
+ },
150
+ handler: async (input) => {
151
+ const query = typeof input.query === "string" ? input.query : "";
152
+ if (query.length === 0) {
153
+ return JSON.stringify({ ok: false, results: [], error: "empty query" });
154
+ }
155
+ const hits = await recallSessionSummaries(state.cwd, query);
156
+ return JSON.stringify({
157
+ ok: true,
158
+ count: hits.length,
159
+ results: hits.map((h) => ({ id: String(h.id), snippet: h.content }))
160
+ });
161
+ }
162
+ };
163
+ }
164
+ function buildMemoryRememberTool(state) {
165
+ return {
166
+ name: "memory_remember",
167
+ description: "Persist a short fact about the user/conversation to memory so it can be recalled in future turns. Pass a single concise sentence as `content`.",
168
+ inputSchema: {
169
+ type: "object",
170
+ properties: {
171
+ content: { type: "string", description: "The fact to remember (one sentence)." }
172
+ },
173
+ required: ["content"],
174
+ additionalProperties: false
175
+ },
176
+ handler: async (input) => {
177
+ const content = typeof input.content === "string" ? input.content : "";
178
+ if (content.length === 0) {
179
+ return JSON.stringify({ ok: false, error: "empty content" });
180
+ }
181
+ const safeContent = sdk.Security.redact(content);
182
+ const id = `${ADAPTER_ID}:${state.counter.next++}`;
183
+ state.facts.set(id, {
184
+ id,
185
+ content: safeContent,
186
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
187
+ });
188
+ return JSON.stringify({ ok: true, id });
189
+ }
190
+ };
191
+ }
192
+ function createInMemoryMarkdownProvider() {
193
+ return {
194
+ async init(opts) {
195
+ const state = {
196
+ facts: /* @__PURE__ */ new Map(),
197
+ counter: { next: 0 },
198
+ cwd: opts.cwd
199
+ };
200
+ const adapter = buildAdapter(state);
201
+ return {
202
+ adapter,
203
+ [INTERNAL_STATE]: state
204
+ };
205
+ },
206
+ buildTools(handle, _agent) {
207
+ const state = handle[INTERNAL_STATE];
208
+ if (state === void 0) return [];
209
+ return [buildMemoryRememberTool(state), buildMemorySearchTool(state)];
210
+ },
211
+ async runActivePass(handle, args) {
212
+ const state = handle[INTERNAL_STATE];
213
+ if (state === void 0) return { facts: [] };
214
+ const inProcessFacts = Array.from(state.facts.values());
215
+ const persistedHits = await recallSessionSummaries(state.cwd, args.userMessage, args.agentId);
216
+ const allFacts = [...inProcessFacts, ...persistedHits];
217
+ if (allFacts.length === 0) return { facts: [] };
218
+ const systemPromptAdditions = [
219
+ "Known facts about the user/session (recall):",
220
+ ...allFacts.map((f) => `- ${f.content}`)
221
+ ].join("\n");
222
+ return { facts: allFacts, systemPromptAdditions };
223
+ },
224
+ sync(_handle) {
225
+ return;
226
+ },
227
+ async recordSessionSummary(args) {
228
+ if (args.status !== "finished") return;
229
+ const sessionsPath = path.join(args.cwd, ".theokit", "memory", "sessions");
230
+ const filePath = path.join(sessionsPath, `${sanitizeRunId(args.runId)}.md`);
231
+ await promises.mkdir(sessionsPath, { recursive: true });
232
+ const body = renderSessionSummaryMarkdown(args);
233
+ await persistence.replaceFileAtomic(filePath, body);
234
+ },
235
+ dispose(handle) {
236
+ const state = handle[INTERNAL_STATE];
237
+ if (state === void 0) return;
238
+ state.facts.clear();
239
+ }
240
+ };
241
+ }
242
+ function redactSecrets(text, opts) {
243
+ return sdk.Security.redact(text, opts);
244
+ }
245
+ function legacyMemoryJsonPath(cwd, config) {
246
+ if (config.storePath !== void 0) {
247
+ return path.resolve(cwd, config.storePath);
248
+ }
249
+ const namespace = pathSafety.sanitizeIdentifier(config.namespace ?? "default");
250
+ const scope = pathSafety.sanitizeIdentifier(config.scope ?? "agent", { maxLen: 16 });
251
+ const userId = pathSafety.sanitizeIdentifier(config.userId ?? "default");
252
+ return pathSafety.safePathJoin(cwd, ".theokit", "memory", namespace, `${scope}-${userId}.json`);
253
+ }
254
+
255
+ // src/internal/markdown-store.ts
256
+ var MEMORY_MD_HEADER = "# Memory\n\n> Auto-managed by @theokit/sdk. Edit freely \u2014 the SDK reads from here.\n";
257
+ var FACTS_HEADING = "## Facts";
258
+ function memoryDir(cwd) {
259
+ return path.join(cwd, ".theokit", "memory");
260
+ }
261
+ function memoryMdPath(cwd) {
262
+ return path.join(memoryDir(cwd), "MEMORY.md");
263
+ }
264
+ function notesDir(cwd) {
265
+ return path.join(memoryDir(cwd), "notes");
266
+ }
267
+ async function readFactsFromMarkdown(cwd) {
268
+ let raw;
269
+ try {
270
+ raw = await promises.readFile(memoryMdPath(cwd), "utf8");
271
+ } catch {
272
+ return [];
273
+ }
274
+ return parseFactsSection(raw);
275
+ }
276
+ function appendFactToMarkdown(cwd, fact) {
277
+ return persistence.withCwdMutex(memoryDir(cwd), async () => {
278
+ const path = memoryMdPath(cwd);
279
+ let raw = "";
280
+ try {
281
+ raw = await promises.readFile(path, "utf8");
282
+ } catch {
283
+ raw = "";
284
+ }
285
+ const sanitized = redactSecrets(fact.text);
286
+ const next = insertFactBullet(raw, sanitized);
287
+ await promises.mkdir(memoryDir(cwd), { recursive: true });
288
+ await persistence.replaceFileAtomic(path, next);
289
+ });
290
+ }
291
+ async function listNotes(cwd) {
292
+ let entries = [];
293
+ try {
294
+ entries = await promises.readdir(notesDir(cwd));
295
+ } catch {
296
+ return [];
297
+ }
298
+ return entries.filter((name) => name.endsWith(".md")).map((name) => ({ slug: name.replace(/\.md$/, ""), path: path.join(notesDir(cwd), name) }));
299
+ }
300
+ function parseFactsSection(raw) {
301
+ const idx = raw.indexOf(FACTS_HEADING);
302
+ if (idx === -1) return [];
303
+ const tail = raw.slice(idx + FACTS_HEADING.length);
304
+ const nextHeading = tail.search(/\n#{1,2}\s/);
305
+ const block = nextHeading === -1 ? tail : tail.slice(0, nextHeading);
306
+ return block.split("\n").map((line) => line.trim()).filter((line) => line.startsWith("- ")).map((line) => ({ text: line.slice(2).trim() }));
307
+ }
308
+ function insertFactBullet(raw, fact) {
309
+ const bullet = `- ${fact}`;
310
+ if (raw.length === 0) {
311
+ return `${MEMORY_MD_HEADER}
312
+ ${FACTS_HEADING}
313
+
314
+ ${bullet}
315
+ `;
316
+ }
317
+ const idx = raw.indexOf(FACTS_HEADING);
318
+ if (idx === -1) {
319
+ const sep = raw.endsWith("\n") ? "" : "\n";
320
+ return `${raw}${sep}
321
+ ${FACTS_HEADING}
322
+
323
+ ${bullet}
324
+ `;
325
+ }
326
+ const after = idx + FACTS_HEADING.length;
327
+ const nextHeading = raw.slice(after).search(/\n#{1,2}\s/);
328
+ if (nextHeading === -1) {
329
+ const trailing = raw.endsWith("\n") ? "" : "\n";
330
+ return `${raw}${trailing}${bullet}
331
+ `;
332
+ }
333
+ const insertAt = after + nextHeading;
334
+ return `${raw.slice(0, insertAt)}
335
+ ${bullet}${raw.slice(insertAt)}`;
336
+ }
337
+ async function readFacts(cwd, config) {
338
+ if (!config.enabled) return [];
339
+ return readFactsFromMarkdown(cwd);
340
+ }
341
+ async function appendFact(cwd, config, fact) {
342
+ if (!config.enabled) return;
343
+ await appendFactToMarkdown(cwd, fact);
344
+ }
345
+
346
+ // src/internal/transcript-store.ts
347
+ async function persistActiveMemoryTranscript(cwd, transcript) {
348
+ try {
349
+ const dir = path.join(memoryDir(cwd), "transcripts", "active-memory");
350
+ const file = path.join(dir, `${transcript.runId}.json`);
351
+ await persistence.atomicWriteJson(file, transcript);
352
+ } catch (cause) {
353
+ const message = cause instanceof Error ? cause.message : String(cause);
354
+ process.stderr.write(`[theokit-sdk] active-memory transcript persist failed: ${message}
355
+ `);
356
+ }
357
+ }
358
+
359
+ // src/internal/active-memory.ts
360
+ var SPAN_MEMORY_RECALL = "memory.recall";
361
+ var HISTOGRAM_MEMORY_RECALL_DURATION_MS = "theokit_memory_recall_duration_ms";
362
+ var DEFAULTS = {
363
+ queryMode: "recent",
364
+ timeoutMs: 15e3,
365
+ maxSummaryChars: 220,
366
+ recentUserTurns: 2
367
+ };
368
+ async function runActiveMemory(args) {
369
+ const started = Date.now();
370
+ const span = startMemoryRecallSpan(args);
371
+ try {
372
+ if (args.options.enabled !== true || args.index === void 0) {
373
+ return endRecallSpan(span, args, {
374
+ summary: void 0,
375
+ durationMs: 0,
376
+ status: "skipped",
377
+ hits: []
378
+ });
379
+ }
380
+ const cfg = { ...DEFAULTS, ...args.options };
381
+ const breakerKey = args.agentKey ?? "default";
382
+ if (args.breaker?.shouldSkip(breakerKey) === true) {
383
+ return endRecallSpan(span, args, {
384
+ summary: void 0,
385
+ durationMs: 0,
386
+ status: "skipped",
387
+ hits: []
388
+ });
389
+ }
390
+ const cached = args.cache?.get(args.userText, cfg.queryMode);
391
+ if (cached !== void 0) return endRecallSpan(span, args, cached);
392
+ const query = buildQuery(args.userText, args.priorMessages, cfg.queryMode, cfg.recentUserTurns);
393
+ if (query.trim().length === 0) {
394
+ const finalized2 = await finalize(args, cfg.queryMode, {
395
+ summary: void 0,
396
+ durationMs: Date.now() - started,
397
+ status: "no-recall",
398
+ hits: []
399
+ });
400
+ return endRecallSpan(span, args, finalized2);
401
+ }
402
+ const result = await searchOnce(args, cfg, query, started);
403
+ notifyBreaker(args.breaker, breakerKey, result.status);
404
+ const finalized = await finalize(args, cfg.queryMode, result);
405
+ return endRecallSpan(span, args, finalized);
406
+ } catch (cause) {
407
+ span.recordException(cause);
408
+ span.setStatus({ code: 2, message: cause instanceof Error ? cause.message : String(cause) });
409
+ span.end();
410
+ throw cause;
411
+ }
412
+ }
413
+ function startMemoryRecallSpan(args) {
414
+ const telemetry = args.telemetry;
415
+ if (telemetry === void 0) {
416
+ return {
417
+ setAttribute: () => {
418
+ },
419
+ setAttributes: () => {
420
+ },
421
+ addEvent: () => {
422
+ },
423
+ setStatus: () => {
424
+ },
425
+ recordException: () => {
426
+ },
427
+ end: () => {
428
+ },
429
+ spanContext: () => ({ traceId: "0".repeat(32), spanId: "0".repeat(16) }),
430
+ isRecording: () => false
431
+ };
432
+ }
433
+ return telemetry.startSpan(SPAN_MEMORY_RECALL, {
434
+ queryMode: args.options.queryMode ?? "recent",
435
+ ...args.userId !== void 0 ? { userId: args.userId } : {},
436
+ ...args.namespace !== void 0 ? { namespace: args.namespace } : {},
437
+ ...args.scope !== void 0 ? { scope: args.scope } : {}
438
+ });
439
+ }
440
+ function endRecallSpan(span, args, result) {
441
+ span.setAttributes({
442
+ hits: result.hits.length,
443
+ status: result.status,
444
+ durationMs: result.durationMs
445
+ });
446
+ span.end();
447
+ args.telemetry?.recordHistogram(HISTOGRAM_MEMORY_RECALL_DURATION_MS, result.durationMs, {
448
+ status: result.status,
449
+ ...args.userId !== void 0 ? { userId: args.userId } : {},
450
+ ...args.namespace !== void 0 ? { namespace: args.namespace } : {},
451
+ ...args.scope !== void 0 ? { scope: args.scope } : {}
452
+ });
453
+ return result;
454
+ }
455
+ async function searchOnce(args, cfg, query, started) {
456
+ if (args.index === void 0) {
457
+ return { summary: void 0, durationMs: 0, status: "skipped", hits: [] };
458
+ }
459
+ try {
460
+ const hits = await withTimeout(args.index.search(query, { maxResults: 5 }), cfg.timeoutMs);
461
+ if (hits === "timeout") {
462
+ return { summary: void 0, durationMs: Date.now() - started, status: "timeout", hits: [] };
463
+ }
464
+ if (hits.length === 0) {
465
+ return {
466
+ summary: void 0,
467
+ durationMs: Date.now() - started,
468
+ status: "no-recall",
469
+ hits: []
470
+ };
471
+ }
472
+ const summary = formatSummary(hits, cfg.maxSummaryChars);
473
+ return { summary, durationMs: Date.now() - started, status: "ok", hits };
474
+ } catch (cause) {
475
+ const message = cause instanceof Error ? cause.message : String(cause);
476
+ process.stderr.write(`[theokit-sdk] active-memory recall failed: ${message}
477
+ `);
478
+ return { summary: void 0, durationMs: Date.now() - started, status: "error", hits: [] };
479
+ }
480
+ }
481
+ function notifyBreaker(breaker, key, status) {
482
+ if (breaker === void 0) return;
483
+ if (status === "timeout") breaker.recordTimeout(key);
484
+ else if (status === "ok" || status === "no-recall") breaker.recordSuccess(key);
485
+ }
486
+ async function finalize(args, queryMode, result) {
487
+ args.cache?.set(args.userText, queryMode, result);
488
+ if (args.persistTranscripts === true && args.cwd !== void 0) {
489
+ await persistActiveMemoryTranscript(args.cwd, {
490
+ runId: args.runId ?? `run-${Date.now()}`,
491
+ startedAtMs: Date.now() - result.durationMs,
492
+ userText: args.userText,
493
+ queryMode,
494
+ status: result.status,
495
+ durationMs: result.durationMs,
496
+ summary: result.summary,
497
+ hits: result.hits.map((h) => ({
498
+ path: h.path,
499
+ startLine: h.startLine,
500
+ endLine: h.endLine,
501
+ score: h.score,
502
+ snippet: h.snippet
503
+ }))
504
+ });
505
+ }
506
+ return result;
507
+ }
508
+ function buildQuery(userText, priorMessages, queryMode, recentUserTurns) {
509
+ if (queryMode === "message") return userText;
510
+ if (queryMode === "full") {
511
+ const allUserTurns = priorMessages.filter((m) => m.role === "user").map((m) => m.text);
512
+ return [...allUserTurns, userText].join("\n");
513
+ }
514
+ const recent = priorMessages.filter((m) => m.role === "user").slice(-recentUserTurns).map((m) => m.text);
515
+ return [...recent, userText].join("\n");
516
+ }
517
+ function formatSummary(hits, maxChars) {
518
+ const lines = hits.map((h) => `- ${h.citation}: ${h.snippet}`);
519
+ let summary = lines.join("\n");
520
+ if (summary.length > maxChars) summary = `${summary.slice(0, maxChars - 1)}\u2026`;
521
+ return summary;
522
+ }
523
+ async function withTimeout(promise, timeoutMs) {
524
+ let timer;
525
+ const timeout = new Promise((resolve) => {
526
+ timer = setTimeout(() => resolve("timeout"), timeoutMs);
527
+ });
528
+ try {
529
+ const winner = await Promise.race([promise, timeout]);
530
+ return winner;
531
+ } finally {
532
+ if (timer !== void 0) clearTimeout(timer);
533
+ }
534
+ }
535
+
536
+ // src/internal/circuit-breaker.ts
537
+ var DEFAULT_MAX_TIMEOUTS = 3;
538
+ var DEFAULT_COOLDOWN_MS = 6e4;
539
+ var CircuitBreaker = class {
540
+ constructor(opts = {}) {
541
+ this.opts = opts;
542
+ }
543
+ opts;
544
+ states = /* @__PURE__ */ new Map();
545
+ /** @returns true when the breaker is open and the call should be skipped. */
546
+ shouldSkip(key) {
547
+ const state = this.states.get(key);
548
+ if (state === void 0) return false;
549
+ if (state.cooldownUntilMs === 0) return false;
550
+ if (this.now() < state.cooldownUntilMs) return true;
551
+ state.cooldownUntilMs = 0;
552
+ state.consecutiveTimeouts = 0;
553
+ return false;
554
+ }
555
+ recordSuccess(key) {
556
+ const state = this.states.get(key);
557
+ if (state === void 0) return;
558
+ state.consecutiveTimeouts = 0;
559
+ state.cooldownUntilMs = 0;
560
+ }
561
+ recordTimeout(key) {
562
+ const state = this.states.get(key) ?? { consecutiveTimeouts: 0, cooldownUntilMs: 0 };
563
+ state.consecutiveTimeouts += 1;
564
+ if (state.consecutiveTimeouts >= (this.opts.maxTimeouts ?? DEFAULT_MAX_TIMEOUTS)) {
565
+ state.cooldownUntilMs = this.now() + (this.opts.cooldownMs ?? DEFAULT_COOLDOWN_MS);
566
+ }
567
+ this.states.set(key, state);
568
+ }
569
+ /** @internal — tests inspect counter state. */
570
+ inspect(key) {
571
+ return this.states.get(key) ?? { consecutiveTimeouts: 0, cooldownUntilMs: 0 };
572
+ }
573
+ now() {
574
+ return this.opts.now?.() ?? Date.now();
575
+ }
576
+ };
577
+ var DEFAULT_TTL_MS = 15e3;
578
+ var DEFAULT_CAPACITY = 1e3;
579
+ var ActiveMemoryCache = class {
580
+ constructor(opts = {}) {
581
+ this.opts = opts;
582
+ }
583
+ opts;
584
+ map = /* @__PURE__ */ new Map();
585
+ get(userText, queryMode, tenantCtx) {
586
+ const key = cacheKey(userText, queryMode, tenantCtx);
587
+ const entry = this.map.get(key);
588
+ if (entry === void 0) return void 0;
589
+ if (this.now() >= entry.expiresAtMs) {
590
+ this.map.delete(key);
591
+ return void 0;
592
+ }
593
+ this.map.delete(key);
594
+ this.map.set(key, entry);
595
+ return entry.result;
596
+ }
597
+ set(userText, queryMode, result, tenantCtx) {
598
+ const key = cacheKey(userText, queryMode, tenantCtx);
599
+ if (this.map.size >= (this.opts.capacity ?? DEFAULT_CAPACITY)) {
600
+ const oldest = this.map.keys().next().value;
601
+ if (oldest !== void 0) this.map.delete(oldest);
602
+ }
603
+ this.map.set(key, {
604
+ expiresAtMs: this.now() + (this.opts.ttlMs ?? DEFAULT_TTL_MS),
605
+ result
606
+ });
607
+ }
608
+ size() {
609
+ return this.map.size;
610
+ }
611
+ now() {
612
+ return this.opts.now?.() ?? Date.now();
613
+ }
614
+ };
615
+ function cacheKey(userText, queryMode, ctx) {
616
+ const parts = [queryMode, ctx?.namespace ?? "", ctx?.userId ?? "", ctx?.scope ?? "", userText];
617
+ return crypto.createHash("sha256").update(parts.join("\0")).digest("hex");
618
+ }
619
+ var RAW_MAX_BYTES = 2048;
620
+ function parseRetryAfter(headers) {
621
+ if (headers === void 0) return void 0;
622
+ const raw = headers.get("retry-after");
623
+ if (raw === null) return void 0;
624
+ const n = Number(raw);
625
+ if (Number.isFinite(n) && n >= 0) return Math.ceil(n);
626
+ return void 0;
627
+ }
628
+ function truncateRaw(body) {
629
+ if (body === null || body === void 0) return void 0;
630
+ const s = typeof body === "string" ? body : JSON.stringify(body);
631
+ const truncated = s.length <= RAW_MAX_BYTES ? s : `${s.slice(0, RAW_MAX_BYTES)}\u2026`;
632
+ return redactSecrets(truncated);
633
+ }
634
+ function buildErrorMetadata(args) {
635
+ const retryAfter = parseRetryAfter(args.headers);
636
+ const raw = truncateRaw(args.body);
637
+ return {
638
+ provider: args.provider,
639
+ endpoint: args.endpoint,
640
+ code: args.code,
641
+ statusCode: args.status,
642
+ ...retryAfter !== void 0 ? { retryAfter } : {},
643
+ ...raw !== void 0 ? { raw } : {}
644
+ };
645
+ }
646
+ function mapOpenAICompatibleError(args) {
647
+ const { providerId, status, body, headers, endpoint } = args;
648
+ const code = mapOpenAiStatusToCode(status, body);
649
+ const message = formatMessage(providerId, status, code);
650
+ const metadata = buildErrorMetadata({
651
+ provider: providerId,
652
+ endpoint,
653
+ code,
654
+ status,
655
+ headers,
656
+ body
657
+ });
658
+ if (status === 401 || status === 403) {
659
+ return new errors.AuthenticationError(message, { code: `${providerId}_auth_failed`, metadata });
660
+ }
661
+ if (status === 429) {
662
+ return new errors.RateLimitError(message, { code: `${providerId}_rate_limit`, metadata });
663
+ }
664
+ if (status === 400) {
665
+ return new errors.ConfigurationError(message, { code: `${providerId}_${code}`, metadata });
666
+ }
667
+ if (status === 408) {
668
+ return new errors.NetworkError(message, { code: `${providerId}_timeout`, metadata });
669
+ }
670
+ if (status >= 500 && status < 600) {
671
+ return new errors.NetworkError(message, { code: `${providerId}_server_error`, metadata });
672
+ }
673
+ return new errors.UnknownAgentError(message, { code: `${providerId}_unknown`, metadata });
674
+ }
675
+ function extractOpenAiErrorCode(body) {
676
+ if (body === null || typeof body !== "object") return void 0;
677
+ const err = body.error;
678
+ if (err === void 0 || err === null) return void 0;
679
+ if (typeof err.code === "string") return err.code;
680
+ if (typeof err.type === "string") return err.type;
681
+ return void 0;
682
+ }
683
+ function mapOpenAiStatusToCode(status, body) {
684
+ const rawCode = extractOpenAiErrorCode(body)?.toLowerCase() ?? "";
685
+ if (rawCode.includes("context_length") || rawCode.includes("context_window") || rawCode.includes("too_many_tokens") || rawCode.includes("max_tokens")) {
686
+ return "context_too_long";
687
+ }
688
+ if (rawCode.includes("content_filter") || rawCode.includes("content_policy") || rawCode.includes("safety")) {
689
+ return "content_filtered";
690
+ }
691
+ if (rawCode.includes("model_not_found") || rawCode.includes("model_unavailable") || rawCode.includes("invalid_model")) {
692
+ return "model_unavailable";
693
+ }
694
+ if (status === 402 || rawCode.includes("insufficient_quota") || rawCode.includes("quota_exceeded")) {
695
+ return "quota_exceeded";
696
+ }
697
+ if (status === 401 || status === 403) return "auth_failed";
698
+ if (status === 429) return "rate_limit";
699
+ if (status === 408) return "timeout";
700
+ if (status === 400) return "invalid_request";
701
+ if (status >= 500 && status < 600) return "server_error";
702
+ return "unknown";
703
+ }
704
+ function formatMessage(providerId, status, code) {
705
+ return `${providerId} API error: ${code} (HTTP ${status})`;
706
+ }
707
+
708
+ // src/internal/embedding-cache.ts
709
+ var LruEmbeddingCache = class {
710
+ constructor(capacity = 5e3) {
711
+ this.capacity = capacity;
712
+ }
713
+ capacity;
714
+ map = /* @__PURE__ */ new Map();
715
+ get(key) {
716
+ const value = this.map.get(key);
717
+ if (value === void 0) return void 0;
718
+ this.map.delete(key);
719
+ this.map.set(key, value);
720
+ return value;
721
+ }
722
+ set(key, value) {
723
+ if (this.map.has(key)) this.map.delete(key);
724
+ else if (this.map.size >= this.capacity) {
725
+ const oldest = this.map.keys().next().value;
726
+ if (oldest !== void 0) this.map.delete(oldest);
727
+ }
728
+ this.map.set(key, value);
729
+ }
730
+ size() {
731
+ return this.map.size;
732
+ }
733
+ };
734
+
735
+ // src/internal/openai-compatible.ts
736
+ var MAX_BATCH = 100;
737
+ var MAX_RETRIES = 2;
738
+ async function createOpenAiCompatibleRuntime(cfg, options) {
739
+ const model = options.model ?? cfg.defaultModel;
740
+ const apiKey = options.apiKey ?? process.env[cfg.apiKeyEnv];
741
+ if (apiKey === void 0 || apiKey.length === 0) {
742
+ throw new errors.AuthenticationError(`${cfg.apiKeyEnv} missing`, {
743
+ code: "embedding_missing_api_key"
744
+ });
745
+ }
746
+ const envBaseUrl = cfg.baseUrlEnv !== void 0 ? process.env[cfg.baseUrlEnv] : void 0;
747
+ const baseUrl = options.baseUrl ?? envBaseUrl ?? cfg.defaultBaseUrl;
748
+ const fetchImpl = options.fetch ?? fetch;
749
+ const cache = options.cache ?? new LruEmbeddingCache();
750
+ const dimension = cfg.dimensionByModel[model];
751
+ if (dimension === void 0) {
752
+ throw new errors.ConfigurationError(
753
+ `${cfg.id} adapter: model "${model}" is not in the dimension table. Use one of: ${Object.keys(cfg.dimensionByModel).join(", ")}. To add it, update dimensionByModel in the adapter source.`,
754
+ { code: "embedding_unknown_model" }
755
+ );
756
+ }
757
+ const embeddingsPath = cfg.embeddingsPath ?? "/v1/embeddings";
758
+ const stats = {
759
+ cacheHits: 0,
760
+ cacheMisses: 0,
761
+ httpCalls: 0,
762
+ retries: 0
763
+ };
764
+ return {
765
+ id: cfg.id,
766
+ model,
767
+ dimension,
768
+ stats: () => ({ ...stats }),
769
+ embed: (texts) => embedTexts({
770
+ texts,
771
+ cache,
772
+ stats,
773
+ model,
774
+ dimension,
775
+ apiKey,
776
+ baseUrl,
777
+ embeddingsPath,
778
+ fetchImpl,
779
+ providerId: cfg.id
780
+ })
781
+ };
782
+ }
783
+ async function embedTexts(input) {
784
+ const { texts, cache, stats, model, dimension } = input;
785
+ const results = new Array(texts.length);
786
+ const pending = [];
787
+ for (let i = 0; i < texts.length; i++) {
788
+ classifyEntry({
789
+ index: i,
790
+ text: texts[i] ?? "",
791
+ model,
792
+ dimension,
793
+ cache,
794
+ stats,
795
+ results,
796
+ pending
797
+ });
798
+ }
799
+ await runBatches(input, pending, results);
800
+ return results.map((v) => v ?? new Array(dimension).fill(0));
801
+ }
802
+ function classifyEntry(args) {
803
+ if (args.text.trim().length === 0) {
804
+ args.results[args.index] = new Array(args.dimension).fill(0);
805
+ return;
806
+ }
807
+ const key = hashKey(args.model, args.text);
808
+ const cached = args.cache.get(key);
809
+ if (cached !== void 0) {
810
+ args.stats.cacheHits += 1;
811
+ args.results[args.index] = cached;
812
+ return;
813
+ }
814
+ args.stats.cacheMisses += 1;
815
+ args.pending.push({ index: args.index, text: args.text, key });
816
+ }
817
+ async function runBatches(input, pending, results) {
818
+ for (let offset = 0; offset < pending.length; offset += MAX_BATCH) {
819
+ const batch = pending.slice(offset, offset + MAX_BATCH);
820
+ const vectors = await embedBatch({
821
+ apiKey: input.apiKey,
822
+ baseUrl: input.baseUrl,
823
+ embeddingsPath: input.embeddingsPath,
824
+ model: input.model,
825
+ inputs: batch.map((b) => b.text),
826
+ fetchImpl: input.fetchImpl,
827
+ stats: input.stats,
828
+ providerId: input.providerId
829
+ });
830
+ for (let j = 0; j < batch.length; j++) {
831
+ const slot = batch[j];
832
+ const vector = vectors[j];
833
+ if (slot === void 0 || vector === void 0) continue;
834
+ results[slot.index] = vector;
835
+ input.cache.set(slot.key, vector);
836
+ }
837
+ }
838
+ }
839
+ async function embedBatch(opts) {
840
+ const url = `${opts.baseUrl.replace(/\/$/, "")}${opts.embeddingsPath}`;
841
+ let attempt = 0;
842
+ while (true) {
843
+ opts.stats.httpCalls += 1;
844
+ const response = await postEmbedRequest(opts, url);
845
+ if (response.ok)
846
+ return await parseEmbedResponse(response, opts.providerId, opts.embeddingsPath);
847
+ if (isRetryable(response.status) && attempt < MAX_RETRIES) {
848
+ attempt += 1;
849
+ opts.stats.retries += 1;
850
+ await sleep(linearBackoffMs(attempt));
851
+ continue;
852
+ }
853
+ const text = await response.text().catch(() => "");
854
+ let body = text;
855
+ try {
856
+ body = JSON.parse(text);
857
+ } catch {
858
+ }
859
+ throw mapOpenAICompatibleError({
860
+ providerId: opts.providerId,
861
+ status: response.status,
862
+ body,
863
+ headers: response.headers,
864
+ endpoint: opts.embeddingsPath
865
+ });
866
+ }
867
+ }
868
+ async function postEmbedRequest(opts, url) {
869
+ return opts.fetchImpl(url, {
870
+ method: "POST",
871
+ headers: {
872
+ "content-type": "application/json",
873
+ authorization: `Bearer ${opts.apiKey}`
874
+ },
875
+ body: JSON.stringify({ model: opts.model, input: opts.inputs })
876
+ });
877
+ }
878
+ async function parseEmbedResponse(response, providerId, endpoint) {
879
+ const json = await response.json();
880
+ if (!Array.isArray(json.data)) {
881
+ throw new errors.NetworkError(`${providerId} ${endpoint} returned no data`, {
882
+ code: "embedding_invalid_response",
883
+ metadata: {
884
+ provider: providerId,
885
+ endpoint,
886
+ code: "invalid_request",
887
+ raw: json
888
+ }
889
+ });
890
+ }
891
+ return json.data.map((d) => d.embedding);
892
+ }
893
+ function isRetryable(status) {
894
+ return status === 429 || status >= 500 && status < 600;
895
+ }
896
+ function hashKey(model, text) {
897
+ return crypto.createHash("sha256").update(`${model} ${text}`).digest("hex");
898
+ }
899
+ function linearBackoffMs(attempt) {
900
+ return 50 * attempt;
901
+ }
902
+ function sleep(ms) {
903
+ return new Promise((resolve) => setTimeout(resolve, ms));
904
+ }
905
+
906
+ // src/internal/deepinfra-embedding.ts
907
+ var DEFAULT_DEEPINFRA_EMBEDDING_MODEL = "BAAI/bge-large-en-v1.5";
908
+ var DIMENSION_BY_MODEL = {
909
+ "BAAI/bge-large-en-v1.5": 1024,
910
+ "BAAI/bge-base-en-v1.5": 768,
911
+ "BAAI/bge-small-en-v1.5": 384,
912
+ "BAAI/bge-m3": 1024,
913
+ "intfloat/e5-large-v2": 1024,
914
+ "intfloat/e5-base-v2": 768,
915
+ "intfloat/multilingual-e5-large": 1024,
916
+ "sentence-transformers/all-MiniLM-L6-v2": 384,
917
+ "thenlper/gte-large": 1024,
918
+ "thenlper/gte-base": 768
919
+ };
920
+ var deepinfraMemoryEmbeddingProviderAdapter = {
921
+ id: "deepinfra",
922
+ defaultModel: DEFAULT_DEEPINFRA_EMBEDDING_MODEL,
923
+ transport: "remote",
924
+ authProviderId: "deepinfra",
925
+ autoSelectPriority: 13,
926
+ create: (options) => createOpenAiCompatibleRuntime(
927
+ {
928
+ id: "deepinfra",
929
+ defaultBaseUrl: "https://api.deepinfra.com",
930
+ apiKeyEnv: "DEEPINFRA_API_KEY",
931
+ baseUrlEnv: "DEEPINFRA_API_BASE_URL",
932
+ defaultModel: DEFAULT_DEEPINFRA_EMBEDDING_MODEL,
933
+ dimensionByModel: DIMENSION_BY_MODEL,
934
+ embeddingsPath: "/v1/openai/embeddings"
935
+ },
936
+ options
937
+ )
938
+ };
939
+
940
+ // src/internal/mistral-embedding.ts
941
+ var DEFAULT_MISTRAL_EMBEDDING_MODEL = "mistral-embed";
942
+ var DIMENSION_BY_MODEL2 = {
943
+ "mistral-embed": 1024
944
+ };
945
+ var mistralMemoryEmbeddingProviderAdapter = {
946
+ id: "mistral",
947
+ defaultModel: DEFAULT_MISTRAL_EMBEDDING_MODEL,
948
+ transport: "remote",
949
+ authProviderId: "mistral",
950
+ autoSelectPriority: 18,
951
+ create: (options) => createOpenAiCompatibleRuntime(
952
+ {
953
+ id: "mistral",
954
+ defaultBaseUrl: "https://api.mistral.ai",
955
+ apiKeyEnv: "MISTRAL_API_KEY",
956
+ baseUrlEnv: "MISTRAL_API_BASE_URL",
957
+ defaultModel: DEFAULT_MISTRAL_EMBEDDING_MODEL,
958
+ dimensionByModel: DIMENSION_BY_MODEL2
959
+ },
960
+ options
961
+ )
962
+ };
963
+
964
+ // src/internal/ollama-embedding.ts
965
+ var DEFAULT_OLLAMA_EMBEDDING_MODEL = "nomic-embed-text";
966
+ var OLLAMA_LOCAL_SENTINEL_KEY = "ollama-local";
967
+ var DIMENSION_BY_MODEL3 = {
968
+ // Nomic — local default, recommended quality/size balance.
969
+ "nomic-embed-text": 768,
970
+ "nomic-embed-text:latest": 768,
971
+ // Sentence-transformers MiniLM — smaller, faster, lower quality.
972
+ "all-minilm": 384,
973
+ "all-minilm:latest": 384,
974
+ // BGE family.
975
+ "bge-large": 1024,
976
+ "bge-large:latest": 1024,
977
+ "bge-m3": 1024,
978
+ "bge-m3:latest": 1024,
979
+ // mxbai-embed-large.
980
+ "mxbai-embed-large": 1024,
981
+ "mxbai-embed-large:latest": 1024
982
+ };
983
+ var ollamaMemoryEmbeddingProviderAdapter = {
984
+ id: "ollama",
985
+ defaultModel: DEFAULT_OLLAMA_EMBEDDING_MODEL,
986
+ transport: "local",
987
+ authProviderId: "ollama",
988
+ // Lower than remote providers — chosen only when explicitly requested
989
+ // OR when no remote provider key is configured (local-first developer).
990
+ autoSelectPriority: 10,
991
+ create: (options) => {
992
+ const apiKey = options.apiKey ?? process.env.OLLAMA_API_KEY ?? OLLAMA_LOCAL_SENTINEL_KEY;
993
+ return createOpenAiCompatibleRuntime(
994
+ {
995
+ id: "ollama",
996
+ defaultBaseUrl: "http://localhost:11434",
997
+ apiKeyEnv: "OLLAMA_API_KEY",
998
+ baseUrlEnv: "OLLAMA_HOST",
999
+ defaultModel: DEFAULT_OLLAMA_EMBEDDING_MODEL,
1000
+ dimensionByModel: DIMENSION_BY_MODEL3
1001
+ },
1002
+ { ...options, apiKey }
1003
+ );
1004
+ }
1005
+ };
1006
+
1007
+ // src/internal/openai-embedding.ts
1008
+ var DEFAULT_OPENAI_EMBEDDING_MODEL = "text-embedding-3-small";
1009
+ var DIMENSION_BY_MODEL4 = {
1010
+ "text-embedding-3-small": 1536,
1011
+ "text-embedding-3-large": 3072,
1012
+ "text-embedding-ada-002": 1536
1013
+ };
1014
+ var openAiMemoryEmbeddingProviderAdapter = {
1015
+ id: "openai",
1016
+ defaultModel: DEFAULT_OPENAI_EMBEDDING_MODEL,
1017
+ transport: "remote",
1018
+ authProviderId: "openai",
1019
+ autoSelectPriority: 20,
1020
+ create: (options) => createOpenAiCompatibleRuntime(
1021
+ {
1022
+ id: "openai",
1023
+ defaultBaseUrl: "https://api.openai.com",
1024
+ apiKeyEnv: "OPENAI_API_KEY",
1025
+ baseUrlEnv: "OPENAI_API_BASE_URL",
1026
+ defaultModel: DEFAULT_OPENAI_EMBEDDING_MODEL,
1027
+ dimensionByModel: DIMENSION_BY_MODEL4
1028
+ },
1029
+ options
1030
+ )
1031
+ };
1032
+
1033
+ // src/internal/openrouter-embedding.ts
1034
+ var DEFAULT_OPENROUTER_EMBEDDING_MODEL = "openai/text-embedding-3-small";
1035
+ var DIMENSION_BY_MODEL5 = {
1036
+ "openai/text-embedding-3-small": 1536,
1037
+ "openai/text-embedding-3-large": 3072,
1038
+ "openai/text-embedding-ada-002": 1536,
1039
+ "mistralai/mistral-embed": 1024
1040
+ };
1041
+ var openRouterMemoryEmbeddingProviderAdapter = {
1042
+ id: "openrouter",
1043
+ defaultModel: DEFAULT_OPENROUTER_EMBEDDING_MODEL,
1044
+ transport: "remote",
1045
+ authProviderId: "openrouter",
1046
+ autoSelectPriority: 15,
1047
+ create: (options) => createOpenAiCompatibleRuntime(
1048
+ {
1049
+ id: "openrouter",
1050
+ defaultBaseUrl: "https://openrouter.ai/api",
1051
+ apiKeyEnv: "OPENROUTER_API_KEY",
1052
+ baseUrlEnv: "OPENROUTER_API_BASE_URL",
1053
+ defaultModel: DEFAULT_OPENROUTER_EMBEDDING_MODEL,
1054
+ dimensionByModel: DIMENSION_BY_MODEL5
1055
+ },
1056
+ options
1057
+ )
1058
+ };
1059
+
1060
+ // src/internal/voyage-embedding.ts
1061
+ var DEFAULT_VOYAGE_EMBEDDING_MODEL = "voyage-3-lite";
1062
+ var DIMENSION_BY_MODEL6 = {
1063
+ "voyage-3-lite": 512,
1064
+ "voyage-3": 1024,
1065
+ "voyage-3-large": 1024,
1066
+ "voyage-code-3": 1024,
1067
+ "voyage-multilingual-2": 1024
1068
+ };
1069
+ var voyageMemoryEmbeddingProviderAdapter = {
1070
+ id: "voyage",
1071
+ defaultModel: DEFAULT_VOYAGE_EMBEDDING_MODEL,
1072
+ transport: "remote",
1073
+ authProviderId: "voyage",
1074
+ autoSelectPriority: 14,
1075
+ create: (options) => createOpenAiCompatibleRuntime(
1076
+ {
1077
+ id: "voyage",
1078
+ defaultBaseUrl: "https://api.voyageai.com",
1079
+ apiKeyEnv: "VOYAGE_API_KEY",
1080
+ baseUrlEnv: "VOYAGE_API_BASE_URL",
1081
+ defaultModel: DEFAULT_VOYAGE_EMBEDDING_MODEL,
1082
+ dimensionByModel: DIMENSION_BY_MODEL6
1083
+ },
1084
+ options
1085
+ )
1086
+ };
1087
+
1088
+ // src/internal/adapter-catalog.ts
1089
+ var MEMORY_EMBEDDING_ADAPTERS = {
1090
+ openai: openAiMemoryEmbeddingProviderAdapter,
1091
+ mistral: mistralMemoryEmbeddingProviderAdapter,
1092
+ openrouter: openRouterMemoryEmbeddingProviderAdapter,
1093
+ voyage: voyageMemoryEmbeddingProviderAdapter,
1094
+ deepinfra: deepinfraMemoryEmbeddingProviderAdapter,
1095
+ ollama: ollamaMemoryEmbeddingProviderAdapter
1096
+ };
1097
+ var HEADING_RE = /^(#{1,6})\s+(.+?)\s*$/;
1098
+ function chunkMarkdown(text, options = {}) {
1099
+ const maxChars = options.maxChars ?? 800;
1100
+ if (text.length === 0) return [];
1101
+ const lines = text.split("\n");
1102
+ const acc = { chunks: [], lines: [], start: 1, heading: void 0 };
1103
+ for (let i = 0; i < lines.length; i++) {
1104
+ const line = lines[i] ?? "";
1105
+ const lineNumber = i + 1;
1106
+ processLine(acc, line, lineNumber, maxChars);
1107
+ }
1108
+ flushAccumulator(acc, lines.length, maxChars);
1109
+ return acc.chunks;
1110
+ }
1111
+ function processLine(acc, line, lineNumber, maxChars) {
1112
+ const headingMatch = HEADING_RE.exec(line);
1113
+ const isBlank = line.trim().length === 0;
1114
+ if (headingMatch !== null && acc.lines.length > 0) {
1115
+ flushAccumulator(acc, lineNumber - 1, maxChars);
1116
+ }
1117
+ if (headingMatch !== null) acc.heading = headingMatch[2];
1118
+ acc.lines.push(line);
1119
+ if (isBlank && acc.lines.length > 1) {
1120
+ const joined = acc.lines.join("\n");
1121
+ if (joined.trim().length > 0) flushAccumulator(acc, lineNumber, maxChars);
1122
+ }
1123
+ }
1124
+ function flushAccumulator(acc, endLineExclusive, maxChars) {
1125
+ if (acc.lines.length === 0) return;
1126
+ const chunkText = acc.lines.join("\n");
1127
+ if (chunkText.trim().length === 0) {
1128
+ acc.lines = [];
1129
+ acc.start = endLineExclusive + 1;
1130
+ return;
1131
+ }
1132
+ if (chunkText.length > maxChars) {
1133
+ pushOversizedSlices(acc.chunks, chunkText, acc.start, acc.heading, maxChars);
1134
+ } else {
1135
+ acc.chunks.push(buildChunk(chunkText, acc.start, endLineExclusive, acc.heading));
1136
+ }
1137
+ acc.lines = [];
1138
+ acc.start = endLineExclusive + 1;
1139
+ }
1140
+ function buildChunk(text, startLine, endLine, heading) {
1141
+ return {
1142
+ startLine,
1143
+ endLine,
1144
+ text,
1145
+ hash: crypto.createHash("sha256").update(text).digest("hex"),
1146
+ ...heading !== void 0 ? { heading } : {}
1147
+ };
1148
+ }
1149
+ function pushOversizedSlices(chunks, text, startLine, heading, maxChars) {
1150
+ let remaining = text;
1151
+ let lineCursor = startLine;
1152
+ while (remaining.length > maxChars) {
1153
+ const splitAt = findWordBoundarySplit(remaining, maxChars);
1154
+ const slice = remaining.slice(0, splitAt);
1155
+ const sliceLines = slice.split("\n").length;
1156
+ chunks.push(buildChunk(slice, lineCursor, lineCursor + sliceLines - 1, heading));
1157
+ remaining = remaining.slice(splitAt).replace(/^\s+/, "");
1158
+ lineCursor += sliceLines - 1;
1159
+ }
1160
+ if (remaining.length > 0) {
1161
+ const sliceLines = remaining.split("\n").length;
1162
+ chunks.push(buildChunk(remaining, lineCursor, lineCursor + sliceLines - 1, heading));
1163
+ }
1164
+ }
1165
+ function findWordBoundarySplit(text, maxChars) {
1166
+ if (text.length <= maxChars) return text.length;
1167
+ for (let i = maxChars; i > Math.max(0, maxChars - 200); i--) {
1168
+ if (/\s/.test(text[i] ?? "x")) return i;
1169
+ }
1170
+ return maxChars;
1171
+ }
1172
+ function diaryPath(cwd) {
1173
+ return path.join(memoryDir(cwd), "dream-diary.md");
1174
+ }
1175
+ function renderDiaryEntry(entry) {
1176
+ const stamp = new Date(entry.timestampMs).toISOString();
1177
+ const hash = entryHash(entry).slice(0, 8);
1178
+ return [
1179
+ `## ${stamp}`,
1180
+ "",
1181
+ `- entry-hash: ${hash}`,
1182
+ `- facts before: ${entry.factsBefore}`,
1183
+ `- facts after: ${entry.factsAfter}`,
1184
+ `- duplicates removed: ${entry.duplicatesRemoved}`,
1185
+ `- clusters created: ${entry.clustersCreated}`,
1186
+ `- notes written: ${entry.notesWritten}`,
1187
+ ""
1188
+ ].join("\n");
1189
+ }
1190
+ async function appendDiaryEntry(cwd, entry) {
1191
+ const path = diaryPath(cwd);
1192
+ let raw = "";
1193
+ try {
1194
+ raw = await promises.readFile(path, "utf8");
1195
+ } catch {
1196
+ raw = "# Dream Diary\n\n";
1197
+ }
1198
+ const next = `${raw.endsWith("\n") ? raw : `${raw}
1199
+ `}${renderDiaryEntry(entry)}`;
1200
+ await persistence.replaceFileAtomic(path, next);
1201
+ }
1202
+ function entryHash(entry) {
1203
+ return crypto.createHash("sha256").update(
1204
+ [
1205
+ entry.factsBefore,
1206
+ entry.factsAfter,
1207
+ entry.duplicatesRemoved,
1208
+ entry.clustersCreated,
1209
+ entry.notesWritten
1210
+ ].join("|")
1211
+ ).digest("hex");
1212
+ }
1213
+
1214
+ // src/internal/dreaming-phases.ts
1215
+ var DEFAULT_DEDUP_THRESHOLD = 0.95;
1216
+ var DEFAULT_CLUSTER_THRESHOLD = 0.75;
1217
+ async function lightPhase(facts, embedding, threshold = DEFAULT_DEDUP_THRESHOLD) {
1218
+ if (facts.length <= 1) return { kept: [...facts], duplicatesRemoved: 0 };
1219
+ const vectors = await embedding.embed(facts.map((f) => f.text));
1220
+ const keptIdx = [];
1221
+ const keptVecs = [];
1222
+ for (let i = 0; i < facts.length; i++) {
1223
+ const vec = vectors[i] ?? [];
1224
+ const isDup = keptVecs.some((kept2) => cosineSimilarity(vec, kept2) >= threshold);
1225
+ if (isDup) continue;
1226
+ keptIdx.push(i);
1227
+ keptVecs.push(vec);
1228
+ }
1229
+ const kept = keptIdx.map((i) => facts[i]);
1230
+ return { kept, duplicatesRemoved: facts.length - kept.length };
1231
+ }
1232
+ var DEFAULT_MAX_FACTS_PER_SWEEP = 500;
1233
+ async function remPhase(facts, embedding, threshold = DEFAULT_CLUSTER_THRESHOLD, maxFactsPerSweep = DEFAULT_MAX_FACTS_PER_SWEEP) {
1234
+ if (facts.length === 0) return { clusters: [] };
1235
+ const capped = facts.length > maxFactsPerSweep ? facts.slice(0, maxFactsPerSweep) : facts;
1236
+ const vectors = await embedding.embed(capped.map((f) => f.text));
1237
+ const clusterOfIdx = unionFindByPairs(vectors, threshold);
1238
+ const groups = bucketFactsByClusterRoot(capped, clusterOfIdx);
1239
+ return { clusters: [...groups.values()].map(buildClusterFromMembers) };
1240
+ }
1241
+ function unionFindByPairs(vectors, threshold) {
1242
+ const clusterOfIdx = vectors.map((_, i) => i);
1243
+ for (let i = 0; i < vectors.length; i++) {
1244
+ for (let j = i + 1; j < vectors.length; j++) {
1245
+ if (cosineSimilarity(vectors[i] ?? [], vectors[j] ?? []) >= threshold) {
1246
+ unifyClusters(clusterOfIdx, i, j);
1247
+ }
1248
+ }
1249
+ }
1250
+ return clusterOfIdx;
1251
+ }
1252
+ function bucketFactsByClusterRoot(facts, clusterOfIdx) {
1253
+ const groups = /* @__PURE__ */ new Map();
1254
+ for (let i = 0; i < facts.length; i++) {
1255
+ const root = findRoot(clusterOfIdx, i);
1256
+ const list = groups.get(root) ?? [];
1257
+ list.push(facts[i]);
1258
+ groups.set(root, list);
1259
+ }
1260
+ return groups;
1261
+ }
1262
+ function buildClusterFromMembers(members) {
1263
+ const sorted = [...members].sort((a, b) => b.text.length - a.text.length);
1264
+ return { representativeText: sorted[0]?.text ?? "", members };
1265
+ }
1266
+ function deepPhase(clusters, timestampMs) {
1267
+ if (clusters.length === 0) return "";
1268
+ const isoStamp = new Date(timestampMs).toISOString();
1269
+ const lines = [`# Dreamed ${isoStamp}`, ""];
1270
+ for (let i = 0; i < clusters.length; i++) {
1271
+ const c = clusters[i];
1272
+ if (c === void 0) continue;
1273
+ lines.push(`## Cluster ${i + 1}: ${c.representativeText}`);
1274
+ lines.push("");
1275
+ for (const member of c.members) lines.push(`- ${member.text}`);
1276
+ lines.push("");
1277
+ }
1278
+ return `${lines.join("\n")}
1279
+ `;
1280
+ }
1281
+ function cosineSimilarity(a, b) {
1282
+ if (a.length === 0 || b.length === 0 || a.length !== b.length) return 0;
1283
+ let dot = 0;
1284
+ let aNorm = 0;
1285
+ let bNorm = 0;
1286
+ for (let i = 0; i < a.length; i++) {
1287
+ const ai = a[i] ?? 0;
1288
+ const bi = b[i] ?? 0;
1289
+ dot += ai * bi;
1290
+ aNorm += ai * ai;
1291
+ bNorm += bi * bi;
1292
+ }
1293
+ const denom = Math.sqrt(aNorm) * Math.sqrt(bNorm);
1294
+ return denom === 0 ? 0 : dot / denom;
1295
+ }
1296
+ function findRoot(parents, i) {
1297
+ let root = i;
1298
+ while (parents[root] !== root) {
1299
+ const next = parents[root] ?? root;
1300
+ if (next === root) break;
1301
+ root = next;
1302
+ }
1303
+ parents[i] = root;
1304
+ return root;
1305
+ }
1306
+ function unifyClusters(parents, a, b) {
1307
+ const rootA = findRoot(parents, a);
1308
+ const rootB = findRoot(parents, b);
1309
+ if (rootA !== rootB) parents[rootB] = rootA;
1310
+ }
1311
+ function runDreamingSweep(options) {
1312
+ return persistence.withCwdMutex(`dream:${options.cwd}`, () => runInner(options));
1313
+ }
1314
+ async function runInner(options) {
1315
+ const now = options.now ?? Date.now;
1316
+ const timestampMs = now();
1317
+ try {
1318
+ const facts = await readFactsFromMarkdown(options.cwd);
1319
+ if (facts.length === 0) {
1320
+ return emptyResult("skipped");
1321
+ }
1322
+ const dedup = await lightPhase(facts, options.embedding, options.dedupThreshold);
1323
+ const rem = await remPhase(dedup.kept, options.embedding, options.clusterThreshold);
1324
+ const notesWritten = await writeConsolidatedNotes(options.cwd, rem.clusters, timestampMs);
1325
+ const result = {
1326
+ status: "ok",
1327
+ factsBefore: facts.length,
1328
+ factsAfter: dedup.kept.length,
1329
+ duplicatesRemoved: dedup.duplicatesRemoved,
1330
+ clustersCreated: rem.clusters.length,
1331
+ notesWritten,
1332
+ diaryEntryHash: void 0
1333
+ };
1334
+ await appendDiaryEntry(options.cwd, {
1335
+ timestampMs,
1336
+ factsBefore: result.factsBefore,
1337
+ factsAfter: result.factsAfter,
1338
+ duplicatesRemoved: result.duplicatesRemoved,
1339
+ clustersCreated: result.clustersCreated,
1340
+ notesWritten: result.notesWritten
1341
+ });
1342
+ return result;
1343
+ } catch (cause) {
1344
+ const message = cause instanceof Error ? cause.message : String(cause);
1345
+ process.stderr.write(`[theokit-sdk] dreaming sweep failed: ${message}
1346
+ `);
1347
+ return emptyResult("error");
1348
+ }
1349
+ }
1350
+ async function writeConsolidatedNotes(cwd, clusters, timestampMs) {
1351
+ if (clusters.length === 0) return 0;
1352
+ const notesDir2 = path.join(memoryDir(cwd), "notes");
1353
+ await promises.mkdir(notesDir2, { recursive: true });
1354
+ const isoSlug = new Date(timestampMs).toISOString().replace(/[^\dT]/g, "-");
1355
+ const file = path.join(notesDir2, `dreamed-${isoSlug}.md`);
1356
+ const body = deepPhase(clusters, timestampMs);
1357
+ await persistence.replaceFileAtomic(file, body);
1358
+ return 1;
1359
+ }
1360
+ function emptyResult(status) {
1361
+ return {
1362
+ status,
1363
+ factsBefore: 0,
1364
+ factsAfter: 0,
1365
+ duplicatesRemoved: 0,
1366
+ clustersCreated: 0,
1367
+ notesWritten: 0,
1368
+ diaryEntryHash: void 0
1369
+ };
1370
+ }
1371
+
1372
+ // src/internal/index-schema.ts
1373
+ var SCHEMA_STATEMENTS = [
1374
+ `CREATE TABLE IF NOT EXISTS files (
1375
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1376
+ path TEXT UNIQUE NOT NULL,
1377
+ rel_path TEXT NOT NULL,
1378
+ mtime INTEGER NOT NULL,
1379
+ hash TEXT NOT NULL,
1380
+ source TEXT NOT NULL DEFAULT 'memory'
1381
+ )`,
1382
+ `CREATE TABLE IF NOT EXISTS chunks (
1383
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
1384
+ file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
1385
+ start_line INTEGER NOT NULL,
1386
+ end_line INTEGER NOT NULL,
1387
+ text TEXT NOT NULL,
1388
+ hash TEXT NOT NULL,
1389
+ session_key TEXT
1390
+ )`,
1391
+ `CREATE VIRTUAL TABLE IF NOT EXISTS chunks_fts USING fts5(
1392
+ text,
1393
+ content='chunks',
1394
+ content_rowid='id'
1395
+ )`,
1396
+ `CREATE TRIGGER IF NOT EXISTS chunks_fts_insert AFTER INSERT ON chunks BEGIN
1397
+ INSERT INTO chunks_fts(rowid, text) VALUES (new.id, new.text);
1398
+ END`,
1399
+ `CREATE TRIGGER IF NOT EXISTS chunks_fts_delete AFTER DELETE ON chunks BEGIN
1400
+ INSERT INTO chunks_fts(chunks_fts, rowid, text) VALUES ('delete', old.id, old.text);
1401
+ END`,
1402
+ `CREATE TABLE IF NOT EXISTS meta (
1403
+ key TEXT PRIMARY KEY,
1404
+ value TEXT
1405
+ )`,
1406
+ `CREATE INDEX IF NOT EXISTS idx_chunks_file_id ON chunks(file_id)`
1407
+ ];
1408
+ var MIGRATION_V2_STATEMENTS = [
1409
+ "ALTER TABLE chunks ADD COLUMN created_at INTEGER",
1410
+ "ALTER TABLE chunks ADD COLUMN importance REAL DEFAULT 0.5",
1411
+ "ALTER TABLE chunks ADD COLUMN scope TEXT DEFAULT '/'"
1412
+ ];
1413
+ var PRAGMA_STATEMENTS = [
1414
+ "PRAGMA synchronous=NORMAL",
1415
+ "PRAGMA foreign_keys=ON"
1416
+ ];
1417
+
1418
+ // src/internal/index-db.ts
1419
+ async function openMemoryDb(opts) {
1420
+ await promises.mkdir(path.dirname(opts.filePath), { recursive: true });
1421
+ try {
1422
+ return await openConcrete(opts.filePath);
1423
+ } catch (cause) {
1424
+ if (opts.recoverCorrupt !== false && isCorruptionError(cause)) {
1425
+ await renameAside(opts.filePath);
1426
+ return await openConcrete(opts.filePath);
1427
+ }
1428
+ throw cause;
1429
+ }
1430
+ }
1431
+ async function openConcrete(filePath) {
1432
+ const db = await loadDriver(filePath);
1433
+ persistence.applyWalWithFallback(db, "memory-index");
1434
+ for (const pragma of PRAGMA_STATEMENTS) db.exec(pragma);
1435
+ for (const stmt of SCHEMA_STATEMENTS) db.exec(stmt);
1436
+ return db;
1437
+ }
1438
+ async function loadDriver(filePath) {
1439
+ try {
1440
+ const mod = await import('better-sqlite3');
1441
+ const Ctor = mod.default ?? mod;
1442
+ const db = new Ctor(filePath);
1443
+ return db;
1444
+ } catch (cause) {
1445
+ const message = cause instanceof Error ? cause.message : String(cause);
1446
+ throw new errors.ConfigurationError(
1447
+ `Failed to load SQLite driver. Install \`better-sqlite3\` or run on Node 22.5+ for built-in \`node:sqlite\`. Cause: ${message}`,
1448
+ { code: "sqlite_driver_unavailable", cause }
1449
+ );
1450
+ }
1451
+ }
1452
+ function isCorruptionError(cause) {
1453
+ if (!(cause instanceof Error)) return false;
1454
+ const msg = cause.message.toLowerCase();
1455
+ return msg.includes("malformed") || msg.includes("not a database") || msg.includes("encrypted") || msg.includes("disk image is malformed");
1456
+ }
1457
+ async function renameAside(filePath) {
1458
+ const asidePath = `${filePath}.corrupt-${Date.now()}`;
1459
+ await promises.rename(filePath, asidePath).catch(() => void 0);
1460
+ await promises.rename(`${filePath}-wal`, `${asidePath}-wal`).catch(() => void 0);
1461
+ await promises.rename(`${filePath}-shm`, `${asidePath}-shm`).catch(() => void 0);
1462
+ process.stderr.write(
1463
+ `[theokit-sdk] memory index corrupt; renamed aside to ${asidePath} and rebuilt schema
1464
+ `
1465
+ );
1466
+ }
1467
+ function defaultIndexPath(cwd) {
1468
+ return path.join(cwd, ".theokit", "memory", ".index", "memory.sqlite");
1469
+ }
1470
+ function requireLance() {
1471
+ try {
1472
+ const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
1473
+ return r("@lancedb/lancedb");
1474
+ } catch (cause) {
1475
+ throw new errors.ConfigurationError(
1476
+ 'Lance backend selected but `@lancedb/lancedb` is not installed. Install with: `pnpm add @lancedb/lancedb`. SQLite remains available as the default (omit `backend: "lance"`).',
1477
+ { code: "lance_backend_unavailable", cause }
1478
+ );
1479
+ }
1480
+ }
1481
+ var LanceIndex = class _LanceIndex {
1482
+ constructor(table, embedding, embeddingDim) {
1483
+ this.table = table;
1484
+ this.embedding = embedding;
1485
+ this.embeddingDim = embeddingDim;
1486
+ }
1487
+ table;
1488
+ embedding;
1489
+ embeddingDim;
1490
+ static async open(opts) {
1491
+ const lance = requireLance();
1492
+ const storagePath = opts.storagePath ?? path.join(opts.cwd, ".theokit", "memory", "lance");
1493
+ fs.mkdirSync(storagePath, { recursive: true });
1494
+ const conn = await lance.connect(storagePath);
1495
+ const dim = opts.embedding.dimension;
1496
+ const tableName = "facts";
1497
+ const existing = await conn.tableNames();
1498
+ let table;
1499
+ if (existing.includes(tableName)) {
1500
+ table = await conn.openTable(tableName);
1501
+ const schema = await table.schema();
1502
+ const embField = schema.fields.find((f) => f.name === "embedding");
1503
+ const existingDim = embField?.type?.listSize ?? embField?.type?.fixedSize;
1504
+ if (typeof existingDim === "number" && existingDim !== dim) {
1505
+ throw new errors.ConfigurationError(
1506
+ `Embedding dimension mismatch in Lance index: storage has ${existingDim}-dim vectors, current provider yields ${dim}-dim. Run \`theokit-migrate-memory\` after switching providers, or use a different storagePath.`,
1507
+ { code: "embedding_dimension_mismatch" }
1508
+ );
1509
+ }
1510
+ } else {
1511
+ const bootstrap = {
1512
+ id: "__bootstrap__",
1513
+ text: "",
1514
+ source: "memory",
1515
+ embedding: new Array(dim).fill(0),
1516
+ namespace: "__bootstrap__",
1517
+ scope: "__bootstrap__",
1518
+ user_id: "__bootstrap__",
1519
+ timestamp: 0
1520
+ };
1521
+ table = await conn.createTable(tableName, [bootstrap]);
1522
+ await table.delete("namespace = '__bootstrap__'");
1523
+ }
1524
+ return new _LanceIndex(table, opts.embedding, dim);
1525
+ }
1526
+ async addFacts(facts) {
1527
+ if (facts.length === 0) return;
1528
+ const texts = facts.map((f) => f.text);
1529
+ const embeddings = await this.embedding.embed(texts);
1530
+ const rows = facts.map((f, i) => ({
1531
+ ...f,
1532
+ embedding: embeddings[i] ?? new Array(this.embeddingDim).fill(0)
1533
+ }));
1534
+ await this.table.add(rows);
1535
+ }
1536
+ /**
1537
+ * Search facts by semantic similarity. EC-1: filters use Lance's
1538
+ * SQL string predicate with `'` → `''` escape — bind parameters are
1539
+ * not supported by Lance 0.30.
1540
+ */
1541
+ async search(query, opts) {
1542
+ const [embedding] = await this.embedding.embed([query]);
1543
+ if (embedding === void 0) return [];
1544
+ const predicates = [`namespace = '${escapeSqlValue(opts.namespace)}'`];
1545
+ if (opts.scope !== void 0) {
1546
+ predicates.push(`scope = '${escapeSqlValue(opts.scope)}'`);
1547
+ }
1548
+ let q = this.table.search(embedding).where(predicates.join(" AND "));
1549
+ if (opts.limit !== void 0) q = q.limit(opts.limit);
1550
+ const results = await q.toArray();
1551
+ return results.filter((r) => opts.sources === void 0 || opts.sources.includes(r.source)).map((r) => ({
1552
+ id: r.id,
1553
+ text: r.text,
1554
+ source: r.source,
1555
+ namespace: r.namespace,
1556
+ scope: r.scope,
1557
+ userId: r.user_id,
1558
+ // Lower _distance = better match; normalize to 0..1 (1=best).
1559
+ score: 1 / (1 + (r._distance ?? 1))
1560
+ }));
1561
+ }
1562
+ async countFacts(namespace) {
1563
+ return this.table.countRows();
1564
+ }
1565
+ async removeFacts(ids) {
1566
+ if (ids.length === 0) return;
1567
+ const escaped = ids.map((id) => `'${id.replace(/'/g, "''")}'`).join(", ");
1568
+ await this.table.delete(`id IN (${escaped})`);
1569
+ }
1570
+ async close() {
1571
+ }
1572
+ };
1573
+ function escapeSqlValue(value) {
1574
+ for (let i = 0; i < value.length; i++) {
1575
+ const code = value.charCodeAt(i);
1576
+ if (code === 0 || code >= 1 && code <= 31 || code === 127) {
1577
+ throw new errors.ConfigurationError(
1578
+ `SQL filter value contains control character at position ${i} (0x${code.toString(16)}). This may indicate an injection attempt.`,
1579
+ { code: "sql_injection_blocked" }
1580
+ );
1581
+ }
1582
+ }
1583
+ return value.replace(/\\/g, "\\\\").replace(/'/g, "''");
1584
+ }
1585
+ function isLanceAvailable() {
1586
+ try {
1587
+ const r = module$1.createRequire((typeof document === 'undefined' ? require('u' + 'rl').pathToFileURL(__filename).href : (_documentCurrentScript && _documentCurrentScript.tagName.toUpperCase() === 'SCRIPT' && _documentCurrentScript.src || new URL('index.cjs', document.baseURI).href)));
1588
+ r("@lancedb/lancedb");
1589
+ return true;
1590
+ } catch {
1591
+ return false;
1592
+ }
1593
+ }
1594
+ function lanceStoragePath(cwd) {
1595
+ return path.join(cwd, ".theokit", "memory", "lance");
1596
+ }
1597
+
1598
+ // src/internal/memory-index.ts
1599
+ function parseSearchOptions(options = {}) {
1600
+ return {
1601
+ maxResults: Math.max(1, options.maxResults ?? 10),
1602
+ minScore: options.minScore ?? 0
1603
+ };
1604
+ }
1605
+
1606
+ // src/internal/lance-memory-adapter.ts
1607
+ var DEFAULT_NAMESPACE = "default";
1608
+ var EMPTY_SYNC_RESULT = Object.freeze({
1609
+ filesScanned: 0,
1610
+ filesUpdated: 0,
1611
+ chunksWritten: 0,
1612
+ chunksEmbedded: 0
1613
+ });
1614
+ var LanceMemoryAdapter = class {
1615
+ constructor(inner) {
1616
+ this.inner = inner;
1617
+ }
1618
+ inner;
1619
+ /**
1620
+ * No-op for Lance — see file header. Returns zero counts so callers'
1621
+ * existing logging (`filesScanned: X`) does not break.
1622
+ */
1623
+ async sync() {
1624
+ return EMPTY_SYNC_RESULT;
1625
+ }
1626
+ // jscpd:ignore-start — search signature + early-out is idiomatic
1627
+ // overlap with IndexManager.search; further factoring would hurt
1628
+ // readability (the bodies diverge sharply right after).
1629
+ async search(query, options = {}) {
1630
+ if (query.trim().length === 0) return [];
1631
+ const { maxResults, minScore } = parseSearchOptions(options);
1632
+ const lanceHits = await this.inner.search(query, {
1633
+ namespace: DEFAULT_NAMESPACE,
1634
+ limit: maxResults * 2,
1635
+ ...options.sources !== void 0 ? { sources: options.sources } : {}
1636
+ });
1637
+ return lanceHits.filter((h) => h.score >= minScore).slice(0, maxResults).map((h) => translateLanceHit(h));
1638
+ }
1639
+ status() {
1640
+ return {
1641
+ backend: "hybrid",
1642
+ filesIndexed: 0,
1643
+ chunksIndexed: 0
1644
+ };
1645
+ }
1646
+ async close() {
1647
+ await this.inner.close();
1648
+ }
1649
+ /**
1650
+ * Escape hatch for advanced callers (migration tool, benchmark script)
1651
+ * that need direct access to addFacts/countFacts/removeFacts.
1652
+ */
1653
+ unwrap() {
1654
+ return this.inner;
1655
+ }
1656
+ };
1657
+ function translateLanceHit(hit) {
1658
+ return {
1659
+ path: hit.id,
1660
+ startLine: 0,
1661
+ endLine: 0,
1662
+ score: hit.score,
1663
+ textScore: 0,
1664
+ vectorScore: hit.score,
1665
+ snippet: hit.text.slice(0, 200),
1666
+ source: hit.source,
1667
+ citation: hit.id
1668
+ };
1669
+ }
1670
+
1671
+ // src/internal/index-manager-dispatch.ts
1672
+ var VALID_BACKENDS = ["sqlite-vec", "lance"];
1673
+ function assertValidBackend(backend) {
1674
+ if (!VALID_BACKENDS.includes(backend)) {
1675
+ throw new errors.ConfigurationError(
1676
+ `Invalid memory backend "${backend}". Valid values: ${VALID_BACKENDS.join(", ")}.`,
1677
+ { code: "invalid_memory_backend" }
1678
+ );
1679
+ }
1680
+ }
1681
+ async function openLanceIndex(opts) {
1682
+ if (opts.embedding === void 0) {
1683
+ throw new errors.ConfigurationError(
1684
+ "Lance backend requires an embedding runtime. Pass `embedding: { provider, model, apiKey }` in Memory.create options (or omit `backend` to use SQLite default).",
1685
+ { code: "lance_requires_embedding" }
1686
+ );
1687
+ }
1688
+ const lance = await LanceIndex.open({
1689
+ cwd: opts.cwd,
1690
+ embedding: opts.embedding,
1691
+ ...opts.filePath !== void 0 ? { storagePath: opts.filePath } : {}
1692
+ });
1693
+ return new LanceMemoryAdapter(lance);
1694
+ }
1695
+ var MAX_TURN_CHARS = 2e3;
1696
+ function sessionsDir(cwd) {
1697
+ return path.join(memoryDir(cwd), "sessions");
1698
+ }
1699
+ function sessionSummaryPath(cwd, runId) {
1700
+ return path.join(sessionsDir(cwd), `${sanitizeRunId2(runId)}.md`);
1701
+ }
1702
+ function sanitizeRunId2(runId) {
1703
+ return runId.replace(/[^a-zA-Z0-9_-]/g, "_").slice(0, 128);
1704
+ }
1705
+ function truncate2(text) {
1706
+ if (text.length <= MAX_TURN_CHARS) return text;
1707
+ return `${text.slice(0, MAX_TURN_CHARS)}\u2026`;
1708
+ }
1709
+ async function writeSessionSummary(input) {
1710
+ if (input.status !== "finished") return;
1711
+ const path = sessionSummaryPath(input.cwd, input.runId);
1712
+ await promises.mkdir(sessionsDir(input.cwd), { recursive: true });
1713
+ const safeUser = redactSecrets(truncate2(input.userText));
1714
+ const safeAssistant = redactSecrets(truncate2(input.assistantText));
1715
+ const iso = new Date(input.at).toISOString();
1716
+ const body = [
1717
+ "---",
1718
+ `runId: ${input.runId}`,
1719
+ `agentId: ${input.agentId}`,
1720
+ `at: ${iso}`,
1721
+ `status: ${input.status}`,
1722
+ "---",
1723
+ "",
1724
+ "## User",
1725
+ "",
1726
+ safeUser,
1727
+ "",
1728
+ "## Assistant",
1729
+ "",
1730
+ safeAssistant,
1731
+ ""
1732
+ ].join("\n");
1733
+ await persistence.replaceFileAtomic(path, body);
1734
+ }
1735
+
1736
+ // src/internal/session-loader.ts
1737
+ async function discoverSessionFiles(cwd) {
1738
+ let entries;
1739
+ try {
1740
+ entries = await promises.readdir(sessionsDir(cwd));
1741
+ } catch {
1742
+ return [];
1743
+ }
1744
+ const root = memoryDir(cwd);
1745
+ return entries.filter((entry) => entry.endsWith(".md")).map((entry) => {
1746
+ const absolutePath = path.join(sessionsDir(cwd), entry);
1747
+ return {
1748
+ absolutePath,
1749
+ relPath: relativeToRoot(root, absolutePath)
1750
+ };
1751
+ });
1752
+ }
1753
+ function relativeToRoot(root, absolutePath) {
1754
+ if (absolutePath.startsWith(`${root}/`)) return absolutePath.slice(root.length + 1);
1755
+ return absolutePath;
1756
+ }
1757
+ async function loadSqliteVecExtension(db) {
1758
+ try {
1759
+ const mod = await import('sqlite-vec');
1760
+ const loadFn = mod.load;
1761
+ if (typeof loadFn !== "function") {
1762
+ throw new Error("sqlite-vec module exposes no `load` export");
1763
+ }
1764
+ loadFn(db);
1765
+ } catch (cause) {
1766
+ const message = cause instanceof Error ? cause.message : String(cause);
1767
+ throw new errors.ConfigurationError(
1768
+ `sqlite-vec extension unavailable. Install \`sqlite-vec\` and ensure the native binary matches your Node + OS. Cause: ${message}`,
1769
+ { code: "sqlite_vec_unavailable", cause }
1770
+ );
1771
+ }
1772
+ }
1773
+ function isSqliteVecLoaded(db) {
1774
+ try {
1775
+ const row = db.prepare("SELECT vec_version() as v").get();
1776
+ return row !== void 0 && row.v !== void 0;
1777
+ } catch {
1778
+ return false;
1779
+ }
1780
+ }
1781
+
1782
+ // src/internal/vec-index.ts
1783
+ var META_KEY_PROVIDER_ID = "embedding.providerId";
1784
+ var META_KEY_MODEL = "embedding.model";
1785
+ var META_KEY_DIMENSION = "embedding.dimension";
1786
+ function readEmbeddingIdentity(db) {
1787
+ const get = (key) => {
1788
+ const row = db.prepare("SELECT value FROM meta WHERE key = ?").get(key);
1789
+ return row !== void 0 ? String(row.value) : void 0;
1790
+ };
1791
+ const providerId = get(META_KEY_PROVIDER_ID);
1792
+ const model = get(META_KEY_MODEL);
1793
+ const dimRaw = get(META_KEY_DIMENSION);
1794
+ if (providerId === void 0 || model === void 0 || dimRaw === void 0) return void 0;
1795
+ const dimension = Number(dimRaw);
1796
+ if (!Number.isFinite(dimension) || dimension <= 0) return void 0;
1797
+ return { providerId, model, dimension };
1798
+ }
1799
+ function writeEmbeddingIdentity(db, identity) {
1800
+ const stmt = db.prepare(
1801
+ "INSERT INTO meta (key, value) VALUES (?, ?) ON CONFLICT(key) DO UPDATE SET value = excluded.value"
1802
+ );
1803
+ stmt.run(META_KEY_PROVIDER_ID, identity.providerId);
1804
+ stmt.run(META_KEY_MODEL, identity.model);
1805
+ stmt.run(META_KEY_DIMENSION, String(identity.dimension));
1806
+ }
1807
+ function identityMatches(a, b) {
1808
+ return a.providerId === b.providerId && a.model === b.model && a.dimension === b.dimension;
1809
+ }
1810
+ function dropVectorIndex(db) {
1811
+ db.exec("DROP TABLE IF EXISTS embeddings");
1812
+ }
1813
+ function createVectorIndex(db, dimension) {
1814
+ db.exec(`CREATE VIRTUAL TABLE IF NOT EXISTS embeddings USING vec0(
1815
+ chunk_id INTEGER PRIMARY KEY,
1816
+ vec FLOAT[${dimension}]
1817
+ )`);
1818
+ }
1819
+ function packVector(vec) {
1820
+ const f32 = new Float32Array(vec.length);
1821
+ for (let i = 0; i < vec.length; i++) f32[i] = vec[i] ?? 0;
1822
+ return Buffer.from(f32.buffer);
1823
+ }
1824
+ function upsertEmbedding(db, chunkId, vec) {
1825
+ const id = BigInt(chunkId);
1826
+ db.prepare("DELETE FROM embeddings WHERE chunk_id = ?").run(id);
1827
+ db.prepare("INSERT INTO embeddings (chunk_id, vec) VALUES (?, ?)").run(id, packVector(vec));
1828
+ }
1829
+ function vectorSearch(db, query, k) {
1830
+ const rows = db.prepare(
1831
+ `SELECT chunk_id, distance FROM embeddings
1832
+ WHERE vec MATCH ? AND k = ?
1833
+ ORDER BY distance`
1834
+ ).all(packVector(query), BigInt(k));
1835
+ return rows.map((row) => ({ chunk_id: Number(row.chunk_id), distance: Number(row.distance) }));
1836
+ }
1837
+ async function embedMissingChunks(args) {
1838
+ const rows = args.db.prepare(
1839
+ `SELECT chunks.id as id, chunks.text as text
1840
+ FROM chunks
1841
+ LEFT JOIN embeddings ON embeddings.chunk_id = chunks.id
1842
+ WHERE embeddings.chunk_id IS NULL`
1843
+ ).all();
1844
+ if (rows.length === 0) return 0;
1845
+ const normalized = rows.map((row) => ({ id: Number(row.id), text: String(row.text ?? "") }));
1846
+ const vectors = await args.runtime.embed(normalized.map((r) => r.text));
1847
+ for (let i = 0; i < normalized.length; i++) {
1848
+ const row = normalized[i];
1849
+ const vector = vectors[i];
1850
+ if (row === void 0 || vector === void 0) continue;
1851
+ upsertEmbedding(args.db, row.id, vector);
1852
+ }
1853
+ return normalized.length;
1854
+ }
1855
+ function wikiDir(cwd) {
1856
+ return path.join(memoryDir(cwd), "wiki");
1857
+ }
1858
+ async function discoverWikiFiles(cwd) {
1859
+ let entries;
1860
+ try {
1861
+ entries = await promises.readdir(wikiDir(cwd));
1862
+ } catch {
1863
+ return [];
1864
+ }
1865
+ const root = memoryDir(cwd);
1866
+ return entries.filter((entry) => entry.endsWith(".md")).map((entry) => ({
1867
+ absolutePath: path.join(wikiDir(cwd), entry),
1868
+ relPath: path.join("wiki", entry)
1869
+ })).map((file) => ({
1870
+ absolutePath: file.absolutePath,
1871
+ relPath: relativeToRoot2(root, file.absolutePath)
1872
+ }));
1873
+ }
1874
+ function relativeToRoot2(root, absolutePath) {
1875
+ if (absolutePath.startsWith(`${root}/`)) return absolutePath.slice(root.length + 1);
1876
+ return absolutePath;
1877
+ }
1878
+
1879
+ // src/internal/index-manager.ts
1880
+ var IndexManager = class _IndexManager {
1881
+ constructor(cwd, db, embedding) {
1882
+ this.cwd = cwd;
1883
+ this.db = db;
1884
+ this.embedding = embedding;
1885
+ }
1886
+ cwd;
1887
+ db;
1888
+ embedding;
1889
+ lastSyncMs;
1890
+ vectorReady = false;
1891
+ static async open(opts) {
1892
+ const backend = opts.backend ?? "sqlite-vec";
1893
+ assertValidBackend(backend);
1894
+ if (backend === "lance") return await openLanceIndex(opts);
1895
+ return await _IndexManager.openSqliteInternal(opts);
1896
+ }
1897
+ /** Internal SQLite-path open. Renamed from previous public `open`. */
1898
+ static async openSqliteInternal(opts) {
1899
+ const filePath = opts.filePath ?? defaultIndexPath(opts.cwd);
1900
+ const db = await openMemoryDb({ filePath });
1901
+ const manager = new _IndexManager(opts.cwd, db, opts.embedding);
1902
+ if (opts.embedding !== void 0) await manager.initVectorBackend(opts.embedding);
1903
+ return manager;
1904
+ }
1905
+ async initVectorBackend(runtime) {
1906
+ await loadSqliteVecExtension(this.db);
1907
+ const currentIdentity = {
1908
+ providerId: runtime.id,
1909
+ model: runtime.model,
1910
+ dimension: runtime.dimension
1911
+ };
1912
+ const persisted = readEmbeddingIdentity(this.db);
1913
+ if (persisted !== void 0 && !identityMatches(persisted, currentIdentity)) {
1914
+ dropVectorIndex(this.db);
1915
+ }
1916
+ createVectorIndex(this.db, runtime.dimension);
1917
+ writeEmbeddingIdentity(this.db, currentIdentity);
1918
+ this.vectorReady = true;
1919
+ }
1920
+ /** Walk the memory corpus + (re)index changed files. */
1921
+ async sync() {
1922
+ const files = await collectMarkdownFiles(this.cwd);
1923
+ let filesUpdated = 0;
1924
+ let chunksWritten = 0;
1925
+ const existingByPath = this.loadFilesIndex();
1926
+ for (const entry of files) {
1927
+ const raw = await promises.readFile(entry.absolutePath, "utf8");
1928
+ const hash = sha256(raw);
1929
+ const existing = existingByPath.get(entry.absolutePath);
1930
+ if (existing !== void 0 && existing.hash === hash) continue;
1931
+ const stats = await promises.stat(entry.absolutePath);
1932
+ const fileId = this.upsertFile(
1933
+ entry.absolutePath,
1934
+ entry.relPath,
1935
+ hash,
1936
+ stats.mtimeMs,
1937
+ entry.source
1938
+ );
1939
+ this.deleteChunksForFile(fileId);
1940
+ const chunks = chunkMarkdown(raw);
1941
+ for (const chunk of chunks) {
1942
+ this.insertChunk(fileId, chunk.startLine, chunk.endLine, chunk.text, chunk.hash);
1943
+ }
1944
+ filesUpdated += 1;
1945
+ chunksWritten += chunks.length;
1946
+ }
1947
+ let chunksEmbedded = 0;
1948
+ if (this.vectorReady && this.embedding !== void 0) {
1949
+ chunksEmbedded = await embedMissingChunks({ db: this.db, runtime: this.embedding });
1950
+ }
1951
+ this.lastSyncMs = Date.now();
1952
+ return { filesScanned: files.length, filesUpdated, chunksWritten, chunksEmbedded };
1953
+ }
1954
+ async search(query, options = {}) {
1955
+ if (query.trim().length === 0) return [];
1956
+ const { maxResults, minScore } = parseSearchOptions(options);
1957
+ const textHits = this.ftsSearch(query, maxResults * 2);
1958
+ const vectorHitsById = await this.vectorSearchById(query, maxResults * 2);
1959
+ const combined = this.combineHybridScores(textHits, vectorHitsById, options);
1960
+ return combined.filter((h) => h.score >= minScore).filter((h) => options.sources === void 0 || options.sources.includes(h.source)).slice(0, maxResults);
1961
+ }
1962
+ status() {
1963
+ const files = this.db.prepare("SELECT COUNT(*) as n FROM files").get() ?? { n: 0 };
1964
+ const chunks = this.db.prepare("SELECT COUNT(*) as n FROM chunks").get() ?? { n: 0 };
1965
+ const status = {
1966
+ backend: this.vectorReady ? "hybrid" : "fts-only",
1967
+ filesIndexed: Number(files.n ?? 0),
1968
+ chunksIndexed: Number(chunks.n ?? 0)
1969
+ };
1970
+ if (this.lastSyncMs !== void 0) status.lastSyncMs = this.lastSyncMs;
1971
+ return status;
1972
+ }
1973
+ // ───── search internals ──────────────────────────────────────────────
1974
+ ftsSearch(query, limit) {
1975
+ const sanitized = persistence.sanitizeFts5Query(query);
1976
+ if (sanitized.length === 0) return [];
1977
+ const stmt = this.db.prepare(
1978
+ `SELECT chunks.id as id, files.rel_path as rel_path, files.source as source,
1979
+ chunks.start_line as start_line, chunks.end_line as end_line,
1980
+ chunks.text as text, bm25(chunks_fts) as bm25_score
1981
+ FROM chunks_fts
1982
+ JOIN chunks ON chunks_fts.rowid = chunks.id
1983
+ JOIN files ON chunks.file_id = files.id
1984
+ WHERE chunks_fts MATCH ?
1985
+ ORDER BY bm25_score
1986
+ LIMIT ?`
1987
+ );
1988
+ let rows = [];
1989
+ try {
1990
+ rows = stmt.all(sanitized, limit);
1991
+ } catch {
1992
+ return [];
1993
+ }
1994
+ return rows.map((row) => {
1995
+ const bm25 = Number(row.bm25_score ?? 0);
1996
+ const textScore = bm25 < 0 ? 1 / (1 + Math.abs(bm25)) : 1 / (1 + bm25);
1997
+ const startLine = Number(row.start_line ?? 0);
1998
+ const endLine = Number(row.end_line ?? 0);
1999
+ const path = String(row.rel_path);
2000
+ return {
2001
+ path,
2002
+ startLine,
2003
+ endLine,
2004
+ score: textScore,
2005
+ textScore,
2006
+ snippet: truncateSnippet(String(row.text ?? "")),
2007
+ source: String(row.source),
2008
+ citation: `${path}:${startLine}-${endLine}`,
2009
+ // sneak the chunk id through for the hybrid join below
2010
+ // (cast away later)
2011
+ chunkId: Number(row.id)
2012
+ };
2013
+ });
2014
+ }
2015
+ async vectorSearchById(query, limit) {
2016
+ if (!this.vectorReady || this.embedding === void 0) return /* @__PURE__ */ new Map();
2017
+ const [queryVec] = await this.embedding.embed([query]);
2018
+ if (queryVec === void 0) return /* @__PURE__ */ new Map();
2019
+ const rows = vectorSearch(this.db, queryVec, limit);
2020
+ const out = /* @__PURE__ */ new Map();
2021
+ for (const row of rows) {
2022
+ const score = 1 / (1 + Math.max(0, row.distance));
2023
+ out.set(row.chunk_id, { vectorScore: score });
2024
+ }
2025
+ return out;
2026
+ }
2027
+ combineHybridScores(textHits, vectorHitsById, options) {
2028
+ const weights = resolveWeights(options);
2029
+ const merged = this.mergeHits(textHits, vectorHitsById);
2030
+ const combined = [...merged.values()].map(
2031
+ (hit) => blendScores(hit, vectorHitsById.get(hit.chunkId)?.vectorScore ?? 0, weights)
2032
+ );
2033
+ return combined.sort((a, b) => b.score - a.score);
2034
+ }
2035
+ mergeHits(textHits, vectorHitsById) {
2036
+ const merged = /* @__PURE__ */ new Map();
2037
+ for (const hit of textHits) merged.set(hit.chunkId, hit);
2038
+ const missingIds = [...vectorHitsById.keys()].filter((id) => !merged.has(id));
2039
+ if (missingIds.length > 0) {
2040
+ for (const hit of this.fetchChunksByIds(missingIds)) {
2041
+ merged.set(hit.chunkId, { ...hit, score: 0, textScore: 0 });
2042
+ }
2043
+ }
2044
+ return merged;
2045
+ }
2046
+ fetchChunksByIds(ids) {
2047
+ if (ids.length === 0) return [];
2048
+ const placeholders = ids.map(() => "?").join(",");
2049
+ const rows = this.db.prepare(
2050
+ `SELECT chunks.id as id, files.rel_path as rel_path, files.source as source,
2051
+ chunks.start_line as start_line, chunks.end_line as end_line,
2052
+ chunks.text as text
2053
+ FROM chunks JOIN files ON chunks.file_id = files.id
2054
+ WHERE chunks.id IN (${placeholders})`
2055
+ ).all(...ids);
2056
+ return rows.map((row) => {
2057
+ const startLine = Number(row.start_line ?? 0);
2058
+ const endLine = Number(row.end_line ?? 0);
2059
+ const path = String(row.rel_path);
2060
+ return {
2061
+ chunkId: Number(row.id),
2062
+ path,
2063
+ startLine,
2064
+ endLine,
2065
+ score: 0,
2066
+ textScore: 0,
2067
+ snippet: truncateSnippet(String(row.text ?? "")),
2068
+ source: String(row.source),
2069
+ citation: `${path}:${startLine}-${endLine}`
2070
+ };
2071
+ });
2072
+ }
2073
+ // ───── persistence helpers ─────────────────────────────────────────
2074
+ loadFilesIndex() {
2075
+ const rows = this.db.prepare("SELECT id, path, hash FROM files").all();
2076
+ return new Map(rows.map((row) => [row.path, { id: row.id, hash: row.hash }]));
2077
+ }
2078
+ upsertFile(absPath, relPath, hash, mtimeMs, source = "memory") {
2079
+ const stmt = this.db.prepare(
2080
+ `INSERT INTO files (path, rel_path, mtime, hash, source) VALUES (?, ?, ?, ?, ?)
2081
+ ON CONFLICT(path) DO UPDATE SET hash = excluded.hash, mtime = excluded.mtime, source = excluded.source
2082
+ RETURNING id`
2083
+ );
2084
+ const row = stmt.get(absPath, relPath, Math.floor(mtimeMs), hash, source);
2085
+ return row.id;
2086
+ }
2087
+ deleteChunksForFile(fileId) {
2088
+ this.db.prepare("DELETE FROM chunks WHERE file_id = ?").run(fileId);
2089
+ }
2090
+ insertChunk(fileId, startLine, endLine, text, hash) {
2091
+ this.db.prepare(
2092
+ "INSERT INTO chunks (file_id, start_line, end_line, text, hash) VALUES (?, ?, ?, ?, ?)"
2093
+ ).run(fileId, startLine, endLine, text, hash);
2094
+ }
2095
+ close() {
2096
+ this.db.close();
2097
+ }
2098
+ };
2099
+ function resolveWeights(options) {
2100
+ const vectorWeight = options.vectorWeight ?? 0.6;
2101
+ const textWeight = options.textWeight ?? 0.4;
2102
+ const total = vectorWeight + textWeight || 1;
2103
+ return { vectorWeight, textWeight, total };
2104
+ }
2105
+ function blendScores(hit, vectorScore, weights) {
2106
+ const score = (vectorScore * weights.vectorWeight + hit.textScore * weights.textWeight) / weights.total;
2107
+ return {
2108
+ path: hit.path,
2109
+ startLine: hit.startLine,
2110
+ endLine: hit.endLine,
2111
+ score,
2112
+ textScore: hit.textScore,
2113
+ snippet: hit.snippet,
2114
+ source: hit.source,
2115
+ citation: hit.citation,
2116
+ ...vectorScore > 0 ? { vectorScore } : {}
2117
+ };
2118
+ }
2119
+ async function collectMarkdownFiles(cwd) {
2120
+ const root = memoryDir(cwd);
2121
+ const results = [];
2122
+ try {
2123
+ await promises.stat(memoryMdPath(cwd));
2124
+ results.push({
2125
+ absolutePath: memoryMdPath(cwd),
2126
+ relPath: path.relative(root, memoryMdPath(cwd)),
2127
+ source: "memory"
2128
+ });
2129
+ } catch {
2130
+ }
2131
+ try {
2132
+ const entries = await promises.readdir(notesDir(cwd));
2133
+ for (const entry of entries) {
2134
+ if (!entry.endsWith(".md")) continue;
2135
+ const abs = path.join(notesDir(cwd), entry);
2136
+ results.push({ absolutePath: abs, relPath: path.relative(root, abs), source: "memory" });
2137
+ }
2138
+ } catch {
2139
+ }
2140
+ const wikiFiles = await discoverWikiFiles(cwd);
2141
+ for (const wiki of wikiFiles) {
2142
+ results.push({
2143
+ absolutePath: wiki.absolutePath,
2144
+ relPath: wiki.relPath,
2145
+ source: "wiki"
2146
+ });
2147
+ }
2148
+ const sessionFiles = await discoverSessionFiles(cwd);
2149
+ for (const session of sessionFiles) {
2150
+ results.push({
2151
+ absolutePath: session.absolutePath,
2152
+ relPath: session.relPath,
2153
+ source: "sessions"
2154
+ });
2155
+ }
2156
+ return results;
2157
+ }
2158
+ function sha256(text) {
2159
+ return crypto.createHash("sha256").update(text).digest("hex");
2160
+ }
2161
+ function truncateSnippet(text) {
2162
+ const max = 500;
2163
+ return text.length <= max ? text : `${text.slice(0, max)}\u2026`;
2164
+ }
2165
+ async function readAllSqliteFacts(cwd) {
2166
+ const dbPath = defaultIndexPath(cwd);
2167
+ if (!fs.existsSync(dbPath)) return [];
2168
+ const db = await openMemoryDb({ filePath: dbPath });
2169
+ try {
2170
+ const stmt = db.prepare("SELECT id, path, source, start_line, end_line, text FROM chunks");
2171
+ const rows = stmt.all();
2172
+ return rows.map((r) => ({
2173
+ ...r,
2174
+ namespace: "default",
2175
+ scope: "agent",
2176
+ user_id: "default"
2177
+ }));
2178
+ } finally {
2179
+ db.close();
2180
+ }
2181
+ }
2182
+ function nfcEqual(a, b) {
2183
+ return a.normalize("NFC") === b.normalize("NFC");
2184
+ }
2185
+ async function migrateSqliteToLance(opts) {
2186
+ const cwd = opts.cwd;
2187
+ const finalPath = lanceStoragePath(cwd);
2188
+ const newPath = path.join(cwd, ".theokit", "memory", "lance-new");
2189
+ const rawLog = opts.logger ?? ((m) => console.log(m));
2190
+ const log = (m) => rawLog(redactSecrets(m));
2191
+ if (fs.existsSync(finalPath)) {
2192
+ throw new errors.ConfigurationError(
2193
+ `Destination already exists: ${finalPath}. Remove it manually (\`rm -rf ${finalPath}\`) and re-run.`,
2194
+ { code: "migration_destination_exists" }
2195
+ );
2196
+ }
2197
+ if (fs.existsSync(newPath)) {
2198
+ log(`Removing stale ${newPath} from a previous run...`);
2199
+ fs.rmSync(newPath, { recursive: true, force: true });
2200
+ }
2201
+ log(`Reading SQLite facts from ${cwd}/.theokit/memory/index.sqlite ...`);
2202
+ const sqliteFacts = await readAllSqliteFacts(cwd);
2203
+ log(`SQLite has ${sqliteFacts.length} facts.`);
2204
+ if (sqliteFacts.length === 0) {
2205
+ return {
2206
+ countSqlite: 0,
2207
+ countLance: 0,
2208
+ validated: true,
2209
+ sampleComparisons: [],
2210
+ lancePath: finalPath,
2211
+ committed: false
2212
+ };
2213
+ }
2214
+ const placeholderEmbedding = {
2215
+ id: "migration-placeholder",
2216
+ model: "deterministic-hash",
2217
+ dimension: 8,
2218
+ embed: async (texts) => {
2219
+ return texts.map((t) => {
2220
+ const arr = new Array(8).fill(0);
2221
+ for (let i = 0; i < t.length; i += 1) {
2222
+ arr[i % 8] += t.charCodeAt(i);
2223
+ }
2224
+ return arr.map((x) => x / 1e3);
2225
+ });
2226
+ },
2227
+ stats: () => ({ cacheHits: 0, cacheMisses: 0, httpCalls: 0, retries: 0 })
2228
+ };
2229
+ fs.mkdirSync(newPath, { recursive: true });
2230
+ log(`Writing Lance index to ${newPath} ...`);
2231
+ const lance = await LanceIndex.open({
2232
+ cwd,
2233
+ embedding: placeholderEmbedding,
2234
+ storagePath: newPath
2235
+ });
2236
+ const batchSize = opts.batchSize ?? 100;
2237
+ for (let i = 0; i < sqliteFacts.length; i += batchSize) {
2238
+ const batch = sqliteFacts.slice(i, i + batchSize).map((f) => ({
2239
+ id: f.id,
2240
+ text: f.text,
2241
+ source: f.source,
2242
+ namespace: f.namespace ?? "default",
2243
+ scope: f.scope ?? "agent",
2244
+ user_id: f.user_id ?? "default",
2245
+ timestamp: 0
2246
+ }));
2247
+ await lance.addFacts(batch);
2248
+ log(` Migrated ${Math.min(i + batchSize, sqliteFacts.length)}/${sqliteFacts.length}`);
2249
+ }
2250
+ const lanceCount = await lance.countFacts("default");
2251
+ log(`Lance has ${lanceCount} facts (expected ${sqliteFacts.length}).`);
2252
+ const sampleSize = Math.min(10, sqliteFacts.length);
2253
+ const sampleComparisons = [];
2254
+ for (let i = 0; i < sampleSize; i += 1) {
2255
+ const sqliteFact = sqliteFacts[Math.floor(i * sqliteFacts.length / sampleSize)];
2256
+ if (sqliteFact === void 0) continue;
2257
+ const hits = await lance.search(sqliteFact.text, {
2258
+ namespace: sqliteFact.namespace ?? "default",
2259
+ limit: 1
2260
+ });
2261
+ const hit = hits[0];
2262
+ const match = hit !== void 0 && nfcEqual(hit.text, sqliteFact.text);
2263
+ sampleComparisons.push({ id: sqliteFact.id, match });
2264
+ }
2265
+ const allMatch = sampleComparisons.every((c) => c.match);
2266
+ const countMatch = lanceCount === sqliteFacts.length;
2267
+ const validated = allMatch && countMatch;
2268
+ await lance.close();
2269
+ if (opts.dryRun === true) {
2270
+ log("Dry-run mode \u2014 discarding Lance staging dir.");
2271
+ fs.rmSync(newPath, { recursive: true, force: true });
2272
+ return {
2273
+ countSqlite: sqliteFacts.length,
2274
+ countLance: lanceCount,
2275
+ validated,
2276
+ sampleComparisons,
2277
+ lancePath: finalPath,
2278
+ committed: false
2279
+ };
2280
+ }
2281
+ if (!validated) {
2282
+ log("Validation FAILED \u2014 leaving SQLite intact, removing Lance staging dir.");
2283
+ fs.rmSync(newPath, { recursive: true, force: true });
2284
+ return {
2285
+ countSqlite: sqliteFacts.length,
2286
+ countLance: lanceCount,
2287
+ validated: false,
2288
+ sampleComparisons,
2289
+ lancePath: finalPath,
2290
+ committed: false
2291
+ };
2292
+ }
2293
+ fs.renameSync(newPath, finalPath);
2294
+ log(`Migration committed to ${finalPath}.`);
2295
+ return {
2296
+ countSqlite: sqliteFacts.length,
2297
+ countLance: lanceCount,
2298
+ validated: true,
2299
+ sampleComparisons,
2300
+ lancePath: finalPath,
2301
+ committed: true
2302
+ };
2303
+ }
2304
+ async function fileExists(path) {
2305
+ try {
2306
+ await promises.access(path);
2307
+ return true;
2308
+ } catch {
2309
+ return false;
2310
+ }
2311
+ }
2312
+ async function readLegacyFacts(jsonPath) {
2313
+ try {
2314
+ const raw = await promises.readFile(jsonPath, "utf8");
2315
+ const parsed = JSON.parse(raw);
2316
+ return Array.isArray(parsed.facts) ? parsed.facts : [];
2317
+ } catch {
2318
+ return void 0;
2319
+ }
2320
+ }
2321
+ async function writeMigratedFacts(cwd, jsonPath, facts) {
2322
+ try {
2323
+ for (const fact of facts) await appendFactToMarkdown(cwd, fact);
2324
+ await promises.unlink(jsonPath).catch(() => void 0);
2325
+ process.stderr.write(
2326
+ `[theokit-sdk] migrated ${facts.length} fact(s) from ${jsonPath} to MEMORY.md
2327
+ `
2328
+ );
2329
+ return { migrated: true, factCount: facts.length };
2330
+ } catch (cause) {
2331
+ const message = cause instanceof Error ? cause.message : String(cause);
2332
+ process.stderr.write(`[theokit-sdk] memory migration failed (readonly fs?): ${message}
2333
+ `);
2334
+ return { migrated: false, factCount: 0, reason: "readonly-fs" };
2335
+ }
2336
+ }
2337
+ var migrationRun = /* @__PURE__ */ new Set();
2338
+ async function migrateLegacyJson(cwd, config) {
2339
+ const key = `${cwd}::${config.namespace ?? "default"}::${config.scope ?? "agent"}::${config.userId ?? "default"}`;
2340
+ if (migrationRun.has(key)) return { migrated: false, factCount: 0, reason: "already-migrated" };
2341
+ migrationRun.add(key);
2342
+ const jsonPath = legacyMemoryJsonPath(cwd, config);
2343
+ if (!await fileExists(jsonPath)) {
2344
+ return { migrated: false, factCount: 0, reason: "no-legacy-json" };
2345
+ }
2346
+ if (await fileExists(memoryMdPath(cwd))) {
2347
+ process.stderr.write(
2348
+ `[theokit-sdk] memory migration skipped: both MEMORY.md and legacy JSON exist at ${jsonPath}; leaving both intact
2349
+ `
2350
+ );
2351
+ return { migrated: false, factCount: 0, reason: "markdown-exists" };
2352
+ }
2353
+ const facts = await readLegacyFacts(jsonPath);
2354
+ if (facts === void 0) return { migrated: false, factCount: 0, reason: "no-legacy-json" };
2355
+ return writeMigratedFacts(cwd, jsonPath, facts);
2356
+ }
2357
+ function resetMigrationStateForTests() {
2358
+ migrationRun.clear();
2359
+ }
2360
+ var DEFAULT_MEMORY_READ_LINES = 200;
2361
+ async function readMemoryFileBounded(opts) {
2362
+ const absolutePath = path.resolve(opts.cwd, opts.relPath);
2363
+ const raw = await promises.readFile(absolutePath, "utf8");
2364
+ const allLines = raw.split("\n");
2365
+ const trimmedLines = allLines.length > 0 && allLines[allLines.length - 1] === "" ? allLines.slice(0, -1) : allLines;
2366
+ const totalLines = trimmedLines.length;
2367
+ const requestedFrom = Math.max(1, opts.from ?? 1);
2368
+ const requestedLines = Math.max(1, opts.lines ?? DEFAULT_MEMORY_READ_LINES);
2369
+ const startIdx = requestedFrom - 1;
2370
+ const endIdx = Math.min(totalLines, startIdx + requestedLines);
2371
+ const slice = trimmedLines.slice(startIdx, endIdx);
2372
+ const linesReturned = slice.length;
2373
+ const remainingLines = Math.max(0, totalLines - endIdx);
2374
+ const truncated = remainingLines > 0;
2375
+ return {
2376
+ path: opts.relPath,
2377
+ from: requestedFrom,
2378
+ linesReturned,
2379
+ totalLines,
2380
+ truncated,
2381
+ remainingLines,
2382
+ text: slice.join("\n")
2383
+ };
2384
+ }
2385
+ var SEARCH_DESCRIPTION = "Mandatory recall step: semantically search MEMORY.md + memory/notes/*.md (and optional wiki supplements) before answering questions about prior work, decisions, dates, people, preferences, or todos. `corpus=memory` (default) restricts to indexed memory files; `corpus=wiki` restricts to read-only wiki supplements; `corpus=all` returns both. Returns ranked `{ path, startLine, endLine, score, snippet, citation }` hits.";
2386
+ var GET_DESCRIPTION = "Safe exact excerpt read from MEMORY.md or memory/notes/*.md. Defaults to a bounded excerpt (200 lines). Returns `{ path, from, linesReturned, totalLines, truncated, remainingLines, text }`. Paths are resolved against the memory root \u2014 attempts to read outside `.theokit/memory/` are rejected.";
2387
+ var MEMORY_SEARCH_SCHEMA = {
2388
+ type: "object",
2389
+ required: ["query"],
2390
+ properties: {
2391
+ query: { type: "string", description: "Natural-language query." },
2392
+ maxResults: { type: "integer", minimum: 1, maximum: 50 },
2393
+ minScore: { type: "number", minimum: 0, maximum: 1 },
2394
+ corpus: { type: "string", enum: ["memory", "sessions", "wiki", "all"] }
2395
+ },
2396
+ additionalProperties: false
2397
+ };
2398
+ var MEMORY_GET_SCHEMA = {
2399
+ type: "object",
2400
+ required: ["path"],
2401
+ properties: {
2402
+ path: { type: "string", description: "Path relative to .theokit/memory/" },
2403
+ from: { type: "integer", minimum: 1 },
2404
+ lines: { type: "integer", minimum: 1, maximum: 2e3 }
2405
+ },
2406
+ additionalProperties: false
2407
+ };
2408
+ var DEFAULT_MAX_TOTAL_CHARS = 16384;
2409
+ function createMemorySearchTool(opts) {
2410
+ const cap = opts.maxTotalChars ?? DEFAULT_MAX_TOTAL_CHARS;
2411
+ return {
2412
+ name: "memory_search",
2413
+ description: SEARCH_DESCRIPTION,
2414
+ inputSchema: MEMORY_SEARCH_SCHEMA,
2415
+ async execute(input) {
2416
+ const query = readString(input.query, "query is required");
2417
+ const maxResults = readNumber(input.maxResults) ?? 10;
2418
+ const minScore = readNumber(input.minScore);
2419
+ const corpus = readString(input.corpus, void 0, true);
2420
+ const sources = mapCorpusToSources(corpus);
2421
+ const searchOptions = {
2422
+ maxResults,
2423
+ ...minScore !== void 0 ? { minScore } : {},
2424
+ ...sources !== void 0 ? { sources } : {}
2425
+ };
2426
+ const hits = await opts.index.search(query, searchOptions);
2427
+ return JSON.stringify(capByTotalChars(hits, cap));
2428
+ }
2429
+ };
2430
+ }
2431
+ function createMemoryGetTool(opts) {
2432
+ const memoryRoot = path.resolve(memoryDir(opts.cwd));
2433
+ return {
2434
+ name: "memory_get",
2435
+ description: GET_DESCRIPTION,
2436
+ inputSchema: MEMORY_GET_SCHEMA,
2437
+ async execute(input) {
2438
+ const relPath = readString(input.path, "path is required");
2439
+ const from = readNumber(input.from);
2440
+ const lines = readNumber(input.lines);
2441
+ const resolved = path.resolve(memoryRoot, relPath);
2442
+ if (!isPathInside(memoryRoot, resolved)) {
2443
+ throw new errors.ConfigurationError(
2444
+ `memory_get rejected path that escapes memory root: ${relPath}`,
2445
+ { code: "memory_path_escapes_root" }
2446
+ );
2447
+ }
2448
+ const readArgs = {
2449
+ cwd: memoryRoot,
2450
+ relPath
2451
+ };
2452
+ if (from !== void 0) readArgs.from = from;
2453
+ if (lines !== void 0) readArgs.lines = lines;
2454
+ const result = await readMemoryFileBounded(readArgs);
2455
+ return JSON.stringify(result);
2456
+ }
2457
+ };
2458
+ }
2459
+ function readString(value, errorIfMissing, allowUndefined = false) {
2460
+ if (typeof value === "string") return value;
2461
+ if (allowUndefined && (value === void 0 || value === null)) return "";
2462
+ if (errorIfMissing !== void 0) {
2463
+ throw new errors.ConfigurationError(errorIfMissing, { code: "memory_tool_bad_args" });
2464
+ }
2465
+ return "";
2466
+ }
2467
+ function readNumber(value) {
2468
+ if (typeof value === "number" && Number.isFinite(value)) return value;
2469
+ if (typeof value === "string" && value.trim().length > 0) {
2470
+ const n = Number(value);
2471
+ if (Number.isFinite(n)) return n;
2472
+ }
2473
+ return void 0;
2474
+ }
2475
+ function mapCorpusToSources(corpus) {
2476
+ if (corpus === "" || corpus === "all") return void 0;
2477
+ if (corpus === "memory") return ["memory"];
2478
+ if (corpus === "wiki") return ["wiki"];
2479
+ if (corpus === "sessions") return ["sessions"];
2480
+ return void 0;
2481
+ }
2482
+ function capByTotalChars(hits, maxTotalChars) {
2483
+ if (hits.length === 0) return { hits: [], truncated: false };
2484
+ const kept = [];
2485
+ let runningChars = 0;
2486
+ for (const hit of hits) {
2487
+ const snippetLen = hit.snippet.length + (hit.citation?.length ?? 0) + 32;
2488
+ if (kept.length > 0 && runningChars + snippetLen > maxTotalChars) {
2489
+ return { hits: kept, truncated: true };
2490
+ }
2491
+ kept.push(hit);
2492
+ runningChars += snippetLen;
2493
+ }
2494
+ return { hits: kept, truncated: false };
2495
+ }
2496
+ function isPathInside(root, candidate) {
2497
+ const normalizedRoot = root.endsWith("/") ? root : `${root}/`;
2498
+ return candidate === root || candidate.startsWith(normalizedRoot);
2499
+ }
2500
+
2501
+ exports.ActiveMemoryCache = ActiveMemoryCache;
2502
+ exports.CircuitBreaker = CircuitBreaker;
2503
+ exports.DEFAULT_DEEPINFRA_EMBEDDING_MODEL = DEFAULT_DEEPINFRA_EMBEDDING_MODEL;
2504
+ exports.DEFAULT_MEMORY_READ_LINES = DEFAULT_MEMORY_READ_LINES;
2505
+ exports.DEFAULT_MISTRAL_EMBEDDING_MODEL = DEFAULT_MISTRAL_EMBEDDING_MODEL;
2506
+ exports.DEFAULT_OLLAMA_EMBEDDING_MODEL = DEFAULT_OLLAMA_EMBEDDING_MODEL;
2507
+ exports.DEFAULT_OPENAI_EMBEDDING_MODEL = DEFAULT_OPENAI_EMBEDDING_MODEL;
2508
+ exports.DEFAULT_OPENROUTER_EMBEDDING_MODEL = DEFAULT_OPENROUTER_EMBEDDING_MODEL;
2509
+ exports.DEFAULT_VOYAGE_EMBEDDING_MODEL = DEFAULT_VOYAGE_EMBEDDING_MODEL;
2510
+ exports.IndexManager = IndexManager;
2511
+ exports.LanceIndex = LanceIndex;
2512
+ exports.LanceMemoryAdapter = LanceMemoryAdapter;
2513
+ exports.MEMORY_EMBEDDING_ADAPTERS = MEMORY_EMBEDDING_ADAPTERS;
2514
+ exports.META_KEY_DIMENSION = META_KEY_DIMENSION;
2515
+ exports.META_KEY_MODEL = META_KEY_MODEL;
2516
+ exports.META_KEY_PROVIDER_ID = META_KEY_PROVIDER_ID;
2517
+ exports.MIGRATION_V2_STATEMENTS = MIGRATION_V2_STATEMENTS;
2518
+ exports.PRAGMA_STATEMENTS = PRAGMA_STATEMENTS;
2519
+ exports.SCHEMA_STATEMENTS = SCHEMA_STATEMENTS;
2520
+ exports.VALID_BACKENDS = VALID_BACKENDS;
2521
+ exports.appendDiaryEntry = appendDiaryEntry;
2522
+ exports.appendFact = appendFact;
2523
+ exports.appendFactToMarkdown = appendFactToMarkdown;
2524
+ exports.assertValidBackend = assertValidBackend;
2525
+ exports.buildErrorMetadata = buildErrorMetadata;
2526
+ exports.chunkMarkdown = chunkMarkdown;
2527
+ exports.createInMemoryMarkdownProvider = createInMemoryMarkdownProvider;
2528
+ exports.createMemoryGetTool = createMemoryGetTool;
2529
+ exports.createMemorySearchTool = createMemorySearchTool;
2530
+ exports.createOpenAiCompatibleRuntime = createOpenAiCompatibleRuntime;
2531
+ exports.createVectorIndex = createVectorIndex;
2532
+ exports.deepPhase = deepPhase;
2533
+ exports.deepinfraMemoryEmbeddingProviderAdapter = deepinfraMemoryEmbeddingProviderAdapter;
2534
+ exports.defaultIndexPath = defaultIndexPath;
2535
+ exports.diaryPath = diaryPath;
2536
+ exports.discoverSessionFiles = discoverSessionFiles;
2537
+ exports.discoverWikiFiles = discoverWikiFiles;
2538
+ exports.dropVectorIndex = dropVectorIndex;
2539
+ exports.embedMissingChunks = embedMissingChunks;
2540
+ exports.entryHash = entryHash;
2541
+ exports.identityMatches = identityMatches;
2542
+ exports.isLanceAvailable = isLanceAvailable;
2543
+ exports.isSqliteVecLoaded = isSqliteVecLoaded;
2544
+ exports.lanceStoragePath = lanceStoragePath;
2545
+ exports.legacyMemoryJsonPath = legacyMemoryJsonPath;
2546
+ exports.lightPhase = lightPhase;
2547
+ exports.listNotes = listNotes;
2548
+ exports.loadSqliteVecExtension = loadSqliteVecExtension;
2549
+ exports.mapOpenAICompatibleError = mapOpenAICompatibleError;
2550
+ exports.memoryDir = memoryDir;
2551
+ exports.memoryMdPath = memoryMdPath;
2552
+ exports.migrateLegacyJson = migrateLegacyJson;
2553
+ exports.migrateSqliteToLance = migrateSqliteToLance;
2554
+ exports.mistralMemoryEmbeddingProviderAdapter = mistralMemoryEmbeddingProviderAdapter;
2555
+ exports.notesDir = notesDir;
2556
+ exports.ollamaMemoryEmbeddingProviderAdapter = ollamaMemoryEmbeddingProviderAdapter;
2557
+ exports.openAiMemoryEmbeddingProviderAdapter = openAiMemoryEmbeddingProviderAdapter;
2558
+ exports.openLanceIndex = openLanceIndex;
2559
+ exports.openMemoryDb = openMemoryDb;
2560
+ exports.openRouterMemoryEmbeddingProviderAdapter = openRouterMemoryEmbeddingProviderAdapter;
2561
+ exports.packVector = packVector;
2562
+ exports.parseRetryAfter = parseRetryAfter;
2563
+ exports.parseSearchOptions = parseSearchOptions;
2564
+ exports.persistActiveMemoryTranscript = persistActiveMemoryTranscript;
2565
+ exports.readEmbeddingIdentity = readEmbeddingIdentity;
2566
+ exports.readFacts = readFacts;
2567
+ exports.readFactsFromMarkdown = readFactsFromMarkdown;
2568
+ exports.readMemoryFileBounded = readMemoryFileBounded;
2569
+ exports.redactSecrets = redactSecrets;
2570
+ exports.remPhase = remPhase;
2571
+ exports.renderDiaryEntry = renderDiaryEntry;
2572
+ exports.resetMigrationStateForTests = resetMigrationStateForTests;
2573
+ exports.runActiveMemory = runActiveMemory;
2574
+ exports.runDreamingSweep = runDreamingSweep;
2575
+ exports.sessionSummaryPath = sessionSummaryPath;
2576
+ exports.sessionsDir = sessionsDir;
2577
+ exports.truncateRaw = truncateRaw;
2578
+ exports.upsertEmbedding = upsertEmbedding;
2579
+ exports.vectorSearch = vectorSearch;
2580
+ exports.voyageMemoryEmbeddingProviderAdapter = voyageMemoryEmbeddingProviderAdapter;
2581
+ exports.wikiDir = wikiDir;
2582
+ exports.writeEmbeddingIdentity = writeEmbeddingIdentity;
2583
+ exports.writeSessionSummary = writeSessionSummary;
2584
+ //# sourceMappingURL=index.cjs.map
2585
+ //# sourceMappingURL=index.cjs.map