@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.
- package/CHANGELOG.md +16 -1
- package/README.md +16 -1
- package/dist/domain-manager.d.ts +19 -0
- package/dist/domain-manager.js +139 -0
- package/dist/domain-manager.js.map +1 -0
- package/dist/facades/forge.facade.js +16 -0
- package/dist/facades/forge.facade.js.map +1 -1
- package/dist/index.js +0 -0
- package/dist/knowledge-installer.d.ts +0 -17
- package/dist/knowledge-installer.js +2 -96
- package/dist/knowledge-installer.js.map +1 -1
- package/dist/lib.d.ts +12 -0
- package/dist/lib.js +12 -0
- package/dist/lib.js.map +1 -0
- package/dist/patching.d.ts +17 -0
- package/dist/patching.js +103 -0
- package/dist/patching.js.map +1 -0
- package/dist/templates/core-facade.js +64 -4
- package/dist/templates/core-facade.js.map +1 -1
- package/dist/templates/entry-point.js +22 -3
- package/dist/templates/entry-point.js.map +1 -1
- package/dist/templates/package-json.js +1 -1
- package/dist/types.d.ts +11 -0
- package/package.json +8 -1
- package/src/__tests__/knowledge-installer.test.ts +3 -7
- package/src/domain-manager.ts +168 -0
- package/src/facades/forge.facade.ts +18 -0
- package/src/knowledge-installer.ts +2 -117
- package/src/lib.ts +19 -0
- package/src/patching.ts +123 -0
- package/src/templates/core-facade.ts +64 -4
- package/src/templates/entry-point.ts +22 -3
- package/src/templates/package-json.ts +1 -1
- package/src/types.ts +12 -0
- package/dist/templates/brain.d.ts +0 -6
- package/dist/templates/brain.js +0 -478
- package/dist/templates/brain.js.map +0 -1
- package/dist/templates/facade-factory.d.ts +0 -1
- package/dist/templates/facade-factory.js +0 -63
- package/dist/templates/facade-factory.js.map +0 -1
- package/dist/templates/facade-types.d.ts +0 -1
- package/dist/templates/facade-types.js +0 -46
- package/dist/templates/facade-types.js.map +0 -1
- package/dist/templates/intelligence-loader.d.ts +0 -1
- package/dist/templates/intelligence-loader.js +0 -43
- package/dist/templates/intelligence-loader.js.map +0 -1
- package/dist/templates/intelligence-types.d.ts +0 -1
- package/dist/templates/intelligence-types.js +0 -24
- package/dist/templates/intelligence-types.js.map +0 -1
- package/dist/templates/llm-key-pool.d.ts +0 -7
- package/dist/templates/llm-key-pool.js +0 -211
- package/dist/templates/llm-key-pool.js.map +0 -1
- package/dist/templates/llm-types.d.ts +0 -5
- package/dist/templates/llm-types.js +0 -161
- package/dist/templates/llm-types.js.map +0 -1
- package/dist/templates/llm-utils.d.ts +0 -5
- package/dist/templates/llm-utils.js +0 -260
- package/dist/templates/llm-utils.js.map +0 -1
- package/dist/templates/planner.d.ts +0 -5
- package/dist/templates/planner.js +0 -150
- package/dist/templates/planner.js.map +0 -1
- package/dist/templates/test-brain.d.ts +0 -6
- package/dist/templates/test-brain.js +0 -474
- package/dist/templates/test-brain.js.map +0 -1
- package/dist/templates/test-llm.d.ts +0 -7
- package/dist/templates/test-llm.js +0 -574
- package/dist/templates/test-llm.js.map +0 -1
- package/dist/templates/test-loader.d.ts +0 -5
- package/dist/templates/test-loader.js +0 -146
- package/dist/templates/test-loader.js.map +0 -1
- package/dist/templates/test-planner.d.ts +0 -5
- package/dist/templates/test-planner.js +0 -271
- package/dist/templates/test-planner.js.map +0 -1
- package/dist/templates/test-vault.d.ts +0 -5
- package/dist/templates/test-vault.js +0 -380
- package/dist/templates/test-vault.js.map +0 -1
- package/dist/templates/vault.d.ts +0 -5
- package/dist/templates/vault.js +0 -263
- package/dist/templates/vault.js.map +0 -1
package/src/patching.ts
ADDED
|
@@ -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': '^
|
|
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;
|