harper-kb 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +288 -0
  3. package/config.yaml +13 -0
  4. package/dist/core/embeddings.d.ts +31 -0
  5. package/dist/core/embeddings.d.ts.map +1 -0
  6. package/dist/core/embeddings.js +199 -0
  7. package/dist/core/embeddings.js.map +1 -0
  8. package/dist/core/entries.d.ts +101 -0
  9. package/dist/core/entries.d.ts.map +1 -0
  10. package/dist/core/entries.js +304 -0
  11. package/dist/core/entries.js.map +1 -0
  12. package/dist/core/history.d.ts +31 -0
  13. package/dist/core/history.d.ts.map +1 -0
  14. package/dist/core/history.js +119 -0
  15. package/dist/core/history.js.map +1 -0
  16. package/dist/core/knowledge-base.d.ts +49 -0
  17. package/dist/core/knowledge-base.d.ts.map +1 -0
  18. package/dist/core/knowledge-base.js +117 -0
  19. package/dist/core/knowledge-base.js.map +1 -0
  20. package/dist/core/search.d.ts +34 -0
  21. package/dist/core/search.d.ts.map +1 -0
  22. package/dist/core/search.js +327 -0
  23. package/dist/core/search.js.map +1 -0
  24. package/dist/core/tags.d.ts +39 -0
  25. package/dist/core/tags.d.ts.map +1 -0
  26. package/dist/core/tags.js +97 -0
  27. package/dist/core/tags.js.map +1 -0
  28. package/dist/core/triage.d.ts +61 -0
  29. package/dist/core/triage.d.ts.map +1 -0
  30. package/dist/core/triage.js +136 -0
  31. package/dist/core/triage.js.map +1 -0
  32. package/dist/core/webhook-endpoints.d.ts +46 -0
  33. package/dist/core/webhook-endpoints.d.ts.map +1 -0
  34. package/dist/core/webhook-endpoints.js +85 -0
  35. package/dist/core/webhook-endpoints.js.map +1 -0
  36. package/dist/hooks.d.ts +67 -0
  37. package/dist/hooks.d.ts.map +1 -0
  38. package/dist/hooks.js +53 -0
  39. package/dist/hooks.js.map +1 -0
  40. package/dist/http-utils.d.ts +38 -0
  41. package/dist/http-utils.d.ts.map +1 -0
  42. package/dist/http-utils.js +133 -0
  43. package/dist/http-utils.js.map +1 -0
  44. package/dist/index.d.ts +27 -0
  45. package/dist/index.d.ts.map +1 -0
  46. package/dist/index.js +78 -0
  47. package/dist/index.js.map +1 -0
  48. package/dist/mcp/protocol.d.ts +25 -0
  49. package/dist/mcp/protocol.d.ts.map +1 -0
  50. package/dist/mcp/protocol.js +105 -0
  51. package/dist/mcp/protocol.js.map +1 -0
  52. package/dist/mcp/server.d.ts +28 -0
  53. package/dist/mcp/server.d.ts.map +1 -0
  54. package/dist/mcp/server.js +144 -0
  55. package/dist/mcp/server.js.map +1 -0
  56. package/dist/mcp/tools.d.ts +26 -0
  57. package/dist/mcp/tools.d.ts.map +1 -0
  58. package/dist/mcp/tools.js +706 -0
  59. package/dist/mcp/tools.js.map +1 -0
  60. package/dist/oauth/authorize.d.ts +28 -0
  61. package/dist/oauth/authorize.d.ts.map +1 -0
  62. package/dist/oauth/authorize.js +421 -0
  63. package/dist/oauth/authorize.js.map +1 -0
  64. package/dist/oauth/init.d.ts +18 -0
  65. package/dist/oauth/init.d.ts.map +1 -0
  66. package/dist/oauth/init.js +30 -0
  67. package/dist/oauth/init.js.map +1 -0
  68. package/dist/oauth/keys.d.ts +34 -0
  69. package/dist/oauth/keys.d.ts.map +1 -0
  70. package/dist/oauth/keys.js +101 -0
  71. package/dist/oauth/keys.js.map +1 -0
  72. package/dist/oauth/metadata.d.ts +23 -0
  73. package/dist/oauth/metadata.d.ts.map +1 -0
  74. package/dist/oauth/metadata.js +57 -0
  75. package/dist/oauth/metadata.js.map +1 -0
  76. package/dist/oauth/middleware.d.ts +23 -0
  77. package/dist/oauth/middleware.d.ts.map +1 -0
  78. package/dist/oauth/middleware.js +65 -0
  79. package/dist/oauth/middleware.js.map +1 -0
  80. package/dist/oauth/register.d.ts +15 -0
  81. package/dist/oauth/register.d.ts.map +1 -0
  82. package/dist/oauth/register.js +78 -0
  83. package/dist/oauth/register.js.map +1 -0
  84. package/dist/oauth/token.d.ts +16 -0
  85. package/dist/oauth/token.d.ts.map +1 -0
  86. package/dist/oauth/token.js +184 -0
  87. package/dist/oauth/token.js.map +1 -0
  88. package/dist/oauth/validate.d.ts +40 -0
  89. package/dist/oauth/validate.d.ts.map +1 -0
  90. package/dist/oauth/validate.js +61 -0
  91. package/dist/oauth/validate.js.map +1 -0
  92. package/dist/resources/HistoryResource.d.ts +41 -0
  93. package/dist/resources/HistoryResource.d.ts.map +1 -0
  94. package/dist/resources/HistoryResource.js +61 -0
  95. package/dist/resources/HistoryResource.js.map +1 -0
  96. package/dist/resources/KnowledgeBaseResource.d.ts +60 -0
  97. package/dist/resources/KnowledgeBaseResource.d.ts.map +1 -0
  98. package/dist/resources/KnowledgeBaseResource.js +118 -0
  99. package/dist/resources/KnowledgeBaseResource.js.map +1 -0
  100. package/dist/resources/KnowledgeEntryResource.d.ts +61 -0
  101. package/dist/resources/KnowledgeEntryResource.d.ts.map +1 -0
  102. package/dist/resources/KnowledgeEntryResource.js +191 -0
  103. package/dist/resources/KnowledgeEntryResource.js.map +1 -0
  104. package/dist/resources/MeResource.d.ts +31 -0
  105. package/dist/resources/MeResource.d.ts.map +1 -0
  106. package/dist/resources/MeResource.js +40 -0
  107. package/dist/resources/MeResource.js.map +1 -0
  108. package/dist/resources/QueryLogResource.d.ts +22 -0
  109. package/dist/resources/QueryLogResource.d.ts.map +1 -0
  110. package/dist/resources/QueryLogResource.js +66 -0
  111. package/dist/resources/QueryLogResource.js.map +1 -0
  112. package/dist/resources/ServiceKeyResource.d.ts +52 -0
  113. package/dist/resources/ServiceKeyResource.d.ts.map +1 -0
  114. package/dist/resources/ServiceKeyResource.js +151 -0
  115. package/dist/resources/ServiceKeyResource.js.map +1 -0
  116. package/dist/resources/TagResource.d.ts +27 -0
  117. package/dist/resources/TagResource.d.ts.map +1 -0
  118. package/dist/resources/TagResource.js +41 -0
  119. package/dist/resources/TagResource.js.map +1 -0
  120. package/dist/resources/TriageResource.d.ts +53 -0
  121. package/dist/resources/TriageResource.d.ts.map +1 -0
  122. package/dist/resources/TriageResource.js +120 -0
  123. package/dist/resources/TriageResource.js.map +1 -0
  124. package/dist/resources/WebhookEndpointResource.d.ts +63 -0
  125. package/dist/resources/WebhookEndpointResource.d.ts.map +1 -0
  126. package/dist/resources/WebhookEndpointResource.js +115 -0
  127. package/dist/resources/WebhookEndpointResource.js.map +1 -0
  128. package/dist/types.d.ts +378 -0
  129. package/dist/types.d.ts.map +1 -0
  130. package/dist/types.js +8 -0
  131. package/dist/types.js.map +1 -0
  132. package/dist/webhooks/github.d.ts +25 -0
  133. package/dist/webhooks/github.d.ts.map +1 -0
  134. package/dist/webhooks/github.js +165 -0
  135. package/dist/webhooks/github.js.map +1 -0
  136. package/dist/webhooks/middleware.d.ts +19 -0
  137. package/dist/webhooks/middleware.d.ts.map +1 -0
  138. package/dist/webhooks/middleware.js +144 -0
  139. package/dist/webhooks/middleware.js.map +1 -0
  140. package/dist/webhooks/types.d.ts +18 -0
  141. package/dist/webhooks/types.d.ts.map +1 -0
  142. package/dist/webhooks/types.js +5 -0
  143. package/dist/webhooks/types.js.map +1 -0
  144. package/package.json +69 -0
  145. package/schema/knowledge.graphql +136 -0
  146. package/schema/oauth.graphql +45 -0
@@ -0,0 +1,304 @@
1
+ /**
2
+ * Knowledge Entry Management
3
+ *
4
+ * CRUD operations for knowledge base entries. Handles embedding generation,
5
+ * tag synchronization, and relationship management.
6
+ */
7
+ import crypto from 'node:crypto';
8
+ import { generateEmbedding } from "./embeddings.js";
9
+ import { syncTags } from "./tags.js";
10
+ import { logEdit } from "./history.js";
11
+ /**
12
+ * Strip embedding vectors from an entry to keep responses compact.
13
+ * Embeddings are large float arrays not useful in API responses.
14
+ *
15
+ * NOTE: Harper database records use non-enumerable properties, so
16
+ * object spread ({...record}) produces an empty object. We must
17
+ * explicitly read each field.
18
+ */
19
+ export function stripEmbedding(entry) {
20
+ return {
21
+ id: entry.id,
22
+ kbId: entry.kbId,
23
+ title: entry.title,
24
+ content: entry.content,
25
+ tags: entry.tags,
26
+ appliesTo: entry.appliesTo,
27
+ source: entry.source,
28
+ sourceUrl: entry.sourceUrl,
29
+ references: entry.references,
30
+ confidence: entry.confidence,
31
+ addedBy: entry.addedBy,
32
+ reviewedBy: entry.reviewedBy,
33
+ supersedesId: entry.supersedesId,
34
+ supersededById: entry.supersededById,
35
+ siblingIds: entry.siblingIds,
36
+ relatedIds: entry.relatedIds,
37
+ metadata: entry.metadata,
38
+ deprecated: entry.deprecated,
39
+ createdAt: entry.createdAt,
40
+ updatedAt: entry.updatedAt,
41
+ // score is present on SearchResult objects
42
+ ...(entry.score !== undefined ? { score: entry.score } : {}),
43
+ };
44
+ }
45
+ /**
46
+ * Create a new knowledge entry.
47
+ *
48
+ * Generates an embedding from title + content, synchronizes tags,
49
+ * and stores the entry. A UUID is generated if no id is provided.
50
+ *
51
+ * @param data - Entry data to create
52
+ * @returns The created knowledge entry
53
+ */
54
+ export async function createEntry(data) {
55
+ const id = data.id || crypto.randomUUID();
56
+ // Generate embedding from title + content
57
+ const embeddingText = `${data.title}\n\n${data.content}`;
58
+ let embedding;
59
+ try {
60
+ embedding = await generateEmbedding(embeddingText);
61
+ }
62
+ catch (error) {
63
+ logger?.warn?.('Failed to generate embedding for new entry:', error.message);
64
+ }
65
+ const entry = {
66
+ id,
67
+ kbId: data.kbId,
68
+ title: data.title,
69
+ content: data.content,
70
+ tags: data.tags || [],
71
+ appliesTo: data.appliesTo,
72
+ source: data.source,
73
+ sourceUrl: data.sourceUrl,
74
+ references: data.references,
75
+ confidence: data.confidence || 'ai-generated',
76
+ addedBy: data.addedBy,
77
+ reviewedBy: data.reviewedBy,
78
+ metadata: data.metadata,
79
+ deprecated: data.deprecated ?? false,
80
+ embedding,
81
+ };
82
+ // Sync tag counts (no previous tags for new entries)
83
+ if (entry.tags.length > 0) {
84
+ await syncTags(data.kbId, entry.tags);
85
+ }
86
+ // Store the entry
87
+ await databases.kb.KnowledgeEntry.put(entry);
88
+ return entry;
89
+ }
90
+ /**
91
+ * Get a knowledge entry by ID.
92
+ *
93
+ * @param id - Entry ID
94
+ * @returns The entry, or null if not found
95
+ */
96
+ export async function getEntry(id) {
97
+ const entry = await databases.kb.KnowledgeEntry.get(id);
98
+ return entry;
99
+ }
100
+ /**
101
+ * Update an existing knowledge entry.
102
+ *
103
+ * Merges the update data with the existing entry. If title or content changed,
104
+ * regenerates the embedding. Synchronizes tag counts if tags changed.
105
+ * Optionally logs the edit to the history table.
106
+ *
107
+ * @param id - ID of the entry to update
108
+ * @param data - Fields to update
109
+ * @param options - Optional edit tracking metadata
110
+ * @returns The updated entry
111
+ * @throws Error if the entry does not exist
112
+ */
113
+ export async function updateEntry(id, data, options) {
114
+ const existing = await databases.kb.KnowledgeEntry.get(id);
115
+ if (!existing) {
116
+ throw new Error(`Knowledge entry not found: ${id}`);
117
+ }
118
+ const existingEntry = existing;
119
+ const previousTags = existingEntry.tags || [];
120
+ // Merge updates
121
+ const updated = {
122
+ ...existingEntry,
123
+ ...data,
124
+ id, // Ensure ID is never overwritten
125
+ };
126
+ // Regenerate embedding if title or content changed
127
+ const titleChanged = data.title !== undefined && data.title !== existingEntry.title;
128
+ const contentChanged = data.content !== undefined && data.content !== existingEntry.content;
129
+ if (titleChanged || contentChanged) {
130
+ const embeddingText = `${updated.title}\n\n${updated.content}`;
131
+ try {
132
+ updated.embedding = await generateEmbedding(embeddingText);
133
+ }
134
+ catch (error) {
135
+ logger?.warn?.('Failed to regenerate embedding for entry update:', error.message);
136
+ }
137
+ }
138
+ // Sync tag counts if tags changed
139
+ if (data.tags !== undefined) {
140
+ await syncTags(existingEntry.kbId, updated.tags, previousTags);
141
+ }
142
+ // Log the edit before overwriting
143
+ if (options?.editedBy) {
144
+ try {
145
+ await logEdit(existingEntry.kbId, id, existingEntry, updated, options.editedBy, options.editSummary);
146
+ }
147
+ catch (error) {
148
+ logger?.warn?.('Failed to log edit history:', error.message);
149
+ }
150
+ }
151
+ // Store the updated entry
152
+ await databases.kb.KnowledgeEntry.put(updated);
153
+ return updated;
154
+ }
155
+ /**
156
+ * Mark an entry as deprecated.
157
+ *
158
+ * @param id - ID of the entry to deprecate
159
+ * @throws Error if the entry does not exist
160
+ */
161
+ export async function deprecateEntry(id) {
162
+ const existing = await databases.kb.KnowledgeEntry.get(id);
163
+ if (!existing) {
164
+ throw new Error(`Knowledge entry not found: ${id}`);
165
+ }
166
+ await databases.kb.KnowledgeEntry.put({
167
+ ...existing,
168
+ id,
169
+ deprecated: true,
170
+ });
171
+ }
172
+ /**
173
+ * Link a new entry as superseding an old entry.
174
+ *
175
+ * Sets newEntry.supersedesId = oldId and oldEntry.supersededById = newId.
176
+ *
177
+ * @param newId - ID of the new (superseding) entry
178
+ * @param oldId - ID of the old (superseded) entry
179
+ * @throws Error if either entry does not exist
180
+ */
181
+ export async function linkSupersedes(newId, oldId) {
182
+ const newEntry = await databases.kb.KnowledgeEntry.get(newId);
183
+ const oldEntry = await databases.kb.KnowledgeEntry.get(oldId);
184
+ if (!newEntry) {
185
+ throw new Error(`New entry not found: ${newId}`);
186
+ }
187
+ if (!oldEntry) {
188
+ throw new Error(`Old entry not found: ${oldId}`);
189
+ }
190
+ await databases.kb.KnowledgeEntry.put({
191
+ ...newEntry,
192
+ id: newId,
193
+ supersedesId: oldId,
194
+ });
195
+ await databases.kb.KnowledgeEntry.put({
196
+ ...oldEntry,
197
+ id: oldId,
198
+ supersededById: newId,
199
+ });
200
+ }
201
+ /**
202
+ * Link multiple entries as siblings.
203
+ *
204
+ * For each entry, adds all other entry IDs to its siblingIds array (deduplicated).
205
+ *
206
+ * @param ids - IDs of entries to link as siblings
207
+ * @throws Error if any entry does not exist
208
+ */
209
+ export async function linkSiblings(ids) {
210
+ if (ids.length < 2) {
211
+ return; // Need at least 2 entries to link
212
+ }
213
+ // Load all entries first to verify they exist
214
+ const entries = [];
215
+ for (const id of ids) {
216
+ const entry = await databases.kb.KnowledgeEntry.get(id);
217
+ if (!entry) {
218
+ throw new Error(`Entry not found: ${id}`);
219
+ }
220
+ entries.push(entry);
221
+ }
222
+ // Update each entry's siblingIds
223
+ for (let i = 0; i < ids.length; i++) {
224
+ const entry = entries[i];
225
+ const entryTyped = entry;
226
+ const existingSiblings = new Set(entryTyped.siblingIds || []);
227
+ // Add all other IDs (not itself)
228
+ for (const otherId of ids) {
229
+ if (otherId !== ids[i]) {
230
+ existingSiblings.add(otherId);
231
+ }
232
+ }
233
+ await databases.kb.KnowledgeEntry.put({
234
+ ...entry,
235
+ id: ids[i],
236
+ siblingIds: Array.from(existingSiblings),
237
+ });
238
+ }
239
+ }
240
+ /**
241
+ * Reindex embeddings for all entries missing them.
242
+ *
243
+ * Iterates every KnowledgeEntry, generates embeddings for any that
244
+ * don't have one yet, and writes them back. Returns counts of
245
+ * processed, updated, and failed entries.
246
+ */
247
+ export async function reindexEmbeddings(kbId) {
248
+ let total = 0;
249
+ let updated = 0;
250
+ let failed = 0;
251
+ let skipped = 0;
252
+ for await (const record of databases.kb.KnowledgeEntry.search({
253
+ conditions: [{ attribute: 'kbId', comparator: 'equals', value: kbId }],
254
+ })) {
255
+ total++;
256
+ const entry = record;
257
+ // Skip entries that already have embeddings
258
+ if (entry.embedding && entry.embedding.length > 0) {
259
+ skipped++;
260
+ continue;
261
+ }
262
+ try {
263
+ const embeddingText = `${entry.title}\n\n${entry.content}`;
264
+ const embedding = await generateEmbedding(embeddingText);
265
+ await databases.kb.KnowledgeEntry.put({
266
+ ...record,
267
+ id: entry.id,
268
+ embedding,
269
+ });
270
+ updated++;
271
+ logger?.info?.(`Reindexed embedding for entry: ${entry.id}`);
272
+ }
273
+ catch (error) {
274
+ failed++;
275
+ logger?.warn?.(`Failed to reindex entry ${entry.id}:`, error.message);
276
+ }
277
+ }
278
+ return { total, updated, failed, skipped };
279
+ }
280
+ /**
281
+ * Link two entries as related.
282
+ *
283
+ * Adds relatedId to the entry's relatedIds array (deduplicated).
284
+ * This is a one-directional link; call twice for bidirectional.
285
+ *
286
+ * @param id - ID of the entry to add a related link to
287
+ * @param relatedId - ID of the related entry
288
+ * @throws Error if the entry does not exist
289
+ */
290
+ export async function linkRelated(id, relatedId) {
291
+ const entry = await databases.kb.KnowledgeEntry.get(id);
292
+ if (!entry) {
293
+ throw new Error(`Entry not found: ${id}`);
294
+ }
295
+ const entryTyped = entry;
296
+ const existingRelated = new Set(entryTyped.relatedIds || []);
297
+ existingRelated.add(relatedId);
298
+ await databases.kb.KnowledgeEntry.put({
299
+ ...entry,
300
+ id,
301
+ relatedIds: Array.from(existingRelated),
302
+ });
303
+ }
304
+ //# sourceMappingURL=entries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"entries.js","sourceRoot":"","sources":["../../src/core/entries.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,31 @@
1
+ /**
2
+ * Edit History Tracking
3
+ *
4
+ * Append-only audit log for knowledge entry edits.
5
+ * Records who changed what, when, and why — with a snapshot
6
+ * of the previous state for diffing.
7
+ */
8
+ import type { KnowledgeEntry, KnowledgeEntryEdit } from '../types.ts';
9
+ /**
10
+ * Log an edit to a knowledge entry.
11
+ *
12
+ * Compares the previous and updated entries to determine which fields changed,
13
+ * then stores an edit record with the previous snapshot.
14
+ *
15
+ * @param entryId - ID of the entry that was edited
16
+ * @param previous - The entry state before the edit
17
+ * @param updated - The entry state after the edit
18
+ * @param editedBy - Username of who made the edit
19
+ * @param editSummary - Optional description of what changed and why
20
+ * @returns The created edit record
21
+ */
22
+ export declare function logEdit(kbId: string, entryId: string, previous: KnowledgeEntry, updated: KnowledgeEntry, editedBy: string, editSummary?: string): Promise<KnowledgeEntryEdit>;
23
+ /**
24
+ * Get the edit history for a knowledge entry, newest first.
25
+ *
26
+ * @param entryId - ID of the entry to get history for
27
+ * @param limit - Maximum number of edits to return (default 50)
28
+ * @returns Array of edit records, newest first
29
+ */
30
+ export declare function getHistory(entryId: string, limit?: number): Promise<KnowledgeEntryEdit[]>;
31
+ //# sourceMappingURL=history.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../../src/core/history.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtE;;;;;;;;;;;;GAYG;AACH,wBAAsB,OAAO,CAC5B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,QAAQ,EAAE,cAAc,EACxB,OAAO,EAAE,cAAc,EACvB,QAAQ,EAAE,MAAM,EAChB,WAAW,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,kBAAkB,CAAC,CAuB7B;AAED;;;;;;GAMG;AACH,wBAAsB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,SAAK,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAY3F"}
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Edit History Tracking
3
+ *
4
+ * Append-only audit log for knowledge entry edits.
5
+ * Records who changed what, when, and why — with a snapshot
6
+ * of the previous state for diffing.
7
+ */
8
+ import crypto from 'node:crypto';
9
+ /**
10
+ * Log an edit to a knowledge entry.
11
+ *
12
+ * Compares the previous and updated entries to determine which fields changed,
13
+ * then stores an edit record with the previous snapshot.
14
+ *
15
+ * @param entryId - ID of the entry that was edited
16
+ * @param previous - The entry state before the edit
17
+ * @param updated - The entry state after the edit
18
+ * @param editedBy - Username of who made the edit
19
+ * @param editSummary - Optional description of what changed and why
20
+ * @returns The created edit record
21
+ */
22
+ export async function logEdit(kbId, entryId, previous, updated, editedBy, editSummary) {
23
+ // Determine which fields actually changed
24
+ const changedFields = detectChangedFields(previous, updated);
25
+ // Build a snapshot of only the previous values for changed fields
26
+ const previousSnapshot = {};
27
+ for (const field of changedFields) {
28
+ previousSnapshot[field] = previous[field];
29
+ }
30
+ const edit = {
31
+ id: crypto.randomUUID(),
32
+ kbId,
33
+ entryId,
34
+ editedBy,
35
+ editSummary,
36
+ previousSnapshot,
37
+ changedFields,
38
+ };
39
+ await databases.kb.KnowledgeEntryEdit.put(edit);
40
+ return edit;
41
+ }
42
+ /**
43
+ * Get the edit history for a knowledge entry, newest first.
44
+ *
45
+ * @param entryId - ID of the entry to get history for
46
+ * @param limit - Maximum number of edits to return (default 50)
47
+ * @returns Array of edit records, newest first
48
+ */
49
+ export async function getHistory(entryId, limit = 50) {
50
+ const edits = [];
51
+ for await (const record of databases.kb.KnowledgeEntryEdit.search({
52
+ conditions: [{ attribute: 'entryId', comparator: 'equals', value: entryId }],
53
+ sort: { attribute: 'createdAt', descending: true },
54
+ limit,
55
+ })) {
56
+ edits.push(record);
57
+ }
58
+ return edits;
59
+ }
60
+ /**
61
+ * Compare two entry states and return the list of fields that differ.
62
+ * Ignores internal fields like embedding and updatedAt.
63
+ */
64
+ function detectChangedFields(previous, updated) {
65
+ const trackableFields = [
66
+ 'title',
67
+ 'content',
68
+ 'tags',
69
+ 'appliesTo',
70
+ 'source',
71
+ 'sourceUrl',
72
+ 'confidence',
73
+ 'addedBy',
74
+ 'reviewedBy',
75
+ 'metadata',
76
+ 'deprecated',
77
+ 'supersedesId',
78
+ 'supersededById',
79
+ 'siblingIds',
80
+ 'relatedIds',
81
+ ];
82
+ const changed = [];
83
+ for (const field of trackableFields) {
84
+ const prev = previous[field];
85
+ const next = updated[field];
86
+ if (!deepEqual(prev, next)) {
87
+ changed.push(field);
88
+ }
89
+ }
90
+ return changed;
91
+ }
92
+ /**
93
+ * Simple deep equality check for JSON-serializable values.
94
+ */
95
+ function deepEqual(a, b) {
96
+ if (a === b)
97
+ return true;
98
+ if (a == null || b == null)
99
+ return a === b;
100
+ if (typeof a !== typeof b)
101
+ return false;
102
+ if (Array.isArray(a) && Array.isArray(b)) {
103
+ if (a.length !== b.length)
104
+ return false;
105
+ return a.every((val, i) => deepEqual(val, b[i]));
106
+ }
107
+ if (typeof a === 'object' && typeof b === 'object') {
108
+ const aObj = a;
109
+ const bObj = b;
110
+ const keys = new Set([...Object.keys(aObj), ...Object.keys(bObj)]);
111
+ for (const key of keys) {
112
+ if (!deepEqual(aObj[key], bObj[key]))
113
+ return false;
114
+ }
115
+ return true;
116
+ }
117
+ return false;
118
+ }
119
+ //# sourceMappingURL=history.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"history.js","sourceRoot":"","sources":["../../src/core/history.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Knowledge Base Management
3
+ *
4
+ * CRUD operations for the KnowledgeBase registry table.
5
+ * Each KB is a tenant — entries, tags, triage items, etc. are scoped to a KB.
6
+ */
7
+ import type { KnowledgeBase, KnowledgeBaseInput, KnowledgeBaseUpdate } from '../types.ts';
8
+ /**
9
+ * Create a new knowledge base.
10
+ *
11
+ * @param data - KB data to create
12
+ * @returns The created knowledge base
13
+ * @throws Error if a KB with the same id already exists
14
+ */
15
+ export declare function createKnowledgeBase(data: KnowledgeBaseInput): Promise<KnowledgeBase>;
16
+ /**
17
+ * Get a knowledge base by ID.
18
+ *
19
+ * @param id - KB identifier
20
+ * @returns The KB record, or null if not found
21
+ */
22
+ export declare function getKnowledgeBase(id: string): Promise<KnowledgeBase | null>;
23
+ /**
24
+ * Update an existing knowledge base.
25
+ *
26
+ * @param id - KB identifier
27
+ * @param data - Fields to update
28
+ * @returns The updated KB record
29
+ * @throws Error if the KB does not exist
30
+ */
31
+ export declare function updateKnowledgeBase(id: string, data: KnowledgeBaseUpdate): Promise<KnowledgeBase>;
32
+ /**
33
+ * Delete a knowledge base.
34
+ *
35
+ * Only deletes the registry record — does NOT cascade to entries, tags, etc.
36
+ * The caller is responsible for cleanup or can leave orphaned data for
37
+ * eventual consistency.
38
+ *
39
+ * @param id - KB identifier
40
+ * @throws Error if the KB does not exist
41
+ */
42
+ export declare function deleteKnowledgeBase(id: string): Promise<void>;
43
+ /**
44
+ * List all knowledge bases.
45
+ *
46
+ * @returns Array of KB records
47
+ */
48
+ export declare function listKnowledgeBases(): Promise<KnowledgeBase[]>;
49
+ //# sourceMappingURL=knowledge-base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knowledge-base.d.ts","sourceRoot":"","sources":["../../src/core/knowledge-base.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,kBAAkB,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAE1F;;;;;;GAMG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,aAAa,CAAC,CAiB1F;AAED;;;;;GAKG;AACH,wBAAsB,gBAAgB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAahF;AAED;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,aAAa,CAAC,CAsBvG;AAED;;;;;;;;;GASG;AACH,wBAAsB,mBAAmB,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMnE;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,IAAI,OAAO,CAAC,aAAa,EAAE,CAAC,CAcnE"}
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Knowledge Base Management
3
+ *
4
+ * CRUD operations for the KnowledgeBase registry table.
5
+ * Each KB is a tenant — entries, tags, triage items, etc. are scoped to a KB.
6
+ */
7
+ /**
8
+ * Create a new knowledge base.
9
+ *
10
+ * @param data - KB data to create
11
+ * @returns The created knowledge base
12
+ * @throws Error if a KB with the same id already exists
13
+ */
14
+ export async function createKnowledgeBase(data) {
15
+ const existing = await databases.kb.KnowledgeBase.get(data.id);
16
+ if (existing) {
17
+ throw new Error(`Knowledge base already exists: ${data.id}`);
18
+ }
19
+ const kb = {
20
+ id: data.id,
21
+ name: data.name,
22
+ description: data.description,
23
+ settings: data.settings,
24
+ createdBy: data.createdBy,
25
+ };
26
+ await databases.kb.KnowledgeBase.put(kb);
27
+ return kb;
28
+ }
29
+ /**
30
+ * Get a knowledge base by ID.
31
+ *
32
+ * @param id - KB identifier
33
+ * @returns The KB record, or null if not found
34
+ */
35
+ export async function getKnowledgeBase(id) {
36
+ const record = await databases.kb.KnowledgeBase.get(id);
37
+ if (!record)
38
+ return null;
39
+ return {
40
+ id: record.id,
41
+ name: record.name,
42
+ description: record.description,
43
+ settings: record.settings,
44
+ createdBy: record.createdBy,
45
+ createdAt: record.createdAt,
46
+ updatedAt: record.updatedAt,
47
+ };
48
+ }
49
+ /**
50
+ * Update an existing knowledge base.
51
+ *
52
+ * @param id - KB identifier
53
+ * @param data - Fields to update
54
+ * @returns The updated KB record
55
+ * @throws Error if the KB does not exist
56
+ */
57
+ export async function updateKnowledgeBase(id, data) {
58
+ const existing = await databases.kb.KnowledgeBase.get(id);
59
+ if (!existing) {
60
+ throw new Error(`Knowledge base not found: ${id}`);
61
+ }
62
+ const updated = { ...existing, id };
63
+ if (data.name !== undefined)
64
+ updated.name = data.name;
65
+ if (data.description !== undefined)
66
+ updated.description = data.description;
67
+ if (data.settings !== undefined)
68
+ updated.settings = data.settings;
69
+ await databases.kb.KnowledgeBase.put(updated);
70
+ return {
71
+ id: updated.id,
72
+ name: updated.name,
73
+ description: updated.description,
74
+ settings: updated.settings,
75
+ createdBy: updated.createdBy,
76
+ createdAt: updated.createdAt,
77
+ updatedAt: updated.updatedAt,
78
+ };
79
+ }
80
+ /**
81
+ * Delete a knowledge base.
82
+ *
83
+ * Only deletes the registry record — does NOT cascade to entries, tags, etc.
84
+ * The caller is responsible for cleanup or can leave orphaned data for
85
+ * eventual consistency.
86
+ *
87
+ * @param id - KB identifier
88
+ * @throws Error if the KB does not exist
89
+ */
90
+ export async function deleteKnowledgeBase(id) {
91
+ const existing = await databases.kb.KnowledgeBase.get(id);
92
+ if (!existing) {
93
+ throw new Error(`Knowledge base not found: ${id}`);
94
+ }
95
+ await databases.kb.KnowledgeBase.delete(id);
96
+ }
97
+ /**
98
+ * List all knowledge bases.
99
+ *
100
+ * @returns Array of KB records
101
+ */
102
+ export async function listKnowledgeBases() {
103
+ const results = [];
104
+ for await (const record of databases.kb.KnowledgeBase.search({})) {
105
+ results.push({
106
+ id: record.id,
107
+ name: record.name,
108
+ description: record.description,
109
+ settings: record.settings,
110
+ createdBy: record.createdBy,
111
+ createdAt: record.createdAt,
112
+ updatedAt: record.updatedAt,
113
+ });
114
+ }
115
+ return results;
116
+ }
117
+ //# sourceMappingURL=knowledge-base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"knowledge-base.js","sourceRoot":"","sources":["../../src/core/knowledge-base.ts"],"names":[],"mappings":"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"}