@soleri/forge 4.2.2 → 5.0.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/domain-manager.d.ts +2 -2
- package/dist/domain-manager.js +35 -16
- package/dist/domain-manager.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/knowledge-installer.js +18 -12
- package/dist/knowledge-installer.js.map +1 -1
- package/dist/patching.d.ts +15 -6
- package/dist/patching.js +39 -12
- package/dist/patching.js.map +1 -1
- package/dist/scaffolder.js +18 -28
- package/dist/scaffolder.js.map +1 -1
- package/dist/templates/brain.d.ts +6 -0
- package/dist/templates/brain.js +478 -0
- package/dist/templates/brain.js.map +1 -0
- package/dist/templates/core-facade.js +95 -47
- package/dist/templates/core-facade.js.map +1 -1
- package/dist/templates/entry-point.d.ts +4 -0
- package/dist/templates/entry-point.js +146 -89
- package/dist/templates/entry-point.js.map +1 -1
- package/dist/templates/facade-factory.d.ts +1 -0
- package/dist/templates/facade-factory.js +63 -0
- package/dist/templates/facade-factory.js.map +1 -0
- package/dist/templates/facade-types.d.ts +1 -0
- package/dist/templates/facade-types.js +46 -0
- package/dist/templates/facade-types.js.map +1 -0
- package/dist/templates/intelligence-loader.d.ts +1 -0
- package/dist/templates/intelligence-loader.js +43 -0
- package/dist/templates/intelligence-loader.js.map +1 -0
- package/dist/templates/intelligence-types.d.ts +1 -0
- package/dist/templates/intelligence-types.js +24 -0
- package/dist/templates/intelligence-types.js.map +1 -0
- package/dist/templates/llm-key-pool.d.ts +7 -0
- package/dist/templates/llm-key-pool.js +211 -0
- package/dist/templates/llm-key-pool.js.map +1 -0
- package/dist/templates/llm-types.d.ts +5 -0
- package/dist/templates/llm-types.js +161 -0
- package/dist/templates/llm-types.js.map +1 -0
- package/dist/templates/llm-utils.d.ts +5 -0
- package/dist/templates/llm-utils.js +260 -0
- package/dist/templates/llm-utils.js.map +1 -0
- package/dist/templates/package-json.js +3 -1
- package/dist/templates/package-json.js.map +1 -1
- package/dist/templates/planner.d.ts +5 -0
- package/dist/templates/planner.js +150 -0
- package/dist/templates/planner.js.map +1 -0
- package/dist/templates/test-brain.d.ts +6 -0
- package/dist/templates/test-brain.js +474 -0
- package/dist/templates/test-brain.js.map +1 -0
- package/dist/templates/test-facades.d.ts +1 -1
- package/dist/templates/test-facades.js +170 -456
- package/dist/templates/test-facades.js.map +1 -1
- package/dist/templates/test-llm.d.ts +7 -0
- package/dist/templates/test-llm.js +574 -0
- package/dist/templates/test-llm.js.map +1 -0
- package/dist/templates/test-loader.d.ts +5 -0
- package/dist/templates/test-loader.js +146 -0
- package/dist/templates/test-loader.js.map +1 -0
- package/dist/templates/test-planner.d.ts +5 -0
- package/dist/templates/test-planner.js +271 -0
- package/dist/templates/test-planner.js.map +1 -0
- package/dist/templates/test-vault.d.ts +5 -0
- package/dist/templates/test-vault.js +380 -0
- package/dist/templates/test-vault.js.map +1 -0
- package/dist/templates/vault.d.ts +5 -0
- package/dist/templates/vault.js +263 -0
- package/dist/templates/vault.js.map +1 -0
- package/dist/types.d.ts +2 -2
- package/package.json +1 -1
- package/src/__tests__/scaffolder.test.ts +52 -109
- package/src/domain-manager.ts +34 -15
- package/src/knowledge-installer.ts +22 -15
- package/src/patching.ts +49 -11
- package/src/scaffolder.ts +18 -29
- package/src/templates/entry-point.ts +146 -91
- package/src/templates/package-json.ts +3 -1
- package/src/templates/test-facades.ts +170 -458
- package/src/templates/core-facade.ts +0 -517
- package/src/templates/llm-client.ts +0 -301
|
@@ -0,0 +1,478 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates the brain.ts source file for a new agent.
|
|
3
|
+
* The Brain provides a local, zero-dependency intelligence layer:
|
|
4
|
+
* TF-IDF scoring, auto-tagging, duplicate detection, and search feedback.
|
|
5
|
+
*/
|
|
6
|
+
export function generateBrain() {
|
|
7
|
+
return BRAIN_TEMPLATE;
|
|
8
|
+
}
|
|
9
|
+
const BRAIN_TEMPLATE = [
|
|
10
|
+
"import type { Vault } from '../vault/vault.js';",
|
|
11
|
+
"import type { SearchResult } from '../vault/vault.js';",
|
|
12
|
+
"import type { IntelligenceEntry } from '../intelligence/types.js';",
|
|
13
|
+
'',
|
|
14
|
+
'// ─── Types ───────────────────────────────────────────────────────────',
|
|
15
|
+
'',
|
|
16
|
+
'export interface ScoringWeights {',
|
|
17
|
+
' semantic: number;',
|
|
18
|
+
' severity: number;',
|
|
19
|
+
' recency: number;',
|
|
20
|
+
' tagOverlap: number;',
|
|
21
|
+
' domainMatch: number;',
|
|
22
|
+
'}',
|
|
23
|
+
'',
|
|
24
|
+
'export interface ScoreBreakdown {',
|
|
25
|
+
' semantic: number;',
|
|
26
|
+
' severity: number;',
|
|
27
|
+
' recency: number;',
|
|
28
|
+
' tagOverlap: number;',
|
|
29
|
+
' domainMatch: number;',
|
|
30
|
+
' total: number;',
|
|
31
|
+
'}',
|
|
32
|
+
'',
|
|
33
|
+
'export interface RankedResult {',
|
|
34
|
+
' entry: IntelligenceEntry;',
|
|
35
|
+
' score: number;',
|
|
36
|
+
' breakdown: ScoreBreakdown;',
|
|
37
|
+
'}',
|
|
38
|
+
'',
|
|
39
|
+
'export interface SearchOptions {',
|
|
40
|
+
' domain?: string;',
|
|
41
|
+
' type?: string;',
|
|
42
|
+
' severity?: string;',
|
|
43
|
+
' limit?: number;',
|
|
44
|
+
' tags?: string[];',
|
|
45
|
+
'}',
|
|
46
|
+
'',
|
|
47
|
+
'export interface CaptureResult {',
|
|
48
|
+
' captured: boolean;',
|
|
49
|
+
' id: string;',
|
|
50
|
+
' autoTags: string[];',
|
|
51
|
+
' duplicate?: { id: string; similarity: number };',
|
|
52
|
+
' blocked?: boolean;',
|
|
53
|
+
'}',
|
|
54
|
+
'',
|
|
55
|
+
'export interface BrainStats {',
|
|
56
|
+
' vocabularySize: number;',
|
|
57
|
+
' feedbackCount: number;',
|
|
58
|
+
' weights: ScoringWeights;',
|
|
59
|
+
'}',
|
|
60
|
+
'',
|
|
61
|
+
'export interface QueryContext {',
|
|
62
|
+
' query: string;',
|
|
63
|
+
' domain?: string;',
|
|
64
|
+
' tags?: string[];',
|
|
65
|
+
'}',
|
|
66
|
+
'',
|
|
67
|
+
'type SparseVector = Map<string, number>;',
|
|
68
|
+
'',
|
|
69
|
+
'// ─── Stopwords ─────────────────────────────────────────────────────',
|
|
70
|
+
'',
|
|
71
|
+
'const STOPWORDS = new Set([',
|
|
72
|
+
" 'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for',",
|
|
73
|
+
" 'of', 'with', 'by', 'from', 'as', 'is', 'was', 'are', 'were', 'been',",
|
|
74
|
+
" 'be', 'have', 'has', 'had', 'do', 'does', 'did', 'will', 'would', 'could',",
|
|
75
|
+
" 'should', 'may', 'might', 'shall', 'can', 'need', 'must', 'it', 'its',",
|
|
76
|
+
" 'this', 'that', 'these', 'those', 'i', 'you', 'he', 'she', 'we', 'they',",
|
|
77
|
+
" 'me', 'him', 'her', 'us', 'them', 'my', 'your', 'his', 'our', 'their',",
|
|
78
|
+
" 'what', 'which', 'who', 'whom', 'when', 'where', 'why', 'how', 'all',",
|
|
79
|
+
" 'each', 'every', 'both', 'few', 'more', 'most', 'other', 'some', 'such',",
|
|
80
|
+
" 'no', 'not', 'only', 'same', 'so', 'than', 'too', 'very', 'just',",
|
|
81
|
+
" 'because', 'if', 'then', 'else', 'about', 'up', 'out', 'into',",
|
|
82
|
+
']);',
|
|
83
|
+
'',
|
|
84
|
+
'// ─── Text Processing (pure functions) ────────────────────────────',
|
|
85
|
+
'',
|
|
86
|
+
'function tokenize(text: string): string[] {',
|
|
87
|
+
' return text',
|
|
88
|
+
' .toLowerCase()',
|
|
89
|
+
" .replace(/[^a-z0-9\\s-]/g, ' ')",
|
|
90
|
+
' .split(/\\s+/)',
|
|
91
|
+
' .filter((t) => t.length > 2 && !STOPWORDS.has(t));',
|
|
92
|
+
'}',
|
|
93
|
+
'',
|
|
94
|
+
'function calculateTf(tokens: string[]): SparseVector {',
|
|
95
|
+
' const tf: SparseVector = new Map();',
|
|
96
|
+
' for (const token of tokens) {',
|
|
97
|
+
' tf.set(token, (tf.get(token) ?? 0) + 1);',
|
|
98
|
+
' }',
|
|
99
|
+
' // Normalize by document length',
|
|
100
|
+
' const len = tokens.length || 1;',
|
|
101
|
+
' for (const [term, count] of tf) {',
|
|
102
|
+
' tf.set(term, count / len);',
|
|
103
|
+
' }',
|
|
104
|
+
' return tf;',
|
|
105
|
+
'}',
|
|
106
|
+
'',
|
|
107
|
+
'function calculateTfIdf(tokens: string[], vocabulary: Map<string, number>): SparseVector {',
|
|
108
|
+
' const tf = calculateTf(tokens);',
|
|
109
|
+
' const tfidf: SparseVector = new Map();',
|
|
110
|
+
' for (const [term, tfValue] of tf) {',
|
|
111
|
+
' const idf = vocabulary.get(term) ?? 0;',
|
|
112
|
+
' if (idf > 0) {',
|
|
113
|
+
' tfidf.set(term, tfValue * idf);',
|
|
114
|
+
' }',
|
|
115
|
+
' }',
|
|
116
|
+
' return tfidf;',
|
|
117
|
+
'}',
|
|
118
|
+
'',
|
|
119
|
+
'function cosineSimilarity(a: SparseVector, b: SparseVector): number {',
|
|
120
|
+
' let dot = 0;',
|
|
121
|
+
' let normA = 0;',
|
|
122
|
+
' let normB = 0;',
|
|
123
|
+
' for (const [term, valA] of a) {',
|
|
124
|
+
' normA += valA * valA;',
|
|
125
|
+
' const valB = b.get(term);',
|
|
126
|
+
' if (valB !== undefined) {',
|
|
127
|
+
' dot += valA * valB;',
|
|
128
|
+
' }',
|
|
129
|
+
' }',
|
|
130
|
+
' for (const [, valB] of b) {',
|
|
131
|
+
' normB += valB * valB;',
|
|
132
|
+
' }',
|
|
133
|
+
' const denom = Math.sqrt(normA) * Math.sqrt(normB);',
|
|
134
|
+
' return denom === 0 ? 0 : dot / denom;',
|
|
135
|
+
'}',
|
|
136
|
+
'',
|
|
137
|
+
'function jaccardSimilarity(a: string[], b: string[]): number {',
|
|
138
|
+
' if (a.length === 0 && b.length === 0) return 0;',
|
|
139
|
+
' const setA = new Set(a);',
|
|
140
|
+
' const setB = new Set(b);',
|
|
141
|
+
' let intersection = 0;',
|
|
142
|
+
' for (const item of setA) {',
|
|
143
|
+
' if (setB.has(item)) intersection++;',
|
|
144
|
+
' }',
|
|
145
|
+
' const union = new Set([...a, ...b]).size;',
|
|
146
|
+
' return union === 0 ? 0 : intersection / union;',
|
|
147
|
+
'}',
|
|
148
|
+
'',
|
|
149
|
+
'// ─── Severity scoring ──────────────────────────────────────────────',
|
|
150
|
+
'',
|
|
151
|
+
'const SEVERITY_SCORES: Record<string, number> = {',
|
|
152
|
+
' critical: 1.0,',
|
|
153
|
+
' warning: 0.7,',
|
|
154
|
+
' suggestion: 0.4,',
|
|
155
|
+
'};',
|
|
156
|
+
'',
|
|
157
|
+
'// ─── Brain Class ─────────────────────────────────────────────────',
|
|
158
|
+
'',
|
|
159
|
+
'const DEFAULT_WEIGHTS: ScoringWeights = {',
|
|
160
|
+
' semantic: 0.40,',
|
|
161
|
+
' severity: 0.15,',
|
|
162
|
+
' recency: 0.15,',
|
|
163
|
+
' tagOverlap: 0.15,',
|
|
164
|
+
' domainMatch: 0.15,',
|
|
165
|
+
'};',
|
|
166
|
+
'',
|
|
167
|
+
'const WEIGHT_BOUND = 0.15;',
|
|
168
|
+
'const FEEDBACK_THRESHOLD = 30;',
|
|
169
|
+
'const DUPLICATE_BLOCK_THRESHOLD = 0.8;',
|
|
170
|
+
'const DUPLICATE_WARN_THRESHOLD = 0.6;',
|
|
171
|
+
'const RECENCY_HALF_LIFE_DAYS = 365;',
|
|
172
|
+
'',
|
|
173
|
+
'export class Brain {',
|
|
174
|
+
' private vault: Vault;',
|
|
175
|
+
' private vocabulary: Map<string, number> = new Map();',
|
|
176
|
+
' private weights: ScoringWeights = { ...DEFAULT_WEIGHTS };',
|
|
177
|
+
'',
|
|
178
|
+
' constructor(vault: Vault) {',
|
|
179
|
+
' this.vault = vault;',
|
|
180
|
+
' this.rebuildVocabulary();',
|
|
181
|
+
' this.recomputeWeights();',
|
|
182
|
+
' }',
|
|
183
|
+
'',
|
|
184
|
+
' intelligentSearch(query: string, options?: SearchOptions): RankedResult[] {',
|
|
185
|
+
' const limit = options?.limit ?? 10;',
|
|
186
|
+
' // Get raw FTS5 results with higher limit for re-ranking',
|
|
187
|
+
' const rawResults = this.vault.search(query, {',
|
|
188
|
+
' domain: options?.domain,',
|
|
189
|
+
' type: options?.type,',
|
|
190
|
+
' severity: options?.severity,',
|
|
191
|
+
' limit: Math.max(limit * 3, 30),',
|
|
192
|
+
' });',
|
|
193
|
+
'',
|
|
194
|
+
' if (rawResults.length === 0) return [];',
|
|
195
|
+
'',
|
|
196
|
+
' const queryTokens = tokenize(query);',
|
|
197
|
+
' const queryTags = options?.tags ?? [];',
|
|
198
|
+
' const queryDomain = options?.domain;',
|
|
199
|
+
' const now = Math.floor(Date.now() / 1000);',
|
|
200
|
+
'',
|
|
201
|
+
' const ranked = rawResults.map((result) => {',
|
|
202
|
+
' const entry = result.entry;',
|
|
203
|
+
' const breakdown = this.scoreEntry(entry, queryTokens, queryTags, queryDomain, now);',
|
|
204
|
+
' return { entry, score: breakdown.total, breakdown };',
|
|
205
|
+
' });',
|
|
206
|
+
'',
|
|
207
|
+
' ranked.sort((a, b) => b.score - a.score);',
|
|
208
|
+
' return ranked.slice(0, limit);',
|
|
209
|
+
' }',
|
|
210
|
+
'',
|
|
211
|
+
" enrichAndCapture(entry: Partial<IntelligenceEntry> & { id: string; type: IntelligenceEntry['type']; domain: string; title: string; severity: IntelligenceEntry['severity']; description: string }): CaptureResult {",
|
|
212
|
+
' // Auto-tag',
|
|
213
|
+
' const autoTags = this.generateTags(entry.title, entry.description, entry.context);',
|
|
214
|
+
' const mergedTags = Array.from(new Set([...(entry.tags ?? []), ...autoTags]));',
|
|
215
|
+
'',
|
|
216
|
+
' // Duplicate detection: compare against top 50 FTS5 matches for the title',
|
|
217
|
+
' const duplicate = this.detectDuplicate(entry.title, entry.domain);',
|
|
218
|
+
'',
|
|
219
|
+
' if (duplicate && duplicate.similarity >= DUPLICATE_BLOCK_THRESHOLD) {',
|
|
220
|
+
' return {',
|
|
221
|
+
' captured: false,',
|
|
222
|
+
' id: entry.id,',
|
|
223
|
+
' autoTags,',
|
|
224
|
+
' duplicate,',
|
|
225
|
+
' blocked: true,',
|
|
226
|
+
' };',
|
|
227
|
+
' }',
|
|
228
|
+
'',
|
|
229
|
+
' // Proceed with capture',
|
|
230
|
+
' const fullEntry: IntelligenceEntry = {',
|
|
231
|
+
' id: entry.id,',
|
|
232
|
+
' type: entry.type,',
|
|
233
|
+
' domain: entry.domain,',
|
|
234
|
+
' title: entry.title,',
|
|
235
|
+
' severity: entry.severity,',
|
|
236
|
+
' description: entry.description,',
|
|
237
|
+
' context: entry.context,',
|
|
238
|
+
' example: entry.example,',
|
|
239
|
+
' counterExample: entry.counterExample,',
|
|
240
|
+
' why: entry.why,',
|
|
241
|
+
' tags: mergedTags,',
|
|
242
|
+
' appliesTo: entry.appliesTo,',
|
|
243
|
+
' };',
|
|
244
|
+
'',
|
|
245
|
+
' this.vault.add(fullEntry);',
|
|
246
|
+
' this.updateVocabularyIncremental(fullEntry);',
|
|
247
|
+
'',
|
|
248
|
+
' const result: CaptureResult = {',
|
|
249
|
+
' captured: true,',
|
|
250
|
+
' id: entry.id,',
|
|
251
|
+
' autoTags,',
|
|
252
|
+
' };',
|
|
253
|
+
'',
|
|
254
|
+
' if (duplicate && duplicate.similarity >= DUPLICATE_WARN_THRESHOLD) {',
|
|
255
|
+
' result.duplicate = duplicate;',
|
|
256
|
+
' }',
|
|
257
|
+
'',
|
|
258
|
+
' return result;',
|
|
259
|
+
' }',
|
|
260
|
+
'',
|
|
261
|
+
" recordFeedback(query: string, entryId: string, action: 'accepted' | 'dismissed'): void {",
|
|
262
|
+
' const db = this.vault.getDb();',
|
|
263
|
+
" db.prepare('INSERT INTO brain_feedback (query, entry_id, action) VALUES (?, ?, ?)').run(query, entryId, action);",
|
|
264
|
+
' this.recomputeWeights();',
|
|
265
|
+
' }',
|
|
266
|
+
'',
|
|
267
|
+
' getRelevantPatterns(context: QueryContext): RankedResult[] {',
|
|
268
|
+
' return this.intelligentSearch(context.query, {',
|
|
269
|
+
' domain: context.domain,',
|
|
270
|
+
' tags: context.tags,',
|
|
271
|
+
' });',
|
|
272
|
+
' }',
|
|
273
|
+
'',
|
|
274
|
+
' rebuildVocabulary(): void {',
|
|
275
|
+
' const entries = this.vault.list({ limit: 100000 });',
|
|
276
|
+
' const docCount = entries.length;',
|
|
277
|
+
' if (docCount === 0) {',
|
|
278
|
+
' this.vocabulary.clear();',
|
|
279
|
+
' this.persistVocabulary();',
|
|
280
|
+
' return;',
|
|
281
|
+
' }',
|
|
282
|
+
'',
|
|
283
|
+
' const termDocFreq = new Map<string, number>();',
|
|
284
|
+
' for (const entry of entries) {',
|
|
285
|
+
" const text = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(' ');",
|
|
286
|
+
' const tokens = new Set(tokenize(text));',
|
|
287
|
+
' for (const token of tokens) {',
|
|
288
|
+
' termDocFreq.set(token, (termDocFreq.get(token) ?? 0) + 1);',
|
|
289
|
+
' }',
|
|
290
|
+
' }',
|
|
291
|
+
'',
|
|
292
|
+
' this.vocabulary.clear();',
|
|
293
|
+
' for (const [term, df] of termDocFreq) {',
|
|
294
|
+
' const idf = Math.log((docCount + 1) / (df + 1)) + 1;',
|
|
295
|
+
' this.vocabulary.set(term, idf);',
|
|
296
|
+
' }',
|
|
297
|
+
'',
|
|
298
|
+
' this.persistVocabulary();',
|
|
299
|
+
' }',
|
|
300
|
+
'',
|
|
301
|
+
' getStats(): BrainStats {',
|
|
302
|
+
' const db = this.vault.getDb();',
|
|
303
|
+
" const feedbackCount = (db.prepare('SELECT COUNT(*) as count FROM brain_feedback').get() as { count: number }).count;",
|
|
304
|
+
' return {',
|
|
305
|
+
' vocabularySize: this.vocabulary.size,',
|
|
306
|
+
' feedbackCount,',
|
|
307
|
+
' weights: { ...this.weights },',
|
|
308
|
+
' };',
|
|
309
|
+
' }',
|
|
310
|
+
'',
|
|
311
|
+
' getVocabularySize(): number {',
|
|
312
|
+
' return this.vocabulary.size;',
|
|
313
|
+
' }',
|
|
314
|
+
'',
|
|
315
|
+
' // ─── Private methods ─────────────────────────────────────────────',
|
|
316
|
+
'',
|
|
317
|
+
' private scoreEntry(',
|
|
318
|
+
' entry: IntelligenceEntry,',
|
|
319
|
+
' queryTokens: string[],',
|
|
320
|
+
' queryTags: string[],',
|
|
321
|
+
' queryDomain: string | undefined,',
|
|
322
|
+
' now: number',
|
|
323
|
+
' ): ScoreBreakdown {',
|
|
324
|
+
' // 1. Semantic (TF-IDF cosine similarity)',
|
|
325
|
+
' let semantic = 0;',
|
|
326
|
+
' if (this.vocabulary.size > 0 && queryTokens.length > 0) {',
|
|
327
|
+
" const entryText = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(' ');",
|
|
328
|
+
' const entryTokens = tokenize(entryText);',
|
|
329
|
+
' const queryVec = calculateTfIdf(queryTokens, this.vocabulary);',
|
|
330
|
+
' const entryVec = calculateTfIdf(entryTokens, this.vocabulary);',
|
|
331
|
+
' semantic = cosineSimilarity(queryVec, entryVec);',
|
|
332
|
+
' }',
|
|
333
|
+
'',
|
|
334
|
+
' // 2. Severity',
|
|
335
|
+
' const severity = SEVERITY_SCORES[entry.severity] ?? 0.4;',
|
|
336
|
+
'',
|
|
337
|
+
' // 3. Recency (exponential decay over 1 year)',
|
|
338
|
+
' const entryAge = now - (entry as unknown as { created_at?: number }).created_at!;',
|
|
339
|
+
' const halfLifeSeconds = RECENCY_HALF_LIFE_DAYS * 86400;',
|
|
340
|
+
' const recency = entryAge > 0 ? Math.exp(-0.693 * entryAge / halfLifeSeconds) : 1;',
|
|
341
|
+
'',
|
|
342
|
+
' // 4. Tag overlap (Jaccard)',
|
|
343
|
+
' const tagOverlap = queryTags.length > 0 ? jaccardSimilarity(queryTags, entry.tags) : 0;',
|
|
344
|
+
'',
|
|
345
|
+
' // 5. Domain match',
|
|
346
|
+
' const domainMatch = queryDomain && entry.domain === queryDomain ? 1.0 : 0;',
|
|
347
|
+
'',
|
|
348
|
+
' const total =',
|
|
349
|
+
' this.weights.semantic * semantic +',
|
|
350
|
+
' this.weights.severity * severity +',
|
|
351
|
+
' this.weights.recency * recency +',
|
|
352
|
+
' this.weights.tagOverlap * tagOverlap +',
|
|
353
|
+
' this.weights.domainMatch * domainMatch;',
|
|
354
|
+
'',
|
|
355
|
+
' return { semantic, severity, recency, tagOverlap, domainMatch, total };',
|
|
356
|
+
' }',
|
|
357
|
+
'',
|
|
358
|
+
' private generateTags(title: string, description: string, context?: string): string[] {',
|
|
359
|
+
" const text = [title, description, context ?? ''].join(' ');",
|
|
360
|
+
' const tokens = tokenize(text);',
|
|
361
|
+
' if (tokens.length === 0) return [];',
|
|
362
|
+
'',
|
|
363
|
+
' // Score tokens by TF-IDF',
|
|
364
|
+
' const tf = calculateTf(tokens);',
|
|
365
|
+
' const scored: Array<[string, number]> = [];',
|
|
366
|
+
' for (const [term, tfValue] of tf) {',
|
|
367
|
+
' const idf = this.vocabulary.get(term) ?? 1;',
|
|
368
|
+
' scored.push([term, tfValue * idf]);',
|
|
369
|
+
' }',
|
|
370
|
+
'',
|
|
371
|
+
' scored.sort((a, b) => b[1] - a[1]);',
|
|
372
|
+
' return scored.slice(0, 5).map(([term]) => term);',
|
|
373
|
+
' }',
|
|
374
|
+
'',
|
|
375
|
+
' private detectDuplicate(title: string, domain: string): { id: string; similarity: number } | null {',
|
|
376
|
+
' let candidates: SearchResult[];',
|
|
377
|
+
' try {',
|
|
378
|
+
' candidates = this.vault.search(title, { domain, limit: 50 });',
|
|
379
|
+
' } catch {',
|
|
380
|
+
' return null;',
|
|
381
|
+
' }',
|
|
382
|
+
' if (candidates.length === 0) return null;',
|
|
383
|
+
'',
|
|
384
|
+
' const titleTokens = tokenize(title);',
|
|
385
|
+
' if (titleTokens.length === 0) return null;',
|
|
386
|
+
' const titleVec = calculateTfIdf(titleTokens, this.vocabulary);',
|
|
387
|
+
' if (titleVec.size === 0) {',
|
|
388
|
+
' // Fallback: use raw TF for comparison when vocabulary is empty',
|
|
389
|
+
' const titleTf = calculateTf(titleTokens);',
|
|
390
|
+
' let bestMatch: { id: string; similarity: number } | null = null;',
|
|
391
|
+
' for (const candidate of candidates) {',
|
|
392
|
+
' const candidateTokens = tokenize(candidate.entry.title);',
|
|
393
|
+
' const candidateTf = calculateTf(candidateTokens);',
|
|
394
|
+
' const sim = cosineSimilarity(titleTf, candidateTf);',
|
|
395
|
+
' if (!bestMatch || sim > bestMatch.similarity) {',
|
|
396
|
+
' bestMatch = { id: candidate.entry.id, similarity: sim };',
|
|
397
|
+
' }',
|
|
398
|
+
' }',
|
|
399
|
+
' return bestMatch;',
|
|
400
|
+
' }',
|
|
401
|
+
'',
|
|
402
|
+
' let bestMatch: { id: string; similarity: number } | null = null;',
|
|
403
|
+
' for (const candidate of candidates) {',
|
|
404
|
+
" const candidateText = [candidate.entry.title, candidate.entry.description].join(' ');",
|
|
405
|
+
' const candidateTokens = tokenize(candidateText);',
|
|
406
|
+
' const candidateVec = calculateTfIdf(candidateTokens, this.vocabulary);',
|
|
407
|
+
' const sim = cosineSimilarity(titleVec, candidateVec);',
|
|
408
|
+
' if (!bestMatch || sim > bestMatch.similarity) {',
|
|
409
|
+
' bestMatch = { id: candidate.entry.id, similarity: sim };',
|
|
410
|
+
' }',
|
|
411
|
+
' }',
|
|
412
|
+
' return bestMatch;',
|
|
413
|
+
' }',
|
|
414
|
+
'',
|
|
415
|
+
' private updateVocabularyIncremental(entry: IntelligenceEntry): void {',
|
|
416
|
+
" const text = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(' ');",
|
|
417
|
+
' const tokens = new Set(tokenize(text));',
|
|
418
|
+
' const totalDocs = this.vault.stats().totalEntries;',
|
|
419
|
+
'',
|
|
420
|
+
' for (const token of tokens) {',
|
|
421
|
+
' const currentDocCount = (this.vocabulary.has(token)) ? Math.round((totalDocs) / Math.exp(this.vocabulary.get(token)! - 1)) + 1 : 1;',
|
|
422
|
+
' const newIdf = Math.log((totalDocs + 1) / (currentDocCount + 1)) + 1;',
|
|
423
|
+
' this.vocabulary.set(token, newIdf);',
|
|
424
|
+
' }',
|
|
425
|
+
'',
|
|
426
|
+
' this.persistVocabulary();',
|
|
427
|
+
' }',
|
|
428
|
+
'',
|
|
429
|
+
' private persistVocabulary(): void {',
|
|
430
|
+
' const db = this.vault.getDb();',
|
|
431
|
+
" db.exec('DELETE FROM brain_vocabulary');",
|
|
432
|
+
' if (this.vocabulary.size === 0) return;',
|
|
433
|
+
" const insert = db.prepare('INSERT INTO brain_vocabulary (term, idf, doc_count) VALUES (?, ?, ?)');",
|
|
434
|
+
' const tx = db.transaction(() => {',
|
|
435
|
+
' for (const [term, idf] of this.vocabulary) {',
|
|
436
|
+
' insert.run(term, idf, 1);',
|
|
437
|
+
' }',
|
|
438
|
+
' });',
|
|
439
|
+
' tx();',
|
|
440
|
+
' }',
|
|
441
|
+
'',
|
|
442
|
+
' private recomputeWeights(): void {',
|
|
443
|
+
' const db = this.vault.getDb();',
|
|
444
|
+
" const feedbackCount = (db.prepare('SELECT COUNT(*) as count FROM brain_feedback').get() as { count: number }).count;",
|
|
445
|
+
' if (feedbackCount < FEEDBACK_THRESHOLD) {',
|
|
446
|
+
' this.weights = { ...DEFAULT_WEIGHTS };',
|
|
447
|
+
' return;',
|
|
448
|
+
' }',
|
|
449
|
+
'',
|
|
450
|
+
' // Count accepted vs dismissed feedback',
|
|
451
|
+
' const accepted = (db.prepare("SELECT COUNT(*) as count FROM brain_feedback WHERE action = \'accepted\'").get() as { count: number }).count;',
|
|
452
|
+
' const acceptRate = feedbackCount > 0 ? accepted / feedbackCount : 0.5;',
|
|
453
|
+
'',
|
|
454
|
+
' // High accept rate -> increase semantic weight, decrease others',
|
|
455
|
+
' // Low accept rate -> decrease semantic weight, increase others',
|
|
456
|
+
' const semanticDelta = (acceptRate - 0.5) * WEIGHT_BOUND * 2;',
|
|
457
|
+
'',
|
|
458
|
+
' const newWeights = { ...DEFAULT_WEIGHTS };',
|
|
459
|
+
' newWeights.semantic = clamp(DEFAULT_WEIGHTS.semantic + semanticDelta, DEFAULT_WEIGHTS.semantic - WEIGHT_BOUND, DEFAULT_WEIGHTS.semantic + WEIGHT_BOUND);',
|
|
460
|
+
'',
|
|
461
|
+
' // Redistribute remaining weight evenly across other dimensions',
|
|
462
|
+
' const remaining = 1.0 - newWeights.semantic;',
|
|
463
|
+
' const otherSum = DEFAULT_WEIGHTS.severity + DEFAULT_WEIGHTS.recency + DEFAULT_WEIGHTS.tagOverlap + DEFAULT_WEIGHTS.domainMatch;',
|
|
464
|
+
' const scale = remaining / otherSum;',
|
|
465
|
+
' newWeights.severity = DEFAULT_WEIGHTS.severity * scale;',
|
|
466
|
+
' newWeights.recency = DEFAULT_WEIGHTS.recency * scale;',
|
|
467
|
+
' newWeights.tagOverlap = DEFAULT_WEIGHTS.tagOverlap * scale;',
|
|
468
|
+
' newWeights.domainMatch = DEFAULT_WEIGHTS.domainMatch * scale;',
|
|
469
|
+
'',
|
|
470
|
+
' this.weights = newWeights;',
|
|
471
|
+
' }',
|
|
472
|
+
'}',
|
|
473
|
+
'',
|
|
474
|
+
'function clamp(value: number, min: number, max: number): number {',
|
|
475
|
+
' return Math.max(min, Math.min(max, value));',
|
|
476
|
+
'}',
|
|
477
|
+
].join('\n');
|
|
478
|
+
//# sourceMappingURL=brain.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"brain.js","sourceRoot":"","sources":["../../src/templates/brain.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,UAAU,aAAa;IAC3B,OAAO,cAAc,CAAC;AACxB,CAAC;AAED,MAAM,cAAc,GAAG;IACrB,iDAAiD;IACjD,wDAAwD;IACxD,oEAAoE;IACpE,EAAE;IACF,0EAA0E;IAC1E,EAAE;IACF,mCAAmC;IACnC,qBAAqB;IACrB,qBAAqB;IACrB,oBAAoB;IACpB,uBAAuB;IACvB,wBAAwB;IACxB,GAAG;IACH,EAAE;IACF,mCAAmC;IACnC,qBAAqB;IACrB,qBAAqB;IACrB,oBAAoB;IACpB,uBAAuB;IACvB,wBAAwB;IACxB,kBAAkB;IAClB,GAAG;IACH,EAAE;IACF,iCAAiC;IACjC,6BAA6B;IAC7B,kBAAkB;IAClB,8BAA8B;IAC9B,GAAG;IACH,EAAE;IACF,kCAAkC;IAClC,oBAAoB;IACpB,kBAAkB;IAClB,sBAAsB;IACtB,mBAAmB;IACnB,oBAAoB;IACpB,GAAG;IACH,EAAE;IACF,kCAAkC;IAClC,sBAAsB;IACtB,eAAe;IACf,uBAAuB;IACvB,mDAAmD;IACnD,sBAAsB;IACtB,GAAG;IACH,EAAE;IACF,+BAA+B;IAC/B,2BAA2B;IAC3B,0BAA0B;IAC1B,4BAA4B;IAC5B,GAAG;IACH,EAAE;IACF,iCAAiC;IACjC,kBAAkB;IAClB,oBAAoB;IACpB,oBAAoB;IACpB,GAAG;IACH,EAAE;IACF,0CAA0C;IAC1C,EAAE;IACF,wEAAwE;IACxE,EAAE;IACF,6BAA6B;IAC7B,wEAAwE;IACxE,yEAAyE;IACzE,8EAA8E;IAC9E,0EAA0E;IAC1E,4EAA4E;IAC5E,0EAA0E;IAC1E,yEAAyE;IACzE,4EAA4E;IAC5E,qEAAqE;IACrE,kEAAkE;IAClE,KAAK;IACL,EAAE;IACF,sEAAsE;IACtE,EAAE;IACF,6CAA6C;IAC7C,eAAe;IACf,oBAAoB;IACpB,qCAAqC;IACrC,oBAAoB;IACpB,wDAAwD;IACxD,GAAG;IACH,EAAE;IACF,wDAAwD;IACxD,uCAAuC;IACvC,iCAAiC;IACjC,8CAA8C;IAC9C,KAAK;IACL,mCAAmC;IACnC,mCAAmC;IACnC,qCAAqC;IACrC,gCAAgC;IAChC,KAAK;IACL,cAAc;IACd,GAAG;IACH,EAAE;IACF,4FAA4F;IAC5F,mCAAmC;IACnC,0CAA0C;IAC1C,uCAAuC;IACvC,4CAA4C;IAC5C,oBAAoB;IACpB,uCAAuC;IACvC,OAAO;IACP,KAAK;IACL,iBAAiB;IACjB,GAAG;IACH,EAAE;IACF,uEAAuE;IACvE,gBAAgB;IAChB,kBAAkB;IAClB,kBAAkB;IAClB,mCAAmC;IACnC,2BAA2B;IAC3B,+BAA+B;IAC/B,+BAA+B;IAC/B,2BAA2B;IAC3B,OAAO;IACP,KAAK;IACL,+BAA+B;IAC/B,2BAA2B;IAC3B,KAAK;IACL,sDAAsD;IACtD,yCAAyC;IACzC,GAAG;IACH,EAAE;IACF,gEAAgE;IAChE,mDAAmD;IACnD,4BAA4B;IAC5B,4BAA4B;IAC5B,yBAAyB;IACzB,8BAA8B;IAC9B,yCAAyC;IACzC,KAAK;IACL,6CAA6C;IAC7C,kDAAkD;IAClD,GAAG;IACH,EAAE;IACF,wEAAwE;IACxE,EAAE;IACF,mDAAmD;IACnD,kBAAkB;IAClB,iBAAiB;IACjB,oBAAoB;IACpB,IAAI;IACJ,EAAE;IACF,sEAAsE;IACtE,EAAE;IACF,2CAA2C;IAC3C,mBAAmB;IACnB,mBAAmB;IACnB,kBAAkB;IAClB,qBAAqB;IACrB,sBAAsB;IACtB,IAAI;IACJ,EAAE;IACF,4BAA4B;IAC5B,gCAAgC;IAChC,wCAAwC;IACxC,uCAAuC;IACvC,qCAAqC;IACrC,EAAE;IACF,sBAAsB;IACtB,yBAAyB;IACzB,wDAAwD;IACxD,6DAA6D;IAC7D,EAAE;IACF,+BAA+B;IAC/B,yBAAyB;IACzB,+BAA+B;IAC/B,8BAA8B;IAC9B,KAAK;IACL,EAAE;IACF,+EAA+E;IAC/E,yCAAyC;IACzC,8DAA8D;IAC9D,mDAAmD;IACnD,gCAAgC;IAChC,4BAA4B;IAC5B,oCAAoC;IACpC,uCAAuC;IACvC,SAAS;IACT,EAAE;IACF,6CAA6C;IAC7C,EAAE;IACF,0CAA0C;IAC1C,4CAA4C;IAC5C,0CAA0C;IAC1C,gDAAgD;IAChD,EAAE;IACF,iDAAiD;IACjD,mCAAmC;IACnC,2FAA2F;IAC3F,4DAA4D;IAC5D,SAAS;IACT,EAAE;IACF,+CAA+C;IAC/C,oCAAoC;IACpC,KAAK;IACL,EAAE;IACF,uNAAuN;IACvN,iBAAiB;IACjB,wFAAwF;IACxF,mFAAmF;IACnF,EAAE;IACF,+EAA+E;IAC/E,wEAAwE;IACxE,EAAE;IACF,2EAA2E;IAC3E,gBAAgB;IAChB,0BAA0B;IAC1B,uBAAuB;IACvB,mBAAmB;IACnB,oBAAoB;IACpB,wBAAwB;IACxB,UAAU;IACV,OAAO;IACP,EAAE;IACF,6BAA6B;IAC7B,4CAA4C;IAC5C,qBAAqB;IACrB,yBAAyB;IACzB,6BAA6B;IAC7B,2BAA2B;IAC3B,iCAAiC;IACjC,uCAAuC;IACvC,+BAA+B;IAC/B,+BAA+B;IAC/B,6CAA6C;IAC7C,uBAAuB;IACvB,yBAAyB;IACzB,mCAAmC;IACnC,QAAQ;IACR,EAAE;IACF,gCAAgC;IAChC,kDAAkD;IAClD,EAAE;IACF,qCAAqC;IACrC,uBAAuB;IACvB,qBAAqB;IACrB,iBAAiB;IACjB,QAAQ;IACR,EAAE;IACF,0EAA0E;IAC1E,qCAAqC;IACrC,OAAO;IACP,EAAE;IACF,oBAAoB;IACpB,KAAK;IACL,EAAE;IACF,4FAA4F;IAC5F,oCAAoC;IACpC,sHAAsH;IACtH,8BAA8B;IAC9B,KAAK;IACL,EAAE;IACF,gEAAgE;IAChE,oDAAoD;IACpD,+BAA+B;IAC/B,2BAA2B;IAC3B,SAAS;IACT,KAAK;IACL,EAAE;IACF,+BAA+B;IAC/B,yDAAyD;IACzD,sCAAsC;IACtC,2BAA2B;IAC3B,gCAAgC;IAChC,iCAAiC;IACjC,eAAe;IACf,OAAO;IACP,EAAE;IACF,oDAAoD;IACpD,oCAAoC;IACpC,2GAA2G;IAC3G,+CAA+C;IAC/C,qCAAqC;IACrC,oEAAoE;IACpE,SAAS;IACT,OAAO;IACP,EAAE;IACF,8BAA8B;IAC9B,6CAA6C;IAC7C,4DAA4D;IAC5D,uCAAuC;IACvC,OAAO;IACP,EAAE;IACF,+BAA+B;IAC/B,KAAK;IACL,EAAE;IACF,4BAA4B;IAC5B,oCAAoC;IACpC,0HAA0H;IAC1H,cAAc;IACd,6CAA6C;IAC7C,sBAAsB;IACtB,qCAAqC;IACrC,QAAQ;IACR,KAAK;IACL,EAAE;IACF,iCAAiC;IACjC,kCAAkC;IAClC,KAAK;IACL,EAAE;IACF,wEAAwE;IACxE,EAAE;IACF,uBAAuB;IACvB,+BAA+B;IAC/B,4BAA4B;IAC5B,0BAA0B;IAC1B,sCAAsC;IACtC,iBAAiB;IACjB,uBAAuB;IACvB,+CAA+C;IAC/C,uBAAuB;IACvB,+DAA+D;IAC/D,gHAAgH;IAChH,gDAAgD;IAChD,sEAAsE;IACtE,sEAAsE;IACtE,wDAAwD;IACxD,OAAO;IACP,EAAE;IACF,oBAAoB;IACpB,8DAA8D;IAC9D,EAAE;IACF,mDAAmD;IACnD,uFAAuF;IACvF,6DAA6D;IAC7D,uFAAuF;IACvF,EAAE;IACF,iCAAiC;IACjC,6FAA6F;IAC7F,EAAE;IACF,wBAAwB;IACxB,gFAAgF;IAChF,EAAE;IACF,mBAAmB;IACnB,0CAA0C;IAC1C,0CAA0C;IAC1C,wCAAwC;IACxC,8CAA8C;IAC9C,+CAA+C;IAC/C,EAAE;IACF,6EAA6E;IAC7E,KAAK;IACL,EAAE;IACF,0FAA0F;IAC1F,iEAAiE;IACjE,oCAAoC;IACpC,yCAAyC;IACzC,EAAE;IACF,+BAA+B;IAC/B,qCAAqC;IACrC,iDAAiD;IACjD,yCAAyC;IACzC,mDAAmD;IACnD,2CAA2C;IAC3C,OAAO;IACP,EAAE;IACF,yCAAyC;IACzC,sDAAsD;IACtD,KAAK;IACL,EAAE;IACF,uGAAuG;IACvG,qCAAqC;IACrC,WAAW;IACX,qEAAqE;IACrE,eAAe;IACf,oBAAoB;IACpB,OAAO;IACP,+CAA+C;IAC/C,EAAE;IACF,0CAA0C;IAC1C,gDAAgD;IAChD,oEAAoE;IACpE,gCAAgC;IAChC,uEAAuE;IACvE,iDAAiD;IACjD,wEAAwE;IACxE,6CAA6C;IAC7C,kEAAkE;IAClE,2DAA2D;IAC3D,6DAA6D;IAC7D,yDAAyD;IACzD,oEAAoE;IACpE,WAAW;IACX,SAAS;IACT,yBAAyB;IACzB,OAAO;IACP,EAAE;IACF,sEAAsE;IACtE,2CAA2C;IAC3C,6FAA6F;IAC7F,wDAAwD;IACxD,8EAA8E;IAC9E,6DAA6D;IAC7D,uDAAuD;IACvD,kEAAkE;IAClE,SAAS;IACT,OAAO;IACP,uBAAuB;IACvB,KAAK;IACL,EAAE;IACF,yEAAyE;IACzE,yGAAyG;IACzG,6CAA6C;IAC7C,wDAAwD;IACxD,EAAE;IACF,mCAAmC;IACnC,2IAA2I;IAC3I,6EAA6E;IAC7E,2CAA2C;IAC3C,OAAO;IACP,EAAE;IACF,+BAA+B;IAC/B,KAAK;IACL,EAAE;IACF,uCAAuC;IACvC,oCAAoC;IACpC,8CAA8C;IAC9C,6CAA6C;IAC7C,wGAAwG;IACxG,uCAAuC;IACvC,oDAAoD;IACpD,mCAAmC;IACnC,SAAS;IACT,SAAS;IACT,WAAW;IACX,KAAK;IACL,EAAE;IACF,sCAAsC;IACtC,oCAAoC;IACpC,0HAA0H;IAC1H,+CAA+C;IAC/C,8CAA8C;IAC9C,eAAe;IACf,OAAO;IACP,EAAE;IACF,6CAA6C;IAC7C,iJAAiJ;IACjJ,4EAA4E;IAC5E,EAAE;IACF,sEAAsE;IACtE,qEAAqE;IACrE,kEAAkE;IAClE,EAAE;IACF,gDAAgD;IAChD,8JAA8J;IAC9J,EAAE;IACF,qEAAqE;IACrE,kDAAkD;IAClD,qIAAqI;IACrI,yCAAyC;IACzC,6DAA6D;IAC7D,2DAA2D;IAC3D,iEAAiE;IACjE,mEAAmE;IACnE,EAAE;IACF,gCAAgC;IAChC,KAAK;IACL,GAAG;IACH,EAAE;IACF,mEAAmE;IACnE,+CAA+C;IAC/C,GAAG;CACJ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC"}
|