@backendkit-labs/agent-enterprise 0.8.8 → 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 +410 -410
  2. package/package.json +40 -40
package/README.md CHANGED
@@ -1,411 +1,411 @@
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
-
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
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.8",
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.38.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
+ }