memorix 1.9.6 → 1.9.8

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.
@@ -55,7 +55,7 @@ Generated guidance also has scope:
55
55
  | Cursor | `memorix setup --agent cursor --global` | Cursor MCP and rules config | Cursor MCP config, `.cursor/rules/memorix.mdc`, skills, and hook guidance | Reload Cursor after setup so it can pick up project config changes. |
56
56
  | Grok Build | `memorix setup --agent grok --global` | `~/.grok/hooks/memorix.json` + `~/.grok/AGENTS.md` | Lifecycle hooks on SessionStart, SessionEnd, UserPromptSubmit, PostToolUse, PostToolUseFailure, PreCompact, PostCompact, Stop, and StopFailure, plus Grok project/global guidance | Uses Grok's own JSON hook directory. MCP stays host-owned unless you pass `--mcp http`, which writes `[mcp_servers.memorix]` with `url = "http://localhost:3211/mcp"` in Grok `config.toml`. Global hooks are trusted; project hooks require Grok `/hooks-trust`. Set `GROK_HOME` to use a custom Grok home. The command is `memorix hook --agent grok`. |
57
57
  | Gemini CLI | `memorix setup --agent gemini-cli --global` | Gemini CLI extension | Extension under `~/.gemini/extensions/memorix` with MCP, `GEMINI.md` context, hooks, commands, and skills | Antigravity CLI has an official Gemini CLI migration path, but Gemini CLI remains an active standalone Google CLI target. |
58
- | OpenCode | `memorix setup --agent opencode --global` | OpenCode local plugin file | Global setup writes `~/.config/opencode/plugins/memorix.js`, `~/.config/opencode/opencode.json`, skills, and `AGENTS.md` guidance; repo-local setup writes the `.opencode/` equivalents. | OpenCode loads local plugin and skill files from its config scope. |
58
+ | OpenCode | `memorix setup --agent opencode --global` | OpenCode local plugin file | Global setup writes `~/.config/opencode/plugins/memorix.js`, `~/.config/opencode/opencode.json`, skills, and `AGENTS.md` guidance; repo-local setup writes the `.opencode/` equivalents. | The generated plugin keeps the v1 named export and also exposes the v2 default `id`/`setup` contract. v2 event subscriptions use the host session ID when forwarding hooks. |
59
59
  | OpenClaw | `memorix setup --agent openclaw --global` | OpenClaw compatible bundle | `~/.openclaw/extensions/memorix` with `.mcp.json`, skills, and OpenClaw `HOOK.md`/`handler.ts` hook pack | Setup attempts `openclaw plugins install <path> --force` and `openclaw hooks enable memorix` when OpenClaw is available. |
60
60
  | Hermes Agent | `memorix setup --agent hermes --global` | Hermes plugin | Hermes home (`%LOCALAPPDATA%\hermes` on native Windows, `~/.hermes` elsewhere, or `HERMES_HOME`), plugin enablement in `config.yaml`, and `mcp_servers.memorix` | The plugin registers hooks, a slash command, a CLI command, and skills through Hermes' plugin context. Existing YAML keys are preserved. |
61
61
  | Oh-my-Pi | `memorix setup --agent omp --global` | Oh-my-Pi `omp.extensions` package | `.omp/packages/memorix` or `~/.omp/agent/packages/memorix`, plus `.omp/mcp.json` or `~/.omp/agent/mcp.json` | Setup attempts `omp plugin link <path>`. This target uses Oh-my-Pi's package/extension manifest, not Pi's legacy package manifest. |
@@ -1,6 +1,6 @@
1
1
  # Memorix 已知边界与路线图
2
2
 
3
- > 最后审阅:2026-09-22(对照 v1.9.6 发布内容)
3
+ > 最后审阅:2026-09-28(对照 v1.9.7 发布内容)
4
4
 
5
5
  这份文档说明仍然成立的产品边界、风险和方向;它不是发布流水账。
6
6
 
package/docs/README.md CHANGED
@@ -126,7 +126,7 @@ Historical/deep-reference documents may describe older designs. If they conflict
126
126
 
127
127
  ## Current Product Line
128
128
 
129
- The current product line is **1.9.6**. Multi-device store sync is opt-in and
129
+ The current product line is **1.9.8**. Multi-device store sync is opt-in and
130
130
  keeps local SQLite canonical; existing users do not upload anything until they
131
131
  configure a relay provider explicitly.
132
132
  The authoritative acceptance contract for the current implementation is
package/docs/SETUP.md CHANGED
@@ -502,7 +502,7 @@ OpenCode can also use a local Memorix plugin generated by setup:
502
502
  memorix setup --agent opencode --global
503
503
  ```
504
504
 
505
- This writes the OpenCode plugin, skills, MCP config, and `AGENTS.md` guidance in the selected scope.
505
+ This writes the OpenCode plugin, skills, MCP config, and `AGENTS.md` guidance in the selected scope. The generated plugin exports both the legacy v1 hook function and the v2 default `id`/`setup` contract, so OpenCode v1 and v2 can load the same file during the migration window.
506
506
 
507
507
  Antigravity setup installs a native plugin at `~/.gemini/config/plugins/memorix` for global setup or `.agents/plugins/memorix` for workspace setup. The plugin includes stdio MCP, official hooks, rules, and skills:
508
508
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "memorix",
3
3
  "mcpName": "io.github.AVIDS2/memorix",
4
- "version": "1.9.6",
4
+ "version": "1.9.8",
5
5
  "description": "Local-first shared memory layer for AI coding agents across MCP clients, Git history, reasoning context, and project sessions.",
6
6
  "type": "module",
7
7
  "sideEffects": false,
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.6"
3
+ "version": "1.9.8"
4
4
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://anthropic.com/claude-code/plugin.schema.json",
3
3
  "name": "memorix",
4
- "version": "1.9.6",
4
+ "version": "1.9.8",
5
5
  "description": "Shared workspace memory for Claude Code and other AI coding agents.",
6
6
  "author": {
7
7
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.6",
3
+ "version": "1.9.8",
4
4
  "description": "Local-first project memory for CodeBuddy Code and other coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.6",
3
+ "version": "1.9.8",
4
4
  "description": "Shared workspace memory for Codex and other AI coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.6",
3
+ "version": "1.9.8",
4
4
  "description": "Shared local workspace memory for GitHub Copilot CLI and other AI coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.6",
3
+ "version": "1.9.8",
4
4
  "mcpServers": {
5
5
  "memorix": {
6
6
  "command": "memorix",
@@ -1,5 +1,5 @@
1
1
  name: memorix
2
- version: "1.9.6"
2
+ version: "1.9.8"
3
3
  description: Shared workspace memory bridge for Hermes Agent.
4
4
  author: AVIDS2
5
5
  license: Apache-2.0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix-omp-package",
3
- "version": "1.9.6",
3
+ "version": "1.9.8",
4
4
  "description": "Memorix shared workspace memory package for Oh-my-Pi.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix",
3
- "version": "1.9.6",
3
+ "version": "1.9.8",
4
4
  "description": "Shared workspace memory for OpenClaw and other AI coding agents.",
5
5
  "author": {
6
6
  "name": "AVIDS2",
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memorix-pi-package",
3
- "version": "1.9.6",
3
+ "version": "1.9.8",
4
4
  "description": "Memorix shared workspace memory package for Pi coding agent.",
5
5
  "license": "Apache-2.0",
6
6
  "keywords": [
package/server.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "url": "https://github.com/AVIDS2/memorix",
8
8
  "source": "github"
9
9
  },
10
- "version": "1.9.6",
10
+ "version": "1.9.8",
11
11
  "websiteUrl": "https://mem.rglens.com",
12
12
  "icons": [
13
13
  {
@@ -22,7 +22,7 @@
22
22
  {
23
23
  "registryType": "npm",
24
24
  "identifier": "memorix",
25
- "version": "1.9.6",
25
+ "version": "1.9.8",
26
26
  "runtimeHint": "npx",
27
27
  "packageArguments": [
28
28
  {
@@ -449,8 +449,10 @@ function generateKiroHookFiles(): Array<{ filename: string; content: string }> {
449
449
  * Format: .opencode/plugins/memorix.js — Bun-compatible JS module
450
450
  * See: https://opencode.ai/docs/plugins/
451
451
  *
452
- * Plugin contract (verified against official docs Apr 2026):
453
- * - Named export: export const MemorixPlugin = async (ctx) => { return { ... } }
452
+ * Plugin contracts:
453
+ * - OpenCode v1: named export `MemorixPlugin` returns an event-hook map.
454
+ * - OpenCode v2: default export contains `id` and `setup(ctx)`, with event
455
+ * subscriptions and tool hooks registered through the context.
454
456
  * - Return object keys are EVENT NAMES (e.g. "session.created", "file.edited")
455
457
  * - Each key maps to an async handler: (input, output) => { ... }
456
458
  * - Session/file/command events: handler receives ({ event }) for event-style hooks
@@ -464,7 +466,7 @@ function generateKiroHookFiles(): Array<{ filename: string; content: string }> {
464
466
  * protocol used by all agents. spawnSync works in both Node.js and Bun
465
467
  * runtimes (OpenCode may fall back to Node.js on Windows).
466
468
  */
467
- const OPENCODE_PLUGIN_VERSION = 7;
469
+ const OPENCODE_PLUGIN_VERSION = 8;
468
470
 
469
471
  const AGENT_SKILL_DIRS: Partial<Record<AgentName, { project: string; global?: string }>> = {
470
472
  cursor: { project: path.join('.cursor', 'skills'), global: path.join('.cursor', 'skills') },
@@ -716,6 +718,161 @@ export const MemorixPlugin = async ({ project, client, $, directory, worktree })
716
718
  },
717
719
  };
718
720
  };
721
+
722
+ /**
723
+ * OpenCode v2 compatibility layer. Keep this beside the v1 export so one
724
+ * generated file can be loaded by both host generations during migration.
725
+ */
726
+ const v2HookCommand = ${hookCommand};
727
+
728
+ function v2ReportHookFailure(eventName, detail) {
729
+ if (process.env.MEMORIX_HOOK_DEBUG !== '1') return;
730
+ console.error('[memorix-plugin] v2 hook delivery failed:', eventName, detail);
731
+ }
732
+
733
+ function v2EventData(event) {
734
+ return event && typeof event.data === 'object' && event.data !== null ? event.data : {};
735
+ }
736
+
737
+ function v2SessionId(event) {
738
+ const data = v2EventData(event);
739
+ const value = data.sessionID ?? data.sessionId ?? data.session_id
740
+ ?? event?.sessionID ?? event?.sessionId;
741
+ return typeof value === 'string' ? value : '';
742
+ }
743
+
744
+ function v2RunHook(payload, directory, sessionId) {
745
+ const nextPayload = { agent: 'opencode', cwd: directory, ...payload };
746
+ if (sessionId) nextPayload.session_id = sessionId;
747
+ const eventName = nextPayload.hook_event_name || 'unknown';
748
+ try {
749
+ const result = spawnSync(v2HookCommand, ['hook'], {
750
+ input: JSON.stringify(nextPayload),
751
+ timeout: 10_000,
752
+ encoding: 'utf-8',
753
+ stdio: ['pipe', 'pipe', 'pipe'],
754
+ shell: process.platform === 'win32',
755
+ windowsHide: true,
756
+ });
757
+ if (result.status !== 0) {
758
+ v2ReportHookFailure(eventName, {
759
+ exit: result.status,
760
+ stderr: (result.stderr || '').slice(0, 200),
761
+ error: result.error?.message,
762
+ });
763
+ }
764
+ } catch (error) {
765
+ v2ReportHookFailure(eventName, error?.message ?? error);
766
+ }
767
+ }
768
+
769
+ function v2SessionState(states, sessionId) {
770
+ const key = sessionId || '__unknown__';
771
+ let state = states.get(key);
772
+ if (!state) {
773
+ state = { pendingText: '', lastDeliveredKey: '' };
774
+ states.set(key, state);
775
+ }
776
+ return state;
777
+ }
778
+
779
+ function v2FlushAssistant(state, directory, sessionId) {
780
+ const text = state.pendingText.trim();
781
+ if (!text || text === state.lastDeliveredKey) return;
782
+ v2RunHook({ hook_event_name: 'message.updated', ai_response: text }, directory, sessionId);
783
+ state.lastDeliveredKey = text;
784
+ state.pendingText = '';
785
+ }
786
+
787
+ function v2FilePath(file) {
788
+ if (typeof file === 'string') return file;
789
+ if (!file || typeof file !== 'object') return '';
790
+ return file.path ?? file.file ?? file.name ?? '';
791
+ }
792
+
793
+ function v2HandleEvent(event, directory, states) {
794
+ if (!event || typeof event !== 'object') return;
795
+ const type = typeof event.type === 'string' ? event.type : '';
796
+ const data = v2EventData(event);
797
+ const sessionId = v2SessionId(event);
798
+ const state = v2SessionState(states, sessionId);
799
+ const run = (payload) => v2RunHook(payload, directory, sessionId);
800
+
801
+ switch (type) {
802
+ case 'session.created':
803
+ run({ hook_event_name: 'session.created' });
804
+ break;
805
+ case 'session.text.delta':
806
+ state.pendingText += typeof data.delta === 'string' ? data.delta : (typeof data.text === 'string' ? data.text : '');
807
+ break;
808
+ case 'session.message.updated':
809
+ case 'message.updated':
810
+ if (data.role === 'assistant' && typeof data.text === 'string') state.pendingText = data.text;
811
+ break;
812
+ case 'session.step.ended': {
813
+ const files = Array.isArray(data.files) ? data.files : [];
814
+ for (const file of files) {
815
+ const filePath = v2FilePath(file);
816
+ if (filePath) run({ hook_event_name: 'file.edited', file_path: filePath });
817
+ }
818
+ if (typeof data.text === 'string') state.pendingText += data.text;
819
+ break;
820
+ }
821
+ case 'session.idle':
822
+ v2FlushAssistant(state, directory, sessionId);
823
+ run({ hook_event_name: 'session.idle' });
824
+ break;
825
+ case 'session.compacted':
826
+ run({ hook_event_name: 'session.compacted' });
827
+ break;
828
+ default:
829
+ break;
830
+ }
831
+ }
832
+
833
+ function setup(ctx) {
834
+ const directory = ctx?.location?.directory || ctx?.location?.worktree || process.cwd();
835
+ const controller = new AbortController();
836
+ const states = new Map();
837
+
838
+ if (ctx?.tool?.hook) {
839
+ try {
840
+ Promise.resolve(ctx.tool.hook('execute.after', async (event) => {
841
+ const sessionId = v2SessionId(event);
842
+ const data = event && typeof event.input === 'object' ? event.input : {};
843
+ const tool = event?.tool ?? event?.name ?? '';
844
+ v2RunHook({
845
+ hook_event_name: 'tool.execute.after',
846
+ tool_name: tool,
847
+ tool_input: data,
848
+ }, directory, sessionId);
849
+ if (tool === 'shell') {
850
+ v2RunHook({
851
+ hook_event_name: 'command.executed',
852
+ command: data.command ?? data.cmd ?? '',
853
+ }, directory, sessionId);
854
+ }
855
+ })).catch((error) => v2ReportHookFailure('execute.after', error?.message ?? error));
856
+ } catch (error) {
857
+ v2ReportHookFailure('execute.after', error?.message ?? error);
858
+ }
859
+ }
860
+
861
+ if (ctx?.event?.subscribe) {
862
+ void (async () => {
863
+ try {
864
+ const stream = ctx.event.subscribe({ signal: controller.signal });
865
+ for await (const event of stream) v2HandleEvent(event, directory, states);
866
+ } catch (error) {
867
+ if (!controller.signal.aborted) v2ReportHookFailure('event.subscribe', error?.message ?? error);
868
+ }
869
+ })();
870
+ }
871
+
872
+ return () => controller.abort();
873
+ }
874
+
875
+ export default { id: 'memorix', setup };
719
876
  `;
720
877
  }
721
878
 
@@ -909,6 +909,11 @@ export async function searchObservations(options: SearchOptions): Promise<IndexE
909
909
  // Fast-tier queries skip embedding entirely (fulltext is sufficient)
910
910
  let queryVector: number[] | null = null;
911
911
  let embeddingProviderForSearch: EmbeddingProvider | null = null;
912
+ // Distinct from `embeddingEnabled` (static config): this tracks whether the
913
+ // current query can use embeddings (provider unavailable, incompatible
914
+ // dimensions, TLS, network, or timeout). `canUsePersistentLexical` below
915
+ // must fall back whenever this is true.
916
+ let embeddingUnavailable = false;
912
917
  if (quality !== 'fast' && embeddingEnabled && hasQuery && tier !== 'fast') {
913
918
  try {
914
919
  const provider = await getEmbeddingProvider();
@@ -916,6 +921,7 @@ export async function searchObservations(options: SearchOptions): Promise<IndexE
916
921
  embeddingProviderForSearch = provider;
917
922
  const activeVectorDimensions = getVectorDimensions();
918
923
  if (activeVectorDimensions !== null && provider.dimensions !== activeVectorDimensions) {
924
+ embeddingUnavailable = true;
919
925
  rememberSearchMode(
920
926
  modeKey,
921
927
  `fulltext (embedding dimension mismatch: provider ${provider.dimensions}d vs index ${activeVectorDimensions}d)`,
@@ -961,9 +967,12 @@ export async function searchObservations(options: SearchOptions): Promise<IndexE
961
967
  : { text: 0.6, vector: 0.4 },
962
968
  };
963
969
  }
970
+ } else {
971
+ embeddingUnavailable = true;
964
972
  }
965
973
  } catch (error) {
966
974
  // Fallback to fulltext if embedding fails or times out
975
+ embeddingUnavailable = true;
967
976
  rememberSearchMode(modeKey, 'fulltext (embedding unavailable)');
968
977
  console.error('[memorix] Embedding failed or timed out, falling back to fulltext search');
969
978
  }
@@ -972,8 +981,14 @@ export async function searchObservations(options: SearchOptions): Promise<IndexE
972
981
  mark('preSearch');
973
982
  let results: SearchResultLike | Awaited<ReturnType<typeof search>>;
974
983
  const persistentCorpusEligible = await shouldUsePersistentSearch(projectIds, options.projectId);
984
+ // `embeddingEnabled` alone misses the common case: embedding is configured
985
+ // (enabled) but the current provider is unavailable, has incompatible
986
+ // dimensions, or failed at runtime. Without this per-query state, those
987
+ // failures leave queryVector null AND canUsePersistentLexical false, so
988
+ // neither the semantic nor lexical persistent path runs — a silent,
989
+ // deterministic empty result despite the FTS5 index having real matches.
975
990
  const canUsePersistentLexical = persistentCorpusEligible && Boolean(hasQuery) && !queryVector &&
976
- (quality === 'fast' || tier === 'fast' || !embeddingEnabled);
991
+ (quality === 'fast' || tier === 'fast' || !embeddingEnabled || embeddingUnavailable);
977
992
  let persistent = persistentCorpusEligible && queryVector && embeddingProviderForSearch
978
993
  ? await searchPersistentSemantically(options, projectIds, requestLimit, queryVector, embeddingProviderForSearch)
979
994
  : canUsePersistentLexical
@@ -1,4 +1,5 @@
1
1
  import type { Observation } from '../types.js';
2
+ import { isTransientSqliteError } from './sqlite-reliability.js';
2
3
 
3
4
  /** Rows returned by the derived FTS5 index before normal Observation parsing. */
4
5
  export interface SqliteLexicalHit {
@@ -56,31 +57,52 @@ export function buildFtsQuery(query: string): string | null {
56
57
  return unique.map((token) => `"${normalizeFtsToken(token)}"`).join(' OR ');
57
58
  }
58
59
 
60
+ /**
61
+ * initializeObservationLexicalIndex runs exactly once per db handle (called
62
+ * from getDatabase's synchronous setup), so a transient cross-process BUSY
63
+ * here would otherwise be cached as a permanent "unavailable" for that
64
+ * handle's entire lifetime, with no other call site ever re-checking it.
65
+ */
66
+ const FTS_INIT_MAX_ATTEMPTS = 3;
67
+
59
68
  /**
60
69
  * Create and reconcile the external-content FTS5 index for one SQLite handle.
61
- * FTS5 is an optional SQLite capability, so an unavailable extension is a
62
- * normal fallback rather than a reason to make the durable store unusable.
70
+ * FTS5 is an optional SQLite capability, so a genuinely unavailable extension
71
+ * is a normal fallback rather than a reason to make the durable store
72
+ * unusable. Cross-process lock contention (SQLITE_BUSY) is not that case —
73
+ * it is retried a few times, mirroring the retry already applied to the
74
+ * write paths in sqlite-store.ts / session-store.ts / mini-skill-store.ts,
75
+ * since the caller here is synchronous and cannot await retrySqliteBusy.
63
76
  */
64
77
  export function initializeObservationLexicalIndex(db: any): boolean {
65
78
  const known = ftsAvailability.get(db);
66
79
  if (known !== undefined) return known;
67
80
 
68
- try {
69
- db.exec(CREATE_OBSERVATION_FTS);
70
- const observationCount = Number(db.prepare('SELECT COUNT(*) AS count FROM observations').get()?.count ?? 0);
71
- const indexedCount = Number(db.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
72
- if (observationCount !== indexedCount) {
73
- db.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
81
+ let lastError: unknown;
82
+ for (let attempt = 1; attempt <= FTS_INIT_MAX_ATTEMPTS; attempt++) {
83
+ try {
84
+ db.exec(CREATE_OBSERVATION_FTS);
85
+ const observationCount = Number(db.prepare('SELECT COUNT(*) AS count FROM observations').get()?.count ?? 0);
86
+ const indexedCount = Number(db.prepare(`SELECT COUNT(*) AS count FROM ${FTS_TABLE}`).get()?.count ?? 0);
87
+ if (observationCount !== indexedCount) {
88
+ db.exec(`INSERT INTO ${FTS_TABLE}(${FTS_TABLE}) VALUES ('rebuild')`);
89
+ }
90
+ ftsAvailability.set(db, true);
91
+ return true;
92
+ } catch (error) {
93
+ lastError = error;
94
+ if (!isTransientSqliteError(error) || attempt === FTS_INIT_MAX_ATTEMPTS) break;
95
+ // better-sqlite3 is synchronous, so there is no await-able delay here;
96
+ // re-entering db.exec/prepare re-enters SQLite's own busy_timeout wait
97
+ // (already configured in sqlite-db.ts), which is the retry itself.
74
98
  }
75
- ftsAvailability.set(db, true);
76
- return true;
77
- } catch (error) {
78
- ftsAvailability.set(db, false);
79
- console.warn(
80
- `[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${error instanceof Error ? error.message : String(error)}`,
81
- );
82
- return false;
83
99
  }
100
+
101
+ ftsAvailability.set(db, false);
102
+ console.warn(
103
+ `[memorix] SQLite FTS5 unavailable; using the existing search fallback: ${lastError instanceof Error ? lastError.message : String(lastError)}`,
104
+ );
105
+ return false;
84
106
  }
85
107
 
86
108
  export function isObservationLexicalIndexEnabled(db: any): boolean {