claude-brain 0.17.3 → 0.17.5

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/VERSION CHANGED
@@ -1 +1 @@
1
- 0.17.3
1
+ 0.17.5
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claude-brain",
3
- "version": "0.17.3",
3
+ "version": "0.17.5",
4
4
  "description": "Local development assistant bridging Obsidian vaults with Claude Code via MCP",
5
5
  "type": "module",
6
6
  "main": "src/index.ts",
@@ -3,7 +3,7 @@ import type { PartialConfig } from './schema'
3
3
  /** Default configuration values for Claude Brain */
4
4
  export const defaultConfig: PartialConfig = {
5
5
  serverName: 'claude-brain',
6
- serverVersion: '0.17.3',
6
+ serverVersion: '0.17.5',
7
7
  logLevel: 'info',
8
8
  logFilePath: './logs/claude-brain.log',
9
9
  dbPath: './data/memory.db',
@@ -284,7 +284,7 @@ export const ConfigSchema = z.object({
284
284
  serverName: z.string().default('claude-brain'),
285
285
 
286
286
  /** Server version in semver format */
287
- serverVersion: z.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be semver format').default('0.17.3'),
287
+ serverVersion: z.string().regex(/^\d+\.\d+\.\d+$/, 'Version must be semver format').default('0.17.5'),
288
288
 
289
289
  /** Logging level */
290
290
  logLevel: LogLevelSchema.default('info'),
@@ -8,6 +8,7 @@ export class ChromaClientManager {
8
8
  private config: ChromaConfig
9
9
  private isConnected: boolean = false
10
10
  private collections: Map<string, Collection> = new Map()
11
+ private pendingCollections: Map<string, Promise<Collection>> = new Map()
11
12
 
12
13
  constructor(logger: Logger, config: ChromaConfig) {
13
14
  this.logger = logger.child({ component: 'chroma-client' })
@@ -106,8 +107,25 @@ export class ChromaClientManager {
106
107
  return this.collections.get(name)!
107
108
  }
108
109
 
110
+ // Deduplicate concurrent calls: if a getOrCreateCollection is already
111
+ // in-flight for this name, reuse the same promise instead of racing.
112
+ if (this.pendingCollections.has(name)) {
113
+ return this.pendingCollections.get(name)!
114
+ }
115
+
116
+ const pending = this.fetchAndCacheCollection(name)
117
+ this.pendingCollections.set(name, pending)
118
+
119
+ try {
120
+ return await pending
121
+ } finally {
122
+ this.pendingCollections.delete(name)
123
+ }
124
+ }
125
+
126
+ private async fetchAndCacheCollection(name: string): Promise<Collection> {
109
127
  try {
110
- const collection = await this.client.getOrCreateCollection({
128
+ const collection = await this.client!.getOrCreateCollection({
111
129
  name,
112
130
  metadata: {
113
131
  'hnsw:space': 'cosine',
@@ -36,13 +36,35 @@ export class TransformersEmbeddingProvider implements EmbeddingProvider {
36
36
 
37
37
  private async getPipeline() {
38
38
  if (!this.pipeline) {
39
- const { pipeline } = await import('@xenova/transformers')
40
- this.pipeline = await pipeline('feature-extraction', this.modelName)
39
+ try {
40
+ const { pipeline } = await import('@xenova/transformers')
41
+ this.pipeline = await pipeline('feature-extraction', this.modelName)
42
+ } catch (error) {
43
+ this.logger.warn({ error }, 'Model load failed, clearing cache and retrying')
44
+ await this.clearModelCache()
45
+ const { pipeline } = await import('@xenova/transformers')
46
+ this.pipeline = await pipeline('feature-extraction', this.modelName)
47
+ }
41
48
  this.logger.info({ model: this.modelName }, 'Transformers pipeline initialized')
42
49
  }
43
50
  return this.pipeline
44
51
  }
45
52
 
53
+ private async clearModelCache(): Promise<void> {
54
+ const { rm } = await import('fs/promises')
55
+ const { join } = await import('path')
56
+ const cacheDir = join(
57
+ require.resolve('@xenova/transformers'),
58
+ '..', '.cache', ...this.modelName.split('/')
59
+ )
60
+ try {
61
+ await rm(cacheDir, { recursive: true, force: true })
62
+ this.logger.info({ cacheDir }, 'Cleared corrupted model cache')
63
+ } catch {
64
+ // Cache dir may not exist, continue
65
+ }
66
+ }
67
+
46
68
  async generate(text: string): Promise<number[]> {
47
69
  const pipe = await this.getPipeline()
48
70
 
@@ -174,7 +174,7 @@ const UPDATE_PHRASES = [
174
174
 
175
175
  // Delete/forget memory
176
176
  const DELETE_PHRASES = [
177
- 'forget that', 'forget about', 'delete that', 'delete the',
177
+ 'forget that', 'forget about', 'delete that', 'delete the memory', 'delete the decision',
178
178
  'remove that memory', 'remove that decision', 'that was wrong',
179
179
  'discard that', 'erase that', 'undo that decision',
180
180
  'remove the memory', 'clear that', 'drop that'
@@ -400,6 +400,7 @@ export class IntentClassifier {
400
400
  }
401
401
 
402
402
  private isDeleteMemory(lower: string): boolean {
403
+ if (lower.length > 150) return false
403
404
  return DELETE_PHRASES.some(p => lower.includes(p))
404
405
  }
405
406