@soleri/core 8.0.0 → 8.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 (134) hide show
  1. package/dist/brain/knowledge-synthesizer.d.ts.map +1 -1
  2. package/dist/brain/knowledge-synthesizer.js +0 -2
  3. package/dist/brain/knowledge-synthesizer.js.map +1 -1
  4. package/dist/curator/classifier.d.ts.map +1 -1
  5. package/dist/curator/classifier.js +0 -2
  6. package/dist/curator/classifier.js.map +1 -1
  7. package/dist/curator/quality-gate.d.ts.map +1 -1
  8. package/dist/curator/quality-gate.js +0 -2
  9. package/dist/curator/quality-gate.js.map +1 -1
  10. package/dist/domain-packs/index.d.ts +0 -3
  11. package/dist/domain-packs/index.d.ts.map +1 -1
  12. package/dist/domain-packs/index.js +0 -3
  13. package/dist/domain-packs/index.js.map +1 -1
  14. package/dist/domain-packs/loader.d.ts.map +1 -1
  15. package/dist/domain-packs/loader.js +20 -4
  16. package/dist/domain-packs/loader.js.map +1 -1
  17. package/dist/domain-packs/pack-runtime.d.ts +5 -5
  18. package/dist/domain-packs/pack-runtime.d.ts.map +1 -1
  19. package/dist/domain-packs/pack-runtime.js +2 -2
  20. package/dist/domain-packs/pack-runtime.js.map +1 -1
  21. package/dist/domain-packs/types.d.ts +8 -2
  22. package/dist/domain-packs/types.d.ts.map +1 -1
  23. package/dist/domain-packs/types.js.map +1 -1
  24. package/dist/engine/bin/soleri-engine.js +12 -2
  25. package/dist/engine/bin/soleri-engine.js.map +1 -1
  26. package/dist/engine/index.d.ts +2 -0
  27. package/dist/engine/index.d.ts.map +1 -1
  28. package/dist/engine/index.js +1 -0
  29. package/dist/engine/index.js.map +1 -1
  30. package/dist/engine/module-manifest.d.ts +28 -0
  31. package/dist/engine/module-manifest.d.ts.map +1 -0
  32. package/dist/engine/module-manifest.js +85 -0
  33. package/dist/engine/module-manifest.js.map +1 -0
  34. package/dist/engine/register-engine.d.ts +19 -0
  35. package/dist/engine/register-engine.d.ts.map +1 -1
  36. package/dist/engine/register-engine.js +15 -2
  37. package/dist/engine/register-engine.js.map +1 -1
  38. package/dist/index.d.ts +0 -1
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +0 -1
  41. package/dist/index.js.map +1 -1
  42. package/dist/intake/content-classifier.d.ts.map +1 -1
  43. package/dist/intake/content-classifier.js +0 -2
  44. package/dist/intake/content-classifier.js.map +1 -1
  45. package/dist/llm/llm-client.d.ts.map +1 -1
  46. package/dist/llm/llm-client.js +8 -4
  47. package/dist/llm/llm-client.js.map +1 -1
  48. package/dist/llm/oauth-discovery.d.ts +0 -8
  49. package/dist/llm/oauth-discovery.d.ts.map +1 -1
  50. package/dist/llm/oauth-discovery.js +0 -19
  51. package/dist/llm/oauth-discovery.js.map +1 -1
  52. package/dist/llm/types.d.ts +4 -2
  53. package/dist/llm/types.d.ts.map +1 -1
  54. package/dist/packs/pack-installer.d.ts +2 -1
  55. package/dist/packs/pack-installer.d.ts.map +1 -1
  56. package/dist/packs/pack-installer.js +10 -1
  57. package/dist/packs/pack-installer.js.map +1 -1
  58. package/dist/persistence/index.d.ts +0 -1
  59. package/dist/persistence/index.d.ts.map +1 -1
  60. package/dist/persistence/index.js +0 -1
  61. package/dist/persistence/index.js.map +1 -1
  62. package/dist/persistence/types.d.ts +2 -6
  63. package/dist/persistence/types.d.ts.map +1 -1
  64. package/dist/plugins/index.d.ts +4 -0
  65. package/dist/plugins/index.d.ts.map +1 -1
  66. package/dist/plugins/index.js +4 -0
  67. package/dist/plugins/index.js.map +1 -1
  68. package/dist/plugins/plugin-registry.d.ts +4 -0
  69. package/dist/plugins/plugin-registry.d.ts.map +1 -1
  70. package/dist/plugins/plugin-registry.js +4 -0
  71. package/dist/plugins/plugin-registry.js.map +1 -1
  72. package/dist/plugins/types.d.ts +32 -27
  73. package/dist/plugins/types.d.ts.map +1 -1
  74. package/dist/plugins/types.js +6 -3
  75. package/dist/plugins/types.js.map +1 -1
  76. package/dist/runtime/claude-md-helpers.d.ts +0 -9
  77. package/dist/runtime/claude-md-helpers.d.ts.map +1 -1
  78. package/dist/runtime/claude-md-helpers.js +1 -14
  79. package/dist/runtime/claude-md-helpers.js.map +1 -1
  80. package/dist/runtime/facades/admin-facade.d.ts.map +1 -1
  81. package/dist/runtime/facades/admin-facade.js +1 -2
  82. package/dist/runtime/facades/admin-facade.js.map +1 -1
  83. package/dist/runtime/pack-ops.d.ts +3 -0
  84. package/dist/runtime/pack-ops.d.ts.map +1 -1
  85. package/dist/runtime/pack-ops.js +18 -1
  86. package/dist/runtime/pack-ops.js.map +1 -1
  87. package/dist/runtime/plugin-ops.d.ts.map +1 -1
  88. package/dist/runtime/plugin-ops.js +3 -0
  89. package/dist/runtime/plugin-ops.js.map +1 -1
  90. package/dist/runtime/session-briefing.d.ts.map +1 -1
  91. package/dist/runtime/session-briefing.js +14 -0
  92. package/dist/runtime/session-briefing.js.map +1 -1
  93. package/dist/runtime/vault-linking-ops.d.ts.map +1 -1
  94. package/dist/runtime/vault-linking-ops.js +2 -4
  95. package/dist/runtime/vault-linking-ops.js.map +1 -1
  96. package/dist/vault/vault.d.ts +9 -0
  97. package/dist/vault/vault.d.ts.map +1 -1
  98. package/dist/vault/vault.js +22 -0
  99. package/dist/vault/vault.js.map +1 -1
  100. package/package.json +6 -4
  101. package/src/__tests__/curator-pipeline-e2e.test.ts +187 -0
  102. package/src/__tests__/module-manifest-drift.test.ts +59 -0
  103. package/src/brain/knowledge-synthesizer.ts +0 -2
  104. package/src/curator/classifier.ts +0 -2
  105. package/src/curator/quality-gate.ts +0 -2
  106. package/src/domain-packs/index.ts +0 -6
  107. package/src/domain-packs/loader.ts +25 -5
  108. package/src/domain-packs/pack-runtime.ts +6 -6
  109. package/src/domain-packs/types.ts +8 -2
  110. package/src/engine/bin/soleri-engine.ts +17 -2
  111. package/src/engine/index.ts +2 -0
  112. package/src/engine/module-manifest.ts +99 -0
  113. package/src/engine/register-engine.ts +21 -2
  114. package/src/index.ts +0 -1
  115. package/src/intake/content-classifier.ts +0 -2
  116. package/src/llm/llm-client.ts +12 -6
  117. package/src/llm/oauth-discovery.ts +0 -18
  118. package/src/llm/types.ts +4 -2
  119. package/src/packs/pack-installer.ts +16 -1
  120. package/src/persistence/index.ts +0 -1
  121. package/src/persistence/types.ts +2 -6
  122. package/src/plugins/index.ts +4 -0
  123. package/src/plugins/plugin-registry.ts +6 -1
  124. package/src/plugins/types.ts +10 -5
  125. package/src/runtime/claude-md-helpers.ts +1 -19
  126. package/src/runtime/facades/admin-facade.ts +1 -2
  127. package/src/runtime/pack-ops.ts +26 -1
  128. package/src/runtime/plugin-ops.ts +3 -0
  129. package/src/runtime/session-briefing.ts +14 -0
  130. package/src/runtime/vault-linking-ops.ts +2 -4
  131. package/src/vault/vault.ts +26 -0
  132. package/src/__tests__/postgres-provider.test.ts +0 -116
  133. package/src/health/doctor-checks.ts +0 -115
  134. package/src/persistence/postgres-provider.ts +0 -310
@@ -14,7 +14,7 @@ import type { Vault } from '../vault/vault.js';
14
14
  */
15
15
  export interface PackProjectContext {
16
16
  id: string;
17
- name: string;
17
+ name?: string;
18
18
  path: string;
19
19
  colors?: {
20
20
  [scale: string]: {
@@ -49,7 +49,7 @@ export interface PackRuntime {
49
49
  getProject(projectId: string): PackProjectContext | undefined;
50
50
 
51
51
  /** List all registered projects */
52
- listProjects(): Array<{ id: string; name: string; path: string }>;
52
+ listProjects(): Array<{ id: string; name?: string; path: string }>;
53
53
 
54
54
  /** Create a session check (for tool chaining) */
55
55
  createCheck(type: string, data: Record<string, unknown>): string;
@@ -70,8 +70,8 @@ export interface PackRuntime {
70
70
  export function createPackRuntime(runtime: {
71
71
  vault: Vault;
72
72
  projectRegistry: {
73
- getProject(id: string): PackProjectContext | undefined;
74
- listProjects(): Array<{ id: string; name: string; path: string }>;
73
+ get(id: string): PackProjectContext | null;
74
+ list(): Array<{ id: string; name?: string; path: string }>;
75
75
  };
76
76
  sessionStore?: {
77
77
  createCheck(type: string, data: Record<string, unknown>): string;
@@ -81,8 +81,8 @@ export function createPackRuntime(runtime: {
81
81
  }): PackRuntime {
82
82
  return {
83
83
  vault: runtime.vault,
84
- getProject: (id) => runtime.projectRegistry.getProject(id),
85
- listProjects: () => runtime.projectRegistry.listProjects(),
84
+ getProject: (id) => runtime.projectRegistry.get(id) ?? undefined,
85
+ listProjects: () => runtime.projectRegistry.list(),
86
86
  createCheck: (type, data) => {
87
87
  if (!runtime.sessionStore) throw new Error('Session store not available');
88
88
  return runtime.sessionStore.createCheck(type, data);
@@ -93,8 +93,14 @@ export interface DomainPack {
93
93
  requires?: string[];
94
94
  /** Called after pack is installed (one-time setup). */
95
95
  onInstall?: (runtime: AgentRuntime) => Promise<void>;
96
- /** Called each time the agent starts (runtime initialization). */
97
- onActivate?: (runtime: AgentRuntime) => Promise<void>;
96
+ /**
97
+ * Called each time the agent starts (runtime initialization).
98
+ *
99
+ * Receives `PackRuntime` (narrowed interface with vault, projects, session checks).
100
+ * The full `AgentRuntime` is passed as second argument for backwards compatibility
101
+ * but is deprecated — packs should only use `PackRuntime`.
102
+ */
103
+ onActivate?: (packRuntime: PackRuntime, runtime?: AgentRuntime) => Promise<void>;
98
104
  }
99
105
 
100
106
  // ---------------------------------------------------------------------------
@@ -117,6 +117,14 @@ async function main(): Promise<void> {
117
117
  // 6. Seed default playbooks
118
118
  seedDefaultPlaybooks(runtime.vault);
119
119
 
120
+ // Log vault stats for first-run visibility
121
+ const vaultStats = runtime.vault.stats();
122
+ console.error(
123
+ `${tag} Vault: ${vaultStats.totalEntries} entries (${Object.entries(vaultStats.byType ?? {})
124
+ .map(([t, n]) => `${n} ${t}`)
125
+ .join(', ')})`,
126
+ );
127
+
120
128
  // 7. Load domain packs
121
129
  const packs = (config.packs ?? []) as Array<{ name: string; package: string; version?: string }>;
122
130
  const loadedPacks: Array<{ name: string; facades?: Array<{ name: string; ops: unknown[] }> }> =
@@ -129,9 +137,12 @@ async function main(): Promise<void> {
129
137
  const manifests = await loadDomainPacksFromConfig(refs);
130
138
 
131
139
  // Packs activate sequentially — order may matter for dependencies
140
+ const { createPackRuntime } = await import('../../domain-packs/pack-runtime.js');
141
+ const narrowedRuntime = createPackRuntime(runtime);
142
+
132
143
  for (const manifest of manifests) {
133
144
  if (manifest.onActivate) {
134
- await manifest.onActivate(runtime); // eslint-disable-line no-await-in-loop
145
+ await manifest.onActivate(narrowedRuntime, runtime); // eslint-disable-line no-await-in-loop
135
146
  }
136
147
  loadedPacks.push(manifest);
137
148
  console.error(`${tag} Domain pack: ${manifest.name}`);
@@ -178,7 +189,7 @@ async function main(): Promise<void> {
178
189
  }));
179
190
 
180
191
  // 12. Register all engine tools
181
- const { tools, totalOps } = registerEngine(server, runtime, {
192
+ const { tools, totalOps, registerTool } = registerEngine(server, runtime, {
182
193
  agentId,
183
194
  coreOps,
184
195
  domains: identity.domains,
@@ -195,6 +206,10 @@ async function main(): Promise<void> {
195
206
 
196
207
  console.error(`${tag} Registered ${tools.length} tools (${totalOps} ops)`);
197
208
 
209
+ // Enable hot reload for post-boot pack/plugin installation
210
+ const { setHotRegister } = await import('../../runtime/pack-ops.js');
211
+ setHotRegister(registerTool);
212
+
198
213
  // 13. Connect stdio transport
199
214
  const transport = new StdioServerTransport();
200
215
  await server.connect(transport);
@@ -17,3 +17,5 @@ export { registerEngine } from './register-engine.js';
17
17
  export type { EngineRegistrationOptions, EngineRegistrationResult } from './register-engine.js';
18
18
  export { createCoreOps } from './core-ops.js';
19
19
  export type { AgentIdentityConfig } from './core-ops.js';
20
+ export { ENGINE_MODULE_MANIFEST, CORE_KEY_OPS } from './module-manifest.js';
21
+ export type { ModuleManifestEntry } from './module-manifest.js';
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Soleri Engine Module Manifest
3
+ *
4
+ * Single source of truth for engine module names, descriptions, and key ops.
5
+ * Used by register-engine.ts at runtime and by @soleri/forge for template generation.
6
+ *
7
+ * This file is intentionally dependency-free so it can be imported by any package.
8
+ */
9
+
10
+ export interface ModuleManifestEntry {
11
+ /** Suffix for tool name: {agentId}_{suffix} */
12
+ suffix: string;
13
+ /** Human-readable description */
14
+ description: string;
15
+ /** Representative ops shown in placeholder tables (max 4) */
16
+ keyOps: string[];
17
+ /** If true, module requires a runtime condition to register */
18
+ conditional?: boolean;
19
+ }
20
+
21
+ /**
22
+ * Canonical list of engine modules.
23
+ * Order here determines order in generated tool tables.
24
+ */
25
+ export const ENGINE_MODULE_MANIFEST: ModuleManifestEntry[] = [
26
+ {
27
+ suffix: 'vault',
28
+ description: 'Knowledge management — search, CRUD, import/export, intake, archival.',
29
+ keyOps: ['search_intelligent', 'capture_knowledge', 'capture_quick'],
30
+ },
31
+ {
32
+ suffix: 'plan',
33
+ description: 'Plan lifecycle — create, approve, execute, reconcile, complete, grading.',
34
+ keyOps: ['create_plan', 'approve_plan', 'plan_split', 'plan_reconcile'],
35
+ },
36
+ {
37
+ suffix: 'brain',
38
+ description: 'Learning system — intelligence pipeline, strengths, feedback, sessions.',
39
+ keyOps: ['recommend', 'strengths', 'feedback'],
40
+ },
41
+ {
42
+ suffix: 'memory',
43
+ description: 'Session & cross-project memory — capture, search, dedup, promote.',
44
+ keyOps: ['memory_search', 'memory_capture', 'session_capture'],
45
+ },
46
+ {
47
+ suffix: 'admin',
48
+ description: 'Infrastructure — health, config, telemetry, tokens, LLM, prompts.',
49
+ keyOps: ['admin_health', 'admin_tool_list', 'admin_diagnostic'],
50
+ },
51
+ {
52
+ suffix: 'curator',
53
+ description: 'Quality — duplicate detection, contradictions, grooming, health audit.',
54
+ keyOps: ['curator_groom', 'curator_status', 'curator_health'],
55
+ },
56
+ {
57
+ suffix: 'loop',
58
+ description: 'Iterative validation loops — start, iterate, cancel, complete, history.',
59
+ keyOps: ['loop_start', 'loop_status', 'loop_cancel'],
60
+ },
61
+ {
62
+ suffix: 'orchestrate',
63
+ description:
64
+ 'Execution orchestration — project registration, playbooks, plan/execute/complete.',
65
+ keyOps: ['orchestrate_plan', 'orchestrate_execute', 'orchestrate_complete'],
66
+ },
67
+ {
68
+ suffix: 'control',
69
+ description: 'Agent behavior — identity, intent routing, morphing, guidelines, governance.',
70
+ keyOps: ['route_intent', 'morph', 'get_behavior_rules'],
71
+ },
72
+ {
73
+ suffix: 'context',
74
+ description: 'Context analysis — entity extraction, knowledge retrieval, confidence scoring.',
75
+ keyOps: ['context_extract_entities', 'context_retrieve_knowledge', 'context_analyze'],
76
+ },
77
+ {
78
+ suffix: 'agency',
79
+ description: 'Proactive intelligence — file watching, pattern surfacing, warnings.',
80
+ keyOps: ['agency_scan_file', 'agency_surface_patterns', 'agency_warnings'],
81
+ },
82
+ {
83
+ suffix: 'chat',
84
+ description: 'Chat transport — session management, response chunking, authentication.',
85
+ keyOps: ['chat_send', 'chat_history', 'chat_session'],
86
+ },
87
+ {
88
+ suffix: 'cognee',
89
+ description: 'Knowledge graph — Cognee search, sync, export, graph stats.',
90
+ keyOps: ['cognee_search', 'cognee_add', 'cognee_cognify'],
91
+ conditional: true,
92
+ },
93
+ ];
94
+
95
+ /** Core facade ops (always present, not in ENGINE_MODULES) */
96
+ export const CORE_KEY_OPS = ['health', 'identity', 'register', 'activate'];
97
+
98
+ /** Engine major version — used for compatibility checks against domain packs. */
99
+ export const ENGINE_MAJOR_VERSION = 8;
@@ -60,6 +60,12 @@ export interface EngineRegistrationResult {
60
60
  tools: string[];
61
61
  /** Total op count across all tools */
62
62
  totalOps: number;
63
+ /**
64
+ * Register a new MCP tool at runtime (hot reload).
65
+ * Call this after installing a pack or activating a plugin post-boot.
66
+ * Automatically notifies connected clients via sendToolListChanged().
67
+ */
68
+ registerTool: (toolName: string, description: string, ops: OpDefinition[]) => void;
63
69
  }
64
70
 
65
71
  // ─── Module Definition ────────────────────────────────────────────────
@@ -75,7 +81,8 @@ interface ModuleDef {
75
81
  condition?: (runtime: AgentRuntime) => boolean;
76
82
  }
77
83
 
78
- const ENGINE_MODULES: ModuleDef[] = [
84
+ /** @internal Exported for drift testing — do not use outside engine */
85
+ export const ENGINE_MODULES: ModuleDef[] = [
79
86
  {
80
87
  suffix: 'vault',
81
88
  description: 'Knowledge management — search, CRUD, import/export, intake, archival.',
@@ -233,7 +240,19 @@ export function registerEngine(
233
240
  }
234
241
  }
235
242
 
236
- return { tools: registeredTools, totalOps };
243
+ const registerTool = (toolName: string, description: string, ops: OpDefinition[]) => {
244
+ registerModuleTool(server, toolName, description, ops, authPolicy);
245
+ registeredTools.push(toolName);
246
+ totalOps += ops.length;
247
+ // Notify connected clients that tool list changed
248
+ try {
249
+ (server as unknown as { sendToolListChanged?: () => void }).sendToolListChanged?.();
250
+ } catch {
251
+ // Server may not support notifications yet — safe to ignore
252
+ }
253
+ };
254
+
255
+ return { tools: registeredTools, totalOps, registerTool };
237
256
  }
238
257
 
239
258
  // ─── Tool Registration (No Factory) ──────────────────────────────────
package/src/index.ts CHANGED
@@ -541,7 +541,6 @@ export type {
541
541
 
542
542
  // ─── Persistence ───────────────────────────────────────────────────────
543
543
  export { SQLitePersistenceProvider } from './persistence/index.js';
544
- export { PostgresPersistenceProvider, translateSql } from './persistence/index.js';
545
544
  export type {
546
545
  PersistenceProvider,
547
546
  PersistenceParams,
@@ -60,8 +60,6 @@ export async function classifyChunk(
60
60
  ): Promise<ClassifiedItem[]> {
61
61
  try {
62
62
  const result = await llm.complete({
63
- provider: 'openai',
64
- model: 'gpt-4o-mini',
65
63
  systemPrompt: CLASSIFICATION_PROMPT,
66
64
  userPrompt: chunkText,
67
65
  maxTokens: 4096,
@@ -160,6 +160,8 @@ interface AnthropicClient {
160
160
  };
161
161
  }
162
162
 
163
+ type ResolvedLLMOptions = LLMCallOptions & { model: string; provider: 'openai' | 'anthropic' };
164
+
163
165
  export class LLMClient {
164
166
  private openaiKeyPool: KeyPool;
165
167
  private anthropicKeyPool: KeyPool;
@@ -181,11 +183,15 @@ export class LLMClient {
181
183
 
182
184
  async complete(options: LLMCallOptions): Promise<LLMCallResult> {
183
185
  const routed = this.router.resolve(options.caller, options.task, options.model);
184
- const resolvedOptions = { ...options, model: routed.model, provider: routed.provider };
186
+ const resolved: ResolvedLLMOptions = {
187
+ ...options,
188
+ model: options.model ?? routed.model,
189
+ provider: options.provider ?? routed.provider,
190
+ };
185
191
 
186
- return resolvedOptions.provider === 'anthropic'
187
- ? this.callAnthropic(resolvedOptions)
188
- : this.callOpenAI(resolvedOptions);
192
+ return resolved.provider === 'anthropic'
193
+ ? this.callAnthropic(resolved)
194
+ : this.callOpenAI(resolved);
189
195
  }
190
196
 
191
197
  isAvailable(): { openai: boolean; anthropic: boolean } {
@@ -203,7 +209,7 @@ export class LLMClient {
203
209
  // OPENAI
204
210
  // ===========================================================================
205
211
 
206
- private async callOpenAI(options: LLMCallOptions): Promise<LLMCallResult> {
212
+ private async callOpenAI(options: ResolvedLLMOptions): Promise<LLMCallResult> {
207
213
  const keyPool = this.openaiKeyPool.hasKeys ? this.openaiKeyPool : null;
208
214
 
209
215
  if (!keyPool) {
@@ -275,7 +281,7 @@ export class LLMClient {
275
281
  // ANTHROPIC
276
282
  // ===========================================================================
277
283
 
278
- private async callAnthropic(options: LLMCallOptions): Promise<LLMCallResult> {
284
+ private async callAnthropic(options: ResolvedLLMOptions): Promise<LLMCallResult> {
279
285
  const client = await this.getAnthropicClient();
280
286
  if (!client) {
281
287
  throw new LLMError('Anthropic API key not configured', { retryable: false });
@@ -43,24 +43,6 @@ export function discoverAnthropicToken(): string | null {
43
43
  return token;
44
44
  }
45
45
 
46
- /**
47
- * Clear the cached token (for testing or rotation).
48
- */
49
- export function resetTokenCache(): void {
50
- cachedToken = null;
51
- cacheTimestamp = 0;
52
- }
53
-
54
- /**
55
- * Get discovery source info (for diagnostics).
56
- */
57
- export function getTokenSource(): string {
58
- if (process.env.ANTHROPIC_API_KEY) return 'env:ANTHROPIC_API_KEY';
59
- if (tryCredentialsFile()) return 'file:credentials';
60
- if (tryPlatformKeychain()) return `keychain:${platform()}`;
61
- return 'none';
62
- }
63
-
64
46
  // ─── Discovery Methods ───────────────────────────────────────────────
65
47
 
66
48
  function tryEnvVar(): string | null {
package/src/llm/types.ts CHANGED
@@ -43,8 +43,10 @@ export class LLMError extends Error {
43
43
  }
44
44
 
45
45
  export interface LLMCallOptions {
46
- provider: 'openai' | 'anthropic';
47
- model: string;
46
+ /** Provider override. If omitted, the model router selects based on caller/task. */
47
+ provider?: 'openai' | 'anthropic';
48
+ /** Model override. If omitted, the model router selects based on caller/task. */
49
+ model?: string;
48
50
  systemPrompt: string;
49
51
  userPrompt: string;
50
52
  temperature?: number;
@@ -21,6 +21,7 @@ import { loadIntelligenceData } from '../intelligence/loader.js';
21
21
  import type { Vault } from '../vault/vault.js';
22
22
  import type { PluginRegistry } from '../plugins/plugin-registry.js';
23
23
  import type { PluginContext } from '../plugins/types.js';
24
+ import type { PackRuntime } from '../domain-packs/pack-runtime.js';
24
25
 
25
26
  const MANIFEST_FILENAME = 'soleri-pack.json';
26
27
 
@@ -108,7 +109,11 @@ export class PackInstaller {
108
109
  /**
109
110
  * Install a knowledge pack from a directory.
110
111
  */
111
- async install(packDir: string, runtimeCtx?: unknown): Promise<InstallResult> {
112
+ async install(
113
+ packDir: string,
114
+ runtimeCtx?: unknown,
115
+ packRuntime?: PackRuntime,
116
+ ): Promise<InstallResult> {
112
117
  // Validate first
113
118
  const validation = this.validate(packDir);
114
119
  if (!validation.valid || !validation.manifest) {
@@ -174,6 +179,16 @@ export class PackInstaller {
174
179
  }
175
180
 
176
181
  const ctx: PluginContext = {
182
+ packRuntime:
183
+ packRuntime ??
184
+ ({
185
+ vault: {},
186
+ getProject: () => undefined,
187
+ listProjects: () => [],
188
+ createCheck: () => '',
189
+ validateCheck: () => null,
190
+ validateAndConsume: () => null,
191
+ } as unknown as PackRuntime),
177
192
  runtime: runtimeCtx ?? {},
178
193
  manifest: pluginLoaded.manifest,
179
194
  directory: packDir,
@@ -6,4 +6,3 @@ export type {
6
6
  FtsSearchOptions,
7
7
  } from './types.js';
8
8
  export { SQLitePersistenceProvider } from './sqlite-provider.js';
9
- export { PostgresPersistenceProvider, translateSql } from './postgres-provider.js';
@@ -35,7 +35,7 @@ export interface PersistenceProvider {
35
35
  transaction<T>(fn: () => T): T;
36
36
 
37
37
  /** Identifies the backend engine. */
38
- readonly backend: 'sqlite' | 'postgres';
38
+ readonly backend: 'sqlite';
39
39
 
40
40
  /** Full-text search abstraction. */
41
41
  ftsSearch<T = Record<string, unknown>>(
@@ -52,12 +52,8 @@ export interface PersistenceProvider {
52
52
  }
53
53
 
54
54
  export interface PersistenceConfig {
55
- type: 'sqlite' | 'postgres';
55
+ type: 'sqlite';
56
56
  path: string;
57
- /** PostgreSQL connection string. */
58
- connectionString?: string;
59
- /** PostgreSQL pool size. */
60
- poolSize?: number;
61
57
  }
62
58
 
63
59
  export interface FtsSearchOptions {
@@ -1,5 +1,9 @@
1
1
  /**
2
2
  * Plugin System — Barrel Exports
3
+ *
4
+ * @deprecated Prefer knowledge packs (soleri-pack.json) for new extensions.
5
+ * The plugin system is maintained for backwards compatibility and is used
6
+ * internally by the pack installer for facade registration.
3
7
  */
4
8
 
5
9
  export {
@@ -1,6 +1,10 @@
1
1
  /**
2
2
  * Plugin Registry — tracks loaded plugins and their lifecycle.
3
3
  *
4
+ * @deprecated The plugin system is superseded by knowledge packs (`soleri-pack.json`).
5
+ * This registry is still used internally by the pack installer to register facades,
6
+ * but new extensions should use the pack system directly.
7
+ *
4
8
  * Not a singleton — lives on AgentRuntime for testability.
5
9
  * Lifecycle: load → register → activate → (deactivate | error)
6
10
  */
@@ -161,7 +165,8 @@ export class PluginRegistry {
161
165
  throw new Error(`Plugin module "${moduleFile}" must export createFacades(ctx)`);
162
166
  } catch (e) {
163
167
  throw new Error(
164
- `Failed to load plugin module "${moduleFile}": ${e instanceof Error ? e.message : String(e)}`, { cause: e },
168
+ `Failed to load plugin module "${moduleFile}": ${e instanceof Error ? e.message : String(e)}`,
169
+ { cause: e },
165
170
  );
166
171
  }
167
172
  }
@@ -1,9 +1,12 @@
1
1
  /**
2
2
  * Plugin System — Types & Manifest Schema
3
3
  *
4
- * A plugin is a directory containing a `soleri-plugin.json` manifest
5
- * and optionally additional intelligence data. Plugins register
6
- * OpDefinition[] (facades) dynamically without re-scaffolding.
4
+ * @deprecated Prefer knowledge packs (`soleri-pack.json`) over plugins (`soleri-plugin.json`).
5
+ * Knowledge packs are a superset of plugins — they support facades, vault entries, skills,
6
+ * hooks, and capability declarations. Plugins only support facades and intelligence entries.
7
+ *
8
+ * This module is maintained for backwards compatibility. New extensions should use
9
+ * the pack system in `../packs/`. See docs/architecture/extension-tiers.md.
7
10
  */
8
11
 
9
12
  import { z } from 'zod';
@@ -101,8 +104,10 @@ export type PluginFacadeBuilder = (ctx: PluginContext) => FacadeConfig[];
101
104
  * Context passed to plugin facade builders during activation.
102
105
  */
103
106
  export interface PluginContext {
104
- /** The agent runtime — full access to vault, brain, planner, etc. */
105
- runtime: unknown; // AgentRuntime — kept as unknown to avoid circular deps
107
+ /** Narrowed runtime — vault, projects, session checks. Preferred over full runtime. */
108
+ packRuntime: import('../domain-packs/pack-runtime.js').PackRuntime;
109
+ /** @deprecated Full agent runtime. Use packRuntime instead — only vault, projects, and session checks are guaranteed. */
110
+ runtime: unknown;
106
111
  /** The plugin's own manifest */
107
112
  manifest: PluginManifest;
108
113
  /** The plugin's directory on disk */
@@ -9,7 +9,6 @@
9
9
  */
10
10
 
11
11
  import type { AgentRuntimeConfig } from './types.js';
12
- import type { OpDefinition } from '../facades/types.js';
13
12
 
14
13
  // ─── Types ────────────────────────────────────────────────────────────
15
14
 
@@ -198,7 +197,7 @@ const ENGINE_RULES_END = '<!-- /soleri:engine-rules -->';
198
197
  /**
199
198
  * Check if engine rules are present in content.
200
199
  */
201
- export function hasEngineRules(content: string): boolean {
200
+ function hasEngineRules(content: string): boolean {
202
201
  return content.includes(ENGINE_RULES_START) && content.includes(ENGINE_RULES_END);
203
202
  }
204
203
 
@@ -217,20 +216,3 @@ export function injectEngineRulesBlock(content: string, engineRulesContent: stri
217
216
  // Append
218
217
  return content.trimEnd() + '\n\n' + engineRulesContent + '\n';
219
218
  }
220
-
221
- /**
222
- * Extract facade summaries from an ops array (for tools table generation).
223
- */
224
- export function extractFacadeSummaries(
225
- agentId: string,
226
- opsMap: Map<string, OpDefinition[]>,
227
- ): FacadeSummary[] {
228
- const summaries: FacadeSummary[] = [];
229
- for (const [suffix, ops] of opsMap) {
230
- summaries.push({
231
- name: `${agentId}_${suffix}`,
232
- ops: ops.map((o) => o.name),
233
- });
234
- }
235
- return summaries;
236
- }
@@ -58,8 +58,7 @@ export function createAdminFacadeOps(runtime: AgentRuntime): OpDefinition[] {
58
58
  }),
59
59
  handler: async (params) => {
60
60
  return llmClient.complete({
61
- provider: 'openai',
62
- model: (params.model as string) ?? '',
61
+ model: (params.model as string) || undefined,
63
62
  systemPrompt: params.systemPrompt as string,
64
63
  userPrompt: params.userPrompt as string,
65
64
  temperature: params.temperature as number | undefined,
@@ -8,6 +8,16 @@ import { z } from 'zod';
8
8
  import type { OpDefinition } from '../facades/types.js';
9
9
  import type { AgentRuntime } from './types.js';
10
10
 
11
+ export type HotRegisterTool = (toolName: string, description: string, ops: OpDefinition[]) => void;
12
+
13
+ /** Mutable slot — set after engine registration to enable hot reload. */
14
+ let _hotRegister: HotRegisterTool | null = null;
15
+
16
+ /** Set the hot-register callback after registerEngine() completes. */
17
+ export function setHotRegister(fn: HotRegisterTool): void {
18
+ _hotRegister = fn;
19
+ }
20
+
11
21
  export function createPackOps(runtime: AgentRuntime): OpDefinition[] {
12
22
  const { packInstaller } = runtime;
13
23
 
@@ -36,7 +46,22 @@ export function createPackOps(runtime: AgentRuntime): OpDefinition[] {
36
46
  packDir: z.string().describe('Path to the knowledge pack directory.'),
37
47
  }),
38
48
  handler: async (params) => {
39
- return packInstaller.install(params.packDir as string, runtime);
49
+ const result = await packInstaller.install(params.packDir as string, runtime);
50
+ // Hot register facades as MCP tools if callback available
51
+ if (_hotRegister && result.installed && result.facades > 0) {
52
+ const plugin = runtime.pluginRegistry.get(result.id);
53
+ if (plugin?.facades) {
54
+ for (const facade of plugin.facades) {
55
+ _hotRegister(
56
+ `${runtime.config.agentId}_${facade.name}`,
57
+ facade.description,
58
+ facade.ops,
59
+ );
60
+ }
61
+ }
62
+ (result as unknown as Record<string, unknown>).hotReloaded = true;
63
+ }
64
+ return result;
40
65
  },
41
66
  },
42
67
 
@@ -8,6 +8,7 @@ import { z } from 'zod';
8
8
  import type { OpDefinition } from '../facades/types.js';
9
9
  import type { AgentRuntime } from './types.js';
10
10
  import { loadPlugins, validateDependencies, sortByDependencies } from '../plugins/index.js';
11
+ import { createPackRuntime } from '../domain-packs/pack-runtime.js';
11
12
 
12
13
  export function createPluginOps(runtime: AgentRuntime, opSink?: OpDefinition[]): OpDefinition[] {
13
14
  const { pluginRegistry, config } = runtime;
@@ -157,6 +158,7 @@ export function createPluginOps(runtime: AgentRuntime, opSink?: OpDefinition[]):
157
158
  if (!plugin) return { error: `Plugin not found: ${pluginId}` };
158
159
 
159
160
  const result = await pluginRegistry.activate(pluginId, {
161
+ packRuntime: createPackRuntime(runtime),
160
162
  runtime,
161
163
  manifest: plugin.manifest,
162
164
  directory: plugin.directory,
@@ -179,6 +181,7 @@ export function createPluginOps(runtime: AgentRuntime, opSink?: OpDefinition[]):
179
181
  const results = await Promise.all(
180
182
  pending.map(async (plugin) => {
181
183
  const activated = await pluginRegistry.activate(plugin.id, {
184
+ packRuntime: createPackRuntime(runtime),
182
185
  runtime,
183
186
  manifest: plugin.manifest,
184
187
  directory: plugin.directory,
@@ -42,6 +42,20 @@ export function createSessionBriefingOps(runtime: AgentRuntime): OpDefinition[]
42
42
  const sections: BriefingSection[] = [];
43
43
  let dataPoints = 0;
44
44
 
45
+ // 0. Day-one welcome (vault has few non-playbook entries)
46
+ try {
47
+ const stats = vault.stats();
48
+ const nonPlaybook = stats.totalEntries - (stats.byType?.playbook ?? 0);
49
+ if (nonPlaybook < 10) {
50
+ sections.push({
51
+ label: 'Welcome',
52
+ content: `Vault has ${nonPlaybook} knowledge entries. Capture patterns as you work — the brain learns from every session. Use op:capture_knowledge to persist insights.`,
53
+ });
54
+ }
55
+ } catch {
56
+ // Vault stats unavailable — skip
57
+ }
58
+
45
59
  // 1. Last session
46
60
  try {
47
61
  const sessions = brainIntelligence.listSessions({ limit: 1, active: false });
@@ -328,13 +328,11 @@ export function createVaultLinkingOps(runtime: AgentRuntime): OpDefinition[] {
328
328
 
329
329
  try {
330
330
  const result = await llmClient.complete({
331
- provider: llmClient.isAvailable().anthropic ? 'anthropic' : 'openai',
332
- model: llmClient.isAvailable().anthropic ? 'claude-sonnet-4-20250514' : 'gpt-4o-mini',
333
331
  systemPrompt: EVAL_SYSTEM_PROMPT,
334
332
  userPrompt: pairsText,
335
333
  maxTokens: 2000,
336
- caller: 'relink_vault',
337
- task: 'link-evaluation',
334
+ caller: 'vault-linking',
335
+ task: 'evaluate-links',
338
336
  });
339
337
  llmCalls++;
340
338