claude-memory-layer 1.0.2 → 1.0.4
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/.claude/settings.local.json +3 -1
- package/.history/package_20260201133143.json +45 -0
- package/CLAUDE.md +14 -0
- package/dist/cli/index.js +836 -9
- package/dist/cli/index.js.map +4 -4
- package/dist/core/index.js +782 -3
- package/dist/core/index.js.map +4 -4
- package/dist/hooks/session-end.js +836 -9
- package/dist/hooks/session-end.js.map +4 -4
- package/dist/hooks/session-start.js +836 -9
- package/dist/hooks/session-start.js.map +4 -4
- package/dist/hooks/stop.js +836 -9
- package/dist/hooks/stop.js.map +4 -4
- package/dist/hooks/user-prompt-submit.js +839 -10
- package/dist/hooks/user-prompt-submit.js.map +4 -4
- package/dist/services/memory-service.js +836 -9
- package/dist/services/memory-service.js.map +4 -4
- package/package.json +1 -1
- package/src/core/index.ts +6 -0
- package/src/core/retriever.ts +118 -2
- package/src/core/shared-event-store.ts +114 -0
- package/src/core/shared-promoter.ts +249 -0
- package/src/core/shared-store.ts +289 -0
- package/src/core/shared-vector-store.ts +203 -0
- package/src/core/types.ts +69 -2
- package/src/hooks/user-prompt-submit.ts +6 -2
- package/src/services/memory-service.ts +145 -6
package/dist/core/index.js
CHANGED
|
@@ -133,7 +133,14 @@ var ConfigSchema = z.object({
|
|
|
133
133
|
sessionSummary: z.boolean().default(true),
|
|
134
134
|
insightExtraction: z.boolean().default(true),
|
|
135
135
|
crossProjectLearning: z.boolean().default(false),
|
|
136
|
-
singleWriterMode: z.boolean().default(true)
|
|
136
|
+
singleWriterMode: z.boolean().default(true),
|
|
137
|
+
sharedStore: z.object({
|
|
138
|
+
enabled: z.boolean().default(true),
|
|
139
|
+
autoPromote: z.boolean().default(true),
|
|
140
|
+
searchShared: z.boolean().default(true),
|
|
141
|
+
minConfidenceForPromotion: z.number().default(0.8),
|
|
142
|
+
sharedStoragePath: z.string().default("~/.claude-code/memory/shared")
|
|
143
|
+
}).default({})
|
|
137
144
|
}).default({}),
|
|
138
145
|
mode: z.enum(["session", "endless"]).default("session"),
|
|
139
146
|
endless: z.object({
|
|
@@ -291,7 +298,8 @@ var EntryTypeSchema = z.enum([
|
|
|
291
298
|
"task_note",
|
|
292
299
|
"reference",
|
|
293
300
|
"preference",
|
|
294
|
-
"pattern"
|
|
301
|
+
"pattern",
|
|
302
|
+
"troubleshooting"
|
|
295
303
|
]);
|
|
296
304
|
var EntrySchema = z.object({
|
|
297
305
|
entryId: z.string(),
|
|
@@ -488,6 +496,34 @@ var ContinuityLogSchema = z.object({
|
|
|
488
496
|
transitionType: TransitionTypeSchema,
|
|
489
497
|
createdAt: z.date()
|
|
490
498
|
});
|
|
499
|
+
var SharedEntryTypeSchema = z.enum([
|
|
500
|
+
"troubleshooting",
|
|
501
|
+
"best_practice",
|
|
502
|
+
"common_error"
|
|
503
|
+
]);
|
|
504
|
+
var SharedTroubleshootingEntrySchema = z.object({
|
|
505
|
+
entryId: z.string(),
|
|
506
|
+
sourceProjectHash: z.string(),
|
|
507
|
+
sourceEntryId: z.string(),
|
|
508
|
+
title: z.string(),
|
|
509
|
+
symptoms: z.array(z.string()),
|
|
510
|
+
rootCause: z.string(),
|
|
511
|
+
solution: z.string(),
|
|
512
|
+
topics: z.array(z.string()),
|
|
513
|
+
technologies: z.array(z.string()).optional(),
|
|
514
|
+
confidence: z.number().min(0).max(1),
|
|
515
|
+
usageCount: z.number().default(0),
|
|
516
|
+
lastUsedAt: z.date().optional(),
|
|
517
|
+
promotedAt: z.date(),
|
|
518
|
+
createdAt: z.date()
|
|
519
|
+
});
|
|
520
|
+
var SharedStoreConfigSchema = z.object({
|
|
521
|
+
enabled: z.boolean().default(true),
|
|
522
|
+
autoPromote: z.boolean().default(true),
|
|
523
|
+
searchShared: z.boolean().default(true),
|
|
524
|
+
minConfidenceForPromotion: z.number().default(0.8),
|
|
525
|
+
sharedStoragePath: z.string().default("~/.claude-code/memory/shared")
|
|
526
|
+
});
|
|
491
527
|
|
|
492
528
|
// src/core/canonical-key.ts
|
|
493
529
|
import { createHash } from "crypto";
|
|
@@ -3129,11 +3165,22 @@ var Retriever = class {
|
|
|
3129
3165
|
vectorStore;
|
|
3130
3166
|
embedder;
|
|
3131
3167
|
matcher;
|
|
3132
|
-
|
|
3168
|
+
sharedStore;
|
|
3169
|
+
sharedVectorStore;
|
|
3170
|
+
constructor(eventStore, vectorStore, embedder, matcher, sharedOptions) {
|
|
3133
3171
|
this.eventStore = eventStore;
|
|
3134
3172
|
this.vectorStore = vectorStore;
|
|
3135
3173
|
this.embedder = embedder;
|
|
3136
3174
|
this.matcher = matcher;
|
|
3175
|
+
this.sharedStore = sharedOptions?.sharedStore;
|
|
3176
|
+
this.sharedVectorStore = sharedOptions?.sharedVectorStore;
|
|
3177
|
+
}
|
|
3178
|
+
/**
|
|
3179
|
+
* Set shared stores after construction
|
|
3180
|
+
*/
|
|
3181
|
+
setSharedStores(sharedStore, sharedVectorStore) {
|
|
3182
|
+
this.sharedStore = sharedStore;
|
|
3183
|
+
this.sharedVectorStore = sharedVectorStore;
|
|
3137
3184
|
}
|
|
3138
3185
|
/**
|
|
3139
3186
|
* Retrieve relevant memories for a query
|
|
@@ -3160,6 +3207,75 @@ var Retriever = class {
|
|
|
3160
3207
|
context
|
|
3161
3208
|
};
|
|
3162
3209
|
}
|
|
3210
|
+
/**
|
|
3211
|
+
* Retrieve with unified search (project + shared)
|
|
3212
|
+
*/
|
|
3213
|
+
async retrieveUnified(query, options = {}) {
|
|
3214
|
+
const projectResult = await this.retrieve(query, options);
|
|
3215
|
+
if (!options.includeShared || !this.sharedStore || !this.sharedVectorStore) {
|
|
3216
|
+
return projectResult;
|
|
3217
|
+
}
|
|
3218
|
+
try {
|
|
3219
|
+
const queryEmbedding = await this.embedder.embed(query);
|
|
3220
|
+
const sharedVectorResults = await this.sharedVectorStore.search(
|
|
3221
|
+
queryEmbedding.vector,
|
|
3222
|
+
{
|
|
3223
|
+
limit: options.topK || 5,
|
|
3224
|
+
minScore: options.minScore || 0.7,
|
|
3225
|
+
excludeProjectHash: options.projectHash
|
|
3226
|
+
}
|
|
3227
|
+
);
|
|
3228
|
+
const sharedMemories = [];
|
|
3229
|
+
for (const result of sharedVectorResults) {
|
|
3230
|
+
const entry = await this.sharedStore.get(result.entryId);
|
|
3231
|
+
if (entry) {
|
|
3232
|
+
if (!options.projectHash || entry.sourceProjectHash !== options.projectHash) {
|
|
3233
|
+
sharedMemories.push(entry);
|
|
3234
|
+
await this.sharedStore.recordUsage(entry.entryId);
|
|
3235
|
+
}
|
|
3236
|
+
}
|
|
3237
|
+
}
|
|
3238
|
+
const unifiedContext = this.buildUnifiedContext(projectResult, sharedMemories);
|
|
3239
|
+
return {
|
|
3240
|
+
...projectResult,
|
|
3241
|
+
context: unifiedContext,
|
|
3242
|
+
totalTokens: this.estimateTokens(unifiedContext),
|
|
3243
|
+
sharedMemories
|
|
3244
|
+
};
|
|
3245
|
+
} catch (error) {
|
|
3246
|
+
console.error("Shared search failed:", error);
|
|
3247
|
+
return projectResult;
|
|
3248
|
+
}
|
|
3249
|
+
}
|
|
3250
|
+
/**
|
|
3251
|
+
* Build unified context combining project and shared memories
|
|
3252
|
+
*/
|
|
3253
|
+
buildUnifiedContext(projectResult, sharedMemories) {
|
|
3254
|
+
let context = projectResult.context;
|
|
3255
|
+
if (sharedMemories.length > 0) {
|
|
3256
|
+
context += "\n\n## Cross-Project Knowledge\n\n";
|
|
3257
|
+
for (const memory of sharedMemories.slice(0, 3)) {
|
|
3258
|
+
context += `### ${memory.title}
|
|
3259
|
+
`;
|
|
3260
|
+
if (memory.symptoms.length > 0) {
|
|
3261
|
+
context += `**Symptoms:** ${memory.symptoms.join(", ")}
|
|
3262
|
+
`;
|
|
3263
|
+
}
|
|
3264
|
+
context += `**Root Cause:** ${memory.rootCause}
|
|
3265
|
+
`;
|
|
3266
|
+
context += `**Solution:** ${memory.solution}
|
|
3267
|
+
`;
|
|
3268
|
+
if (memory.technologies && memory.technologies.length > 0) {
|
|
3269
|
+
context += `**Technologies:** ${memory.technologies.join(", ")}
|
|
3270
|
+
`;
|
|
3271
|
+
}
|
|
3272
|
+
context += `_Confidence: ${(memory.confidence * 100).toFixed(0)}%_
|
|
3273
|
+
|
|
3274
|
+
`;
|
|
3275
|
+
}
|
|
3276
|
+
}
|
|
3277
|
+
return context;
|
|
3278
|
+
}
|
|
3163
3279
|
/**
|
|
3164
3280
|
* Retrieve memories from a specific session
|
|
3165
3281
|
*/
|
|
@@ -4488,6 +4604,658 @@ var TaskProjector = class {
|
|
|
4488
4604
|
return this.processAll();
|
|
4489
4605
|
}
|
|
4490
4606
|
};
|
|
4607
|
+
|
|
4608
|
+
// src/core/shared-event-store.ts
|
|
4609
|
+
var SharedEventStore = class {
|
|
4610
|
+
constructor(dbPath) {
|
|
4611
|
+
this.dbPath = dbPath;
|
|
4612
|
+
this.db = createDatabase(dbPath);
|
|
4613
|
+
}
|
|
4614
|
+
db;
|
|
4615
|
+
initialized = false;
|
|
4616
|
+
async initialize() {
|
|
4617
|
+
if (this.initialized)
|
|
4618
|
+
return;
|
|
4619
|
+
await dbRun(this.db, `
|
|
4620
|
+
CREATE TABLE IF NOT EXISTS shared_troubleshooting (
|
|
4621
|
+
entry_id VARCHAR PRIMARY KEY,
|
|
4622
|
+
source_project_hash VARCHAR NOT NULL,
|
|
4623
|
+
source_entry_id VARCHAR NOT NULL,
|
|
4624
|
+
title VARCHAR NOT NULL,
|
|
4625
|
+
symptoms JSON NOT NULL,
|
|
4626
|
+
root_cause TEXT NOT NULL,
|
|
4627
|
+
solution TEXT NOT NULL,
|
|
4628
|
+
topics JSON NOT NULL,
|
|
4629
|
+
technologies JSON,
|
|
4630
|
+
confidence REAL NOT NULL DEFAULT 0.8,
|
|
4631
|
+
usage_count INTEGER DEFAULT 0,
|
|
4632
|
+
last_used_at TIMESTAMP,
|
|
4633
|
+
promoted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
4634
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
4635
|
+
UNIQUE(source_project_hash, source_entry_id)
|
|
4636
|
+
)
|
|
4637
|
+
`);
|
|
4638
|
+
await dbRun(this.db, `
|
|
4639
|
+
CREATE TABLE IF NOT EXISTS shared_best_practices (
|
|
4640
|
+
entry_id VARCHAR PRIMARY KEY,
|
|
4641
|
+
source_project_hash VARCHAR NOT NULL,
|
|
4642
|
+
source_entry_id VARCHAR NOT NULL,
|
|
4643
|
+
title VARCHAR NOT NULL,
|
|
4644
|
+
content_json JSON NOT NULL,
|
|
4645
|
+
topics JSON NOT NULL,
|
|
4646
|
+
confidence REAL NOT NULL DEFAULT 0.8,
|
|
4647
|
+
usage_count INTEGER DEFAULT 0,
|
|
4648
|
+
promoted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
4649
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
4650
|
+
UNIQUE(source_project_hash, source_entry_id)
|
|
4651
|
+
)
|
|
4652
|
+
`);
|
|
4653
|
+
await dbRun(this.db, `
|
|
4654
|
+
CREATE TABLE IF NOT EXISTS shared_common_errors (
|
|
4655
|
+
entry_id VARCHAR PRIMARY KEY,
|
|
4656
|
+
source_project_hash VARCHAR NOT NULL,
|
|
4657
|
+
source_entry_id VARCHAR NOT NULL,
|
|
4658
|
+
title VARCHAR NOT NULL,
|
|
4659
|
+
error_pattern TEXT NOT NULL,
|
|
4660
|
+
solution TEXT NOT NULL,
|
|
4661
|
+
topics JSON NOT NULL,
|
|
4662
|
+
technologies JSON,
|
|
4663
|
+
confidence REAL NOT NULL DEFAULT 0.8,
|
|
4664
|
+
usage_count INTEGER DEFAULT 0,
|
|
4665
|
+
promoted_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
4666
|
+
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
4667
|
+
UNIQUE(source_project_hash, source_entry_id)
|
|
4668
|
+
)
|
|
4669
|
+
`);
|
|
4670
|
+
await dbRun(this.db, `
|
|
4671
|
+
CREATE INDEX IF NOT EXISTS idx_shared_ts_confidence
|
|
4672
|
+
ON shared_troubleshooting(confidence DESC)
|
|
4673
|
+
`);
|
|
4674
|
+
await dbRun(this.db, `
|
|
4675
|
+
CREATE INDEX IF NOT EXISTS idx_shared_ts_usage
|
|
4676
|
+
ON shared_troubleshooting(usage_count DESC)
|
|
4677
|
+
`);
|
|
4678
|
+
await dbRun(this.db, `
|
|
4679
|
+
CREATE INDEX IF NOT EXISTS idx_shared_ts_source
|
|
4680
|
+
ON shared_troubleshooting(source_project_hash)
|
|
4681
|
+
`);
|
|
4682
|
+
this.initialized = true;
|
|
4683
|
+
}
|
|
4684
|
+
getDatabase() {
|
|
4685
|
+
return this.db;
|
|
4686
|
+
}
|
|
4687
|
+
isInitialized() {
|
|
4688
|
+
return this.initialized;
|
|
4689
|
+
}
|
|
4690
|
+
async close() {
|
|
4691
|
+
await dbClose(this.db);
|
|
4692
|
+
this.initialized = false;
|
|
4693
|
+
}
|
|
4694
|
+
};
|
|
4695
|
+
function createSharedEventStore(dbPath) {
|
|
4696
|
+
return new SharedEventStore(dbPath);
|
|
4697
|
+
}
|
|
4698
|
+
|
|
4699
|
+
// src/core/shared-store.ts
|
|
4700
|
+
import { randomUUID as randomUUID8 } from "crypto";
|
|
4701
|
+
var SharedStore = class {
|
|
4702
|
+
constructor(sharedEventStore) {
|
|
4703
|
+
this.sharedEventStore = sharedEventStore;
|
|
4704
|
+
}
|
|
4705
|
+
get db() {
|
|
4706
|
+
return this.sharedEventStore.getDatabase();
|
|
4707
|
+
}
|
|
4708
|
+
/**
|
|
4709
|
+
* Promote a verified troubleshooting entry to shared storage
|
|
4710
|
+
*/
|
|
4711
|
+
async promoteEntry(input) {
|
|
4712
|
+
const entryId = randomUUID8();
|
|
4713
|
+
await dbRun(
|
|
4714
|
+
this.db,
|
|
4715
|
+
`INSERT INTO shared_troubleshooting (
|
|
4716
|
+
entry_id, source_project_hash, source_entry_id,
|
|
4717
|
+
title, symptoms, root_cause, solution, topics,
|
|
4718
|
+
technologies, confidence, promoted_at
|
|
4719
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
|
|
4720
|
+
ON CONFLICT (source_project_hash, source_entry_id)
|
|
4721
|
+
DO UPDATE SET
|
|
4722
|
+
title = excluded.title,
|
|
4723
|
+
symptoms = excluded.symptoms,
|
|
4724
|
+
root_cause = excluded.root_cause,
|
|
4725
|
+
solution = excluded.solution,
|
|
4726
|
+
topics = excluded.topics,
|
|
4727
|
+
technologies = excluded.technologies,
|
|
4728
|
+
confidence = CASE
|
|
4729
|
+
WHEN excluded.confidence > shared_troubleshooting.confidence
|
|
4730
|
+
THEN excluded.confidence
|
|
4731
|
+
ELSE shared_troubleshooting.confidence
|
|
4732
|
+
END`,
|
|
4733
|
+
[
|
|
4734
|
+
entryId,
|
|
4735
|
+
input.sourceProjectHash,
|
|
4736
|
+
input.sourceEntryId,
|
|
4737
|
+
input.title,
|
|
4738
|
+
JSON.stringify(input.symptoms),
|
|
4739
|
+
input.rootCause,
|
|
4740
|
+
input.solution,
|
|
4741
|
+
JSON.stringify(input.topics),
|
|
4742
|
+
JSON.stringify(input.technologies || []),
|
|
4743
|
+
input.confidence
|
|
4744
|
+
]
|
|
4745
|
+
);
|
|
4746
|
+
return entryId;
|
|
4747
|
+
}
|
|
4748
|
+
/**
|
|
4749
|
+
* Search troubleshooting entries by text query
|
|
4750
|
+
*/
|
|
4751
|
+
async search(query, options) {
|
|
4752
|
+
const topK = options?.topK || 5;
|
|
4753
|
+
const minConfidence = options?.minConfidence || 0.5;
|
|
4754
|
+
const searchPattern = `%${query}%`;
|
|
4755
|
+
const rows = await dbAll(
|
|
4756
|
+
this.db,
|
|
4757
|
+
`SELECT * FROM shared_troubleshooting
|
|
4758
|
+
WHERE (title LIKE ? OR root_cause LIKE ? OR solution LIKE ?)
|
|
4759
|
+
AND confidence >= ?
|
|
4760
|
+
ORDER BY confidence DESC, usage_count DESC
|
|
4761
|
+
LIMIT ?`,
|
|
4762
|
+
[searchPattern, searchPattern, searchPattern, minConfidence, topK]
|
|
4763
|
+
);
|
|
4764
|
+
return rows.map(this.rowToEntry);
|
|
4765
|
+
}
|
|
4766
|
+
/**
|
|
4767
|
+
* Search by topics
|
|
4768
|
+
*/
|
|
4769
|
+
async searchByTopics(topics, options) {
|
|
4770
|
+
const topK = options?.topK || 5;
|
|
4771
|
+
if (topics.length === 0) {
|
|
4772
|
+
return [];
|
|
4773
|
+
}
|
|
4774
|
+
const topicConditions = topics.map(() => `topics LIKE ?`).join(" OR ");
|
|
4775
|
+
const topicParams = topics.map((t) => `%"${t}"%`);
|
|
4776
|
+
let query = `SELECT * FROM shared_troubleshooting WHERE (${topicConditions})`;
|
|
4777
|
+
const params = [...topicParams];
|
|
4778
|
+
if (options?.excludeProjectHash) {
|
|
4779
|
+
query += ` AND source_project_hash != ?`;
|
|
4780
|
+
params.push(options.excludeProjectHash);
|
|
4781
|
+
}
|
|
4782
|
+
query += ` ORDER BY confidence DESC, usage_count DESC LIMIT ?`;
|
|
4783
|
+
params.push(topK);
|
|
4784
|
+
const rows = await dbAll(this.db, query, params);
|
|
4785
|
+
return rows.map(this.rowToEntry);
|
|
4786
|
+
}
|
|
4787
|
+
/**
|
|
4788
|
+
* Record usage of a shared entry (for ranking)
|
|
4789
|
+
*/
|
|
4790
|
+
async recordUsage(entryId) {
|
|
4791
|
+
await dbRun(
|
|
4792
|
+
this.db,
|
|
4793
|
+
`UPDATE shared_troubleshooting
|
|
4794
|
+
SET usage_count = usage_count + 1,
|
|
4795
|
+
last_used_at = CURRENT_TIMESTAMP
|
|
4796
|
+
WHERE entry_id = ?`,
|
|
4797
|
+
[entryId]
|
|
4798
|
+
);
|
|
4799
|
+
}
|
|
4800
|
+
/**
|
|
4801
|
+
* Get entry by ID
|
|
4802
|
+
*/
|
|
4803
|
+
async get(entryId) {
|
|
4804
|
+
const rows = await dbAll(
|
|
4805
|
+
this.db,
|
|
4806
|
+
`SELECT * FROM shared_troubleshooting WHERE entry_id = ?`,
|
|
4807
|
+
[entryId]
|
|
4808
|
+
);
|
|
4809
|
+
if (rows.length === 0)
|
|
4810
|
+
return null;
|
|
4811
|
+
return this.rowToEntry(rows[0]);
|
|
4812
|
+
}
|
|
4813
|
+
/**
|
|
4814
|
+
* Get entry by source (project hash + entry ID)
|
|
4815
|
+
*/
|
|
4816
|
+
async getBySource(projectHash, sourceEntryId) {
|
|
4817
|
+
const rows = await dbAll(
|
|
4818
|
+
this.db,
|
|
4819
|
+
`SELECT * FROM shared_troubleshooting
|
|
4820
|
+
WHERE source_project_hash = ? AND source_entry_id = ?`,
|
|
4821
|
+
[projectHash, sourceEntryId]
|
|
4822
|
+
);
|
|
4823
|
+
if (rows.length === 0)
|
|
4824
|
+
return null;
|
|
4825
|
+
return this.rowToEntry(rows[0]);
|
|
4826
|
+
}
|
|
4827
|
+
/**
|
|
4828
|
+
* Check if an entry already exists in shared store
|
|
4829
|
+
*/
|
|
4830
|
+
async exists(projectHash, sourceEntryId) {
|
|
4831
|
+
const result = await dbAll(
|
|
4832
|
+
this.db,
|
|
4833
|
+
`SELECT COUNT(*) as count FROM shared_troubleshooting
|
|
4834
|
+
WHERE source_project_hash = ? AND source_entry_id = ?`,
|
|
4835
|
+
[projectHash, sourceEntryId]
|
|
4836
|
+
);
|
|
4837
|
+
return (result[0]?.count || 0) > 0;
|
|
4838
|
+
}
|
|
4839
|
+
/**
|
|
4840
|
+
* Get all entries (with limit for safety)
|
|
4841
|
+
*/
|
|
4842
|
+
async getAll(options) {
|
|
4843
|
+
const limit = options?.limit || 100;
|
|
4844
|
+
const rows = await dbAll(
|
|
4845
|
+
this.db,
|
|
4846
|
+
`SELECT * FROM shared_troubleshooting
|
|
4847
|
+
ORDER BY confidence DESC, usage_count DESC
|
|
4848
|
+
LIMIT ?`,
|
|
4849
|
+
[limit]
|
|
4850
|
+
);
|
|
4851
|
+
return rows.map(this.rowToEntry);
|
|
4852
|
+
}
|
|
4853
|
+
/**
|
|
4854
|
+
* Get total count
|
|
4855
|
+
*/
|
|
4856
|
+
async count() {
|
|
4857
|
+
const result = await dbAll(
|
|
4858
|
+
this.db,
|
|
4859
|
+
`SELECT COUNT(*) as count FROM shared_troubleshooting`
|
|
4860
|
+
);
|
|
4861
|
+
return result[0]?.count || 0;
|
|
4862
|
+
}
|
|
4863
|
+
/**
|
|
4864
|
+
* Get statistics
|
|
4865
|
+
*/
|
|
4866
|
+
async getStats() {
|
|
4867
|
+
const countResult = await dbAll(
|
|
4868
|
+
this.db,
|
|
4869
|
+
`SELECT COUNT(*) as count FROM shared_troubleshooting`
|
|
4870
|
+
);
|
|
4871
|
+
const total = countResult[0]?.count || 0;
|
|
4872
|
+
const avgResult = await dbAll(
|
|
4873
|
+
this.db,
|
|
4874
|
+
`SELECT AVG(confidence) as avg FROM shared_troubleshooting`
|
|
4875
|
+
);
|
|
4876
|
+
const averageConfidence = avgResult[0]?.avg || 0;
|
|
4877
|
+
const usageResult = await dbAll(
|
|
4878
|
+
this.db,
|
|
4879
|
+
`SELECT SUM(usage_count) as total FROM shared_troubleshooting`
|
|
4880
|
+
);
|
|
4881
|
+
const totalUsageCount = usageResult[0]?.total || 0;
|
|
4882
|
+
const entries = await this.getAll({ limit: 1e3 });
|
|
4883
|
+
const topicCounts = {};
|
|
4884
|
+
for (const entry of entries) {
|
|
4885
|
+
for (const topic of entry.topics) {
|
|
4886
|
+
topicCounts[topic] = (topicCounts[topic] || 0) + 1;
|
|
4887
|
+
}
|
|
4888
|
+
}
|
|
4889
|
+
const topTopics = Object.entries(topicCounts).map(([topic, count]) => ({ topic, count })).sort((a, b) => b.count - a.count).slice(0, 10);
|
|
4890
|
+
return { total, averageConfidence, topTopics, totalUsageCount };
|
|
4891
|
+
}
|
|
4892
|
+
/**
|
|
4893
|
+
* Delete an entry
|
|
4894
|
+
*/
|
|
4895
|
+
async delete(entryId) {
|
|
4896
|
+
const before = await this.count();
|
|
4897
|
+
await dbRun(
|
|
4898
|
+
this.db,
|
|
4899
|
+
`DELETE FROM shared_troubleshooting WHERE entry_id = ?`,
|
|
4900
|
+
[entryId]
|
|
4901
|
+
);
|
|
4902
|
+
const after = await this.count();
|
|
4903
|
+
return before > after;
|
|
4904
|
+
}
|
|
4905
|
+
rowToEntry(row) {
|
|
4906
|
+
return {
|
|
4907
|
+
entryId: row.entry_id,
|
|
4908
|
+
sourceProjectHash: row.source_project_hash,
|
|
4909
|
+
sourceEntryId: row.source_entry_id,
|
|
4910
|
+
title: row.title,
|
|
4911
|
+
symptoms: JSON.parse(row.symptoms || "[]"),
|
|
4912
|
+
rootCause: row.root_cause,
|
|
4913
|
+
solution: row.solution,
|
|
4914
|
+
topics: JSON.parse(row.topics || "[]"),
|
|
4915
|
+
technologies: JSON.parse(row.technologies || "[]"),
|
|
4916
|
+
confidence: row.confidence,
|
|
4917
|
+
usageCount: row.usage_count || 0,
|
|
4918
|
+
lastUsedAt: row.last_used_at ? toDate(row.last_used_at) : void 0,
|
|
4919
|
+
promotedAt: toDate(row.promoted_at),
|
|
4920
|
+
createdAt: toDate(row.created_at)
|
|
4921
|
+
};
|
|
4922
|
+
}
|
|
4923
|
+
};
|
|
4924
|
+
function createSharedStore(sharedEventStore) {
|
|
4925
|
+
return new SharedStore(sharedEventStore);
|
|
4926
|
+
}
|
|
4927
|
+
|
|
4928
|
+
// src/core/shared-vector-store.ts
|
|
4929
|
+
import * as lancedb2 from "@lancedb/lancedb";
|
|
4930
|
+
var SharedVectorStore = class {
|
|
4931
|
+
constructor(dbPath) {
|
|
4932
|
+
this.dbPath = dbPath;
|
|
4933
|
+
}
|
|
4934
|
+
db = null;
|
|
4935
|
+
table = null;
|
|
4936
|
+
tableName = "shared_knowledge";
|
|
4937
|
+
/**
|
|
4938
|
+
* Initialize LanceDB connection
|
|
4939
|
+
*/
|
|
4940
|
+
async initialize() {
|
|
4941
|
+
if (this.db)
|
|
4942
|
+
return;
|
|
4943
|
+
this.db = await lancedb2.connect(this.dbPath);
|
|
4944
|
+
try {
|
|
4945
|
+
const tables = await this.db.tableNames();
|
|
4946
|
+
if (tables.includes(this.tableName)) {
|
|
4947
|
+
this.table = await this.db.openTable(this.tableName);
|
|
4948
|
+
}
|
|
4949
|
+
} catch {
|
|
4950
|
+
this.table = null;
|
|
4951
|
+
}
|
|
4952
|
+
}
|
|
4953
|
+
/**
|
|
4954
|
+
* Add or update a shared vector record
|
|
4955
|
+
*/
|
|
4956
|
+
async upsert(record) {
|
|
4957
|
+
await this.initialize();
|
|
4958
|
+
if (!this.db) {
|
|
4959
|
+
throw new Error("Database not initialized");
|
|
4960
|
+
}
|
|
4961
|
+
const data = {
|
|
4962
|
+
id: record.id,
|
|
4963
|
+
entryId: record.entryId,
|
|
4964
|
+
entryType: record.entryType,
|
|
4965
|
+
content: record.content,
|
|
4966
|
+
vector: record.vector,
|
|
4967
|
+
topics: JSON.stringify(record.topics),
|
|
4968
|
+
sourceProjectHash: record.sourceProjectHash || ""
|
|
4969
|
+
};
|
|
4970
|
+
if (!this.table) {
|
|
4971
|
+
this.table = await this.db.createTable(this.tableName, [data]);
|
|
4972
|
+
} else {
|
|
4973
|
+
try {
|
|
4974
|
+
await this.table.delete(`entryId = '${record.entryId}'`);
|
|
4975
|
+
} catch {
|
|
4976
|
+
}
|
|
4977
|
+
await this.table.add([data]);
|
|
4978
|
+
}
|
|
4979
|
+
}
|
|
4980
|
+
/**
|
|
4981
|
+
* Add multiple records in batch
|
|
4982
|
+
*/
|
|
4983
|
+
async upsertBatch(records) {
|
|
4984
|
+
if (records.length === 0)
|
|
4985
|
+
return;
|
|
4986
|
+
await this.initialize();
|
|
4987
|
+
if (!this.db) {
|
|
4988
|
+
throw new Error("Database not initialized");
|
|
4989
|
+
}
|
|
4990
|
+
const data = records.map((record) => ({
|
|
4991
|
+
id: record.id,
|
|
4992
|
+
entryId: record.entryId,
|
|
4993
|
+
entryType: record.entryType,
|
|
4994
|
+
content: record.content,
|
|
4995
|
+
vector: record.vector,
|
|
4996
|
+
topics: JSON.stringify(record.topics),
|
|
4997
|
+
sourceProjectHash: record.sourceProjectHash || ""
|
|
4998
|
+
}));
|
|
4999
|
+
if (!this.table) {
|
|
5000
|
+
this.table = await this.db.createTable(this.tableName, data);
|
|
5001
|
+
} else {
|
|
5002
|
+
await this.table.add(data);
|
|
5003
|
+
}
|
|
5004
|
+
}
|
|
5005
|
+
/**
|
|
5006
|
+
* Search for similar vectors
|
|
5007
|
+
*/
|
|
5008
|
+
async search(queryVector, options = {}) {
|
|
5009
|
+
await this.initialize();
|
|
5010
|
+
if (!this.table) {
|
|
5011
|
+
return [];
|
|
5012
|
+
}
|
|
5013
|
+
const { limit = 5, minScore = 0.7, excludeProjectHash, entryType } = options;
|
|
5014
|
+
let query = this.table.search(queryVector).distanceType("cosine").limit(limit * 2);
|
|
5015
|
+
const filters = [];
|
|
5016
|
+
if (excludeProjectHash) {
|
|
5017
|
+
filters.push(`sourceProjectHash != '${excludeProjectHash}'`);
|
|
5018
|
+
}
|
|
5019
|
+
if (entryType) {
|
|
5020
|
+
filters.push(`entryType = '${entryType}'`);
|
|
5021
|
+
}
|
|
5022
|
+
if (filters.length > 0) {
|
|
5023
|
+
query = query.where(filters.join(" AND "));
|
|
5024
|
+
}
|
|
5025
|
+
const results = await query.toArray();
|
|
5026
|
+
return results.filter((r) => {
|
|
5027
|
+
const distance = r._distance || 0;
|
|
5028
|
+
const score = 1 - distance / 2;
|
|
5029
|
+
return score >= minScore;
|
|
5030
|
+
}).slice(0, limit).map((r) => {
|
|
5031
|
+
const distance = r._distance || 0;
|
|
5032
|
+
const score = 1 - distance / 2;
|
|
5033
|
+
return {
|
|
5034
|
+
id: r.id,
|
|
5035
|
+
entryId: r.entryId,
|
|
5036
|
+
content: r.content,
|
|
5037
|
+
score,
|
|
5038
|
+
entryType: r.entryType
|
|
5039
|
+
};
|
|
5040
|
+
});
|
|
5041
|
+
}
|
|
5042
|
+
/**
|
|
5043
|
+
* Delete vector by entry ID
|
|
5044
|
+
*/
|
|
5045
|
+
async delete(entryId) {
|
|
5046
|
+
if (!this.table)
|
|
5047
|
+
return;
|
|
5048
|
+
await this.table.delete(`entryId = '${entryId}'`);
|
|
5049
|
+
}
|
|
5050
|
+
/**
|
|
5051
|
+
* Get total count
|
|
5052
|
+
*/
|
|
5053
|
+
async count() {
|
|
5054
|
+
if (!this.table)
|
|
5055
|
+
return 0;
|
|
5056
|
+
return this.table.countRows();
|
|
5057
|
+
}
|
|
5058
|
+
/**
|
|
5059
|
+
* Check if vector exists for entry
|
|
5060
|
+
*/
|
|
5061
|
+
async exists(entryId) {
|
|
5062
|
+
if (!this.table)
|
|
5063
|
+
return false;
|
|
5064
|
+
try {
|
|
5065
|
+
const results = await this.table.search([]).where(`entryId = '${entryId}'`).limit(1).toArray();
|
|
5066
|
+
return results.length > 0;
|
|
5067
|
+
} catch {
|
|
5068
|
+
return false;
|
|
5069
|
+
}
|
|
5070
|
+
}
|
|
5071
|
+
};
|
|
5072
|
+
function createSharedVectorStore(dbPath) {
|
|
5073
|
+
return new SharedVectorStore(dbPath);
|
|
5074
|
+
}
|
|
5075
|
+
|
|
5076
|
+
// src/core/shared-promoter.ts
|
|
5077
|
+
import { randomUUID as randomUUID9 } from "crypto";
|
|
5078
|
+
var SharedPromoter = class {
|
|
5079
|
+
constructor(sharedStore, sharedVectorStore, embedder, config) {
|
|
5080
|
+
this.sharedStore = sharedStore;
|
|
5081
|
+
this.sharedVectorStore = sharedVectorStore;
|
|
5082
|
+
this.embedder = embedder;
|
|
5083
|
+
this.config = config;
|
|
5084
|
+
}
|
|
5085
|
+
/**
|
|
5086
|
+
* Check if an entry is eligible for promotion
|
|
5087
|
+
*/
|
|
5088
|
+
isEligibleForPromotion(entry) {
|
|
5089
|
+
if (entry.entryType !== "troubleshooting") {
|
|
5090
|
+
return false;
|
|
5091
|
+
}
|
|
5092
|
+
if (entry.stage !== "verified" && entry.stage !== "certified") {
|
|
5093
|
+
return false;
|
|
5094
|
+
}
|
|
5095
|
+
if (entry.status !== "active") {
|
|
5096
|
+
return false;
|
|
5097
|
+
}
|
|
5098
|
+
return true;
|
|
5099
|
+
}
|
|
5100
|
+
/**
|
|
5101
|
+
* Promote a verified troubleshooting entry to shared storage
|
|
5102
|
+
*/
|
|
5103
|
+
async promoteEntry(entry, projectHash) {
|
|
5104
|
+
if (!this.isEligibleForPromotion(entry)) {
|
|
5105
|
+
return {
|
|
5106
|
+
success: false,
|
|
5107
|
+
skipped: true,
|
|
5108
|
+
skipReason: `Entry not eligible: type=${entry.entryType}, stage=${entry.stage}, status=${entry.status}`
|
|
5109
|
+
};
|
|
5110
|
+
}
|
|
5111
|
+
const exists = await this.sharedStore.exists(projectHash, entry.entryId);
|
|
5112
|
+
if (exists) {
|
|
5113
|
+
return {
|
|
5114
|
+
success: true,
|
|
5115
|
+
skipped: true,
|
|
5116
|
+
skipReason: "Entry already exists in shared store"
|
|
5117
|
+
};
|
|
5118
|
+
}
|
|
5119
|
+
try {
|
|
5120
|
+
const content = entry.contentJson;
|
|
5121
|
+
const confidence = this.calculateConfidence(entry);
|
|
5122
|
+
const minConfidence = this.config?.minConfidenceForPromotion ?? 0.8;
|
|
5123
|
+
if (confidence < minConfidence) {
|
|
5124
|
+
return {
|
|
5125
|
+
success: false,
|
|
5126
|
+
skipped: true,
|
|
5127
|
+
skipReason: `Confidence ${confidence} below threshold ${minConfidence}`
|
|
5128
|
+
};
|
|
5129
|
+
}
|
|
5130
|
+
const input = {
|
|
5131
|
+
sourceProjectHash: projectHash,
|
|
5132
|
+
sourceEntryId: entry.entryId,
|
|
5133
|
+
title: entry.title,
|
|
5134
|
+
symptoms: content.symptoms || [],
|
|
5135
|
+
rootCause: content.rootCause || "",
|
|
5136
|
+
solution: content.solution || "",
|
|
5137
|
+
topics: this.extractTopics(entry),
|
|
5138
|
+
technologies: content.technologies || [],
|
|
5139
|
+
confidence
|
|
5140
|
+
};
|
|
5141
|
+
const entryId = await this.sharedStore.promoteEntry(input);
|
|
5142
|
+
const embeddingContent = this.createEmbeddingContent(input);
|
|
5143
|
+
const embedding = await this.embedder.embed(embeddingContent);
|
|
5144
|
+
await this.sharedVectorStore.upsert({
|
|
5145
|
+
id: randomUUID9(),
|
|
5146
|
+
entryId,
|
|
5147
|
+
entryType: "troubleshooting",
|
|
5148
|
+
content: embeddingContent,
|
|
5149
|
+
vector: embedding.vector,
|
|
5150
|
+
topics: input.topics,
|
|
5151
|
+
sourceProjectHash: projectHash
|
|
5152
|
+
});
|
|
5153
|
+
return {
|
|
5154
|
+
success: true,
|
|
5155
|
+
entryId
|
|
5156
|
+
};
|
|
5157
|
+
} catch (error) {
|
|
5158
|
+
return {
|
|
5159
|
+
success: false,
|
|
5160
|
+
error: error instanceof Error ? error.message : String(error)
|
|
5161
|
+
};
|
|
5162
|
+
}
|
|
5163
|
+
}
|
|
5164
|
+
/**
|
|
5165
|
+
* Batch promote multiple entries
|
|
5166
|
+
*/
|
|
5167
|
+
async promoteEntries(entries, projectHash) {
|
|
5168
|
+
const results = /* @__PURE__ */ new Map();
|
|
5169
|
+
for (const entry of entries) {
|
|
5170
|
+
const result = await this.promoteEntry(entry, projectHash);
|
|
5171
|
+
results.set(entry.entryId, result);
|
|
5172
|
+
}
|
|
5173
|
+
return results;
|
|
5174
|
+
}
|
|
5175
|
+
/**
|
|
5176
|
+
* Extract topics from entry
|
|
5177
|
+
*/
|
|
5178
|
+
extractTopics(entry) {
|
|
5179
|
+
const topics = [];
|
|
5180
|
+
const titleWords = entry.title.toLowerCase().split(/[\s\-_]+/).filter((w) => w.length > 3 && !this.isStopWord(w));
|
|
5181
|
+
topics.push(...titleWords);
|
|
5182
|
+
const content = entry.contentJson;
|
|
5183
|
+
if (content.topics && Array.isArray(content.topics)) {
|
|
5184
|
+
topics.push(...content.topics.map((t) => String(t).toLowerCase()));
|
|
5185
|
+
}
|
|
5186
|
+
if (content.technologies && Array.isArray(content.technologies)) {
|
|
5187
|
+
topics.push(...content.technologies.map((t) => String(t).toLowerCase()));
|
|
5188
|
+
}
|
|
5189
|
+
return [...new Set(topics)];
|
|
5190
|
+
}
|
|
5191
|
+
/**
|
|
5192
|
+
* Check if word is a stop word
|
|
5193
|
+
*/
|
|
5194
|
+
isStopWord(word) {
|
|
5195
|
+
const stopWords = /* @__PURE__ */ new Set([
|
|
5196
|
+
"the",
|
|
5197
|
+
"and",
|
|
5198
|
+
"for",
|
|
5199
|
+
"with",
|
|
5200
|
+
"this",
|
|
5201
|
+
"that",
|
|
5202
|
+
"from",
|
|
5203
|
+
"have",
|
|
5204
|
+
"been",
|
|
5205
|
+
"were",
|
|
5206
|
+
"are",
|
|
5207
|
+
"was",
|
|
5208
|
+
"had",
|
|
5209
|
+
"has",
|
|
5210
|
+
"will",
|
|
5211
|
+
"would",
|
|
5212
|
+
"could",
|
|
5213
|
+
"should",
|
|
5214
|
+
"when",
|
|
5215
|
+
"where",
|
|
5216
|
+
"what",
|
|
5217
|
+
"which",
|
|
5218
|
+
"while",
|
|
5219
|
+
"error",
|
|
5220
|
+
"problem",
|
|
5221
|
+
"issue"
|
|
5222
|
+
]);
|
|
5223
|
+
return stopWords.has(word);
|
|
5224
|
+
}
|
|
5225
|
+
/**
|
|
5226
|
+
* Calculate confidence score for entry
|
|
5227
|
+
*/
|
|
5228
|
+
calculateConfidence(entry) {
|
|
5229
|
+
let confidence = 0.8;
|
|
5230
|
+
if (entry.stage === "certified") {
|
|
5231
|
+
confidence = 0.95;
|
|
5232
|
+
}
|
|
5233
|
+
return Math.min(confidence, 1);
|
|
5234
|
+
}
|
|
5235
|
+
/**
|
|
5236
|
+
* Create embedding content from input
|
|
5237
|
+
*/
|
|
5238
|
+
createEmbeddingContent(input) {
|
|
5239
|
+
const parts = [];
|
|
5240
|
+
parts.push(`Problem: ${input.title}`);
|
|
5241
|
+
if (input.symptoms.length > 0) {
|
|
5242
|
+
parts.push(`Symptoms: ${input.symptoms.join(", ")}`);
|
|
5243
|
+
}
|
|
5244
|
+
if (input.rootCause) {
|
|
5245
|
+
parts.push(`Root Cause: ${input.rootCause}`);
|
|
5246
|
+
}
|
|
5247
|
+
if (input.solution) {
|
|
5248
|
+
parts.push(`Solution: ${input.solution}`);
|
|
5249
|
+
}
|
|
5250
|
+
if (input.technologies && input.technologies.length > 0) {
|
|
5251
|
+
parts.push(`Technologies: ${input.technologies.join(", ")}`);
|
|
5252
|
+
}
|
|
5253
|
+
return parts.join("\n");
|
|
5254
|
+
}
|
|
5255
|
+
};
|
|
5256
|
+
function createSharedPromoter(sharedStore, sharedVectorStore, embedder, config) {
|
|
5257
|
+
return new SharedPromoter(sharedStore, sharedVectorStore, embedder, config);
|
|
5258
|
+
}
|
|
4491
5259
|
export {
|
|
4492
5260
|
AlignedEvidenceSchema,
|
|
4493
5261
|
BlockerKindSchema,
|
|
@@ -4545,6 +5313,13 @@ export {
|
|
|
4545
5313
|
Retriever,
|
|
4546
5314
|
SearchIndexItemSchema,
|
|
4547
5315
|
SessionSchema,
|
|
5316
|
+
SharedEntryTypeSchema,
|
|
5317
|
+
SharedEventStore,
|
|
5318
|
+
SharedPromoter,
|
|
5319
|
+
SharedStore,
|
|
5320
|
+
SharedStoreConfigSchema,
|
|
5321
|
+
SharedTroubleshootingEntrySchema,
|
|
5322
|
+
SharedVectorStore,
|
|
4548
5323
|
TaskBlockersSetPayloadSchema,
|
|
4549
5324
|
TaskCreatedPayloadSchema,
|
|
4550
5325
|
TaskCurrentJsonSchema,
|
|
@@ -4567,6 +5342,10 @@ export {
|
|
|
4567
5342
|
WorkingSetItemSchema,
|
|
4568
5343
|
createGraduationPipeline,
|
|
4569
5344
|
createRetriever,
|
|
5345
|
+
createSharedEventStore,
|
|
5346
|
+
createSharedPromoter,
|
|
5347
|
+
createSharedStore,
|
|
5348
|
+
createSharedVectorStore,
|
|
4570
5349
|
createVectorWorker,
|
|
4571
5350
|
createVectorWorkerV2,
|
|
4572
5351
|
getDefaultAligner,
|