2ndbrain 2026.1.35 → 2026.1.37

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "2ndbrain",
3
- "version": "2026.1.35",
3
+ "version": "2026.1.37",
4
4
  "description": "Always-on Node.js service bridging Telegram messaging to Claude AI with knowledge graph, journal, project management, and semantic search.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -31,6 +31,7 @@ class EmbeddingsEngine {
31
31
  this.config = config;
32
32
  this.logger = logger;
33
33
  this._dimensions = null;
34
+ this._initialized = false;
34
35
  }
35
36
 
36
37
  /**
@@ -42,6 +43,16 @@ class EmbeddingsEngine {
42
43
  return Boolean(this.config.EMBEDDING_PROVIDER);
43
44
  }
44
45
 
46
+ /**
47
+ * Returns true when the embedding engine has been successfully initialized
48
+ * (tables created / verified).
49
+ *
50
+ * @returns {boolean}
51
+ */
52
+ isInitialized() {
53
+ return this._initialized;
54
+ }
55
+
45
56
  /**
46
57
  * Run startup configuration resolution.
47
58
  *
@@ -87,6 +98,7 @@ class EmbeddingsEngine {
87
98
 
88
99
  if (!tableCheck.rows[0].table_exists) {
89
100
  await this._firstTimeSetup(provider, model, dimensions);
101
+ this._initialized = true;
90
102
  return;
91
103
  }
92
104
 
@@ -98,6 +110,7 @@ class EmbeddingsEngine {
98
110
  if (configRow.rows.length === 0) {
99
111
  // Table present but empty -- treat as first-time setup
100
112
  await this._firstTimeSetup(provider, model, dimensions);
113
+ this._initialized = true;
101
114
  return;
102
115
  }
103
116
 
@@ -110,11 +123,13 @@ class EmbeddingsEngine {
110
123
  ) {
111
124
  // Configuration unchanged
112
125
  this.logger.info('embeddings', 'Embedding configuration unchanged.');
126
+ this._initialized = true;
113
127
  return;
114
128
  }
115
129
 
116
130
  // Configuration differs -- perform model switch
117
131
  await this._handleModelSwitch(current, { provider, model, dimensions });
132
+ this._initialized = true;
118
133
  }
119
134
 
120
135
  /**
@@ -53,6 +53,9 @@ class EmbeddingWorker {
53
53
 
54
54
  /** Guard to prevent overlapping iterations. */
55
55
  this._processing = false;
56
+
57
+ /** Whether the embeddings table has been verified to exist. */
58
+ this._tableVerified = false;
56
59
  }
57
60
 
58
61
  /**
@@ -125,6 +128,25 @@ class EmbeddingWorker {
125
128
  * Fetch and process a batch of rows with NULL vectors.
126
129
  */
127
130
  async _processQueue() {
131
+ // On first call, verify the embeddings table exists
132
+ if (!this._tableVerified) {
133
+ const check = await this.db.query(
134
+ `SELECT EXISTS (
135
+ SELECT FROM information_schema.tables
136
+ WHERE table_schema = 'public' AND table_name = 'embeddings'
137
+ ) AS ok`,
138
+ );
139
+ if (!check.rows[0].ok) {
140
+ this.logger.warn(
141
+ 'embedding-worker',
142
+ 'Embeddings table does not exist; stopping worker.',
143
+ );
144
+ this.stop();
145
+ return;
146
+ }
147
+ this._tableVerified = true;
148
+ }
149
+
128
150
  const result = await this.db.query(
129
151
  `SELECT id, entity_type, entity_id
130
152
  FROM embeddings
package/src/index.js CHANGED
@@ -518,7 +518,7 @@ async function main() {
518
518
  }
519
519
 
520
520
  // Start background embedding worker
521
- if (embeddingsEngine.isEnabled() && dbReady) {
521
+ if (embeddingsEngine.isInitialized() && dbReady) {
522
522
  embeddingWorker = new EmbeddingWorker({ db: { query }, config, logger });
523
523
  embeddingWorker.start();
524
524
  }