suemo 0.1.7 → 0.1.9

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/README.md CHANGED
@@ -11,7 +11,25 @@
11
11
 
12
12
  suemo gives AI agents a memory that survives across sessions, models, and runtimes. Write observations from a Telegram bot, query them from OpenCode, consolidate overnight — all agents share one source of truth in SurrealDB.
13
13
 
14
- Canonical behavioral specification: **[`SPEC.md`](./SPEC.md)**.
14
+ Specifications:
15
+
16
+ - Index: **[`specs/SPEC.md`](./specs/SPEC.md)**
17
+ - Technical/runtime contract: **[`specs/SPEC-technical.md`](./specs/SPEC-technical.md)**
18
+ - Psychological/critique framing: **[`specs/SPEC-psychological.md`](./specs/SPEC-psychological.md)**
19
+
20
+ ## For professional critique authors (longevity-focused)
21
+
22
+ If you're designing high-quality external critiques for suemo, evaluate it as **persistent coding-agent memory infrastructure**, not as a general AGI cognition claim.
23
+
24
+ Use this framing:
25
+
26
+ 1. **Psychological intent** — what memory quality should hold over time?
27
+ 2. **Technical mechanism** — what exact table/field/tool enforces that quality?
28
+ 3. **Operational evidence** — what logs/tests/metrics prove it under drift and contradiction?
29
+
30
+ Questions are most useful when they stress long-horizon behavior (30–180 day operation), contradiction handling, consolidation quality, retrieval interference, and scope/session isolation.
31
+
32
+ For rigorous critique language and category framing, start from **[`specs/SPEC-psychological.md`](./specs/SPEC-psychological.md)**.
15
33
 
16
34
  ---
17
35
 
@@ -466,7 +484,7 @@ Current behavior: only write paths invoked with `sessionId` append to `memory_id
466
484
  - CLI: `suemo believe "..." --session <sessionId>`
467
485
  - MCP: `believe({ content, sessionId, ... })`
468
486
 
469
- See `SPEC.md` for full normative semantics and hardening targets.
487
+ See `specs/SPEC-technical.md` for full normative semantics and hardening targets.
470
488
 
471
489
  ### Scope and longevity notes
472
490
 
@@ -526,7 +544,7 @@ These scenarios intentionally exercise all memory kinds and all relation kinds,
526
544
  - **Runtime** — [Bun](https://bun.sh)
527
545
  - **Language** — TypeScript (strict, `exactOptionalPropertyTypes`)
528
546
  - **Database** — [SurrealDB](https://surrealdb.com) v3.0+ (SurrealKV)
529
- - **SDK** — [surrealdb.js](https://github.com/surrealdb/surrealdb.js) v2 + `@surrealdb/node`
547
+ - **SDK** — [surrealdb.js](https://github.com/surrealdb/surrealdb.js) v2 + `@mdrv/surreal-node` (fork of `@surrealdb/node`)
530
548
  - **CLI** — [crust](https://github.com/chenxin-yan/crust) (`@crustjs/core`)
531
549
  - **HTTP / MCP** — [Elysia](https://elysiajs.com) v1.4
532
550
  - **Validation** — [Zod](https://zod.dev) v4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suemo",
3
- "version": "0.1.7",
3
+ "version": "0.1.9",
4
4
  "description": "Persistent semantic memory for AI agents — backed by SurrealDB.",
5
5
  "author": {
6
6
  "name": "Umar Alfarouk",
@@ -37,10 +37,11 @@
37
37
  "scripts": {
38
38
  "dev": "bun run src/cli/index.ts",
39
39
  "start": "bun run src/cli/index.ts",
40
+ "test": "bun test",
40
41
  "ssot:check": "bun run scripts/ssot.ts check",
41
42
  "ssot:sync": "bun run scripts/ssot.ts sync",
42
43
  "fmt": "dprint fmt",
43
- "check": "bun run fmt && bun tsc --noEmit && bun run ssot:check",
44
+ "check": "bun run fmt && bun tsc --noEmit && bun test && bun run ssot:check",
44
45
  "sync": "bun run ssot:sync"
45
46
  },
46
47
  "dependencies": {
@@ -50,12 +51,13 @@
50
51
  "@crustjs/style": "^0.0.6",
51
52
  "@logtape/file": "^2.0.4",
52
53
  "@logtape/logtape": "^2.0.4",
53
- "@surrealdb/node": "^3.0.3",
54
+ "@mdrv/surreal-node": "latest",
54
55
  "elysia": "^1.4.28",
55
56
  "surrealdb": "^2.0.3",
56
57
  "zod": "^4.3.6"
57
58
  },
58
59
  "devDependencies": {
60
+ "@opencode-ai/plugin": "^1.3.0",
59
61
  "@types/bun": "^1.3.11",
60
62
  "typescript": "^5.9.3"
61
63
  }
@@ -3,14 +3,14 @@ name: suemo
3
3
  description: OpenCode-focused persistent memory workflow for suemo with CLI/MCP parity and versioned references.
4
4
  license: GPL-3.0-only
5
5
  compatibility: opencode
6
- version: 0.1.7
6
+ version: 0.1.9
7
7
  ---
8
8
 
9
9
  # suemo skill
10
10
 
11
11
  Use suemo to persist technical context across sessions with minimal, project-scoped memory.
12
12
 
13
- ## Strict defaults (v0.1.7)
13
+ ## Strict defaults (v0.1.8)
14
14
 
15
15
  - Always run pre-checks before implementation:
16
16
  - `goal_list({ scope })`
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agents-snippet
3
3
  description: AGENTS.md snippet optimized for suemo skill discovery and usage.
4
- version: 0.1.7
4
+ version: 0.1.9
5
5
  ---
6
6
 
7
7
  # AGENTS.md snippet
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: cli-reference
3
3
  description: CLI command reference for suemo v0.0.6 including skill access.
4
- version: 0.1.7
4
+ version: 0.1.9
5
5
  ---
6
6
 
7
7
  # CLI reference
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: core-workflow
3
3
  description: Canonical suemo operating loop for OpenCode agents.
4
- version: 0.1.7
4
+ version: 0.1.9
5
5
  ---
6
6
 
7
7
  # Core workflow
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: manual-test-plan
3
3
  description: Comprehensive manual test matrix for suemo features and commands.
4
- version: 0.1.7
4
+ version: 0.1.9
5
5
  ---
6
6
 
7
7
  # Manual test plan
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: mcp-reference
3
3
  description: MCP tool reference for suemo v0.0.6.
4
- version: 0.1.7
4
+ version: 0.1.9
5
5
  ---
6
6
 
7
7
  # MCP tools
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: schema-retention-longevity
3
3
  description: Long-term schema and retention design expectations for suemo.
4
- version: 0.1.7
4
+ version: 0.1.9
5
5
  ---
6
6
 
7
7
  # Schema + retention longevity
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: sync-local-vps
3
3
  description: Two-way sync manual scenario for local and VPS SurrealDB.
4
- version: 0.1.7
4
+ version: 0.1.9
5
5
  ---
6
6
 
7
7
  # Local ↔ VPS sync scenario
package/src/AGENTS.md CHANGED
@@ -127,4 +127,4 @@ Load docs once per session unless requirements changed.
127
127
 
128
128
  ---
129
129
 
130
- _Version: 0.1.7 | Updated: 2026-03-23 | Summary: stricter schema + explicit completion contract_
130
+ _Version: 0.1.9 | Updated: 2026-03-24 | Summary: stricter schema + explicit completion contract_
@@ -29,7 +29,7 @@ const reportCmd = health.sub('report')
29
29
  let db: Surreal | undefined
30
30
  try {
31
31
  db = await connect(config.surreal)
32
- const report = await healthReport(db)
32
+ const report = await healthReport(db, { embeddingProvider: config.embedding.provider })
33
33
  if (outputMode === 'json') {
34
34
  printCliJson(report, flags)
35
35
  } else {
@@ -129,7 +129,7 @@ export const healthCmd = health
129
129
  let db: Surreal | undefined
130
130
  try {
131
131
  db = await connect(config.surreal)
132
- const report = await healthReport(db)
132
+ const report = await healthReport(db, { embeddingProvider: config.embedding.provider })
133
133
  if (outputMode === 'json') {
134
134
  printCliJson(report, flags)
135
135
  } else {
@@ -10,6 +10,9 @@ export const recallCmd = app.sub('recall')
10
10
  .meta({ description: 'Fetch a single node + its neighbours (ticks FSRS)' })
11
11
  .args([{ name: 'nodeId', type: 'string', required: true }])
12
12
  .flags({
13
+ rating: { type: 'number', short: 'r', description: 'FSRS rating (1=Again,2=Hard,3=Good,4=Easy)' },
14
+ at: { type: 'string', description: 'ISO datetime override for deterministic replay/testing' },
15
+ 'dry-run': { type: 'boolean', description: 'Compute FSRS schedule without writing', default: false },
13
16
  json: { type: 'boolean', description: 'Output full JSON' },
14
17
  pretty: { type: 'boolean', description: 'Human-readable output (default)' },
15
18
  })
@@ -25,13 +28,38 @@ export const recallCmd = app.sub('recall')
25
28
  const config = await loadConfig(process.cwd(), flags.config)
26
29
  const db = await connect(config.surreal)
27
30
  try {
28
- const result = await recall(db, args.nodeId)
31
+ const rating = flags.rating === undefined ? undefined : Number(flags.rating)
32
+ if (rating !== undefined && ![1, 2, 3, 4].includes(rating)) {
33
+ throw new Error(`Invalid --rating value: ${flags.rating}. Expected one of: 1,2,3,4`)
34
+ }
35
+ if (flags.at) {
36
+ const atDate = new Date(String(flags.at))
37
+ if (Number.isNaN(atDate.getTime())) {
38
+ throw new Error(`Invalid --at datetime: ${flags.at}`)
39
+ }
40
+ }
41
+
42
+ const result = await recall(db, args.nodeId, {
43
+ ...(rating !== undefined ? { rating: rating as 1 | 2 | 3 | 4 } : {}),
44
+ ...(flags.at ? { at: String(flags.at) } : {}),
45
+ dryRun: Boolean(flags['dry-run']),
46
+ })
47
+ log.debug('recall command completed', {
48
+ nodeId: args.nodeId,
49
+ rating: result.fsrs.rating,
50
+ nextReview: result.fsrs.nextReview,
51
+ stateAfter: result.fsrs.stateAfter,
52
+ dryRun: result.fsrs.dryRun,
53
+ })
29
54
  if (outputMode === 'json') {
30
55
  printCliJson(result, flags)
31
56
  } else {
32
57
  console.log(`[${result.node.kind}] ${result.node.id}`)
33
58
  console.log(` ${result.node.content.slice(0, 120)}`)
34
59
  console.log(` neighbors: ${result.neighbors.length}`)
60
+ console.log(` fsrs: rating=${result.fsrs.rating} interval=${result.fsrs.intervalDays}d`)
61
+ console.log(` next_review: ${result.fsrs.nextReview} (${result.fsrs.stateAfter})`)
62
+ if (result.fsrs.dryRun) console.log(' [dry-run] no FSRS state written')
35
63
  }
36
64
  } finally {
37
65
  await disconnect()
@@ -192,12 +192,24 @@ export const serveCmd = app.sub('serve')
192
192
  }
193
193
  if (flags.stdio) {
194
194
  log.debug('Starting MCP stdio transport')
195
+ log.debug('Serve transport resolved', {
196
+ transport: 'stdio',
197
+ embeddingProvider: config.embedding.provider,
198
+ autoSyncEnabled: Boolean(sync?.auto.enabled),
199
+ })
195
200
  await runServerWithDevRetry({ stdio: true, config })
196
201
  return
197
202
  }
198
203
  if (flags.port) config.mcp.port = flags.port
199
204
  if (flags.host) config.mcp.host = flags.host
200
205
  log.debug('Starting MCP HTTP transport', { host: config.mcp.host, port: config.mcp.port })
206
+ log.debug('Serve transport resolved', {
207
+ transport: 'http',
208
+ host: config.mcp.host,
209
+ port: config.mcp.port,
210
+ embeddingProvider: config.embedding.provider,
211
+ autoSyncEnabled: Boolean(sync?.auto.enabled),
212
+ })
201
213
  await runServerWithDevRetry({ stdio: false, config })
202
214
  // Server runs indefinitely — no disconnect
203
215
  })
@@ -168,7 +168,12 @@ async function runNREM(
168
168
  consolidated_into: NONE,
169
169
  fsrs_stability: NONE,
170
170
  fsrs_difficulty: NONE,
171
- fsrs_next_review: NONE
171
+ fsrs_next_review: NONE,
172
+ fsrs_state: NONE,
173
+ fsrs_last_review: NONE,
174
+ fsrs_reps: NONE,
175
+ fsrs_lapses: NONE,
176
+ fsrs_last_grade: NONE
172
177
  }
173
178
  `,
174
179
  {
@@ -1,3 +1,4 @@
1
+ import type { EmbeddingProvider } from '@/src/config.ts'
1
2
  import { checkCompatibility } from '@/src/db/preflight.ts'
2
3
  import { getLogger } from '@/src/logger.ts'
3
4
  import type { ConsolidationRun, HealthReport, SuemoStats } from '@/src/types.ts'
@@ -5,8 +6,14 @@ import type { Surreal } from 'surrealdb'
5
6
 
6
7
  const log = getLogger(['suemo', 'cognitive', 'health'])
7
8
 
8
- export async function healthReport(db: Surreal): Promise<HealthReport> {
9
- log.debug('healthReport()')
9
+ export async function healthReport(
10
+ db: Surreal,
11
+ options: { embeddingProvider?: EmbeddingProvider['provider'] } = {},
12
+ ): Promise<HealthReport> {
13
+ log.debug('healthReport()', {
14
+ embeddingProvider: options.embeddingProvider ?? null,
15
+ context: 'health:report',
16
+ })
10
17
 
11
18
  const [
12
19
  totalResult,
@@ -61,9 +68,24 @@ export async function healthReport(db: Surreal): Promise<HealthReport> {
61
68
  'SELECT * FROM consolidation_run ORDER BY started_at DESC LIMIT 1',
62
69
  ),
63
70
  // compat check (non-blocking)
64
- checkCompatibility(db),
71
+ checkCompatibility(db, {
72
+ ...(options.embeddingProvider ? { embeddingProvider: options.embeddingProvider } : {}),
73
+ context: 'health:report',
74
+ }),
65
75
  ])
66
76
 
77
+ log.debug('healthReport() query batches complete', {
78
+ totalRows: totalResult[0]?.length ?? 0,
79
+ activeRows: activeResult[0]?.length ?? 0,
80
+ consolidatedRows: consolidatedResult[0]?.length ?? 0,
81
+ byKindRows: byKindResult[0]?.length ?? 0,
82
+ byScopeRows: byScopeResult[0]?.length ?? 0,
83
+ relationRows: relationCountResult[0]?.length ?? 0,
84
+ goalsRows: activeGoalsResult[0]?.length ?? 0,
85
+ fsrsDueRows: frssDueResult[0]?.length ?? 0,
86
+ compatOk: compatResult.ok,
87
+ })
88
+
67
89
  const byKind: Record<string, number> = {}
68
90
  for (const row of byKindResult[0] ?? []) byKind[row.kind] = row.count
69
91
 
package/src/db/client.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  // createNodeEngines() patches in the Node.js WebSocket implementation.
4
4
  import type { SurrealTarget } from '@/src/config.ts'
5
5
  import { getLogger } from '@/src/logger.ts'
6
- import { createNodeEngines } from '@surrealdb/node'
6
+ import { createNodeEngines } from '@mdrv/surreal-node'
7
7
  import { createRemoteEngines, Surreal } from 'surrealdb'
8
8
 
9
9
  const log = getLogger(['suemo', 'db', 'client'])
@@ -18,6 +18,11 @@ export interface CompatibilityOptions {
18
18
  * Keep true for runtime paths that execute vector/embed queries.
19
19
  */
20
20
  requireEmbedding?: boolean
21
+ /**
22
+ * Embedding provider profile for this runtime.
23
+ * When set, requireEmbedding defaults to true only for surrealml.
24
+ */
25
+ embeddingProvider?: 'surrealml' | 'openai-compatible' | 'stub'
21
26
  /**
22
27
  * Human-readable label for logging this preflight execution.
23
28
  */
@@ -35,10 +40,12 @@ export async function checkCompatibility(
35
40
  let surrealkv = false
36
41
  let retention_ok = false
37
42
  let embedding = false
38
- const requireEmbedding = options.requireEmbedding ?? true
43
+ const embeddingProvider = options.embeddingProvider
44
+ const requireEmbedding = options.requireEmbedding
45
+ ?? (embeddingProvider ? embeddingProvider === 'surrealml' : true)
39
46
  const context = options.context ?? 'default'
40
47
 
41
- log.info('Running preflight compatibility checks', { requireEmbedding, context })
48
+ log.info('Running preflight compatibility checks', { requireEmbedding, embeddingProvider, context })
42
49
 
43
50
  // ── Check 1: version string ───────────────────────────────────────────────
44
51
  try {
@@ -126,31 +133,41 @@ export async function checkCompatibility(
126
133
  // ── Check 4: fn::embed() resolves ────────────────────────────────────────
127
134
  // We don't actually embed anything — we just check that the function exists.
128
135
  // An "Unknown function" error means embedding runtime is unavailable.
129
- try {
130
- // fn::embed requires the ML module and a configured model.
131
- // If it throws "No embedding model configured" that's acceptable — the
132
- // function exists. If it throws "Unknown function 'fn::embed'" → not available.
133
- await db.query(`RETURN fn::embed("suemo preflight test")`)
134
- embedding = true
135
- log.debug('Embedding function probe passed')
136
- } catch (e: unknown) {
137
- const msg = String(e).toLowerCase()
138
- if (msg.includes('unknown function') || msg.includes('fn::embed')) {
139
- embedding = false
140
- if (requireEmbedding) {
141
- errors.push(
142
- 'fn::embed() is not available in this SurrealDB database. Import/configure a SurrealML embedding model for this namespace/database, then retry.',
143
- )
136
+ if (!requireEmbedding && embeddingProvider && embeddingProvider !== 'surrealml') {
137
+ embedding = false
138
+ log.info('Skipping fn::embed preflight check for non-surrealml provider', {
139
+ context,
140
+ embeddingProvider,
141
+ })
142
+ } else {
143
+ try {
144
+ // fn::embed requires the ML module and a configured model.
145
+ // If it throws "No embedding model configured" that's acceptable — the
146
+ // function exists. If it throws "Unknown function 'fn::embed'" → not available.
147
+ await db.query(`RETURN fn::embed("suemo preflight test")`)
148
+ embedding = true
149
+ log.debug('Embedding function probe passed')
150
+ } catch (e: unknown) {
151
+ const msg = String(e).toLowerCase()
152
+ if (msg.includes('unknown function') || msg.includes('fn::embed')) {
153
+ embedding = false
154
+ if (requireEmbedding) {
155
+ errors.push(
156
+ 'fn::embed() is not available in this SurrealDB database. Import/configure a SurrealML embedding model for this namespace/database, then retry.',
157
+ )
158
+ } else {
159
+ log.warn('Embedding function unavailable; continuing due to non-strict preflight mode', {
160
+ context,
161
+ error: String(e),
162
+ })
163
+ }
144
164
  } else {
145
- log.warn('Embedding function unavailable; continuing due to non-strict preflight mode', {
146
- context,
165
+ // Other error (e.g. no model configured but function exists) treat as available
166
+ embedding = true
167
+ log.debug('fn::embed() exists but returned an error (model may not be configured yet)', {
147
168
  error: String(e),
148
169
  })
149
170
  }
150
- } else {
151
- // Other error (e.g. no model configured but function exists) — treat as available
152
- embedding = true
153
- log.debug('fn::embed() exists but returned an error (model may not be configured yet)', { error: String(e) })
154
171
  }
155
172
  }
156
173
 
@@ -158,9 +175,18 @@ export async function checkCompatibility(
158
175
  const embedSkipped = !requireEmbedding && !embedding
159
176
 
160
177
  if (ok) {
161
- log.info('All preflight checks passed', { surrealVersion, surrealkv, retention_ok, embedding })
178
+ log.info('All preflight checks passed', {
179
+ surrealVersion,
180
+ surrealkv,
181
+ retention_ok,
182
+ embedding,
183
+ embeddingProvider,
184
+ })
162
185
  if (embedSkipped) {
163
- log.info('fn::embed preflight check skipped due to non-surrealml embedding profile', { context })
186
+ log.info('fn::embed preflight check skipped due to non-surrealml embedding profile', {
187
+ context,
188
+ embeddingProvider,
189
+ })
164
190
  }
165
191
  } else {
166
192
  log.error('Preflight checks failed', { errors })
@@ -172,8 +198,11 @@ export async function checkCompatibility(
172
198
  /**
173
199
  * Hard-exit variant. Call in CLI commands; throw in MCP server startup.
174
200
  */
175
- export async function requireCompatibility(db: Surreal): Promise<void> {
176
- const result = await checkCompatibility(db)
201
+ export async function requireCompatibility(
202
+ db: Surreal,
203
+ options: CompatibilityOptions = {},
204
+ ): Promise<void> {
205
+ const result = await checkCompatibility(db, options)
177
206
  if (!result.ok) {
178
207
  const details = result.errors.map((err) => ` ✗ ${err}`).join('\n')
179
208
  throw new Error(`\n[suemo] Compatibility check failed:\n\n${details}\n\nFix the issues above and retry.\n`)
@@ -38,6 +38,13 @@ DEFINE FIELD OVERWRITE consolidated_into ON memory
38
38
  DEFINE FIELD OVERWRITE fsrs_stability ON memory TYPE option<float>;
39
39
  DEFINE FIELD OVERWRITE fsrs_difficulty ON memory TYPE option<float>;
40
40
  DEFINE FIELD OVERWRITE fsrs_next_review ON memory TYPE option<datetime>;
41
+ DEFINE FIELD OVERWRITE fsrs_state ON memory TYPE option<string>
42
+ ASSERT $value = NONE OR $value INSIDE ['new','learning','review','relearning'];
43
+ DEFINE FIELD OVERWRITE fsrs_last_review ON memory TYPE option<datetime>;
44
+ DEFINE FIELD OVERWRITE fsrs_reps ON memory TYPE option<int>;
45
+ DEFINE FIELD OVERWRITE fsrs_lapses ON memory TYPE option<int>;
46
+ DEFINE FIELD OVERWRITE fsrs_last_grade ON memory TYPE option<int>
47
+ ASSERT $value = NONE OR $value INSIDE [1,2,3,4];
41
48
 
42
49
  -- Vector index: HNSW (keep syntax compatible with current SurrealDB target)
43
50
  DEFINE INDEX OVERWRITE idx_memory_embedding
@@ -110,8 +110,23 @@ export async function handleToolCall(
110
110
  }
111
111
 
112
112
  case 'recall': {
113
- const parsed = z.object({ nodeId: z.string() }).parse(params)
114
- return recall(db, parsed.nodeId)
113
+ const parsed = z.object({
114
+ nodeId: z.string(),
115
+ rating: z.number().int().min(1).max(4).optional(),
116
+ at: z.string().optional(),
117
+ dryRun: z.boolean().optional(),
118
+ }).parse(params)
119
+ log.debug('Dispatch recall tool', {
120
+ nodeId: parsed.nodeId,
121
+ rating: parsed.rating ?? 3,
122
+ hasAt: parsed.at !== undefined,
123
+ dryRun: parsed.dryRun ?? false,
124
+ })
125
+ return recall(db, parsed.nodeId, {
126
+ ...(parsed.rating !== undefined ? { rating: parsed.rating as 1 | 2 | 3 | 4 } : {}),
127
+ ...(parsed.at !== undefined ? { at: parsed.at } : {}),
128
+ ...(parsed.dryRun !== undefined ? { dryRun: parsed.dryRun } : {}),
129
+ })
115
130
  }
116
131
 
117
132
  case 'wander': {
@@ -143,9 +158,20 @@ export async function handleToolCall(
143
158
  }
144
159
 
145
160
  case 'context': {
146
- const parsed = z.object({ scope: z.string().optional(), limit: z.number().optional() }).parse(params)
161
+ const parsed = z.object({
162
+ scope: z.string().optional(),
163
+ sessionId: z.string().optional(),
164
+ limit: z.number().optional(),
165
+ }).parse(params)
166
+ const resolvedScope = parsed.scope?.trim() || inferredScope
167
+ log.debug('Dispatch context tool', {
168
+ scope: resolvedScope,
169
+ sessionId: parsed.sessionId ?? null,
170
+ limit: parsed.limit ?? null,
171
+ })
147
172
  return context(db, {
148
- scope: parsed.scope?.trim() || inferredScope,
173
+ scope: resolvedScope,
174
+ ...(parsed.sessionId !== undefined ? { sessionId: parsed.sessionId } : {}),
149
175
  ...(parsed.limit !== undefined ? { limit: parsed.limit } : {}),
150
176
  })
151
177
  }
@@ -205,7 +231,7 @@ export async function handleToolCall(
205
231
  }
206
232
 
207
233
  case 'health':
208
- return healthReport(db)
234
+ return healthReport(db, { embeddingProvider: config.embedding.provider })
209
235
 
210
236
  case 'stats':
211
237
  return suemoStats(db)
package/src/mcp/server.ts CHANGED
@@ -127,7 +127,14 @@ export async function startMcpServer(config: SuemoConfig): Promise<void> {
127
127
  projectDir: config.main?.projectDir ?? '.ua',
128
128
  })
129
129
  const db = await connect(config.surreal)
130
- await requireCompatibility(db)
130
+ log.debug('Running MCP HTTP compatibility preflight', {
131
+ embeddingProvider: config.embedding.provider,
132
+ context: 'mcp:http-startup',
133
+ })
134
+ await requireCompatibility(db, {
135
+ embeddingProvider: config.embedding.provider,
136
+ context: 'mcp:http-startup',
137
+ })
131
138
  await runSchema(db)
132
139
  const autoSync = createAutoSyncRunner(db, config)
133
140
  autoSync.start()
@@ -163,7 +170,13 @@ export async function startMcpStdioServer(config: SuemoConfig): Promise<void> {
163
170
  const autoSync = createAutoSyncRunner(db, config)
164
171
  autoSync.start()
165
172
  try {
173
+ log.debug('Running MCP stdio compatibility preflight', {
174
+ embeddingProvider: config.embedding.provider,
175
+ requireEmbedding: config.embedding.provider === 'surrealml',
176
+ context: 'mcp:stdio-startup',
177
+ })
166
178
  const compat = await checkCompatibility(db, {
179
+ embeddingProvider: config.embedding.provider,
167
180
  requireEmbedding: config.embedding.provider === 'surrealml',
168
181
  context: 'mcp:stdio-startup',
169
182
  })