@andespindola/brainlink 1.0.5 → 1.0.7

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 (61) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/README.md +46 -0
  3. package/dist/application/add-note.js +2 -2
  4. package/dist/application/build-context.js +16 -10
  5. package/dist/application/canonical-context-links.js +44 -5
  6. package/dist/application/check-package-update.js +105 -0
  7. package/dist/application/find-similar-notes.js +75 -0
  8. package/dist/application/frontend/client/chunk-fetch.js +236 -0
  9. package/dist/application/frontend/client/controls.js +178 -0
  10. package/dist/application/frontend/client/elements.js +122 -0
  11. package/dist/application/frontend/client/input.js +202 -0
  12. package/dist/application/frontend/client/node-details.js +191 -0
  13. package/dist/application/frontend/client/rendering.js +296 -0
  14. package/dist/application/frontend/client/scope-theme.js +114 -0
  15. package/dist/application/frontend/client/spatial.js +98 -0
  16. package/dist/application/frontend/client/storage.js +215 -0
  17. package/dist/application/frontend/client/upload.js +90 -0
  18. package/dist/application/frontend/client/worker-bootstrap.js +147 -0
  19. package/dist/application/frontend/client-js.js +24 -1837
  20. package/dist/application/frontend/client-render-worker-js.js +1 -1
  21. package/dist/application/index-vault-phases.js +190 -0
  22. package/dist/application/index-vault.js +46 -167
  23. package/dist/application/memory-suggestions.js +4 -1
  24. package/dist/application/search-knowledge.js +19 -3
  25. package/dist/cli/commands/write/dedupe-commands.js +59 -0
  26. package/dist/cli/commands/write/index-commands.js +205 -0
  27. package/dist/cli/commands/write/link-commands.js +68 -0
  28. package/dist/cli/commands/write/note-commands.js +146 -0
  29. package/dist/cli/commands/write/server-commands.js +553 -0
  30. package/dist/cli/commands/write/shared.js +35 -0
  31. package/dist/cli/commands/write/vault-lifecycle-commands.js +270 -0
  32. package/dist/cli/commands/write-commands.js +12 -1303
  33. package/dist/cli/main.js +39 -3
  34. package/dist/domain/context.js +60 -11
  35. package/dist/domain/diversity.js +64 -0
  36. package/dist/domain/embeddings.js +148 -6
  37. package/dist/domain/graph-contexts.js +62 -57
  38. package/dist/domain/graph-layout/cauliflower-layout.js +116 -0
  39. package/dist/domain/graph-layout/collisions.js +100 -0
  40. package/dist/domain/graph-layout/hierarchy.js +135 -0
  41. package/dist/domain/graph-layout/metrics.js +111 -0
  42. package/dist/domain/graph-layout/segments.js +76 -0
  43. package/dist/domain/graph-layout/star-layout.js +110 -0
  44. package/dist/domain/graph-layout.js +4 -625
  45. package/dist/domain/markdown.js +10 -1
  46. package/dist/domain/scoring.js +42 -0
  47. package/dist/domain/tokens.js +17 -1
  48. package/dist/infrastructure/config.js +33 -1
  49. package/dist/infrastructure/file-index.js +227 -60
  50. package/dist/infrastructure/semantic-prefilter.js +24 -0
  51. package/dist/mcp/server.js +23 -1
  52. package/dist/mcp/tool-guard.js +29 -0
  53. package/dist/mcp/tools/maintenance-tools.js +409 -0
  54. package/dist/mcp/tools/read-tools.js +537 -0
  55. package/dist/mcp/tools/shared.js +216 -0
  56. package/dist/mcp/tools/write-tools.js +353 -0
  57. package/dist/mcp/tools.js +3 -1357
  58. package/docs/AGENT_USAGE.md +4 -1
  59. package/docs/ARCHITECTURE.md +4 -3
  60. package/docs/QUICKSTART.md +4 -0
  61. package/package.json +2 -2
@@ -0,0 +1,537 @@
1
+ import { z } from 'zod';
2
+ import { getBrokenLinksReport, getOrphansReport, getStats, validateVault } from '../../application/analyze-vault.js';
3
+ import { buildContextPackage, readContextDataSignature } from '../../application/build-context.js';
4
+ import { getGraph } from '../../application/get-graph.js';
5
+ import { getGraphContexts } from '../../application/get-graph-contexts.js';
6
+ import { explainSearchResults, suggestBrokenLinkFixes, suggestContextLinks } from '../../application/memory-suggestions.js';
7
+ import { buildActionableDoctor } from '../../application/operational-workflows.js';
8
+ import { searchKnowledge } from '../../application/search-knowledge.js';
9
+ import { findSimilarNotes } from '../../application/find-similar-notes.js';
10
+ import { sanitizeContextStrategy, sanitizeSearchMode } from '../../infrastructure/config.js';
11
+ import { clearContextPacks, listContextPacks } from '../../infrastructure/context-packs.js';
12
+ import { getBootstrapPolicy, getBootstrapSessionStatus, getContextSessionStatus, touchContextSession } from '../../infrastructure/session-state.js';
13
+ import { getRuntimeMetadata } from '../runtime.js';
14
+ import { agentInput, contextStrategyInput, ensureBootstrapReady, ensureContextReady, jsonResult, optionalPositiveInteger, positiveInteger, resolveExecutionContext, searchModeInput, vaultInput } from './shared.js';
15
+ export const contextInputSchema = {
16
+ ...vaultInput,
17
+ ...agentInput,
18
+ ...searchModeInput,
19
+ ...contextStrategyInput,
20
+ query: z.string().min(1).describe('Task or question to retrieve Brainlink context for.'),
21
+ limit: optionalPositiveInteger().describe('Maximum search results before context selection.'),
22
+ tokens: optionalPositiveInteger().describe('Maximum estimated context tokens.')
23
+ };
24
+ export const contextPacksInputSchema = {
25
+ ...vaultInput,
26
+ ...agentInput,
27
+ action: z.enum(['list', 'clear']).optional().default('list').describe('Action to perform on persisted CAG context packs.'),
28
+ staleOnly: z.boolean().optional().default(false).describe('When clearing, remove only packs stale for the current index and volatile-memory signature.')
29
+ };
30
+ export const searchInputSchema = {
31
+ ...vaultInput,
32
+ ...agentInput,
33
+ ...searchModeInput,
34
+ query: z.string().min(1).describe('Search query.'),
35
+ limit: optionalPositiveInteger().describe('Maximum result count.')
36
+ };
37
+ export const explainInputSchema = {
38
+ ...vaultInput,
39
+ ...agentInput,
40
+ ...searchModeInput,
41
+ query: z.string().min(1).describe('Search query to explain.'),
42
+ limit: optionalPositiveInteger().describe('Maximum result count.')
43
+ };
44
+ export const similarNotesInputSchema = {
45
+ ...vaultInput,
46
+ ...agentInput,
47
+ ...searchModeInput,
48
+ title: z.string().min(1).optional().describe('Title of the note to find neighbors for. Use agent to disambiguate.'),
49
+ path: z.string().min(1).optional().describe('Vault-relative or absolute path of the note to find neighbors for.'),
50
+ limit: optionalPositiveInteger().describe('Maximum number of similar notes to return. Defaults to the configured search limit.')
51
+ };
52
+ export const validateInputSchema = {
53
+ ...vaultInput,
54
+ ...agentInput
55
+ };
56
+ export const doctorActionsInputSchema = {
57
+ ...vaultInput,
58
+ ...agentInput
59
+ };
60
+ export const graphInputSchema = {
61
+ ...vaultInput,
62
+ ...agentInput
63
+ };
64
+ export const graphContextsInputSchema = {
65
+ ...vaultInput,
66
+ ...agentInput
67
+ };
68
+ export const brokenLinksInputSchema = {
69
+ ...vaultInput,
70
+ ...agentInput
71
+ };
72
+ export const suggestLinksInputSchema = {
73
+ ...vaultInput,
74
+ ...agentInput,
75
+ content: z.string().min(1).optional().describe('Content to inspect for Context Link suggestions. Required unless broken=true.'),
76
+ broken: z.boolean().optional().default(false).describe('Suggest fixes for unresolved wiki links instead of content links.'),
77
+ limit: positiveInteger(5).describe('Maximum suggestions to return.')
78
+ };
79
+ export const orphansInputSchema = {
80
+ ...vaultInput,
81
+ ...agentInput
82
+ };
83
+ export const statsInputSchema = {
84
+ ...vaultInput,
85
+ ...agentInput
86
+ };
87
+ export const versionInputSchema = {
88
+ ...vaultInput,
89
+ ...agentInput
90
+ };
91
+ export const recommendationsInputSchema = {
92
+ ...vaultInput,
93
+ ...agentInput,
94
+ ...searchModeInput,
95
+ ...contextStrategyInput,
96
+ query: z.string().min(1).optional().describe('Optional current task query to generate context-focused recommendations.'),
97
+ limit: optionalPositiveInteger().describe('Optional context limit override for generated recommendations.'),
98
+ tokens: optionalPositiveInteger().describe('Optional context token budget override for generated recommendations.')
99
+ };
100
+ export const contextTool = async (input) => {
101
+ const context = await resolveExecutionContext(input);
102
+ const readiness = await ensureBootstrapReady(context, input, 'brainlink_context');
103
+ if (readiness.preflight) {
104
+ return readiness.preflight;
105
+ }
106
+ const mode = sanitizeSearchMode(input.mode, context.defaults.defaultSearchMode);
107
+ const strategy = sanitizeContextStrategy(input.strategy, context.defaults.defaultContextStrategy);
108
+ const limit = input.limit ?? context.defaults.defaultSearchLimit;
109
+ const tokens = input.tokens ?? context.defaults.defaultContextTokens;
110
+ const contextPackage = await buildContextPackage(context.vault, input.query, limit, tokens, context.agent, mode, strategy, context.defaults.defaultContextCacheTtlMs);
111
+ const contextSession = await touchContextSession(context.vault, context.agent);
112
+ return jsonResult({
113
+ vault: context.vault,
114
+ agent: context.agent,
115
+ mode,
116
+ strategy,
117
+ limit,
118
+ tokens,
119
+ contextSession,
120
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
121
+ ...contextPackage
122
+ });
123
+ };
124
+ export const contextPacksTool = async (input) => {
125
+ const context = await resolveExecutionContext(input);
126
+ const dataSignature = await readContextDataSignature(context.vault);
127
+ if (input.action === 'clear') {
128
+ const result = await clearContextPacks(context.vault, {
129
+ staleOnly: input.staleOnly === true,
130
+ dataSignature
131
+ });
132
+ return jsonResult({
133
+ vault: context.vault,
134
+ agent: context.agent,
135
+ dataSignature,
136
+ action: 'clear',
137
+ staleOnly: input.staleOnly === true,
138
+ ...result
139
+ });
140
+ }
141
+ const packs = await listContextPacks(context.vault, dataSignature);
142
+ return jsonResult({
143
+ vault: context.vault,
144
+ agent: context.agent,
145
+ dataSignature,
146
+ action: 'list',
147
+ packs
148
+ });
149
+ };
150
+ export const searchTool = async (input) => {
151
+ const context = await resolveExecutionContext(input);
152
+ const readiness = await ensureBootstrapReady(context, input, 'brainlink_search');
153
+ if (readiness.preflight) {
154
+ return readiness.preflight;
155
+ }
156
+ const contextReadiness = await ensureContextReady(context, input, 'brainlink_search');
157
+ if (contextReadiness.preflight) {
158
+ return contextReadiness.preflight;
159
+ }
160
+ const mode = sanitizeSearchMode(input.mode, context.defaults.defaultSearchMode);
161
+ const limit = input.limit ?? context.defaults.defaultSearchLimit;
162
+ const results = await searchKnowledge(context.vault, input.query, limit, context.agent, mode);
163
+ return jsonResult({
164
+ vault: context.vault,
165
+ agent: context.agent,
166
+ query: input.query,
167
+ limit,
168
+ mode,
169
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
170
+ ...(contextReadiness.context ? { contextReadiness: contextReadiness.context } : {}),
171
+ results
172
+ });
173
+ };
174
+ export const similarNotesTool = async (input) => {
175
+ const context = await resolveExecutionContext(input);
176
+ const readiness = await ensureBootstrapReady(context, input, 'brainlink_similar_notes');
177
+ if (readiness.preflight) {
178
+ return readiness.preflight;
179
+ }
180
+ const mode = sanitizeSearchMode(input.mode, context.defaults.defaultSearchMode);
181
+ const limit = input.limit ?? context.defaults.defaultSearchLimit;
182
+ const result = await findSimilarNotes(context.vault, {
183
+ title: input.title,
184
+ path: input.path,
185
+ agentId: context.agent,
186
+ limit,
187
+ mode
188
+ });
189
+ return jsonResult({
190
+ vault: context.vault,
191
+ agent: context.agent,
192
+ mode,
193
+ limit,
194
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
195
+ ...result
196
+ });
197
+ };
198
+ export const explainTool = async (input) => {
199
+ const context = await resolveExecutionContext(input);
200
+ const readiness = await ensureBootstrapReady(context, input, 'brainlink_explain');
201
+ if (readiness.preflight) {
202
+ return readiness.preflight;
203
+ }
204
+ const contextReadiness = await ensureContextReady(context, input, 'brainlink_explain');
205
+ if (contextReadiness.preflight) {
206
+ return contextReadiness.preflight;
207
+ }
208
+ const mode = sanitizeSearchMode(input.mode, context.defaults.defaultSearchMode);
209
+ const limit = input.limit ?? context.defaults.defaultSearchLimit;
210
+ const results = await explainSearchResults(context.vault, input.query, limit, context.agent, mode);
211
+ return jsonResult({
212
+ vault: context.vault,
213
+ agent: context.agent,
214
+ query: input.query,
215
+ limit,
216
+ mode,
217
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
218
+ ...(contextReadiness.context ? { contextReadiness: contextReadiness.context } : {}),
219
+ results
220
+ });
221
+ };
222
+ export const validateTool = async (input) => {
223
+ const context = await resolveExecutionContext(input);
224
+ const readiness = await ensureBootstrapReady(context, input, 'brainlink_validate');
225
+ if (readiness.preflight) {
226
+ return readiness.preflight;
227
+ }
228
+ const contextReadiness = await ensureContextReady(context, input, 'brainlink_validate');
229
+ if (contextReadiness.preflight) {
230
+ return contextReadiness.preflight;
231
+ }
232
+ const validation = await validateVault(context.vault, context.agent);
233
+ return jsonResult({
234
+ vault: context.vault,
235
+ agent: context.agent,
236
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
237
+ ...(contextReadiness.context ? { contextReadiness: contextReadiness.context } : {}),
238
+ ...validation
239
+ });
240
+ };
241
+ export const doctorActionsTool = async (input) => {
242
+ const context = await resolveExecutionContext(input);
243
+ const readiness = await ensureBootstrapReady(context, input, 'brainlink_doctor_actions');
244
+ if (readiness.preflight) {
245
+ return readiness.preflight;
246
+ }
247
+ const contextReadiness = await ensureContextReady(context, input, 'brainlink_doctor_actions');
248
+ if (contextReadiness.preflight) {
249
+ return contextReadiness.preflight;
250
+ }
251
+ const report = await buildActionableDoctor(context.vault);
252
+ return jsonResult({
253
+ vault: context.vault,
254
+ agent: context.agent,
255
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
256
+ ...(contextReadiness.context ? { contextReadiness: contextReadiness.context } : {}),
257
+ ...report
258
+ });
259
+ };
260
+ export const graphTool = async (input) => {
261
+ const context = await resolveExecutionContext(input);
262
+ const readiness = await ensureBootstrapReady(context, input, 'brainlink_graph');
263
+ if (readiness.preflight) {
264
+ return readiness.preflight;
265
+ }
266
+ const contextReadiness = await ensureContextReady(context, input, 'brainlink_graph');
267
+ if (contextReadiness.preflight) {
268
+ return contextReadiness.preflight;
269
+ }
270
+ const graph = await getGraph(context.vault, context.agent);
271
+ return jsonResult({
272
+ vault: context.vault,
273
+ agent: context.agent,
274
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
275
+ ...(contextReadiness.context ? { contextReadiness: contextReadiness.context } : {}),
276
+ ...graph
277
+ });
278
+ };
279
+ export const graphContextsTool = async (input) => {
280
+ const context = await resolveExecutionContext(input);
281
+ const readiness = await ensureBootstrapReady(context, input, 'brainlink_graph_contexts');
282
+ if (readiness.preflight) {
283
+ return readiness.preflight;
284
+ }
285
+ const contextReadiness = await ensureContextReady(context, input, 'brainlink_graph_contexts');
286
+ if (contextReadiness.preflight) {
287
+ return contextReadiness.preflight;
288
+ }
289
+ const contexts = await getGraphContexts(context.vault, context.agent);
290
+ return jsonResult({
291
+ vault: context.vault,
292
+ agent: context.agent,
293
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
294
+ ...(contextReadiness.context ? { contextReadiness: contextReadiness.context } : {}),
295
+ contexts
296
+ });
297
+ };
298
+ export const brokenLinksTool = async (input) => {
299
+ const context = await resolveExecutionContext(input);
300
+ const readiness = await ensureBootstrapReady(context, input, 'brainlink_broken_links');
301
+ if (readiness.preflight) {
302
+ return readiness.preflight;
303
+ }
304
+ const contextReadiness = await ensureContextReady(context, input, 'brainlink_broken_links');
305
+ if (contextReadiness.preflight) {
306
+ return contextReadiness.preflight;
307
+ }
308
+ const brokenLinks = await getBrokenLinksReport(context.vault, context.agent);
309
+ return jsonResult({
310
+ vault: context.vault,
311
+ agent: context.agent,
312
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
313
+ ...(contextReadiness.context ? { contextReadiness: contextReadiness.context } : {}),
314
+ brokenLinks
315
+ });
316
+ };
317
+ export const suggestLinksTool = async (input) => {
318
+ const context = await resolveExecutionContext(input);
319
+ const readiness = await ensureBootstrapReady(context, input, 'brainlink_suggest_links');
320
+ if (readiness.preflight) {
321
+ return readiness.preflight;
322
+ }
323
+ const contextReadiness = await ensureContextReady(context, input, 'brainlink_suggest_links');
324
+ if (contextReadiness.preflight) {
325
+ return contextReadiness.preflight;
326
+ }
327
+ if (input.broken) {
328
+ const suggestions = await suggestBrokenLinkFixes(context.vault, context.agent, input.limit);
329
+ return jsonResult({
330
+ vault: context.vault,
331
+ agent: context.agent,
332
+ mode: 'broken-links',
333
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
334
+ ...(contextReadiness.context ? { contextReadiness: contextReadiness.context } : {}),
335
+ suggestions
336
+ });
337
+ }
338
+ if (!input.content || input.content.trim().length === 0) {
339
+ throw new Error('content is required when broken=false.');
340
+ }
341
+ const suggestions = await suggestContextLinks(context.vault, input.content, context.agent, input.limit);
342
+ return jsonResult({
343
+ vault: context.vault,
344
+ agent: context.agent,
345
+ mode: 'content',
346
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
347
+ ...(contextReadiness.context ? { contextReadiness: contextReadiness.context } : {}),
348
+ suggestions
349
+ });
350
+ };
351
+ export const orphansTool = async (input) => {
352
+ const context = await resolveExecutionContext(input);
353
+ const readiness = await ensureBootstrapReady(context, input, 'brainlink_orphans');
354
+ if (readiness.preflight) {
355
+ return readiness.preflight;
356
+ }
357
+ const contextReadiness = await ensureContextReady(context, input, 'brainlink_orphans');
358
+ if (contextReadiness.preflight) {
359
+ return contextReadiness.preflight;
360
+ }
361
+ const orphans = await getOrphansReport(context.vault, context.agent);
362
+ return jsonResult({
363
+ vault: context.vault,
364
+ agent: context.agent,
365
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
366
+ ...(contextReadiness.context ? { contextReadiness: contextReadiness.context } : {}),
367
+ orphans
368
+ });
369
+ };
370
+ export const statsTool = async (input) => {
371
+ const context = await resolveExecutionContext(input);
372
+ const readiness = await ensureBootstrapReady(context, input, 'brainlink_stats');
373
+ if (readiness.preflight) {
374
+ return readiness.preflight;
375
+ }
376
+ const contextReadiness = await ensureContextReady(context, input, 'brainlink_stats');
377
+ if (contextReadiness.preflight) {
378
+ return contextReadiness.preflight;
379
+ }
380
+ const stats = await getStats(context.vault, context.agent);
381
+ return jsonResult({
382
+ vault: context.vault,
383
+ agent: context.agent,
384
+ ...(readiness.bootstrap ? { bootstrap: readiness.bootstrap } : {}),
385
+ ...(contextReadiness.context ? { contextReadiness: contextReadiness.context } : {}),
386
+ stats
387
+ });
388
+ };
389
+ export const versionTool = async (input) => {
390
+ const context = await resolveExecutionContext(input);
391
+ return jsonResult({
392
+ vault: context.vault,
393
+ agent: context.agent,
394
+ runtime: getRuntimeMetadata()
395
+ });
396
+ };
397
+ export const recommendationsTool = async (input) => {
398
+ const context = await resolveExecutionContext(input);
399
+ const policy = await getBootstrapPolicy();
400
+ const bootstrapStatus = await getBootstrapSessionStatus(context.vault, context.agent);
401
+ const contextStatus = await getContextSessionStatus(context.vault, context.agent);
402
+ const stats = await getStats(context.vault, context.agent);
403
+ const mode = sanitizeSearchMode(input.mode, context.defaults.defaultSearchMode);
404
+ const strategy = sanitizeContextStrategy(input.strategy, context.defaults.defaultContextStrategy);
405
+ const limit = input.limit ?? context.defaults.defaultSearchLimit;
406
+ const tokens = input.tokens ?? context.defaults.defaultContextTokens;
407
+ const query = input.query?.trim();
408
+ const recommendations = [
409
+ ...(policy.enforceBootstrap && (!policy.autoBootstrapOnRead || !policy.autoBootstrapOnStartup)
410
+ ? [
411
+ {
412
+ tool: 'brainlink_policy',
413
+ reason: 'Enable fully automatic bootstrap for plug-and-play agent usage.',
414
+ args: {
415
+ preset: 'fully-auto'
416
+ }
417
+ }
418
+ ]
419
+ : []),
420
+ ...(!bootstrapStatus.ready && !policy.autoBootstrapOnRead
421
+ ? [
422
+ {
423
+ tool: 'brainlink_bootstrap',
424
+ reason: 'Bootstrap is required before read tools when auto-bootstrap-on-read is disabled.',
425
+ args: {
426
+ vault: context.vault,
427
+ ...(context.agent ? { agent: context.agent } : {}),
428
+ mode,
429
+ strategy,
430
+ ...(query ? { query } : {})
431
+ }
432
+ }
433
+ ]
434
+ : []),
435
+ ...(policy.enforceContextFirst && !contextStatus.ready
436
+ ? [
437
+ {
438
+ tool: 'brainlink_context',
439
+ reason: 'Context-first policy is enabled. Load context before other read operations.',
440
+ args: {
441
+ vault: context.vault,
442
+ ...(context.agent ? { agent: context.agent } : {}),
443
+ query: query ?? '<task>',
444
+ mode,
445
+ strategy,
446
+ limit,
447
+ tokens
448
+ }
449
+ }
450
+ ]
451
+ : []),
452
+ ...(stats.documentCount === 0
453
+ ? [
454
+ {
455
+ tool: 'brainlink_add_note',
456
+ reason: 'Seed the vault with a first durable note so retrieval can return useful context.',
457
+ args: {
458
+ vault: context.vault,
459
+ ...(context.agent ? { agent: context.agent } : {}),
460
+ title: 'Architecture',
461
+ content: 'Seed durable memory with explicit [[links]] and #tags.'
462
+ }
463
+ },
464
+ {
465
+ tool: 'brainlink_index',
466
+ reason: 'Rebuild index after writing the first notes.',
467
+ args: {
468
+ vault: context.vault
469
+ }
470
+ }
471
+ ]
472
+ : []),
473
+ {
474
+ tool: 'brainlink_context',
475
+ reason: 'Retrieve grounded memory context before responding.',
476
+ args: {
477
+ vault: context.vault,
478
+ ...(context.agent ? { agent: context.agent } : {}),
479
+ query: query ?? '<task>',
480
+ mode,
481
+ strategy,
482
+ limit,
483
+ tokens
484
+ }
485
+ },
486
+ {
487
+ tool: 'brainlink_dedupe',
488
+ reason: 'Detect and resolve duplicate durable notes to keep memory quality high.',
489
+ args: {
490
+ vault: context.vault,
491
+ ...(context.agent ? { agent: context.agent } : {}),
492
+ limit: 10,
493
+ minScore: 0.92,
494
+ semantic: true
495
+ }
496
+ },
497
+ {
498
+ tool: 'brainlink_add_note',
499
+ reason: 'Persist durable outcomes after task completion (write responses include connectivity metadata).',
500
+ args: {
501
+ vault: context.vault,
502
+ ...(context.agent ? { agent: context.agent } : {}),
503
+ title: 'Task Update',
504
+ content: 'Durable findings connected to [[existing notes]].'
505
+ }
506
+ }
507
+ ];
508
+ return jsonResult({
509
+ vault: context.vault,
510
+ agent: context.agent,
511
+ defaults: {
512
+ mode,
513
+ strategy,
514
+ limit,
515
+ tokens
516
+ },
517
+ contextStrategies: [
518
+ {
519
+ strategy: 'rag',
520
+ useWhen: 'Use for fresh retrieval and context assembly from the current index.'
521
+ },
522
+ {
523
+ strategy: 'cag',
524
+ useWhen: 'Use for repeated or stable task context so Brainlink can reuse a fresh persisted context pack.'
525
+ },
526
+ {
527
+ strategy: 'auto',
528
+ useWhen: 'Use when the agent wants Brainlink to choose CAG on fresh pack hits and RAG otherwise.'
529
+ }
530
+ ],
531
+ policy,
532
+ bootstrapStatus,
533
+ contextStatus,
534
+ stats,
535
+ recommendations
536
+ });
537
+ };