kongbrain 0.5.0 → 0.5.2

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.
@@ -0,0 +1,11 @@
1
+ import {
2
+ buildSystemPrompt,
3
+ buildTranscript,
4
+ writeExtractionResults
5
+ } from "./chunk-B3QUPMCI.js";
6
+ import "./chunk-XSIONAGJ.js";
7
+ export {
8
+ buildSystemPrompt,
9
+ buildTranscript,
10
+ writeExtractionResults
11
+ };
@@ -5,7 +5,15 @@
5
5
  "kind": "context-engine",
6
6
  "requires": {
7
7
  "bins": ["surreal"],
8
- "env": ["SURREAL_URL", "SURREAL_USER", "SURREAL_PASS", "SURREAL_NS", "SURREAL_DB"]
8
+ "env": ["SURREAL_URL", "SURREAL_USER", "SURREAL_PASS", "SURREAL_NS", "SURREAL_DB"],
9
+ "optionalEnv": [
10
+ "KONGBRAIN_EMBED_PROVIDER",
11
+ "EMBED_MODEL_PATH",
12
+ "OPENAI_BASE_URL",
13
+ "OPENAI_API_KEY",
14
+ "KONGBRAIN_LOG_LEVEL",
15
+ "KONGBRAIN_DEBUG"
16
+ ]
9
17
  },
10
18
  "uiHints": {
11
19
  "surreal.url": {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "kongbrain",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
4
4
  "description": "Graph-backed persistent memory engine for OpenClaw. Replaces the default context window with SurrealDB + vector embeddings that learn across sessions.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -31,8 +31,30 @@
31
31
  "extensions": [
32
32
  "./src/index.ts"
33
33
  ],
34
+ "runtimeExtensions": [
35
+ "./dist/index.js"
36
+ ],
34
37
  "install": {
35
- "minHostVersion": ">=2026.3.23"
38
+ "minHostVersion": ">=2026.3.23",
39
+ "type": "code-plugin",
40
+ "requires": {
41
+ "bins": [
42
+ "surreal"
43
+ ],
44
+ "env": [
45
+ "SURREAL_URL",
46
+ "SURREAL_USER",
47
+ "SURREAL_PASS",
48
+ "SURREAL_NS",
49
+ "SURREAL_DB"
50
+ ],
51
+ "optionalEnv": [
52
+ "KONGBRAIN_EMBED_PROVIDER",
53
+ "EMBED_MODEL_PATH",
54
+ "OPENAI_BASE_URL",
55
+ "OPENAI_API_KEY"
56
+ ]
57
+ }
36
58
  },
37
59
  "compat": {
38
60
  "pluginApi": ">=2026.3.23",
@@ -48,7 +70,7 @@
48
70
  "postpack": "cp README.github.md README.md && rm README.github.md",
49
71
  "test": "vitest run",
50
72
  "test:watch": "vitest",
51
- "build": "tsc --noEmit",
73
+ "build": "tsup src/index.ts --format esm --out-dir dist --clean",
52
74
  "typecheck": "tsc --noEmit"
53
75
  },
54
76
  "dependencies": {
@@ -57,6 +79,8 @@
57
79
  "surrealdb": "^2.0.3"
58
80
  },
59
81
  "devDependencies": {
82
+ "@types/node": "^25.6.0",
83
+ "tsup": "^8.5.1",
60
84
  "vitest": "^3.2.4"
61
85
  },
62
86
  "peerDependencies": {
package/src/config.ts CHANGED
@@ -51,6 +51,14 @@ export interface KongBrainConfig {
51
51
  thresholds: ThresholdConfig;
52
52
  }
53
53
 
54
+ const DEFAULT_EMBEDDING_DIMENSIONS = 1024;
55
+
56
+ function parsePositiveInteger(value: unknown, fallback: number): number {
57
+ return typeof value === "number" && Number.isInteger(value) && value > 0
58
+ ? value
59
+ : fallback;
60
+ }
61
+
54
62
  function parseEmbeddingConfig(raw: Record<string, unknown>): EmbeddingConfig {
55
63
  const openaiCompatRaw = (raw.openaiCompat ?? {}) as Record<string, unknown>;
56
64
 
@@ -63,7 +71,7 @@ function parseEmbeddingConfig(raw: Record<string, unknown>): EmbeddingConfig {
63
71
 
64
72
  return {
65
73
  provider,
66
- dimensions: typeof raw.dimensions === "number" ? raw.dimensions : 1024,
74
+ dimensions: parsePositiveInteger(raw.dimensions, DEFAULT_EMBEDDING_DIMENSIONS),
67
75
  modelPath:
68
76
  process.env.EMBED_MODEL_PATH ??
69
77
  (typeof raw.modelPath === "string"
@@ -57,7 +57,7 @@ export class KongBrainContextEngine implements ContextEngine {
57
57
  readonly info: ContextEngineInfo = {
58
58
  id: "kongbrain",
59
59
  name: "KongBrain",
60
- version: "0.4.2",
60
+ version: "0.5.1",
61
61
  ownsCompaction: true,
62
62
  };
63
63
 
@@ -76,7 +76,9 @@ export class KongBrainContextEngine implements ContextEngine {
76
76
  // Run schema once per process (idempotent but expensive on every bootstrap)
77
77
  if (!this.state.schemaApplied) {
78
78
  try {
79
- const schemaSql = loadSchema();
79
+ const schemaSql = loadSchema({
80
+ embeddingDimensions: this.state.config.embedding.dimensions,
81
+ });
80
82
  await store.queryExec(schemaSql);
81
83
  this.state.schemaApplied = true;
82
84
  } catch (e) {
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ import { parsePluginConfig } from "./config.js";
12
12
  import { SurrealStore } from "./surreal.js";
13
13
  import { createEmbeddingService } from "./embeddings.js";
14
14
  import { GlobalPluginState, type CompleteFn } from "./state.js";
15
+ import { resolveModelRef } from "./model-resolution.js";
15
16
  import { KongBrainContextEngine } from "./context-engine.js";
16
17
  import { createRecallToolDef } from "./tools/recall.js";
17
18
  import { createCoreMemoryToolDef } from "./tools/core-memory.js";
@@ -333,6 +334,38 @@ async function checkEmbeddingProviderMismatch(
333
334
  }
334
335
  }
335
336
 
337
+ /**
338
+ * Detect vectors whose dimensionality doesn't match the configured dimension.
339
+ * DEFINE INDEX IF NOT EXISTS is non-destructive — changing embedding.dimensions
340
+ * after initial setup leaves the HNSW index at the old dimension while new
341
+ * vectors are written at the new size. Warn so the user knows to rebuild.
342
+ */
343
+ async function checkEmbeddingDimensionMismatch(
344
+ store: SurrealStore,
345
+ configuredDimensions: number,
346
+ logger: { warn: (msg: string) => void },
347
+ ): Promise<void> {
348
+ if (!store.isAvailable()) return;
349
+ const tables = ["concept", "memory", "turn", "artifact", "identity_chunk", "skill", "reflection", "monologue"];
350
+ for (const t of tables) {
351
+ try {
352
+ const rows = await store.queryFirst<{ len: number }>(
353
+ `SELECT array::len(embedding) AS len FROM ${t} WHERE embedding != NONE AND array::len(embedding) > 0 LIMIT 1`,
354
+ );
355
+ if (rows.length > 0 && rows[0].len !== configuredDimensions) {
356
+ logger.warn(
357
+ `Embedding dimension mismatch: existing vectors are ${rows[0].len}-dimensional but embedding.dimensions is configured as ${configuredDimensions}. ` +
358
+ `HNSW indexes created at the old dimension are not updated by DEFINE INDEX IF NOT EXISTS. ` +
359
+ `To fix: drop and recreate the vector indexes, then re-embed affected rows.`,
360
+ );
361
+ return;
362
+ }
363
+ } catch (e) {
364
+ swallow.warn(`factory:dimensionMismatchCheck:${t}`, e);
365
+ }
366
+ }
367
+ }
368
+
336
369
  export default definePluginEntry({
337
370
  id: "kongbrain",
338
371
  name: "KongBrain",
@@ -349,7 +382,9 @@ export default definePluginEntry({
349
382
  // ensure a single instance survives across module reloads.
350
383
  let globalState = getGlobalState();
351
384
  if (!globalState) {
352
- const store = new SurrealStore(config.surreal);
385
+ const store = new SurrealStore(config.surreal, {
386
+ embeddingDimensions: config.embedding.dimensions,
387
+ });
353
388
  const embeddings = createEmbeddingService(config.embedding);
354
389
  // Tag every embedding write and filter every embedding search by this
355
390
  // provider id, so vectors from different models (different vector
@@ -383,27 +418,22 @@ export default definePluginEntry({
383
418
  }
384
419
  piAi = await import(piAiPath);
385
420
  }
386
- // Fall back to calling pi-ai directly (runtime.complete not in OpenClaw 2026.3.24)
387
- const provider = params.provider ?? apiRef.runtime.agent.defaults.provider;
388
- const rawModel = params.model ?? apiRef.runtime.agent.defaults.model;
389
- // defaults.model may be an object {primary: '...', fallbacks: []} — unwrap it
390
- const modelIdRaw = typeof rawModel === 'object' && rawModel !== null
391
- ? (rawModel as any).primary ?? (rawModel as any).id ?? String(rawModel)
392
- : rawModel;
393
- // modelId may be "provider/model" format — split if provider not set
394
- let resolvedProvider = provider;
395
- let modelId = modelIdRaw;
396
- if (typeof modelId === 'string' && modelId.includes('/') && !resolvedProvider) {
397
- const idx = modelId.indexOf('/');
398
- resolvedProvider = modelId.slice(0, idx);
399
- modelId = modelId.slice(idx + 1);
400
- }
401
- const model = piAi!.getModel(resolvedProvider, modelId);
421
+ // Fall back to calling pi-ai directly (runtime.complete not in OpenClaw 2026.3.24).
422
+ // Fully-qualified OpenClaw refs (provider/model) are authoritative:
423
+ // stale runtime default providers must not override e.g.
424
+ // "openrouter/google/gemini-3-flash-preview".
425
+ const cfg = apiRef.runtime.config.loadConfig();
426
+ const resolved = resolveModelRef({
427
+ explicitProvider: params.provider,
428
+ explicitModel: params.model,
429
+ config: cfg,
430
+ runtimeDefaults: apiRef.runtime.agent.defaults,
431
+ });
432
+ const model = piAi!.getModel(resolved.provider, resolved.modelId);
402
433
  if (!model) {
403
- throw new Error(`Model "${modelId}" not found for provider "${provider}"`);
434
+ throw new Error(`Model "${resolved.modelId}" not found for provider "${resolved.provider}"`);
404
435
  }
405
436
  // Resolve auth via OpenClaw's runtime (handles profiles, env vars, etc.)
406
- const cfg = apiRef.runtime.config.loadConfig();
407
437
  const auth = await apiRef.runtime.modelAuth.getApiKeyForModel({ model, cfg });
408
438
  // Build context
409
439
  const now = Date.now();
@@ -417,7 +447,7 @@ export default definePluginEntry({
417
447
  );
418
448
  const context = { systemPrompt: params.system, messages };
419
449
  // Pass apiKey directly in options so the provider can use it
420
- log.info(`complete(): provider=${resolvedProvider} model=${modelId} msgs=${params.messages.length}`);
450
+ log.info(`complete(): provider=${resolved.provider} model=${resolved.modelId} msgs=${params.messages.length}`);
421
451
  // NOTE: outputFormat (structured output) is intentionally NOT passed to pi-ai.
422
452
  // pi-ai's SimpleStreamOptions doesn't support it, and injecting it via onPayload
423
453
  // causes the Anthropic API to return empty responses. The daemon's JSON parsing
@@ -471,6 +501,8 @@ export default definePluginEntry({
471
501
  // re-embedded with the active provider.
472
502
  checkEmbeddingProviderMismatch(store, embeddings.providerId, logger)
473
503
  .catch(e => swallow.warn("factory:providerMismatchCheck", e));
504
+ checkEmbeddingDimensionMismatch(store, config.embedding.dimensions, logger)
505
+ .catch(e => swallow.warn("factory:dimensionMismatchCheck", e));
474
506
  }
475
507
  } catch (e) {
476
508
  logger.warn(`Embeddings init failed — running in degraded mode: ${e}`);
@@ -0,0 +1,98 @@
1
+ export interface ResolveModelRefInput {
2
+ explicitProvider?: unknown;
3
+ explicitModel?: unknown;
4
+ config?: unknown;
5
+ runtimeDefaults?: unknown;
6
+ }
7
+
8
+ export interface ResolvedModelRef {
9
+ provider: string;
10
+ modelId: string;
11
+ }
12
+
13
+ function asRecord(value: unknown): Record<string, unknown> | null {
14
+ return typeof value === "object" && value !== null
15
+ ? value as Record<string, unknown>
16
+ : null;
17
+ }
18
+
19
+ function nonEmptyString(value: unknown): string | undefined {
20
+ return typeof value === "string" && value.trim().length > 0
21
+ ? value.trim()
22
+ : undefined;
23
+ }
24
+
25
+ function unwrapModelRef(value: unknown): string | undefined {
26
+ const direct = nonEmptyString(value);
27
+ if (direct) return direct;
28
+
29
+ const record = asRecord(value);
30
+ if (!record) return undefined;
31
+
32
+ return unwrapModelRef(record.primary) ?? unwrapModelRef(record.id);
33
+ }
34
+
35
+ function defaultModelFromConfig(config: unknown): string | undefined {
36
+ const cfg = asRecord(config);
37
+ const agents = asRecord(cfg?.agents);
38
+ const defaults = asRecord(agents?.defaults);
39
+ return unwrapModelRef(defaults?.model);
40
+ }
41
+
42
+ function defaultModelFromRuntime(runtimeDefaults: unknown): string | undefined {
43
+ const defaults = asRecord(runtimeDefaults);
44
+ return unwrapModelRef(defaults?.model);
45
+ }
46
+
47
+ function providerFromRuntime(runtimeDefaults: unknown): string | undefined {
48
+ const defaults = asRecord(runtimeDefaults);
49
+ return nonEmptyString(defaults?.provider);
50
+ }
51
+
52
+ function splitQualifiedModelRef(modelRef: string): { provider: string; modelId: string } | null {
53
+ const slash = modelRef.indexOf("/");
54
+ if (slash <= 0 || slash === modelRef.length - 1) return null;
55
+ return {
56
+ provider: modelRef.slice(0, slash),
57
+ modelId: modelRef.slice(slash + 1),
58
+ };
59
+ }
60
+
61
+ function chooseModelRef(input: ResolveModelRefInput): { modelRef: string; explicit: boolean } | null {
62
+ const explicit = unwrapModelRef(input.explicitModel);
63
+ if (explicit) return { modelRef: explicit, explicit: true };
64
+
65
+ const configDefault = defaultModelFromConfig(input.config);
66
+ if (configDefault) return { modelRef: configDefault, explicit: false };
67
+
68
+ const runtimeDefault = defaultModelFromRuntime(input.runtimeDefaults);
69
+ if (runtimeDefault) return { modelRef: runtimeDefault, explicit: false };
70
+
71
+ return null;
72
+ }
73
+
74
+ export function resolveModelRef(input: ResolveModelRefInput): ResolvedModelRef {
75
+ const chosen = chooseModelRef(input);
76
+
77
+ if (!chosen) {
78
+ throw new Error("No LLM model configured for KongBrain internal completion");
79
+ }
80
+
81
+ const explicitProvider = nonEmptyString(input.explicitProvider);
82
+ if (chosen.explicit && explicitProvider) {
83
+ return { provider: explicitProvider, modelId: chosen.modelRef };
84
+ }
85
+
86
+ const qualified = splitQualifiedModelRef(chosen.modelRef);
87
+ if (qualified) return qualified;
88
+
89
+ const provider =
90
+ explicitProvider ??
91
+ providerFromRuntime(input.runtimeDefaults);
92
+
93
+ if (!provider) {
94
+ throw new Error(`No LLM provider configured for model "${chosen.modelRef}"`);
95
+ }
96
+
97
+ return { provider, modelId: chosen.modelRef };
98
+ }
@@ -12,12 +12,30 @@ import { fileURLToPath } from "node:url";
12
12
 
13
13
  const __dirname = dirname(fileURLToPath(import.meta.url));
14
14
 
15
- export function loadSchema(): string {
15
+ const DEFAULT_EMBEDDING_DIMENSIONS = 1024;
16
+ const DIMENSION_PLACEHOLDER = "__KONGBRAIN_EMBEDDING_DIMENSIONS__";
17
+
18
+ export interface LoadSchemaOptions {
19
+ embeddingDimensions?: number;
20
+ }
21
+
22
+ function normalizeDimensions(value: unknown): number {
23
+ return typeof value === "number" && Number.isInteger(value) && value > 0
24
+ ? value
25
+ : DEFAULT_EMBEDDING_DIMENSIONS;
26
+ }
27
+
28
+ export function loadSchema(options: LoadSchemaOptions = {}): string {
16
29
  const primary = join(__dirname, "schema.surql");
30
+ let schema: string;
17
31
  try {
18
- return readFileSync(primary, "utf-8");
32
+ schema = readFileSync(primary, "utf-8");
19
33
  } catch {
20
34
  // Dev fallback: compiled output lives in dist/, schema source in src/
21
- return readFileSync(join(__dirname, "..", "src", "schema.surql"), "utf-8");
35
+ schema = readFileSync(join(__dirname, "..", "src", "schema.surql"), "utf-8");
22
36
  }
37
+ return schema.replaceAll(
38
+ DIMENSION_PLACEHOLDER,
39
+ String(normalizeDimensions(options.embeddingDimensions)),
40
+ );
23
41
  }
package/src/schema.surql CHANGED
@@ -42,7 +42,7 @@ DEFINE FIELD IF NOT EXISTS embedding ON artifact TYPE option<array<float>>;
42
42
  DEFINE FIELD IF NOT EXISTS embedding_provider ON artifact TYPE option<string>;
43
43
  DEFINE FIELD IF NOT EXISTS tags ON artifact TYPE option<array>;
44
44
  DEFINE FIELD IF NOT EXISTS created_at ON artifact TYPE datetime DEFAULT time::now();
45
- DEFINE INDEX IF NOT EXISTS artifact_vec_idx ON artifact FIELDS embedding HNSW DIMENSION 1024 DIST COSINE;
45
+ DEFINE INDEX IF NOT EXISTS artifact_vec_idx ON artifact FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
46
46
  DEFINE INDEX IF NOT EXISTS artifact_type_idx ON artifact FIELDS type;
47
47
  DEFINE INDEX IF NOT EXISTS artifact_emb_provider_idx ON artifact FIELDS embedding_provider;
48
48
 
@@ -62,7 +62,7 @@ DEFINE FIELD IF NOT EXISTS tags ON concept TYPE option<array>;
62
62
  DEFINE FIELD IF NOT EXISTS source ON concept TYPE option<string>;
63
63
  DEFINE FIELD IF NOT EXISTS created_at ON concept TYPE datetime DEFAULT time::now();
64
64
  DEFINE FIELD IF NOT EXISTS last_accessed ON concept TYPE option<datetime>;
65
- DEFINE INDEX IF NOT EXISTS concept_vec_idx ON concept FIELDS embedding HNSW DIMENSION 1024 DIST COSINE;
65
+ DEFINE INDEX IF NOT EXISTS concept_vec_idx ON concept FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
66
66
  DEFINE INDEX IF NOT EXISTS concept_emb_provider_idx ON concept FIELDS embedding_provider;
67
67
 
68
68
  -- ============================================================
@@ -82,7 +82,7 @@ DEFINE FIELD IF NOT EXISTS token_count ON turn TYPE option<int>;
82
82
  DEFINE FIELD IF NOT EXISTS tool_name ON turn TYPE option<string>;
83
83
  DEFINE FIELD IF NOT EXISTS model ON turn TYPE option<string>;
84
84
  DEFINE FIELD IF NOT EXISTS usage ON turn TYPE option<object>;
85
- DEFINE INDEX IF NOT EXISTS turn_vec_idx ON turn FIELDS embedding HNSW DIMENSION 1024 DIST COSINE;
85
+ DEFINE INDEX IF NOT EXISTS turn_vec_idx ON turn FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
86
86
  DEFINE INDEX IF NOT EXISTS turn_session_idx ON turn FIELDS session_id;
87
87
  DEFINE INDEX IF NOT EXISTS turn_tool_name_idx ON turn FIELDS tool_name;
88
88
  DEFINE INDEX IF NOT EXISTS turn_emb_provider_idx ON turn FIELDS embedding_provider;
@@ -96,7 +96,7 @@ DEFINE FIELD IF NOT EXISTS text ON identity_chunk TYPE string;
96
96
  DEFINE FIELD IF NOT EXISTS embedding ON identity_chunk TYPE option<array<float>>;
97
97
  DEFINE FIELD IF NOT EXISTS embedding_provider ON identity_chunk TYPE option<string>;
98
98
  DEFINE FIELD IF NOT EXISTS importance ON identity_chunk TYPE float DEFAULT 0.5;
99
- DEFINE INDEX IF NOT EXISTS identity_vec_idx ON identity_chunk FIELDS embedding HNSW DIMENSION 1024 DIST COSINE;
99
+ DEFINE INDEX IF NOT EXISTS identity_vec_idx ON identity_chunk FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
100
100
  DEFINE INDEX IF NOT EXISTS identity_emb_provider_idx ON identity_chunk FIELDS embedding_provider;
101
101
 
102
102
  -- Sessions (lightweight, links to task for 5-pillar integration)
@@ -125,7 +125,7 @@ DEFINE FIELD IF NOT EXISTS last_accessed ON memory TYPE option<datetime>;
125
125
  DEFINE FIELD IF NOT EXISTS status ON memory TYPE option<string> DEFAULT "active";
126
126
  DEFINE FIELD IF NOT EXISTS resolved_at ON memory TYPE option<datetime>;
127
127
  DEFINE FIELD IF NOT EXISTS resolved_by ON memory TYPE option<string>;
128
- DEFINE INDEX IF NOT EXISTS memory_vec_idx ON memory FIELDS embedding HNSW DIMENSION 1024 DIST COSINE;
128
+ DEFINE INDEX IF NOT EXISTS memory_vec_idx ON memory FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
129
129
  DEFINE INDEX IF NOT EXISTS memory_category_idx ON memory FIELDS category;
130
130
  DEFINE INDEX IF NOT EXISTS memory_emb_provider_idx ON memory FIELDS embedding_provider;
131
131
 
@@ -276,7 +276,7 @@ DEFINE FIELD IF NOT EXISTS failure_count ON skill TYPE int DEFAULT 0;
276
276
  DEFINE FIELD IF NOT EXISTS avg_duration_ms ON skill TYPE float DEFAULT 0.0;
277
277
  DEFINE FIELD IF NOT EXISTS last_used ON skill TYPE option<datetime>;
278
278
  DEFINE FIELD IF NOT EXISTS created_at ON skill TYPE datetime DEFAULT time::now();
279
- DEFINE INDEX IF NOT EXISTS skill_vec_idx ON skill FIELDS embedding HNSW DIMENSION 1024 DIST COSINE;
279
+ DEFINE INDEX IF NOT EXISTS skill_vec_idx ON skill FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
280
280
  DEFINE INDEX IF NOT EXISTS skill_active_idx ON skill FIELDS active;
281
281
  DEFINE INDEX IF NOT EXISTS skill_emb_provider_idx ON skill FIELDS embedding_provider;
282
282
 
@@ -296,7 +296,7 @@ DEFINE FIELD IF NOT EXISTS severity ON reflection TYPE string DEFAULT "minor"; -
296
296
  DEFINE FIELD IF NOT EXISTS importance ON reflection TYPE float DEFAULT 7.0;
297
297
  DEFINE FIELD IF NOT EXISTS access_count ON reflection TYPE int DEFAULT 0;
298
298
  DEFINE FIELD IF NOT EXISTS created_at ON reflection TYPE datetime DEFAULT time::now();
299
- DEFINE INDEX IF NOT EXISTS reflection_vec_idx ON reflection FIELDS embedding HNSW DIMENSION 1024 DIST COSINE;
299
+ DEFINE INDEX IF NOT EXISTS reflection_vec_idx ON reflection FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
300
300
  DEFINE INDEX IF NOT EXISTS reflection_emb_provider_idx ON reflection FIELDS embedding_provider;
301
301
 
302
302
  DEFINE TABLE IF NOT EXISTS reflects_on TYPE RELATION IN reflection OUT session;
@@ -347,7 +347,7 @@ DEFINE FIELD IF NOT EXISTS embedding_provider ON monologue TYPE option<string>;
347
347
  DEFINE FIELD IF NOT EXISTS timestamp ON monologue TYPE datetime DEFAULT time::now();
348
348
  DEFINE INDEX IF NOT EXISTS monologue_session ON monologue FIELDS session_id;
349
349
  DEFINE INDEX IF NOT EXISTS monologue_category ON monologue FIELDS category;
350
- DEFINE INDEX IF NOT EXISTS monologue_vec_idx ON monologue FIELDS embedding HNSW DIMENSION 1024 DIST COSINE;
350
+ DEFINE INDEX IF NOT EXISTS monologue_vec_idx ON monologue FIELDS embedding HNSW DIMENSION __KONGBRAIN_EMBEDDING_DIMENSIONS__ DIST COSINE;
351
351
  DEFINE INDEX IF NOT EXISTS monologue_emb_provider_idx ON monologue FIELDS embedding_provider;
352
352
 
353
353
  -- Fibonacci resurfacing: proactive memory that fades over time
package/src/surreal.ts CHANGED
@@ -47,6 +47,10 @@ export interface UtilityCacheEntry {
47
47
  retrieval_count: number;
48
48
  }
49
49
 
50
+ export interface SurrealStoreOptions {
51
+ embeddingDimensions?: number;
52
+ }
53
+
50
54
  const RECORD_ID_RE = /^[a-zA-Z_][a-zA-Z0-9_]*:[a-zA-Z0-9_]+$/;
51
55
 
52
56
  function assertRecordId(id: string): void {
@@ -132,9 +136,11 @@ export class SurrealStore {
132
136
  * deployments keep working if the wire-up step is ever skipped.
133
137
  */
134
138
  private activeProvider: string = "local-bge-m3";
139
+ private schemaOptions: SurrealStoreOptions;
135
140
 
136
- constructor(config: SurrealConfig) {
141
+ constructor(config: SurrealConfig, options: SurrealStoreOptions = {}) {
137
142
  this.config = config;
143
+ this.schemaOptions = options;
138
144
  this.db = new Surreal();
139
145
  }
140
146
 
@@ -216,7 +222,9 @@ export class SurrealStore {
216
222
  }
217
223
 
218
224
  private async runSchema(): Promise<void> {
219
- const schema = loadSchema();
225
+ const schema = loadSchema({
226
+ embeddingDimensions: this.schemaOptions.embeddingDimensions,
227
+ });
220
228
  await this.db.query(schema);
221
229
  }
222
230