@soleri/core 2.0.1 → 2.1.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 (68) hide show
  1. package/dist/brain/brain.d.ts +2 -49
  2. package/dist/brain/brain.d.ts.map +1 -1
  3. package/dist/brain/brain.js +1 -158
  4. package/dist/brain/brain.js.map +1 -1
  5. package/dist/brain/intelligence.d.ts +51 -0
  6. package/dist/brain/intelligence.d.ts.map +1 -0
  7. package/dist/brain/intelligence.js +666 -0
  8. package/dist/brain/intelligence.js.map +1 -0
  9. package/dist/brain/types.d.ts +165 -0
  10. package/dist/brain/types.d.ts.map +1 -0
  11. package/dist/brain/types.js +2 -0
  12. package/dist/brain/types.js.map +1 -0
  13. package/dist/curator/curator.d.ts +28 -0
  14. package/dist/curator/curator.d.ts.map +1 -0
  15. package/dist/curator/curator.js +525 -0
  16. package/dist/curator/curator.js.map +1 -0
  17. package/dist/curator/types.d.ts +87 -0
  18. package/dist/curator/types.d.ts.map +1 -0
  19. package/dist/curator/types.js +3 -0
  20. package/dist/curator/types.js.map +1 -0
  21. package/dist/facades/types.d.ts +1 -1
  22. package/dist/index.d.ts +11 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +11 -0
  25. package/dist/index.js.map +1 -1
  26. package/dist/llm/llm-client.d.ts +28 -0
  27. package/dist/llm/llm-client.d.ts.map +1 -0
  28. package/dist/llm/llm-client.js +226 -0
  29. package/dist/llm/llm-client.js.map +1 -0
  30. package/dist/runtime/core-ops.d.ts +17 -0
  31. package/dist/runtime/core-ops.d.ts.map +1 -0
  32. package/dist/runtime/core-ops.js +613 -0
  33. package/dist/runtime/core-ops.js.map +1 -0
  34. package/dist/runtime/domain-ops.d.ts +25 -0
  35. package/dist/runtime/domain-ops.d.ts.map +1 -0
  36. package/dist/runtime/domain-ops.js +130 -0
  37. package/dist/runtime/domain-ops.js.map +1 -0
  38. package/dist/runtime/runtime.d.ts +19 -0
  39. package/dist/runtime/runtime.d.ts.map +1 -0
  40. package/dist/runtime/runtime.js +66 -0
  41. package/dist/runtime/runtime.js.map +1 -0
  42. package/dist/runtime/types.d.ts +41 -0
  43. package/dist/runtime/types.d.ts.map +1 -0
  44. package/dist/runtime/types.js +2 -0
  45. package/dist/runtime/types.js.map +1 -0
  46. package/dist/text/similarity.d.ts +8 -0
  47. package/dist/text/similarity.d.ts.map +1 -0
  48. package/dist/text/similarity.js +161 -0
  49. package/dist/text/similarity.js.map +1 -0
  50. package/package.json +6 -2
  51. package/src/__tests__/brain-intelligence.test.ts +623 -0
  52. package/src/__tests__/core-ops.test.ts +218 -0
  53. package/src/__tests__/curator.test.ts +574 -0
  54. package/src/__tests__/domain-ops.test.ts +160 -0
  55. package/src/__tests__/llm-client.test.ts +69 -0
  56. package/src/__tests__/runtime.test.ts +95 -0
  57. package/src/brain/brain.ts +27 -221
  58. package/src/brain/intelligence.ts +1061 -0
  59. package/src/brain/types.ts +176 -0
  60. package/src/curator/curator.ts +699 -0
  61. package/src/curator/types.ts +114 -0
  62. package/src/index.ts +55 -1
  63. package/src/llm/llm-client.ts +310 -0
  64. package/src/runtime/core-ops.ts +665 -0
  65. package/src/runtime/domain-ops.ts +144 -0
  66. package/src/runtime/runtime.ts +76 -0
  67. package/src/runtime/types.ts +39 -0
  68. package/src/text/similarity.ts +168 -0
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Domain facade factory — creates the standard 5-op domain facade pattern.
3
+ *
4
+ * Every domain gets: get_patterns, search, get_entry, capture, remove.
5
+ * This replaces per-domain generated facade files.
6
+ */
7
+
8
+ import { z } from 'zod';
9
+ import type { FacadeConfig, OpDefinition } from '../facades/types.js';
10
+ import type { AgentRuntime } from './types.js';
11
+
12
+ function capitalize(s: string): string {
13
+ return s.charAt(0).toUpperCase() + s.slice(1);
14
+ }
15
+
16
+ /**
17
+ * Create a single domain facade with 5 standard ops.
18
+ *
19
+ * @param runtime - The agent runtime (vault + brain)
20
+ * @param agentId - Agent identifier (used for facade naming)
21
+ * @param domain - Domain name (e.g. 'security', 'api-design')
22
+ */
23
+ export function createDomainFacade(
24
+ runtime: AgentRuntime,
25
+ agentId: string,
26
+ domain: string,
27
+ ): FacadeConfig {
28
+ const { vault, brain } = runtime;
29
+ const facadeName = `${agentId}_${domain.replace(/-/g, '_')}`;
30
+
31
+ const ops: OpDefinition[] = [
32
+ {
33
+ name: 'get_patterns',
34
+ description: `Get ${domain} patterns filtered by tags or severity.`,
35
+ auth: 'read',
36
+ schema: z.object({
37
+ tags: z.array(z.string()).optional(),
38
+ severity: z.enum(['critical', 'warning', 'suggestion']).optional(),
39
+ type: z.enum(['pattern', 'anti-pattern', 'rule']).optional(),
40
+ limit: z.number().optional(),
41
+ }),
42
+ handler: async (params) => {
43
+ return vault.list({
44
+ domain,
45
+ severity: params.severity as string | undefined,
46
+ type: params.type as string | undefined,
47
+ tags: params.tags as string[] | undefined,
48
+ limit: (params.limit as number) ?? 20,
49
+ });
50
+ },
51
+ },
52
+ {
53
+ name: 'search',
54
+ description: `Search ${domain} knowledge with natural language query. Results ranked by TF-IDF + severity + recency.`,
55
+ auth: 'read',
56
+ schema: z.object({
57
+ query: z.string(),
58
+ tags: z.array(z.string()).optional(),
59
+ limit: z.number().optional(),
60
+ }),
61
+ handler: async (params) => {
62
+ return brain.intelligentSearch(params.query as string, {
63
+ domain,
64
+ tags: params.tags as string[] | undefined,
65
+ limit: (params.limit as number) ?? 10,
66
+ });
67
+ },
68
+ },
69
+ {
70
+ name: 'get_entry',
71
+ description: `Get a specific ${domain} knowledge entry by ID.`,
72
+ auth: 'read',
73
+ schema: z.object({ id: z.string() }),
74
+ handler: async (params) => {
75
+ const entry = vault.get(params.id as string);
76
+ if (!entry) return { error: 'Entry not found: ' + params.id };
77
+ return entry;
78
+ },
79
+ },
80
+ {
81
+ name: 'capture',
82
+ description: `Capture a new ${domain} pattern, anti-pattern, or rule. Auto-tags and checks for duplicates.`,
83
+ auth: 'write',
84
+ schema: z.object({
85
+ id: z.string(),
86
+ type: z.enum(['pattern', 'anti-pattern', 'rule']),
87
+ title: z.string(),
88
+ severity: z.enum(['critical', 'warning', 'suggestion']),
89
+ description: z.string(),
90
+ context: z.string().optional(),
91
+ example: z.string().optional(),
92
+ counterExample: z.string().optional(),
93
+ why: z.string().optional(),
94
+ tags: z.array(z.string()).optional().default([]),
95
+ }),
96
+ handler: async (params) => {
97
+ return brain.enrichAndCapture({
98
+ id: params.id as string,
99
+ type: params.type as 'pattern' | 'anti-pattern' | 'rule',
100
+ domain,
101
+ title: params.title as string,
102
+ severity: params.severity as 'critical' | 'warning' | 'suggestion',
103
+ description: params.description as string,
104
+ context: params.context as string | undefined,
105
+ example: params.example as string | undefined,
106
+ counterExample: params.counterExample as string | undefined,
107
+ why: params.why as string | undefined,
108
+ tags: params.tags as string[],
109
+ });
110
+ },
111
+ },
112
+ {
113
+ name: 'remove',
114
+ description: `Remove a ${domain} knowledge entry by ID.`,
115
+ auth: 'admin',
116
+ schema: z.object({ id: z.string() }),
117
+ handler: async (params) => {
118
+ const removed = vault.remove(params.id as string);
119
+ return { removed, id: params.id };
120
+ },
121
+ },
122
+ ];
123
+
124
+ return {
125
+ name: facadeName,
126
+ description: `${capitalize(domain.replace(/-/g, ' '))} patterns, rules, and guidance.`,
127
+ ops,
128
+ };
129
+ }
130
+
131
+ /**
132
+ * Create domain facades for all domains.
133
+ *
134
+ * @param runtime - The agent runtime
135
+ * @param agentId - Agent identifier
136
+ * @param domains - Array of domain names
137
+ */
138
+ export function createDomainFacades(
139
+ runtime: AgentRuntime,
140
+ agentId: string,
141
+ domains: string[],
142
+ ): FacadeConfig[] {
143
+ return domains.map((d) => createDomainFacade(runtime, agentId, d));
144
+ }
@@ -0,0 +1,76 @@
1
+ /**
2
+ * Agent runtime factory — one call to initialize all modules.
3
+ *
4
+ * ```ts
5
+ * const runtime = createAgentRuntime({ agentId: 'my-agent' });
6
+ * // runtime.vault, runtime.brain, runtime.planner, etc. all ready
7
+ * ```
8
+ */
9
+
10
+ import { join } from 'node:path';
11
+ import { homedir } from 'node:os';
12
+ import { Vault } from '../vault/vault.js';
13
+ import { Brain } from '../brain/brain.js';
14
+ import { BrainIntelligence } from '../brain/intelligence.js';
15
+ import { Planner } from '../planning/planner.js';
16
+ import { Curator } from '../curator/curator.js';
17
+ import { KeyPool, loadKeyPoolConfig } from '../llm/key-pool.js';
18
+ import { loadIntelligenceData } from '../intelligence/loader.js';
19
+ import { LLMClient } from '../llm/llm-client.js';
20
+ import type { AgentRuntimeConfig, AgentRuntime } from './types.js';
21
+
22
+ /**
23
+ * Create a fully initialized agent runtime.
24
+ *
25
+ * All modules (vault, brain, planner, curator, key pools, LLM client)
26
+ * are initialized and wired together. New modules added to core in
27
+ * future versions will be included automatically — existing agents
28
+ * just `npm update @soleri/core`.
29
+ */
30
+ export function createAgentRuntime(config: AgentRuntimeConfig): AgentRuntime {
31
+ const { agentId } = config;
32
+ const agentHome = join(homedir(), `.${agentId}`);
33
+ const vaultPath = config.vaultPath ?? join(agentHome, 'vault.db');
34
+ const plansPath = config.plansPath ?? join(agentHome, 'plans.json');
35
+
36
+ // Vault — persistent SQLite knowledge store
37
+ const vault = new Vault(vaultPath);
38
+
39
+ // Seed intelligence data if dataDir provided
40
+ if (config.dataDir) {
41
+ const entries = loadIntelligenceData(config.dataDir);
42
+ if (entries.length > 0) {
43
+ vault.seed(entries);
44
+ }
45
+ }
46
+
47
+ // Planner — multi-step task tracking
48
+ const planner = new Planner(plansPath);
49
+
50
+ // Brain — intelligence layer (TF-IDF scoring, auto-tagging, dedup)
51
+ const brain = new Brain(vault);
52
+
53
+ // Brain Intelligence — pattern strengths, session knowledge, intelligence pipeline
54
+ const brainIntelligence = new BrainIntelligence(vault, brain);
55
+
56
+ // Curator — vault self-maintenance (dedup, contradictions, grooming, health)
57
+ const curator = new Curator(vault);
58
+
59
+ // LLM key pools and client
60
+ const keyPoolFiles = loadKeyPoolConfig(agentId);
61
+ const openaiKeyPool = new KeyPool(keyPoolFiles.openai);
62
+ const anthropicKeyPool = new KeyPool(keyPoolFiles.anthropic);
63
+ const llmClient = new LLMClient(openaiKeyPool, anthropicKeyPool, agentId);
64
+
65
+ return {
66
+ config,
67
+ vault,
68
+ brain,
69
+ brainIntelligence,
70
+ planner,
71
+ curator,
72
+ keyPool: { openai: openaiKeyPool, anthropic: anthropicKeyPool },
73
+ llmClient,
74
+ close: () => vault.close(),
75
+ };
76
+ }
@@ -0,0 +1,39 @@
1
+ import type { Vault } from '../vault/vault.js';
2
+ import type { Brain } from '../brain/brain.js';
3
+ import type { BrainIntelligence } from '../brain/intelligence.js';
4
+ import type { Planner } from '../planning/planner.js';
5
+ import type { Curator } from '../curator/curator.js';
6
+ import type { KeyPool } from '../llm/key-pool.js';
7
+ import type { LLMClient } from '../llm/llm-client.js';
8
+
9
+ /**
10
+ * Configuration for creating an agent runtime.
11
+ * Only `agentId` is required — everything else has sensible defaults.
12
+ */
13
+ export interface AgentRuntimeConfig {
14
+ /** Agent identifier (kebab-case), e.g. 'my-agent'. Used for paths: ~/.{agentId}/ */
15
+ agentId: string;
16
+ /** Path to vault database. Default: ~/.{agentId}/vault.db */
17
+ vaultPath?: string;
18
+ /** Path to plans JSON store. Default: ~/.{agentId}/plans.json */
19
+ plansPath?: string;
20
+ /** Intelligence data directory to seed vault from. Optional. */
21
+ dataDir?: string;
22
+ }
23
+
24
+ /**
25
+ * Fully initialized agent runtime — all modules ready.
26
+ * Created by `createAgentRuntime(config)`.
27
+ */
28
+ export interface AgentRuntime {
29
+ config: AgentRuntimeConfig;
30
+ vault: Vault;
31
+ brain: Brain;
32
+ brainIntelligence: BrainIntelligence;
33
+ planner: Planner;
34
+ curator: Curator;
35
+ keyPool: { openai: KeyPool; anthropic: KeyPool };
36
+ llmClient: LLMClient;
37
+ /** Close the vault database connection. Call on shutdown. */
38
+ close(): void;
39
+ }
@@ -0,0 +1,168 @@
1
+ // ─── Shared Text Processing & Similarity Utilities ──────────────────
2
+ // Pure functions used by Brain (ranked search) and Curator (dedup, contradictions).
3
+
4
+ export type SparseVector = Map<string, number>;
5
+
6
+ // ─── Stopwords ─────────────────────────────────────────────────────
7
+
8
+ export const STOPWORDS = new Set([
9
+ 'a',
10
+ 'an',
11
+ 'the',
12
+ 'and',
13
+ 'or',
14
+ 'but',
15
+ 'in',
16
+ 'on',
17
+ 'at',
18
+ 'to',
19
+ 'for',
20
+ 'of',
21
+ 'with',
22
+ 'by',
23
+ 'from',
24
+ 'as',
25
+ 'is',
26
+ 'was',
27
+ 'are',
28
+ 'were',
29
+ 'been',
30
+ 'be',
31
+ 'have',
32
+ 'has',
33
+ 'had',
34
+ 'do',
35
+ 'does',
36
+ 'did',
37
+ 'will',
38
+ 'would',
39
+ 'could',
40
+ 'should',
41
+ 'may',
42
+ 'might',
43
+ 'shall',
44
+ 'can',
45
+ 'need',
46
+ 'must',
47
+ 'it',
48
+ 'its',
49
+ 'this',
50
+ 'that',
51
+ 'these',
52
+ 'those',
53
+ 'i',
54
+ 'you',
55
+ 'he',
56
+ 'she',
57
+ 'we',
58
+ 'they',
59
+ 'me',
60
+ 'him',
61
+ 'her',
62
+ 'us',
63
+ 'them',
64
+ 'my',
65
+ 'your',
66
+ 'his',
67
+ 'our',
68
+ 'their',
69
+ 'what',
70
+ 'which',
71
+ 'who',
72
+ 'whom',
73
+ 'when',
74
+ 'where',
75
+ 'why',
76
+ 'how',
77
+ 'all',
78
+ 'each',
79
+ 'every',
80
+ 'both',
81
+ 'few',
82
+ 'more',
83
+ 'most',
84
+ 'other',
85
+ 'some',
86
+ 'such',
87
+ 'no',
88
+ 'not',
89
+ 'only',
90
+ 'same',
91
+ 'so',
92
+ 'than',
93
+ 'too',
94
+ 'very',
95
+ 'just',
96
+ 'because',
97
+ 'if',
98
+ 'then',
99
+ 'else',
100
+ 'about',
101
+ 'up',
102
+ 'out',
103
+ 'into',
104
+ ]);
105
+
106
+ // ─── Text Processing ───────────────────────────────────────────────
107
+
108
+ export function tokenize(text: string): string[] {
109
+ return text
110
+ .toLowerCase()
111
+ .replace(/[^a-z0-9\s-]/g, ' ')
112
+ .split(/\s+/)
113
+ .filter((t) => t.length > 2 && !STOPWORDS.has(t));
114
+ }
115
+
116
+ export function calculateTf(tokens: string[]): SparseVector {
117
+ const tf: SparseVector = new Map();
118
+ for (const token of tokens) {
119
+ tf.set(token, (tf.get(token) ?? 0) + 1);
120
+ }
121
+ const len = tokens.length || 1;
122
+ for (const [term, count] of tf) {
123
+ tf.set(term, count / len);
124
+ }
125
+ return tf;
126
+ }
127
+
128
+ export function calculateTfIdf(tokens: string[], vocabulary: Map<string, number>): SparseVector {
129
+ const tf = calculateTf(tokens);
130
+ const tfidf: SparseVector = new Map();
131
+ for (const [term, tfValue] of tf) {
132
+ const idf = vocabulary.get(term) ?? 0;
133
+ if (idf > 0) {
134
+ tfidf.set(term, tfValue * idf);
135
+ }
136
+ }
137
+ return tfidf;
138
+ }
139
+
140
+ export function cosineSimilarity(a: SparseVector, b: SparseVector): number {
141
+ let dot = 0;
142
+ let normA = 0;
143
+ let normB = 0;
144
+ for (const [term, valA] of a) {
145
+ normA += valA * valA;
146
+ const valB = b.get(term);
147
+ if (valB !== undefined) {
148
+ dot += valA * valB;
149
+ }
150
+ }
151
+ for (const [, valB] of b) {
152
+ normB += valB * valB;
153
+ }
154
+ const denom = Math.sqrt(normA) * Math.sqrt(normB);
155
+ return denom === 0 ? 0 : dot / denom;
156
+ }
157
+
158
+ export function jaccardSimilarity(a: string[], b: string[]): number {
159
+ if (a.length === 0 && b.length === 0) return 0;
160
+ const setA = new Set(a);
161
+ const setB = new Set(b);
162
+ let intersection = 0;
163
+ for (const item of setA) {
164
+ if (setB.has(item)) intersection++;
165
+ }
166
+ const union = new Set([...a, ...b]).size;
167
+ return union === 0 ? 0 : intersection / union;
168
+ }