@soleri/core 2.0.2 → 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.
- package/dist/brain/brain.d.ts +12 -50
- package/dist/brain/brain.d.ts.map +1 -1
- package/dist/brain/brain.js +147 -12
- package/dist/brain/brain.js.map +1 -1
- package/dist/brain/intelligence.d.ts +51 -0
- package/dist/brain/intelligence.d.ts.map +1 -0
- package/dist/brain/intelligence.js +666 -0
- package/dist/brain/intelligence.js.map +1 -0
- package/dist/brain/types.d.ts +165 -0
- package/dist/brain/types.d.ts.map +1 -0
- package/dist/brain/types.js +2 -0
- package/dist/brain/types.js.map +1 -0
- package/dist/cognee/client.d.ts +35 -0
- package/dist/cognee/client.d.ts.map +1 -0
- package/dist/cognee/client.js +291 -0
- package/dist/cognee/client.js.map +1 -0
- package/dist/cognee/types.d.ts +46 -0
- package/dist/cognee/types.d.ts.map +1 -0
- package/dist/cognee/types.js +3 -0
- package/dist/cognee/types.js.map +1 -0
- package/dist/curator/curator.d.ts.map +1 -1
- package/dist/curator/curator.js +7 -5
- package/dist/curator/curator.js.map +1 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -1
- package/dist/llm/llm-client.d.ts.map +1 -1
- package/dist/llm/llm-client.js +9 -2
- package/dist/llm/llm-client.js.map +1 -1
- package/dist/runtime/core-ops.d.ts +3 -3
- package/dist/runtime/core-ops.d.ts.map +1 -1
- package/dist/runtime/core-ops.js +180 -15
- package/dist/runtime/core-ops.js.map +1 -1
- package/dist/runtime/runtime.d.ts.map +1 -1
- package/dist/runtime/runtime.js +4 -0
- package/dist/runtime/runtime.js.map +1 -1
- package/dist/runtime/types.d.ts +2 -0
- package/dist/runtime/types.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/brain-intelligence.test.ts +623 -0
- package/src/__tests__/brain.test.ts +265 -27
- package/src/__tests__/cognee-client.test.ts +524 -0
- package/src/__tests__/core-ops.test.ts +77 -49
- package/src/__tests__/curator.test.ts +126 -31
- package/src/__tests__/domain-ops.test.ts +45 -9
- package/src/__tests__/runtime.test.ts +13 -11
- package/src/brain/brain.ts +194 -65
- package/src/brain/intelligence.ts +1061 -0
- package/src/brain/types.ts +176 -0
- package/src/cognee/client.ts +352 -0
- package/src/cognee/types.ts +62 -0
- package/src/curator/curator.ts +52 -15
- package/src/index.ts +26 -1
- package/src/llm/llm-client.ts +18 -24
- package/src/runtime/core-ops.ts +219 -26
- package/src/runtime/runtime.ts +5 -0
- package/src/runtime/types.ts +2 -0
package/src/curator/curator.ts
CHANGED
|
@@ -188,7 +188,13 @@ export class Curator {
|
|
|
188
188
|
JSON.stringify(dedupedTags),
|
|
189
189
|
entryId,
|
|
190
190
|
);
|
|
191
|
-
this.logChange(
|
|
191
|
+
this.logChange(
|
|
192
|
+
'normalize_tags',
|
|
193
|
+
entryId,
|
|
194
|
+
JSON.stringify(entry.tags),
|
|
195
|
+
JSON.stringify(dedupedTags),
|
|
196
|
+
'Tag normalization',
|
|
197
|
+
);
|
|
192
198
|
}
|
|
193
199
|
|
|
194
200
|
return results;
|
|
@@ -245,13 +251,13 @@ export class Curator {
|
|
|
245
251
|
// Build vectors for all entries
|
|
246
252
|
const vectors = new Map<string, SparseVector>();
|
|
247
253
|
for (const entry of entries) {
|
|
248
|
-
const text = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(
|
|
254
|
+
const text = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(
|
|
255
|
+
' ',
|
|
256
|
+
);
|
|
249
257
|
vectors.set(entry.id, calculateTfIdf(tokenize(text), vocabulary));
|
|
250
258
|
}
|
|
251
259
|
|
|
252
|
-
const targetEntries = entryId
|
|
253
|
-
? entries.filter((e) => e.id === entryId)
|
|
254
|
-
: entries;
|
|
260
|
+
const targetEntries = entryId ? entries.filter((e) => e.id === entryId) : entries;
|
|
255
261
|
|
|
256
262
|
const results: DuplicateDetectionResult[] = [];
|
|
257
263
|
|
|
@@ -428,7 +434,8 @@ export class Curator {
|
|
|
428
434
|
const dryRun = options?.dryRun ?? true;
|
|
429
435
|
const staleDaysThreshold = options?.staleDaysThreshold ?? DEFAULT_STALE_DAYS;
|
|
430
436
|
const duplicateThreshold = options?.duplicateThreshold ?? DEFAULT_DUPLICATE_THRESHOLD;
|
|
431
|
-
const contradictionThreshold =
|
|
437
|
+
const contradictionThreshold =
|
|
438
|
+
options?.contradictionThreshold ?? DEFAULT_CONTRADICTION_THRESHOLD;
|
|
432
439
|
|
|
433
440
|
// Detect duplicates
|
|
434
441
|
const duplicates = this.detectDuplicates(undefined, duplicateThreshold);
|
|
@@ -455,7 +462,13 @@ export class Curator {
|
|
|
455
462
|
VALUES (?, 'archived', unixepoch())
|
|
456
463
|
ON CONFLICT(entry_id) DO UPDATE SET status = 'archived', last_groomed_at = unixepoch()`,
|
|
457
464
|
).run(entryId);
|
|
458
|
-
this.logChange(
|
|
465
|
+
this.logChange(
|
|
466
|
+
'archive',
|
|
467
|
+
entryId,
|
|
468
|
+
'active',
|
|
469
|
+
'archived',
|
|
470
|
+
'Stale entry archived during consolidation',
|
|
471
|
+
);
|
|
459
472
|
mutations++;
|
|
460
473
|
}
|
|
461
474
|
|
|
@@ -526,9 +539,21 @@ export class Curator {
|
|
|
526
539
|
const hasAntiPatterns = typeCount['anti-pattern'] > 0;
|
|
527
540
|
const hasRules = typeCount.rule > 0;
|
|
528
541
|
let coverageScore = 1;
|
|
529
|
-
if (!hasPatterns) {
|
|
530
|
-
|
|
531
|
-
|
|
542
|
+
if (!hasPatterns) {
|
|
543
|
+
score -= 10;
|
|
544
|
+
coverageScore -= 0.33;
|
|
545
|
+
recommendations.push('No patterns found — add patterns to improve coverage.');
|
|
546
|
+
}
|
|
547
|
+
if (!hasAntiPatterns) {
|
|
548
|
+
score -= 5;
|
|
549
|
+
coverageScore -= 0.17;
|
|
550
|
+
recommendations.push('No anti-patterns found — add anti-patterns to detect contradictions.');
|
|
551
|
+
}
|
|
552
|
+
if (!hasRules) {
|
|
553
|
+
score -= 5;
|
|
554
|
+
coverageScore -= 0.17;
|
|
555
|
+
recommendations.push('No rules found — add rules for completeness.');
|
|
556
|
+
}
|
|
532
557
|
coverageScore = Math.max(0, coverageScore);
|
|
533
558
|
|
|
534
559
|
// Freshness: penalize stale entries
|
|
@@ -536,14 +561,18 @@ export class Curator {
|
|
|
536
561
|
const now = Math.floor(Date.now() / 1000);
|
|
537
562
|
const staleThreshold = now - DEFAULT_STALE_DAYS * 86400;
|
|
538
563
|
const staleCount = (
|
|
539
|
-
db
|
|
564
|
+
db
|
|
565
|
+
.prepare('SELECT COUNT(*) as count FROM entries WHERE updated_at < ?')
|
|
566
|
+
.get(staleThreshold) as { count: number }
|
|
540
567
|
).count;
|
|
541
568
|
const staleRatio = staleCount / entries.length;
|
|
542
569
|
const freshnessScore = 1 - staleRatio;
|
|
543
570
|
if (staleRatio > 0.3) {
|
|
544
571
|
const penalty = Math.min(20, Math.round(staleRatio * 30));
|
|
545
572
|
score -= penalty;
|
|
546
|
-
recommendations.push(
|
|
573
|
+
recommendations.push(
|
|
574
|
+
`${staleCount} stale entries (${Math.round(staleRatio * 100)}%) — run grooming to update.`,
|
|
575
|
+
);
|
|
547
576
|
}
|
|
548
577
|
|
|
549
578
|
// Quality: penalize duplicates and contradictions
|
|
@@ -571,12 +600,18 @@ export class Curator {
|
|
|
571
600
|
if (lowTagRatio > 0.3) {
|
|
572
601
|
const penalty = Math.min(10, Math.round(lowTagRatio * 15));
|
|
573
602
|
score -= penalty;
|
|
574
|
-
recommendations.push(
|
|
603
|
+
recommendations.push(
|
|
604
|
+
`${lowTagEntries.length} entries have fewer than 2 tags — improve tagging.`,
|
|
605
|
+
);
|
|
575
606
|
}
|
|
576
607
|
|
|
577
608
|
// Penalize ungroomed entries
|
|
578
609
|
const groomedCount = (
|
|
579
|
-
db
|
|
610
|
+
db
|
|
611
|
+
.prepare(
|
|
612
|
+
'SELECT COUNT(*) as count FROM curator_entry_state WHERE last_groomed_at IS NOT NULL',
|
|
613
|
+
)
|
|
614
|
+
.get() as { count: number }
|
|
580
615
|
).count;
|
|
581
616
|
if (groomedCount < entries.length) {
|
|
582
617
|
const ungroomed = entries.length - groomedCount;
|
|
@@ -609,7 +644,9 @@ export class Curator {
|
|
|
609
644
|
const docCount = entries.length;
|
|
610
645
|
const termDocFreq = new Map<string, number>();
|
|
611
646
|
for (const entry of entries) {
|
|
612
|
-
const text = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(
|
|
647
|
+
const text = [entry.title, entry.description, entry.context ?? '', entry.tags.join(' ')].join(
|
|
648
|
+
' ',
|
|
649
|
+
);
|
|
613
650
|
const tokens = new Set(tokenize(text));
|
|
614
651
|
for (const token of tokens) {
|
|
615
652
|
termDocFreq.set(token, (termDocFreq.get(token) ?? 0) + 1);
|
package/src/index.ts
CHANGED
|
@@ -39,6 +39,7 @@ export type {
|
|
|
39
39
|
|
|
40
40
|
// ─── Brain ───────────────────────────────────────────────────────────
|
|
41
41
|
export { Brain } from './brain/brain.js';
|
|
42
|
+
export { BrainIntelligence } from './brain/intelligence.js';
|
|
42
43
|
export type {
|
|
43
44
|
ScoringWeights,
|
|
44
45
|
ScoreBreakdown,
|
|
@@ -47,7 +48,31 @@ export type {
|
|
|
47
48
|
CaptureResult,
|
|
48
49
|
BrainStats,
|
|
49
50
|
QueryContext,
|
|
50
|
-
|
|
51
|
+
PatternStrength,
|
|
52
|
+
StrengthsQuery,
|
|
53
|
+
BrainSession,
|
|
54
|
+
SessionLifecycleInput,
|
|
55
|
+
KnowledgeProposal,
|
|
56
|
+
ExtractionResult,
|
|
57
|
+
GlobalPattern,
|
|
58
|
+
DomainProfile,
|
|
59
|
+
BuildIntelligenceResult,
|
|
60
|
+
BrainIntelligenceStats,
|
|
61
|
+
SessionContext,
|
|
62
|
+
BrainExportData,
|
|
63
|
+
BrainImportResult,
|
|
64
|
+
} from './brain/types.js';
|
|
65
|
+
|
|
66
|
+
// ─── Cognee ─────────────────────────────────────────────────────────
|
|
67
|
+
export { CogneeClient } from './cognee/client.js';
|
|
68
|
+
export type {
|
|
69
|
+
CogneeConfig,
|
|
70
|
+
CogneeSearchResult,
|
|
71
|
+
CogneeSearchType,
|
|
72
|
+
CogneeStatus,
|
|
73
|
+
CogneeAddResult,
|
|
74
|
+
CogneeCognifyResult,
|
|
75
|
+
} from './cognee/types.js';
|
|
51
76
|
|
|
52
77
|
// ─── Planning ────────────────────────────────────────────────────────
|
|
53
78
|
export { Planner } from './planning/planner.js';
|
package/src/llm/llm-client.ts
CHANGED
|
@@ -11,12 +11,7 @@ import * as path from 'node:path';
|
|
|
11
11
|
import { homedir } from 'node:os';
|
|
12
12
|
import { LLMError } from './types.js';
|
|
13
13
|
import { CircuitBreaker, retry, parseRateLimitHeaders } from './utils.js';
|
|
14
|
-
import type {
|
|
15
|
-
LLMCallOptions,
|
|
16
|
-
LLMCallResult,
|
|
17
|
-
RouteEntry,
|
|
18
|
-
RoutingConfig,
|
|
19
|
-
} from './types.js';
|
|
14
|
+
import type { LLMCallOptions, LLMCallResult, RouteEntry, RoutingConfig } from './types.js';
|
|
20
15
|
import type { KeyPool } from './key-pool.js';
|
|
21
16
|
|
|
22
17
|
// =============================================================================
|
|
@@ -69,7 +64,11 @@ class ModelRouter {
|
|
|
69
64
|
private config: RoutingConfig;
|
|
70
65
|
|
|
71
66
|
constructor(config?: RoutingConfig) {
|
|
72
|
-
this.config = config ?? {
|
|
67
|
+
this.config = config ?? {
|
|
68
|
+
routes: [],
|
|
69
|
+
defaultOpenAIModel: 'gpt-4o-mini',
|
|
70
|
+
defaultAnthropicModel: 'claude-sonnet-4-20250514',
|
|
71
|
+
};
|
|
73
72
|
}
|
|
74
73
|
|
|
75
74
|
resolve(
|
|
@@ -78,17 +77,13 @@ class ModelRouter {
|
|
|
78
77
|
originalModel?: string,
|
|
79
78
|
): { model: string; provider: 'openai' | 'anthropic' } {
|
|
80
79
|
if (task) {
|
|
81
|
-
const exactMatch = this.config.routes.find(
|
|
82
|
-
(r) => r.caller === caller && r.task === task,
|
|
83
|
-
);
|
|
80
|
+
const exactMatch = this.config.routes.find((r) => r.caller === caller && r.task === task);
|
|
84
81
|
if (exactMatch) {
|
|
85
82
|
return { model: exactMatch.model, provider: exactMatch.provider };
|
|
86
83
|
}
|
|
87
84
|
}
|
|
88
85
|
|
|
89
|
-
const callerMatch = this.config.routes.find(
|
|
90
|
-
(r) => r.caller === caller && !r.task,
|
|
91
|
-
);
|
|
86
|
+
const callerMatch = this.config.routes.find((r) => r.caller === caller && !r.task);
|
|
92
87
|
if (callerMatch) {
|
|
93
88
|
return { model: callerMatch.model, provider: callerMatch.provider };
|
|
94
89
|
}
|
|
@@ -148,11 +143,7 @@ export class LLMClient {
|
|
|
148
143
|
}
|
|
149
144
|
|
|
150
145
|
async complete(options: LLMCallOptions): Promise<LLMCallResult> {
|
|
151
|
-
const routed = this.router.resolve(
|
|
152
|
-
options.caller,
|
|
153
|
-
options.task,
|
|
154
|
-
options.model,
|
|
155
|
-
);
|
|
146
|
+
const routed = this.router.resolve(options.caller, options.task, options.model);
|
|
156
147
|
const resolvedOptions = { ...options, model: routed.model, provider: routed.provider };
|
|
157
148
|
|
|
158
149
|
return resolvedOptions.provider === 'anthropic'
|
|
@@ -219,10 +210,10 @@ export class LLMClient {
|
|
|
219
210
|
}
|
|
220
211
|
|
|
221
212
|
const errorBody = await response.text();
|
|
222
|
-
throw new LLMError(
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
);
|
|
213
|
+
throw new LLMError(`OpenAI API error: ${response.status} - ${errorBody}`, {
|
|
214
|
+
retryable: response.status === 429 || response.status >= 500,
|
|
215
|
+
statusCode: response.status,
|
|
216
|
+
});
|
|
226
217
|
}
|
|
227
218
|
|
|
228
219
|
const data = (await response.json()) as {
|
|
@@ -270,7 +261,8 @@ export class LLMClient {
|
|
|
270
261
|
|
|
271
262
|
const text = response.content
|
|
272
263
|
.filter(
|
|
273
|
-
(block): block is { type: 'text'; text: string } =>
|
|
264
|
+
(block): block is { type: 'text'; text: string } =>
|
|
265
|
+
block.type === 'text' && typeof block.text === 'string',
|
|
274
266
|
)
|
|
275
267
|
.map((block) => block.text)
|
|
276
268
|
.join('\n');
|
|
@@ -305,7 +297,9 @@ export class LLMClient {
|
|
|
305
297
|
try {
|
|
306
298
|
// Dynamic import — @anthropic-ai/sdk is an optional peer dep.
|
|
307
299
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
308
|
-
const mod = await (Function('return import("@anthropic-ai/sdk")')() as Promise<{
|
|
300
|
+
const mod = await (Function('return import("@anthropic-ai/sdk")')() as Promise<{
|
|
301
|
+
default: new (opts: { apiKey: string }) => AnthropicClient;
|
|
302
|
+
}>);
|
|
309
303
|
this.anthropicClient = new mod.default({ apiKey: currentKey });
|
|
310
304
|
return this.anthropicClient;
|
|
311
305
|
} catch {
|
package/src/runtime/core-ops.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Generic core operations factory —
|
|
2
|
+
* Generic core operations factory — 37 ops that every agent gets.
|
|
3
3
|
*
|
|
4
4
|
* These ops are agent-agnostic (no persona, no activation).
|
|
5
5
|
* The 5 agent-specific ops (health, identity, activate, inject_claude_md, setup)
|
|
@@ -12,19 +12,20 @@ import type { IntelligenceEntry } from '../intelligence/types.js';
|
|
|
12
12
|
import type { AgentRuntime } from './types.js';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
|
-
* Create the
|
|
15
|
+
* Create the 37 generic core operations for an agent runtime.
|
|
16
16
|
*
|
|
17
17
|
* Groups: search/vault (4), memory (4), export (1), planning (5),
|
|
18
|
-
* brain (4), curator (8).
|
|
18
|
+
* brain (4), brain intelligence (11), curator (8).
|
|
19
19
|
*/
|
|
20
20
|
export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
21
|
-
const { vault, brain, planner, curator, llmClient, keyPool } = runtime;
|
|
21
|
+
const { vault, brain, brainIntelligence, planner, curator, llmClient, keyPool } = runtime;
|
|
22
22
|
|
|
23
23
|
return [
|
|
24
24
|
// ─── Search / Vault ──────────────────────────────────────────
|
|
25
25
|
{
|
|
26
26
|
name: 'search',
|
|
27
|
-
description:
|
|
27
|
+
description:
|
|
28
|
+
'Search across all knowledge domains. Results ranked by TF-IDF + severity + recency + tag overlap + domain match.',
|
|
28
29
|
auth: 'read',
|
|
29
30
|
schema: z.object({
|
|
30
31
|
query: z.string(),
|
|
@@ -75,7 +76,8 @@ export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
75
76
|
},
|
|
76
77
|
{
|
|
77
78
|
name: 'register',
|
|
78
|
-
description:
|
|
79
|
+
description:
|
|
80
|
+
'Register a project for this session. Call on every new session to track usage and get context.',
|
|
79
81
|
auth: 'write',
|
|
80
82
|
schema: z.object({
|
|
81
83
|
projectPath: z.string().optional().default('.'),
|
|
@@ -167,7 +169,8 @@ export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
167
169
|
},
|
|
168
170
|
{
|
|
169
171
|
name: 'session_capture',
|
|
170
|
-
description:
|
|
172
|
+
description:
|
|
173
|
+
'Capture a session summary before context compaction. Called automatically by PreCompact hook.',
|
|
171
174
|
auth: 'write',
|
|
172
175
|
schema: z.object({
|
|
173
176
|
projectPath: z.string().optional().default('.'),
|
|
@@ -195,17 +198,17 @@ export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
195
198
|
// ─── Export ───────────────────────────────────────────────────
|
|
196
199
|
{
|
|
197
200
|
name: 'export',
|
|
198
|
-
description:
|
|
201
|
+
description:
|
|
202
|
+
'Export vault entries as JSON intelligence bundles — one per domain. Enables version control and sharing.',
|
|
199
203
|
auth: 'read',
|
|
200
204
|
schema: z.object({
|
|
201
205
|
domain: z.string().optional().describe('Export only this domain. Omit to export all.'),
|
|
202
206
|
}),
|
|
203
207
|
handler: async (params) => {
|
|
204
208
|
const stats = vault.stats();
|
|
205
|
-
const domains = params.domain
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
const bundles: Array<{ domain: string; version: string; entries: IntelligenceEntry[] }> = [];
|
|
209
|
+
const domains = params.domain ? [params.domain as string] : Object.keys(stats.byDomain);
|
|
210
|
+
const bundles: Array<{ domain: string; version: string; entries: IntelligenceEntry[] }> =
|
|
211
|
+
[];
|
|
209
212
|
for (const d of domains) {
|
|
210
213
|
const entries = vault.list({ domain: d, limit: 10000 });
|
|
211
214
|
bundles.push({ domain: d, version: '1.0.0', entries });
|
|
@@ -222,13 +225,17 @@ export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
222
225
|
// ─── Planning ────────────────────────────────────────────────
|
|
223
226
|
{
|
|
224
227
|
name: 'create_plan',
|
|
225
|
-
description:
|
|
228
|
+
description:
|
|
229
|
+
'Create a new plan in draft status. Plans track multi-step tasks with decisions and sub-tasks.',
|
|
226
230
|
auth: 'write',
|
|
227
231
|
schema: z.object({
|
|
228
232
|
objective: z.string().describe('What the plan aims to achieve'),
|
|
229
233
|
scope: z.string().describe('Which parts of the codebase are affected'),
|
|
230
234
|
decisions: z.array(z.string()).optional().default([]),
|
|
231
|
-
tasks: z
|
|
235
|
+
tasks: z
|
|
236
|
+
.array(z.object({ title: z.string(), description: z.string() }))
|
|
237
|
+
.optional()
|
|
238
|
+
.default([]),
|
|
232
239
|
}),
|
|
233
240
|
handler: async (params) => {
|
|
234
241
|
const plan = planner.create({
|
|
@@ -262,7 +269,11 @@ export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
262
269
|
auth: 'write',
|
|
263
270
|
schema: z.object({
|
|
264
271
|
planId: z.string(),
|
|
265
|
-
startExecution: z
|
|
272
|
+
startExecution: z
|
|
273
|
+
.boolean()
|
|
274
|
+
.optional()
|
|
275
|
+
.default(false)
|
|
276
|
+
.describe('If true, immediately start execution after approval'),
|
|
266
277
|
}),
|
|
267
278
|
handler: async (params) => {
|
|
268
279
|
let plan = planner.approve(params.planId as string);
|
|
@@ -313,7 +324,8 @@ export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
313
324
|
// ─── Brain ───────────────────────────────────────────────────
|
|
314
325
|
{
|
|
315
326
|
name: 'record_feedback',
|
|
316
|
-
description:
|
|
327
|
+
description:
|
|
328
|
+
'Record feedback on a search result — accepted or dismissed. Used for adaptive weight tuning.',
|
|
317
329
|
auth: 'write',
|
|
318
330
|
schema: z.object({
|
|
319
331
|
query: z.string().describe('The original search query'),
|
|
@@ -326,7 +338,12 @@ export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
326
338
|
params.entryId as string,
|
|
327
339
|
params.action as 'accepted' | 'dismissed',
|
|
328
340
|
);
|
|
329
|
-
return {
|
|
341
|
+
return {
|
|
342
|
+
recorded: true,
|
|
343
|
+
query: params.query,
|
|
344
|
+
entryId: params.entryId,
|
|
345
|
+
action: params.action,
|
|
346
|
+
};
|
|
330
347
|
},
|
|
331
348
|
},
|
|
332
349
|
{
|
|
@@ -340,15 +357,19 @@ export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
340
357
|
},
|
|
341
358
|
{
|
|
342
359
|
name: 'brain_stats',
|
|
343
|
-
description:
|
|
360
|
+
description:
|
|
361
|
+
'Get brain intelligence stats — vocabulary size, feedback count, current scoring weights, intelligence pipeline stats.',
|
|
344
362
|
auth: 'read',
|
|
345
363
|
handler: async () => {
|
|
346
|
-
|
|
364
|
+
const base = brain.getStats();
|
|
365
|
+
const intelligence = brainIntelligence.getStats();
|
|
366
|
+
return { ...base, intelligence };
|
|
347
367
|
},
|
|
348
368
|
},
|
|
349
369
|
{
|
|
350
370
|
name: 'llm_status',
|
|
351
|
-
description:
|
|
371
|
+
description:
|
|
372
|
+
'LLM client status — provider availability, key pool status, model routing config.',
|
|
352
373
|
auth: 'read',
|
|
353
374
|
handler: async () => {
|
|
354
375
|
const available = llmClient.isAvailable();
|
|
@@ -368,6 +389,165 @@ export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
368
389
|
},
|
|
369
390
|
},
|
|
370
391
|
|
|
392
|
+
// ─── Brain Intelligence ───────────────────────────────────────
|
|
393
|
+
{
|
|
394
|
+
name: 'brain_session_context',
|
|
395
|
+
description:
|
|
396
|
+
'Get recent session context — sessions, tool usage frequency, file change frequency.',
|
|
397
|
+
auth: 'read',
|
|
398
|
+
schema: z.object({
|
|
399
|
+
limit: z.number().optional().describe('Number of recent sessions. Default 10.'),
|
|
400
|
+
}),
|
|
401
|
+
handler: async (params) => {
|
|
402
|
+
return brainIntelligence.getSessionContext((params.limit as number) ?? 10);
|
|
403
|
+
},
|
|
404
|
+
},
|
|
405
|
+
{
|
|
406
|
+
name: 'brain_strengths',
|
|
407
|
+
description:
|
|
408
|
+
'Get pattern strength scores. 4-signal scoring: usage (0-25) + spread (0-25) + success (0-25) + recency (0-25).',
|
|
409
|
+
auth: 'read',
|
|
410
|
+
schema: z.object({
|
|
411
|
+
domain: z.string().optional(),
|
|
412
|
+
minStrength: z.number().optional().describe('Minimum strength score (0-100).'),
|
|
413
|
+
limit: z.number().optional(),
|
|
414
|
+
}),
|
|
415
|
+
handler: async (params) => {
|
|
416
|
+
return brainIntelligence.getStrengths({
|
|
417
|
+
domain: params.domain as string | undefined,
|
|
418
|
+
minStrength: params.minStrength as number | undefined,
|
|
419
|
+
limit: (params.limit as number) ?? 50,
|
|
420
|
+
});
|
|
421
|
+
},
|
|
422
|
+
},
|
|
423
|
+
{
|
|
424
|
+
name: 'brain_global_patterns',
|
|
425
|
+
description:
|
|
426
|
+
'Get cross-domain pattern registry — patterns that appear across multiple domains.',
|
|
427
|
+
auth: 'read',
|
|
428
|
+
schema: z.object({
|
|
429
|
+
limit: z.number().optional(),
|
|
430
|
+
}),
|
|
431
|
+
handler: async (params) => {
|
|
432
|
+
return brainIntelligence.getGlobalPatterns((params.limit as number) ?? 20);
|
|
433
|
+
},
|
|
434
|
+
},
|
|
435
|
+
{
|
|
436
|
+
name: 'brain_recommend',
|
|
437
|
+
description:
|
|
438
|
+
'Get pattern recommendations for a task context. Matches domain and task terms against known strengths.',
|
|
439
|
+
auth: 'read',
|
|
440
|
+
schema: z.object({
|
|
441
|
+
domain: z.string().optional(),
|
|
442
|
+
task: z.string().optional().describe('Task description for contextual matching.'),
|
|
443
|
+
limit: z.number().optional(),
|
|
444
|
+
}),
|
|
445
|
+
handler: async (params) => {
|
|
446
|
+
return brainIntelligence.recommend({
|
|
447
|
+
domain: params.domain as string | undefined,
|
|
448
|
+
task: params.task as string | undefined,
|
|
449
|
+
limit: (params.limit as number) ?? 5,
|
|
450
|
+
});
|
|
451
|
+
},
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
name: 'brain_build_intelligence',
|
|
455
|
+
description:
|
|
456
|
+
'Run the full intelligence pipeline: compute strengths → build global registry → build domain profiles.',
|
|
457
|
+
auth: 'write',
|
|
458
|
+
handler: async () => {
|
|
459
|
+
return brainIntelligence.buildIntelligence();
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
{
|
|
463
|
+
name: 'brain_export',
|
|
464
|
+
description:
|
|
465
|
+
'Export all brain intelligence data — strengths, sessions, proposals, global patterns, domain profiles.',
|
|
466
|
+
auth: 'read',
|
|
467
|
+
handler: async () => {
|
|
468
|
+
return brainIntelligence.exportData();
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
{
|
|
472
|
+
name: 'brain_import',
|
|
473
|
+
description: 'Import brain intelligence data from a previous export.',
|
|
474
|
+
auth: 'write',
|
|
475
|
+
schema: z.object({
|
|
476
|
+
data: z.any().describe('BrainExportData object from brain_export.'),
|
|
477
|
+
}),
|
|
478
|
+
handler: async (params) => {
|
|
479
|
+
return brainIntelligence.importData(
|
|
480
|
+
params.data as import('../brain/types.js').BrainExportData,
|
|
481
|
+
);
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
{
|
|
485
|
+
name: 'brain_extract_knowledge',
|
|
486
|
+
description:
|
|
487
|
+
'Extract knowledge proposals from a session using 6 heuristic rules (repeated tools, multi-file edits, long sessions, plan outcomes, feedback ratios).',
|
|
488
|
+
auth: 'write',
|
|
489
|
+
schema: z.object({
|
|
490
|
+
sessionId: z.string().describe('Session ID to extract knowledge from.'),
|
|
491
|
+
}),
|
|
492
|
+
handler: async (params) => {
|
|
493
|
+
return brainIntelligence.extractKnowledge(params.sessionId as string);
|
|
494
|
+
},
|
|
495
|
+
},
|
|
496
|
+
{
|
|
497
|
+
name: 'brain_archive_sessions',
|
|
498
|
+
description: 'Archive (delete) completed sessions older than N days.',
|
|
499
|
+
auth: 'write',
|
|
500
|
+
schema: z.object({
|
|
501
|
+
olderThanDays: z.number().optional().describe('Days threshold. Default 30.'),
|
|
502
|
+
}),
|
|
503
|
+
handler: async (params) => {
|
|
504
|
+
return brainIntelligence.archiveSessions((params.olderThanDays as number) ?? 30);
|
|
505
|
+
},
|
|
506
|
+
},
|
|
507
|
+
{
|
|
508
|
+
name: 'brain_promote_proposals',
|
|
509
|
+
description:
|
|
510
|
+
'Promote knowledge proposals to vault entries. Creates intelligence entries from auto-extracted patterns.',
|
|
511
|
+
auth: 'write',
|
|
512
|
+
schema: z.object({
|
|
513
|
+
proposalIds: z.array(z.string()).describe('IDs of proposals to promote.'),
|
|
514
|
+
}),
|
|
515
|
+
handler: async (params) => {
|
|
516
|
+
return brainIntelligence.promoteProposals(params.proposalIds as string[]);
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
{
|
|
520
|
+
name: 'brain_lifecycle',
|
|
521
|
+
description:
|
|
522
|
+
'Start or end a brain session. Sessions track tool usage, file changes, and plan context.',
|
|
523
|
+
auth: 'write',
|
|
524
|
+
schema: z.object({
|
|
525
|
+
action: z.enum(['start', 'end']),
|
|
526
|
+
sessionId: z
|
|
527
|
+
.string()
|
|
528
|
+
.optional()
|
|
529
|
+
.describe('Required for end. Auto-generated for start if omitted.'),
|
|
530
|
+
domain: z.string().optional(),
|
|
531
|
+
context: z.string().optional(),
|
|
532
|
+
toolsUsed: z.array(z.string()).optional(),
|
|
533
|
+
filesModified: z.array(z.string()).optional(),
|
|
534
|
+
planId: z.string().optional(),
|
|
535
|
+
planOutcome: z.string().optional(),
|
|
536
|
+
}),
|
|
537
|
+
handler: async (params) => {
|
|
538
|
+
return brainIntelligence.lifecycle({
|
|
539
|
+
action: params.action as 'start' | 'end',
|
|
540
|
+
sessionId: params.sessionId as string | undefined,
|
|
541
|
+
domain: params.domain as string | undefined,
|
|
542
|
+
context: params.context as string | undefined,
|
|
543
|
+
toolsUsed: params.toolsUsed as string[] | undefined,
|
|
544
|
+
filesModified: params.filesModified as string[] | undefined,
|
|
545
|
+
planId: params.planId as string | undefined,
|
|
546
|
+
planOutcome: params.planOutcome as string | undefined,
|
|
547
|
+
});
|
|
548
|
+
},
|
|
549
|
+
},
|
|
550
|
+
|
|
371
551
|
// ─── Curator ─────────────────────────────────────────────────
|
|
372
552
|
{
|
|
373
553
|
name: 'curator_status',
|
|
@@ -404,7 +584,9 @@ export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
404
584
|
if (params.detect) {
|
|
405
585
|
curator.detectContradictions();
|
|
406
586
|
}
|
|
407
|
-
return curator.getContradictions(
|
|
587
|
+
return curator.getContradictions(
|
|
588
|
+
params.status as 'open' | 'resolved' | 'dismissed' | undefined,
|
|
589
|
+
);
|
|
408
590
|
},
|
|
409
591
|
},
|
|
410
592
|
{
|
|
@@ -443,13 +625,23 @@ export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
443
625
|
},
|
|
444
626
|
{
|
|
445
627
|
name: 'curator_consolidate',
|
|
446
|
-
description:
|
|
628
|
+
description:
|
|
629
|
+
'Consolidate vault — find duplicates, stale entries, contradictions. Dry-run by default.',
|
|
447
630
|
auth: 'write',
|
|
448
631
|
schema: z.object({
|
|
449
632
|
dryRun: z.boolean().optional().describe('Default true. Set false to apply mutations.'),
|
|
450
|
-
staleDaysThreshold: z
|
|
451
|
-
|
|
452
|
-
|
|
633
|
+
staleDaysThreshold: z
|
|
634
|
+
.number()
|
|
635
|
+
.optional()
|
|
636
|
+
.describe('Days before entry is stale. Default 90.'),
|
|
637
|
+
duplicateThreshold: z
|
|
638
|
+
.number()
|
|
639
|
+
.optional()
|
|
640
|
+
.describe('Cosine similarity threshold. Default 0.45.'),
|
|
641
|
+
contradictionThreshold: z
|
|
642
|
+
.number()
|
|
643
|
+
.optional()
|
|
644
|
+
.describe('Contradiction threshold. Default 0.4.'),
|
|
453
645
|
}),
|
|
454
646
|
handler: async (params) => {
|
|
455
647
|
return curator.consolidate({
|
|
@@ -462,7 +654,8 @@ export function createCoreOps(runtime: AgentRuntime): OpDefinition[] {
|
|
|
462
654
|
},
|
|
463
655
|
{
|
|
464
656
|
name: 'curator_health_audit',
|
|
465
|
-
description:
|
|
657
|
+
description:
|
|
658
|
+
'Audit vault health — score (0-100), coverage, freshness, quality, tag health, recommendations.',
|
|
466
659
|
auth: 'read',
|
|
467
660
|
handler: async () => {
|
|
468
661
|
return curator.healthAudit();
|
package/src/runtime/runtime.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { join } from 'node:path';
|
|
|
11
11
|
import { homedir } from 'node:os';
|
|
12
12
|
import { Vault } from '../vault/vault.js';
|
|
13
13
|
import { Brain } from '../brain/brain.js';
|
|
14
|
+
import { BrainIntelligence } from '../brain/intelligence.js';
|
|
14
15
|
import { Planner } from '../planning/planner.js';
|
|
15
16
|
import { Curator } from '../curator/curator.js';
|
|
16
17
|
import { KeyPool, loadKeyPoolConfig } from '../llm/key-pool.js';
|
|
@@ -49,6 +50,9 @@ export function createAgentRuntime(config: AgentRuntimeConfig): AgentRuntime {
|
|
|
49
50
|
// Brain — intelligence layer (TF-IDF scoring, auto-tagging, dedup)
|
|
50
51
|
const brain = new Brain(vault);
|
|
51
52
|
|
|
53
|
+
// Brain Intelligence — pattern strengths, session knowledge, intelligence pipeline
|
|
54
|
+
const brainIntelligence = new BrainIntelligence(vault, brain);
|
|
55
|
+
|
|
52
56
|
// Curator — vault self-maintenance (dedup, contradictions, grooming, health)
|
|
53
57
|
const curator = new Curator(vault);
|
|
54
58
|
|
|
@@ -62,6 +66,7 @@ export function createAgentRuntime(config: AgentRuntimeConfig): AgentRuntime {
|
|
|
62
66
|
config,
|
|
63
67
|
vault,
|
|
64
68
|
brain,
|
|
69
|
+
brainIntelligence,
|
|
65
70
|
planner,
|
|
66
71
|
curator,
|
|
67
72
|
keyPool: { openai: openaiKeyPool, anthropic: anthropicKeyPool },
|
package/src/runtime/types.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { Vault } from '../vault/vault.js';
|
|
2
2
|
import type { Brain } from '../brain/brain.js';
|
|
3
|
+
import type { BrainIntelligence } from '../brain/intelligence.js';
|
|
3
4
|
import type { Planner } from '../planning/planner.js';
|
|
4
5
|
import type { Curator } from '../curator/curator.js';
|
|
5
6
|
import type { KeyPool } from '../llm/key-pool.js';
|
|
@@ -28,6 +29,7 @@ export interface AgentRuntime {
|
|
|
28
29
|
config: AgentRuntimeConfig;
|
|
29
30
|
vault: Vault;
|
|
30
31
|
brain: Brain;
|
|
32
|
+
brainIntelligence: BrainIntelligence;
|
|
31
33
|
planner: Planner;
|
|
32
34
|
curator: Curator;
|
|
33
35
|
keyPool: { openai: KeyPool; anthropic: KeyPool };
|