@andespindola/brainlink 0.1.0-beta.99 → 1.0.1
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.
- package/AGENTS.md +6 -6
- package/CHANGELOG.md +14 -0
- package/README.md +198 -38
- package/dist/application/add-note.js +13 -44
- package/dist/application/analyze-vault.js +1 -1
- package/dist/application/auto-migrate-configured-vault.js +37 -0
- package/dist/application/build-context.js +119 -20
- package/dist/application/canonical-context-links.js +209 -0
- package/dist/application/delete-note.js +80 -0
- package/dist/application/frontend/client-css.js +212 -42
- package/dist/application/frontend/client-html.js +42 -28
- package/dist/application/frontend/client-js.js +1294 -3222
- package/dist/application/frontend/client-render-worker-js.js +676 -0
- package/dist/application/get-graph-contexts.js +33 -0
- package/dist/application/get-graph-layout.js +62 -8
- package/dist/application/get-graph-stream-chunk.js +326 -0
- package/dist/application/get-graph-view.js +246 -0
- package/dist/application/graph-view-state.js +66 -0
- package/dist/application/import-legacy-sqlite.js +3 -33
- package/dist/application/index-vault.js +35 -22
- package/dist/application/migrate-context-links.js +79 -0
- package/dist/application/search-graph-node-ids.js +63 -3
- package/dist/application/server/routes.js +197 -12
- package/dist/cli/commands/read-commands.js +39 -3
- package/dist/cli/commands/vault-commands.js +182 -0
- package/dist/cli/commands/write-commands.js +172 -12
- package/dist/cli/main.js +2 -0
- package/dist/cli/runtime.js +10 -2
- package/dist/domain/context.js +1 -0
- package/dist/domain/graph-contexts.js +180 -0
- package/dist/domain/graph-layout.js +347 -21
- package/dist/domain/markdown.js +53 -9
- package/dist/infrastructure/config.js +105 -6
- package/dist/infrastructure/context-packs.js +122 -0
- package/dist/infrastructure/file-index.js +6 -3
- package/dist/infrastructure/file-system-vault.js +21 -1
- package/dist/infrastructure/index-state.js +2 -0
- package/dist/infrastructure/vault-migration-state.js +69 -0
- package/dist/infrastructure/volatile-memory.js +100 -0
- package/dist/mcp/http-server.js +97 -0
- package/dist/mcp/runtime.js +20 -0
- package/dist/mcp/server.js +41 -13
- package/dist/mcp/tools.js +226 -14
- package/docs/AGENT_USAGE.md +60 -5
- package/docs/ARCHITECTURE.md +11 -0
- package/docs/QUICKSTART.md +3 -1
- package/docs/RELEASE.md +4 -3
- package/package.json +3 -1
package/dist/mcp/server.js
CHANGED
|
@@ -1,18 +1,11 @@
|
|
|
1
1
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import { addNoteInputSchema, addFileInputSchema, addFileTool, addNoteTool, dedupeInputSchema, dedupeResolveInputSchema, dedupeResolveTool, dedupeTool, brokenLinksInputSchema, brokenLinksTool, bootstrapInputSchema, bootstrapTool, contextInputSchema, contextTool, graphInputSchema, graphTool, indexInputSchema, indexTool, orphansInputSchema, orphansTool, policyInputSchema, policyTool, recommendationsInputSchema, recommendationsTool, searchInputSchema, searchTool, statsInputSchema, statsTool, syncInputSchema, syncTool, validateInputSchema, validateTool } from './tools.js';
|
|
6
|
-
const readPackageVersion = () => {
|
|
7
|
-
const packagePath = join(dirname(fileURLToPath(import.meta.url)), '../../package.json');
|
|
8
|
-
const metadata = JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
9
|
-
return metadata.version ?? '0.0.0';
|
|
10
|
-
};
|
|
2
|
+
import { addNoteInputSchema, addFileInputSchema, addFileTool, addNoteTool, deleteNoteInputSchema, deleteNoteTool, volatileAddInputSchema, volatileAddTool, volatileClearInputSchema, volatileClearTool, dedupeInputSchema, dedupeResolveInputSchema, dedupeResolveTool, dedupeTool, brokenLinksInputSchema, brokenLinksTool, bootstrapInputSchema, bootstrapTool, canonicalizeContextLinksInputSchema, canonicalizeContextLinksTool, contextInputSchema, contextPacksInputSchema, contextPacksTool, contextTool, graphContextsInputSchema, graphContextsTool, graphInputSchema, graphTool, indexInputSchema, indexTool, orphansInputSchema, orphansTool, policyInputSchema, policyTool, recommendationsInputSchema, recommendationsTool, searchInputSchema, searchTool, statsInputSchema, statsTool, syncInputSchema, syncTool, validateInputSchema, validateTool, versionInputSchema, versionTool } from './tools.js';
|
|
3
|
+
import { getRuntimeVersion } from './runtime.js';
|
|
11
4
|
export const createBrainlinkMcpServer = () => {
|
|
12
5
|
const server = new McpServer({
|
|
13
6
|
name: 'brainlink',
|
|
14
7
|
title: 'Brainlink',
|
|
15
|
-
version:
|
|
8
|
+
version: getRuntimeVersion(),
|
|
16
9
|
description: 'Local-first Markdown memory tools for AI agents.'
|
|
17
10
|
});
|
|
18
11
|
server.registerTool('brainlink_bootstrap', {
|
|
@@ -25,16 +18,26 @@ export const createBrainlinkMcpServer = () => {
|
|
|
25
18
|
description: 'Read or update bootstrap enforcement policy and inspect bootstrap readiness for the current vault/agent.',
|
|
26
19
|
inputSchema: policyInputSchema
|
|
27
20
|
}, policyTool);
|
|
21
|
+
server.registerTool('brainlink_version', {
|
|
22
|
+
title: 'Read Brainlink Runtime Version',
|
|
23
|
+
description: 'Return the current Brainlink MCP runtime package version and metadata.',
|
|
24
|
+
inputSchema: versionInputSchema
|
|
25
|
+
}, versionTool);
|
|
28
26
|
server.registerTool('brainlink_recommendations', {
|
|
29
27
|
title: 'Brainlink Recommended MCP Workflow',
|
|
30
|
-
description: 'Return a plug-and-play action plan for this vault/agent, including policy, bootstrap, context retrieval and durable write guidance.',
|
|
28
|
+
description: 'Return a plug-and-play action plan for this vault/agent, including policy, bootstrap, RAG/CAG context strategy, retrieval and durable write guidance.',
|
|
31
29
|
inputSchema: recommendationsInputSchema
|
|
32
30
|
}, recommendationsTool);
|
|
33
31
|
server.registerTool('brainlink_context', {
|
|
34
32
|
title: 'Build Brainlink Context',
|
|
35
|
-
description: 'Read indexed Brainlink memory for a task or question. Usually called after brainlink_bootstrap. This is read-only and does not create graph links.',
|
|
33
|
+
description: 'Read indexed Brainlink memory for a task or question. Agents can choose strategy per call: rag for fresh retrieval assembly, cag for persisted context packs, or auto for pack-hit CAG with RAG fallback. Usually called after brainlink_bootstrap. This is read-only and does not create graph links.',
|
|
36
34
|
inputSchema: contextInputSchema
|
|
37
35
|
}, contextTool);
|
|
36
|
+
server.registerTool('brainlink_context_packs', {
|
|
37
|
+
title: 'Manage Brainlink Context Packs',
|
|
38
|
+
description: 'List or clear persisted CAG context packs. Packs are derived artifacts and can be rebuilt from Markdown/index state.',
|
|
39
|
+
inputSchema: contextPacksInputSchema
|
|
40
|
+
}, contextPacksTool);
|
|
38
41
|
server.registerTool('brainlink_search', {
|
|
39
42
|
title: 'Search Brainlink Memory',
|
|
40
43
|
description: 'Search indexed Brainlink notes with FTS, semantic or hybrid retrieval.',
|
|
@@ -55,14 +58,34 @@ export const createBrainlinkMcpServer = () => {
|
|
|
55
58
|
description: 'Write durable Markdown memory, then reindex the vault. Include explicit [[wiki links]] for connected graph memory. Add priority markers near links, such as priority: high, #important or #critical, when a relationship should be weighted higher.',
|
|
56
59
|
inputSchema: addNoteInputSchema
|
|
57
60
|
}, addNoteTool);
|
|
61
|
+
server.registerTool('brainlink_delete_note', {
|
|
62
|
+
title: 'Delete Brainlink Note',
|
|
63
|
+
description: 'Delete a durable Markdown note from the vault after explicit confirmation. Select by title or path; reindexes by default.',
|
|
64
|
+
inputSchema: deleteNoteInputSchema
|
|
65
|
+
}, deleteNoteTool);
|
|
66
|
+
server.registerTool('brainlink_volatile_add', {
|
|
67
|
+
title: 'Add Volatile Brainlink Memory',
|
|
68
|
+
description: 'Write temporary agent-decided memory with TTL. Use for transient task state without polluting durable Markdown memory.',
|
|
69
|
+
inputSchema: volatileAddInputSchema
|
|
70
|
+
}, volatileAddTool);
|
|
71
|
+
server.registerTool('brainlink_volatile_clear', {
|
|
72
|
+
title: 'Clear Volatile Brainlink Memory',
|
|
73
|
+
description: 'Clear active volatile memory for the current vault/agent namespace.',
|
|
74
|
+
inputSchema: volatileClearInputSchema
|
|
75
|
+
}, volatileClearTool);
|
|
58
76
|
server.registerTool('brainlink_add_file', {
|
|
59
77
|
title: 'Ingest Markdown File',
|
|
60
78
|
description: 'Read a local markdown/text file and ingest it as a Brainlink note. Reindex defaults to true.',
|
|
61
79
|
inputSchema: addFileInputSchema
|
|
62
80
|
}, addFileTool);
|
|
81
|
+
server.registerTool('brainlink_canonicalize_context_links', {
|
|
82
|
+
title: 'Canonicalize Brainlink Context Links',
|
|
83
|
+
description: 'Ensure notes have canonical Context Links to inferred context hubs. Supports dry-run and can create missing hub notes.',
|
|
84
|
+
inputSchema: canonicalizeContextLinksInputSchema
|
|
85
|
+
}, canonicalizeContextLinksTool);
|
|
63
86
|
server.registerTool('brainlink_index', {
|
|
64
87
|
title: 'Index Brainlink Vault',
|
|
65
|
-
description: 'Rebuild the local Brainlink index from Markdown notes.',
|
|
88
|
+
description: 'Rebuild the local Brainlink index from Markdown notes. Pass full=true to force a complete source rebuild.',
|
|
66
89
|
inputSchema: indexInputSchema
|
|
67
90
|
}, indexTool);
|
|
68
91
|
server.registerTool('brainlink_stats', {
|
|
@@ -85,6 +108,11 @@ export const createBrainlinkMcpServer = () => {
|
|
|
85
108
|
description: 'Read indexed graph nodes and wiki-link edges. Edges include weight and priority fields so agents can rank importance and priority.',
|
|
86
109
|
inputSchema: graphInputSchema
|
|
87
110
|
}, graphTool);
|
|
111
|
+
server.registerTool('brainlink_graph_contexts', {
|
|
112
|
+
title: 'List Brainlink Graph Contexts',
|
|
113
|
+
description: 'List visual graph contexts used by the Brainlink server to separate memory domains such as preferences, repositories and machine configuration.',
|
|
114
|
+
inputSchema: graphContextsInputSchema
|
|
115
|
+
}, graphContextsTool);
|
|
88
116
|
server.registerTool('brainlink_broken_links', {
|
|
89
117
|
title: 'List Brainlink Broken Links',
|
|
90
118
|
description: 'List unresolved indexed wiki links.',
|
package/dist/mcp/tools.js
CHANGED
|
@@ -3,15 +3,21 @@ import { basename, extname } from 'node:path';
|
|
|
3
3
|
import { z } from 'zod';
|
|
4
4
|
import { getBrokenLinksReport, getOrphansReport, getStats, validateVault } from '../application/analyze-vault.js';
|
|
5
5
|
import { addNoteWithMetadata } from '../application/add-note.js';
|
|
6
|
-
import { buildContextPackage } from '../application/build-context.js';
|
|
6
|
+
import { buildContextPackage, readContextDataSignature } from '../application/build-context.js';
|
|
7
|
+
import { canonicalizeContextLinks } from '../application/canonical-context-links.js';
|
|
8
|
+
import { deleteNote } from '../application/delete-note.js';
|
|
7
9
|
import { resolveDuplicateNotes, scanDuplicateNotes } from '../application/dedupe-notes.js';
|
|
8
10
|
import { getGraph } from '../application/get-graph.js';
|
|
11
|
+
import { getGraphContexts } from '../application/get-graph-contexts.js';
|
|
9
12
|
import { indexVault } from '../application/index-vault.js';
|
|
10
13
|
import { searchKnowledge } from '../application/search-knowledge.js';
|
|
11
|
-
import { resolveAgentRuntimeDefaults, sanitizeSearchMode } from '../infrastructure/config.js';
|
|
14
|
+
import { resolveAgentRuntimeDefaults, sanitizeContextStrategy, sanitizeSearchMode } from '../infrastructure/config.js';
|
|
15
|
+
import { clearContextPacks, listContextPacks } from '../infrastructure/context-packs.js';
|
|
12
16
|
import { loadBrainlinkConfig } from '../infrastructure/config.js';
|
|
13
17
|
import { assertVaultAllowed } from '../infrastructure/file-system-vault.js';
|
|
18
|
+
import { addVolatileMemory, clearVolatileMemory } from '../infrastructure/volatile-memory.js';
|
|
14
19
|
import { getBootstrapPolicy, getBootstrapSessionStatus, getContextSessionStatus, setBootstrapPolicy, touchBootstrapSession, touchContextSession } from '../infrastructure/session-state.js';
|
|
20
|
+
import { getRuntimeMetadata } from './runtime.js';
|
|
15
21
|
const positiveInteger = (fallback) => z
|
|
16
22
|
.number()
|
|
17
23
|
.int()
|
|
@@ -36,6 +42,12 @@ const agentInput = {
|
|
|
36
42
|
const searchModeInput = {
|
|
37
43
|
mode: z.enum(['fts', 'semantic', 'hybrid']).optional().describe('Search mode. Defaults to the Brainlink config value.')
|
|
38
44
|
};
|
|
45
|
+
const contextStrategyInput = {
|
|
46
|
+
strategy: z
|
|
47
|
+
.enum(['rag', 'cag', 'auto'])
|
|
48
|
+
.optional()
|
|
49
|
+
.describe('Context strategy per call. Use rag for fresh retrieval assembly, cag to reuse persisted context packs when fresh, or auto to choose CAG on fresh pack hits and RAG otherwise. Defaults to the Brainlink config value.')
|
|
50
|
+
};
|
|
39
51
|
const resolveExecutionContext = async (input) => {
|
|
40
52
|
const config = await loadBrainlinkConfig();
|
|
41
53
|
const vault = await assertVaultAllowed(input.vault ?? config.vault, config.allowedVaults);
|
|
@@ -118,12 +130,14 @@ const ensureBootstrapReady = async (context, input, toolName) => {
|
|
|
118
130
|
};
|
|
119
131
|
}
|
|
120
132
|
const mode = typeof input.mode === 'string' && ['fts', 'semantic', 'hybrid'].includes(input.mode) ? input.mode : 'hybrid';
|
|
133
|
+
const strategy = sanitizeContextStrategy(typeof input.strategy === 'string' ? input.strategy : undefined, 'rag');
|
|
121
134
|
const query = typeof input.query === 'string' && input.query.trim().length > 0 ? input.query : undefined;
|
|
122
135
|
const bootstrapArgs = {
|
|
123
136
|
vault: context.vault,
|
|
124
137
|
...(context.agent ? { agent: context.agent } : {}),
|
|
125
138
|
...(query ? { query } : {}),
|
|
126
|
-
mode
|
|
139
|
+
mode,
|
|
140
|
+
strategy
|
|
127
141
|
};
|
|
128
142
|
const nextActions = [
|
|
129
143
|
{
|
|
@@ -174,6 +188,7 @@ const ensureContextReady = async (context, input, toolName) => {
|
|
|
174
188
|
? input.contextQuery
|
|
175
189
|
: '<task>';
|
|
176
190
|
const mode = sanitizeSearchMode(typeof input.mode === 'string' ? input.mode : undefined, context.defaults.defaultSearchMode);
|
|
191
|
+
const strategy = sanitizeContextStrategy(typeof input.strategy === 'string' ? input.strategy : undefined, context.defaults.defaultContextStrategy);
|
|
177
192
|
const limit = typeof input.limit === 'number' && Number.isFinite(input.limit) && input.limit > 0
|
|
178
193
|
? input.limit
|
|
179
194
|
: typeof input.contextLimit === 'number' && Number.isFinite(input.contextLimit) && input.contextLimit > 0
|
|
@@ -189,6 +204,7 @@ const ensureContextReady = async (context, input, toolName) => {
|
|
|
189
204
|
...(context.agent ? { agent: context.agent } : {}),
|
|
190
205
|
query: queryFromInput,
|
|
191
206
|
mode,
|
|
207
|
+
strategy,
|
|
192
208
|
limit,
|
|
193
209
|
tokens
|
|
194
210
|
};
|
|
@@ -215,10 +231,17 @@ export const contextInputSchema = {
|
|
|
215
231
|
...vaultInput,
|
|
216
232
|
...agentInput,
|
|
217
233
|
...searchModeInput,
|
|
234
|
+
...contextStrategyInput,
|
|
218
235
|
query: z.string().min(1).describe('Task or question to retrieve Brainlink context for.'),
|
|
219
236
|
limit: optionalPositiveInteger().describe('Maximum search results before context selection.'),
|
|
220
237
|
tokens: optionalPositiveInteger().describe('Maximum estimated context tokens.')
|
|
221
238
|
};
|
|
239
|
+
export const contextPacksInputSchema = {
|
|
240
|
+
...vaultInput,
|
|
241
|
+
...agentInput,
|
|
242
|
+
action: z.enum(['list', 'clear']).optional().default('list').describe('Action to perform on persisted CAG context packs.'),
|
|
243
|
+
staleOnly: z.boolean().optional().default(false).describe('When clearing, remove only packs stale for the current index and volatile-memory signature.')
|
|
244
|
+
};
|
|
222
245
|
export const searchInputSchema = {
|
|
223
246
|
...vaultInput,
|
|
224
247
|
...agentInput,
|
|
@@ -235,7 +258,33 @@ export const addNoteInputSchema = {
|
|
|
235
258
|
.describe('Durable Markdown memory. Include explicit [[wiki links]] and #tags when the memory should be connected. Put priority markers near important links, for example priority: high, #important or #critical.'),
|
|
236
259
|
...agentInput,
|
|
237
260
|
allowSensitive: z.boolean().optional().default(false).describe('Allow content that looks like a secret.'),
|
|
238
|
-
autoIndex: z.boolean().optional().default(true).describe('Reindex vault after writing note.')
|
|
261
|
+
autoIndex: z.boolean().optional().default(true).describe('Reindex vault after writing note.'),
|
|
262
|
+
autoContextLinks: z
|
|
263
|
+
.boolean()
|
|
264
|
+
.optional()
|
|
265
|
+
.describe('Automatically add canonical Context Links to the inferred visual context hub. Defaults to Brainlink config.')
|
|
266
|
+
};
|
|
267
|
+
export const deleteNoteInputSchema = {
|
|
268
|
+
...vaultInput,
|
|
269
|
+
...agentInput,
|
|
270
|
+
title: z.string().min(1).optional().describe('Note title to delete. Use agent to disambiguate namespaced notes.'),
|
|
271
|
+
path: z.string().min(1).optional().describe('Vault-relative or absolute Markdown note path to delete.'),
|
|
272
|
+
confirm: z.boolean().describe('Must be true to confirm deletion.'),
|
|
273
|
+
autoIndex: z.boolean().optional().default(true).describe('Reindex vault after deletion. Defaults to true.')
|
|
274
|
+
};
|
|
275
|
+
export const volatileAddInputSchema = {
|
|
276
|
+
...vaultInput,
|
|
277
|
+
...agentInput,
|
|
278
|
+
content: z
|
|
279
|
+
.string()
|
|
280
|
+
.min(1)
|
|
281
|
+
.describe('Temporary agent-decided memory. Use for current task state, hypotheses, transient user preferences and unconfirmed findings.'),
|
|
282
|
+
ttlMinutes: optionalPositiveInteger().describe('Minutes before this volatile memory expires. Defaults to 240.'),
|
|
283
|
+
tags: z.array(z.string()).optional().default([]).describe('Optional tags for volatile retrieval.')
|
|
284
|
+
};
|
|
285
|
+
export const volatileClearInputSchema = {
|
|
286
|
+
...vaultInput,
|
|
287
|
+
...agentInput
|
|
239
288
|
};
|
|
240
289
|
export const addFileInputSchema = {
|
|
241
290
|
...vaultInput,
|
|
@@ -245,8 +294,20 @@ export const addFileInputSchema = {
|
|
|
245
294
|
autoIndex: z.boolean().optional().default(true).describe('Reindex vault after ingesting file.'),
|
|
246
295
|
allowSensitive: z.boolean().optional().default(false).describe('Allow content that looks like a secret.')
|
|
247
296
|
};
|
|
297
|
+
export const canonicalizeContextLinksInputSchema = {
|
|
298
|
+
...vaultInput,
|
|
299
|
+
...agentInput,
|
|
300
|
+
dryRun: z.boolean().optional().default(false).describe('Preview canonical context-link writes without changing Markdown.'),
|
|
301
|
+
createHubs: z.boolean().optional().default(true).describe('Create missing context hub notes when needed.'),
|
|
302
|
+
autoIndex: z.boolean().optional().default(true).describe('Reindex after canonicalization when files changed.')
|
|
303
|
+
};
|
|
248
304
|
export const indexInputSchema = {
|
|
249
|
-
...vaultInput
|
|
305
|
+
...vaultInput,
|
|
306
|
+
full: z
|
|
307
|
+
.boolean()
|
|
308
|
+
.optional()
|
|
309
|
+
.default(false)
|
|
310
|
+
.describe('Force a complete reindex from Markdown source without reusing unchanged index entries.')
|
|
250
311
|
};
|
|
251
312
|
export const validateInputSchema = {
|
|
252
313
|
...vaultInput,
|
|
@@ -256,6 +317,10 @@ export const graphInputSchema = {
|
|
|
256
317
|
...vaultInput,
|
|
257
318
|
...agentInput
|
|
258
319
|
};
|
|
320
|
+
export const graphContextsInputSchema = {
|
|
321
|
+
...vaultInput,
|
|
322
|
+
...agentInput
|
|
323
|
+
};
|
|
259
324
|
export const brokenLinksInputSchema = {
|
|
260
325
|
...vaultInput,
|
|
261
326
|
...agentInput
|
|
@@ -273,6 +338,7 @@ export const syncInputSchema = {
|
|
|
273
338
|
...agentInput,
|
|
274
339
|
contextQuery: z.string().min(1).optional().describe('Optional context smoke query. Omit to skip context probe.'),
|
|
275
340
|
mode: z.enum(['fts', 'semantic', 'hybrid']).optional().describe('Search mode for the optional context probe. Defaults to config value.'),
|
|
341
|
+
strategy: z.enum(['rag', 'cag', 'auto']).optional().describe('Context strategy for the optional context probe. Defaults to the Brainlink config value.'),
|
|
276
342
|
contextLimit: optionalPositiveInteger().describe('Context smoke result limit when contextQuery is provided.'),
|
|
277
343
|
contextTokens: optionalPositiveInteger().describe('Context smoke token target when contextQuery is provided.')
|
|
278
344
|
};
|
|
@@ -280,6 +346,7 @@ export const bootstrapInputSchema = {
|
|
|
280
346
|
...vaultInput,
|
|
281
347
|
...agentInput,
|
|
282
348
|
...searchModeInput,
|
|
349
|
+
...contextStrategyInput,
|
|
283
350
|
query: z
|
|
284
351
|
.string()
|
|
285
352
|
.min(1)
|
|
@@ -304,10 +371,15 @@ export const policyInputSchema = {
|
|
|
304
371
|
.describe('Run automatic bootstrap during MCP server startup using configured default vault/agent.'),
|
|
305
372
|
staleAfterMinutes: positiveInteger(120).describe('Bootstrap freshness window in minutes before read tools require a new bootstrap.')
|
|
306
373
|
};
|
|
374
|
+
export const versionInputSchema = {
|
|
375
|
+
...vaultInput,
|
|
376
|
+
...agentInput
|
|
377
|
+
};
|
|
307
378
|
export const recommendationsInputSchema = {
|
|
308
379
|
...vaultInput,
|
|
309
380
|
...agentInput,
|
|
310
381
|
...searchModeInput,
|
|
382
|
+
...contextStrategyInput,
|
|
311
383
|
query: z.string().min(1).optional().describe('Optional current task query to generate context-focused recommendations.'),
|
|
312
384
|
limit: optionalPositiveInteger().describe('Optional context limit override for generated recommendations.'),
|
|
313
385
|
tokens: optionalPositiveInteger().describe('Optional context token budget override for generated recommendations.')
|
|
@@ -333,14 +405,16 @@ export const contextTool = async (input) => {
|
|
|
333
405
|
return readiness.preflight;
|
|
334
406
|
}
|
|
335
407
|
const mode = sanitizeSearchMode(input.mode, context.defaults.defaultSearchMode);
|
|
408
|
+
const strategy = sanitizeContextStrategy(input.strategy, context.defaults.defaultContextStrategy);
|
|
336
409
|
const limit = input.limit ?? context.defaults.defaultSearchLimit;
|
|
337
410
|
const tokens = input.tokens ?? context.defaults.defaultContextTokens;
|
|
338
|
-
const contextPackage = await buildContextPackage(context.vault, input.query, limit, tokens, context.agent, mode);
|
|
411
|
+
const contextPackage = await buildContextPackage(context.vault, input.query, limit, tokens, context.agent, mode, strategy, context.defaults.defaultContextCacheTtlMs);
|
|
339
412
|
const contextSession = await touchContextSession(context.vault, context.agent);
|
|
340
413
|
return jsonResult({
|
|
341
414
|
vault: context.vault,
|
|
342
415
|
agent: context.agent,
|
|
343
416
|
mode,
|
|
417
|
+
strategy,
|
|
344
418
|
limit,
|
|
345
419
|
tokens,
|
|
346
420
|
contextSession,
|
|
@@ -348,6 +422,32 @@ export const contextTool = async (input) => {
|
|
|
348
422
|
...contextPackage
|
|
349
423
|
});
|
|
350
424
|
};
|
|
425
|
+
export const contextPacksTool = async (input) => {
|
|
426
|
+
const context = await resolveExecutionContext(input);
|
|
427
|
+
const dataSignature = await readContextDataSignature(context.vault);
|
|
428
|
+
if (input.action === 'clear') {
|
|
429
|
+
const result = await clearContextPacks(context.vault, {
|
|
430
|
+
staleOnly: input.staleOnly === true,
|
|
431
|
+
dataSignature
|
|
432
|
+
});
|
|
433
|
+
return jsonResult({
|
|
434
|
+
vault: context.vault,
|
|
435
|
+
agent: context.agent,
|
|
436
|
+
dataSignature,
|
|
437
|
+
action: 'clear',
|
|
438
|
+
staleOnly: input.staleOnly === true,
|
|
439
|
+
...result
|
|
440
|
+
});
|
|
441
|
+
}
|
|
442
|
+
const packs = await listContextPacks(context.vault, dataSignature);
|
|
443
|
+
return jsonResult({
|
|
444
|
+
vault: context.vault,
|
|
445
|
+
agent: context.agent,
|
|
446
|
+
dataSignature,
|
|
447
|
+
action: 'list',
|
|
448
|
+
packs
|
|
449
|
+
});
|
|
450
|
+
};
|
|
351
451
|
export const searchTool = async (input) => {
|
|
352
452
|
const context = await resolveExecutionContext(input);
|
|
353
453
|
const readiness = await ensureBootstrapReady(context, input, 'brainlink_search');
|
|
@@ -376,7 +476,8 @@ export const addNoteTool = async (input) => {
|
|
|
376
476
|
const context = await resolveExecutionContext(input);
|
|
377
477
|
const shouldIndex = isTruthy(input.autoIndex);
|
|
378
478
|
const added = await addNoteWithMetadata(context.vault, input.title, input.content, context.agent, {
|
|
379
|
-
allowSensitive: input.allowSensitive
|
|
479
|
+
allowSensitive: input.allowSensitive,
|
|
480
|
+
autoContextLinks: input.autoContextLinks ?? context.config.autoCanonicalContextLinks
|
|
380
481
|
});
|
|
381
482
|
const index = shouldIndex ? await indexVault(context.vault) : undefined;
|
|
382
483
|
const focusPath = added.path.includes('agents/') ? added.path.slice(added.path.indexOf('agents/')).replaceAll('\\', '/') : undefined;
|
|
@@ -395,12 +496,47 @@ export const addNoteTool = async (input) => {
|
|
|
395
496
|
writeConnectivity: {
|
|
396
497
|
autoLinked: added.autoLinked,
|
|
397
498
|
linkTarget: added.linkTarget,
|
|
398
|
-
|
|
499
|
+
context: added.context,
|
|
500
|
+
hubCreated: added.hubCreated,
|
|
501
|
+
guaranteedEdge: added.autoLinked
|
|
399
502
|
},
|
|
400
503
|
possibleDuplicates,
|
|
401
504
|
...(index ? { index } : {})
|
|
402
505
|
});
|
|
403
506
|
};
|
|
507
|
+
export const deleteNoteTool = async (input) => {
|
|
508
|
+
const context = await resolveExecutionContext(input);
|
|
509
|
+
const result = await deleteNote(context.vault, {
|
|
510
|
+
title: input.title,
|
|
511
|
+
path: input.path,
|
|
512
|
+
agentId: context.agent,
|
|
513
|
+
confirm: input.confirm,
|
|
514
|
+
autoIndex: input.autoIndex
|
|
515
|
+
});
|
|
516
|
+
return jsonResult({
|
|
517
|
+
vault: context.vault,
|
|
518
|
+
...result
|
|
519
|
+
});
|
|
520
|
+
};
|
|
521
|
+
export const volatileAddTool = async (input) => {
|
|
522
|
+
const context = await resolveExecutionContext(input);
|
|
523
|
+
const entry = await addVolatileMemory(context.vault, input.content, context.agent ?? 'shared', input.ttlMinutes ?? 240, input.tags);
|
|
524
|
+
return jsonResult({
|
|
525
|
+
vault: context.vault,
|
|
526
|
+
agent: context.agent,
|
|
527
|
+
volatile: true,
|
|
528
|
+
entry
|
|
529
|
+
});
|
|
530
|
+
};
|
|
531
|
+
export const volatileClearTool = async (input) => {
|
|
532
|
+
const context = await resolveExecutionContext(input);
|
|
533
|
+
const cleared = await clearVolatileMemory(context.vault, context.agent);
|
|
534
|
+
return jsonResult({
|
|
535
|
+
vault: context.vault,
|
|
536
|
+
agent: context.agent,
|
|
537
|
+
cleared
|
|
538
|
+
});
|
|
539
|
+
};
|
|
404
540
|
export const addFileTool = async (input) => {
|
|
405
541
|
const context = await resolveExecutionContext(input);
|
|
406
542
|
const content = await readFile(input.filePath, 'utf8');
|
|
@@ -411,7 +547,8 @@ export const addFileTool = async (input) => {
|
|
|
411
547
|
}
|
|
412
548
|
const shouldIndex = isTruthy(input.autoIndex);
|
|
413
549
|
const added = await addNoteWithMetadata(context.vault, title, content, context.agent, {
|
|
414
|
-
allowSensitive: input.allowSensitive
|
|
550
|
+
allowSensitive: input.allowSensitive,
|
|
551
|
+
autoContextLinks: context.config.autoCanonicalContextLinks
|
|
415
552
|
});
|
|
416
553
|
const index = shouldIndex ? await indexVault(context.vault) : undefined;
|
|
417
554
|
return jsonResult({
|
|
@@ -423,14 +560,35 @@ export const addFileTool = async (input) => {
|
|
|
423
560
|
writeConnectivity: {
|
|
424
561
|
autoLinked: added.autoLinked,
|
|
425
562
|
linkTarget: added.linkTarget,
|
|
426
|
-
|
|
563
|
+
context: added.context,
|
|
564
|
+
hubCreated: added.hubCreated,
|
|
565
|
+
guaranteedEdge: added.autoLinked
|
|
427
566
|
},
|
|
428
567
|
...(index ? { index } : {})
|
|
429
568
|
});
|
|
430
569
|
};
|
|
570
|
+
export const canonicalizeContextLinksTool = async (input) => {
|
|
571
|
+
const context = await resolveExecutionContext(input);
|
|
572
|
+
const result = await canonicalizeContextLinks(context.vault, {
|
|
573
|
+
agentId: context.agent,
|
|
574
|
+
dryRun: input.dryRun === true,
|
|
575
|
+
createMissingHubs: input.createHubs !== false
|
|
576
|
+
});
|
|
577
|
+
const index = input.autoIndex !== false && !result.dryRun && result.changed > 0
|
|
578
|
+
? await indexVault(context.vault, { full: true })
|
|
579
|
+
: undefined;
|
|
580
|
+
return jsonResult({
|
|
581
|
+
vault: context.vault,
|
|
582
|
+
agent: context.agent,
|
|
583
|
+
...result,
|
|
584
|
+
...(index ? { index } : {})
|
|
585
|
+
});
|
|
586
|
+
};
|
|
431
587
|
export const indexTool = async (input) => {
|
|
432
588
|
const context = await resolveExecutionContext(input);
|
|
433
|
-
const result = await indexVault(context.vault
|
|
589
|
+
const result = await indexVault(context.vault, {
|
|
590
|
+
full: input.full === true
|
|
591
|
+
});
|
|
434
592
|
return jsonResult({
|
|
435
593
|
vault: context.vault,
|
|
436
594
|
...result
|
|
@@ -474,6 +632,25 @@ export const graphTool = async (input) => {
|
|
|
474
632
|
...graph
|
|
475
633
|
});
|
|
476
634
|
};
|
|
635
|
+
export const graphContextsTool = async (input) => {
|
|
636
|
+
const context = await resolveExecutionContext(input);
|
|
637
|
+
const readiness = await ensureBootstrapReady(context, input, 'brainlink_graph_contexts');
|
|
638
|
+
if (readiness.preflight) {
|
|
639
|
+
return readiness.preflight;
|
|
640
|
+
}
|
|
641
|
+
const contextReadiness = await ensureContextReady(context, input, 'brainlink_graph_contexts');
|
|
642
|
+
if (contextReadiness.preflight) {
|
|
643
|
+
return contextReadiness.preflight;
|
|
644
|
+
}
|
|
645
|
+
const contexts = await getGraphContexts(context.vault, context.agent);
|
|
646
|
+
return jsonResult({
|
|
647
|
+
vault: context.vault,
|
|
648
|
+
agent: context.agent,
|
|
649
|
+
...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
|
|
650
|
+
...(contextReadiness.context ? { contextReadiness: contextReadiness.context } : {}),
|
|
651
|
+
contexts
|
|
652
|
+
});
|
|
653
|
+
};
|
|
477
654
|
export const brokenLinksTool = async (input) => {
|
|
478
655
|
const context = await resolveExecutionContext(input);
|
|
479
656
|
const readiness = await ensureBootstrapReady(context, input, 'brainlink_broken_links');
|
|
@@ -561,14 +738,16 @@ export const syncTool = async (input) => {
|
|
|
561
738
|
return jsonResult(response);
|
|
562
739
|
}
|
|
563
740
|
const mode = sanitizeSearchMode(input.mode, context.defaults.defaultSearchMode);
|
|
741
|
+
const strategy = sanitizeContextStrategy(input.strategy, context.defaults.defaultContextStrategy);
|
|
564
742
|
const contextLimit = input.contextLimit ?? context.defaults.defaultSearchLimit;
|
|
565
743
|
const contextTokens = input.contextTokens ?? context.defaults.defaultContextTokens;
|
|
566
|
-
const contextPackage = await buildContextPackage(context.vault, input.contextQuery, contextLimit, contextTokens, context.agent, mode);
|
|
744
|
+
const contextPackage = await buildContextPackage(context.vault, input.contextQuery, contextLimit, contextTokens, context.agent, mode, strategy, context.defaults.defaultContextCacheTtlMs);
|
|
567
745
|
const contextSession = await touchContextSession(context.vault, context.agent);
|
|
568
746
|
return jsonResult({
|
|
569
747
|
...response,
|
|
570
748
|
context: {
|
|
571
749
|
mode,
|
|
750
|
+
strategy,
|
|
572
751
|
contextSession,
|
|
573
752
|
...contextPackage
|
|
574
753
|
}
|
|
@@ -580,10 +759,11 @@ export const bootstrapTool = async (input) => {
|
|
|
580
759
|
const stats = await getStats(context.vault, context.agent);
|
|
581
760
|
const validation = await validateVault(context.vault, context.agent);
|
|
582
761
|
const mode = sanitizeSearchMode(input.mode, context.defaults.defaultSearchMode);
|
|
762
|
+
const strategy = sanitizeContextStrategy(input.strategy, context.defaults.defaultContextStrategy);
|
|
583
763
|
const limit = input.limit ?? context.defaults.defaultSearchLimit;
|
|
584
764
|
const tokens = input.tokens ?? context.defaults.defaultContextTokens;
|
|
585
765
|
const contextPackage = input.query
|
|
586
|
-
? await buildContextPackage(context.vault, input.query, limit, tokens, context.agent, mode)
|
|
766
|
+
? await buildContextPackage(context.vault, input.query, limit, tokens, context.agent, mode, strategy, context.defaults.defaultContextCacheTtlMs)
|
|
587
767
|
: undefined;
|
|
588
768
|
const contextSession = input.query ? await touchContextSession(context.vault, context.agent) : undefined;
|
|
589
769
|
const guidance = stats.documentCount === 0
|
|
@@ -637,6 +817,7 @@ export const bootstrapTool = async (input) => {
|
|
|
637
817
|
...(context.agent ? { agent: context.agent } : {}),
|
|
638
818
|
query: '<task>',
|
|
639
819
|
mode,
|
|
820
|
+
strategy,
|
|
640
821
|
limit,
|
|
641
822
|
tokens
|
|
642
823
|
}
|
|
@@ -646,6 +827,7 @@ export const bootstrapTool = async (input) => {
|
|
|
646
827
|
vault: context.vault,
|
|
647
828
|
agent: context.agent,
|
|
648
829
|
mode,
|
|
830
|
+
strategy,
|
|
649
831
|
limit,
|
|
650
832
|
tokens,
|
|
651
833
|
index,
|
|
@@ -701,7 +883,8 @@ export const policyTool = async (input) => {
|
|
|
701
883
|
args: {
|
|
702
884
|
vault: context.vault,
|
|
703
885
|
...(context.agent ? { agent: context.agent } : {}),
|
|
704
|
-
mode: context.defaults.defaultSearchMode
|
|
886
|
+
mode: context.defaults.defaultSearchMode,
|
|
887
|
+
strategy: context.defaults.defaultContextStrategy
|
|
705
888
|
}
|
|
706
889
|
}
|
|
707
890
|
];
|
|
@@ -716,6 +899,7 @@ export const policyTool = async (input) => {
|
|
|
716
899
|
...(context.agent ? { agent: context.agent } : {}),
|
|
717
900
|
query: '<task>',
|
|
718
901
|
mode: context.defaults.defaultSearchMode,
|
|
902
|
+
strategy: context.defaults.defaultContextStrategy,
|
|
719
903
|
limit: context.defaults.defaultSearchLimit,
|
|
720
904
|
tokens: context.defaults.defaultContextTokens
|
|
721
905
|
}
|
|
@@ -725,12 +909,21 @@ export const policyTool = async (input) => {
|
|
|
725
909
|
return jsonResult(withNextActions({
|
|
726
910
|
vault: context.vault,
|
|
727
911
|
agent: context.agent,
|
|
912
|
+
runtime: getRuntimeMetadata(),
|
|
728
913
|
policy,
|
|
729
914
|
bootstrapStatus,
|
|
730
915
|
contextStatus,
|
|
731
916
|
...(input.preset ? { presetApplied: input.preset } : {})
|
|
732
917
|
}, withContextAction));
|
|
733
918
|
};
|
|
919
|
+
export const versionTool = async (input) => {
|
|
920
|
+
const context = await resolveExecutionContext(input);
|
|
921
|
+
return jsonResult({
|
|
922
|
+
vault: context.vault,
|
|
923
|
+
agent: context.agent,
|
|
924
|
+
runtime: getRuntimeMetadata()
|
|
925
|
+
});
|
|
926
|
+
};
|
|
734
927
|
export const recommendationsTool = async (input) => {
|
|
735
928
|
const context = await resolveExecutionContext(input);
|
|
736
929
|
const policy = await getBootstrapPolicy();
|
|
@@ -738,6 +931,7 @@ export const recommendationsTool = async (input) => {
|
|
|
738
931
|
const contextStatus = await getContextSessionStatus(context.vault, context.agent);
|
|
739
932
|
const stats = await getStats(context.vault, context.agent);
|
|
740
933
|
const mode = sanitizeSearchMode(input.mode, context.defaults.defaultSearchMode);
|
|
934
|
+
const strategy = sanitizeContextStrategy(input.strategy, context.defaults.defaultContextStrategy);
|
|
741
935
|
const limit = input.limit ?? context.defaults.defaultSearchLimit;
|
|
742
936
|
const tokens = input.tokens ?? context.defaults.defaultContextTokens;
|
|
743
937
|
const query = input.query?.trim();
|
|
@@ -762,6 +956,7 @@ export const recommendationsTool = async (input) => {
|
|
|
762
956
|
vault: context.vault,
|
|
763
957
|
...(context.agent ? { agent: context.agent } : {}),
|
|
764
958
|
mode,
|
|
959
|
+
strategy,
|
|
765
960
|
...(query ? { query } : {})
|
|
766
961
|
}
|
|
767
962
|
}
|
|
@@ -777,6 +972,7 @@ export const recommendationsTool = async (input) => {
|
|
|
777
972
|
...(context.agent ? { agent: context.agent } : {}),
|
|
778
973
|
query: query ?? '<task>',
|
|
779
974
|
mode,
|
|
975
|
+
strategy,
|
|
780
976
|
limit,
|
|
781
977
|
tokens
|
|
782
978
|
}
|
|
@@ -812,6 +1008,7 @@ export const recommendationsTool = async (input) => {
|
|
|
812
1008
|
...(context.agent ? { agent: context.agent } : {}),
|
|
813
1009
|
query: query ?? '<task>',
|
|
814
1010
|
mode,
|
|
1011
|
+
strategy,
|
|
815
1012
|
limit,
|
|
816
1013
|
tokens
|
|
817
1014
|
}
|
|
@@ -843,9 +1040,24 @@ export const recommendationsTool = async (input) => {
|
|
|
843
1040
|
agent: context.agent,
|
|
844
1041
|
defaults: {
|
|
845
1042
|
mode,
|
|
1043
|
+
strategy,
|
|
846
1044
|
limit,
|
|
847
1045
|
tokens
|
|
848
1046
|
},
|
|
1047
|
+
contextStrategies: [
|
|
1048
|
+
{
|
|
1049
|
+
strategy: 'rag',
|
|
1050
|
+
useWhen: 'Use for fresh retrieval and context assembly from the current index.'
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
strategy: 'cag',
|
|
1054
|
+
useWhen: 'Use for repeated or stable task context so Brainlink can reuse a fresh persisted context pack.'
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
strategy: 'auto',
|
|
1058
|
+
useWhen: 'Use when the agent wants Brainlink to choose CAG on fresh pack hits and RAG otherwise.'
|
|
1059
|
+
}
|
|
1060
|
+
],
|
|
849
1061
|
policy,
|
|
850
1062
|
bootstrapStatus,
|
|
851
1063
|
contextStatus,
|