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