@soleri/core 7.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 (294) hide show
  1. package/dist/agency/agency-manager.d.ts +27 -1
  2. package/dist/agency/agency-manager.d.ts.map +1 -1
  3. package/dist/agency/agency-manager.js +180 -9
  4. package/dist/agency/agency-manager.js.map +1 -1
  5. package/dist/agency/default-rules.d.ts +7 -0
  6. package/dist/agency/default-rules.d.ts.map +1 -0
  7. package/dist/agency/default-rules.js +79 -0
  8. package/dist/agency/default-rules.js.map +1 -0
  9. package/dist/agency/types.d.ts +48 -0
  10. package/dist/agency/types.d.ts.map +1 -1
  11. package/dist/brain/brain.d.ts +17 -2
  12. package/dist/brain/brain.d.ts.map +1 -1
  13. package/dist/brain/brain.js +118 -8
  14. package/dist/brain/brain.js.map +1 -1
  15. package/dist/brain/knowledge-synthesizer.d.ts +37 -0
  16. package/dist/brain/knowledge-synthesizer.d.ts.map +1 -0
  17. package/dist/brain/knowledge-synthesizer.js +159 -0
  18. package/dist/brain/knowledge-synthesizer.js.map +1 -0
  19. package/dist/brain/learning-radar.d.ts +96 -0
  20. package/dist/brain/learning-radar.d.ts.map +1 -0
  21. package/dist/brain/learning-radar.js +202 -0
  22. package/dist/brain/learning-radar.js.map +1 -0
  23. package/dist/brain/types.d.ts +15 -0
  24. package/dist/brain/types.d.ts.map +1 -1
  25. package/dist/context/context-engine.d.ts.map +1 -1
  26. package/dist/context/context-engine.js +82 -17
  27. package/dist/context/context-engine.js.map +1 -1
  28. package/dist/context/types.d.ts +5 -0
  29. package/dist/context/types.d.ts.map +1 -1
  30. package/dist/control/intent-router.d.ts +12 -1
  31. package/dist/control/intent-router.d.ts.map +1 -1
  32. package/dist/control/intent-router.js +68 -0
  33. package/dist/control/intent-router.js.map +1 -1
  34. package/dist/control/types.d.ts +17 -0
  35. package/dist/control/types.d.ts.map +1 -1
  36. package/dist/curator/classifier.d.ts +18 -0
  37. package/dist/curator/classifier.d.ts.map +1 -0
  38. package/dist/curator/classifier.js +59 -0
  39. package/dist/curator/classifier.js.map +1 -0
  40. package/dist/curator/quality-gate.d.ts +29 -0
  41. package/dist/curator/quality-gate.d.ts.map +1 -0
  42. package/dist/curator/quality-gate.js +86 -0
  43. package/dist/curator/quality-gate.js.map +1 -0
  44. package/dist/domain-packs/index.d.ts +0 -3
  45. package/dist/domain-packs/index.d.ts.map +1 -1
  46. package/dist/domain-packs/index.js +0 -3
  47. package/dist/domain-packs/index.js.map +1 -1
  48. package/dist/domain-packs/loader.d.ts.map +1 -1
  49. package/dist/domain-packs/loader.js +20 -4
  50. package/dist/domain-packs/loader.js.map +1 -1
  51. package/dist/domain-packs/pack-runtime.d.ts +5 -5
  52. package/dist/domain-packs/pack-runtime.d.ts.map +1 -1
  53. package/dist/domain-packs/pack-runtime.js +2 -2
  54. package/dist/domain-packs/pack-runtime.js.map +1 -1
  55. package/dist/domain-packs/types.d.ts +8 -2
  56. package/dist/domain-packs/types.d.ts.map +1 -1
  57. package/dist/domain-packs/types.js.map +1 -1
  58. package/dist/engine/bin/soleri-engine.js +13 -2
  59. package/dist/engine/bin/soleri-engine.js.map +1 -1
  60. package/dist/engine/index.d.ts +2 -0
  61. package/dist/engine/index.d.ts.map +1 -1
  62. package/dist/engine/index.js +1 -0
  63. package/dist/engine/index.js.map +1 -1
  64. package/dist/engine/module-manifest.d.ts +28 -0
  65. package/dist/engine/module-manifest.d.ts.map +1 -0
  66. package/dist/engine/module-manifest.js +85 -0
  67. package/dist/engine/module-manifest.js.map +1 -0
  68. package/dist/engine/register-engine.d.ts +19 -0
  69. package/dist/engine/register-engine.d.ts.map +1 -1
  70. package/dist/engine/register-engine.js +15 -2
  71. package/dist/engine/register-engine.js.map +1 -1
  72. package/dist/events/event-bus.d.ts +30 -0
  73. package/dist/events/event-bus.d.ts.map +1 -0
  74. package/dist/events/event-bus.js +51 -0
  75. package/dist/events/event-bus.js.map +1 -0
  76. package/dist/flows/chain-runner.d.ts +46 -0
  77. package/dist/flows/chain-runner.d.ts.map +1 -0
  78. package/dist/flows/chain-runner.js +271 -0
  79. package/dist/flows/chain-runner.js.map +1 -0
  80. package/dist/flows/chain-types.d.ts +103 -0
  81. package/dist/flows/chain-types.d.ts.map +1 -0
  82. package/dist/flows/chain-types.js +23 -0
  83. package/dist/flows/chain-types.js.map +1 -0
  84. package/dist/health/doctor-checks.d.ts +15 -0
  85. package/dist/health/doctor-checks.d.ts.map +1 -0
  86. package/dist/health/doctor-checks.js +98 -0
  87. package/dist/health/doctor-checks.js.map +1 -0
  88. package/dist/index.d.ts +0 -1
  89. package/dist/index.d.ts.map +1 -1
  90. package/dist/index.js +0 -1
  91. package/dist/index.js.map +1 -1
  92. package/dist/intake/content-classifier.d.ts.map +1 -1
  93. package/dist/intake/content-classifier.js +0 -2
  94. package/dist/intake/content-classifier.js.map +1 -1
  95. package/dist/intake/text-ingester.d.ts +52 -0
  96. package/dist/intake/text-ingester.d.ts.map +1 -0
  97. package/dist/intake/text-ingester.js +181 -0
  98. package/dist/intake/text-ingester.js.map +1 -0
  99. package/dist/llm/llm-client.d.ts.map +1 -1
  100. package/dist/llm/llm-client.js +45 -5
  101. package/dist/llm/llm-client.js.map +1 -1
  102. package/dist/llm/oauth-discovery.d.ts +18 -0
  103. package/dist/llm/oauth-discovery.d.ts.map +1 -0
  104. package/dist/llm/oauth-discovery.js +130 -0
  105. package/dist/llm/oauth-discovery.js.map +1 -0
  106. package/dist/llm/types.d.ts +4 -2
  107. package/dist/llm/types.d.ts.map +1 -1
  108. package/dist/packs/pack-installer.d.ts +2 -1
  109. package/dist/packs/pack-installer.d.ts.map +1 -1
  110. package/dist/packs/pack-installer.js +10 -1
  111. package/dist/packs/pack-installer.js.map +1 -1
  112. package/dist/persistence/index.d.ts +0 -1
  113. package/dist/persistence/index.d.ts.map +1 -1
  114. package/dist/persistence/index.js +0 -1
  115. package/dist/persistence/index.js.map +1 -1
  116. package/dist/persistence/types.d.ts +2 -6
  117. package/dist/persistence/types.d.ts.map +1 -1
  118. package/dist/planning/evidence-collector.d.ts +41 -0
  119. package/dist/planning/evidence-collector.d.ts.map +1 -0
  120. package/dist/planning/evidence-collector.js +194 -0
  121. package/dist/planning/evidence-collector.js.map +1 -0
  122. package/dist/planning/planner.d.ts +4 -0
  123. package/dist/planning/planner.d.ts.map +1 -1
  124. package/dist/planning/planner.js +11 -0
  125. package/dist/planning/planner.js.map +1 -1
  126. package/dist/plugins/index.d.ts +4 -0
  127. package/dist/plugins/index.d.ts.map +1 -1
  128. package/dist/plugins/index.js +4 -0
  129. package/dist/plugins/index.js.map +1 -1
  130. package/dist/plugins/plugin-registry.d.ts +4 -0
  131. package/dist/plugins/plugin-registry.d.ts.map +1 -1
  132. package/dist/plugins/plugin-registry.js +4 -0
  133. package/dist/plugins/plugin-registry.js.map +1 -1
  134. package/dist/plugins/types.d.ts +32 -27
  135. package/dist/plugins/types.d.ts.map +1 -1
  136. package/dist/plugins/types.js +6 -3
  137. package/dist/plugins/types.js.map +1 -1
  138. package/dist/queue/job-queue.d.ts +92 -0
  139. package/dist/queue/job-queue.d.ts.map +1 -0
  140. package/dist/queue/job-queue.js +180 -0
  141. package/dist/queue/job-queue.js.map +1 -0
  142. package/dist/queue/pipeline-runner.d.ts +62 -0
  143. package/dist/queue/pipeline-runner.d.ts.map +1 -0
  144. package/dist/queue/pipeline-runner.js +126 -0
  145. package/dist/queue/pipeline-runner.js.map +1 -0
  146. package/dist/runtime/admin-setup-ops.d.ts +20 -0
  147. package/dist/runtime/admin-setup-ops.d.ts.map +1 -0
  148. package/dist/runtime/admin-setup-ops.js +583 -0
  149. package/dist/runtime/admin-setup-ops.js.map +1 -0
  150. package/dist/runtime/chain-ops.d.ts +9 -0
  151. package/dist/runtime/chain-ops.d.ts.map +1 -0
  152. package/dist/runtime/chain-ops.js +107 -0
  153. package/dist/runtime/chain-ops.js.map +1 -0
  154. package/dist/runtime/claude-md-helpers.d.ts +56 -0
  155. package/dist/runtime/claude-md-helpers.d.ts.map +1 -0
  156. package/dist/runtime/claude-md-helpers.js +160 -0
  157. package/dist/runtime/claude-md-helpers.js.map +1 -0
  158. package/dist/runtime/curator-extra-ops.d.ts +3 -2
  159. package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
  160. package/dist/runtime/curator-extra-ops.js +81 -3
  161. package/dist/runtime/curator-extra-ops.js.map +1 -1
  162. package/dist/runtime/facades/admin-facade.d.ts.map +1 -1
  163. package/dist/runtime/facades/admin-facade.js +5 -2
  164. package/dist/runtime/facades/admin-facade.js.map +1 -1
  165. package/dist/runtime/facades/agency-facade.d.ts.map +1 -1
  166. package/dist/runtime/facades/agency-facade.js +64 -0
  167. package/dist/runtime/facades/agency-facade.js.map +1 -1
  168. package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
  169. package/dist/runtime/facades/brain-facade.js +122 -1
  170. package/dist/runtime/facades/brain-facade.js.map +1 -1
  171. package/dist/runtime/facades/control-facade.d.ts.map +1 -1
  172. package/dist/runtime/facades/control-facade.js +42 -0
  173. package/dist/runtime/facades/control-facade.js.map +1 -1
  174. package/dist/runtime/facades/memory-facade.d.ts.map +1 -1
  175. package/dist/runtime/facades/memory-facade.js +20 -2
  176. package/dist/runtime/facades/memory-facade.js.map +1 -1
  177. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  178. package/dist/runtime/facades/plan-facade.js +2 -0
  179. package/dist/runtime/facades/plan-facade.js.map +1 -1
  180. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  181. package/dist/runtime/facades/vault-facade.js +25 -5
  182. package/dist/runtime/facades/vault-facade.js.map +1 -1
  183. package/dist/runtime/intake-ops.d.ts +7 -5
  184. package/dist/runtime/intake-ops.d.ts.map +1 -1
  185. package/dist/runtime/intake-ops.js +98 -5
  186. package/dist/runtime/intake-ops.js.map +1 -1
  187. package/dist/runtime/memory-extra-ops.d.ts +6 -3
  188. package/dist/runtime/memory-extra-ops.d.ts.map +1 -1
  189. package/dist/runtime/memory-extra-ops.js +292 -4
  190. package/dist/runtime/memory-extra-ops.js.map +1 -1
  191. package/dist/runtime/pack-ops.d.ts +3 -0
  192. package/dist/runtime/pack-ops.d.ts.map +1 -1
  193. package/dist/runtime/pack-ops.js +18 -1
  194. package/dist/runtime/pack-ops.js.map +1 -1
  195. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  196. package/dist/runtime/planning-extra-ops.js +85 -0
  197. package/dist/runtime/planning-extra-ops.js.map +1 -1
  198. package/dist/runtime/playbook-ops.js +1 -1
  199. package/dist/runtime/playbook-ops.js.map +1 -1
  200. package/dist/runtime/plugin-ops.d.ts.map +1 -1
  201. package/dist/runtime/plugin-ops.js +3 -0
  202. package/dist/runtime/plugin-ops.js.map +1 -1
  203. package/dist/runtime/runtime.d.ts.map +1 -1
  204. package/dist/runtime/runtime.js +143 -2
  205. package/dist/runtime/runtime.js.map +1 -1
  206. package/dist/runtime/session-briefing.d.ts +23 -0
  207. package/dist/runtime/session-briefing.d.ts.map +1 -0
  208. package/dist/runtime/session-briefing.js +154 -0
  209. package/dist/runtime/session-briefing.js.map +1 -0
  210. package/dist/runtime/types.d.ts +23 -0
  211. package/dist/runtime/types.d.ts.map +1 -1
  212. package/dist/runtime/vault-linking-ops.d.ts.map +1 -1
  213. package/dist/runtime/vault-linking-ops.js +3 -7
  214. package/dist/runtime/vault-linking-ops.js.map +1 -1
  215. package/dist/vault/vault.d.ts +34 -0
  216. package/dist/vault/vault.d.ts.map +1 -1
  217. package/dist/vault/vault.js +89 -3
  218. package/dist/vault/vault.js.map +1 -1
  219. package/package.json +6 -4
  220. package/src/__tests__/admin-setup-ops.test.ts +355 -0
  221. package/src/__tests__/async-infrastructure.test.ts +307 -0
  222. package/src/__tests__/cognee-client-gaps.test.ts +6 -2
  223. package/src/__tests__/cognee-hybrid-search.test.ts +49 -35
  224. package/src/__tests__/cognee-sync-manager-deep.test.ts +89 -65
  225. package/src/__tests__/curator-extra-ops.test.ts +6 -2
  226. package/src/__tests__/curator-pipeline-e2e.test.ts +545 -0
  227. package/src/__tests__/memory-extra-ops.test.ts +2 -2
  228. package/src/__tests__/module-manifest-drift.test.ts +59 -0
  229. package/src/__tests__/planning-extra-ops.test.ts +2 -2
  230. package/src/__tests__/second-brain-features.test.ts +583 -0
  231. package/src/agency/agency-manager.ts +217 -9
  232. package/src/agency/default-rules.ts +83 -0
  233. package/src/agency/types.ts +61 -0
  234. package/src/brain/brain.ts +110 -8
  235. package/src/brain/knowledge-synthesizer.ts +216 -0
  236. package/src/brain/learning-radar.ts +340 -0
  237. package/src/brain/types.ts +16 -0
  238. package/src/context/context-engine.ts +114 -15
  239. package/src/context/types.ts +5 -0
  240. package/src/control/intent-router.ts +107 -0
  241. package/src/control/types.ts +10 -0
  242. package/src/curator/classifier.ts +86 -0
  243. package/src/curator/quality-gate.ts +127 -0
  244. package/src/domain-packs/index.ts +0 -6
  245. package/src/domain-packs/loader.ts +25 -5
  246. package/src/domain-packs/pack-runtime.ts +6 -6
  247. package/src/domain-packs/types.ts +8 -2
  248. package/src/engine/bin/soleri-engine.ts +18 -2
  249. package/src/engine/index.ts +2 -0
  250. package/src/engine/module-manifest.ts +99 -0
  251. package/src/engine/register-engine.ts +21 -2
  252. package/src/events/event-bus.ts +58 -0
  253. package/src/flows/chain-runner.ts +369 -0
  254. package/src/flows/chain-types.ts +57 -0
  255. package/src/index.ts +0 -1
  256. package/src/intake/content-classifier.ts +0 -2
  257. package/src/intake/text-ingester.ts +234 -0
  258. package/src/llm/llm-client.ts +50 -7
  259. package/src/llm/oauth-discovery.ts +151 -0
  260. package/src/llm/types.ts +4 -2
  261. package/src/packs/pack-installer.ts +16 -1
  262. package/src/persistence/index.ts +0 -1
  263. package/src/persistence/types.ts +2 -6
  264. package/src/planning/evidence-collector.ts +247 -0
  265. package/src/planning/planner.ts +11 -0
  266. package/src/plugins/index.ts +4 -0
  267. package/src/plugins/plugin-registry.ts +6 -1
  268. package/src/plugins/types.ts +10 -5
  269. package/src/queue/job-queue.ts +281 -0
  270. package/src/queue/pipeline-runner.ts +149 -0
  271. package/src/runtime/admin-setup-ops.ts +664 -0
  272. package/src/runtime/chain-ops.ts +121 -0
  273. package/src/runtime/claude-md-helpers.ts +218 -0
  274. package/src/runtime/curator-extra-ops.ts +86 -3
  275. package/src/runtime/facades/admin-facade.ts +5 -2
  276. package/src/runtime/facades/agency-facade.ts +68 -0
  277. package/src/runtime/facades/brain-facade.ts +142 -1
  278. package/src/runtime/facades/control-facade.ts +45 -0
  279. package/src/runtime/facades/memory-facade.ts +20 -2
  280. package/src/runtime/facades/plan-facade.ts +2 -0
  281. package/src/runtime/facades/vault-facade.ts +28 -5
  282. package/src/runtime/intake-ops.ts +107 -5
  283. package/src/runtime/memory-extra-ops.ts +312 -4
  284. package/src/runtime/pack-ops.ts +26 -1
  285. package/src/runtime/planning-extra-ops.ts +94 -0
  286. package/src/runtime/playbook-ops.ts +1 -1
  287. package/src/runtime/plugin-ops.ts +3 -0
  288. package/src/runtime/runtime.ts +138 -2
  289. package/src/runtime/session-briefing.ts +175 -0
  290. package/src/runtime/types.ts +23 -0
  291. package/src/runtime/vault-linking-ops.ts +3 -7
  292. package/src/vault/vault.ts +105 -4
  293. package/src/__tests__/postgres-provider.test.ts +0 -116
  294. package/src/persistence/postgres-provider.ts +0 -310
@@ -75,6 +75,7 @@ async function main(): Promise<void> {
75
75
  const runtime = createAgentRuntime({
76
76
  agentId,
77
77
  vaultPath,
78
+ agentDir,
78
79
  cognee: (engineConfig.cognee as boolean) ?? false,
79
80
  });
80
81
 
@@ -116,6 +117,14 @@ async function main(): Promise<void> {
116
117
  // 6. Seed default playbooks
117
118
  seedDefaultPlaybooks(runtime.vault);
118
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
+
119
128
  // 7. Load domain packs
120
129
  const packs = (config.packs ?? []) as Array<{ name: string; package: string; version?: string }>;
121
130
  const loadedPacks: Array<{ name: string; facades?: Array<{ name: string; ops: unknown[] }> }> =
@@ -128,9 +137,12 @@ async function main(): Promise<void> {
128
137
  const manifests = await loadDomainPacksFromConfig(refs);
129
138
 
130
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
+
131
143
  for (const manifest of manifests) {
132
144
  if (manifest.onActivate) {
133
- await manifest.onActivate(runtime); // eslint-disable-line no-await-in-loop
145
+ await manifest.onActivate(narrowedRuntime, runtime); // eslint-disable-line no-await-in-loop
134
146
  }
135
147
  loadedPacks.push(manifest);
136
148
  console.error(`${tag} Domain pack: ${manifest.name}`);
@@ -177,7 +189,7 @@ async function main(): Promise<void> {
177
189
  }));
178
190
 
179
191
  // 12. Register all engine tools
180
- const { tools, totalOps } = registerEngine(server, runtime, {
192
+ const { tools, totalOps, registerTool } = registerEngine(server, runtime, {
181
193
  agentId,
182
194
  coreOps,
183
195
  domains: identity.domains,
@@ -194,6 +206,10 @@ async function main(): Promise<void> {
194
206
 
195
207
  console.error(`${tag} Registered ${tools.length} tools (${totalOps} ops)`);
196
208
 
209
+ // Enable hot reload for post-boot pack/plugin installation
210
+ const { setHotRegister } = await import('../../runtime/pack-ops.js');
211
+ setHotRegister(registerTool);
212
+
197
213
  // 13. Connect stdio transport
198
214
  const transport = new StdioServerTransport();
199
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) ──────────────────────────────────
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Typed Event Bus — generic event emitter for decoupled subsystem communication.
3
+ *
4
+ * Wraps Node EventEmitter with type-safe emit/subscribe.
5
+ * Any module can define its own event map and create a bus.
6
+ */
7
+
8
+ import { EventEmitter } from 'node:events';
9
+
10
+ /**
11
+ * Create a typed event bus.
12
+ *
13
+ * Usage:
14
+ * ```ts
15
+ * type MyEvents = {
16
+ * 'entry:created': { id: string; title: string };
17
+ * 'entry:deleted': { id: string };
18
+ * };
19
+ * const bus = new TypedEventBus<MyEvents>();
20
+ * bus.on('entry:created', (payload) => console.log(payload.title));
21
+ * bus.emit('entry:created', { id: '1', title: 'Hello' });
22
+ * ```
23
+ */
24
+ export class TypedEventBus<TEvents extends Record<string, unknown>> {
25
+ private emitter = new EventEmitter();
26
+
27
+ on<E extends keyof TEvents & string>(event: E, listener: (payload: TEvents[E]) => void): this {
28
+ this.emitter.on(event, listener as (...args: unknown[]) => void);
29
+ return this;
30
+ }
31
+
32
+ once<E extends keyof TEvents & string>(event: E, listener: (payload: TEvents[E]) => void): this {
33
+ this.emitter.once(event, listener as (...args: unknown[]) => void);
34
+ return this;
35
+ }
36
+
37
+ off<E extends keyof TEvents & string>(event: E, listener: (payload: TEvents[E]) => void): this {
38
+ this.emitter.off(event, listener as (...args: unknown[]) => void);
39
+ return this;
40
+ }
41
+
42
+ emit<E extends keyof TEvents & string>(event: E, payload: TEvents[E]): boolean {
43
+ return this.emitter.emit(event, payload);
44
+ }
45
+
46
+ listenerCount(): number {
47
+ let total = 0;
48
+ for (const name of this.emitter.eventNames()) {
49
+ total += this.emitter.listenerCount(name);
50
+ }
51
+ return total;
52
+ }
53
+
54
+ removeAllListeners(): this {
55
+ this.emitter.removeAllListeners();
56
+ return this;
57
+ }
58
+ }
@@ -0,0 +1,369 @@
1
+ /**
2
+ * Chain Runner — executes multi-step workflows with data flow between steps.
3
+ *
4
+ * Steps call facade ops via a dispatch function. Each step's output is stored
5
+ * in a context object and can be referenced by subsequent steps via $variable syntax.
6
+ *
7
+ * Gates pause execution. Resume via approve(). State persists to SQLite.
8
+ */
9
+
10
+ import { randomUUID } from 'node:crypto';
11
+ import type { PersistenceProvider } from '../persistence/types.js';
12
+ import type { ChainDef, ChainInstance, StepOutput } from './chain-types.js';
13
+
14
+ // ─── Types ───────────────────────────────────────────────────────────
15
+
16
+ export type DispatchFn = (op: string, params: Record<string, unknown>) => Promise<unknown>;
17
+
18
+ type GateCheckFn = (
19
+ gate: string,
20
+ stepId: string,
21
+ stepResult: unknown,
22
+ ) => Promise<{ passed: boolean; message?: string }>;
23
+
24
+ // ─── Class ───────────────────────────────────────────────────────────
25
+
26
+ export class ChainRunner {
27
+ private provider: PersistenceProvider;
28
+
29
+ constructor(provider: PersistenceProvider) {
30
+ this.provider = provider;
31
+ this.initializeTable();
32
+ }
33
+
34
+ private initializeTable(): void {
35
+ this.provider.execSql(`
36
+ CREATE TABLE IF NOT EXISTS chain_instances (
37
+ id TEXT PRIMARY KEY,
38
+ chain_id TEXT NOT NULL,
39
+ chain_name TEXT NOT NULL DEFAULT '',
40
+ status TEXT NOT NULL DEFAULT 'running',
41
+ current_step TEXT,
42
+ paused_at_gate TEXT,
43
+ input TEXT NOT NULL DEFAULT '{}',
44
+ context TEXT NOT NULL DEFAULT '{}',
45
+ step_outputs TEXT NOT NULL DEFAULT '[]',
46
+ steps_completed INTEGER NOT NULL DEFAULT 0,
47
+ total_steps INTEGER NOT NULL DEFAULT 0,
48
+ created_at TEXT NOT NULL DEFAULT (datetime('now')),
49
+ updated_at TEXT NOT NULL DEFAULT (datetime('now'))
50
+ );
51
+ `);
52
+ }
53
+
54
+ /**
55
+ * Execute a chain from the beginning (or a specific step).
56
+ */
57
+ async execute(
58
+ chainDef: ChainDef,
59
+ input: Record<string, unknown>,
60
+ dispatch: DispatchFn,
61
+ gateCheck?: GateCheckFn,
62
+ startFromStep?: string,
63
+ ): Promise<ChainInstance> {
64
+ const instanceId = randomUUID().slice(0, 12);
65
+ const instance: ChainInstance = {
66
+ id: instanceId,
67
+ chainId: chainDef.id,
68
+ chainName: chainDef.name ?? chainDef.id,
69
+ status: 'running',
70
+ currentStep: null,
71
+ pausedAtGate: null,
72
+ input,
73
+ context: { input },
74
+ stepOutputs: [],
75
+ stepsCompleted: 0,
76
+ totalSteps: chainDef.steps.length,
77
+ createdAt: new Date().toISOString(),
78
+ updatedAt: new Date().toISOString(),
79
+ };
80
+
81
+ this.persist(instance);
82
+
83
+ // Find start index
84
+ let startIdx = 0;
85
+ if (startFromStep) {
86
+ const idx = chainDef.steps.findIndex((s) => s.id === startFromStep);
87
+ if (idx >= 0) startIdx = idx;
88
+ }
89
+
90
+ return this.runSteps(chainDef, instance, dispatch, gateCheck, startIdx);
91
+ }
92
+
93
+ /**
94
+ * Resume a paused chain from where it left off.
95
+ */
96
+ async resume(
97
+ instanceId: string,
98
+ chainDef: ChainDef,
99
+ dispatch: DispatchFn,
100
+ gateCheck?: GateCheckFn,
101
+ ): Promise<ChainInstance> {
102
+ const instance = this.getInstance(instanceId);
103
+ if (!instance) throw new Error(`Chain instance not found: ${instanceId}`);
104
+ if (instance.status !== 'paused') throw new Error(`Chain is ${instance.status}, not paused`);
105
+
106
+ // Find the step after the paused gate
107
+ const pausedStep = instance.pausedAtGate;
108
+ if (!pausedStep) throw new Error('No paused gate to resume from');
109
+
110
+ const stepIdx = chainDef.steps.findIndex((s) => s.id === pausedStep);
111
+ if (stepIdx < 0) throw new Error(`Paused step ${pausedStep} not found in chain def`);
112
+
113
+ // Mark step as approved, move to next
114
+ instance.status = 'running';
115
+ instance.pausedAtGate = null;
116
+ this.persist(instance);
117
+
118
+ return this.runSteps(chainDef, instance, dispatch, gateCheck, stepIdx + 1);
119
+ }
120
+
121
+ /**
122
+ * Approve a gate-paused step and resume the chain.
123
+ */
124
+ async approve(
125
+ instanceId: string,
126
+ chainDef: ChainDef,
127
+ dispatch: DispatchFn,
128
+ gateCheck?: GateCheckFn,
129
+ ): Promise<ChainInstance> {
130
+ return this.resume(instanceId, chainDef, dispatch, gateCheck);
131
+ }
132
+
133
+ /**
134
+ * Get chain instance status.
135
+ */
136
+ getInstance(instanceId: string): ChainInstance | null {
137
+ const row = this.provider.get<InstanceRow>('SELECT * FROM chain_instances WHERE id = ?', [
138
+ instanceId,
139
+ ]);
140
+ return row ? rowToInstance(row) : null;
141
+ }
142
+
143
+ /**
144
+ * List all chain instances.
145
+ */
146
+ list(limit: number = 20): ChainInstance[] {
147
+ const rows = this.provider.all<InstanceRow>(
148
+ 'SELECT * FROM chain_instances ORDER BY updated_at DESC LIMIT ?',
149
+ [limit],
150
+ );
151
+ return rows.map(rowToInstance);
152
+ }
153
+
154
+ // ─── Core Execution Loop ──────────────────────────────────────────
155
+
156
+ private async runSteps(
157
+ chainDef: ChainDef,
158
+ instance: ChainInstance,
159
+ dispatch: DispatchFn,
160
+ gateCheck: GateCheckFn | undefined,
161
+ startIdx: number,
162
+ ): Promise<ChainInstance> {
163
+ // Steps MUST run sequentially — step N output feeds step N+1 input.
164
+ // Using recursive approach to satisfy no-await-in-loop lint rule.
165
+ return this.runStep(chainDef, instance, dispatch, gateCheck, startIdx);
166
+ }
167
+
168
+ private async runStep(
169
+ chainDef: ChainDef,
170
+ instance: ChainInstance,
171
+ dispatch: DispatchFn,
172
+ gateCheck: GateCheckFn | undefined,
173
+ idx: number,
174
+ ): Promise<ChainInstance> {
175
+ if (idx >= chainDef.steps.length) {
176
+ instance.status = 'completed';
177
+ instance.currentStep = null;
178
+ this.persist(instance);
179
+ return instance;
180
+ }
181
+
182
+ const step = chainDef.steps[idx];
183
+ instance.currentStep = step.id;
184
+ this.persist(instance);
185
+
186
+ const resolvedParams = resolveParams(step.params ?? {}, instance.context);
187
+
188
+ const stepStart = Date.now();
189
+ let result: unknown;
190
+ let stepStatus: StepOutput['status'] = 'completed';
191
+
192
+ try {
193
+ result = await dispatch(step.op, resolvedParams);
194
+ } catch (err) {
195
+ result = { error: (err as Error).message };
196
+ stepStatus = 'failed';
197
+ }
198
+
199
+ const output: StepOutput = {
200
+ stepId: step.id,
201
+ op: step.op,
202
+ result,
203
+ status: stepStatus,
204
+ durationMs: Date.now() - stepStart,
205
+ };
206
+
207
+ instance.stepOutputs.push(output);
208
+ instance.context[step.id] = result;
209
+ if (step.output) instance.context[step.output] = result;
210
+ if (stepStatus === 'completed') instance.stepsCompleted++;
211
+
212
+ // Check gate
213
+ if (step.gate && step.gate !== 'none' && stepStatus === 'completed') {
214
+ const gateResult = await this.evaluateChainGate(step.gate, step.id, result, gateCheck);
215
+ if (!gateResult.passed) {
216
+ if (step.gate === 'user-approval') {
217
+ instance.status = 'paused';
218
+ instance.pausedAtGate = step.id;
219
+ this.persist(instance);
220
+ return instance;
221
+ }
222
+ instance.status = 'failed';
223
+ this.persist(instance);
224
+ return instance;
225
+ }
226
+ }
227
+
228
+ if (stepStatus === 'failed') {
229
+ instance.status = 'failed';
230
+ this.persist(instance);
231
+ return instance;
232
+ }
233
+
234
+ this.persist(instance);
235
+ return this.runStep(chainDef, instance, dispatch, gateCheck, idx + 1);
236
+ }
237
+
238
+ private async evaluateChainGate(
239
+ gate: string,
240
+ stepId: string,
241
+ stepResult: unknown,
242
+ gateCheck?: GateCheckFn,
243
+ ): Promise<{ passed: boolean; message?: string }> {
244
+ switch (gate) {
245
+ case 'user-approval':
246
+ return { passed: false, message: 'Awaiting user approval' };
247
+
248
+ case 'auto-test': {
249
+ const result = stepResult as Record<string, unknown> | null;
250
+ if (!result) return { passed: false, message: 'Step returned no result' };
251
+ if (result.error) return { passed: false, message: `Step error: ${result.error}` };
252
+ return { passed: true };
253
+ }
254
+
255
+ case 'vault-check': {
256
+ if (gateCheck) return gateCheck(gate, stepId, stepResult);
257
+ return { passed: true };
258
+ }
259
+
260
+ default:
261
+ return { passed: true };
262
+ }
263
+ }
264
+
265
+ // ─── Persistence ──────────────────────────────────────────────────
266
+
267
+ private persist(instance: ChainInstance): void {
268
+ instance.updatedAt = new Date().toISOString();
269
+ this.provider.run(
270
+ `INSERT OR REPLACE INTO chain_instances
271
+ (id, chain_id, chain_name, status, current_step, paused_at_gate, input, context, step_outputs, steps_completed, total_steps, created_at, updated_at)
272
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
273
+ [
274
+ instance.id,
275
+ instance.chainId,
276
+ instance.chainName,
277
+ instance.status,
278
+ instance.currentStep,
279
+ instance.pausedAtGate,
280
+ JSON.stringify(instance.input),
281
+ JSON.stringify(instance.context),
282
+ JSON.stringify(instance.stepOutputs),
283
+ instance.stepsCompleted,
284
+ instance.totalSteps,
285
+ instance.createdAt,
286
+ instance.updatedAt,
287
+ ],
288
+ );
289
+ }
290
+ }
291
+
292
+ // ─── Variable Resolution ─────────────────────────────────────────────
293
+
294
+ /**
295
+ * Resolve $variable references in params.
296
+ * $input.url → context.input.url
297
+ * $research.title → context.research.title
298
+ * $stepId → context.stepId (whole object)
299
+ */
300
+ function resolveParams(
301
+ params: Record<string, unknown>,
302
+ context: Record<string, unknown>,
303
+ ): Record<string, unknown> {
304
+ const resolved: Record<string, unknown> = {};
305
+ for (const [key, value] of Object.entries(params)) {
306
+ resolved[key] = resolveValue(value, context);
307
+ }
308
+ return resolved;
309
+ }
310
+
311
+ function resolveValue(value: unknown, context: Record<string, unknown>): unknown {
312
+ if (typeof value === 'string' && value.startsWith('$')) {
313
+ return resolveReference(value.slice(1), context);
314
+ }
315
+ if (Array.isArray(value)) {
316
+ return value.map((v) => resolveValue(v, context));
317
+ }
318
+ if (value !== null && typeof value === 'object') {
319
+ return resolveParams(value as Record<string, unknown>, context);
320
+ }
321
+ return value;
322
+ }
323
+
324
+ function resolveReference(path: string, context: Record<string, unknown>): unknown {
325
+ const parts = path.split('.');
326
+ let current: unknown = context;
327
+ for (const part of parts) {
328
+ if (current === null || current === undefined) return undefined;
329
+ if (typeof current !== 'object') return undefined;
330
+ current = (current as Record<string, unknown>)[part];
331
+ }
332
+ return current;
333
+ }
334
+
335
+ // ─── Row Types ───────────────────────────────────────────────────────
336
+
337
+ interface InstanceRow {
338
+ id: string;
339
+ chain_id: string;
340
+ chain_name: string;
341
+ status: string;
342
+ current_step: string | null;
343
+ paused_at_gate: string | null;
344
+ input: string;
345
+ context: string;
346
+ step_outputs: string;
347
+ steps_completed: number;
348
+ total_steps: number;
349
+ created_at: string;
350
+ updated_at: string;
351
+ }
352
+
353
+ function rowToInstance(row: InstanceRow): ChainInstance {
354
+ return {
355
+ id: row.id,
356
+ chainId: row.chain_id,
357
+ chainName: row.chain_name,
358
+ status: row.status as ChainInstance['status'],
359
+ currentStep: row.current_step,
360
+ pausedAtGate: row.paused_at_gate,
361
+ input: JSON.parse(row.input) as Record<string, unknown>,
362
+ context: JSON.parse(row.context) as Record<string, unknown>,
363
+ stepOutputs: JSON.parse(row.step_outputs) as StepOutput[],
364
+ stepsCompleted: row.steps_completed,
365
+ totalSteps: row.total_steps,
366
+ createdAt: row.created_at,
367
+ updatedAt: row.updated_at,
368
+ };
369
+ }
@@ -0,0 +1,57 @@
1
+ /**
2
+ * Chain types — composable multi-step workflows with data flow.
3
+ */
4
+
5
+ import { z } from 'zod';
6
+
7
+ // ─── Chain Definition (YAML schema) ──────────────────────────────────
8
+
9
+ export const chainStepSchema = z.object({
10
+ id: z.string(),
11
+ op: z.string().describe('Facade op to call'),
12
+ params: z
13
+ .record(z.unknown())
14
+ .optional()
15
+ .describe('Params — use $input.x or $stepId.field for data flow'),
16
+ output: z.string().optional().describe('Key to store step result under'),
17
+ gate: z.enum(['user-approval', 'auto-test', 'vault-check', 'none']).optional(),
18
+ description: z.string().optional(),
19
+ });
20
+
21
+ export const chainDefSchema = z.object({
22
+ id: z.string(),
23
+ name: z.string().optional(),
24
+ description: z.string().optional(),
25
+ steps: z.array(chainStepSchema).min(1),
26
+ });
27
+
28
+ export type ChainDef = z.infer<typeof chainDefSchema>;
29
+ export type ChainStep = z.infer<typeof chainStepSchema>;
30
+
31
+ // ─── Chain Instance (runtime state) ──────────────────────────────────
32
+
33
+ export type ChainStatus = 'running' | 'paused' | 'completed' | 'failed';
34
+
35
+ export interface StepOutput {
36
+ stepId: string;
37
+ op: string;
38
+ result: unknown;
39
+ status: 'completed' | 'failed' | 'skipped';
40
+ durationMs: number;
41
+ }
42
+
43
+ export interface ChainInstance {
44
+ id: string;
45
+ chainId: string;
46
+ chainName: string;
47
+ status: ChainStatus;
48
+ currentStep: string | null;
49
+ pausedAtGate: string | null;
50
+ input: Record<string, unknown>;
51
+ context: Record<string, unknown>;
52
+ stepOutputs: StepOutput[];
53
+ stepsCompleted: number;
54
+ totalSteps: number;
55
+ createdAt: string;
56
+ updatedAt: string;
57
+ }
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,