@andespindola/brainlink 1.0.5 → 1.0.6

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