context-vault 2.17.0 → 3.0.1

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 (110) hide show
  1. package/bin/cli.js +783 -108
  2. package/node_modules/@context-vault/core/dist/capture.d.ts +21 -0
  3. package/node_modules/@context-vault/core/dist/capture.d.ts.map +1 -0
  4. package/node_modules/@context-vault/core/dist/capture.js +269 -0
  5. package/node_modules/@context-vault/core/dist/capture.js.map +1 -0
  6. package/node_modules/@context-vault/core/dist/categories.d.ts +6 -0
  7. package/node_modules/@context-vault/core/dist/categories.d.ts.map +1 -0
  8. package/node_modules/@context-vault/core/dist/categories.js +50 -0
  9. package/node_modules/@context-vault/core/dist/categories.js.map +1 -0
  10. package/node_modules/@context-vault/core/dist/config.d.ts +4 -0
  11. package/node_modules/@context-vault/core/dist/config.d.ts.map +1 -0
  12. package/node_modules/@context-vault/core/dist/config.js +190 -0
  13. package/node_modules/@context-vault/core/dist/config.js.map +1 -0
  14. package/node_modules/@context-vault/core/dist/constants.d.ts +33 -0
  15. package/node_modules/@context-vault/core/dist/constants.d.ts.map +1 -0
  16. package/node_modules/@context-vault/core/dist/constants.js +23 -0
  17. package/node_modules/@context-vault/core/dist/constants.js.map +1 -0
  18. package/node_modules/@context-vault/core/dist/db.d.ts +13 -0
  19. package/node_modules/@context-vault/core/dist/db.d.ts.map +1 -0
  20. package/node_modules/@context-vault/core/dist/db.js +191 -0
  21. package/node_modules/@context-vault/core/dist/db.js.map +1 -0
  22. package/node_modules/@context-vault/core/dist/embed.d.ts +5 -0
  23. package/node_modules/@context-vault/core/dist/embed.d.ts.map +1 -0
  24. package/node_modules/@context-vault/core/dist/embed.js +78 -0
  25. package/node_modules/@context-vault/core/dist/embed.js.map +1 -0
  26. package/node_modules/@context-vault/core/dist/files.d.ts +13 -0
  27. package/node_modules/@context-vault/core/dist/files.d.ts.map +1 -0
  28. package/node_modules/@context-vault/core/dist/files.js +66 -0
  29. package/node_modules/@context-vault/core/dist/files.js.map +1 -0
  30. package/node_modules/@context-vault/core/dist/formatters.d.ts +8 -0
  31. package/node_modules/@context-vault/core/dist/formatters.d.ts.map +1 -0
  32. package/node_modules/@context-vault/core/dist/formatters.js +18 -0
  33. package/node_modules/@context-vault/core/dist/formatters.js.map +1 -0
  34. package/node_modules/@context-vault/core/dist/frontmatter.d.ts +12 -0
  35. package/node_modules/@context-vault/core/dist/frontmatter.d.ts.map +1 -0
  36. package/node_modules/@context-vault/core/dist/frontmatter.js +101 -0
  37. package/node_modules/@context-vault/core/dist/frontmatter.js.map +1 -0
  38. package/node_modules/@context-vault/core/dist/index.d.ts +10 -0
  39. package/node_modules/@context-vault/core/dist/index.d.ts.map +1 -0
  40. package/node_modules/@context-vault/core/dist/index.js +297 -0
  41. package/node_modules/@context-vault/core/dist/index.js.map +1 -0
  42. package/node_modules/@context-vault/core/dist/ingest-url.d.ts +20 -0
  43. package/node_modules/@context-vault/core/dist/ingest-url.d.ts.map +1 -0
  44. package/node_modules/@context-vault/core/dist/ingest-url.js +113 -0
  45. package/node_modules/@context-vault/core/dist/ingest-url.js.map +1 -0
  46. package/node_modules/@context-vault/core/dist/main.d.ts +14 -0
  47. package/node_modules/@context-vault/core/dist/main.d.ts.map +1 -0
  48. package/node_modules/@context-vault/core/dist/main.js +25 -0
  49. package/node_modules/@context-vault/core/dist/main.js.map +1 -0
  50. package/node_modules/@context-vault/core/dist/search.d.ts +18 -0
  51. package/node_modules/@context-vault/core/dist/search.d.ts.map +1 -0
  52. package/node_modules/@context-vault/core/dist/search.js +238 -0
  53. package/node_modules/@context-vault/core/dist/search.js.map +1 -0
  54. package/node_modules/@context-vault/core/dist/types.d.ts +176 -0
  55. package/node_modules/@context-vault/core/dist/types.d.ts.map +1 -0
  56. package/node_modules/@context-vault/core/dist/types.js +2 -0
  57. package/node_modules/@context-vault/core/dist/types.js.map +1 -0
  58. package/node_modules/@context-vault/core/package.json +66 -16
  59. package/node_modules/@context-vault/core/src/capture.ts +308 -0
  60. package/node_modules/@context-vault/core/src/categories.ts +54 -0
  61. package/node_modules/@context-vault/core/src/{core/config.js → config.ts} +34 -33
  62. package/node_modules/@context-vault/core/src/{constants.js → constants.ts} +6 -3
  63. package/node_modules/@context-vault/core/src/db.ts +229 -0
  64. package/node_modules/@context-vault/core/src/{index/embed.js → embed.ts} +10 -35
  65. package/node_modules/@context-vault/core/src/files.ts +80 -0
  66. package/node_modules/@context-vault/core/src/{capture/formatters.js → formatters.ts} +13 -11
  67. package/node_modules/@context-vault/core/src/{core/frontmatter.js → frontmatter.ts} +27 -33
  68. package/node_modules/@context-vault/core/src/index.ts +351 -0
  69. package/node_modules/@context-vault/core/src/ingest-url.ts +99 -0
  70. package/node_modules/@context-vault/core/src/main.ts +111 -0
  71. package/node_modules/@context-vault/core/src/search.ts +285 -0
  72. package/node_modules/@context-vault/core/src/types.ts +166 -0
  73. package/package.json +12 -7
  74. package/scripts/postinstall.js +1 -1
  75. package/{node_modules/@context-vault/core/src/core → src}/error-log.js +1 -15
  76. package/{node_modules/@context-vault/core/src/server → src}/helpers.js +9 -4
  77. package/src/linking.js +100 -0
  78. package/{node_modules/@context-vault/core/src/server/tools.js → src/register-tools.js} +14 -15
  79. package/src/{server/index.js → server.js} +8 -35
  80. package/src/status.js +235 -0
  81. package/{node_modules/@context-vault/core/src/core → src}/telemetry.js +9 -19
  82. package/src/temporal.js +97 -0
  83. package/{node_modules/@context-vault/core/src/server → src}/tools/context-status.js +3 -4
  84. package/{node_modules/@context-vault/core/src/server → src}/tools/create-snapshot.js +43 -75
  85. package/{node_modules/@context-vault/core/src/server → src}/tools/delete-context.js +0 -2
  86. package/{node_modules/@context-vault/core/src/server → src}/tools/get-context.js +118 -35
  87. package/{node_modules/@context-vault/core/src/server → src}/tools/ingest-project.js +5 -6
  88. package/{node_modules/@context-vault/core/src/server → src}/tools/ingest-url.js +3 -4
  89. package/{node_modules/@context-vault/core/src/server → src}/tools/list-buckets.js +4 -5
  90. package/{node_modules/@context-vault/core/src/server → src}/tools/list-context.js +3 -6
  91. package/{node_modules/@context-vault/core/src/server → src}/tools/save-context.js +41 -21
  92. package/{node_modules/@context-vault/core/src/server → src}/tools/session-start.js +9 -16
  93. package/node_modules/@context-vault/core/src/capture/file-ops.js +0 -97
  94. package/node_modules/@context-vault/core/src/capture/import-pipeline.js +0 -46
  95. package/node_modules/@context-vault/core/src/capture/importers.js +0 -387
  96. package/node_modules/@context-vault/core/src/capture/index.js +0 -236
  97. package/node_modules/@context-vault/core/src/capture/ingest-url.js +0 -252
  98. package/node_modules/@context-vault/core/src/consolidation/index.js +0 -112
  99. package/node_modules/@context-vault/core/src/core/categories.js +0 -72
  100. package/node_modules/@context-vault/core/src/core/files.js +0 -108
  101. package/node_modules/@context-vault/core/src/core/status.js +0 -350
  102. package/node_modules/@context-vault/core/src/index/db.js +0 -416
  103. package/node_modules/@context-vault/core/src/index/index.js +0 -522
  104. package/node_modules/@context-vault/core/src/index.js +0 -66
  105. package/node_modules/@context-vault/core/src/retrieve/index.js +0 -500
  106. package/node_modules/@context-vault/core/src/server/tools/submit-feedback.js +0 -55
  107. package/node_modules/@context-vault/core/src/sync/sync.js +0 -235
  108. package/src/hooks/post-tool-call.mjs +0 -62
  109. package/src/hooks/session-end.mjs +0 -492
  110. /package/{node_modules/@context-vault/core/src/server → src}/tools/clear-context.js +0 -0
@@ -1,10 +1,11 @@
1
1
  import { z } from "zod";
2
- import { captureAndIndex, updateEntryFile } from "../../capture/index.js";
3
- import { indexEntry } from "../../index/index.js";
4
- import { categoryFor, defaultTierFor } from "../../core/categories.js";
5
- import { normalizeKind } from "../../core/files.js";
2
+ import { captureAndIndex, updateEntryFile } from "@context-vault/core/capture";
3
+ import { indexEntry } from "@context-vault/core/index";
4
+ import { categoryFor, defaultTierFor } from "@context-vault/core/categories";
5
+ import { normalizeKind } from "@context-vault/core/files";
6
6
  import { ok, err, ensureVaultExists, ensureValidKind } from "../helpers.js";
7
- import { maybeShowFeedbackPrompt } from "../../core/telemetry.js";
7
+ import { maybeShowFeedbackPrompt } from "../telemetry.js";
8
+ import { validateRelatedTo } from "../linking.js";
8
9
  import {
9
10
  MAX_BODY_LENGTH,
10
11
  MAX_TITLE_LENGTH,
@@ -14,7 +15,7 @@ import {
14
15
  MAX_META_LENGTH,
15
16
  MAX_SOURCE_LENGTH,
16
17
  MAX_IDENTITY_KEY_LENGTH,
17
- } from "../../constants.js";
18
+ } from "@context-vault/core/constants";
18
19
 
19
20
  const DEFAULT_SIMILARITY_THRESHOLD = 0.85;
20
21
  const SKIP_THRESHOLD = 0.95;
@@ -24,7 +25,7 @@ async function findSimilar(
24
25
  ctx,
25
26
  embedding,
26
27
  threshold,
27
- userId,
28
+
28
29
  { hydrate = false } = {},
29
30
  ) {
30
31
  try {
@@ -43,9 +44,15 @@ async function findSimilar(
43
44
 
44
45
  const rowids = vecRows.map((vr) => vr.rowid);
45
46
  const placeholders = rowids.map(() => "?").join(",");
46
- const columns = hydrate
47
- ? "rowid, id, title, body, kind, tags, category, user_id, updated_at"
48
- : "rowid, id, title, category, user_id";
47
+ // Local mode has no user_id column — omit it from the SELECT list.
48
+ const isLocal = ctx.stmts._mode === "local";
49
+ const columns = isLocal
50
+ ? hydrate
51
+ ? "rowid, id, title, body, kind, tags, category, updated_at"
52
+ : "rowid, id, title, category"
53
+ : hydrate
54
+ ? "rowid, id, title, body, kind, tags, category, updated_at"
55
+ : "rowid, id, title, category";
49
56
  const hydratedRows = ctx.db
50
57
  .prepare(`SELECT ${columns} FROM vault WHERE rowid IN (${placeholders})`)
51
58
  .all(...rowids);
@@ -59,7 +66,6 @@ async function findSimilar(
59
66
  if (similarity < threshold) continue;
60
67
  const row = byRowid.get(vr.rowid);
61
68
  if (!row) continue;
62
- if (userId !== undefined && row.user_id !== userId) continue;
63
69
  if (row.category === "entity") continue;
64
70
  const entry = { id: row.id, title: row.title, score: similarity };
65
71
  if (hydrate) {
@@ -294,6 +300,12 @@ export const inputSchema = {
294
300
  .describe(
295
301
  "Array of entry IDs that this entry supersedes/replaces. Those entries will be marked with superseded_by pointing to this new entry and excluded from future search results by default.",
296
302
  ),
303
+ related_to: z
304
+ .array(z.string())
305
+ .optional()
306
+ .describe(
307
+ "Array of entry IDs this entry is related to. Enables bidirectional graph traversal — use get_context with follow_links:true to retrieve linked entries.",
308
+ ),
297
309
  source_files: z
298
310
  .array(
299
311
  z.object({
@@ -359,6 +371,7 @@ export async function handler(
359
371
  identity_key,
360
372
  expires_at,
361
373
  supersedes,
374
+ related_to,
362
375
  source_files,
363
376
  dry_run,
364
377
  similarity_threshold,
@@ -369,12 +382,14 @@ export async function handler(
369
382
  { ensureIndexed },
370
383
  ) {
371
384
  const { config } = ctx;
372
- const userId = ctx.userId !== undefined ? ctx.userId : undefined;
373
385
  const suggestMode = conflict_resolution !== "off";
374
386
 
375
387
  const vaultErr = ensureVaultExists(config);
376
388
  if (vaultErr) return vaultErr;
377
389
 
390
+ const relatedToErr = validateRelatedTo(related_to);
391
+ if (relatedToErr) return err(relatedToErr, "INVALID_INPUT");
392
+
378
393
  const inputErr = validateSaveInput({
379
394
  kind,
380
395
  title,
@@ -395,7 +410,6 @@ export async function handler(
395
410
  if (!existing) return err(`Entry not found: ${id}`, "NOT_FOUND");
396
411
 
397
412
  // Ownership check: don't leak existence across users
398
- if (userId !== undefined && existing.user_id !== userId) {
399
413
  return err(`Entry not found: ${id}`, "NOT_FOUND");
400
414
  }
401
415
 
@@ -428,9 +442,15 @@ export async function handler(
428
442
  source,
429
443
  expires_at,
430
444
  supersedes,
445
+ related_to,
431
446
  source_files,
432
447
  });
433
448
  await indexEntry(ctx, entry);
449
+ if (entry.related_to?.length && ctx.stmts.updateRelatedTo) {
450
+ ctx.stmts.updateRelatedTo.run(JSON.stringify(entry.related_to), entry.id);
451
+ } else if (entry.related_to === null && ctx.stmts.updateRelatedTo) {
452
+ ctx.stmts.updateRelatedTo.run(null, entry.id);
453
+ }
434
454
  const relPath = entry.filePath
435
455
  ? entry.filePath.replace(config.vaultDir + "/", "")
436
456
  : entry.filePath;
@@ -474,7 +494,7 @@ export async function handler(
474
494
  ctx,
475
495
  queryEmbedding,
476
496
  threshold,
477
- userId,
497
+
478
498
  { hydrate: suggestMode },
479
499
  );
480
500
  }
@@ -531,8 +551,9 @@ export async function handler(
531
551
  identity_key,
532
552
  expires_at,
533
553
  supersedes,
554
+ related_to,
534
555
  source_files,
535
- userId,
556
+
536
557
  tier: effectiveTier,
537
558
  });
538
559
 
@@ -561,9 +582,8 @@ export async function handler(
561
582
  (t) => typeof t === "string" && t.startsWith("bucket:"),
562
583
  );
563
584
  for (const bt of bucketTags) {
564
- const bucketUserClause = userId !== undefined ? "AND user_id = ?" : "";
565
- const bucketParams =
566
- userId !== undefined ? [bt, userId] : [bt];
585
+ const bucketUserClause = "";
586
+ const bucketParams = false ? [bt] : [bt];
567
587
  const exists = ctx.db
568
588
  .prepare(
569
589
  `SELECT 1 FROM vault WHERE kind = 'bucket' AND identity_key = ? ${bucketUserClause} LIMIT 1`,
@@ -591,11 +611,11 @@ export async function handler(
591
611
  try {
592
612
  const countRow = ctx.db
593
613
  .prepare(
594
- userId !== undefined
595
- ? "SELECT COUNT(*) as c FROM vault WHERE user_id = ?"
614
+ false
615
+
596
616
  : "SELECT COUNT(*) as c FROM vault",
597
617
  )
598
- .get(...(userId !== undefined ? [userId] : []));
618
+ .get(...([]));
599
619
  if (countRow.c >= criticalLimit) {
600
620
  parts.push(
601
621
  ``,
@@ -79,7 +79,6 @@ function formatEntry(entry) {
79
79
 
80
80
  export async function handler({ project, max_tokens, buckets }, ctx, { ensureIndexed }) {
81
81
  const { config } = ctx;
82
- const userId = ctx.userId !== undefined ? ctx.userId : undefined;
83
82
 
84
83
  const vaultErr = ensureVaultExists(config);
85
84
  if (vaultErr) return vaultErr;
@@ -110,7 +109,7 @@ export async function handler({ project, max_tokens, buckets }, ctx, { ensureInd
110
109
  );
111
110
  tokensUsed += estimateTokens(sections.join("\n"));
112
111
 
113
- const lastSession = queryLastSession(ctx, userId, effectiveTags);
112
+ const lastSession = queryLastSession(ctx, effectiveTags);
114
113
  if (lastSession) {
115
114
  const sessionBlock = [
116
115
  "## Last Session Summary",
@@ -128,7 +127,7 @@ export async function handler({ project, max_tokens, buckets }, ctx, { ensureInd
128
127
  ctx,
129
128
  PRIORITY_KINDS,
130
129
  sinceDate,
131
- userId,
130
+
132
131
  effectiveTags,
133
132
  );
134
133
  if (decisions.length > 0) {
@@ -150,7 +149,7 @@ export async function handler({ project, max_tokens, buckets }, ctx, { ensureInd
150
149
  }
151
150
  }
152
151
 
153
- const recent = queryRecent(ctx, sinceDate, userId, effectiveTags);
152
+ const recent = queryRecent(ctx, sinceDate, effectiveTags);
154
153
  const seenIds = new Set(decisions.map((d) => d.id));
155
154
  if (lastSession) seenIds.add(lastSession.id);
156
155
  const deduped = recent.filter((r) => !seenIds.has(r.id));
@@ -202,13 +201,11 @@ export async function handler({ project, max_tokens, buckets }, ctx, { ensureInd
202
201
  return result;
203
202
  }
204
203
 
205
- function queryLastSession(ctx, userId, effectiveTags) {
204
+ function queryLastSession(ctx, effectiveTags) {
206
205
  const clauses = [`kind = '${SESSION_SUMMARY_KIND}'`];
207
206
  const params = [];
208
207
 
209
- if (userId !== undefined) {
210
- clauses.push("user_id = ?");
211
- params.push(userId);
208
+ if (false) {
212
209
  }
213
210
  clauses.push("(expires_at IS NULL OR expires_at > datetime('now'))");
214
211
  clauses.push("superseded_by IS NULL");
@@ -228,7 +225,7 @@ function queryLastSession(ctx, userId, effectiveTags) {
228
225
  return rows[0] || null;
229
226
  }
230
227
 
231
- function queryByKinds(ctx, kinds, since, userId, effectiveTags) {
228
+ function queryByKinds(ctx, kinds, since, effectiveTags) {
232
229
  const kindPlaceholders = kinds.map(() => "?").join(",");
233
230
  const clauses = [`kind IN (${kindPlaceholders})`];
234
231
  const params = [...kinds];
@@ -236,9 +233,7 @@ function queryByKinds(ctx, kinds, since, userId, effectiveTags) {
236
233
  clauses.push("created_at >= ?");
237
234
  params.push(since);
238
235
 
239
- if (userId !== undefined) {
240
- clauses.push("user_id = ?");
241
- params.push(userId);
236
+ if (false) {
242
237
  }
243
238
  clauses.push("(expires_at IS NULL OR expires_at > datetime('now'))");
244
239
  clauses.push("superseded_by IS NULL");
@@ -258,13 +253,11 @@ function queryByKinds(ctx, kinds, since, userId, effectiveTags) {
258
253
  return rows;
259
254
  }
260
255
 
261
- function queryRecent(ctx, since, userId, effectiveTags) {
256
+ function queryRecent(ctx, since, effectiveTags) {
262
257
  const clauses = ["created_at >= ?"];
263
258
  const params = [since];
264
259
 
265
- if (userId !== undefined) {
266
- clauses.push("user_id = ?");
267
- params.push(userId);
260
+ if (false) {
268
261
  }
269
262
  clauses.push("(expires_at IS NULL OR expires_at > datetime('now'))");
270
263
  clauses.push("superseded_by IS NULL");
@@ -1,97 +0,0 @@
1
- /**
2
- * file-ops.js — Capture-specific file operations
3
- *
4
- * Writes markdown entry files with frontmatter to the vault directory.
5
- */
6
-
7
- import { mkdirSync, writeFileSync } from "node:fs";
8
- import { resolve, relative } from "node:path";
9
- import { formatFrontmatter } from "../core/frontmatter.js";
10
- import { slugify, kindToPath } from "../core/files.js";
11
- import { formatBody } from "./formatters.js";
12
-
13
- export function safeFolderPath(vaultDir, kind, folder) {
14
- const base = resolve(vaultDir, kindToPath(kind));
15
- if (!folder) return base;
16
- const resolved = resolve(base, folder);
17
- const rel = relative(base, resolved);
18
- if (rel.startsWith("..") || resolve(base, rel) !== resolved) {
19
- throw new Error(`Folder path escapes vault: "${folder}"`);
20
- }
21
- return resolved;
22
- }
23
-
24
- export function writeEntryFile(
25
- vaultDir,
26
- kind,
27
- {
28
- id,
29
- title,
30
- body,
31
- meta,
32
- tags,
33
- source,
34
- createdAt,
35
- updatedAt,
36
- folder,
37
- category,
38
- identity_key,
39
- expires_at,
40
- supersedes,
41
- },
42
- ) {
43
- // P5: folder is now a top-level param; also accept from meta for backward compat
44
- const resolvedFolder = folder || meta?.folder || "";
45
- const dir = safeFolderPath(vaultDir, kind, resolvedFolder);
46
-
47
- try {
48
- mkdirSync(dir, { recursive: true });
49
- } catch (e) {
50
- throw new Error(`Failed to create directory "${dir}": ${e.message}`);
51
- }
52
-
53
- const created = createdAt || new Date().toISOString();
54
- const fmFields = { id };
55
-
56
- // Add kind-specific meta fields to frontmatter (flattened, not nested)
57
- if (meta) {
58
- for (const [k, v] of Object.entries(meta)) {
59
- if (k === "folder") continue;
60
- if (v !== null && v !== undefined) fmFields[k] = v;
61
- }
62
- }
63
-
64
- if (identity_key) fmFields.identity_key = identity_key;
65
- if (expires_at) fmFields.expires_at = expires_at;
66
- if (supersedes?.length) fmFields.supersedes = supersedes;
67
- fmFields.tags = tags || [];
68
- fmFields.source = source || "claude-code";
69
- fmFields.created = created;
70
- if (updatedAt && updatedAt !== created) fmFields.updated = updatedAt;
71
-
72
- const mdBody = formatBody(kind, { title, body, meta });
73
-
74
- // Entity kinds: deterministic filename from identity_key (no ULID suffix)
75
- let filename;
76
- if (category === "entity" && identity_key) {
77
- const identitySlug = slugify(identity_key);
78
- filename = identitySlug
79
- ? `${identitySlug}.md`
80
- : `${id.slice(-8).toLowerCase()}.md`;
81
- } else {
82
- const slug = slugify((title || body).slice(0, 40));
83
- const shortId = id.slice(-8).toLowerCase();
84
- filename = slug ? `${slug}-${shortId}.md` : `${shortId}.md`;
85
- }
86
-
87
- const filePath = resolve(dir, filename);
88
- const md = formatFrontmatter(fmFields) + mdBody;
89
-
90
- try {
91
- writeFileSync(filePath, md);
92
- } catch (e) {
93
- throw new Error(`Failed to write entry file "${filePath}": ${e.message}`);
94
- }
95
-
96
- return filePath;
97
- }
@@ -1,46 +0,0 @@
1
- import { captureAndIndex } from "./index.js";
2
-
3
- export async function importEntries(ctx, entries, opts = {}) {
4
- const { onProgress, source } = opts;
5
- let imported = 0;
6
- let failed = 0;
7
- const errors = [];
8
-
9
- for (let i = 0; i < entries.length; i++) {
10
- const entry = entries[i];
11
-
12
- if (onProgress) {
13
- onProgress(i + 1, entries.length);
14
- }
15
-
16
- try {
17
- if (!entry.body?.trim()) {
18
- failed++;
19
- errors.push({ index: i, title: entry.title, error: "Empty body" });
20
- continue;
21
- }
22
-
23
- await captureAndIndex(ctx, {
24
- kind: entry.kind || "insight",
25
- title: entry.title || null,
26
- body: entry.body,
27
- meta: entry.meta,
28
- tags: entry.tags,
29
- source: entry.source || source || "import",
30
- identity_key: entry.identity_key,
31
- expires_at: entry.expires_at,
32
- userId: ctx.userId || null,
33
- });
34
- imported++;
35
- } catch (err) {
36
- failed++;
37
- errors.push({
38
- index: i,
39
- title: entry.title || null,
40
- error: err.message,
41
- });
42
- }
43
- }
44
-
45
- return { imported, failed, errors };
46
- }