@equationalapplications/expo-llm-wiki 7.15.1 → 7.15.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.
Files changed (2) hide show
  1. package/README.md +22 -431
  2. package/package.json +4 -4
package/README.md CHANGED
@@ -10,21 +10,14 @@ Local-first LLM memory for Expo and React Native. Combines the core semantic sea
10
10
 
11
11
  > Inspired by [Andrej Karpathy's LLM Wiki memory spec](https://gist.github.com/karpathy/442a6bf555914893e9891c11519de94f).
12
12
 
13
- ## Features
13
+ **📖 Documentation: [equationalapplications.github.io/expo-llm-wiki/packages/expo](https://equationalapplications.github.io/expo-llm-wiki/packages/expo/)**. Start with the [Expo quickstart](https://equationalapplications.github.io/expo-llm-wiki/start/quickstart-expo/).
14
14
 
15
- - **Expo-ready** — Pre-configured for React Native + Expo
16
- - **Built on `expo-sqlite`** — Stable, well-supported SQLite driver
17
- - **Hermes-ready** — Secure record ID generation via [`expo-crypto`](https://docs.expo.dev/versions/latest/sdk/crypto/); wired automatically at import (no manual `crypto` polyfill)
18
- - **Semantic search** — Vector embeddings via `embed` function, with MiniSearch fallback
19
- - **Retrieval tuning** — Per-call overrides for search behavior (pre-filter, hybrid blend, tier weights)
20
- - **Multi-entity reads** — Search across multiple `entity_id` namespaces in one pass with `tierWeights`
21
- - **Source provenance** — `WikiFact.source_type` distinguishes immutable document facts (`immutable_document`) from mutable derived/user facts. Immutable document content is protected from librarian/heal rewriting and only changed by `forget()` or re-ingest.
22
- - **Seeded ontologies** — Enforce strict taxonomies or allow emergent graph relationship extraction (`useOntologyManifest`, `useSetOntologyManifest`; Strict, Emergent, or Off; defaults to Off).
23
- - **React hooks** — `WikiProvider`, `useMemoryRead`, `useOntologyManifest`, `useSetOntologyManifest`, `useWikiTraversal`, and all other hooks re-exported from `@equationalapplications/expo-llm-wiki`
24
- - **Full-featured memory** — Facts, tasks, events, maintenance jobs (librarian, heal, reembed, prune)
25
- - **Interoperability:** Supports [Open Knowledge Format (OKF)](https://github.com/GoogleCloudPlatform/knowledge-catalog/tree/main/okf) v0.1 + v0.2 import and export via the [llm-wiki OKF profiles](https://github.com/equationalapplications/expo-llm-wiki/blob/main/docs/okf-profile.md) (default `llm-wiki/2`, back-compat `llm-wiki/1`).
15
+ ## Features
26
16
 
27
- - **GraphRAG on React Native** — `useWikiTraversal` + `formatGraphContext` give you the same SQLite-only graph retrieval as the core package; pair with `@equationalapplications/schema-org-llm-wiki` for the canonical ontology. See [root README: GraphRAG](../../README.md#graphrag-sql-only-graph-retrieval).
17
+ - **Expo-ready.** Core plus an `expo-sqlite` adapter, so there's nothing to wire up.
18
+ - **Hermes-ready.** Secure record IDs through [`expo-crypto`](https://docs.expo.dev/versions/latest/sdk/crypto/), registered automatically on import. No `crypto` polyfill needed.
19
+ - **React hooks.** `WikiProvider`, `useMemoryRead`, `useWikiTraversal` and the rest of [`react-llm-wiki`](https://equationalapplications.github.io/expo-llm-wiki/reference/hooks/), re-exported.
20
+ - **Everything in core.** Semantic and keyword search, multi-entity reads, seeded ontologies and [GraphRAG](https://equationalapplications.github.io/expo-llm-wiki/guides/graphrag/), background maintenance, and OKF import and export.
28
21
 
29
22
  ## Installation
30
23
 
@@ -33,435 +26,33 @@ npx expo install expo-sqlite expo-crypto
33
26
  npm install @equationalapplications/expo-llm-wiki
34
27
  ```
35
28
 
36
- `expo-crypto` is a peer dependency. The package wires its `getRandomValues` into the core engine at module load — Hermes and React Native lack the Web `crypto` global, and wiki writes need a cryptographically secure random source for record IDs. No extra setup is required after install; importing `@equationalapplications/expo-llm-wiki` (or the `/factory` subpath) activates it before any `createWiki()` call.
37
-
38
- ## Semantic Search
39
-
40
- Enable vector-based retrieval by providing an `embed` function:
41
-
42
- ```typescript
43
- import { createWiki } from '@equationalapplications/expo-llm-wiki';
44
- import { openDatabaseSync } from 'expo-sqlite';
45
-
46
- const db = openDatabaseSync('wiki.db');
47
-
48
- const wiki = createWiki(db, {
49
- config: {
50
- // Optimize retrieval for large memory stores
51
- preFilterLimit: 50, // Limit cosine scoring to top-50 keyword matches
52
- hybridWeight: 0.7, // Blend semantic (0.7) + keyword (0.3)
53
- },
54
- llmProvider: {
55
- generateText: async ({ systemPrompt, userPrompt }) => {
56
- // Your LLM call — must return the model output as a string
57
- return 'Model output';
58
- },
59
- maxOutputTokens: 4096, // optional — your model's output ceiling; lets maintenance passes size their LLM calls
60
- embed: async (text: string) => {
61
- // Your embedding service (e.g., OpenAI, Cohere)
62
- // Use an absolute URL — React Native / Expo apps do not have a browser
63
- // origin to resolve relative URLs against on device or simulator.
64
- const response = await fetch('https://your-api.example.com/api/embed', {
65
- method: 'POST',
66
- body: JSON.stringify({ text })
67
- });
68
- const { embedding } = await response.json();
69
- return embedding; // number[]
70
- },
71
- },
72
- onRetrievalFallback: (error) => {
73
- console.warn('Embedding unavailable, using keyword search:', error);
74
- },
75
- });
76
-
77
- await wiki.setup();
78
-
79
- // Semantic query
80
- const memory = await wiki.read('user-123', 'what activities should I do this weekend?');
81
- // Matches facts like "Saturday hiking trip" even with no lexical overlap
82
-
83
- // Per-call overrides
84
- const fasterSearch = await wiki.read('user-123', 'activities', {
85
- maxResults: 5,
86
- preFilterLimit: 20, // Tighter pre-filter for speed
87
- hybridWeight: 0.5, // More keyword weight
88
- });
89
-
90
- // Multi-entity with tier weights
91
- const multiMemory = await wiki.read(['tier_wisdom', 'tier_fact', 'tier_working'], 'activities', {
92
- maxResults: 8,
93
- tierWeights: {
94
- tier_wisdom: 2, // boost curated notes 2×
95
- tier_fact: 1, // neutral baseline
96
- tier_working: 0.25, // downrank unvetted context
97
- },
98
- // includeZeroWeightEntities: true — include 0-weight entities as bottom-ranked filler
99
- });
100
- // multiMemory.factScores — Record<factId, weightedScore> | undefined (array entityId only, populated when query is non-empty and at least one fact scored)
101
- // multiMemory.metadata — { query, entityIds, tierWeights }
102
- ```
103
-
104
- ## Configuration
105
-
106
- All `WikiConfig` fields are optional:
107
-
108
- ```typescript
109
- const wiki = createWiki(db, {
110
- llmProvider: { /* ... */ },
111
- config: {
112
- tablePrefix: 'llm_wiki_', // default: 'llm_wiki_'
113
- maxResults: 10, // default: 10
114
- autoLibrarianThreshold: 20, // default: 20 — events before librarian auto-runs
115
- autoHealThreshold: 100, // default: 100 — events before heal auto-runs
116
- maxChunkLength: 12000, // default: 12000 (char count per ingestDocument chunk)
117
- chunkOverlap: 400, // default: 400 (overlap between chunks in characters)
118
- chunkConcurrency: 1, // default: 1 (parallel LLM calls per ingestDocument)
119
- pruneRetainSoftDeletedFor: 7, // default: 7 (days before hard-deleting soft-deleted facts)
120
- pruneEventsAfter: 30, // default: 30 (days before hard-deleting old events)
121
- orphanAfterDays: 30, // default: 30 (days before runHeal flags sourceless facts; null to disable)
122
- staleInferredAfterDays: 60, // default: 60 (days before runHeal downgrades inferred facts; null to disable)
123
- preFilterLimit: 50, // default: undefined — MiniSearch pre-filter before cosine scan; recommended for >500 facts
124
- hybridWeight: 0.7, // default: undefined — blend semantic (1.0) ↔ keyword (0.0); pure semantic when unset
125
- excludeDrafts: false, // default: false — hide unreviewed drafts from reads; traversal skips discovered drafts (a draft start fact is still returned)
126
- grounding: { mode: 'off' }, // default: off — 'draft' stores facts that don't quote their source as drafts
127
-
128
- // Global prompt overrides — librarianSystemPrompt and healSystemPrompt apply to write() auto-runs;
129
- // ingestSystemPrompt applies only to explicit ingestDocument() calls.
130
- // ⚠ Overrides replace the entire default prompt, including the JSON output contract.
131
- // Your prompt must instruct the LLM to return the required JSON shape — see packages/core/README.md#prompt-management--overrides.
132
- prompts: {
133
- ingestSystemPrompt: `Extract core facts from this document: {{documentChunk}}\n\nReturn ONLY valid JSON: { "facts": [{ "title": "string", "body": "string", "tags": ["string"], "confidence": "certain|inferred|tentative" }] }. No markdown.`,
134
- librarianSystemPrompt: `Synthesize these thoughts into insights:\n{{events}}\n\nReturn ONLY valid JSON: { "facts": [{ "title": "string", "body": "string", "tags": ["string"], "confidence": "certain|inferred|tentative" }], "tasks": [{ "description": "string", "priority": 0 }] }. No markdown.`,
135
- healSystemPrompt: `Fix the memory graph based on these candidates: {{healCandidates}}\n\nReturn ONLY valid JSON: { "downgraded": ["factId"], "deleted": ["factId"], "newFacts": [{ "title": "string", "body": "string", "tags": ["string"], "confidence": "certain|inferred|tentative" }] }. No markdown.`,
136
- },
137
- },
138
- });
139
- ```
140
-
141
- **Diagnostics and draft review.** Pass `onDiagnostic` beside `llmProvider` (not inside `config`) to receive typed, content-free reports of dropped chunks, facts, edges and failed background jobs. `useMemoryRead` and `useWikiTraversal` accept `excludeDrafts` per call, which overrides the config default. There are no dedicated hooks for listing or promoting drafts yet: call `listDrafts` / `promoteDraft` on the `WikiMemory` instance from `useWiki()`, as shown below. With `grounding` on, facts that don't quote their source land as drafts; see [core: Grounding](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/core/README.md#grounding). See [core: Diagnostics](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/core/README.md#diagnostics) and [core: Draft Review](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/core/README.md#draft-review). Other `WikiConfig` fields, such as `ontology`, are documented in the [core README](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/core/README.md#configuration).
142
-
143
- ```tsx
144
- import { useWiki } from '@equationalapplications/expo-llm-wiki';
29
+ Use `npx expo install` so Expo picks native builds that match your SDK.
145
30
 
146
- function DraftReview({ entityId }: { entityId: string }) {
147
- const wiki = useWiki(); // the WikiMemory instance; throws outside <WikiProvider>
148
- const loadDrafts = () => wiki.listDrafts(entityId, { limit: 20 }); // → { facts, nextCursor }
149
- // ...
150
- }
151
- ```
31
+ | Peer dependency | Range |
32
+ |---|---|
33
+ | `expo-sqlite` | `^14 \|\| ^15 \|\| ^55 \|\| ^56 \|\| ^57 \|\| ^58` |
34
+ | `expo-crypto` | `>=12` |
35
+ | `react` | `>=17` |
152
36
 
153
- `useWiki()` also exposes the read-only health and instruction APIs for components that need them:
37
+ ## Quick look
154
38
 
155
39
  ```tsx
156
- function HealthAndInstructions({ entityId, batch }: { entityId: string; batch: Array<{ sourceRef: string; sourceHash: string }> }) {
157
- const wiki = useWiki();
40
+ import * as SQLite from 'expo-sqlite';
41
+ import { createWiki, WikiProvider, useMemoryRead } from '@equationalapplications/expo-llm-wiki';
158
42
 
159
- // Lint: read-only health report for one entity (dangling edges, manifest violations, drafts, etc.)
160
- const loadReport = () => wiki.lint(entityId);
161
- // → { danglingEdges, manifestViolations, untypedFacts, drafts, unverifiedInferred,
162
- // sample: { danglingEdgeIds, manifestViolationEdgeIds } }
163
-
164
- // pendingSources: per-input change status including the partial-ingest state.
165
- const loadStatuses = () => wiki.pendingSources(entityId, batch);
166
- // → Array<{ sourceRef: string; status: 'new' | 'changed' | 'partial' | 'current' }>
167
-
168
- // getInstructions: effective system prompts for each writer, with overrides applied.
169
- const loadInstructions = () => wiki.getInstructions(entityId);
170
- // → { ingest, librarian, heal, ontologyBackfill }
171
- }
172
- ```
173
-
174
- See [core: Lint](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/core/README.md#lint), [core: Batch Change Detection](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/core/README.md#batch-change-detection), and [core: Effective instructions](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/core/README.md#effective-instructions-getinstructions).
175
-
176
- ## Retrieval Tuning
177
-
178
- Optimize `read()` performance and blend retrieval strategies:
179
-
180
- ```typescript
181
- const config = {
182
- // Limit cosine similarity scoring to top-K MiniSearch keyword candidates
183
- preFilterLimit: 50,
184
-
185
- // Blend semantic and keyword scores (0.0 = pure keyword, 1.0 = pure semantic)
186
- hybridWeight: 0.7,
187
-
188
- // Max results returned per read
189
- maxResults: 10,
190
- };
191
-
192
- const wiki = createWiki(db, {
193
- config,
194
- llmProvider: { /* ... */ },
195
- });
196
- ```
197
-
198
- **Hybrid scoring blends:**
199
- - `hybridWeight: 1.0` → pure semantic ranking among the candidates being scored; if `preFilterLimit` is set, semantic scoring is still limited to the top-K MiniSearch matches
200
- - `hybridWeight: 0.5` → balanced semantic + keyword (50/50 blend)
201
- - `hybridWeight: 0.0` → pure keyword ranking, skips `embed()` entirely (no LLM API cost)
202
-
203
- **Pre-filtering optimization:**
204
- When `preFilterLimit: 50` is set with 1000 facts, cosine similarity is computed only for the top 50 MiniSearch keyword matches, reducing O(N) scoring to O(50).
205
-
206
- ## Usage
207
-
208
- ```typescript
209
- import { createWiki } from '@equationalapplications/expo-llm-wiki';
210
- import { openDatabaseSync } from 'expo-sqlite';
211
-
212
- const db = openDatabaseSync('wiki.db');
213
-
214
- const wiki = createWiki(db, {
215
- llmProvider: {
216
- generateText: async ({ systemPrompt, userPrompt }) => {
217
- // Your LLM call — must return the model output as a string
218
- return 'Model output';
219
- },
220
- },
221
- });
222
-
223
- // Initialize tables (call once on app startup)
43
+ const db = await SQLite.openDatabaseAsync('app.db');
44
+ const wiki = createWiki(db, { llmProvider }); // pass the expo-sqlite database directly
224
45
  await wiki.setup();
225
46
 
226
- // Auto-runs: uses config.prompts for background librarian/heal triggers
227
- await wiki.write('user-123', { event_type: 'observation', summary: '...' });
228
-
229
- // Manual executions: runtime promptOverride applies only to this single call.
230
- // Must include the JSON output contract — overrides replace the entire default prompt.
231
- await wiki.runLibrarian('user-123', {
232
- promptOverride: `Strict domain extraction task:\n{{events}}\n\nReturn ONLY valid JSON: { "facts": [{ "title": "string", "body": "string", "tags": ["string"], "confidence": "certain|inferred|tentative" }], "tasks": [{ "description": "string", "priority": 0 }] }. No markdown.`,
233
- });
234
-
235
- await wiki.ingestDocument('user-123', {
236
- sourceRef: 'doc-1',
237
- sourceHash: sha256(content),
238
- documentChunk: content,
239
- promptOverride: `Focus strictly on technical APIs: {{documentChunk}}\n\nReturn ONLY valid JSON: { "facts": [{ "title": "string", "body": "string", "tags": ["string"], "confidence": "certain|inferred|tentative" }] }. No markdown.`,
240
- });
241
- ```
242
-
243
- ## With React
244
-
245
- `@equationalapplications/expo-llm-wiki` re-exports all hooks and `WikiProvider` from `@equationalapplications/react-llm-wiki`:
246
-
247
- ```typescript
248
- import { WikiProvider } from '@equationalapplications/expo-llm-wiki';
249
-
250
- <WikiProvider wiki={wiki}>
251
- <MyApp />
252
- </WikiProvider>
253
- ```
254
-
255
- Then use hooks in components:
256
-
257
- ```typescript
258
- import { useMemoryRead } from '@equationalapplications/expo-llm-wiki';
259
-
260
- export function UserProfile({ userId }: { userId: string }) {
261
- const { data, isPending } = useMemoryRead(userId, 'preferences');
262
-
263
- if (isPending) return <Text>Loading...</Text>;
264
- return <Text>{data?.facts.map(f => f.title).join(', ')}</Text>;
265
- }
266
- ```
267
-
268
- For live background-job status (e.g. a loading spinner while a document ingests or the librarian runs):
269
-
270
- ```typescript
271
- import { useEntityStatus } from '@equationalapplications/expo-llm-wiki';
272
-
273
- export function EntityLoadingSpinner({ entityId }: { entityId: string }) {
274
- const { ingesting, librarian, heal } = useEntityStatus(entityId);
275
- if (!ingesting && !librarian && !heal) return null;
276
- return <Spinner label={ingesting ? 'Ingesting…' : librarian ? 'Organizing…' : 'Healing…'} />;
277
- }
278
- ```
279
-
280
- ### `useOntologyManifest(entityId)`
281
-
282
- Reactive read — fetches on mount and when `entityId` or `wiki` changes:
283
-
284
- ```typescript
285
- import { useOntologyManifest } from '@equationalapplications/expo-llm-wiki';
286
-
287
- const { manifest, mode, isPending, error, refetch } = useOntologyManifest('user-123');
288
- // manifest: OntologyManifest | null
289
- // mode: OntologyMode | null ('strict' | 'emergent' | 'off' when present)
290
- ```
291
-
292
- Note: `manifest` and `mode` are `null` when the entity has no persisted or seeded manifest (`getOntologyManifest` returned `null`). Call `refetch()` after mutations to refresh.
293
-
294
- ### `useSetOntologyManifest()`
295
-
296
- Mutation — same `{ execute, isPending, error, lastResult }` contract as `useWikiWrite`:
297
-
298
- ```typescript
299
- import { useOntologyManifest, useSetOntologyManifest } from '@equationalapplications/expo-llm-wiki';
300
-
301
- export function OntologySettings({ entityId }: { entityId: string }) {
302
- const { manifest, mode, refetch } = useOntologyManifest(entityId);
303
- const { execute, isPending, error } = useSetOntologyManifest();
304
-
305
- const handleSave = async () => {
306
- await execute(entityId, {
307
- node_types: [{ type: 'person', description: 'An individual.' }],
308
- edge_types: [{
309
- type: 'reports_to',
310
- source_type: 'person',
311
- target_type: 'person',
312
- description: 'Reporting hierarchy.',
313
- }],
314
- }, { mode: 'strict' });
315
- refetch();
316
- };
317
-
318
- // render manifest/mode; wire handleSave to a save button
47
+ function App() {
48
+ const { data, isPending } = useMemoryRead('user-123', 'preferences');
49
+ // ...
319
50
  }
320
- ```
321
-
322
- Global defaults and `seedManifests` bootstrap are configured at construction time via `createWiki(..., { config: { ontology: ... } })`. See the [core package README § Per-Entity Seeded Ontology](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/core/README.md#per-entity-seeded-ontology) for mode semantics and manifest schema.
323
-
324
- `useSetOntologyManifest` does not automatically refresh `useOntologyManifest` — call `refetch()` after a successful `execute()`, same as `useWikiWrite` + `useMemoryRead`.
325
-
326
- ### `useWikiTraversal(entityId, options)`
327
-
328
- Reactive read — fetches on mount and whenever `entityId` or `options` change. Walks the knowledge graph N hops outward from a fact (`options.sourceId`) using edges written by `runLibrarian()`/`ingestDocument()`'s Seeded Ontology extraction pass:
329
-
330
- ```typescript
331
- import { useWikiTraversal, formatGraphContext } from '@equationalapplications/expo-llm-wiki';
332
-
333
- const { nodes, edges, isPending, error, refetch } = useWikiTraversal('user-123', {
334
- sourceId: 'fact_42',
335
- maxDepth: 2,
336
- direction: 'both',
337
- });
338
-
339
- const promptContext = formatGraphContext({ nodes, edges });
340
- ```
341
-
342
- - `maxDepth` is clamped to `[1, 3]` regardless of input.
343
- - `edgeTypes: []` (explicit empty array) matches nothing; omitting it matches all edge types.
344
- - Defaults (`maxTraversalNodes`, `minTraversalConfidence`, `traversalDirection`, `excludeSourceTypes`) can be set globally via `createWiki(..., { config: { maxTraversalNodes: 20, ... } })` and overridden per-call.
345
- - `formatGraphContext()` is a pure function — call it with the hook's `{ nodes, edges }` to get a dense text block suitable for prompt injection.
346
-
347
- ## Component Lifecycle
348
-
349
- ```mermaid
350
- flowchart TD
351
- A["<WikiProvider wiki={wiki}>"] --> B["App Components"]
352
- B --> C{"Use Hook?"}
353
- C -->|"useMemoryRead(entityId, query, options?)"| D["[Read Memory]"]
354
- C -->|"useWikiWrite()"| E["[Write Memory]"]
355
- C -->|"useWikiIngest()"| F["[Ingest Document]"]
356
- C -->|"useWikiForget()"| G["[Delete Memory]"]
357
- C -->|"useWikiMaintenance()"| H["[Run Jobs]"]
358
- C -->|"useOntologyManifest(entityId)"| S["[Read Ontology]"]
359
- C -->|"useSetOntologyManifest()"| T["[Update Ontology]"]
360
- D --> I{"entityId, query, wiki,<br/>or ReadOptions changed?"}
361
- I -->|"Yes"| J["Auto-refetch"]
362
- I -->|"No"| K["Return cached data"]
363
- J --> L["Trigger read()"]
364
- L --> M["Embed query<br/>if embed available"]
365
- M --> N["Phase 1: Score facts<br/>Phase 2: Fetch winners"]
366
- N --> O["Update component state"]
367
- O --> P["Re-render with data"]
368
- S --> I2{"entityId or wiki changed?"}
369
- I2 -->|"Yes"| J2["Auto-refetch"]
370
- I2 -->|"No"| K2["Return cached manifest/mode"]
371
- J2 --> L2["Trigger getOntologyManifest()"]
372
- L2 --> O2["Update component state"]
373
- O2 --> P2["Re-render with manifest/mode"]
374
- E --> Q["Execute write()"]
375
- F --> Q
376
- G --> Q
377
- H --> Q
378
- T --> Q
379
- Q --> R["Write completes"]
380
- ```
381
-
382
- **Data flow:**
383
- 1. **Wrap app** with `<WikiProvider wiki={wiki}>` — provides wiki context
384
- 2. **Use hooks** in components — access memory reactively
385
- 3. **Read operations** auto-refetch when `entityId`, `query`, `wiki`, or `ReadOptions` values change; call `refetch()` to refresh manually
386
- 4. **Ontology reads** auto-refetch when `entityId` or `wiki` changes; call `refetch()` manually after ontology mutations
387
- 5. **Write operations** (write, ingest, forget, maintenance) do not automatically re-trigger `useMemoryRead`; call `refetch()` after a write to refresh read results
388
- 6. **Ontology writes** (`useSetOntologyManifest`) do not automatically re-trigger `useOntologyManifest` in the same component unless `refetch()` is called after `execute()` succeeds
389
- 7. **Re-render** with new data flowing back to UI
390
-
391
- ## Retrieval Engine Internals
392
-
393
- ```mermaid
394
- flowchart TD
395
- A["read(entityId | entityId[], query, options?)"] --> B{hybridWeight = 0?}
396
- B -->|Yes| C["MiniSearch only<br/>(skip embed)"]
397
- B -->|No| D{embed available?}
398
- D -->|No| C
399
- D -->|Yes| F["Embed query"]
400
- F -->|throws| E["onRetrievalFallback<br/>callback"]
401
- E --> C
402
- F -->|succeeds| G{preFilterLimit<br/>active?}
403
- G -->|Yes| H["MiniSearch pre-filter<br/>top K candidates"]
404
- H --> I["Phase 1: Cosine score<br/>top K candidates"]
405
- G -->|No| J["Phase 1: Cosine score<br/>all facts"]
406
- J --> K["Cache vectors<br/>in-memory<br/>(full scan only)"]
407
- K --> L{hybridWeight = 1?}
408
- I --> L
409
- L -->|Yes| M["Pure semantic<br/>ranking"]
410
- L -->|No| N["Hybrid blend:<br/>semantic + keyword<br/>via MiniSearch"]
411
- M --> O["Phase 2: Fetch full rows<br/>top maxResults"]
412
- N --> O
413
- C --> P["MiniSearch ranking"]
414
- P --> O
415
- O --> R["Track access"]
416
- R --> Q["Return MemoryBundle"]
417
- ```
418
-
419
- The flowchart shows:
420
- 1. **Fast-path** when `hybridWeight = 0` (pure keyword, no embed cost)
421
- 2. **Fallback chain** when embed unavailable (MiniSearch silently) or throws (`onRetrievalFallback` callback, then MiniSearch)
422
- 3. **Pre-filtering** to limit cosine scoring to top-K keyword matches (O(N) → O(K))
423
- 4. **Two-phase SELECT**: phase 1 scores all/filtered facts with minimal columns, phase 2 fetches full rows for winners
424
- 5. **Hybrid scoring** to blend semantic and keyword rankings
425
- 6. **Vector caching** on full scans only; reads with `preFilterLimit` active skip cache population
426
51
 
427
- ## Multi-Entity Reads
428
-
429
- `read()` accepts a single entity ID or an array to search across namespaces in one retrieval pass. Pass `tierWeights` to control per-entity ranking before the final top-K results:
430
-
431
- ```typescript
432
- const memory = await wiki.read(
433
- ['tier_wisdom', 'tier_fact', 'tier_working'],
434
- 'What do I know about this topic?',
435
- {
436
- maxResults: 8,
437
- tierWeights: {
438
- tier_wisdom: 2, // boost curated notes 2×
439
- tier_fact: 1, // neutral
440
- tier_working: 0.25, // downrank unvetted context
441
- },
442
- }
443
- );
444
- // memory.factScores — Record<factId, weightedScore> | undefined
445
- // attached for array-shaped reads when the query is non-empty and at least one fact is scored
446
- // memory.metadata — { query, entityIds, tierWeights }
447
- // tasks capped at min(20 × entityCount, 200); events at min(10 × entityCount, 100)
52
+ // <WikiProvider wiki={wiki}><App /></WikiProvider>
448
53
  ```
449
54
 
450
- For full details on `{{mustache}}` prompt templating and the strict distinction between global auto-runs and runtime overrides, see [Prompt Management & Overrides](https://github.com/equationalapplications/expo-llm-wiki/blob/main/packages/core/README.md#prompt-management--overrides) in `@equationalapplications/core-llm-wiki`.
451
-
452
- ## Concurrency
453
-
454
- All write APIs are safe to call from concurrent async contexts. Transactions are
455
- serialized internally on the single database connection — you never need to know that
456
- SQLite forbids nested `BEGIN`, and you never need to throttle callers yourself.
457
-
458
- - One connection per database file per process is the supported topology.
459
- - Non-transactional reads are **not** serialized, so read latency is unaffected.
460
- - Inside a transaction callback, use only the provided `tx` handle — never the outer
461
- database handle. Using the outer handle deadlocks; opening a nested transaction throws.
462
-
463
- Driver errors that escape a transaction surface as `WikiTransactionError` (re-exported
464
- from `@equationalapplications/core-llm-wiki`) with a top-level `sqliteErrorCode`.
55
+ See the [Expo quickstart](https://equationalapplications.github.io/expo-llm-wiki/start/quickstart-expo/) for a complete app, including the `llmProvider`. On React Native, give `embed` an absolute URL.
465
56
 
466
57
  ## Monorepo Ecosystem
467
58
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@equationalapplications/expo-llm-wiki",
3
- "version": "7.15.1",
3
+ "version": "7.15.2",
4
4
  "description": "Local-first LLM memory for Expo and React Native. Combines the core semantic search and extraction engine with expo-sqlite and ready-to-use React hooks.",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",
@@ -49,14 +49,14 @@
49
49
  "bugs": {
50
50
  "url": "https://github.com/equationalapplications/expo-llm-wiki/issues"
51
51
  },
52
- "homepage": "https://github.com/equationalapplications/expo-llm-wiki/tree/main/packages/expo#readme",
52
+ "homepage": "https://equationalapplications.github.io/expo-llm-wiki/packages/expo/",
53
53
  "publishConfig": {
54
54
  "access": "public",
55
55
  "registry": "https://registry.npmjs.org"
56
56
  },
57
57
  "dependencies": {
58
- "@equationalapplications/core-llm-wiki": "7.15.1",
59
- "@equationalapplications/react-llm-wiki": "7.15.1"
58
+ "@equationalapplications/core-llm-wiki": "7.15.2",
59
+ "@equationalapplications/react-llm-wiki": "7.15.2"
60
60
  },
61
61
  "peerDependencies": {
62
62
  "expo-crypto": ">=12",