@soleri/forge 4.0.0 → 4.2.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 (79) hide show
  1. package/CHANGELOG.md +16 -1
  2. package/README.md +16 -1
  3. package/dist/domain-manager.d.ts +19 -0
  4. package/dist/domain-manager.js +139 -0
  5. package/dist/domain-manager.js.map +1 -0
  6. package/dist/facades/forge.facade.js +16 -0
  7. package/dist/facades/forge.facade.js.map +1 -1
  8. package/dist/index.js +0 -0
  9. package/dist/knowledge-installer.d.ts +0 -17
  10. package/dist/knowledge-installer.js +2 -96
  11. package/dist/knowledge-installer.js.map +1 -1
  12. package/dist/lib.d.ts +12 -0
  13. package/dist/lib.js +12 -0
  14. package/dist/lib.js.map +1 -0
  15. package/dist/patching.d.ts +17 -0
  16. package/dist/patching.js +103 -0
  17. package/dist/patching.js.map +1 -0
  18. package/dist/templates/core-facade.js +64 -4
  19. package/dist/templates/core-facade.js.map +1 -1
  20. package/dist/templates/entry-point.js +22 -3
  21. package/dist/templates/entry-point.js.map +1 -1
  22. package/dist/templates/package-json.js +1 -1
  23. package/dist/types.d.ts +11 -0
  24. package/package.json +8 -1
  25. package/src/__tests__/knowledge-installer.test.ts +3 -7
  26. package/src/domain-manager.ts +168 -0
  27. package/src/facades/forge.facade.ts +18 -0
  28. package/src/knowledge-installer.ts +2 -117
  29. package/src/lib.ts +19 -0
  30. package/src/patching.ts +123 -0
  31. package/src/templates/core-facade.ts +64 -4
  32. package/src/templates/entry-point.ts +22 -3
  33. package/src/templates/package-json.ts +1 -1
  34. package/src/types.ts +12 -0
  35. package/dist/templates/brain.d.ts +0 -6
  36. package/dist/templates/brain.js +0 -478
  37. package/dist/templates/brain.js.map +0 -1
  38. package/dist/templates/facade-factory.d.ts +0 -1
  39. package/dist/templates/facade-factory.js +0 -63
  40. package/dist/templates/facade-factory.js.map +0 -1
  41. package/dist/templates/facade-types.d.ts +0 -1
  42. package/dist/templates/facade-types.js +0 -46
  43. package/dist/templates/facade-types.js.map +0 -1
  44. package/dist/templates/intelligence-loader.d.ts +0 -1
  45. package/dist/templates/intelligence-loader.js +0 -43
  46. package/dist/templates/intelligence-loader.js.map +0 -1
  47. package/dist/templates/intelligence-types.d.ts +0 -1
  48. package/dist/templates/intelligence-types.js +0 -24
  49. package/dist/templates/intelligence-types.js.map +0 -1
  50. package/dist/templates/llm-key-pool.d.ts +0 -7
  51. package/dist/templates/llm-key-pool.js +0 -211
  52. package/dist/templates/llm-key-pool.js.map +0 -1
  53. package/dist/templates/llm-types.d.ts +0 -5
  54. package/dist/templates/llm-types.js +0 -161
  55. package/dist/templates/llm-types.js.map +0 -1
  56. package/dist/templates/llm-utils.d.ts +0 -5
  57. package/dist/templates/llm-utils.js +0 -260
  58. package/dist/templates/llm-utils.js.map +0 -1
  59. package/dist/templates/planner.d.ts +0 -5
  60. package/dist/templates/planner.js +0 -150
  61. package/dist/templates/planner.js.map +0 -1
  62. package/dist/templates/test-brain.d.ts +0 -6
  63. package/dist/templates/test-brain.js +0 -474
  64. package/dist/templates/test-brain.js.map +0 -1
  65. package/dist/templates/test-llm.d.ts +0 -7
  66. package/dist/templates/test-llm.js +0 -574
  67. package/dist/templates/test-llm.js.map +0 -1
  68. package/dist/templates/test-loader.d.ts +0 -5
  69. package/dist/templates/test-loader.js +0 -146
  70. package/dist/templates/test-loader.js.map +0 -1
  71. package/dist/templates/test-planner.d.ts +0 -5
  72. package/dist/templates/test-planner.js +0 -271
  73. package/dist/templates/test-planner.js.map +0 -1
  74. package/dist/templates/test-vault.d.ts +0 -5
  75. package/dist/templates/test-vault.js +0 -380
  76. package/dist/templates/test-vault.js.map +0 -1
  77. package/dist/templates/vault.d.ts +0 -5
  78. package/dist/templates/vault.js +0 -263
  79. package/dist/templates/vault.js.map +0 -1
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Source file patching utilities — shared between knowledge-installer and domain-manager.
3
+ *
4
+ * These functions modify an agent's src/index.ts and src/activation/claude-md-content.ts
5
+ * to register new domain facades and their CLAUDE.md table rows.
6
+ */
7
+ import { pascalCase } from './templates/domain-facade.js';
8
+
9
+ /**
10
+ * Patch the agent's src/index.ts to add imports and facade registrations
11
+ * for new domains.
12
+ *
13
+ * Anchor patterns:
14
+ * - Import: insert before `import { createCoreFacade }`
15
+ * - Facade array: insert before `createCoreFacade(`
16
+ */
17
+ export function patchIndexTs(
18
+ source: string,
19
+ newDomains: string[],
20
+ hasBrain: boolean,
21
+ ): string | null {
22
+ // Filter out domains whose imports already exist (idempotent)
23
+ const domainsToImport = newDomains.filter((d) => {
24
+ const fn = `create${pascalCase(d)}Facade`;
25
+ return !source.includes(`import { ${fn} }`);
26
+ });
27
+
28
+ // Filter out domains whose facade calls already exist
29
+ const domainsToRegister = newDomains.filter((d) => {
30
+ const fn = `create${pascalCase(d)}Facade(`;
31
+ return !source.includes(fn);
32
+ });
33
+
34
+ // Nothing to patch
35
+ if (domainsToImport.length === 0 && domainsToRegister.length === 0) {
36
+ return source;
37
+ }
38
+
39
+ let patched = source;
40
+
41
+ // ── Insert imports ──
42
+ if (domainsToImport.length > 0) {
43
+ const importAnchor = /^(import \{ createCoreFacade \}.*$)/m;
44
+ if (!importAnchor.test(patched)) return null;
45
+
46
+ const newImports = domainsToImport
47
+ .map((d) => {
48
+ const fn = `create${pascalCase(d)}Facade`;
49
+ return `import { ${fn} } from './facades/${d}.facade.js';`;
50
+ })
51
+ .join('\n');
52
+
53
+ patched = patched.replace(importAnchor, `${newImports}\n$1`);
54
+ }
55
+
56
+ // ── Insert facade creations ──
57
+ if (domainsToRegister.length > 0) {
58
+ const facadeAnchor = /^(\s+createCoreFacade\()/m;
59
+ if (!facadeAnchor.test(patched)) return null;
60
+
61
+ const newCreations = domainsToRegister
62
+ .map((d) => {
63
+ const fn = `create${pascalCase(d)}Facade`;
64
+ const args = hasBrain ? 'vault, brain' : 'vault';
65
+ return ` ${fn}(${args}),`;
66
+ })
67
+ .join('\n');
68
+
69
+ patched = patched.replace(facadeAnchor, `${newCreations}\n$1`);
70
+ }
71
+
72
+ return patched;
73
+ }
74
+
75
+ /**
76
+ * Patch the agent's src/activation/claude-md-content.ts to add
77
+ * facade table rows for new domains.
78
+ *
79
+ * Primary anchor: line containing `| Memory search |` (newer agents)
80
+ * Fallback anchor: line containing `## Intent Detection` (older agents without memory/brain rows)
81
+ */
82
+ export function patchClaudeMdContent(
83
+ source: string,
84
+ agentId: string,
85
+ newDomains: string[],
86
+ ): string | null {
87
+ const facadeId = agentId.replace(/-/g, '_');
88
+ const bt = '`';
89
+
90
+ // Filter out domains whose rows already exist (idempotent)
91
+ const domainsToAdd = newDomains.filter((d) => {
92
+ const toolName = `${facadeId}_${d.replace(/-/g, '_')}`;
93
+ return !source.includes(`${toolName}`);
94
+ });
95
+
96
+ if (domainsToAdd.length === 0) return source;
97
+
98
+ // Try primary anchor first, then fallback for older agents
99
+ let anchorIdx = source.indexOf("'| Memory search |");
100
+ if (anchorIdx === -1) {
101
+ // Older agents: insert before the empty line preceding ## Intent Detection
102
+ anchorIdx = source.indexOf("'## Intent Detection'");
103
+ if (anchorIdx === -1) return null;
104
+ // Back up to include the preceding empty string line ('',)
105
+ const emptyLineIdx = source.lastIndexOf("'',", anchorIdx);
106
+ if (emptyLineIdx !== -1 && anchorIdx - emptyLineIdx < 20) {
107
+ // Find the start of that line (the indentation)
108
+ const lineStart = source.lastIndexOf('\n', emptyLineIdx);
109
+ anchorIdx = lineStart === -1 ? emptyLineIdx : lineStart + 1;
110
+ }
111
+ }
112
+
113
+ const newRows = domainsToAdd.flatMap((d) => {
114
+ const toolName = `${facadeId}_${d.replace(/-/g, '_')}`;
115
+ return [
116
+ ` '| ${d} patterns | ${bt}${toolName}${bt} | ${bt}get_patterns${bt} |',`,
117
+ ` '| Search ${d} | ${bt}${toolName}${bt} | ${bt}search${bt} |',`,
118
+ ` '| Capture ${d} | ${bt}${toolName}${bt} | ${bt}capture${bt} |',`,
119
+ ];
120
+ });
121
+
122
+ return source.slice(0, anchorIdx) + newRows.join('\n') + '\n' + source.slice(anchorIdx);
123
+ }
@@ -6,20 +6,20 @@ import type { AgentConfig } from '../types.js';
6
6
  */
7
7
  export function generateCoreFacade(config: AgentConfig): string {
8
8
  return `import { z } from 'zod';
9
- import type { FacadeConfig, Vault, IntelligenceEntry, Planner, Brain, KeyPool } from '@soleri/core';
9
+ import type { FacadeConfig, Vault, IntelligenceEntry, Planner, Brain, KeyPool, CogneeClient } from '@soleri/core';
10
10
  import { PERSONA } from '../identity/persona.js';
11
11
  import { activateAgent, deactivateAgent } from '../activation/activate.js';
12
12
  import { injectClaudeMd, injectClaudeMdGlobal, hasAgentMarker } from '../activation/inject-claude-md.js';
13
13
  import type { LLMClient } from '../llm/llm-client.js';
14
14
 
15
- export function createCoreFacade(vault: Vault, planner: Planner, brain: Brain, llmClient?: LLMClient, openaiKeyPool?: KeyPool, anthropicKeyPool?: KeyPool): FacadeConfig {
15
+ export function createCoreFacade(vault: Vault, planner: Planner, brain: Brain, cognee?: CogneeClient, llmClient?: LLMClient, openaiKeyPool?: KeyPool, anthropicKeyPool?: KeyPool): FacadeConfig {
16
16
  return {
17
17
  name: '${config.id}_core',
18
18
  description: 'Core operations — vault stats, cross-domain search, health check, identity, and activation system.',
19
19
  ops: [
20
20
  {
21
21
  name: 'search',
22
- description: 'Search across all knowledge domains. Results ranked by TF-IDF + severity + recency + tag overlap + domain match.',
22
+ description: 'Search across all knowledge domains. Results ranked by TF-IDF + severity + recency + tag overlap + domain match. When Cognee is available, adds vector similarity for hybrid ranking.',
23
23
  auth: 'read',
24
24
  schema: z.object({
25
25
  query: z.string(),
@@ -30,7 +30,7 @@ export function createCoreFacade(vault: Vault, planner: Planner, brain: Brain, l
30
30
  limit: z.number().optional(),
31
31
  }),
32
32
  handler: async (params) => {
33
- return brain.intelligentSearch(params.query as string, {
33
+ return await brain.intelligentSearch(params.query as string, {
34
34
  domain: params.domain as string | undefined,
35
35
  type: params.type as string | undefined,
36
36
  severity: params.severity as string | undefined,
@@ -450,6 +450,66 @@ export function createCoreFacade(vault: Vault, planner: Planner, brain: Brain, l
450
450
  };
451
451
  },
452
452
  },
453
+ {
454
+ name: 'cognee_status',
455
+ description: 'Check Cognee vector search availability and connection status.',
456
+ auth: 'read',
457
+ handler: async () => {
458
+ if (!cognee) return { available: false, message: 'Cognee not configured' };
459
+ const status = cognee.getStatus();
460
+ const cfg = cognee.getConfig();
461
+ return {
462
+ available: cognee.isAvailable,
463
+ config: {
464
+ baseUrl: cfg.baseUrl,
465
+ dataset: cfg.dataset,
466
+ timeoutMs: cfg.timeoutMs,
467
+ searchTimeoutMs: cfg.searchTimeoutMs,
468
+ healthTimeoutMs: cfg.healthTimeoutMs,
469
+ healthCacheTtlMs: cfg.healthCacheTtlMs,
470
+ cognifyDebounceMs: cfg.cognifyDebounceMs,
471
+ hasApiToken: Boolean(cfg.apiToken),
472
+ hasServiceCredentials: Boolean(cfg.serviceEmail && cfg.servicePassword),
473
+ },
474
+ lastHealth: status,
475
+ };
476
+ },
477
+ },
478
+ {
479
+ name: 'cognee_sync',
480
+ description: 'Force sync all vault entries to Cognee and trigger knowledge graph build. Requires Cognee to be running.',
481
+ auth: 'write',
482
+ handler: async () => {
483
+ if (!cognee?.isAvailable) {
484
+ return { synced: false, message: 'Cognee not available. Start with: docker compose -f docker/docker-compose.cognee.yml up -d' };
485
+ }
486
+ const result = await brain.syncToCognee();
487
+ return { ok: true, entriesSynced: result.synced, cognified: result.cognified };
488
+ },
489
+ },
490
+ {
491
+ name: 'graph_search',
492
+ description: 'Search using Cognee knowledge graph completion. Returns semantically related entries that keyword search might miss.',
493
+ auth: 'read',
494
+ schema: z.object({
495
+ query: z.string(),
496
+ searchType: z.enum(['SUMMARIES', 'CHUNKS', 'RAG_COMPLETION', 'GRAPH_COMPLETION', 'GRAPH_SUMMARY_COMPLETION', 'NATURAL_LANGUAGE', 'FEELING_LUCKY', 'CHUNKS_LEXICAL']).optional().default('CHUNKS'),
497
+ limit: z.number().optional().default(10),
498
+ }),
499
+ handler: async (params) => {
500
+ if (!cognee?.isAvailable) {
501
+ return { results: [], message: 'Cognee not available — use search op for FTS5 results' };
502
+ }
503
+ const results = await cognee.search(
504
+ params.query as string,
505
+ {
506
+ searchType: params.searchType as 'SUMMARIES' | 'CHUNKS' | 'GRAPH_COMPLETION',
507
+ limit: params.limit as number,
508
+ },
509
+ );
510
+ return { results, count: results.length };
511
+ },
512
+ },
453
513
  ],
454
514
  };
455
515
  }
@@ -24,7 +24,7 @@ import { dirname, join } from 'node:path';
24
24
  import { fileURLToPath } from 'node:url';
25
25
  import { homedir } from 'node:os';
26
26
 
27
- import { Vault, Brain, Planner, KeyPool, loadKeyPoolConfig, loadIntelligenceData, registerAllFacades } from '@soleri/core';
27
+ import { Vault, Brain, Planner, KeyPool, CogneeClient, loadKeyPoolConfig, loadIntelligenceData, registerAllFacades } from '@soleri/core';
28
28
  ${facadeImports}
29
29
  import { createCoreFacade } from './facades/core.facade.js';
30
30
  import { LLMClient } from './llm/llm-client.js';
@@ -53,10 +53,29 @@ async function main(): Promise<void> {
53
53
  const planner = new Planner(plansPath);
54
54
  console.error(\`[\${PERSONA.name.toLowerCase()}] Planner: \${plansPath}\`);
55
55
 
56
+ // Initialize Cognee client (optional — vector search + knowledge graph)
57
+ // Auto-registers a service account on first use. Override with env vars.
58
+ const cognee = new CogneeClient({
59
+ baseUrl: process.env.COGNEE_URL ?? 'http://localhost:8000',
60
+ dataset: '${config.id}-vault',
61
+ ...(process.env.COGNEE_API_TOKEN ? { apiToken: process.env.COGNEE_API_TOKEN } : {}),
62
+ ...(process.env.COGNEE_EMAIL ? { serviceEmail: process.env.COGNEE_EMAIL } : {}),
63
+ ...(process.env.COGNEE_PASSWORD ? { servicePassword: process.env.COGNEE_PASSWORD } : {}),
64
+ });
65
+ await cognee.healthCheck();
66
+ console.error(\`[\${PERSONA.name.toLowerCase()}] Cognee: \${cognee.isAvailable ? 'available' : 'not running (FTS5 fallback)'}\`);
67
+
56
68
  // Initialize brain — intelligence layer for ranked search, auto-tagging, dedup
57
- const brain = new Brain(vault);
69
+ const brain = new Brain(vault, cognee.isAvailable ? cognee : undefined);
58
70
  console.error(\`[\${PERSONA.name.toLowerCase()}] Brain: vocabulary \${brain.getVocabularySize()} terms\`);
59
71
 
72
+ // Background sync vault to Cognee if available
73
+ if (cognee.isAvailable) {
74
+ brain.syncToCognee().catch((err) => {
75
+ console.error(\`[\${PERSONA.name.toLowerCase()}] Cognee sync failed:\`, err);
76
+ });
77
+ }
78
+
60
79
  // Initialize LLM client (optional — works without API keys)
61
80
  const keyPoolFiles = loadKeyPoolConfig('${config.id}');
62
81
  const openaiKeyPool = new KeyPool(keyPoolFiles.openai);
@@ -79,7 +98,7 @@ async function main(): Promise<void> {
79
98
  // Create and register facades
80
99
  const facades = [
81
100
  ${facadeCreations}
82
- createCoreFacade(vault, planner, brain, llmClient, openaiKeyPool, anthropicKeyPool),
101
+ createCoreFacade(vault, planner, brain, cognee.isAvailable ? cognee : undefined, llmClient, openaiKeyPool, anthropicKeyPool),
83
102
  ];
84
103
 
85
104
  registerAllFacades(server, facades);
@@ -23,7 +23,7 @@ export function generatePackageJson(config: AgentConfig): string {
23
23
  dependencies: {
24
24
  '@anthropic-ai/sdk': '^0.39.0',
25
25
  '@modelcontextprotocol/sdk': '^1.12.1',
26
- '@soleri/core': '^1.0.0',
26
+ '@soleri/core': '^2.0.0',
27
27
  zod: '^3.24.2',
28
28
  },
29
29
  devDependencies: {
package/src/types.ts CHANGED
@@ -51,6 +51,18 @@ export interface ScaffoldPreview {
51
51
  persona: { name: string; role: string };
52
52
  }
53
53
 
54
+ /** Result of adding a domain to an existing agent */
55
+ export interface AddDomainResult {
56
+ success: boolean;
57
+ agentPath: string;
58
+ domain: string;
59
+ agentId: string;
60
+ facadeGenerated: boolean;
61
+ buildOutput: string;
62
+ warnings: string[];
63
+ summary: string;
64
+ }
65
+
54
66
  /** Result of installing knowledge packs into an existing agent */
55
67
  export interface InstallKnowledgeResult {
56
68
  success: boolean;
@@ -1,6 +0,0 @@
1
- /**
2
- * Generates the brain.ts source file for a new agent.
3
- * The Brain provides a local, zero-dependency intelligence layer:
4
- * TF-IDF scoring, auto-tagging, duplicate detection, and search feedback.
5
- */
6
- export declare function generateBrain(): string;