@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
@@ -71,11 +71,34 @@ const STOP_PATTERNS = new Set([
71
71
  'out-of-the-box',
72
72
  ]);
73
73
 
74
- // ─── Confidence Thresholds ──────────────────────────────────────────
74
+ // ─── Scoring Constants (tunable) ────────────────────────────────────
75
75
 
76
+ /** Confidence thresholds for discrete levels. */
76
77
  const HIGH_CONFIDENCE = 0.75;
77
78
  const MEDIUM_CONFIDENCE = 0.45;
78
79
 
80
+ /** Weights for knowledge item multi-signal scoring. */
81
+ const KNOWLEDGE_WEIGHTS = {
82
+ /** Base FTS/vector score weight. */
83
+ baseScore: 0.4,
84
+ /** Title keyword overlap weight. */
85
+ titleMatch: 0.25,
86
+ /** Tag overlap weight. */
87
+ tagOverlap: 0.2,
88
+ /** Intent/domain alignment weight. */
89
+ intentBoost: 0.15,
90
+ };
91
+
92
+ /** Weights for confidence computation. */
93
+ const CONFIDENCE_WEIGHTS = {
94
+ entitySignalPerEntity: 0.08,
95
+ entitySignalMax: 0.4,
96
+ actionSignal: 0.2,
97
+ knowledgeSignalMultiplier: 0.3,
98
+ sourceDiversityPerExtra: 0.05,
99
+ sourceDiversityMax: 0.1,
100
+ };
101
+
79
102
  // ─── Class ──────────────────────────────────────────────────────────
80
103
 
81
104
  export class ContextEngine {
@@ -120,7 +143,13 @@ export class ContextEngine {
120
143
  if (seen.has(key)) continue;
121
144
  if (type === 'pattern' && STOP_PATTERNS.has(value)) continue;
122
145
  seen.add(key);
123
- entities.push({ type, value, confidence });
146
+ entities.push({
147
+ type,
148
+ value,
149
+ confidence,
150
+ start: match.index,
151
+ end: match.index + match[0].length,
152
+ });
124
153
  }
125
154
  }
126
155
 
@@ -147,15 +176,31 @@ export class ContextEngine {
147
176
  domain,
148
177
  limit: this.config.vaultSearchLimit,
149
178
  });
150
- // Normalize FTS5 -rank scores to 0-1 range
179
+ // Normalize FTS5 -rank scores to 0-1 range, then apply multi-signal scoring
151
180
  const maxScore = vaultResults.length > 0 ? Math.max(...vaultResults.map((r) => r.score)) : 1;
181
+ const promptTokens = new Set(
182
+ prompt
183
+ .toLowerCase()
184
+ .split(/\s+/)
185
+ .filter((t) => t.length >= 3),
186
+ );
152
187
  for (const r of vaultResults) {
188
+ const baseScore = maxScore > 0 ? r.score / maxScore : 0.5;
189
+ const enrichedScore = scoreKnowledgeItem(
190
+ baseScore,
191
+ r.entry.title,
192
+ r.entry.tags,
193
+ promptTokens,
194
+ domain,
195
+ r.entry.domain,
196
+ );
153
197
  items.push({
154
198
  id: r.entry.id,
155
199
  title: r.entry.title,
156
- score: maxScore > 0 ? r.score / maxScore : 0.5,
200
+ score: enrichedScore,
157
201
  source: 'vault',
158
202
  domain: r.entry.domain,
203
+ tags: r.entry.tags,
159
204
  });
160
205
  vaultHits++;
161
206
  }
@@ -252,25 +297,29 @@ export class ContextEngine {
252
297
  ): number {
253
298
  let score = 0;
254
299
 
255
- // Entity signal (0-0.4): more entities = clearer prompt
300
+ // Entity signal: more entities = clearer prompt
256
301
  const entityCount = entities.entities.length;
257
- const entitySignal = Math.min(0.4, entityCount * 0.08);
258
- score += entitySignal;
302
+ score += Math.min(
303
+ CONFIDENCE_WEIGHTS.entitySignalMax,
304
+ entityCount * CONFIDENCE_WEIGHTS.entitySignalPerEntity,
305
+ );
259
306
 
260
- // Action signal (0-0.2): explicit actions boost confidence
307
+ // Action signal: explicit actions boost confidence
261
308
  const actions = entities.byType.action ?? [];
262
- if (actions.length > 0) score += 0.2;
309
+ if (actions.length > 0) score += CONFIDENCE_WEIGHTS.actionSignal;
263
310
 
264
- // Knowledge signal (0-0.3): relevant knowledge found
311
+ // Knowledge signal: relevant knowledge found
265
312
  if (knowledge.items.length > 0) {
266
- const topScore = knowledge.items[0].score;
267
- score += topScore * 0.3;
313
+ score += knowledge.items[0].score * CONFIDENCE_WEIGHTS.knowledgeSignalMultiplier;
268
314
  }
269
315
 
270
- // Source diversity bonus (0-0.1): multiple sources = more confident
316
+ // Source diversity bonus: multiple sources = more confident
271
317
  const sources = new Set(knowledge.items.map((i) => i.source));
272
- if (sources.size >= 2) score += 0.05;
273
- if (sources.size >= 3) score += 0.05;
318
+ const diversityBonus = Math.min(
319
+ CONFIDENCE_WEIGHTS.sourceDiversityMax,
320
+ Math.max(0, sources.size - 1) * CONFIDENCE_WEIGHTS.sourceDiversityPerExtra,
321
+ );
322
+ score += diversityBonus;
274
323
 
275
324
  return Math.min(1, score);
276
325
  }
@@ -300,3 +349,53 @@ export class ContextEngine {
300
349
  return [...domains];
301
350
  }
302
351
  }
352
+
353
+ // ─── Multi-Signal Knowledge Scoring ─────────────────────────────────
354
+
355
+ /**
356
+ * Score a knowledge item using multiple signals:
357
+ * - baseScore: FTS5 rank or vector similarity (0-1)
358
+ * - titleMatch: keyword overlap between prompt and entry title
359
+ * - tagOverlap: how many prompt tokens appear in entry tags
360
+ * - intentBoost: domain alignment between query domain and entry domain
361
+ */
362
+ function scoreKnowledgeItem(
363
+ baseScore: number,
364
+ title: string,
365
+ tags: string[],
366
+ promptTokens: Set<string>,
367
+ queryDomain: string | undefined,
368
+ entryDomain: string | undefined,
369
+ ): number {
370
+ // Title match: fraction of prompt tokens found in title
371
+ const titleTokens = new Set(
372
+ title
373
+ .toLowerCase()
374
+ .split(/\s+/)
375
+ .filter((t) => t.length >= 3),
376
+ );
377
+ let titleOverlap = 0;
378
+ for (const t of promptTokens) {
379
+ if (titleTokens.has(t)) titleOverlap++;
380
+ }
381
+ const titleMatch = promptTokens.size > 0 ? titleOverlap / promptTokens.size : 0;
382
+
383
+ // Tag overlap: fraction of tags that match prompt tokens
384
+ const lowerTags = tags.map((t) => t.toLowerCase());
385
+ let tagHits = 0;
386
+ for (const tag of lowerTags) {
387
+ if (promptTokens.has(tag)) tagHits++;
388
+ }
389
+ const tagOverlap = lowerTags.length > 0 ? tagHits / lowerTags.length : 0;
390
+
391
+ // Intent boost: 1.0 if domains match, 0.0 otherwise
392
+ const intentBoost = queryDomain && entryDomain && queryDomain === entryDomain ? 1.0 : 0.0;
393
+
394
+ return Math.min(
395
+ 1.0,
396
+ baseScore * KNOWLEDGE_WEIGHTS.baseScore +
397
+ titleMatch * KNOWLEDGE_WEIGHTS.titleMatch +
398
+ tagOverlap * KNOWLEDGE_WEIGHTS.tagOverlap +
399
+ intentBoost * KNOWLEDGE_WEIGHTS.intentBoost,
400
+ );
401
+ }
@@ -10,6 +10,10 @@ export interface ExtractedEntity {
10
10
  type: EntityType;
11
11
  value: string;
12
12
  confidence: number;
13
+ /** Start offset in the original prompt. */
14
+ start?: number;
15
+ /** End offset in the original prompt. */
16
+ end?: number;
13
17
  }
14
18
 
15
19
  export interface EntityExtractionResult {
@@ -25,6 +29,7 @@ export interface KnowledgeItem {
25
29
  score: number;
26
30
  source: 'vault' | 'cognee' | 'brain';
27
31
  domain?: string;
32
+ tags?: string[];
28
33
  }
29
34
 
30
35
  export interface KnowledgeRetrievalResult {
@@ -16,6 +16,7 @@ import type {
16
16
  IntentClassification,
17
17
  ModeConfig,
18
18
  MorphResult,
19
+ RoutingAccuracyReport,
19
20
  } from './types.js';
20
21
 
21
22
  // ─── Token Stemming ─────────────────────────────────────────────────
@@ -168,6 +169,16 @@ export class IntentRouter {
168
169
  matched_keywords TEXT NOT NULL DEFAULT '[]',
169
170
  created_at TEXT NOT NULL DEFAULT (datetime('now'))
170
171
  );
172
+
173
+ CREATE TABLE IF NOT EXISTS routing_feedback (
174
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
175
+ routing_log_id INTEGER,
176
+ initial_intent TEXT NOT NULL,
177
+ actual_intent TEXT NOT NULL,
178
+ correction INTEGER NOT NULL DEFAULT 0,
179
+ confidence REAL NOT NULL,
180
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
181
+ );
171
182
  `);
172
183
  }
173
184
 
@@ -314,6 +325,102 @@ export class IntentRouter {
314
325
  }
315
326
  }
316
327
 
328
+ // ─── Routing Feedback ─────────────────────────────────────────────
329
+
330
+ recordRoutingFeedback(input: {
331
+ routingLogId?: number;
332
+ initialIntent: string;
333
+ actualIntent: string;
334
+ confidence: number;
335
+ correction: boolean;
336
+ }): { recorded: boolean; id: number } {
337
+ const result = this.provider.run(
338
+ `INSERT INTO routing_feedback (routing_log_id, initial_intent, actual_intent, correction, confidence)
339
+ VALUES (?, ?, ?, ?, ?)`,
340
+ [
341
+ input.routingLogId ?? null,
342
+ input.initialIntent,
343
+ input.actualIntent,
344
+ input.correction ? 1 : 0,
345
+ input.confidence,
346
+ ],
347
+ );
348
+ return { recorded: true, id: Number(result.lastInsertRowid) };
349
+ }
350
+
351
+ getRoutingAccuracy(periodDays: number = 30): RoutingAccuracyReport {
352
+ const cutoff = new Date(Date.now() - periodDays * 86400000).toISOString();
353
+
354
+ const total = this.provider.get<{ count: number }>(
355
+ 'SELECT COUNT(*) as count FROM routing_feedback WHERE created_at >= ?',
356
+ [cutoff],
357
+ )!.count;
358
+
359
+ const correct = this.provider.get<{ count: number }>(
360
+ 'SELECT COUNT(*) as count FROM routing_feedback WHERE initial_intent = actual_intent AND created_at >= ?',
361
+ [cutoff],
362
+ )!.count;
363
+
364
+ const corrections = this.provider.get<{ count: number }>(
365
+ 'SELECT COUNT(*) as count FROM routing_feedback WHERE correction = 1 AND created_at >= ?',
366
+ [cutoff],
367
+ )!.count;
368
+
369
+ // Common misroutes
370
+ const misrouteRows = this.provider.all<{
371
+ from_intent: string;
372
+ to_intent: string;
373
+ count: number;
374
+ }>(
375
+ `SELECT initial_intent as from_intent, actual_intent as to_intent, COUNT(*) as count
376
+ FROM routing_feedback
377
+ WHERE initial_intent != actual_intent AND created_at >= ?
378
+ GROUP BY initial_intent, actual_intent
379
+ ORDER BY count DESC
380
+ LIMIT 10`,
381
+ [cutoff],
382
+ );
383
+
384
+ // Confidence calibration: group by confidence bucket and check accuracy per bucket
385
+ const calibrationRows = this.provider.all<{ bucket: string; total: number; correct: number }>(
386
+ `SELECT
387
+ CASE
388
+ WHEN confidence >= 0.8 THEN 'high'
389
+ WHEN confidence >= 0.4 THEN 'medium'
390
+ ELSE 'low'
391
+ END as bucket,
392
+ COUNT(*) as total,
393
+ SUM(CASE WHEN initial_intent = actual_intent THEN 1 ELSE 0 END) as correct
394
+ FROM routing_feedback
395
+ WHERE created_at >= ?
396
+ GROUP BY bucket`,
397
+ [cutoff],
398
+ );
399
+
400
+ const calibration: Record<string, { total: number; correct: number; accuracy: number }> = {};
401
+ for (const row of calibrationRows) {
402
+ calibration[row.bucket] = {
403
+ total: row.total,
404
+ correct: row.correct,
405
+ accuracy: row.total > 0 ? Math.round((row.correct / row.total) * 100) : 0,
406
+ };
407
+ }
408
+
409
+ return {
410
+ periodDays,
411
+ total,
412
+ correct,
413
+ accuracy: total > 0 ? Math.round((correct / total) * 100) : 100,
414
+ corrections,
415
+ commonMisroutes: misrouteRows.map((r) => ({
416
+ from: r.from_intent,
417
+ to: r.to_intent,
418
+ count: r.count,
419
+ })),
420
+ confidenceCalibration: calibration,
421
+ };
422
+ }
423
+
317
424
  // ─── Analytics ──────────────────────────────────────────────────────
318
425
 
319
426
  getRoutingStats(): {
@@ -100,3 +100,13 @@ export interface MorphResult {
100
100
  currentMode: OperationalMode;
101
101
  behaviorRules: string[];
102
102
  }
103
+
104
+ export interface RoutingAccuracyReport {
105
+ periodDays: number;
106
+ total: number;
107
+ correct: number;
108
+ accuracy: number;
109
+ corrections: number;
110
+ commonMisroutes: Array<{ from: string; to: string; count: number }>;
111
+ confidenceCalibration: Record<string, { total: number; correct: number; accuracy: number }>;
112
+ }
@@ -0,0 +1,86 @@
1
+ /**
2
+ * Classifier — LLM-based auto-categorization for vault entries.
3
+ *
4
+ * Suggests category, severity, and additional tags based on entry content.
5
+ * Graceful degradation: returns empty suggestions when LLM is unavailable.
6
+ */
7
+
8
+ import type { LLMClient } from '../llm/llm-client.js';
9
+ import type { IntelligenceEntry } from '../intelligence/types.js';
10
+
11
+ // ─── Types ───────────────────────────────────────────────────────────
12
+
13
+ export interface ClassificationResult {
14
+ classified: boolean;
15
+ suggestedDomain: string | null;
16
+ suggestedSeverity: string | null;
17
+ suggestedTags: string[];
18
+ confidence: number;
19
+ error?: string;
20
+ }
21
+
22
+ // ─── Classify ────────────────────────────────────────────────────────
23
+
24
+ const CLASSIFY_PROMPT = `Classify this knowledge entry. Suggest the best domain, severity, and 3-5 tags.
25
+
26
+ **Title:** {title}
27
+ **Type:** {type}
28
+ **Current tags:** {tags}
29
+ **Description:** {description}
30
+
31
+ Respond with ONLY this JSON (no markdown fences):
32
+ {"domain":"string","severity":"critical|warning|suggestion","tags":["tag1","tag2","tag3"],"confidence":0.0-1.0}
33
+
34
+ Rules:
35
+ - domain: a single word (e.g., architecture, testing, security, design, performance, accessibility)
36
+ - severity: critical = must-know, warning = important, suggestion = nice-to-know
37
+ - tags: lowercase, hyphenated, specific and useful for search
38
+ - confidence: how sure you are about this classification (0.0-1.0)`;
39
+
40
+ export async function classifyEntry(
41
+ entry: IntelligenceEntry,
42
+ llm: LLMClient | null,
43
+ ): Promise<ClassificationResult> {
44
+ const fallback: ClassificationResult = {
45
+ classified: false,
46
+ suggestedDomain: null,
47
+ suggestedSeverity: null,
48
+ suggestedTags: [],
49
+ confidence: 0,
50
+ };
51
+
52
+ if (!llm) return fallback;
53
+
54
+ const prompt = CLASSIFY_PROMPT.replace('{title}', entry.title)
55
+ .replace('{type}', entry.type)
56
+ .replace('{tags}', entry.tags.join(', ') || 'none')
57
+ .replace('{description}', entry.description);
58
+
59
+ try {
60
+ const result = await llm.complete({
61
+ systemPrompt: 'You are a knowledge classifier. Respond only with JSON.',
62
+ userPrompt: prompt,
63
+ temperature: 0.1,
64
+ maxTokens: 300,
65
+ caller: 'classifier',
66
+ task: 'classify',
67
+ });
68
+
69
+ const parsed = JSON.parse(result.text) as {
70
+ domain: string;
71
+ severity: string;
72
+ tags: string[];
73
+ confidence: number;
74
+ };
75
+
76
+ return {
77
+ classified: true,
78
+ suggestedDomain: parsed.domain ?? null,
79
+ suggestedSeverity: parsed.severity ?? null,
80
+ suggestedTags: parsed.tags ?? [],
81
+ confidence: parsed.confidence ?? 0.5,
82
+ };
83
+ } catch (err) {
84
+ return { ...fallback, error: (err as Error).message };
85
+ }
86
+ }
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Quality Gate — LLM-based entry quality evaluation.
3
+ *
4
+ * Evaluates vault entries on 5 criteria (novelty, actionability, specificity,
5
+ * relevance, informationDensity). Entries scoring below threshold are rejected.
6
+ *
7
+ * Graceful degradation: returns ACCEPT when LLM is unavailable.
8
+ */
9
+
10
+ import type { LLMClient } from '../llm/llm-client.js';
11
+ import type { IntelligenceEntry } from '../intelligence/types.js';
12
+
13
+ // ─── Types ───────────────────────────────────────────────────────────
14
+
15
+ export type QualityVerdict = 'ACCEPT' | 'REJECT';
16
+
17
+ export interface QualityScores {
18
+ novelty: number;
19
+ actionability: number;
20
+ specificity: number;
21
+ relevance: number;
22
+ informationDensity: number;
23
+ }
24
+
25
+ export interface QualityResult {
26
+ evaluated: boolean;
27
+ verdict: QualityVerdict;
28
+ overallScore: number;
29
+ scores: QualityScores;
30
+ reasoning: string;
31
+ rejectReasons?: string[];
32
+ error?: string;
33
+ }
34
+
35
+ // ─── Constants ───────────────────────────────────────────────────────
36
+
37
+ const REJECT_THRESHOLD = 50;
38
+ const CRITICAL_LOW = 15;
39
+
40
+ const QUALITY_PROMPT = `You are a strict knowledge base curator deciding whether an entry deserves to be in a high-quality vault.
41
+
42
+ Your job is to REJECT junk and keep the vault clean. Be ruthless but fair.
43
+
44
+ ## Entry Under Review
45
+ - **Type:** {type}
46
+ - **Title:** {title}
47
+ - **Tags:** {tags}
48
+ - **Description:** {description}
49
+ {why}{example}{context}
50
+
51
+ ## Scoring Criteria (each 0-100)
52
+
53
+ 1. **novelty** — Is this genuinely new knowledge or a truism? ("Write clean code" = 5, "Use driver adapter pattern to avoid Prisma lock-in" = 90)
54
+ 2. **actionability** — Can someone act on this? Vague advice = low, specific do/don't = high
55
+ 3. **specificity** — Is this specific to a real context or generic fluff?
56
+ 4. **relevance** — Does this belong in a technical knowledge vault?
57
+ 5. **informationDensity** — Is there real substance or mostly filler?
58
+
59
+ ## Verdict Rules
60
+ - Overall score = average of all 5 criteria
61
+ - **REJECT** if overall < ${REJECT_THRESHOLD}, or if ANY criterion scores <= ${CRITICAL_LOW}
62
+ - **ACCEPT** otherwise
63
+ - When in doubt, REJECT.
64
+
65
+ Respond with ONLY this JSON (no markdown fences):
66
+ {"verdict":"ACCEPT or REJECT","overallScore":0-100,"scores":{"novelty":N,"actionability":N,"specificity":N,"relevance":N,"informationDensity":N},"reasoning":"2-3 sentences","rejectReasons":["reason1"]}`;
67
+
68
+ // ─── Evaluate ────────────────────────────────────────────────────────
69
+
70
+ export async function evaluateQuality(
71
+ entry: IntelligenceEntry,
72
+ llm: LLMClient | null,
73
+ ): Promise<QualityResult> {
74
+ const fallback: QualityResult = {
75
+ evaluated: false,
76
+ verdict: 'ACCEPT',
77
+ overallScore: 50,
78
+ scores: {
79
+ novelty: 50,
80
+ actionability: 50,
81
+ specificity: 50,
82
+ relevance: 50,
83
+ informationDensity: 50,
84
+ },
85
+ reasoning: 'LLM unavailable — defaulting to accept',
86
+ };
87
+
88
+ if (!llm) return fallback;
89
+
90
+ const prompt = QUALITY_PROMPT.replace('{type}', entry.type)
91
+ .replace('{title}', entry.title)
92
+ .replace('{tags}', entry.tags.join(', ') || 'none')
93
+ .replace('{description}', entry.description)
94
+ .replace('{why}', entry.why ? `- **Why:** ${entry.why}\n` : '')
95
+ .replace('{example}', entry.example ? `- **Example:** ${entry.example}\n` : '')
96
+ .replace('{context}', entry.context ? `- **Context:** ${entry.context}\n` : '');
97
+
98
+ try {
99
+ const result = await llm.complete({
100
+ systemPrompt: 'You are a knowledge quality evaluator. Respond only with JSON.',
101
+ userPrompt: prompt,
102
+ temperature: 0.1,
103
+ maxTokens: 500,
104
+ caller: 'quality-gate',
105
+ task: 'evaluate',
106
+ });
107
+
108
+ const parsed = JSON.parse(result.text) as {
109
+ verdict: string;
110
+ overallScore: number;
111
+ scores: QualityScores;
112
+ reasoning: string;
113
+ rejectReasons?: string[];
114
+ };
115
+
116
+ return {
117
+ evaluated: true,
118
+ verdict: (parsed.verdict === 'REJECT' ? 'REJECT' : 'ACCEPT') as QualityVerdict,
119
+ overallScore: parsed.overallScore ?? 50,
120
+ scores: parsed.scores ?? fallback.scores,
121
+ reasoning: parsed.reasoning ?? '',
122
+ rejectReasons: parsed.rejectReasons,
123
+ };
124
+ } catch (err) {
125
+ return { ...fallback, error: (err as Error).message };
126
+ }
127
+ }
@@ -11,12 +11,6 @@ export {
11
11
 
12
12
  export { loadDomainPack, loadDomainPacksFromConfig, resolveDependencies } from './loader.js';
13
13
 
14
- export { installKnowledge, type KnowledgeInstallResult } from './knowledge-installer.js';
15
-
16
- export { installSkills, type SkillsInstallResult } from './skills-installer.js';
17
-
18
- export { injectDomainRules, removeDomainRules } from './inject-rules.js';
19
-
20
14
  export {
21
15
  type PackRuntime,
22
16
  type PackProjectContext,
@@ -18,7 +18,8 @@ export async function loadDomainPack(packageName: string): Promise<DomainPackMan
18
18
  mod = await import(packageName);
19
19
  } catch (err) {
20
20
  throw new Error(
21
- `Failed to import domain pack "${packageName}": ${err instanceof Error ? err.message : String(err)}`, { cause: err },
21
+ `Failed to import domain pack "${packageName}": ${err instanceof Error ? err.message : String(err)}`,
22
+ { cause: err },
22
23
  );
23
24
  }
24
25
 
@@ -33,10 +34,29 @@ export async function loadDomainPack(packageName: string): Promise<DomainPackMan
33
34
  throw new Error(`Domain pack "${packageName}" failed validation: ${result.errors.message}`);
34
35
  }
35
36
 
36
- return {
37
- ...result.data,
38
- packageName,
39
- };
37
+ const manifest: DomainPackManifest = { ...result.data, packageName };
38
+
39
+ // Warn if pack's engine requirement is mismatched
40
+ try {
41
+ const pkgJson = await import(`${packageName}/package.json`, { with: { type: 'json' } })
42
+ .then((m) => m.default)
43
+ .catch(() => null);
44
+ const peerCore = pkgJson?.peerDependencies?.['@soleri/core'];
45
+ if (peerCore) {
46
+ const requiredMajor = parseInt(peerCore.replace(/[^0-9]/g, ''), 10);
47
+ const { ENGINE_MAJOR_VERSION } = await import('../engine/module-manifest.js');
48
+ if (requiredMajor && ENGINE_MAJOR_VERSION && requiredMajor > ENGINE_MAJOR_VERSION) {
49
+ console.error(
50
+ `[warn] Domain pack "${packageName}" requires @soleri/core ${peerCore} ` +
51
+ `but engine is v${ENGINE_MAJOR_VERSION}. Upgrade @soleri/core.`,
52
+ );
53
+ }
54
+ }
55
+ } catch {
56
+ // Version check is best-effort — don't block loading
57
+ }
58
+
59
+ return manifest;
40
60
  }
41
61
 
42
62
  /**
@@ -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
  // ---------------------------------------------------------------------------