ghagga-core 2.8.0 → 2.9.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.
- package/dist/acp/adapter.d.ts +91 -0
- package/dist/acp/adapter.d.ts.map +1 -0
- package/dist/acp/adapter.js +315 -0
- package/dist/acp/adapter.js.map +1 -0
- package/dist/acp/index.d.ts +4 -0
- package/dist/acp/index.d.ts.map +1 -0
- package/dist/acp/index.js +2 -0
- package/dist/acp/index.js.map +1 -0
- package/dist/acp/types.d.ts +142 -0
- package/dist/acp/types.d.ts.map +1 -0
- package/dist/acp/types.js +13 -0
- package/dist/acp/types.js.map +1 -0
- package/dist/adversarial-qa.d.ts +60 -0
- package/dist/adversarial-qa.d.ts.map +1 -0
- package/dist/adversarial-qa.js +85 -0
- package/dist/adversarial-qa.js.map +1 -0
- package/dist/agents/audit.d.ts +18 -0
- package/dist/agents/audit.d.ts.map +1 -0
- package/dist/agents/audit.js +78 -0
- package/dist/agents/audit.js.map +1 -0
- package/dist/agents/consensus.d.ts +1 -1
- package/dist/agents/consensus.d.ts.map +1 -1
- package/dist/agents/consensus.js +10 -8
- package/dist/agents/consensus.js.map +1 -1
- package/dist/agents/diagnostic.d.ts.map +1 -1
- package/dist/agents/diagnostic.js +22 -20
- package/dist/agents/diagnostic.js.map +1 -1
- package/dist/agents/fan-out-lenses.d.ts +41 -0
- package/dist/agents/fan-out-lenses.d.ts.map +1 -1
- package/dist/agents/fan-out-lenses.js +117 -3
- package/dist/agents/fan-out-lenses.js.map +1 -1
- package/dist/agents/prompts.d.ts +12 -0
- package/dist/agents/prompts.d.ts.map +1 -1
- package/dist/agents/prompts.js +31 -0
- package/dist/agents/prompts.js.map +1 -1
- package/dist/agents/simple.d.ts +1 -1
- package/dist/agents/simple.d.ts.map +1 -1
- package/dist/agents/simple.js +10 -6
- package/dist/agents/simple.js.map +1 -1
- package/dist/agents/workflow.d.ts +1 -1
- package/dist/agents/workflow.d.ts.map +1 -1
- package/dist/agents/workflow.js +9 -8
- package/dist/agents/workflow.js.map +1 -1
- package/dist/aisvs.d.ts +44 -0
- package/dist/aisvs.d.ts.map +1 -0
- package/dist/aisvs.js +189 -0
- package/dist/aisvs.js.map +1 -0
- package/dist/checklist/context.d.ts.map +1 -1
- package/dist/checklist/context.js +2 -8
- package/dist/checklist/context.js.map +1 -1
- package/dist/checklist/defaults.d.ts.map +1 -1
- package/dist/checklist/defaults.js.map +1 -1
- package/dist/checklist/scorer.d.ts.map +1 -1
- package/dist/checklist/scorer.js +105 -12
- package/dist/checklist/scorer.js.map +1 -1
- package/dist/code-intel/client.d.ts +30 -0
- package/dist/code-intel/client.d.ts.map +1 -0
- package/dist/code-intel/client.js +91 -0
- package/dist/code-intel/client.js.map +1 -0
- package/dist/code-intel/context.d.ts +21 -0
- package/dist/code-intel/context.d.ts.map +1 -0
- package/dist/code-intel/context.js +72 -0
- package/dist/code-intel/context.js.map +1 -0
- package/dist/code-intel/index.d.ts +10 -0
- package/dist/code-intel/index.d.ts.map +1 -0
- package/dist/code-intel/index.js +11 -0
- package/dist/code-intel/index.js.map +1 -0
- package/dist/code-intel/types.d.ts +63 -0
- package/dist/code-intel/types.d.ts.map +1 -0
- package/dist/code-intel/types.js +9 -0
- package/dist/code-intel/types.js.map +1 -0
- package/dist/compress/index.d.ts +55 -0
- package/dist/compress/index.d.ts.map +1 -0
- package/dist/compress/index.js +166 -0
- package/dist/compress/index.js.map +1 -0
- package/dist/cost-footer.d.ts +38 -0
- package/dist/cost-footer.d.ts.map +1 -0
- package/dist/cost-footer.js +95 -0
- package/dist/cost-footer.js.map +1 -0
- package/dist/critique/critique.d.ts +40 -0
- package/dist/critique/critique.d.ts.map +1 -0
- package/dist/critique/critique.js +194 -0
- package/dist/critique/critique.js.map +1 -0
- package/dist/critique/cross-model.d.ts +123 -0
- package/dist/critique/cross-model.d.ts.map +1 -0
- package/dist/critique/cross-model.js +267 -0
- package/dist/critique/cross-model.js.map +1 -0
- package/dist/critique/index.d.ts +8 -0
- package/dist/critique/index.d.ts.map +1 -0
- package/dist/critique/index.js +6 -0
- package/dist/critique/index.js.map +1 -0
- package/dist/critique/prompts.d.ts +11 -0
- package/dist/critique/prompts.d.ts.map +1 -0
- package/dist/critique/prompts.js +66 -0
- package/dist/critique/prompts.js.map +1 -0
- package/dist/critique/types.d.ts +84 -0
- package/dist/critique/types.d.ts.map +1 -0
- package/dist/critique/types.js +13 -0
- package/dist/critique/types.js.map +1 -0
- package/dist/doc-validation/index.d.ts +9 -0
- package/dist/doc-validation/index.d.ts.map +1 -0
- package/dist/doc-validation/index.js +9 -0
- package/dist/doc-validation/index.js.map +1 -0
- package/dist/doc-validation/scanner.d.ts +40 -0
- package/dist/doc-validation/scanner.d.ts.map +1 -0
- package/dist/doc-validation/scanner.js +163 -0
- package/dist/doc-validation/scanner.js.map +1 -0
- package/dist/doc-validation/types.d.ts +27 -0
- package/dist/doc-validation/types.d.ts.map +1 -0
- package/dist/doc-validation/types.js +8 -0
- package/dist/doc-validation/types.js.map +1 -0
- package/dist/embed.d.ts +27 -0
- package/dist/embed.d.ts.map +1 -0
- package/dist/embed.js +47 -0
- package/dist/embed.js.map +1 -0
- package/dist/enhance/enhance.d.ts.map +1 -1
- package/dist/enhance/enhance.js +7 -25
- package/dist/enhance/enhance.js.map +1 -1
- package/dist/enhance/types.d.ts +5 -0
- package/dist/enhance/types.d.ts.map +1 -1
- package/dist/exploitability/analyzer.d.ts +42 -0
- package/dist/exploitability/analyzer.d.ts.map +1 -1
- package/dist/exploitability/analyzer.js +225 -0
- package/dist/exploitability/analyzer.js.map +1 -1
- package/dist/exploitability/index.d.ts +3 -2
- package/dist/exploitability/index.d.ts.map +1 -1
- package/dist/exploitability/index.js +1 -2
- package/dist/exploitability/index.js.map +1 -1
- package/dist/exploitability/types.d.ts +27 -0
- package/dist/exploitability/types.d.ts.map +1 -1
- package/dist/fetch-fix.d.ts +60 -0
- package/dist/fetch-fix.d.ts.map +1 -0
- package/dist/fetch-fix.js +137 -0
- package/dist/fetch-fix.js.map +1 -0
- package/dist/flood/index.d.ts +34 -0
- package/dist/flood/index.d.ts.map +1 -0
- package/dist/flood/index.js +67 -0
- package/dist/flood/index.js.map +1 -0
- package/dist/format.d.ts.map +1 -1
- package/dist/format.js +6 -1
- package/dist/format.js.map +1 -1
- package/dist/graph/blast-radius.js +2 -2
- package/dist/graph/blast-radius.js.map +1 -1
- package/dist/graph/call-chain.d.ts +36 -0
- package/dist/graph/call-chain.d.ts.map +1 -0
- package/dist/graph/call-chain.js +291 -0
- package/dist/graph/call-chain.js.map +1 -0
- package/dist/graph/index.d.ts +4 -0
- package/dist/graph/index.d.ts.map +1 -1
- package/dist/graph/index.js +2 -0
- package/dist/graph/index.js.map +1 -1
- package/dist/graph/reverse-deps.d.ts +37 -0
- package/dist/graph/reverse-deps.d.ts.map +1 -0
- package/dist/graph/reverse-deps.js +136 -0
- package/dist/graph/reverse-deps.js.map +1 -0
- package/dist/index.d.ts +50 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +34 -7
- package/dist/index.js.map +1 -1
- package/dist/injection-corpus.d.ts +41 -0
- package/dist/injection-corpus.d.ts.map +1 -0
- package/dist/injection-corpus.js +181 -0
- package/dist/injection-corpus.js.map +1 -0
- package/dist/latent-comms.d.ts +31 -0
- package/dist/latent-comms.d.ts.map +1 -0
- package/dist/latent-comms.js +139 -0
- package/dist/latent-comms.js.map +1 -0
- package/dist/memory/pageindex/chunker.d.ts +33 -0
- package/dist/memory/pageindex/chunker.d.ts.map +1 -0
- package/dist/memory/pageindex/chunker.js +112 -0
- package/dist/memory/pageindex/chunker.js.map +1 -0
- package/dist/memory/pageindex/example.d.ts +22 -0
- package/dist/memory/pageindex/example.d.ts.map +1 -0
- package/dist/memory/pageindex/example.js +94 -0
- package/dist/memory/pageindex/example.js.map +1 -0
- package/dist/memory/pageindex/index.d.ts +15 -0
- package/dist/memory/pageindex/index.d.ts.map +1 -0
- package/dist/memory/pageindex/index.js +17 -0
- package/dist/memory/pageindex/index.js.map +1 -0
- package/dist/memory/pageindex/service.d.ts +53 -0
- package/dist/memory/pageindex/service.d.ts.map +1 -0
- package/dist/memory/pageindex/service.js +229 -0
- package/dist/memory/pageindex/service.js.map +1 -0
- package/dist/memory/pageindex/types.d.ts +67 -0
- package/dist/memory/pageindex/types.d.ts.map +1 -0
- package/dist/memory/pageindex/types.js +14 -0
- package/dist/memory/pageindex/types.js.map +1 -0
- package/dist/memory/persist.d.ts.map +1 -1
- package/dist/memory/persist.js +6 -2
- package/dist/memory/persist.js.map +1 -1
- package/dist/memory/sqlite.d.ts +69 -2
- package/dist/memory/sqlite.d.ts.map +1 -1
- package/dist/memory/sqlite.js +312 -5
- package/dist/memory/sqlite.js.map +1 -1
- package/dist/memory/taxonomy.d.ts +34 -0
- package/dist/memory/taxonomy.d.ts.map +1 -0
- package/dist/memory/taxonomy.js +189 -0
- package/dist/memory/taxonomy.js.map +1 -0
- package/dist/memory/versioning.d.ts.map +1 -1
- package/dist/memory/versioning.js.map +1 -1
- package/dist/negative.d.ts +23 -0
- package/dist/negative.d.ts.map +1 -0
- package/dist/negative.js +40 -0
- package/dist/negative.js.map +1 -0
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +455 -46
- package/dist/pipeline.js.map +1 -1
- package/dist/prompt-intel.d.ts +39 -0
- package/dist/prompt-intel.d.ts.map +1 -0
- package/dist/prompt-intel.js +148 -0
- package/dist/prompt-intel.js.map +1 -0
- package/dist/providers/cli-bridge.d.ts +4 -0
- package/dist/providers/cli-bridge.d.ts.map +1 -1
- package/dist/providers/cli-bridge.js +4 -0
- package/dist/providers/cli-bridge.js.map +1 -1
- package/dist/providers/generate-fn.d.ts +3 -15
- package/dist/providers/generate-fn.d.ts.map +1 -1
- package/dist/providers/generate-fn.js +3 -30
- package/dist/providers/generate-fn.js.map +1 -1
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/ollama.d.ts +15 -0
- package/dist/providers/ollama.d.ts.map +1 -0
- package/dist/providers/ollama.js +30 -0
- package/dist/providers/ollama.js.map +1 -0
- package/dist/ranking/index.d.ts +9 -0
- package/dist/ranking/index.d.ts.map +1 -0
- package/dist/ranking/index.js +82 -0
- package/dist/ranking/index.js.map +1 -0
- package/dist/recursive/circuit-breaker.d.ts +36 -0
- package/dist/recursive/circuit-breaker.d.ts.map +1 -0
- package/dist/recursive/circuit-breaker.js +62 -0
- package/dist/recursive/circuit-breaker.js.map +1 -0
- package/dist/recursive/index.d.ts +4 -0
- package/dist/recursive/index.d.ts.map +1 -1
- package/dist/recursive/index.js +18 -1
- package/dist/recursive/index.js.map +1 -1
- package/dist/recursive/types.d.ts +2 -0
- package/dist/recursive/types.d.ts.map +1 -1
- package/dist/recursive/types.js +1 -0
- package/dist/recursive/types.js.map +1 -1
- package/dist/scope/diff-mapper.js.map +1 -1
- package/dist/scope/entity-diff.d.ts +58 -0
- package/dist/scope/entity-diff.d.ts.map +1 -0
- package/dist/scope/entity-diff.js +224 -0
- package/dist/scope/entity-diff.js.map +1 -0
- package/dist/scope/extractor.d.ts.map +1 -1
- package/dist/scope/extractor.js.map +1 -1
- package/dist/scope/index.d.ts +3 -1
- package/dist/scope/index.d.ts.map +1 -1
- package/dist/scope/index.js +3 -0
- package/dist/scope/index.js.map +1 -1
- package/dist/scope/parser.d.ts +1 -1
- package/dist/scope/parser.d.ts.map +1 -1
- package/dist/scope/parser.js.map +1 -1
- package/dist/scope/types.d.ts +32 -0
- package/dist/scope/types.d.ts.map +1 -1
- package/dist/scope/types.js +7 -1
- package/dist/scope/types.js.map +1 -1
- package/dist/search/index.d.ts +11 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +10 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/indexer.d.ts +67 -0
- package/dist/search/indexer.d.ts.map +1 -0
- package/dist/search/indexer.js +196 -0
- package/dist/search/indexer.js.map +1 -0
- package/dist/search/searcher.d.ts +34 -0
- package/dist/search/searcher.d.ts.map +1 -0
- package/dist/search/searcher.js +101 -0
- package/dist/search/searcher.js.map +1 -0
- package/dist/search/types.d.ts +81 -0
- package/dist/search/types.d.ts.map +1 -0
- package/dist/search/types.js +8 -0
- package/dist/search/types.js.map +1 -0
- package/dist/self-improve/index.d.ts +53 -0
- package/dist/self-improve/index.d.ts.map +1 -0
- package/dist/self-improve/index.js +136 -0
- package/dist/self-improve/index.js.map +1 -0
- package/dist/semantic-diff/index.d.ts +31 -0
- package/dist/semantic-diff/index.d.ts.map +1 -0
- package/dist/semantic-diff/index.js +215 -0
- package/dist/semantic-diff/index.js.map +1 -0
- package/dist/testing/index.d.ts +67 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +76 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/vitest-helpers.d.ts +26 -0
- package/dist/testing/vitest-helpers.d.ts.map +1 -0
- package/dist/testing/vitest-helpers.js +37 -0
- package/dist/testing/vitest-helpers.js.map +1 -0
- package/dist/tools/index.d.ts +3 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +2 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/remote-query.d.ts +40 -0
- package/dist/tools/remote-query.d.ts.map +1 -0
- package/dist/tools/remote-query.js +71 -0
- package/dist/tools/remote-query.js.map +1 -0
- package/dist/tracing/index.d.ts +39 -0
- package/dist/tracing/index.d.ts.map +1 -0
- package/dist/tracing/index.js +70 -0
- package/dist/tracing/index.js.map +1 -0
- package/dist/trajectory.d.ts +65 -0
- package/dist/trajectory.d.ts.map +1 -0
- package/dist/trajectory.js +126 -0
- package/dist/trajectory.js.map +1 -0
- package/dist/trust/index.d.ts +34 -0
- package/dist/trust/index.d.ts.map +1 -0
- package/dist/trust/index.js +78 -0
- package/dist/trust/index.js.map +1 -0
- package/dist/types.d.ts +144 -5
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +2 -11
- package/dist/types.js.map +1 -1
- package/package.json +1 -3
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Indexer — builds and maintains an inverted index for BM25 search.
|
|
3
|
+
*
|
|
4
|
+
* Tokenizes documents, builds an inverted index with positional data,
|
|
5
|
+
* and supports incremental indexing (add/remove documents).
|
|
6
|
+
* No external dependencies — pure TypeScript implementation.
|
|
7
|
+
*/
|
|
8
|
+
// ─── Tokenizer ─────────────────────────────────────────────────
|
|
9
|
+
/**
|
|
10
|
+
* Common English stop words to exclude from indexing.
|
|
11
|
+
* Keeps the index lean and improves search relevance.
|
|
12
|
+
*/
|
|
13
|
+
export const STOP_WORDS = new Set([
|
|
14
|
+
'a', 'an', 'and', 'are', 'as', 'at', 'be', 'but', 'by',
|
|
15
|
+
'for', 'if', 'in', 'into', 'is', 'it', 'no', 'not', 'of',
|
|
16
|
+
'on', 'or', 'such', 'that', 'the', 'their', 'then', 'there',
|
|
17
|
+
'these', 'they', 'this', 'to', 'was', 'will', 'with',
|
|
18
|
+
]);
|
|
19
|
+
/** Minimum token length to index. */
|
|
20
|
+
const MIN_TOKEN_LENGTH = 2;
|
|
21
|
+
/**
|
|
22
|
+
* Tokenize text into lowercase terms, filtering stop words and short tokens.
|
|
23
|
+
*
|
|
24
|
+
* @param text - Raw text to tokenize.
|
|
25
|
+
* @returns Array of normalized tokens in order of appearance.
|
|
26
|
+
*/
|
|
27
|
+
export function tokenize(text) {
|
|
28
|
+
return text
|
|
29
|
+
.toLowerCase()
|
|
30
|
+
.replace(/[^a-z0-9_-]/g, ' ')
|
|
31
|
+
.split(/\s+/)
|
|
32
|
+
.filter((token) => token.length >= MIN_TOKEN_LENGTH && !STOP_WORDS.has(token));
|
|
33
|
+
}
|
|
34
|
+
// ─── SearchIndexer ─────────────────────────────────────────────
|
|
35
|
+
export class SearchIndexer {
|
|
36
|
+
/** Inverted index: term -> posting list with positions. */
|
|
37
|
+
terms = new Map();
|
|
38
|
+
/** Per-document term counts for BM25 length normalization. */
|
|
39
|
+
docLengths = new Map();
|
|
40
|
+
/** Stored documents for retrieval. */
|
|
41
|
+
documents = new Map();
|
|
42
|
+
/** Running sum of all document lengths for avgDocLength. */
|
|
43
|
+
totalTerms = 0;
|
|
44
|
+
// ── Public API ──────────────────────────────────────────────
|
|
45
|
+
/** Number of indexed documents. */
|
|
46
|
+
get totalDocs() {
|
|
47
|
+
return this.documents.size;
|
|
48
|
+
}
|
|
49
|
+
/** Average document length in terms. Returns 0 when empty. */
|
|
50
|
+
get avgDocLength() {
|
|
51
|
+
return this.documents.size === 0 ? 0 : this.totalTerms / this.documents.size;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Index a single document.
|
|
55
|
+
* If the document ID already exists, it is replaced (remove + add).
|
|
56
|
+
*/
|
|
57
|
+
addDocument(doc) {
|
|
58
|
+
// Replace if exists
|
|
59
|
+
if (this.documents.has(doc.id)) {
|
|
60
|
+
this.removeDocument(doc.id);
|
|
61
|
+
}
|
|
62
|
+
this.documents.set(doc.id, doc);
|
|
63
|
+
const tokens = tokenize(doc.content);
|
|
64
|
+
this.docLengths.set(doc.id, tokens.length);
|
|
65
|
+
this.totalTerms += tokens.length;
|
|
66
|
+
// Build term frequency and position maps for this document
|
|
67
|
+
const termPositions = new Map();
|
|
68
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
69
|
+
const token = tokens[i];
|
|
70
|
+
const positions = termPositions.get(token);
|
|
71
|
+
if (positions) {
|
|
72
|
+
positions.push(i);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
termPositions.set(token, [i]);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// Update the inverted index
|
|
79
|
+
for (const [term, positions] of termPositions) {
|
|
80
|
+
const posting = {
|
|
81
|
+
docId: doc.id,
|
|
82
|
+
tf: positions.length,
|
|
83
|
+
positions,
|
|
84
|
+
};
|
|
85
|
+
const existing = this.terms.get(term);
|
|
86
|
+
if (existing) {
|
|
87
|
+
existing.df += 1;
|
|
88
|
+
existing.postings.push(posting);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
this.terms.set(term, { df: 1, postings: [posting] });
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Index multiple documents at once.
|
|
97
|
+
*/
|
|
98
|
+
addDocuments(docs) {
|
|
99
|
+
for (const doc of docs) {
|
|
100
|
+
this.addDocument(doc);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Remove a document from the index by ID.
|
|
105
|
+
* Returns true if the document was found and removed.
|
|
106
|
+
*/
|
|
107
|
+
removeDocument(docId) {
|
|
108
|
+
const doc = this.documents.get(docId);
|
|
109
|
+
if (!doc)
|
|
110
|
+
return false;
|
|
111
|
+
const docLength = this.docLengths.get(docId) ?? 0;
|
|
112
|
+
this.totalTerms -= docLength;
|
|
113
|
+
this.docLengths.delete(docId);
|
|
114
|
+
this.documents.delete(docId);
|
|
115
|
+
// Remove postings for this document from every term
|
|
116
|
+
for (const [term, stats] of this.terms) {
|
|
117
|
+
const idx = stats.postings.findIndex((p) => p.docId === docId);
|
|
118
|
+
if (idx !== -1) {
|
|
119
|
+
stats.postings.splice(idx, 1);
|
|
120
|
+
stats.df -= 1;
|
|
121
|
+
// Clean up empty term entries
|
|
122
|
+
if (stats.postings.length === 0) {
|
|
123
|
+
this.terms.delete(term);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return true;
|
|
128
|
+
}
|
|
129
|
+
/** Check if a document ID is already indexed. */
|
|
130
|
+
hasDocument(docId) {
|
|
131
|
+
return this.documents.has(docId);
|
|
132
|
+
}
|
|
133
|
+
/** Get a stored document by ID. */
|
|
134
|
+
getDocument(docId) {
|
|
135
|
+
return this.documents.get(docId);
|
|
136
|
+
}
|
|
137
|
+
// ── Snapshot (serialization) ─────────────────────────────────
|
|
138
|
+
/**
|
|
139
|
+
* Export the full index as a JSON-serializable snapshot.
|
|
140
|
+
* Use this for persistence (save to disk / SQLite / etc.).
|
|
141
|
+
*/
|
|
142
|
+
toSnapshot() {
|
|
143
|
+
const termsObj = {};
|
|
144
|
+
for (const [term, stats] of this.terms) {
|
|
145
|
+
termsObj[term] = stats;
|
|
146
|
+
}
|
|
147
|
+
const docLengthsObj = {};
|
|
148
|
+
for (const [docId, length] of this.docLengths) {
|
|
149
|
+
docLengthsObj[docId] = length;
|
|
150
|
+
}
|
|
151
|
+
const documentsObj = {};
|
|
152
|
+
for (const [docId, doc] of this.documents) {
|
|
153
|
+
documentsObj[docId] = doc;
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
version: 1,
|
|
157
|
+
totalDocs: this.documents.size,
|
|
158
|
+
avgDocLength: this.avgDocLength,
|
|
159
|
+
docLengths: docLengthsObj,
|
|
160
|
+
terms: termsObj,
|
|
161
|
+
documents: documentsObj,
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Restore the index from a snapshot.
|
|
166
|
+
* Clears any existing state before loading.
|
|
167
|
+
*/
|
|
168
|
+
static fromSnapshot(snapshot) {
|
|
169
|
+
const indexer = new SearchIndexer();
|
|
170
|
+
for (const [term, stats] of Object.entries(snapshot.terms)) {
|
|
171
|
+
indexer.terms.set(term, stats);
|
|
172
|
+
}
|
|
173
|
+
for (const [docId, length] of Object.entries(snapshot.docLengths)) {
|
|
174
|
+
indexer.docLengths.set(docId, length);
|
|
175
|
+
}
|
|
176
|
+
for (const [docId, doc] of Object.entries(snapshot.documents)) {
|
|
177
|
+
indexer.documents.set(docId, doc);
|
|
178
|
+
}
|
|
179
|
+
// Recompute totalTerms from docLengths
|
|
180
|
+
indexer.totalTerms = 0;
|
|
181
|
+
for (const length of indexer.docLengths.values()) {
|
|
182
|
+
indexer.totalTerms += length;
|
|
183
|
+
}
|
|
184
|
+
return indexer;
|
|
185
|
+
}
|
|
186
|
+
// ── Internal accessors (used by SearchEngine) ────────────────
|
|
187
|
+
/** Get term stats from the inverted index. */
|
|
188
|
+
getTermStats(term) {
|
|
189
|
+
return this.terms.get(term);
|
|
190
|
+
}
|
|
191
|
+
/** Get the term count for a specific document. */
|
|
192
|
+
getDocLength(docId) {
|
|
193
|
+
return this.docLengths.get(docId) ?? 0;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
//# sourceMappingURL=indexer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"indexer.js","sourceRoot":"","sources":["../../src/search/indexer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Engine — BM25 ranked retrieval over the inverted index.
|
|
3
|
+
*
|
|
4
|
+
* Implements the Okapi BM25 scoring function for ranking documents
|
|
5
|
+
* by relevance to a free-text query. No external dependencies.
|
|
6
|
+
*
|
|
7
|
+
* BM25 formula:
|
|
8
|
+
* score(D, Q) = SUM_i IDF(qi) * (tf(qi, D) * (k1 + 1)) / (tf(qi, D) + k1 * (1 - b + b * |D| / avgdl))
|
|
9
|
+
*
|
|
10
|
+
* Where:
|
|
11
|
+
* - IDF(qi) = ln((N - df(qi) + 0.5) / (df(qi) + 0.5) + 1)
|
|
12
|
+
* - tf(qi, D) = frequency of term qi in document D
|
|
13
|
+
* - |D| = document length in terms
|
|
14
|
+
* - avgdl = average document length
|
|
15
|
+
* - k1 = term frequency saturation (default 1.2)
|
|
16
|
+
* - b = document length normalization (default 0.75)
|
|
17
|
+
*/
|
|
18
|
+
import type { SearchIndexer } from './indexer.js';
|
|
19
|
+
import type { BM25Params, SearchOptions, SearchResult } from './types.js';
|
|
20
|
+
export declare class SearchEngine {
|
|
21
|
+
private indexer;
|
|
22
|
+
private k1;
|
|
23
|
+
private b;
|
|
24
|
+
constructor(indexer: SearchIndexer, params?: BM25Params);
|
|
25
|
+
/**
|
|
26
|
+
* Search the index with a free-text query using BM25 scoring.
|
|
27
|
+
*
|
|
28
|
+
* @param query - Natural language search query (e.g., "memory leak in connection pool").
|
|
29
|
+
* @param options - Search options (limit, source filter, min score).
|
|
30
|
+
* @returns Ranked array of search results, highest score first.
|
|
31
|
+
*/
|
|
32
|
+
search(query: string, options?: SearchOptions): SearchResult[];
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=searcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searcher.d.ts","sourceRoot":"","sources":["../../src/search/searcher.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAS1E,qBAAa,YAAY;IACvB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,EAAE,CAAS;IACnB,OAAO,CAAC,CAAC,CAAS;gBAEN,OAAO,EAAE,aAAa,EAAE,MAAM,GAAE,UAAe;IAM3D;;;;;;OAMG;IACH,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,YAAY,EAAE;CAsEnE"}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Search Engine — BM25 ranked retrieval over the inverted index.
|
|
3
|
+
*
|
|
4
|
+
* Implements the Okapi BM25 scoring function for ranking documents
|
|
5
|
+
* by relevance to a free-text query. No external dependencies.
|
|
6
|
+
*
|
|
7
|
+
* BM25 formula:
|
|
8
|
+
* score(D, Q) = SUM_i IDF(qi) * (tf(qi, D) * (k1 + 1)) / (tf(qi, D) + k1 * (1 - b + b * |D| / avgdl))
|
|
9
|
+
*
|
|
10
|
+
* Where:
|
|
11
|
+
* - IDF(qi) = ln((N - df(qi) + 0.5) / (df(qi) + 0.5) + 1)
|
|
12
|
+
* - tf(qi, D) = frequency of term qi in document D
|
|
13
|
+
* - |D| = document length in terms
|
|
14
|
+
* - avgdl = average document length
|
|
15
|
+
* - k1 = term frequency saturation (default 1.2)
|
|
16
|
+
* - b = document length normalization (default 0.75)
|
|
17
|
+
*/
|
|
18
|
+
import { tokenize } from './indexer.js';
|
|
19
|
+
// ─── Default BM25 Parameters ──────────────────────────────────
|
|
20
|
+
const DEFAULT_K1 = 1.2;
|
|
21
|
+
const DEFAULT_B = 0.75;
|
|
22
|
+
// ─── SearchEngine ──────────────────────────────────────────────
|
|
23
|
+
export class SearchEngine {
|
|
24
|
+
indexer;
|
|
25
|
+
k1;
|
|
26
|
+
b;
|
|
27
|
+
constructor(indexer, params = {}) {
|
|
28
|
+
this.indexer = indexer;
|
|
29
|
+
this.k1 = params.k1 ?? DEFAULT_K1;
|
|
30
|
+
this.b = params.b ?? DEFAULT_B;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Search the index with a free-text query using BM25 scoring.
|
|
34
|
+
*
|
|
35
|
+
* @param query - Natural language search query (e.g., "memory leak in connection pool").
|
|
36
|
+
* @param options - Search options (limit, source filter, min score).
|
|
37
|
+
* @returns Ranked array of search results, highest score first.
|
|
38
|
+
*/
|
|
39
|
+
search(query, options = {}) {
|
|
40
|
+
const { limit = 10, source, minScore = 0 } = options;
|
|
41
|
+
const queryTerms = tokenize(query);
|
|
42
|
+
if (queryTerms.length === 0)
|
|
43
|
+
return [];
|
|
44
|
+
const totalDocs = this.indexer.totalDocs;
|
|
45
|
+
if (totalDocs === 0)
|
|
46
|
+
return [];
|
|
47
|
+
const avgDocLength = this.indexer.avgDocLength;
|
|
48
|
+
// Collect scores for each document that matches at least one query term
|
|
49
|
+
const docScores = new Map();
|
|
50
|
+
for (const term of queryTerms) {
|
|
51
|
+
const termStats = this.indexer.getTermStats(term);
|
|
52
|
+
if (!termStats)
|
|
53
|
+
continue;
|
|
54
|
+
// IDF: inverse document frequency with BM25 smoothing
|
|
55
|
+
const idf = Math.log((totalDocs - termStats.df + 0.5) / (termStats.df + 0.5) + 1);
|
|
56
|
+
for (const posting of termStats.postings) {
|
|
57
|
+
const docLength = this.indexer.getDocLength(posting.docId);
|
|
58
|
+
// BM25 term score
|
|
59
|
+
const tfNorm = (posting.tf * (this.k1 + 1)) /
|
|
60
|
+
(posting.tf + this.k1 * (1 - this.b + this.b * (docLength / avgDocLength)));
|
|
61
|
+
const termScore = idf * tfNorm;
|
|
62
|
+
const existing = docScores.get(posting.docId);
|
|
63
|
+
if (existing) {
|
|
64
|
+
existing.score += termScore;
|
|
65
|
+
existing.matchedTerms.add(term);
|
|
66
|
+
}
|
|
67
|
+
else {
|
|
68
|
+
docScores.set(posting.docId, {
|
|
69
|
+
score: termScore,
|
|
70
|
+
matchedTerms: new Set([term]),
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
// Build results, applying filters
|
|
76
|
+
const results = [];
|
|
77
|
+
for (const [docId, { score, matchedTerms }] of docScores) {
|
|
78
|
+
if (score < minScore)
|
|
79
|
+
continue;
|
|
80
|
+
const document = this.indexer.getDocument(docId);
|
|
81
|
+
if (!document)
|
|
82
|
+
continue;
|
|
83
|
+
if (source && document.source !== source)
|
|
84
|
+
continue;
|
|
85
|
+
results.push({
|
|
86
|
+
document,
|
|
87
|
+
score,
|
|
88
|
+
matchedTerms: [...matchedTerms],
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// Sort by score descending, then by recency (newer first) as tiebreaker
|
|
92
|
+
results.sort((a, b) => {
|
|
93
|
+
const scoreDiff = b.score - a.score;
|
|
94
|
+
if (Math.abs(scoreDiff) > 0.001)
|
|
95
|
+
return scoreDiff;
|
|
96
|
+
return b.document.createdAt.localeCompare(a.document.createdAt);
|
|
97
|
+
});
|
|
98
|
+
return results.slice(0, limit);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
//# sourceMappingURL=searcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"searcher.js","sourceRoot":"","sources":["../../src/search/searcher.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Semantic Search Types
|
|
3
|
+
*
|
|
4
|
+
* Types for searching over historical review comments and PR descriptions.
|
|
5
|
+
* Uses BM25 text scoring — no external embedding dependencies required.
|
|
6
|
+
*/
|
|
7
|
+
/** A searchable document representing a review comment or PR description. */
|
|
8
|
+
export interface SearchDocument {
|
|
9
|
+
/** Unique identifier for this document. */
|
|
10
|
+
id: string;
|
|
11
|
+
/** The full text content to index and search. */
|
|
12
|
+
content: string;
|
|
13
|
+
/** Source of the document (review comment, PR description, etc.). */
|
|
14
|
+
source: SearchDocumentSource;
|
|
15
|
+
/** Associated PR number, if applicable. */
|
|
16
|
+
prNumber?: number;
|
|
17
|
+
/** File paths related to this document. */
|
|
18
|
+
filePaths?: string[];
|
|
19
|
+
/** ISO timestamp when the document was created. */
|
|
20
|
+
createdAt: string;
|
|
21
|
+
/** Optional metadata for filtering or display. */
|
|
22
|
+
metadata?: Record<string, unknown>;
|
|
23
|
+
}
|
|
24
|
+
export type SearchDocumentSource = 'review-comment' | 'pr-description' | 'review-summary';
|
|
25
|
+
/** A single term entry in the inverted index. */
|
|
26
|
+
export interface TermPosting {
|
|
27
|
+
/** Document ID. */
|
|
28
|
+
docId: string;
|
|
29
|
+
/** Term frequency in this document. */
|
|
30
|
+
tf: number;
|
|
31
|
+
/** Positions of the term in the document (for phrase queries). */
|
|
32
|
+
positions: number[];
|
|
33
|
+
}
|
|
34
|
+
/** Statistics for a single term across the corpus. */
|
|
35
|
+
export interface TermStats {
|
|
36
|
+
/** Number of documents containing this term (document frequency). */
|
|
37
|
+
df: number;
|
|
38
|
+
/** Postings list for this term. */
|
|
39
|
+
postings: TermPosting[];
|
|
40
|
+
}
|
|
41
|
+
/** Serializable snapshot of the inverted index. */
|
|
42
|
+
export interface IndexSnapshot {
|
|
43
|
+
/** Version tag for forward-compatible deserialization. */
|
|
44
|
+
version: 1;
|
|
45
|
+
/** Total number of indexed documents. */
|
|
46
|
+
totalDocs: number;
|
|
47
|
+
/** Average document length (in terms). */
|
|
48
|
+
avgDocLength: number;
|
|
49
|
+
/** Per-document term counts (docId -> number of terms). */
|
|
50
|
+
docLengths: Record<string, number>;
|
|
51
|
+
/** The inverted index: term -> stats. */
|
|
52
|
+
terms: Record<string, TermStats>;
|
|
53
|
+
/** Stored documents for retrieval. */
|
|
54
|
+
documents: Record<string, SearchDocument>;
|
|
55
|
+
}
|
|
56
|
+
/** A single search result with its relevance score. */
|
|
57
|
+
export interface SearchResult {
|
|
58
|
+
/** The matched document. */
|
|
59
|
+
document: SearchDocument;
|
|
60
|
+
/** BM25 relevance score (higher = more relevant). */
|
|
61
|
+
score: number;
|
|
62
|
+
/** Matched terms from the query that appeared in this document. */
|
|
63
|
+
matchedTerms: string[];
|
|
64
|
+
}
|
|
65
|
+
/** Options for configuring search behavior. */
|
|
66
|
+
export interface SearchOptions {
|
|
67
|
+
/** Maximum number of results to return. Defaults to 10. */
|
|
68
|
+
limit?: number;
|
|
69
|
+
/** Filter results by document source. */
|
|
70
|
+
source?: SearchDocumentSource;
|
|
71
|
+
/** Minimum BM25 score threshold. Results below this are excluded. Defaults to 0. */
|
|
72
|
+
minScore?: number;
|
|
73
|
+
}
|
|
74
|
+
/** BM25 tuning parameters. */
|
|
75
|
+
export interface BM25Params {
|
|
76
|
+
/** Term frequency saturation parameter. Higher values give more weight to term frequency. Defaults to 1.2. */
|
|
77
|
+
k1?: number;
|
|
78
|
+
/** Document length normalization parameter (0 = no normalization, 1 = full). Defaults to 0.75. */
|
|
79
|
+
b?: number;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/search/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAIH,6EAA6E;AAC7E,MAAM,WAAW,cAAc;IAC7B,2CAA2C;IAC3C,EAAE,EAAE,MAAM,CAAC;IAEX,iDAAiD;IACjD,OAAO,EAAE,MAAM,CAAC;IAEhB,qEAAqE;IACrE,MAAM,EAAE,oBAAoB,CAAC;IAE7B,2CAA2C;IAC3C,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,2CAA2C;IAC3C,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;IAErB,mDAAmD;IACnD,SAAS,EAAE,MAAM,CAAC;IAElB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACpC;AAED,MAAM,MAAM,oBAAoB,GAAG,gBAAgB,GAAG,gBAAgB,GAAG,gBAAgB,CAAC;AAI1F,iDAAiD;AACjD,MAAM,WAAW,WAAW;IAC1B,mBAAmB;IACnB,KAAK,EAAE,MAAM,CAAC;IAEd,uCAAuC;IACvC,EAAE,EAAE,MAAM,CAAC;IAEX,kEAAkE;IAClE,SAAS,EAAE,MAAM,EAAE,CAAC;CACrB;AAED,sDAAsD;AACtD,MAAM,WAAW,SAAS;IACxB,qEAAqE;IACrE,EAAE,EAAE,MAAM,CAAC;IAEX,mCAAmC;IACnC,QAAQ,EAAE,WAAW,EAAE,CAAC;CACzB;AAED,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC5B,0DAA0D;IAC1D,OAAO,EAAE,CAAC,CAAC;IAEX,yCAAyC;IACzC,SAAS,EAAE,MAAM,CAAC;IAElB,0CAA0C;IAC1C,YAAY,EAAE,MAAM,CAAC;IAErB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEnC,yCAAyC;IACzC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;IAEjC,sCAAsC;IACtC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;CAC3C;AAID,uDAAuD;AACvD,MAAM,WAAW,YAAY;IAC3B,4BAA4B;IAC5B,QAAQ,EAAE,cAAc,CAAC;IAEzB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IAEd,mEAAmE;IACnE,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB;AAED,+CAA+C;AAC/C,MAAM,WAAW,aAAa;IAC5B,2DAA2D;IAC3D,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf,yCAAyC;IACzC,MAAM,CAAC,EAAE,oBAAoB,CAAC;IAE9B,oFAAoF;IACpF,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,8BAA8B;AAC9B,MAAM,WAAW,UAAU;IACzB,8GAA8G;IAC9G,EAAE,CAAC,EAAE,MAAM,CAAC;IAEZ,kGAAkG;IAClG,CAAC,CAAC,EAAE,MAAM,CAAC;CACZ"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/search/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-Improving Review Loop
|
|
3
|
+
*
|
|
4
|
+
* Tracks which findings get accepted/rejected by users and uses that
|
|
5
|
+
* signal to derive improvement rules for future reviews.
|
|
6
|
+
*/
|
|
7
|
+
export type FindingOutcome = 'accepted' | 'rejected' | 'modified';
|
|
8
|
+
export interface FindingFeedback {
|
|
9
|
+
findingHash: string;
|
|
10
|
+
outcome: FindingOutcome;
|
|
11
|
+
category: string;
|
|
12
|
+
severity: string;
|
|
13
|
+
modelUsed: string;
|
|
14
|
+
recordedAt: string;
|
|
15
|
+
}
|
|
16
|
+
export interface ImprovementRule {
|
|
17
|
+
pattern: string;
|
|
18
|
+
category: string;
|
|
19
|
+
action: 'suppress' | 'boost_priority';
|
|
20
|
+
confidence: number;
|
|
21
|
+
sampleCount: number;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Append a feedback record to the JSONL file at storagePath.
|
|
25
|
+
* Creates the file (and parent directories) if they do not exist.
|
|
26
|
+
*/
|
|
27
|
+
export declare function recordFeedback(feedback: FindingFeedback, storagePath: string): Promise<void>;
|
|
28
|
+
/**
|
|
29
|
+
* Load all feedback records from a JSONL file.
|
|
30
|
+
* Returns an empty array if the file does not exist.
|
|
31
|
+
*/
|
|
32
|
+
export declare function loadFeedback(storagePath: string): Promise<FindingFeedback[]>;
|
|
33
|
+
/**
|
|
34
|
+
* Derive improvement rules from accumulated feedback.
|
|
35
|
+
*
|
|
36
|
+
* Logic per category:
|
|
37
|
+
* - rejection_rate > 0.7 AND sampleCount >= 5 → suppress
|
|
38
|
+
* - acceptance_rate > 0.8 AND sampleCount >= 5 → boost_priority
|
|
39
|
+
*
|
|
40
|
+
* Returns rules sorted by confidence descending.
|
|
41
|
+
*/
|
|
42
|
+
export declare function deriveRules(feedback: FindingFeedback[]): ImprovementRule[];
|
|
43
|
+
/**
|
|
44
|
+
* Format derived improvement rules as a prompt section.
|
|
45
|
+
*
|
|
46
|
+
* Injects rules into the system prompt so the agent adjusts its behavior
|
|
47
|
+
* based on historical feedback.
|
|
48
|
+
*
|
|
49
|
+
* @param rules - Derived rules from deriveRules()
|
|
50
|
+
* @returns Formatted string for injection into system prompt
|
|
51
|
+
*/
|
|
52
|
+
export declare function formatRulesForPrompt(rules: ImprovementRule[]): string;
|
|
53
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/self-improve/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAOH,MAAM,MAAM,cAAc,GAAG,UAAU,GAAG,UAAU,GAAG,UAAU,CAAC;AAElE,MAAM,WAAW,eAAe;IAC9B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,cAAc,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,UAAU,GAAG,gBAAgB,CAAC;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAID;;;GAGG;AACH,wBAAsB,cAAc,CAClC,QAAQ,EAAE,eAAe,EACzB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,IAAI,CAAC,CAQf;AAED;;;GAGG;AACH,wBAAsB,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAqBlF;AAQD;;;;;;;;GAQG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,eAAe,EAAE,GAAG,eAAe,EAAE,CA4C1E;AAID;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,eAAe,EAAE,GAAG,MAAM,CA4BrE"}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Self-Improving Review Loop
|
|
3
|
+
*
|
|
4
|
+
* Tracks which findings get accepted/rejected by users and uses that
|
|
5
|
+
* signal to derive improvement rules for future reviews.
|
|
6
|
+
*/
|
|
7
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
|
|
8
|
+
import { dirname } from 'node:path';
|
|
9
|
+
// ─── Storage ─────────────────────────────────────────────────────
|
|
10
|
+
/**
|
|
11
|
+
* Append a feedback record to the JSONL file at storagePath.
|
|
12
|
+
* Creates the file (and parent directories) if they do not exist.
|
|
13
|
+
*/
|
|
14
|
+
export async function recordFeedback(feedback, storagePath) {
|
|
15
|
+
const dir = dirname(storagePath);
|
|
16
|
+
if (!existsSync(dir)) {
|
|
17
|
+
mkdirSync(dir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
const line = `${JSON.stringify(feedback)}\n`;
|
|
20
|
+
appendFileSync(storagePath, line, 'utf-8');
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Load all feedback records from a JSONL file.
|
|
24
|
+
* Returns an empty array if the file does not exist.
|
|
25
|
+
*/
|
|
26
|
+
export async function loadFeedback(storagePath) {
|
|
27
|
+
if (!existsSync(storagePath)) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
const raw = readFileSync(storagePath, 'utf-8').trim();
|
|
31
|
+
if (!raw)
|
|
32
|
+
return [];
|
|
33
|
+
const records = [];
|
|
34
|
+
for (const line of raw.split('\n')) {
|
|
35
|
+
const trimmed = line.trim();
|
|
36
|
+
if (!trimmed)
|
|
37
|
+
continue;
|
|
38
|
+
try {
|
|
39
|
+
const parsed = JSON.parse(trimmed);
|
|
40
|
+
records.push(parsed);
|
|
41
|
+
}
|
|
42
|
+
catch {
|
|
43
|
+
// Skip malformed lines
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return records;
|
|
47
|
+
}
|
|
48
|
+
// ─── Rule Derivation ─────────────────────────────────────────────
|
|
49
|
+
const MIN_SAMPLE_COUNT = 5;
|
|
50
|
+
const SUPPRESS_REJECTION_THRESHOLD = 0.7;
|
|
51
|
+
const BOOST_ACCEPTANCE_THRESHOLD = 0.8;
|
|
52
|
+
/**
|
|
53
|
+
* Derive improvement rules from accumulated feedback.
|
|
54
|
+
*
|
|
55
|
+
* Logic per category:
|
|
56
|
+
* - rejection_rate > 0.7 AND sampleCount >= 5 → suppress
|
|
57
|
+
* - acceptance_rate > 0.8 AND sampleCount >= 5 → boost_priority
|
|
58
|
+
*
|
|
59
|
+
* Returns rules sorted by confidence descending.
|
|
60
|
+
*/
|
|
61
|
+
export function deriveRules(feedback) {
|
|
62
|
+
if (feedback.length === 0)
|
|
63
|
+
return [];
|
|
64
|
+
// Group by category
|
|
65
|
+
const byCategory = new Map();
|
|
66
|
+
for (const fb of feedback) {
|
|
67
|
+
const existing = byCategory.get(fb.category) ?? [];
|
|
68
|
+
existing.push(fb);
|
|
69
|
+
byCategory.set(fb.category, existing);
|
|
70
|
+
}
|
|
71
|
+
const rules = [];
|
|
72
|
+
for (const [category, records] of byCategory) {
|
|
73
|
+
const total = records.length;
|
|
74
|
+
if (total < MIN_SAMPLE_COUNT)
|
|
75
|
+
continue;
|
|
76
|
+
const rejected = records.filter((r) => r.outcome === 'rejected').length;
|
|
77
|
+
const accepted = records.filter((r) => r.outcome === 'accepted').length;
|
|
78
|
+
const rejectionRate = rejected / total;
|
|
79
|
+
const acceptanceRate = accepted / total;
|
|
80
|
+
if (rejectionRate > SUPPRESS_REJECTION_THRESHOLD) {
|
|
81
|
+
rules.push({
|
|
82
|
+
pattern: `category:${category}`,
|
|
83
|
+
category,
|
|
84
|
+
action: 'suppress',
|
|
85
|
+
confidence: Math.round(rejectionRate * 100) / 100,
|
|
86
|
+
sampleCount: total,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else if (acceptanceRate > BOOST_ACCEPTANCE_THRESHOLD) {
|
|
90
|
+
rules.push({
|
|
91
|
+
pattern: `category:${category}`,
|
|
92
|
+
category,
|
|
93
|
+
action: 'boost_priority',
|
|
94
|
+
confidence: Math.round(acceptanceRate * 100) / 100,
|
|
95
|
+
sampleCount: total,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
// Sort by confidence descending
|
|
100
|
+
return rules.sort((a, b) => b.confidence - a.confidence);
|
|
101
|
+
}
|
|
102
|
+
// ─── Prompt Formatting ────────────────────────────────────────────
|
|
103
|
+
/**
|
|
104
|
+
* Format derived improvement rules as a prompt section.
|
|
105
|
+
*
|
|
106
|
+
* Injects rules into the system prompt so the agent adjusts its behavior
|
|
107
|
+
* based on historical feedback.
|
|
108
|
+
*
|
|
109
|
+
* @param rules - Derived rules from deriveRules()
|
|
110
|
+
* @returns Formatted string for injection into system prompt
|
|
111
|
+
*/
|
|
112
|
+
export function formatRulesForPrompt(rules) {
|
|
113
|
+
if (rules.length === 0)
|
|
114
|
+
return '';
|
|
115
|
+
const lines = [
|
|
116
|
+
'## Review Improvement Rules',
|
|
117
|
+
'',
|
|
118
|
+
'Based on past feedback, apply these adjustments to your review:',
|
|
119
|
+
'',
|
|
120
|
+
];
|
|
121
|
+
for (const rule of rules) {
|
|
122
|
+
if (rule.action === 'suppress') {
|
|
123
|
+
lines.push(`- **SUPPRESS** findings in category \`${rule.category}\` — ` +
|
|
124
|
+
`${Math.round(rule.confidence * 100)}% rejection rate across ${rule.sampleCount} samples. ` +
|
|
125
|
+
`Only report if you have very strong evidence.`);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
lines.push(`- **PRIORITIZE** findings in category \`${rule.category}\` — ` +
|
|
129
|
+
`${Math.round(rule.confidence * 100)}% acceptance rate across ${rule.sampleCount} samples. ` +
|
|
130
|
+
`These findings are highly valued by the team.`);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
lines.push('');
|
|
134
|
+
return lines.join('\n');
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/self-improve/index.ts"],"names":[],"mappings":"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"}
|