@backendkit-labs/agent-enterprise 0.3.0 → 0.3.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 (51) hide show
  1. package/README.md +446 -111
  2. package/dist/factory.d.ts +47 -0
  3. package/dist/factory.d.ts.map +1 -0
  4. package/dist/factory.js +146 -0
  5. package/dist/factory.js.map +1 -0
  6. package/dist/index.d.ts +3 -0
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +15 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/profiles/constants.d.ts +22 -0
  11. package/dist/profiles/constants.d.ts.map +1 -0
  12. package/dist/profiles/constants.js +25 -0
  13. package/dist/profiles/constants.js.map +1 -0
  14. package/dist/profiles/finance.d.ts +3 -0
  15. package/dist/profiles/finance.d.ts.map +1 -0
  16. package/dist/profiles/finance.js +34 -0
  17. package/dist/profiles/finance.js.map +1 -0
  18. package/dist/profiles/general.d.ts +3 -0
  19. package/dist/profiles/general.d.ts.map +1 -0
  20. package/dist/profiles/general.js +32 -0
  21. package/dist/profiles/general.js.map +1 -0
  22. package/dist/profiles/hr.d.ts +3 -0
  23. package/dist/profiles/hr.d.ts.map +1 -0
  24. package/dist/profiles/hr.js +34 -0
  25. package/dist/profiles/hr.js.map +1 -0
  26. package/dist/profiles/index.d.ts +10 -0
  27. package/dist/profiles/index.d.ts.map +1 -0
  28. package/dist/profiles/index.js +33 -0
  29. package/dist/profiles/index.js.map +1 -0
  30. package/dist/profiles/ops.d.ts +3 -0
  31. package/dist/profiles/ops.d.ts.map +1 -0
  32. package/dist/profiles/ops.js +33 -0
  33. package/dist/profiles/ops.js.map +1 -0
  34. package/dist/profiles/sales.d.ts +3 -0
  35. package/dist/profiles/sales.d.ts.map +1 -0
  36. package/dist/profiles/sales.js +30 -0
  37. package/dist/profiles/sales.js.map +1 -0
  38. package/dist/profiles/support.d.ts +3 -0
  39. package/dist/profiles/support.d.ts.map +1 -0
  40. package/dist/profiles/support.js +28 -0
  41. package/dist/profiles/support.js.map +1 -0
  42. package/dist/rag/ObsidianRAGProvider.js +4 -4
  43. package/dist/rag/ObsidianRAGProvider.js.map +1 -1
  44. package/dist/rag/VaultIndexer.d.ts.map +1 -1
  45. package/dist/rag/VaultIndexer.js +4 -1
  46. package/dist/rag/VaultIndexer.js.map +1 -1
  47. package/dist/rag/VaultWriter.d.ts +1 -0
  48. package/dist/rag/VaultWriter.d.ts.map +1 -1
  49. package/dist/rag/VaultWriter.js +8 -3
  50. package/dist/rag/VaultWriter.js.map +1 -1
  51. package/package.json +1 -1
package/README.md CHANGED
@@ -1,187 +1,522 @@
1
1
  # @backendkit-labs/agent-enterprise
2
2
 
3
- **Status: core** — RAG and VaultWriter are required for the enterprise story; changes go through full review.
3
+ Enterprise-grade AI agent setup with RAG (Retrieval-Augmented Generation) over an Obsidian vault, six pre-configured department agent profiles, a vault writing tool, and a factory that wires everything into an `AgentEngine`-compatible setup.
4
4
 
5
- Enterprise knowledge layer for the BackendKit Agent Framework — local RAG over Obsidian vaults, shared knowledge vault with agent write-back, and pluggable embedders.
5
+ [![npm](https://img.shields.io/npm/v/@backendkit-labs/agent-enterprise)](https://www.npmjs.com/package/@backendkit-labs/agent-enterprise)
6
6
 
7
- ```bash
8
- npm install @backendkit-labs/agent-enterprise
9
- ```
7
+ ## Table of contents
10
8
 
11
- > Requires `@backendkit-labs/agent-core`. Works with any LLM provider — no cloud dependency required.
9
+ - [Installation](#installation)
10
+ - [Quick start](#quick-start)
11
+ - [createEnterpriseSetup](#createenterprisesetup)
12
+ - [Enterprise agent profiles](#enterprise-agent-profiles)
13
+ - [RAG pipeline](#rag-pipeline)
14
+ - [VaultIndexer](#vaultindexer)
15
+ - [VaultWriter](#vaultwriter)
16
+ - [Embedders](#embedders)
17
+ - [ObsidianRAGProvider](#obsidianragprovider)
18
+ - [VectorStore](#vectorstore)
19
+ - [Full production example with AgentServer](#full-production-example-with-agentserver)
12
20
 
13
21
  ---
14
22
 
15
- ## What it does
23
+ ## Installation
16
24
 
17
- Gives your agents a **shared, persistent knowledge base** backed by Markdown files:
25
+ ```bash
26
+ npm install @backendkit-labs/agent-enterprise @backendkit-labs/agent-core
27
+ ```
18
28
 
19
- - **Read** — agents search the vault semantically (`search_knowledge` tool)
20
- - **Write** — agents contribute new knowledge back to the vault (`write_knowledge` tool)
21
- - **Index** — incremental indexing by file mtime; only re-embeds new or modified files
22
- - **Human-readable** — the vault is plain Markdown, openable in Obsidian or any editor
29
+ For the full web stack:
30
+
31
+ ```bash
32
+ npm install @backendkit-labs/agent-enterprise @backendkit-labs/agent-core @backendkit-labs/agent-web
33
+ ```
23
34
 
24
35
  ---
25
36
 
26
- ## Quick start — RAG over an Obsidian vault
37
+ ## Quick start
27
38
 
28
- ```ts
29
- import { AgentEngine, ToolRegistry } from '@backendkit-labs/agent-core';
30
- import { ObsidianRAGProvider } from '@backendkit-labs/agent-enterprise';
39
+ ```typescript
40
+ import { createEnterpriseSetup } from '@backendkit-labs/agent-enterprise';
41
+ import { ProviderRegistry } from '@backendkit-labs/agent-core';
42
+ import { AgentServer } from '@backendkit-labs/agent-web';
31
43
 
32
- const rag = new ObsidianRAGProvider({
33
- vaultPath: '/path/to/your/vault',
34
- ollamaHost: 'http://localhost:11434', // default
35
- embeddingModel: 'nomic-embed-text', // default
44
+ // 1. Configure your LLM providers
45
+ const providers = new ProviderRegistry();
46
+ providers.register('openai', myOpenAIProvider);
47
+
48
+ // 2. Create the enterprise setup
49
+ const enterprise = createEnterpriseSetup({
50
+ vaultPath: '/shared/obsidian-vault',
51
+ providers,
52
+ defaultProvider: 'openai',
36
53
  });
37
54
 
38
- // Index the vault (incremental — only changed files on subsequent runs)
39
- await rag.index({ verbose: true });
55
+ // 3. Index the vault (once at startup — subsequent runs only re-index changed files)
56
+ await enterprise.indexAll({ verbose: true });
40
57
 
41
- // Register the search_knowledge tool with your engine
42
- const tools = new ToolRegistry();
43
- tools.register(rag.createTool());
44
- ```
58
+ // 4. Start the web server
59
+ const server = new AgentServer({
60
+ port: 3000,
61
+ engineFactory: enterprise.engineFactory,
62
+ cors: '*',
63
+ });
45
64
 
46
- The agent calls `search_knowledge` automatically when it needs information from the vault.
65
+ await server.start();
66
+ ```
47
67
 
48
68
  ---
49
69
 
50
- ## Shared vault with agent write-back
70
+ ## createEnterpriseSetup
51
71
 
52
- ```ts
53
- import { ObsidianRAGProvider, VaultWriter } from '@backendkit-labs/agent-enterprise';
72
+ The main factory. Returns `{ indexAll, engineFactory }`.
54
73
 
55
- const rag = new ObsidianRAGProvider({ vaultPath: '/vault', ...opts });
56
- const writer = new VaultWriter({ vaultPath: '/vault' });
74
+ ### Options
57
75
 
58
- const tools = new ToolRegistry();
59
- tools.register(rag.createTool()); // search_knowledge
60
- tools.register(writer.createTool('analyst')); // write_knowledge — baked in with agent id
76
+ ```typescript
77
+ interface EnterpriseSetupOptions {
78
+ // Required
79
+ vaultPath: string; // absolute path to the Obsidian vault
80
+ providers: ProviderRegistry;
81
+ defaultProvider: string; // provider ID used when no per-agent override is set
61
82
 
62
- // After the agent writes a note, re-index to make it searchable:
63
- await rag.index();
83
+ // Optional
84
+ indexDir?: string; // where to store index files
85
+ // default: ~/.bk-agent/rag/enterprise/
86
+ embedder?: Embedder; // custom embedder, default: SimpleEmbedder
87
+ sessionsDir?: string; // per-session memory dir
88
+ // default: ~/.bk-agent/sessions/
89
+ extraProfiles?: AgentProfile[]; // add custom agent profiles alongside the 6 defaults
90
+ maxIterations?: number; // per-engine run limit, default: 20
91
+ }
64
92
  ```
65
93
 
66
- Agent-written notes land in `<vaultPath>/agentes/` with standard frontmatter:
94
+ ### Return value
95
+
96
+ ```typescript
97
+ interface EnterpriseSetup {
98
+ // Call once at startup (or periodically to pick up new vault notes)
99
+ indexAll(opts?: { verbose?: boolean }): Promise<void>;
100
+
101
+ // Compatible with AgentServer's engineFactory option
102
+ engineFactory: (sessionId: string, transport: Transport) => AgentEngine;
103
+ }
104
+ ```
105
+
106
+ ### What gets created per session
107
+
108
+ Each call to `engineFactory` assembles a fresh, isolated `AgentEngine` with:
109
+
110
+ - One `ObsidianRAGProvider` per department scoped to that department's vault folders
111
+ - One global `ObsidianRAGProvider` for the General agent (all vault folders)
112
+ - A shared `VaultWriter` for persisting knowledge back to the vault
113
+ - All six enterprise `AgentProfile`s plus any `extraProfiles`
114
+ - Per-session episodic, semantic, and procedural `MemorySystem` stored in `sessionsDir/<sessionId>/`
67
115
 
68
- ```markdown
69
- ---
70
- title: "Cache Decision for RAG Embeddings"
71
- author: "agent/analyst"
72
- date: 2026-06-10
73
- tags: ["área/rag", "tipo/decisión"]
74
- description: "LRU in-memory cache chosen over persistent or semantic cache"
75
116
  ---
76
117
 
77
- ## Context
78
- ...
118
+ ## Enterprise agent profiles
119
+
120
+ Six department profiles are built in. Each has a system prompt, a scoped RAG tool, and an optional provider override for on-prem data isolation.
121
+
122
+ | Profile ID | Agent Name | RAG tool | Default provider |
123
+ |-----------|-----------|----------|-----------------|
124
+ | `general` | General Assistant | `search_kb` (global vault) | `defaultProvider` |
125
+ | `support` | Support Agent | `search_support_kb` | `defaultProvider` |
126
+ | `hr` | HR Agent | `search_hr_kb` | `local` (sensitive) |
127
+ | `sales` | Sales Agent | `search_sales_kb` | `defaultProvider` |
128
+ | `finance` | Finance Agent | `search_finance_kb` | `local` (sensitive) |
129
+ | `ops` | Operations Agent | `search_ops_kb` | `defaultProvider` |
130
+
131
+ HR and Finance default to the `local` provider so sensitive payroll and financial data never leaves your network.
132
+
133
+ ### Using profiles individually
134
+
135
+ ```typescript
136
+ import {
137
+ GENERAL_PROFILE,
138
+ SUPPORT_PROFILE,
139
+ HR_PROFILE,
140
+ SALES_PROFILE,
141
+ FINANCE_PROFILE,
142
+ OPS_PROFILE,
143
+ ALL_ENTERPRISE_PROFILES,
144
+ ENTERPRISE_TOOLS,
145
+ VAULT_FOLDERS,
146
+ } from '@backendkit-labs/agent-enterprise';
147
+
148
+ // Register only the profiles you need
149
+ const agents = new AgentRegistry();
150
+ agents.register(GENERAL_PROFILE);
151
+ agents.register(SUPPORT_PROFILE);
152
+ agents.register({ ...HR_PROFILE, provider: 'ollama' }); // force on-prem
153
+ agents.register({ ...FINANCE_PROFILE, provider: 'ollama' }); // force on-prem
154
+
155
+ // Tool name constants
156
+ ENTERPRISE_TOOLS.SEARCH_KB; // 'search_kb'
157
+ ENTERPRISE_TOOLS.SEARCH_SUPPORT; // 'search_support_kb'
158
+ ENTERPRISE_TOOLS.SEARCH_HR; // 'search_hr_kb'
159
+ ENTERPRISE_TOOLS.SEARCH_SALES; // 'search_sales_kb'
160
+ ENTERPRISE_TOOLS.SEARCH_FINANCE; // 'search_finance_kb'
161
+ ENTERPRISE_TOOLS.SEARCH_OPS; // 'search_ops_kb'
162
+
163
+ // Vault folder name constants
164
+ VAULT_FOLDERS.GENERAL; // 'General'
165
+ VAULT_FOLDERS.SUPPORT; // 'Support'
166
+ VAULT_FOLDERS.HR; // 'HR'
167
+ VAULT_FOLDERS.SALES; // 'Sales'
168
+ VAULT_FOLDERS.FINANCE; // 'Finance'
169
+ VAULT_FOLDERS.OPS; // 'Operations'
170
+ ```
171
+
172
+ ### Adding custom profiles
173
+
174
+ ```typescript
175
+ const enterprise = createEnterpriseSetup({
176
+ ...opts,
177
+ extraProfiles: [
178
+ {
179
+ id: 'legal',
180
+ name: 'Legal Advisor',
181
+ systemPrompt: `You are a legal knowledge assistant.
182
+ Search the knowledge base for relevant precedents and policies.
183
+ Always recommend consulting a licensed attorney for case-specific advice.`,
184
+ tools: ['search_kb'],
185
+ provider: 'local', // sensitive — stays on-prem
186
+ },
187
+ {
188
+ id: 'engineering',
189
+ name: 'Engineering Assistant',
190
+ systemPrompt: 'You are a software engineering assistant with access to our internal technical documentation.',
191
+ tools: ['search_kb', 'write_vault'],
192
+ },
193
+ ],
194
+ });
79
195
  ```
80
196
 
81
197
  ---
82
198
 
83
- ## Custom embedder — no Ollama required
199
+ ## RAG pipeline
84
200
 
85
- Pass any object that implements `Embedder` to skip Ollama:
201
+ The RAG pipeline works in two phases: **indexing** and **search**.
86
202
 
87
- ```ts
88
- import { ObsidianRAGProvider, type Embedder } from '@backendkit-labs/agent-enterprise';
203
+ ### Indexing phase
89
204
 
90
- class MyEmbedder implements Embedder {
91
- readonly model = 'my-model-v1';
92
- async embed(texts: string[]): Promise<number[][]> { /* ... */ }
93
- async embedOne(text: string): Promise<number[]> { /* ... */ }
94
- }
205
+ At startup, `indexAll()` walks each vault folder, chunks the markdown files, embeds each chunk with the configured embedder, and saves the index to disk as JSON.
95
206
 
96
- const rag = new ObsidianRAGProvider({
97
- vaultPath: '/vault',
98
- embedder: new MyEmbedder(), // ollamaHost and embeddingModel are ignored
99
- });
207
+ ```typescript
208
+ // Full or incremental re-index (skips unchanged files)
209
+ await enterprise.indexAll({ verbose: true });
210
+ // [VaultIndexer] Indexed 142 files, 1,847 chunks in 4.2s (38 skipped, unchanged)
211
+ ```
212
+
213
+ ### Search phase
214
+
215
+ When an agent's RAG tool is called, it performs cosine-similarity search over the pre-built index and injects the top-K chunks as context into the LLM prompt:
216
+
217
+ ```
218
+ User: "What is our parental leave policy?"
219
+ → HR agent selected
220
+ → search_hr_kb("parental leave policy")
221
+ → Top 5 chunks from HR/ and General/ folders returned
222
+ → LLM answers using the retrieved excerpts as grounding context
100
223
  ```
101
224
 
102
225
  ---
103
226
 
104
- ## SimpleEmbedder — local testing without Ollama
227
+ ## VaultIndexer
105
228
 
106
- For tests and CI where Ollama is not available:
229
+ Use `VaultIndexer` directly for fine-grained control over indexing.
107
230
 
108
- ```ts
109
- // Not exported from the package — copy from pilot/src/embedders/SimpleEmbedder.ts
110
- // TF-IDF feature hashing, 256 dims, pure Node.js, zero dependencies
111
- const rag = new ObsidianRAGProvider({
112
- vaultPath: '/vault',
113
- embedder: new SimpleEmbedder(),
114
- minScore: 0.1, // lower threshold for keyword-based vectors
231
+ ```typescript
232
+ import { VaultIndexer } from '@backendkit-labs/agent-enterprise';
233
+
234
+ const indexer = new VaultIndexer({
235
+ vaultPath: '/shared/obsidian-vault',
236
+ indexPath: '/cache/support-index.json',
237
+ embedder: myEmbedder,
238
+ folders: ['Support', 'General'], // only index these folders (omit for all)
239
+ chunkSize: 500, // tokens per chunk
240
+ chunkOverlap: 50, // overlap between adjacent chunks
115
241
  });
242
+
243
+ await indexer.index({ verbose: true });
244
+
245
+ const stats = indexer.getStats();
246
+ // { files: 42, chunks: 587, lastIndexedAt: '2026-06-11T09:00:00.000Z' }
116
247
  ```
117
248
 
118
- ---
249
+ ### Incremental re-indexing
250
+
251
+ The indexer tracks file modification times. Re-running `index()` only processes changed or new files:
119
252
 
120
- ## ObsidianRAGProvider options
253
+ ```typescript
254
+ // First run — full index (may take seconds to minutes depending on vault size)
255
+ await indexer.index({ verbose: true });
121
256
 
122
- ```ts
123
- new ObsidianRAGProvider({
124
- vaultPath: string; // absolute path to vault directory
125
- folders?: string[]; // sub-folders to index (default: all)
126
- indexPath?: string; // where to save the vector index JSON
127
- embedder?: Embedder; // custom embedder (overrides ollamaHost/embeddingModel)
128
- ollamaHost?: string; // default: http://localhost:11434
129
- embeddingModel?: string; // default: nomic-embed-text
130
- topK?: number; // max results per search (default: 5)
131
- minScore?: number; // min cosine similarity 0–1 (default: 0.3)
132
- excludeTags?: string[]; // frontmatter tags to skip (default: ['skill'])
133
- })
257
+ // Subsequent runs — only re-indexes changed files (very fast)
258
+ await indexer.index({ verbose: true });
259
+ // [VaultIndexer] 3 files changed, re-indexed 28 chunks
134
260
  ```
135
261
 
136
262
  ---
137
263
 
138
- ## VaultWriter options
264
+ ## VaultWriter
265
+
266
+ `VaultWriter` lets agents persist knowledge back to the Obsidian vault — closing the knowledge loop.
267
+
268
+ ```typescript
269
+ import { VaultWriter } from '@backendkit-labs/agent-enterprise';
270
+
271
+ const writer = new VaultWriter({
272
+ vaultPath: '/shared/obsidian-vault',
273
+ allowedFolders: ['General', 'Support'], // restrict where agents can write
274
+ });
139
275
 
140
- ```ts
141
- new VaultWriter({
142
- vaultPath: string; // must match the ObsidianRAGProvider vaultPath
143
- agentFolder?: string; // sub-folder for agent notes (default: 'agentes')
144
- })
276
+ // Create a ToolDefinition the engine can call
277
+ const writeTool = writer.createTool('general'); // 'write_vault' tool
145
278
 
146
- writer.createTool(agentId: string): ToolDefinition
147
- // Returns write_knowledge tool with agentId baked into each note's author field
279
+ // Write directly (from non-agent code)
280
+ await writer.writeNote({
281
+ folder: 'General',
282
+ filename: 'api-auth-pattern.md',
283
+ content: '# API Auth Pattern\n\nAll endpoints require a Bearer token...',
284
+ tags: ['api', 'patterns', 'security'],
285
+ });
286
+ ```
287
+
288
+ ### How agents use VaultWriter
289
+
290
+ Agents with `write_vault` in their tool list can invoke it directly in conversation:
291
+
292
+ ```
293
+ User: "Document the new API authentication pattern we agreed on"
294
+ → General agent
295
+ → LLM calls write_vault({
296
+ folder: "General",
297
+ filename: "api-auth-2026.md",
298
+ content: "# API Auth Pattern\n\n..."
299
+ })
300
+ → File created/updated in the vault
301
+ → LLM: "I've documented the auth pattern in General/api-auth-2026.md"
148
302
  ```
149
303
 
150
304
  ---
151
305
 
152
- ## Vault structure
306
+ ## Embedders
307
+
308
+ Embedders convert text strings into float vectors for similarity search.
309
+
310
+ ### SimpleEmbedder (default — no external dependencies)
311
+
312
+ A deterministic, hash-based embedder. Requires no API key or running service. Lower accuracy than neural models but always available and zero-latency:
313
+
314
+ ```typescript
315
+ import { SimpleEmbedder } from '@backendkit-labs/agent-enterprise';
316
+
317
+ const embedder = new SimpleEmbedder();
318
+ // Good for: prototyping, CI/CD, air-gapped systems, and fallback
319
+ ```
320
+
321
+ ### OllamaEmbedder (recommended for production)
322
+
323
+ Uses a local Ollama embedding model for high-quality semantic embeddings. Requires a running Ollama instance with an embedding model installed:
324
+
325
+ ```bash
326
+ ollama pull nomic-embed-text # 274 MB — general purpose, fast
327
+ ollama pull mxbai-embed-large # 669 MB — higher accuracy
328
+ ```
329
+
330
+ ```typescript
331
+ import { OllamaEmbedder, OLLAMA_EMBED_DEFAULT_HOST, OLLAMA_EMBED_DEFAULT_MODEL } from '@backendkit-labs/agent-enterprise';
153
332
 
154
- Recommended layout for enterprise deployments:
333
+ const embedder = new OllamaEmbedder({
334
+ host: 'http://localhost:11434', // default: OLLAMA_EMBED_DEFAULT_HOST
335
+ model: 'nomic-embed-text', // default: OLLAMA_EMBED_DEFAULT_MODEL
336
+ });
155
337
 
338
+ const enterprise = createEnterpriseSetup({
339
+ vaultPath: '/vault',
340
+ providers,
341
+ defaultProvider: 'local',
342
+ embedder,
343
+ });
156
344
  ```
157
- vault/
158
- rrhh/ ← HR domain — only HR Agent indexed here
159
- ventas/ ← Sales domain
160
- ops/ ← Operations
161
- compartido/ ← Cross-domain, all agents read
162
- agentes/ ← Auto-created — agent-written notes land here
345
+
346
+ ### Custom embedder (OpenAI, Cohere, Hugging Face, etc.)
347
+
348
+ ```typescript
349
+ import type { Embedder } from '@backendkit-labs/agent-enterprise';
350
+
351
+ class OpenAIEmbedder implements Embedder {
352
+ async embed(texts: string[]): Promise<number[][]> {
353
+ const res = await openai.embeddings.create({
354
+ model: 'text-embedding-3-small',
355
+ input: texts,
356
+ });
357
+ return res.data.map(d => d.embedding);
358
+ }
359
+ }
360
+
361
+ const enterprise = createEnterpriseSetup({
362
+ ...opts,
363
+ embedder: new OpenAIEmbedder(),
364
+ });
163
365
  ```
164
366
 
165
- Mount only the relevant folders per agent:
367
+ ---
368
+
369
+ ## ObsidianRAGProvider
370
+
371
+ Combines `VaultIndexer` + `VectorStore` in one class with a search API and a tool factory. Use it directly for custom RAG configurations.
166
372
 
167
- ```ts
168
- new ObsidianRAGProvider({ vaultPath, folders: ['ventas', 'compartido', 'agentes'] })
373
+ ```typescript
374
+ import { ObsidianRAGProvider } from '@backendkit-labs/agent-enterprise';
375
+
376
+ const rag = new ObsidianRAGProvider({
377
+ vaultPath: '/shared/obsidian-vault',
378
+ indexPath: '/cache/support-index.json',
379
+ embedder: new OllamaEmbedder({ model: 'nomic-embed-text' }),
380
+ folders: ['Support', 'General'],
381
+ topK: 5, // return top 5 results
382
+ minScore: 0.15, // discard results below this similarity threshold
383
+ });
384
+
385
+ // Index the vault
386
+ await rag.index({ verbose: true });
387
+
388
+ // Search
389
+ const results = await rag.search('How do I reset my password?');
390
+ // [
391
+ // { content: "To reset your password...", score: 0.82, source: "Support/password-reset.md" },
392
+ // { content: "Users can request a reset...", score: 0.71, source: "General/user-guide.md" },
393
+ // ]
394
+
395
+ // Create a typed ToolDefinition for the engine
396
+ const searchTool = rag.createTool('search_support_kb');
397
+ // → ToolDefinition with name 'search_support_kb' that calls rag.search()
169
398
  ```
170
399
 
171
400
  ---
172
401
 
173
- ## How indexing works
402
+ ## VectorStore
403
+
404
+ Low-level cosine-similarity search engine. Use it when you have your own embedding pipeline.
405
+
406
+ ```typescript
407
+ import { VectorStore } from '@backendkit-labs/agent-enterprise';
408
+ import type { VectorChunk, SearchResult } from '@backendkit-labs/agent-enterprise';
409
+
410
+ const store = new VectorStore();
411
+
412
+ // Add pre-embedded chunks
413
+ store.add([
414
+ { id: 'c1', content: 'Password reset procedure...', embedding: [...512 floats...], source: 'support.md' },
415
+ { id: 'c2', content: 'Password policy requires...', embedding: [...512 floats...], source: 'policy.md' },
416
+ ]);
174
417
 
175
- 1. `VaultIndexer` scans `.md` files and reads their `mtime`
176
- 2. Compares against stored mtimes in `VectorStore`
177
- 3. Only embeds files that are new or modified since last index
178
- 4. Chunks each file by `##` headings (max 500 words per chunk)
179
- 5. Stores vectors + metadata in a single JSON file on disk
418
+ // Search by query embedding
419
+ const queryEmbeddings = await embedder.embed(['reset password steps']);
420
+ const results: SearchResult[] = store.search(queryEmbeddings[0], { topK: 3, minScore: 0.1 });
180
421
 
181
- The JSON store scales to ~5–10k chunks. For larger vaults, use a proper vector DB (pgvector, Qdrant) with a custom `Embedder` adapter.
422
+ // Persist index to disk
423
+ store.saveToFile('/cache/my-index.json');
424
+
425
+ // Load from disk (on restart)
426
+ const loaded = VectorStore.loadFromFile('/cache/my-index.json');
427
+ ```
182
428
 
183
429
  ---
184
430
 
185
- ## License
431
+ ## Full production example with AgentServer
432
+
433
+ A complete on-prem enterprise deployment with Ollama embeddings, DeepSeek for general agents, local Ollama for HR/Finance, Redis sessions, and JWT auth:
434
+
435
+ ```typescript
436
+ import { createEnterpriseSetup, OllamaEmbedder } from '@backendkit-labs/agent-enterprise';
437
+ import { ProviderRegistry } from '@backendkit-labs/agent-core';
438
+ import { AgentServer, JwtAuth, TriggerBus, RedisSessionMetaStore } from '@backendkit-labs/agent-web';
439
+ import Redis from 'ioredis';
440
+
441
+ // ── LLM Providers ─────────────────────────────────────────────────────────────
442
+
443
+ const providers = new ProviderRegistry();
444
+
445
+ // Cloud: general, support, sales, ops
446
+ providers.register('deepseek', new DeepSeekProvider({
447
+ apiKey: process.env.DEEPSEEK_API_KEY!,
448
+ model: 'deepseek-chat',
449
+ }));
450
+
451
+ // On-prem: HR and Finance (sensitive data)
452
+ providers.register('local', new OllamaProvider({
453
+ host: 'http://ollama.internal:11434',
454
+ model: 'llama3.2',
455
+ }));
456
+
457
+ // ── Enterprise Setup ───────────────────────────────────────────────────────────
458
+
459
+ const enterprise = createEnterpriseSetup({
460
+ vaultPath: process.env.VAULT_PATH!,
461
+ providers,
462
+ defaultProvider: 'deepseek',
463
+ embedder: new OllamaEmbedder({
464
+ host: 'http://ollama.internal:11434',
465
+ model: 'nomic-embed-text',
466
+ }),
467
+ indexDir: '/var/bk-agent/rag-indexes',
468
+ sessionsDir: '/var/bk-agent/sessions',
469
+ maxIterations: 20,
470
+ });
471
+
472
+ await enterprise.indexAll({ verbose: true });
473
+
474
+ // Re-index every night at 2 AM to pick up new vault notes
475
+ import { CronJob } from 'node-cron';
476
+ CronJob.schedule('0 2 * * *', () => enterprise.indexAll());
477
+
478
+ // ── Infrastructure ─────────────────────────────────────────────────────────────
479
+
480
+ const redis = new Redis(process.env.REDIS_URL!);
481
+ const jwt = new JwtAuth({ secret: process.env.JWT_SECRET!, sessionIdClaim: 'sub' });
482
+ const triggers = new TriggerBus(enterprise.engineFactory)
483
+ .addCron({
484
+ name: 'weekly-digest',
485
+ schedule: '0 9 * * 1',
486
+ buildPrompt: () => 'Summarize all knowledge base updates from the past week.',
487
+ onResult: (out) => intranet.post('/announcements', out),
488
+ });
489
+
490
+ // ── Server ─────────────────────────────────────────────────────────────────────
491
+
492
+ const server = new AgentServer({
493
+ port: 3000,
494
+ engineFactory: enterprise.engineFactory,
495
+ auth: jwt.hook(),
496
+ store: new RedisSessionMetaStore(redis),
497
+ cors: ['https://intranet.company.com'],
498
+ rateLimiting: { max: 30, timeWindow: '1 minute' },
499
+ metrics: { enabled: true, prefix: 'enterprise_' },
500
+ maxIdleMs: 30 * 60 * 1000,
501
+ triggers,
502
+ });
503
+
504
+ await server.start();
505
+ console.log('Enterprise agent server running on :3000');
506
+
507
+ process.on('SIGTERM', () => server.stop());
508
+ ```
509
+
510
+ ### Required vault folder structure
511
+
512
+ ```
513
+ /obsidian-vault
514
+ ├── General/ ← all agents can search here
515
+ ├── Support/ ← Support agent
516
+ ├── HR/ ← HR agent (on-prem LLM)
517
+ ├── Sales/ ← Sales agent
518
+ ├── Finance/ ← Finance agent (on-prem LLM)
519
+ └── Operations/ ← Ops agent
520
+ ```
186
521
 
187
- MIT © [BackendKit Labs](https://github.com/BackendKit-labs)
522
+ Create these folders in Obsidian and add `.md` files. The indexer discovers all markdown files recursively.