@soleri/core 2.8.0 → 2.10.0

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 (123) hide show
  1. package/dist/extensions/index.d.ts +3 -0
  2. package/dist/extensions/index.d.ts.map +1 -0
  3. package/dist/extensions/index.js +2 -0
  4. package/dist/extensions/index.js.map +1 -0
  5. package/dist/extensions/middleware.d.ts +13 -0
  6. package/dist/extensions/middleware.d.ts.map +1 -0
  7. package/dist/extensions/middleware.js +47 -0
  8. package/dist/extensions/middleware.js.map +1 -0
  9. package/dist/extensions/types.d.ts +64 -0
  10. package/dist/extensions/types.d.ts.map +1 -0
  11. package/dist/extensions/types.js +2 -0
  12. package/dist/extensions/types.js.map +1 -0
  13. package/dist/index.d.ts +8 -16
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +7 -16
  16. package/dist/index.js.map +1 -1
  17. package/dist/planning/gap-analysis.d.ts.map +1 -1
  18. package/dist/planning/gap-analysis.js +3 -1
  19. package/dist/planning/gap-analysis.js.map +1 -1
  20. package/dist/runtime/core-ops.d.ts +1 -1
  21. package/dist/runtime/core-ops.js +1 -1
  22. package/dist/runtime/facades/admin-facade.d.ts +8 -0
  23. package/dist/runtime/facades/admin-facade.d.ts.map +1 -0
  24. package/dist/runtime/facades/admin-facade.js +90 -0
  25. package/dist/runtime/facades/admin-facade.js.map +1 -0
  26. package/dist/runtime/facades/brain-facade.d.ts +8 -0
  27. package/dist/runtime/facades/brain-facade.d.ts.map +1 -0
  28. package/dist/runtime/facades/brain-facade.js +294 -0
  29. package/dist/runtime/facades/brain-facade.js.map +1 -0
  30. package/dist/runtime/facades/cognee-facade.d.ts +8 -0
  31. package/dist/runtime/facades/cognee-facade.d.ts.map +1 -0
  32. package/dist/runtime/facades/cognee-facade.js +154 -0
  33. package/dist/runtime/facades/cognee-facade.js.map +1 -0
  34. package/dist/runtime/facades/control-facade.d.ts +8 -0
  35. package/dist/runtime/facades/control-facade.d.ts.map +1 -0
  36. package/dist/runtime/facades/control-facade.js +244 -0
  37. package/dist/runtime/facades/control-facade.js.map +1 -0
  38. package/dist/runtime/facades/curator-facade.d.ts +8 -0
  39. package/dist/runtime/facades/curator-facade.d.ts.map +1 -0
  40. package/dist/runtime/facades/curator-facade.js +117 -0
  41. package/dist/runtime/facades/curator-facade.js.map +1 -0
  42. package/dist/runtime/facades/index.d.ts +10 -0
  43. package/dist/runtime/facades/index.d.ts.map +1 -0
  44. package/dist/runtime/facades/index.js +71 -0
  45. package/dist/runtime/facades/index.js.map +1 -0
  46. package/dist/runtime/facades/loop-facade.d.ts +8 -0
  47. package/dist/runtime/facades/loop-facade.d.ts.map +1 -0
  48. package/dist/runtime/facades/loop-facade.js +9 -0
  49. package/dist/runtime/facades/loop-facade.js.map +1 -0
  50. package/dist/runtime/facades/memory-facade.d.ts +8 -0
  51. package/dist/runtime/facades/memory-facade.d.ts.map +1 -0
  52. package/dist/runtime/facades/memory-facade.js +108 -0
  53. package/dist/runtime/facades/memory-facade.js.map +1 -0
  54. package/dist/runtime/facades/orchestrate-facade.d.ts +8 -0
  55. package/dist/runtime/facades/orchestrate-facade.d.ts.map +1 -0
  56. package/dist/runtime/facades/orchestrate-facade.js +58 -0
  57. package/dist/runtime/facades/orchestrate-facade.js.map +1 -0
  58. package/dist/runtime/facades/plan-facade.d.ts +8 -0
  59. package/dist/runtime/facades/plan-facade.d.ts.map +1 -0
  60. package/dist/runtime/facades/plan-facade.js +110 -0
  61. package/dist/runtime/facades/plan-facade.js.map +1 -0
  62. package/dist/runtime/facades/vault-facade.d.ts +8 -0
  63. package/dist/runtime/facades/vault-facade.d.ts.map +1 -0
  64. package/dist/runtime/facades/vault-facade.js +194 -0
  65. package/dist/runtime/facades/vault-facade.js.map +1 -0
  66. package/dist/runtime/vault-extra-ops.d.ts +2 -2
  67. package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
  68. package/dist/runtime/vault-extra-ops.js +37 -2
  69. package/dist/runtime/vault-extra-ops.js.map +1 -1
  70. package/dist/streams/index.d.ts +4 -0
  71. package/dist/streams/index.d.ts.map +1 -0
  72. package/dist/streams/index.js +3 -0
  73. package/dist/streams/index.js.map +1 -0
  74. package/dist/streams/normalize.d.ts +14 -0
  75. package/dist/streams/normalize.d.ts.map +1 -0
  76. package/dist/streams/normalize.js +43 -0
  77. package/dist/streams/normalize.js.map +1 -0
  78. package/dist/streams/replayable-stream.d.ts +19 -0
  79. package/dist/streams/replayable-stream.d.ts.map +1 -0
  80. package/dist/streams/replayable-stream.js +90 -0
  81. package/dist/streams/replayable-stream.js.map +1 -0
  82. package/dist/vault/content-hash.d.ts +16 -0
  83. package/dist/vault/content-hash.d.ts.map +1 -0
  84. package/dist/vault/content-hash.js +21 -0
  85. package/dist/vault/content-hash.js.map +1 -0
  86. package/dist/vault/vault.d.ts +9 -0
  87. package/dist/vault/vault.d.ts.map +1 -1
  88. package/dist/vault/vault.js +49 -3
  89. package/dist/vault/vault.js.map +1 -1
  90. package/package.json +1 -1
  91. package/src/__tests__/content-hash.test.ts +60 -0
  92. package/src/__tests__/core-ops.test.ts +10 -7
  93. package/src/__tests__/extensions.test.ts +233 -0
  94. package/src/__tests__/grading-ops.test.ts +2 -2
  95. package/src/__tests__/memory-cross-project-ops.test.ts +2 -2
  96. package/src/__tests__/normalize.test.ts +75 -0
  97. package/src/__tests__/playbook.test.ts +4 -4
  98. package/src/__tests__/replayable-stream.test.ts +66 -0
  99. package/src/__tests__/vault-extra-ops.test.ts +1 -1
  100. package/src/__tests__/vault.test.ts +72 -0
  101. package/src/extensions/index.ts +2 -0
  102. package/src/extensions/middleware.ts +53 -0
  103. package/src/extensions/types.ts +64 -0
  104. package/src/index.ts +14 -17
  105. package/src/planning/gap-analysis.ts +52 -7
  106. package/src/runtime/facades/admin-facade.ts +101 -0
  107. package/src/runtime/facades/brain-facade.ts +331 -0
  108. package/src/runtime/facades/cognee-facade.ts +162 -0
  109. package/src/runtime/facades/control-facade.ts +279 -0
  110. package/src/runtime/facades/curator-facade.ts +132 -0
  111. package/src/runtime/facades/index.ts +74 -0
  112. package/src/runtime/facades/loop-facade.ts +12 -0
  113. package/src/runtime/facades/memory-facade.ts +114 -0
  114. package/src/runtime/facades/orchestrate-facade.ts +68 -0
  115. package/src/runtime/facades/plan-facade.ts +119 -0
  116. package/src/runtime/facades/vault-facade.ts +223 -0
  117. package/src/runtime/vault-extra-ops.ts +38 -2
  118. package/src/streams/index.ts +3 -0
  119. package/src/streams/normalize.ts +56 -0
  120. package/src/streams/replayable-stream.ts +92 -0
  121. package/src/vault/content-hash.ts +31 -0
  122. package/src/vault/vault.ts +73 -3
  123. package/src/runtime/core-ops.ts +0 -1443
package/src/index.ts CHANGED
@@ -29,8 +29,6 @@ export type {
29
29
  MergedPlaybook,
30
30
  PlaybookMatchResult,
31
31
  } from './playbooks/index.js';
32
- export { createPlaybookOps } from './runtime/playbook-ops.js';
33
-
34
32
  // ─── Text Utilities ─────────────────────────────────────────────────
35
33
  export {
36
34
  tokenize,
@@ -239,6 +237,10 @@ export {
239
237
  export { KeyPool, loadKeyPoolConfig } from './llm/key-pool.js';
240
238
  export type { KeyPoolFiles } from './llm/key-pool.js';
241
239
 
240
+ // ─── Extensions ──────────────────────────────────────────────────────
241
+ export type { AgentExtensions, OpMiddleware, MiddlewareContext } from './extensions/index.js';
242
+ export { wrapWithMiddleware } from './extensions/index.js';
243
+
242
244
  // ─── Facades ─────────────────────────────────────────────────────────
243
245
  export { registerFacade, registerAllFacades } from './facades/facade-factory.js';
244
246
  export { facadeInputSchema } from './facades/types.js';
@@ -285,22 +287,8 @@ export type { LogLevel, LogEntry, LogContext, LoggerConfig } from './logging/typ
285
287
 
286
288
  // ─── Runtime Factory ────────────────────────────────────────────────
287
289
  export { createAgentRuntime } from './runtime/runtime.js';
288
- export { createCoreOps } from './runtime/core-ops.js';
290
+ export { createSemanticFacades } from './runtime/facades/index.js';
289
291
  export { createDomainFacade, createDomainFacades } from './runtime/domain-ops.js';
290
- export { createPlanningExtraOps } from './runtime/planning-extra-ops.js';
291
- export { createMemoryExtraOps } from './runtime/memory-extra-ops.js';
292
- export { createVaultExtraOps } from './runtime/vault-extra-ops.js';
293
- export { createAdminOps } from './runtime/admin-ops.js';
294
- export { createAdminExtraOps } from './runtime/admin-extra-ops.js';
295
- export { createLoopOps } from './runtime/loop-ops.js';
296
- export { createOrchestrateOps } from './runtime/orchestrate-ops.js';
297
- export { createGradingOps } from './runtime/grading-ops.js';
298
- export { createCaptureOps } from './runtime/capture-ops.js';
299
- export { createCuratorExtraOps } from './runtime/curator-extra-ops.js';
300
- export { createProjectOps } from './runtime/project-ops.js';
301
- export { createMemoryCrossProjectOps } from './runtime/memory-cross-project-ops.js';
302
- export { createCogneeSyncOps } from './runtime/cognee-sync-ops.js';
303
- export { createIntakeOps } from './runtime/intake-ops.js';
304
292
  export type { AgentRuntimeConfig, AgentRuntime } from './runtime/types.js';
305
293
 
306
294
  // ─── Errors ────────────────────────────────────────────────────────────
@@ -337,6 +325,15 @@ export type {
337
325
  FtsSearchOptions,
338
326
  } from './persistence/index.js';
339
327
 
328
+ // ─── Streams ──────────────────────────────────────────────────────────
329
+ export { ReplayableStream } from './streams/index.js';
330
+ export { normalize, collect } from './streams/index.js';
331
+ export type { NestableInput } from './streams/index.js';
332
+
333
+ // ─── Content Hashing ──────────────────────────────────────────────────
334
+ export { computeContentHash } from './vault/content-hash.js';
335
+ export type { HashableEntry } from './vault/content-hash.js';
336
+
340
337
  // ─── Prompts ───────────────────────────────────────────────────────────
341
338
  export { TemplateManager, parseVariables, resolveIncludes } from './prompts/index.js';
342
339
  export type { PromptTemplate, TemplateVariable, RenderOptions } from './prompts/index.js';
@@ -550,21 +550,45 @@ function analyzeKnowledgeDepth(plan: Plan): PlanGap[] {
550
550
  const matches = desc.match(/[a-z]+-[a-z]+(-[a-z]+)*/g) || [];
551
551
  // Filter to likely pattern IDs (hyphenated, 2+ segments, not common words)
552
552
  const patternRefs = matches.filter(
553
- (m) => m.length > 8 && NAMED_PATTERN_REGEX.test(m) && !['front-end', 'back-end', 'real-time', 'client-side', 'server-side'].includes(m),
553
+ (m) =>
554
+ m.length > 8 &&
555
+ NAMED_PATTERN_REGEX.test(m) &&
556
+ !['front-end', 'back-end', 'real-time', 'client-side', 'server-side'].includes(m),
554
557
  );
555
558
  namedPatternCount += patternRefs.length;
556
559
  }
557
560
 
558
561
  if (namedPatternCount >= 5) {
559
562
  gaps.push(
560
- gap('bonus', 'knowledge-depth', `${namedPatternCount} vault pattern references across tasks — strong knowledge-informed plan.`, '', 'tasks', 'vault_pattern_refs_high'),
563
+ gap(
564
+ 'bonus',
565
+ 'knowledge-depth',
566
+ `${namedPatternCount} vault pattern references across tasks — strong knowledge-informed plan.`,
567
+ '',
568
+ 'tasks',
569
+ 'vault_pattern_refs_high',
570
+ ),
561
571
  );
562
572
  gaps.push(
563
- gap('bonus', 'knowledge-depth', 'Vault pattern density indicates expert-level domain knowledge.', '', 'tasks', 'vault_pattern_density'),
573
+ gap(
574
+ 'bonus',
575
+ 'knowledge-depth',
576
+ 'Vault pattern density indicates expert-level domain knowledge.',
577
+ '',
578
+ 'tasks',
579
+ 'vault_pattern_density',
580
+ ),
564
581
  );
565
582
  } else if (namedPatternCount >= 2) {
566
583
  gaps.push(
567
- gap('bonus', 'knowledge-depth', `${namedPatternCount} vault pattern references across tasks.`, '', 'tasks', 'vault_pattern_refs_medium'),
584
+ gap(
585
+ 'bonus',
586
+ 'knowledge-depth',
587
+ `${namedPatternCount} vault pattern references across tasks.`,
588
+ '',
589
+ 'tasks',
590
+ 'vault_pattern_refs_medium',
591
+ ),
568
592
  );
569
593
  }
570
594
 
@@ -580,7 +604,14 @@ function analyzeKnowledgeDepth(plan: Plan): PlanGap[] {
580
604
 
581
605
  if (plan.tasks.length > 0 && tasksWithCriteria / plan.tasks.length >= 0.8) {
582
606
  gaps.push(
583
- gap('bonus', 'knowledge-depth', `${tasksWithCriteria}/${plan.tasks.length} tasks have acceptance criteria (${totalCriteria} total).`, '', 'tasks', 'high_acceptance_criteria'),
607
+ gap(
608
+ 'bonus',
609
+ 'knowledge-depth',
610
+ `${tasksWithCriteria}/${plan.tasks.length} tasks have acceptance criteria (${totalCriteria} total).`,
611
+ '',
612
+ 'tasks',
613
+ 'high_acceptance_criteria',
614
+ ),
584
615
  );
585
616
  }
586
617
 
@@ -592,7 +623,14 @@ function analyzeKnowledgeDepth(plan: Plan): PlanGap[] {
592
623
 
593
624
  if (indicatorHits >= 4) {
594
625
  gaps.push(
595
- gap('bonus', 'knowledge-depth', `${indicatorHits} domain-specific knowledge indicators found (WCAG, ARIA, contrast ratios, touch targets, etc.).`, '', 'tasks', 'domain_knowledge_indicators'),
626
+ gap(
627
+ 'bonus',
628
+ 'knowledge-depth',
629
+ `${indicatorHits} domain-specific knowledge indicators found (WCAG, ARIA, contrast ratios, touch targets, etc.).`,
630
+ '',
631
+ 'tasks',
632
+ 'domain_knowledge_indicators',
633
+ ),
596
634
  );
597
635
  }
598
636
 
@@ -602,7 +640,14 @@ function analyzeKnowledgeDepth(plan: Plan): PlanGap[] {
602
640
  plan.tasks.reduce((sum, t) => sum + (t.description?.length ?? 0), 0) / plan.tasks.length;
603
641
  if (avgDescLength >= 80) {
604
642
  gaps.push(
605
- gap('bonus', 'knowledge-depth', `Task descriptions average ${Math.round(avgDescLength)} chars — detailed and specific.`, '', 'tasks', 'rich_task_descriptions'),
643
+ gap(
644
+ 'bonus',
645
+ 'knowledge-depth',
646
+ `Task descriptions average ${Math.round(avgDescLength)} chars — detailed and specific.`,
647
+ '',
648
+ 'tasks',
649
+ 'rich_task_descriptions',
650
+ ),
606
651
  );
607
652
  }
608
653
  }
@@ -0,0 +1,101 @@
1
+ /**
2
+ * Admin facade — infrastructure ops.
3
+ * health, config, telemetry, tokens, LLM, prompts.
4
+ */
5
+
6
+ import { z } from 'zod';
7
+ import type { OpDefinition } from '../../facades/types.js';
8
+ import type { AgentRuntime } from '../types.js';
9
+ import { createAdminOps } from '../admin-ops.js';
10
+ import { createAdminExtraOps } from '../admin-extra-ops.js';
11
+
12
+ export function createAdminFacadeOps(runtime: AgentRuntime): OpDefinition[] {
13
+ const { llmClient, keyPool } = runtime;
14
+
15
+ return [
16
+ // ─── LLM (inline from core-ops.ts) ──────────────────────────
17
+ {
18
+ name: 'llm_rotate',
19
+ description:
20
+ 'Force rotate the active API key for a provider. Useful when rate-limited or key is failing.',
21
+ auth: 'write',
22
+ schema: z.object({
23
+ provider: z.enum(['openai', 'anthropic']),
24
+ }),
25
+ handler: async (params) => {
26
+ const provider = params.provider as 'openai' | 'anthropic';
27
+ const pool = keyPool[provider];
28
+ if (!pool.hasKeys) return { rotated: false, error: `No ${provider} keys configured` };
29
+ const newKey = pool.rotateOnError();
30
+ return {
31
+ rotated: newKey !== null,
32
+ activeKeyIndex: pool.activeKeyIndex,
33
+ poolSize: pool.poolSize,
34
+ exhausted: pool.exhausted,
35
+ };
36
+ },
37
+ },
38
+ {
39
+ name: 'llm_call',
40
+ description: 'Make an LLM completion call. Uses model routing config and key pool rotation.',
41
+ auth: 'write',
42
+ schema: z.object({
43
+ systemPrompt: z.string().describe('System prompt for the LLM.'),
44
+ userPrompt: z.string().describe('User prompt / task input.'),
45
+ model: z
46
+ .string()
47
+ .optional()
48
+ .describe('Model name. Routed via model-routing.json if omitted.'),
49
+ temperature: z.number().optional().describe('Sampling temperature (0-2). Default 0.3.'),
50
+ maxTokens: z.number().optional().describe('Max output tokens. Default 500.'),
51
+ caller: z.string().optional().describe('Caller name for routing. Default "core-ops".'),
52
+ task: z.string().optional().describe('Task name for routing.'),
53
+ }),
54
+ handler: async (params) => {
55
+ return llmClient.complete({
56
+ provider: 'openai',
57
+ model: (params.model as string) ?? '',
58
+ systemPrompt: params.systemPrompt as string,
59
+ userPrompt: params.userPrompt as string,
60
+ temperature: params.temperature as number | undefined,
61
+ maxTokens: params.maxTokens as number | undefined,
62
+ caller: (params.caller as string) ?? 'core-ops',
63
+ task: params.task as string | undefined,
64
+ });
65
+ },
66
+ },
67
+
68
+ // ─── Prompt Templates (inline from core-ops.ts) ─────────────
69
+ {
70
+ name: 'render_prompt',
71
+ description:
72
+ 'Render a prompt template with variable substitution. Templates are .prompt files loaded from the templates directory.',
73
+ auth: 'read' as const,
74
+ schema: z.object({
75
+ template: z.string().describe('Template name (without .prompt extension)'),
76
+ variables: z.record(z.string()).optional().default({}),
77
+ strict: z.boolean().optional().default(true),
78
+ }),
79
+ handler: async (params) => {
80
+ const rendered = runtime.templateManager.render(
81
+ params.template as string,
82
+ (params.variables ?? {}) as Record<string, string>,
83
+ { strict: params.strict as boolean },
84
+ );
85
+ return { rendered };
86
+ },
87
+ },
88
+ {
89
+ name: 'list_templates',
90
+ description: 'List all loaded prompt templates.',
91
+ auth: 'read' as const,
92
+ handler: async () => ({
93
+ templates: runtime.templateManager.listTemplates(),
94
+ }),
95
+ },
96
+
97
+ // ─── Satellite ops ───────────────────────────────────────────
98
+ ...createAdminOps(runtime),
99
+ ...createAdminExtraOps(runtime),
100
+ ];
101
+ }
@@ -0,0 +1,331 @@
1
+ /**
2
+ * Brain facade — learning system ops.
3
+ * intelligence pipeline, strengths, feedback, sessions.
4
+ */
5
+
6
+ import { z } from 'zod';
7
+ import type { OpDefinition } from '../../facades/types.js';
8
+ import type { AgentRuntime } from '../types.js';
9
+
10
+ export function createBrainFacadeOps(runtime: AgentRuntime): OpDefinition[] {
11
+ const { brain, brainIntelligence, llmClient, keyPool, governance } = runtime;
12
+
13
+ return [
14
+ // ─── Brain (inline from core-ops.ts) ────────────────────────
15
+ {
16
+ name: 'record_feedback',
17
+ description:
18
+ 'Record feedback on a search result — accepted or dismissed. Used for adaptive weight tuning.',
19
+ auth: 'write',
20
+ schema: z.object({
21
+ query: z.string().describe('The original search query'),
22
+ entryId: z.string().describe('The entry ID that was accepted or dismissed'),
23
+ action: z.enum(['accepted', 'dismissed']),
24
+ }),
25
+ handler: async (params) => {
26
+ brain.recordFeedback(
27
+ params.query as string,
28
+ params.entryId as string,
29
+ params.action as 'accepted' | 'dismissed',
30
+ );
31
+ return {
32
+ recorded: true,
33
+ query: params.query,
34
+ entryId: params.entryId,
35
+ action: params.action,
36
+ };
37
+ },
38
+ },
39
+ {
40
+ name: 'brain_feedback',
41
+ description:
42
+ 'Enhanced feedback with typed actions (accepted/dismissed/modified/failed), source tracking, confidence, duration, and reason.',
43
+ auth: 'write',
44
+ schema: z.object({
45
+ query: z.string().describe('The original search query'),
46
+ entryId: z.string().describe('The entry ID'),
47
+ action: z.enum(['accepted', 'dismissed', 'modified', 'failed']),
48
+ source: z
49
+ .enum(['search', 'recommendation', 'tool-execution', 'explicit'])
50
+ .optional()
51
+ .describe("Feedback source. Default 'search'."),
52
+ confidence: z.number().optional().describe('Confidence 0-1. Default 0.6.'),
53
+ duration: z.number().optional().describe('Duration in ms.'),
54
+ context: z.string().optional().describe("JSON context string. Default '{}'."),
55
+ reason: z.string().optional().describe('Human-readable reason.'),
56
+ }),
57
+ handler: async (params) => {
58
+ const entry = brain.recordFeedback({
59
+ query: params.query as string,
60
+ entryId: params.entryId as string,
61
+ action: params.action as 'accepted' | 'dismissed' | 'modified' | 'failed',
62
+ source: params.source as
63
+ | 'search'
64
+ | 'recommendation'
65
+ | 'tool-execution'
66
+ | 'explicit'
67
+ | undefined,
68
+ confidence: params.confidence as number | undefined,
69
+ duration: params.duration as number | undefined,
70
+ context: params.context as string | undefined,
71
+ reason: params.reason as string | undefined,
72
+ });
73
+ return entry;
74
+ },
75
+ },
76
+ {
77
+ name: 'brain_feedback_stats',
78
+ description:
79
+ 'Feedback statistics — counts by action and source, acceptance rate, average confidence.',
80
+ auth: 'read',
81
+ handler: async () => {
82
+ return brain.getFeedbackStats();
83
+ },
84
+ },
85
+ {
86
+ name: 'rebuild_vocabulary',
87
+ description: 'Force rebuild the TF-IDF vocabulary from all vault entries.',
88
+ auth: 'write',
89
+ handler: async () => {
90
+ brain.rebuildVocabulary();
91
+ return { rebuilt: true, vocabularySize: brain.getVocabularySize() };
92
+ },
93
+ },
94
+ {
95
+ name: 'brain_stats',
96
+ description:
97
+ 'Get brain intelligence stats — vocabulary size, feedback count, current scoring weights, intelligence pipeline stats.',
98
+ auth: 'read',
99
+ handler: async () => {
100
+ const base = brain.getStats();
101
+ const intelligence = brainIntelligence.getStats();
102
+ return { ...base, intelligence };
103
+ },
104
+ },
105
+ {
106
+ name: 'brain_decay_report',
107
+ description:
108
+ 'Show temporal decay scores for entries matching a query — reveals which entries are expiring, active, or expired.',
109
+ auth: 'read',
110
+ schema: z.object({
111
+ query: z.string().describe('Search query to find entries'),
112
+ limit: z.number().optional().describe('Max results (default 10)'),
113
+ }),
114
+ handler: async (params) => {
115
+ const results = await brain.getDecayReport(
116
+ params.query as string,
117
+ (params.limit as number | undefined) ?? 10,
118
+ );
119
+ return { results, count: results.length };
120
+ },
121
+ },
122
+ {
123
+ name: 'llm_status',
124
+ description:
125
+ 'LLM client status — provider availability, key pool status, model routing config.',
126
+ auth: 'read',
127
+ handler: async () => {
128
+ const available = llmClient.isAvailable();
129
+ return {
130
+ providers: {
131
+ openai: {
132
+ available: available.openai,
133
+ keyPool: keyPool.openai.hasKeys ? keyPool.openai.getStatus() : null,
134
+ },
135
+ anthropic: {
136
+ available: available.anthropic,
137
+ keyPool: keyPool.anthropic.hasKeys ? keyPool.anthropic.getStatus() : null,
138
+ },
139
+ },
140
+ routes: llmClient.getRoutes(),
141
+ };
142
+ },
143
+ },
144
+
145
+ // ─── Brain Intelligence ──────────────────────────────────────
146
+ {
147
+ name: 'brain_session_context',
148
+ description:
149
+ 'Get recent session context — sessions, tool usage frequency, file change frequency.',
150
+ auth: 'read',
151
+ schema: z.object({
152
+ limit: z.number().optional().describe('Number of recent sessions. Default 10.'),
153
+ }),
154
+ handler: async (params) => {
155
+ return brainIntelligence.getSessionContext((params.limit as number) ?? 10);
156
+ },
157
+ },
158
+ {
159
+ name: 'brain_strengths',
160
+ description:
161
+ 'Get pattern strength scores. 4-signal scoring: usage (0-25) + spread (0-25) + success (0-25) + recency (0-25).',
162
+ auth: 'read',
163
+ schema: z.object({
164
+ domain: z.string().optional(),
165
+ minStrength: z.number().optional().describe('Minimum strength score (0-100).'),
166
+ limit: z.number().optional(),
167
+ }),
168
+ handler: async (params) => {
169
+ return brainIntelligence.getStrengths({
170
+ domain: params.domain as string | undefined,
171
+ minStrength: params.minStrength as number | undefined,
172
+ limit: (params.limit as number) ?? 50,
173
+ });
174
+ },
175
+ },
176
+ {
177
+ name: 'brain_global_patterns',
178
+ description:
179
+ 'Get cross-domain pattern registry — patterns that appear across multiple domains.',
180
+ auth: 'read',
181
+ schema: z.object({
182
+ limit: z.number().optional(),
183
+ }),
184
+ handler: async (params) => {
185
+ return brainIntelligence.getGlobalPatterns((params.limit as number) ?? 20);
186
+ },
187
+ },
188
+ {
189
+ name: 'brain_recommend',
190
+ description:
191
+ 'Get pattern recommendations for a task context. Matches domain, task terms, and source-specific acceptance rates against known strengths.',
192
+ auth: 'read',
193
+ schema: z.object({
194
+ domain: z.string().optional(),
195
+ task: z.string().optional().describe('Task description for contextual matching.'),
196
+ source: z
197
+ .string()
198
+ .optional()
199
+ .describe(
200
+ 'Feedback source to boost by (search, recommendation, tool-execution, explicit).',
201
+ ),
202
+ limit: z.number().optional(),
203
+ }),
204
+ handler: async (params) => {
205
+ return brainIntelligence.recommend({
206
+ domain: params.domain as string | undefined,
207
+ task: params.task as string | undefined,
208
+ source: params.source as string | undefined,
209
+ limit: (params.limit as number) ?? 5,
210
+ });
211
+ },
212
+ },
213
+ {
214
+ name: 'brain_build_intelligence',
215
+ description:
216
+ 'Run the full intelligence pipeline: compute strengths → build global registry → build domain profiles.',
217
+ auth: 'write',
218
+ handler: async () => {
219
+ return brainIntelligence.buildIntelligence();
220
+ },
221
+ },
222
+ {
223
+ name: 'brain_export',
224
+ description:
225
+ 'Export all brain intelligence data — strengths, sessions, proposals, global patterns, domain profiles.',
226
+ auth: 'read',
227
+ handler: async () => {
228
+ return brainIntelligence.exportData();
229
+ },
230
+ },
231
+ {
232
+ name: 'brain_import',
233
+ description: 'Import brain intelligence data from a previous export.',
234
+ auth: 'write',
235
+ schema: z.object({
236
+ data: z.any().describe('BrainExportData object from brain_export.'),
237
+ }),
238
+ handler: async (params) => {
239
+ return brainIntelligence.importData(
240
+ params.data as import('../../brain/types.js').BrainExportData,
241
+ );
242
+ },
243
+ },
244
+ {
245
+ name: 'brain_extract_knowledge',
246
+ description:
247
+ 'Extract knowledge proposals from a session using 6 heuristic rules (repeated tools, multi-file edits, long sessions, plan outcomes, feedback ratios).',
248
+ auth: 'write',
249
+ schema: z.object({
250
+ sessionId: z.string().describe('Session ID to extract knowledge from.'),
251
+ }),
252
+ handler: async (params) => {
253
+ return brainIntelligence.extractKnowledge(params.sessionId as string);
254
+ },
255
+ },
256
+ {
257
+ name: 'brain_archive_sessions',
258
+ description: 'Archive (delete) completed sessions older than N days.',
259
+ auth: 'write',
260
+ schema: z.object({
261
+ olderThanDays: z.number().optional().describe('Days threshold. Default 30.'),
262
+ }),
263
+ handler: async (params) => {
264
+ return brainIntelligence.archiveSessions((params.olderThanDays as number) ?? 30);
265
+ },
266
+ },
267
+ {
268
+ name: 'brain_promote_proposals',
269
+ description:
270
+ 'Promote knowledge proposals to vault entries. Creates intelligence entries from auto-extracted patterns. Gated by governance policies.',
271
+ auth: 'write',
272
+ schema: z.object({
273
+ proposalIds: z.array(z.string()).describe('IDs of proposals to promote.'),
274
+ projectPath: z.string().optional().default('.'),
275
+ }),
276
+ handler: async (params) => {
277
+ const pp = (params.projectPath as string | undefined) ?? '.';
278
+ return brainIntelligence.promoteProposals(params.proposalIds as string[], governance, pp);
279
+ },
280
+ },
281
+ {
282
+ name: 'brain_lifecycle',
283
+ description:
284
+ 'Start or end a brain session. Sessions track tool usage, file changes, and plan context.',
285
+ auth: 'write',
286
+ schema: z.object({
287
+ action: z.enum(['start', 'end']),
288
+ sessionId: z
289
+ .string()
290
+ .optional()
291
+ .describe('Required for end. Auto-generated for start if omitted.'),
292
+ domain: z.string().optional(),
293
+ context: z.string().optional(),
294
+ toolsUsed: z.array(z.string()).optional(),
295
+ filesModified: z.array(z.string()).optional(),
296
+ planId: z.string().optional(),
297
+ planOutcome: z.string().optional(),
298
+ }),
299
+ handler: async (params) => {
300
+ return brainIntelligence.lifecycle({
301
+ action: params.action as 'start' | 'end',
302
+ sessionId: params.sessionId as string | undefined,
303
+ domain: params.domain as string | undefined,
304
+ context: params.context as string | undefined,
305
+ toolsUsed: params.toolsUsed as string[] | undefined,
306
+ filesModified: params.filesModified as string[] | undefined,
307
+ planId: params.planId as string | undefined,
308
+ planOutcome: params.planOutcome as string | undefined,
309
+ });
310
+ },
311
+ },
312
+ {
313
+ name: 'brain_reset_extracted',
314
+ description:
315
+ 'Reset extraction status on brain sessions, allowing re-extraction. Filter by sessionId, since date, or all.',
316
+ auth: 'write',
317
+ schema: z.object({
318
+ sessionId: z.string().optional().describe('Reset a specific session.'),
319
+ since: z.string().optional().describe('Reset sessions extracted since this ISO date.'),
320
+ all: z.boolean().optional().describe('Reset all extracted sessions.'),
321
+ }),
322
+ handler: async (params) => {
323
+ return brainIntelligence.resetExtracted({
324
+ sessionId: params.sessionId as string | undefined,
325
+ since: params.since as string | undefined,
326
+ all: params.all as boolean | undefined,
327
+ });
328
+ },
329
+ },
330
+ ];
331
+ }