@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
@@ -1,11 +1,14 @@
1
1
  /**
2
- * Extended memory operations — 8 ops for advanced memory management.
2
+ * Extended memory operations — 18 ops for advanced memory management.
3
3
  *
4
4
  * These complement the 4 base memory ops in core-ops.ts:
5
5
  * memory_search, memory_capture, memory_list, session_capture
6
6
  *
7
- * New ops: memory_delete, memory_stats, memory_export, memory_import,
8
- * memory_prune, memory_deduplicate, memory_topics, memory_by_project
7
+ * CRUD: memory_delete, memory_stats, memory_export, memory_import,
8
+ * memory_prune, memory_deduplicate, memory_topics, memory_by_project
9
+ * Governance (#213): memory_get, session_search, knowledge_audit, smart_capture,
10
+ * knowledge_health, merge_patterns, knowledge_reorganize,
11
+ * list_project_knowledge, list_projects, knowledge_debug
9
12
  */
10
13
 
11
14
  import { z } from 'zod';
@@ -13,7 +16,7 @@ import type { OpDefinition } from '../facades/types.js';
13
16
  import type { AgentRuntime } from './types.js';
14
17
 
15
18
  export function createMemoryExtraOps(runtime: AgentRuntime): OpDefinition[] {
16
- const { vault } = runtime;
19
+ const { vault, brain, curator, linkManager } = runtime;
17
20
 
18
21
  return [
19
22
  {
@@ -116,6 +119,11 @@ export function createMemoryExtraOps(runtime: AgentRuntime): OpDefinition[] {
116
119
  topics: (m.topics as string[]) ?? [],
117
120
  filesModified: (m.filesModified as string[]) ?? [],
118
121
  toolsUsed: (m.toolsUsed as string[]) ?? [],
122
+ intent: (m.intent as string) ?? null,
123
+ decisions: (m.decisions as string[]) ?? [],
124
+ currentState: (m.currentState as string) ?? null,
125
+ nextSteps: (m.nextSteps as string[]) ?? [],
126
+ vaultEntriesReferenced: (m.vaultEntriesReferenced as string[]) ?? [],
119
127
  createdAt: m.createdAt as number,
120
128
  archivedAt: (m.archivedAt as number | null) ?? null,
121
129
  }));
@@ -182,5 +190,305 @@ export function createMemoryExtraOps(runtime: AgentRuntime): OpDefinition[] {
182
190
  return { count: groups.length, projects: groups };
183
191
  },
184
192
  },
193
+
194
+ // ─── Knowledge Governance (#213) ─────────────────────────────────
195
+
196
+ {
197
+ name: 'memory_get',
198
+ description: 'Get a single memory entry by ID.',
199
+ auth: 'read',
200
+ schema: z.object({
201
+ id: z.string().describe('Memory ID'),
202
+ }),
203
+ handler: async (params) => {
204
+ const memory = vault.getMemory(params.id as string);
205
+ if (!memory) return { found: false, id: params.id };
206
+ return memory;
207
+ },
208
+ },
209
+ {
210
+ name: 'session_search',
211
+ description: 'Search session memories with optional includeArchived flag.',
212
+ auth: 'read',
213
+ schema: z.object({
214
+ query: z.string().describe('Search query'),
215
+ includeArchived: z.boolean().optional().default(false),
216
+ intent: z.string().optional().describe('Filter by session intent'),
217
+ limit: z.number().optional().default(10),
218
+ }),
219
+ handler: async (params) => {
220
+ // Use searchMemories with type=session
221
+ const results = vault.searchMemories(params.query as string, {
222
+ type: 'session',
223
+ intent: params.intent as string | undefined,
224
+ limit: params.limit as number,
225
+ });
226
+ // If includeArchived, also search archived
227
+ if (params.includeArchived) {
228
+ try {
229
+ const archived = vault
230
+ .getProvider()
231
+ .all<Record<string, unknown>>(
232
+ "SELECT * FROM memories WHERE type = 'session' AND archived_at IS NOT NULL AND summary LIKE @q ORDER BY created_at DESC LIMIT @limit",
233
+ { q: `%${params.query}%`, limit: params.limit as number },
234
+ );
235
+ // Minimal parsing for archived results
236
+ const archivedMemories = archived.map((r) => ({
237
+ id: r.id,
238
+ summary: r.summary,
239
+ intent: r.intent ?? null,
240
+ createdAt: r.created_at,
241
+ archived: true,
242
+ }));
243
+ return { active: results, archived: archivedMemories };
244
+ } catch {
245
+ return { active: results, archived: [] };
246
+ }
247
+ }
248
+ return { results };
249
+ },
250
+ },
251
+ {
252
+ name: 'knowledge_audit',
253
+ description:
254
+ 'Audit vault knowledge quality — coverage, freshness, tag health, recommendations.',
255
+ auth: 'read',
256
+ handler: async () => {
257
+ const healthAudit = curator.healthAudit();
258
+ const vaultStats = vault.stats();
259
+ const brainStats = brain.getStats();
260
+ return {
261
+ vault: {
262
+ totalEntries: vaultStats.totalEntries,
263
+ byType: vaultStats.byType,
264
+ byDomain: vaultStats.byDomain,
265
+ },
266
+ health: healthAudit,
267
+ brain: brainStats,
268
+ };
269
+ },
270
+ },
271
+ {
272
+ name: 'smart_capture',
273
+ description:
274
+ 'Capture knowledge with auto-classification — infers type, tags, and severity from content.',
275
+ auth: 'write',
276
+ schema: z.object({
277
+ title: z.string(),
278
+ description: z.string(),
279
+ domain: z.string().optional().default('general'),
280
+ context: z.string().optional(),
281
+ why: z.string().optional(),
282
+ }),
283
+ handler: async (params) => {
284
+ const id = `smart-${Date.now()}-${Math.random().toString(36).slice(2, 6)}`;
285
+ // Auto-infer type from keywords
286
+ const desc = (params.description as string).toLowerCase();
287
+ const inferredType: 'pattern' | 'anti-pattern' =
288
+ desc.includes('never') ||
289
+ desc.includes("don't") ||
290
+ desc.includes('avoid') ||
291
+ desc.includes('anti')
292
+ ? 'anti-pattern'
293
+ : 'pattern';
294
+ const inferredSeverity: 'critical' | 'warning' | 'suggestion' =
295
+ desc.includes('must') || desc.includes('critical') || desc.includes('always')
296
+ ? 'critical'
297
+ : desc.includes('should') || desc.includes('important')
298
+ ? 'warning'
299
+ : 'suggestion';
300
+
301
+ const result = brain.enrichAndCapture({
302
+ id,
303
+ type: inferredType,
304
+ domain: params.domain as string,
305
+ title: params.title as string,
306
+ description: params.description as string,
307
+ severity: inferredSeverity,
308
+ context: params.context as string | undefined,
309
+ why: params.why as string | undefined,
310
+ });
311
+
312
+ return {
313
+ ...result,
314
+ inferred: { type: inferredType, severity: inferredSeverity },
315
+ };
316
+ },
317
+ },
318
+ {
319
+ name: 'knowledge_health',
320
+ description: 'Knowledge base health — freshness, staleness, contradictions, coverage gaps.',
321
+ auth: 'read',
322
+ handler: async () => {
323
+ const audit = curator.healthAudit();
324
+ const ageReport = vault.getAgeReport();
325
+ const contradictions = curator.detectContradictions();
326
+ return {
327
+ score: audit.score,
328
+ metrics: audit.metrics,
329
+ ageDistribution: ageReport,
330
+ openContradictions: contradictions.filter((c) => c.status === 'open').length,
331
+ recommendations: audit.recommendations,
332
+ };
333
+ },
334
+ },
335
+ {
336
+ name: 'merge_patterns',
337
+ description: 'Merge two vault entries into one — combines tags, preserves links from both.',
338
+ auth: 'write',
339
+ schema: z.object({
340
+ keepId: z.string().describe('Entry ID to keep (survives the merge)'),
341
+ removeId: z.string().describe('Entry ID to remove (merged into keepId)'),
342
+ }),
343
+ handler: async (params) => {
344
+ const keepEntry = vault.get(params.keepId as string);
345
+ const removeEntry = vault.get(params.removeId as string);
346
+ if (!keepEntry) return { error: `Entry not found: ${params.keepId}` };
347
+ if (!removeEntry) return { error: `Entry not found: ${params.removeId}` };
348
+
349
+ // Merge tags
350
+ const mergedTags = [...new Set([...keepEntry.tags, ...removeEntry.tags])];
351
+
352
+ // Update the kept entry with merged tags and enriched description
353
+ vault.update(params.keepId as string, {
354
+ tags: mergedTags,
355
+ description: keepEntry.description.includes(removeEntry.title)
356
+ ? keepEntry.description
357
+ : `${keepEntry.description}\n\n[Merged from: ${removeEntry.title}] ${removeEntry.description}`,
358
+ });
359
+
360
+ // Transfer links from removed entry to kept entry
361
+ if (linkManager) {
362
+ try {
363
+ const links = linkManager.getLinks(params.removeId as string);
364
+ for (const link of links) {
365
+ const otherId = link.sourceId === params.removeId ? link.targetId : link.sourceId;
366
+ if (otherId !== params.keepId) {
367
+ try {
368
+ linkManager.addLink(
369
+ params.keepId as string,
370
+ otherId,
371
+ link.linkType,
372
+ `merged from ${params.removeId}`,
373
+ );
374
+ } catch {
375
+ /* duplicate link — skip */
376
+ }
377
+ }
378
+ }
379
+ } catch {
380
+ /* link manager ops failed — non-critical */
381
+ }
382
+ }
383
+
384
+ // Remove the merged entry
385
+ vault.remove(params.removeId as string);
386
+
387
+ return {
388
+ merged: true,
389
+ keptId: params.keepId,
390
+ removedId: params.removeId,
391
+ mergedTags,
392
+ };
393
+ },
394
+ },
395
+ {
396
+ name: 'knowledge_reorganize',
397
+ description: 'Re-categorize vault entries — change domain, retag, with dry-run preview.',
398
+ auth: 'write',
399
+ schema: z.object({
400
+ fromDomain: z.string().describe('Current domain to reorganize'),
401
+ toDomain: z.string().describe('Target domain'),
402
+ addTags: z.array(z.string()).optional().describe('Tags to add to all affected entries'),
403
+ removeTags: z
404
+ .array(z.string())
405
+ .optional()
406
+ .describe('Tags to remove from all affected entries'),
407
+ dryRun: z.boolean().optional().default(true).describe('Preview without applying'),
408
+ }),
409
+ handler: async (params) => {
410
+ const entries = vault.list({ limit: 10000 }).filter((e) => e.domain === params.fromDomain);
411
+ const addTags = (params.addTags as string[]) ?? [];
412
+ const removeTags = new Set((params.removeTags as string[]) ?? []);
413
+ const toDomain = params.toDomain as string;
414
+
415
+ if (params.dryRun) {
416
+ return {
417
+ dryRun: true,
418
+ affected: entries.length,
419
+ fromDomain: params.fromDomain,
420
+ toDomain,
421
+ entries: entries.slice(0, 20).map((e) => ({ id: e.id, title: e.title })),
422
+ };
423
+ }
424
+
425
+ let updated = 0;
426
+ for (const entry of entries) {
427
+ const newTags = [...entry.tags.filter((t) => !removeTags.has(t)), ...addTags];
428
+ vault.update(entry.id, { domain: toDomain, tags: [...new Set(newTags)] });
429
+ updated++;
430
+ }
431
+
432
+ return { applied: true, updated, fromDomain: params.fromDomain, toDomain };
433
+ },
434
+ },
435
+ {
436
+ name: 'list_project_knowledge',
437
+ description: 'List vault entries scoped to a project (by tier tag or domain).',
438
+ auth: 'read',
439
+ schema: z.object({
440
+ project: z.string().describe('Project name or path to filter by'),
441
+ limit: z.number().optional().default(50),
442
+ }),
443
+ handler: async () => {
444
+ // Entries with tier='project' or tagged with project name
445
+ const all = vault.list({ limit: 10000 });
446
+ const projectEntries = all.filter((e) => e.tier === 'project' || e.origin === 'user');
447
+ return {
448
+ count: projectEntries.length,
449
+ entries: projectEntries.slice(0, 50).map((e) => ({
450
+ id: e.id,
451
+ title: e.title,
452
+ type: e.type,
453
+ domain: e.domain,
454
+ tier: e.tier,
455
+ })),
456
+ };
457
+ },
458
+ },
459
+ {
460
+ name: 'list_projects',
461
+ description:
462
+ 'List all distinct domains and tiers in the vault — shows knowledge distribution.',
463
+ auth: 'read',
464
+ handler: async () => {
465
+ const stats = vault.stats();
466
+ return {
467
+ domains: Object.entries(stats.byDomain ?? {}).map(([domain, count]) => ({
468
+ domain,
469
+ count,
470
+ })),
471
+ types: Object.entries(stats.byType ?? {}).map(([type, count]) => ({ type, count })),
472
+ total: stats.totalEntries,
473
+ };
474
+ },
475
+ },
476
+ {
477
+ name: 'knowledge_debug',
478
+ description:
479
+ 'Debug knowledge system internals — vault DB stats, brain state, curator state, memory counts.',
480
+ auth: 'admin',
481
+ handler: async () => {
482
+ return {
483
+ vault: {
484
+ stats: vault.stats(),
485
+ recentCount: vault.getRecent(1).length > 0 ? 'has entries' : 'empty',
486
+ },
487
+ brain: brain.getStats(),
488
+ curator: curator.getStatus(),
489
+ memory: vault.memoryStats(),
490
+ };
491
+ },
492
+ },
185
493
  ];
186
494
  }
@@ -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
 
@@ -13,6 +13,7 @@ import { z } from 'zod';
13
13
  import type { OpDefinition } from '../facades/types.js';
14
14
  import type { AgentRuntime } from './types.js';
15
15
  import type { DriftItem, TaskEvidence } from '../planning/planner.js';
16
+ import { collectGitEvidence } from '../planning/evidence-collector.js';
16
17
  import { matchPlaybooks, type PlaybookMatchResult } from '../playbooks/index.js';
17
18
  import { entryToPlaybookDefinition } from '../playbooks/index.js';
18
19
 
@@ -714,5 +715,98 @@ export function createPlanningExtraOps(runtime: AgentRuntime): OpDefinition[] {
714
715
  }
715
716
  },
716
717
  },
718
+
719
+ // ─── Evidence-Based Reconciliation (#206) ─────────────────────
720
+ {
721
+ name: 'plan_reconcile_with_evidence',
722
+ description:
723
+ 'Cross-reference plan tasks against git diff to produce an evidence-based drift report. ' +
724
+ 'Shows which tasks have matching file changes, which are missing, and what unplanned work was done.',
725
+ auth: 'read',
726
+ schema: z.object({
727
+ planId: z.string().describe('Plan ID to verify against git'),
728
+ projectPath: z.string().describe('Project root (must be a git repo)'),
729
+ baseBranch: z
730
+ .string()
731
+ .optional()
732
+ .default('main')
733
+ .describe('Branch to diff against (default: main)'),
734
+ }),
735
+ handler: async (params) => {
736
+ try {
737
+ const plan = planner.get(params.planId as string);
738
+ if (!plan) return { error: `Plan not found: ${params.planId}` };
739
+
740
+ return collectGitEvidence(
741
+ plan,
742
+ params.projectPath as string,
743
+ params.baseBranch as string,
744
+ );
745
+ } catch (err) {
746
+ return { error: (err as Error).message };
747
+ }
748
+ },
749
+ },
750
+
751
+ // ─── Purge Plans (#215) ──────────────────────────────────────────
752
+ {
753
+ name: 'plan_purge',
754
+ description:
755
+ 'Permanently delete plans by mode: "archived" (only archived), "completed" (completed + archived), ' +
756
+ '"stale" (draft/approved older than 24h), or "specific" (by IDs). Use dryRun to preview.',
757
+ auth: 'admin',
758
+ schema: z.object({
759
+ mode: z.enum(['archived', 'completed', 'stale', 'specific']).describe('Purge mode'),
760
+ planIds: z.array(z.string()).optional().describe('Plan IDs for specific mode'),
761
+ dryRun: z.boolean().optional().default(false).describe('Preview without deleting'),
762
+ }),
763
+ handler: async (params) => {
764
+ const mode = params.mode as string;
765
+ const dryRun = params.dryRun as boolean;
766
+ const plans = planner.list();
767
+ const now = Date.now();
768
+ const staleThresholdMs = 24 * 60 * 60 * 1000;
769
+
770
+ let toPurge: typeof plans;
771
+ if (mode === 'archived') {
772
+ toPurge = plans.filter((p) => p.status === 'archived');
773
+ } else if (mode === 'completed') {
774
+ toPurge = plans.filter((p) => p.status === 'completed' || p.status === 'archived');
775
+ } else if (mode === 'stale') {
776
+ toPurge = plans.filter(
777
+ (p) =>
778
+ (p.status === 'draft' || p.status === 'approved' || p.status === 'brainstorming') &&
779
+ p.createdAt &&
780
+ now - p.createdAt > staleThresholdMs,
781
+ );
782
+ } else if (mode === 'specific') {
783
+ const ids = new Set((params.planIds as string[]) ?? []);
784
+ toPurge = plans.filter((p) => ids.has(p.id));
785
+ } else {
786
+ return { error: `Unknown purge mode: ${mode}` };
787
+ }
788
+
789
+ if (dryRun) {
790
+ return {
791
+ dryRun: true,
792
+ mode,
793
+ wouldPurge: toPurge.length,
794
+ plans: toPurge.map((p) => ({ id: p.id, status: p.status, objective: p.objective })),
795
+ };
796
+ }
797
+
798
+ let purged = 0;
799
+ for (const p of toPurge) {
800
+ try {
801
+ planner.remove(p.id);
802
+ purged++;
803
+ } catch {
804
+ // Skip plans that can't be removed
805
+ }
806
+ }
807
+
808
+ return { purged, mode };
809
+ },
810
+ },
717
811
  ];
718
812
  }
@@ -88,7 +88,7 @@ export function createPlaybookOps(runtime: AgentRuntime): OpDefinition[] {
88
88
  validation?: string;
89
89
  }>;
90
90
 
91
- const steps = rawSteps.map((s, i) => ({ ...s, order: i + 1 }));
91
+ const steps = rawSteps.map((s, i) => Object.assign({}, s, { order: i + 1 }));
92
92
  const id =
93
93
  (params.id as string | undefined) ??
94
94
  `playbook-${domain}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
@@ -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,