@context-vault/core 2.15.0 → 2.17.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.
- package/package.json +2 -3
- package/src/capture/file-ops.js +2 -0
- package/src/capture/index.js +14 -0
- package/src/constants.js +7 -2
- package/src/core/categories.js +1 -0
- package/src/core/config.js +9 -2
- package/src/core/files.js +6 -29
- package/src/core/frontmatter.js +1 -0
- package/src/core/linking.js +161 -0
- package/src/core/migrate-dirs.js +196 -0
- package/src/core/status.js +28 -2
- package/src/core/temporal.js +146 -0
- package/src/index/db.js +178 -8
- package/src/index/index.js +113 -48
- package/src/index.js +5 -0
- package/src/retrieve/index.js +16 -136
- package/src/server/tools/context-status.js +7 -0
- package/src/server/tools/create-snapshot.js +37 -68
- package/src/server/tools/get-context.js +120 -19
- package/src/server/tools/save-context.js +29 -6
- package/src/server/tools.js +0 -2
- package/src/server/tools/submit-feedback.js +0 -55
|
@@ -5,6 +5,7 @@ import { categoryFor, defaultTierFor } from "../../core/categories.js";
|
|
|
5
5
|
import { normalizeKind } from "../../core/files.js";
|
|
6
6
|
import { ok, err, ensureVaultExists, ensureValidKind } from "../helpers.js";
|
|
7
7
|
import { maybeShowFeedbackPrompt } from "../../core/telemetry.js";
|
|
8
|
+
import { validateRelatedTo } from "../../core/linking.js";
|
|
8
9
|
import {
|
|
9
10
|
MAX_BODY_LENGTH,
|
|
10
11
|
MAX_TITLE_LENGTH,
|
|
@@ -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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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, user_id, updated_at"
|
|
55
|
+
: "rowid, id, title, category, user_id";
|
|
49
56
|
const hydratedRows = ctx.db
|
|
50
57
|
.prepare(`SELECT ${columns} FROM vault WHERE rowid IN (${placeholders})`)
|
|
51
58
|
.all(...rowids);
|
|
@@ -59,7 +66,7 @@ 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;
|
|
69
|
+
if (!isLocal && userId !== undefined && row.user_id !== userId) continue;
|
|
63
70
|
if (row.category === "entity") continue;
|
|
64
71
|
const entry = { id: row.id, title: row.title, score: similarity };
|
|
65
72
|
if (hydrate) {
|
|
@@ -294,6 +301,12 @@ export const inputSchema = {
|
|
|
294
301
|
.describe(
|
|
295
302
|
"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
303
|
),
|
|
304
|
+
related_to: z
|
|
305
|
+
.array(z.string())
|
|
306
|
+
.optional()
|
|
307
|
+
.describe(
|
|
308
|
+
"Array of entry IDs this entry is related to. Enables bidirectional graph traversal — use get_context with follow_links:true to retrieve linked entries.",
|
|
309
|
+
),
|
|
297
310
|
source_files: z
|
|
298
311
|
.array(
|
|
299
312
|
z.object({
|
|
@@ -359,6 +372,7 @@ export async function handler(
|
|
|
359
372
|
identity_key,
|
|
360
373
|
expires_at,
|
|
361
374
|
supersedes,
|
|
375
|
+
related_to,
|
|
362
376
|
source_files,
|
|
363
377
|
dry_run,
|
|
364
378
|
similarity_threshold,
|
|
@@ -375,6 +389,9 @@ export async function handler(
|
|
|
375
389
|
const vaultErr = ensureVaultExists(config);
|
|
376
390
|
if (vaultErr) return vaultErr;
|
|
377
391
|
|
|
392
|
+
const relatedToErr = validateRelatedTo(related_to);
|
|
393
|
+
if (relatedToErr) return err(relatedToErr, "INVALID_INPUT");
|
|
394
|
+
|
|
378
395
|
const inputErr = validateSaveInput({
|
|
379
396
|
kind,
|
|
380
397
|
title,
|
|
@@ -428,9 +445,15 @@ export async function handler(
|
|
|
428
445
|
source,
|
|
429
446
|
expires_at,
|
|
430
447
|
supersedes,
|
|
448
|
+
related_to,
|
|
431
449
|
source_files,
|
|
432
450
|
});
|
|
433
451
|
await indexEntry(ctx, entry);
|
|
452
|
+
if (entry.related_to?.length && ctx.stmts.updateRelatedTo) {
|
|
453
|
+
ctx.stmts.updateRelatedTo.run(JSON.stringify(entry.related_to), entry.id);
|
|
454
|
+
} else if (entry.related_to === null && ctx.stmts.updateRelatedTo) {
|
|
455
|
+
ctx.stmts.updateRelatedTo.run(null, entry.id);
|
|
456
|
+
}
|
|
434
457
|
const relPath = entry.filePath
|
|
435
458
|
? entry.filePath.replace(config.vaultDir + "/", "")
|
|
436
459
|
: entry.filePath;
|
|
@@ -531,6 +554,7 @@ export async function handler(
|
|
|
531
554
|
identity_key,
|
|
532
555
|
expires_at,
|
|
533
556
|
supersedes,
|
|
557
|
+
related_to,
|
|
534
558
|
source_files,
|
|
535
559
|
userId,
|
|
536
560
|
tier: effectiveTier,
|
|
@@ -562,8 +586,7 @@ export async function handler(
|
|
|
562
586
|
);
|
|
563
587
|
for (const bt of bucketTags) {
|
|
564
588
|
const bucketUserClause = userId !== undefined ? "AND user_id = ?" : "";
|
|
565
|
-
const bucketParams =
|
|
566
|
-
userId !== undefined ? [bt, userId] : [bt];
|
|
589
|
+
const bucketParams = userId !== undefined ? [bt, userId] : [bt];
|
|
567
590
|
const exists = ctx.db
|
|
568
591
|
.prepare(
|
|
569
592
|
`SELECT 1 FROM vault WHERE kind = 'bucket' AND identity_key = ? ${bucketUserClause} LIMIT 1`,
|
package/src/server/tools.js
CHANGED
|
@@ -8,7 +8,6 @@ import * as getContext from "./tools/get-context.js";
|
|
|
8
8
|
import * as saveContext from "./tools/save-context.js";
|
|
9
9
|
import * as listContext from "./tools/list-context.js";
|
|
10
10
|
import * as deleteContext from "./tools/delete-context.js";
|
|
11
|
-
import * as submitFeedback from "./tools/submit-feedback.js";
|
|
12
11
|
import * as ingestUrl from "./tools/ingest-url.js";
|
|
13
12
|
import * as contextStatus from "./tools/context-status.js";
|
|
14
13
|
import * as clearContext from "./tools/clear-context.js";
|
|
@@ -22,7 +21,6 @@ const toolModules = [
|
|
|
22
21
|
saveContext,
|
|
23
22
|
listContext,
|
|
24
23
|
deleteContext,
|
|
25
|
-
submitFeedback,
|
|
26
24
|
ingestUrl,
|
|
27
25
|
ingestProject,
|
|
28
26
|
contextStatus,
|
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { captureAndIndex } from "../../capture/index.js";
|
|
3
|
-
import { ok, ensureVaultExists } from "../helpers.js";
|
|
4
|
-
|
|
5
|
-
export const name = "submit_feedback";
|
|
6
|
-
|
|
7
|
-
export const description =
|
|
8
|
-
"Report a bug, request a feature, or suggest an improvement. Feedback is stored in the vault and triaged by the development pipeline.";
|
|
9
|
-
|
|
10
|
-
export const inputSchema = {
|
|
11
|
-
type: z.enum(["bug", "feature", "improvement"]).describe("Type of feedback"),
|
|
12
|
-
title: z.string().describe("Short summary of the feedback"),
|
|
13
|
-
body: z.string().describe("Detailed description"),
|
|
14
|
-
severity: z
|
|
15
|
-
.enum(["low", "medium", "high"])
|
|
16
|
-
.optional()
|
|
17
|
-
.describe("Severity level (default: medium)"),
|
|
18
|
-
};
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* @param {object} args
|
|
22
|
-
* @param {import('../types.js').BaseCtx & Partial<import('../types.js').HostedCtxExtensions>} ctx
|
|
23
|
-
* @param {import('../types.js').ToolShared} shared
|
|
24
|
-
*/
|
|
25
|
-
export async function handler(
|
|
26
|
-
{ type, title, body, severity },
|
|
27
|
-
ctx,
|
|
28
|
-
{ ensureIndexed },
|
|
29
|
-
) {
|
|
30
|
-
const { config } = ctx;
|
|
31
|
-
const userId = ctx.userId !== undefined ? ctx.userId : undefined;
|
|
32
|
-
|
|
33
|
-
const vaultErr = ensureVaultExists(config);
|
|
34
|
-
if (vaultErr) return vaultErr;
|
|
35
|
-
|
|
36
|
-
await ensureIndexed();
|
|
37
|
-
|
|
38
|
-
const effectiveSeverity = severity || "medium";
|
|
39
|
-
const entry = await captureAndIndex(ctx, {
|
|
40
|
-
kind: "feedback",
|
|
41
|
-
title,
|
|
42
|
-
body,
|
|
43
|
-
tags: [type, effectiveSeverity],
|
|
44
|
-
source: "submit_feedback",
|
|
45
|
-
meta: { feedback_type: type, severity: effectiveSeverity, status: "new" },
|
|
46
|
-
userId,
|
|
47
|
-
});
|
|
48
|
-
|
|
49
|
-
const relPath = entry.filePath
|
|
50
|
-
? entry.filePath.replace(config.vaultDir + "/", "")
|
|
51
|
-
: entry.filePath;
|
|
52
|
-
return ok(
|
|
53
|
-
`Feedback submitted: ${type} [${effectiveSeverity}] → ${relPath}\n id: ${entry.id}\n title: ${title}`,
|
|
54
|
-
);
|
|
55
|
-
}
|