@soleri/core 2.4.0 → 2.5.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 (300) hide show
  1. package/dist/brain/brain.d.ts +7 -0
  2. package/dist/brain/brain.d.ts.map +1 -1
  3. package/dist/brain/brain.js +56 -9
  4. package/dist/brain/brain.js.map +1 -1
  5. package/dist/brain/types.d.ts +2 -2
  6. package/dist/brain/types.d.ts.map +1 -1
  7. package/dist/cognee/client.d.ts +3 -0
  8. package/dist/cognee/client.d.ts.map +1 -1
  9. package/dist/cognee/client.js +17 -0
  10. package/dist/cognee/client.js.map +1 -1
  11. package/dist/cognee/sync-manager.d.ts +94 -0
  12. package/dist/cognee/sync-manager.d.ts.map +1 -0
  13. package/dist/cognee/sync-manager.js +293 -0
  14. package/dist/cognee/sync-manager.js.map +1 -0
  15. package/dist/curator/curator.d.ts +8 -1
  16. package/dist/curator/curator.d.ts.map +1 -1
  17. package/dist/curator/curator.js +64 -1
  18. package/dist/curator/curator.js.map +1 -1
  19. package/dist/errors/classify.d.ts +13 -0
  20. package/dist/errors/classify.d.ts.map +1 -0
  21. package/dist/errors/classify.js +97 -0
  22. package/dist/errors/classify.js.map +1 -0
  23. package/dist/errors/index.d.ts +6 -0
  24. package/dist/errors/index.d.ts.map +1 -0
  25. package/dist/errors/index.js +4 -0
  26. package/dist/errors/index.js.map +1 -0
  27. package/dist/errors/retry.d.ts +40 -0
  28. package/dist/errors/retry.d.ts.map +1 -0
  29. package/dist/errors/retry.js +97 -0
  30. package/dist/errors/retry.js.map +1 -0
  31. package/dist/errors/types.d.ts +48 -0
  32. package/dist/errors/types.d.ts.map +1 -0
  33. package/dist/errors/types.js +59 -0
  34. package/dist/errors/types.js.map +1 -0
  35. package/dist/index.d.ts +25 -5
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +21 -3
  38. package/dist/index.js.map +1 -1
  39. package/dist/intake/content-classifier.d.ts +14 -0
  40. package/dist/intake/content-classifier.d.ts.map +1 -0
  41. package/dist/intake/content-classifier.js +125 -0
  42. package/dist/intake/content-classifier.js.map +1 -0
  43. package/dist/intake/dedup-gate.d.ts +17 -0
  44. package/dist/intake/dedup-gate.d.ts.map +1 -0
  45. package/dist/intake/dedup-gate.js +66 -0
  46. package/dist/intake/dedup-gate.js.map +1 -0
  47. package/dist/intake/intake-pipeline.d.ts +63 -0
  48. package/dist/intake/intake-pipeline.d.ts.map +1 -0
  49. package/dist/intake/intake-pipeline.js +373 -0
  50. package/dist/intake/intake-pipeline.js.map +1 -0
  51. package/dist/intake/types.d.ts +65 -0
  52. package/dist/intake/types.d.ts.map +1 -0
  53. package/dist/intake/types.js +3 -0
  54. package/dist/intake/types.js.map +1 -0
  55. package/dist/intelligence/loader.js +1 -1
  56. package/dist/intelligence/loader.js.map +1 -1
  57. package/dist/intelligence/types.d.ts +3 -1
  58. package/dist/intelligence/types.d.ts.map +1 -1
  59. package/dist/loop/loop-manager.d.ts +58 -7
  60. package/dist/loop/loop-manager.d.ts.map +1 -1
  61. package/dist/loop/loop-manager.js +280 -6
  62. package/dist/loop/loop-manager.js.map +1 -1
  63. package/dist/loop/types.d.ts +69 -1
  64. package/dist/loop/types.d.ts.map +1 -1
  65. package/dist/loop/types.js +4 -1
  66. package/dist/loop/types.js.map +1 -1
  67. package/dist/persistence/index.d.ts +3 -0
  68. package/dist/persistence/index.d.ts.map +1 -0
  69. package/dist/persistence/index.js +2 -0
  70. package/dist/persistence/index.js.map +1 -0
  71. package/dist/persistence/sqlite-provider.d.ts +25 -0
  72. package/dist/persistence/sqlite-provider.d.ts.map +1 -0
  73. package/dist/persistence/sqlite-provider.js +59 -0
  74. package/dist/persistence/sqlite-provider.js.map +1 -0
  75. package/dist/persistence/types.d.ts +36 -0
  76. package/dist/persistence/types.d.ts.map +1 -0
  77. package/dist/persistence/types.js +8 -0
  78. package/dist/persistence/types.js.map +1 -0
  79. package/dist/planning/gap-analysis.d.ts +47 -4
  80. package/dist/planning/gap-analysis.d.ts.map +1 -1
  81. package/dist/planning/gap-analysis.js +190 -13
  82. package/dist/planning/gap-analysis.js.map +1 -1
  83. package/dist/planning/gap-types.d.ts +1 -1
  84. package/dist/planning/gap-types.d.ts.map +1 -1
  85. package/dist/planning/gap-types.js.map +1 -1
  86. package/dist/planning/planner.d.ts +277 -9
  87. package/dist/planning/planner.d.ts.map +1 -1
  88. package/dist/planning/planner.js +611 -46
  89. package/dist/planning/planner.js.map +1 -1
  90. package/dist/playbooks/generic/brainstorming.d.ts +9 -0
  91. package/dist/playbooks/generic/brainstorming.d.ts.map +1 -0
  92. package/dist/playbooks/generic/brainstorming.js +105 -0
  93. package/dist/playbooks/generic/brainstorming.js.map +1 -0
  94. package/dist/playbooks/generic/code-review.d.ts +11 -0
  95. package/dist/playbooks/generic/code-review.d.ts.map +1 -0
  96. package/dist/playbooks/generic/code-review.js +176 -0
  97. package/dist/playbooks/generic/code-review.js.map +1 -0
  98. package/dist/playbooks/generic/subagent-execution.d.ts +9 -0
  99. package/dist/playbooks/generic/subagent-execution.d.ts.map +1 -0
  100. package/dist/playbooks/generic/subagent-execution.js +68 -0
  101. package/dist/playbooks/generic/subagent-execution.js.map +1 -0
  102. package/dist/playbooks/generic/systematic-debugging.d.ts +9 -0
  103. package/dist/playbooks/generic/systematic-debugging.d.ts.map +1 -0
  104. package/dist/playbooks/generic/systematic-debugging.js +87 -0
  105. package/dist/playbooks/generic/systematic-debugging.js.map +1 -0
  106. package/dist/playbooks/generic/tdd.d.ts +9 -0
  107. package/dist/playbooks/generic/tdd.d.ts.map +1 -0
  108. package/dist/playbooks/generic/tdd.js +70 -0
  109. package/dist/playbooks/generic/tdd.js.map +1 -0
  110. package/dist/playbooks/generic/verification.d.ts +9 -0
  111. package/dist/playbooks/generic/verification.d.ts.map +1 -0
  112. package/dist/playbooks/generic/verification.js +74 -0
  113. package/dist/playbooks/generic/verification.js.map +1 -0
  114. package/dist/playbooks/index.d.ts +4 -0
  115. package/dist/playbooks/index.d.ts.map +1 -0
  116. package/dist/playbooks/index.js +5 -0
  117. package/dist/playbooks/index.js.map +1 -0
  118. package/dist/playbooks/playbook-registry.d.ts +42 -0
  119. package/dist/playbooks/playbook-registry.d.ts.map +1 -0
  120. package/dist/playbooks/playbook-registry.js +227 -0
  121. package/dist/playbooks/playbook-registry.js.map +1 -0
  122. package/dist/playbooks/playbook-seeder.d.ts +47 -0
  123. package/dist/playbooks/playbook-seeder.d.ts.map +1 -0
  124. package/dist/playbooks/playbook-seeder.js +104 -0
  125. package/dist/playbooks/playbook-seeder.js.map +1 -0
  126. package/dist/playbooks/playbook-types.d.ts +132 -0
  127. package/dist/playbooks/playbook-types.d.ts.map +1 -0
  128. package/dist/playbooks/playbook-types.js +12 -0
  129. package/dist/playbooks/playbook-types.js.map +1 -0
  130. package/dist/project/project-registry.d.ts.map +1 -1
  131. package/dist/project/project-registry.js +9 -11
  132. package/dist/project/project-registry.js.map +1 -1
  133. package/dist/prompts/index.d.ts +4 -0
  134. package/dist/prompts/index.d.ts.map +1 -0
  135. package/dist/prompts/index.js +3 -0
  136. package/dist/prompts/index.js.map +1 -0
  137. package/dist/prompts/parser.d.ts +17 -0
  138. package/dist/prompts/parser.d.ts.map +1 -0
  139. package/dist/prompts/parser.js +47 -0
  140. package/dist/prompts/parser.js.map +1 -0
  141. package/dist/prompts/template-manager.d.ts +25 -0
  142. package/dist/prompts/template-manager.d.ts.map +1 -0
  143. package/dist/prompts/template-manager.js +71 -0
  144. package/dist/prompts/template-manager.js.map +1 -0
  145. package/dist/prompts/types.d.ts +26 -0
  146. package/dist/prompts/types.d.ts.map +1 -0
  147. package/dist/prompts/types.js +5 -0
  148. package/dist/prompts/types.js.map +1 -0
  149. package/dist/runtime/admin-extra-ops.d.ts +5 -3
  150. package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
  151. package/dist/runtime/admin-extra-ops.js +322 -11
  152. package/dist/runtime/admin-extra-ops.js.map +1 -1
  153. package/dist/runtime/admin-ops.d.ts.map +1 -1
  154. package/dist/runtime/admin-ops.js +10 -3
  155. package/dist/runtime/admin-ops.js.map +1 -1
  156. package/dist/runtime/capture-ops.d.ts.map +1 -1
  157. package/dist/runtime/capture-ops.js +20 -2
  158. package/dist/runtime/capture-ops.js.map +1 -1
  159. package/dist/runtime/cognee-sync-ops.d.ts +12 -0
  160. package/dist/runtime/cognee-sync-ops.d.ts.map +1 -0
  161. package/dist/runtime/cognee-sync-ops.js +55 -0
  162. package/dist/runtime/cognee-sync-ops.js.map +1 -0
  163. package/dist/runtime/core-ops.d.ts +8 -6
  164. package/dist/runtime/core-ops.d.ts.map +1 -1
  165. package/dist/runtime/core-ops.js +226 -9
  166. package/dist/runtime/core-ops.js.map +1 -1
  167. package/dist/runtime/curator-extra-ops.d.ts +2 -2
  168. package/dist/runtime/curator-extra-ops.d.ts.map +1 -1
  169. package/dist/runtime/curator-extra-ops.js +15 -3
  170. package/dist/runtime/curator-extra-ops.js.map +1 -1
  171. package/dist/runtime/domain-ops.js +2 -2
  172. package/dist/runtime/domain-ops.js.map +1 -1
  173. package/dist/runtime/grading-ops.d.ts.map +1 -1
  174. package/dist/runtime/grading-ops.js.map +1 -1
  175. package/dist/runtime/intake-ops.d.ts +14 -0
  176. package/dist/runtime/intake-ops.d.ts.map +1 -0
  177. package/dist/runtime/intake-ops.js +110 -0
  178. package/dist/runtime/intake-ops.js.map +1 -0
  179. package/dist/runtime/loop-ops.d.ts +5 -4
  180. package/dist/runtime/loop-ops.d.ts.map +1 -1
  181. package/dist/runtime/loop-ops.js +84 -12
  182. package/dist/runtime/loop-ops.js.map +1 -1
  183. package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -1
  184. package/dist/runtime/memory-cross-project-ops.js.map +1 -1
  185. package/dist/runtime/memory-extra-ops.js +5 -5
  186. package/dist/runtime/memory-extra-ops.js.map +1 -1
  187. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  188. package/dist/runtime/orchestrate-ops.js +8 -2
  189. package/dist/runtime/orchestrate-ops.js.map +1 -1
  190. package/dist/runtime/planning-extra-ops.d.ts +13 -5
  191. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  192. package/dist/runtime/planning-extra-ops.js +381 -18
  193. package/dist/runtime/planning-extra-ops.js.map +1 -1
  194. package/dist/runtime/playbook-ops.d.ts +14 -0
  195. package/dist/runtime/playbook-ops.d.ts.map +1 -0
  196. package/dist/runtime/playbook-ops.js +141 -0
  197. package/dist/runtime/playbook-ops.js.map +1 -0
  198. package/dist/runtime/project-ops.d.ts.map +1 -1
  199. package/dist/runtime/project-ops.js +7 -2
  200. package/dist/runtime/project-ops.js.map +1 -1
  201. package/dist/runtime/runtime.d.ts.map +1 -1
  202. package/dist/runtime/runtime.js +27 -8
  203. package/dist/runtime/runtime.js.map +1 -1
  204. package/dist/runtime/types.d.ts +8 -0
  205. package/dist/runtime/types.d.ts.map +1 -1
  206. package/dist/runtime/vault-extra-ops.d.ts +3 -2
  207. package/dist/runtime/vault-extra-ops.d.ts.map +1 -1
  208. package/dist/runtime/vault-extra-ops.js +345 -4
  209. package/dist/runtime/vault-extra-ops.js.map +1 -1
  210. package/dist/vault/playbook.d.ts +34 -0
  211. package/dist/vault/playbook.d.ts.map +1 -0
  212. package/dist/vault/playbook.js +60 -0
  213. package/dist/vault/playbook.js.map +1 -0
  214. package/dist/vault/vault.d.ts +31 -32
  215. package/dist/vault/vault.d.ts.map +1 -1
  216. package/dist/vault/vault.js +201 -181
  217. package/dist/vault/vault.js.map +1 -1
  218. package/package.json +7 -3
  219. package/src/__tests__/admin-extra-ops.test.ts +62 -15
  220. package/src/__tests__/admin-ops.test.ts +2 -2
  221. package/src/__tests__/brain.test.ts +3 -3
  222. package/src/__tests__/cognee-integration.test.ts +80 -0
  223. package/src/__tests__/cognee-sync-manager.test.ts +103 -0
  224. package/src/__tests__/core-ops.test.ts +30 -4
  225. package/src/__tests__/curator-extra-ops.test.ts +24 -2
  226. package/src/__tests__/errors.test.ts +388 -0
  227. package/src/__tests__/grading-ops.test.ts +28 -7
  228. package/src/__tests__/intake-pipeline.test.ts +162 -0
  229. package/src/__tests__/loop-ops.test.ts +74 -3
  230. package/src/__tests__/memory-cross-project-ops.test.ts +3 -1
  231. package/src/__tests__/orchestrate-ops.test.ts +8 -3
  232. package/src/__tests__/persistence.test.ts +225 -0
  233. package/src/__tests__/planner.test.ts +99 -21
  234. package/src/__tests__/planning-extra-ops.test.ts +168 -10
  235. package/src/__tests__/playbook-registry.test.ts +326 -0
  236. package/src/__tests__/playbook-seeder.test.ts +163 -0
  237. package/src/__tests__/playbook.test.ts +389 -0
  238. package/src/__tests__/project-ops.test.ts +18 -4
  239. package/src/__tests__/template-manager.test.ts +222 -0
  240. package/src/__tests__/vault-extra-ops.test.ts +82 -7
  241. package/src/brain/brain.ts +71 -9
  242. package/src/brain/types.ts +2 -2
  243. package/src/cognee/client.ts +18 -0
  244. package/src/cognee/sync-manager.ts +389 -0
  245. package/src/curator/curator.ts +88 -7
  246. package/src/errors/classify.ts +102 -0
  247. package/src/errors/index.ts +5 -0
  248. package/src/errors/retry.ts +132 -0
  249. package/src/errors/types.ts +81 -0
  250. package/src/index.ts +114 -3
  251. package/src/intake/content-classifier.ts +146 -0
  252. package/src/intake/dedup-gate.ts +92 -0
  253. package/src/intake/intake-pipeline.ts +503 -0
  254. package/src/intake/types.ts +69 -0
  255. package/src/intelligence/loader.ts +1 -1
  256. package/src/intelligence/types.ts +3 -1
  257. package/src/loop/loop-manager.ts +325 -7
  258. package/src/loop/types.ts +72 -1
  259. package/src/persistence/index.ts +7 -0
  260. package/src/persistence/sqlite-provider.ts +62 -0
  261. package/src/persistence/types.ts +44 -0
  262. package/src/planning/gap-analysis.ts +286 -17
  263. package/src/planning/gap-types.ts +4 -1
  264. package/src/planning/planner.ts +828 -55
  265. package/src/playbooks/generic/brainstorming.ts +110 -0
  266. package/src/playbooks/generic/code-review.ts +181 -0
  267. package/src/playbooks/generic/subagent-execution.ts +74 -0
  268. package/src/playbooks/generic/systematic-debugging.ts +92 -0
  269. package/src/playbooks/generic/tdd.ts +75 -0
  270. package/src/playbooks/generic/verification.ts +79 -0
  271. package/src/playbooks/index.ts +27 -0
  272. package/src/playbooks/playbook-registry.ts +284 -0
  273. package/src/playbooks/playbook-seeder.ts +119 -0
  274. package/src/playbooks/playbook-types.ts +162 -0
  275. package/src/project/project-registry.ts +29 -17
  276. package/src/prompts/index.ts +3 -0
  277. package/src/prompts/parser.ts +59 -0
  278. package/src/prompts/template-manager.ts +77 -0
  279. package/src/prompts/types.ts +28 -0
  280. package/src/runtime/admin-extra-ops.ts +358 -13
  281. package/src/runtime/admin-ops.ts +17 -6
  282. package/src/runtime/capture-ops.ts +25 -6
  283. package/src/runtime/cognee-sync-ops.ts +63 -0
  284. package/src/runtime/core-ops.ts +258 -8
  285. package/src/runtime/curator-extra-ops.ts +17 -3
  286. package/src/runtime/domain-ops.ts +2 -2
  287. package/src/runtime/grading-ops.ts +11 -2
  288. package/src/runtime/intake-ops.ts +126 -0
  289. package/src/runtime/loop-ops.ts +96 -13
  290. package/src/runtime/memory-cross-project-ops.ts +1 -2
  291. package/src/runtime/memory-extra-ops.ts +5 -5
  292. package/src/runtime/orchestrate-ops.ts +8 -2
  293. package/src/runtime/planning-extra-ops.ts +414 -23
  294. package/src/runtime/playbook-ops.ts +169 -0
  295. package/src/runtime/project-ops.ts +9 -3
  296. package/src/runtime/runtime.ts +35 -9
  297. package/src/runtime/types.ts +8 -0
  298. package/src/runtime/vault-extra-ops.ts +385 -4
  299. package/src/vault/playbook.ts +87 -0
  300. package/src/vault/vault.ts +301 -235
@@ -91,7 +91,9 @@ export function createProjectOps(runtime: AgentRuntime): OpDefinition[] {
91
91
  auth: 'write',
92
92
  schema: z.object({
93
93
  projectId: z.string().describe('Project ID to add the rule to'),
94
- category: z.enum(['behavior', 'preference', 'restriction', 'convention']).describe('Rule category'),
94
+ category: z
95
+ .enum(['behavior', 'preference', 'restriction', 'convention'])
96
+ .describe('Rule category'),
95
97
  text: z.string().describe('Rule text'),
96
98
  priority: z.number().default(0).describe('Priority (higher = more important)'),
97
99
  }),
@@ -143,7 +145,10 @@ export function createProjectOps(runtime: AgentRuntime): OpDefinition[] {
143
145
  schema: z.object({
144
146
  sourceId: z.string().describe('Source project ID'),
145
147
  targetId: z.string().describe('Target project ID'),
146
- linkType: z.enum(['related', 'parent', 'child', 'fork']).optional().describe('Specific link type to remove'),
148
+ linkType: z
149
+ .enum(['related', 'parent', 'child', 'fork'])
150
+ .optional()
151
+ .describe('Specific link type to remove'),
147
152
  }),
148
153
  handler: async (params) => {
149
154
  const count = projectRegistry.unlink(
@@ -168,7 +173,8 @@ export function createProjectOps(runtime: AgentRuntime): OpDefinition[] {
168
173
  },
169
174
  {
170
175
  name: 'project_linked_projects',
171
- description: 'Get linked projects with full details — project info, link type, and direction.',
176
+ description:
177
+ 'Get linked projects with full details — project info, link type, and direction.',
172
178
  auth: 'read',
173
179
  schema: z.object({
174
180
  projectId: z.string().describe('Project ID'),
@@ -22,8 +22,12 @@ import { IntentRouter } from '../control/intent-router.js';
22
22
  import { KeyPool, loadKeyPoolConfig } from '../llm/key-pool.js';
23
23
  import { loadIntelligenceData } from '../intelligence/loader.js';
24
24
  import { LLMClient } from '../llm/llm-client.js';
25
+ import { CogneeSyncManager } from '../cognee/sync-manager.js';
26
+ import { IntakePipeline } from '../intake/intake-pipeline.js';
25
27
  import { Telemetry } from '../telemetry/telemetry.js';
26
28
  import { ProjectRegistry } from '../project/project-registry.js';
29
+ import { TemplateManager } from '../prompts/template-manager.js';
30
+ import { existsSync } from 'node:fs';
27
31
  import { createLogger } from '../logging/logger.js';
28
32
  import type { AgentRuntimeConfig, AgentRuntime } from './types.js';
29
33
 
@@ -58,25 +62,25 @@ export function createAgentRuntime(config: AgentRuntimeConfig): AgentRuntime {
58
62
  // Planner — multi-step task tracking
59
63
  const planner = new Planner(plansPath);
60
64
 
65
+ // Cognee — vector search client (graceful degradation if Cognee is down)
66
+ const cogneePartial: Partial<import('../cognee/types.js').CogneeConfig> = { dataset: agentId };
67
+ if (process.env.COGNEE_BASE_URL) cogneePartial.baseUrl = process.env.COGNEE_BASE_URL;
68
+ if (process.env.COGNEE_API_TOKEN) cogneePartial.apiToken = process.env.COGNEE_API_TOKEN;
69
+ if (process.env.COGNEE_DATASET) cogneePartial.dataset = process.env.COGNEE_DATASET;
70
+ const cognee = new CogneeClient(cogneePartial);
71
+
61
72
  // Brain — intelligence layer (TF-IDF scoring, auto-tagging, dedup)
62
- const brain = new Brain(vault);
73
+ const brain = new Brain(vault, cognee);
63
74
 
64
75
  // Brain Intelligence — pattern strengths, session knowledge, intelligence pipeline
65
76
  const brainIntelligence = new BrainIntelligence(vault, brain);
66
77
 
67
78
  // Curator — vault self-maintenance (dedup, contradictions, grooming, health)
68
- const curator = new Curator(vault);
79
+ const curator = new Curator(vault, cognee);
69
80
 
70
81
  // Governance — policy engine + proposal tracker for gated knowledge capture
71
82
  const governance = new Governance(vault);
72
83
 
73
- // Cognee — vector search client (graceful degradation if Cognee is down)
74
- const cogneePartial: Partial<import('../cognee/types.js').CogneeConfig> = { dataset: agentId };
75
- if (process.env.COGNEE_BASE_URL) cogneePartial.baseUrl = process.env.COGNEE_BASE_URL;
76
- if (process.env.COGNEE_API_TOKEN) cogneePartial.apiToken = process.env.COGNEE_API_TOKEN;
77
- if (process.env.COGNEE_DATASET) cogneePartial.dataset = process.env.COGNEE_DATASET;
78
- const cognee = new CogneeClient(cogneePartial);
79
-
80
84
  // Loop Manager — iterative validation loop tracking (in-memory, session-scoped)
81
85
  const loop = new LoopManager();
82
86
 
@@ -92,12 +96,30 @@ export function createAgentRuntime(config: AgentRuntimeConfig): AgentRuntime {
92
96
  // Project Registry — multi-project tracking with rules and links
93
97
  const projectRegistry = new ProjectRegistry(vault.getDb());
94
98
 
99
+ // Template Manager — prompt templates with variable substitution
100
+ const templatesDir = config.templatesDir ?? join(agentHome, 'templates');
101
+ const templateManager = new TemplateManager(templatesDir);
102
+ if (existsSync(templatesDir)) {
103
+ templateManager.load();
104
+ }
105
+
95
106
  // LLM key pools and client
96
107
  const keyPoolFiles = loadKeyPoolConfig(agentId);
97
108
  const openaiKeyPool = new KeyPool(keyPoolFiles.openai);
98
109
  const anthropicKeyPool = new KeyPool(keyPoolFiles.anthropic);
99
110
  const llmClient = new LLMClient(openaiKeyPool, anthropicKeyPool, agentId);
100
111
 
112
+ // Cognee Sync Manager — queue-based dirty tracking with offline resilience
113
+ const syncManager = new CogneeSyncManager(
114
+ vault.getProvider(),
115
+ cognee,
116
+ cogneePartial.dataset ?? agentId,
117
+ );
118
+ vault.setSyncManager(syncManager);
119
+
120
+ // Intake Pipeline — PDF/book ingestion with LLM classification
121
+ const intakePipeline = new IntakePipeline(vault.getProvider(), vault, llmClient);
122
+
101
123
  return {
102
124
  config,
103
125
  logger,
@@ -115,8 +137,12 @@ export function createAgentRuntime(config: AgentRuntimeConfig): AgentRuntime {
115
137
  llmClient,
116
138
  telemetry,
117
139
  projectRegistry,
140
+ templateManager,
141
+ syncManager,
142
+ intakePipeline,
118
143
  createdAt: Date.now(),
119
144
  close: () => {
145
+ syncManager.close();
120
146
  cognee.resetPendingCognify();
121
147
  vault.close();
122
148
  },
@@ -12,6 +12,9 @@ import type { IntentRouter } from '../control/intent-router.js';
12
12
  import type { LoopManager } from '../loop/loop-manager.js';
13
13
  import type { Telemetry } from '../telemetry/telemetry.js';
14
14
  import type { ProjectRegistry } from '../project/project-registry.js';
15
+ import type { TemplateManager } from '../prompts/template-manager.js';
16
+ import type { CogneeSyncManager } from '../cognee/sync-manager.js';
17
+ import type { IntakePipeline } from '../intake/intake-pipeline.js';
15
18
  import type { Logger } from '../logging/logger.js';
16
19
  import type { LogLevel } from '../logging/types.js';
17
20
 
@@ -28,6 +31,8 @@ export interface AgentRuntimeConfig {
28
31
  plansPath?: string;
29
32
  /** Intelligence data directory to seed vault from. Optional. */
30
33
  dataDir?: string;
34
+ /** Path to prompt templates directory. Default: ~/.{agentId}/templates */
35
+ templatesDir?: string;
31
36
  /** Minimum log level. Default: 'info' (or SOLERI_LOG_LEVEL env var). */
32
37
  logLevel?: LogLevel;
33
38
  }
@@ -53,6 +58,9 @@ export interface AgentRuntime {
53
58
  llmClient: LLMClient;
54
59
  telemetry: Telemetry;
55
60
  projectRegistry: ProjectRegistry;
61
+ templateManager: TemplateManager;
62
+ syncManager: CogneeSyncManager;
63
+ intakePipeline: IntakePipeline;
56
64
  /** Timestamp (ms since epoch) when this runtime was created. */
57
65
  createdAt: number;
58
66
  /** Close the vault database connection. Call on shutdown. */
@@ -1,17 +1,20 @@
1
1
  /**
2
- * Extra vault operations — 12 ops that extend the 4 base vault ops in core-ops.ts.
2
+ * Extra vault operations — 20 ops that extend the 4 base vault ops in core-ops.ts.
3
3
  *
4
- * Groups: single-entry CRUD (3), bulk (2), discovery (3), import/export (3), analytics (1).
4
+ * Groups: single-entry CRUD (3), bulk (2), discovery (3), import/export (3),
5
+ * analytics (1), seed canonical (1), knowledge lifecycle (4), temporal (3).
5
6
  */
6
7
 
7
8
  import { z } from 'zod';
9
+ import { readFileSync, readdirSync, existsSync } from 'node:fs';
10
+ import { join, basename } from 'node:path';
8
11
  import type { OpDefinition } from '../facades/types.js';
9
12
  import type { IntelligenceEntry } from '../intelligence/types.js';
10
13
  import type { AgentRuntime } from './types.js';
11
14
 
12
15
  const entrySchema = z.object({
13
16
  id: z.string(),
14
- type: z.enum(['pattern', 'anti-pattern', 'rule']),
17
+ type: z.enum(['pattern', 'anti-pattern', 'rule', 'playbook']),
15
18
  domain: z.string(),
16
19
  title: z.string(),
17
20
  severity: z.enum(['critical', 'warning', 'suggestion']),
@@ -56,7 +59,7 @@ export function createVaultExtraOps(runtime: AgentRuntime): OpDefinition[] {
56
59
  tags: z.array(z.string()).optional(),
57
60
  appliesTo: z.array(z.string()).optional(),
58
61
  severity: z.enum(['critical', 'warning', 'suggestion']).optional(),
59
- type: z.enum(['pattern', 'anti-pattern', 'rule']).optional(),
62
+ type: z.enum(['pattern', 'anti-pattern', 'rule', 'playbook']).optional(),
60
63
  domain: z.string().optional(),
61
64
  }),
62
65
  handler: async (params) => {
@@ -221,5 +224,383 @@ export function createVaultExtraOps(runtime: AgentRuntime): OpDefinition[] {
221
224
  return vault.getAgeReport();
222
225
  },
223
226
  },
227
+
228
+ // ─── Seed Canonical (#153) ───────────────────────────────────
229
+ {
230
+ name: 'vault_seed_canonical',
231
+ description:
232
+ 'Seed vault knowledge from structured markdown files with YAML frontmatter. ' +
233
+ 'Reads .md files from a directory, parses them into IntelligenceEntry objects, and upserts. Idempotent.',
234
+ auth: 'write',
235
+ schema: z.object({
236
+ directory: z
237
+ .string()
238
+ .describe('Path to directory containing .md files with YAML frontmatter'),
239
+ domain: z
240
+ .string()
241
+ .optional()
242
+ .describe('Override domain for all entries (default: from frontmatter or filename)'),
243
+ }),
244
+ handler: async (params) => {
245
+ try {
246
+ const dir = params.directory as string;
247
+ const domainOverride = params.domain as string | undefined;
248
+
249
+ if (!existsSync(dir)) {
250
+ return { error: `Directory not found: ${dir}` };
251
+ }
252
+
253
+ const files = readdirSync(dir).filter((f) => f.endsWith('.md'));
254
+ const entries: IntelligenceEntry[] = [];
255
+ const errors: Array<{ file: string; error: string }> = [];
256
+
257
+ for (const file of files) {
258
+ try {
259
+ const content = readFileSync(join(dir, file), 'utf-8');
260
+ const entry = parseMarkdownEntry(content, file, domainOverride);
261
+ if (entry) entries.push(entry);
262
+ } catch (err) {
263
+ errors.push({ file, error: (err as Error).message });
264
+ }
265
+ }
266
+
267
+ const seeded = entries.length > 0 ? vault.seed(entries) : 0;
268
+
269
+ return {
270
+ seeded,
271
+ filesProcessed: files.length,
272
+ errors: errors.length > 0 ? errors : undefined,
273
+ total: vault.stats().totalEntries,
274
+ };
275
+ } catch (err) {
276
+ return { error: (err as Error).message };
277
+ }
278
+ },
279
+ },
280
+
281
+ // ─── Knowledge Audit (#155) ──────────────────────────────────
282
+ {
283
+ name: 'knowledge_audit',
284
+ description:
285
+ 'Audit vault quality — coverage gaps, stale entries, tag health, and recommendations.',
286
+ auth: 'read',
287
+ handler: async () => {
288
+ try {
289
+ const stats = vault.stats();
290
+ const tags = vault.getTags();
291
+ const domains = vault.getDomains();
292
+ const ageReport = vault.getAgeReport();
293
+
294
+ // Check coverage
295
+ const entriesWithoutTags = tags.length === 0 ? stats.totalEntries : 0;
296
+ const singletonTags = tags.filter((t) => t.count === 1).length;
297
+
298
+ // Staleness: entries older than 90 days
299
+ const staleCount = ageReport.buckets.find((b) => b.label === 'older')?.count ?? 0;
300
+
301
+ const recommendations: string[] = [];
302
+ if (stats.totalEntries < 10)
303
+ recommendations.push('Vault has few entries — capture more knowledge');
304
+ if (singletonTags > tags.length * 0.5)
305
+ recommendations.push('Many singleton tags — consolidate tagging');
306
+ if (staleCount > stats.totalEntries * 0.3)
307
+ recommendations.push('>30% entries are stale — review and update');
308
+ if (domains.length === 1)
309
+ recommendations.push('Only one domain — consider categorizing by domain');
310
+
311
+ return {
312
+ totalEntries: stats.totalEntries,
313
+ domainCount: domains.length,
314
+ tagCount: tags.length,
315
+ singletonTags,
316
+ staleEntries: staleCount,
317
+ entriesWithoutTags,
318
+ recommendations,
319
+ };
320
+ } catch (err) {
321
+ return { error: (err as Error).message };
322
+ }
323
+ },
324
+ },
325
+
326
+ // ─── Knowledge Health (#155) ─────────────────────────────────
327
+ {
328
+ name: 'knowledge_health',
329
+ description:
330
+ 'Knowledge base health metrics — entry counts, freshness, staleness, contradiction signals.',
331
+ auth: 'read',
332
+ handler: async () => {
333
+ try {
334
+ const stats = vault.stats();
335
+ const ageReport = vault.getAgeReport();
336
+ const domains = vault.getDomains();
337
+ const tags = vault.getTags();
338
+
339
+ // Detect potential contradictions: entries with same tags but different types (pattern vs anti-pattern)
340
+ const db = vault.getDb();
341
+ const contradictionSignals = db
342
+ .prepare(
343
+ `SELECT t.value as tag, COUNT(DISTINCT e.type) as type_count
344
+ FROM entries e, json_each(e.tags) t
345
+ GROUP BY t.value HAVING type_count > 1 LIMIT 10`,
346
+ )
347
+ .all() as Array<{ tag: string; type_count: number }>;
348
+
349
+ return {
350
+ totalEntries: stats.totalEntries,
351
+ freshEntries:
352
+ (ageReport.buckets.find((b) => b.label === 'today')?.count ?? 0) +
353
+ (ageReport.buckets.find((b) => b.label === 'this_week')?.count ?? 0),
354
+ staleEntries: ageReport.buckets.find((b) => b.label === 'older')?.count ?? 0,
355
+ domainCount: domains.length,
356
+ tagCount: tags.length,
357
+ contradictionSignals: contradictionSignals.length,
358
+ contradictionTags: contradictionSignals.map((c) => c.tag),
359
+ oldestTimestamp: ageReport.oldestTimestamp,
360
+ newestTimestamp: ageReport.newestTimestamp,
361
+ };
362
+ } catch (err) {
363
+ return { error: (err as Error).message };
364
+ }
365
+ },
366
+ },
367
+
368
+ // ─── Merge Patterns (#155) ───────────────────────────────────
369
+ {
370
+ name: 'knowledge_merge',
371
+ description:
372
+ 'Merge two similar patterns into one — keeps the best metadata from both, removes the duplicate.',
373
+ auth: 'write',
374
+ schema: z.object({
375
+ keepId: z.string().describe('ID of the entry to keep (will receive merged data)'),
376
+ removeId: z.string().describe('ID of the duplicate entry to remove after merge'),
377
+ }),
378
+ handler: async (params) => {
379
+ try {
380
+ const keep = vault.get(params.keepId as string);
381
+ const remove = vault.get(params.removeId as string);
382
+ if (!keep) return { error: `Entry not found: ${params.keepId}` };
383
+ if (!remove) return { error: `Entry not found: ${params.removeId}` };
384
+
385
+ // Merge tags (deduplicated union)
386
+ const mergedTags = [...new Set([...(keep.tags ?? []), ...(remove.tags ?? [])])];
387
+
388
+ // Merge fields — prefer non-empty from either side
389
+ const updates: Partial<IntelligenceEntry> = {
390
+ tags: mergedTags,
391
+ description: keep.description || remove.description,
392
+ context: keep.context || remove.context,
393
+ example: keep.example || remove.example,
394
+ counterExample: keep.counterExample || remove.counterExample,
395
+ why: keep.why || remove.why,
396
+ appliesTo: keep.appliesTo?.length ? keep.appliesTo : remove.appliesTo,
397
+ };
398
+
399
+ vault.update(keep.id, updates);
400
+ vault.remove(remove.id);
401
+
402
+ return {
403
+ merged: true,
404
+ keptId: keep.id,
405
+ removedId: remove.id,
406
+ mergedTags,
407
+ };
408
+ } catch (err) {
409
+ return { error: (err as Error).message };
410
+ }
411
+ },
412
+ },
413
+
414
+ // ─── Knowledge Reorganize (#155) ─────────────────────────────
415
+ {
416
+ name: 'knowledge_reorganize',
417
+ description:
418
+ 'Re-categorize vault entries — reassign domains, clean up tags, deduplicate. Dry-run by default.',
419
+ auth: 'write',
420
+ schema: z.object({
421
+ dryRun: z
422
+ .boolean()
423
+ .optional()
424
+ .describe('If true, only report what would change (default true)'),
425
+ retagRules: z
426
+ .array(
427
+ z.object({
428
+ from: z.string().describe('Tag to rename/remove'),
429
+ to: z.string().optional().describe('New tag (omit to remove the tag)'),
430
+ }),
431
+ )
432
+ .optional()
433
+ .describe('Tag rename/removal rules'),
434
+ domainRules: z
435
+ .array(
436
+ z.object({
437
+ from: z.string().describe('Old domain name'),
438
+ to: z.string().describe('New domain name'),
439
+ }),
440
+ )
441
+ .optional()
442
+ .describe('Domain rename rules'),
443
+ }),
444
+ handler: async (params) => {
445
+ try {
446
+ const dryRun = (params.dryRun as boolean | undefined) ?? true;
447
+ const retagRules = (params.retagRules as Array<{ from: string; to?: string }>) ?? [];
448
+ const domainRules = (params.domainRules as Array<{ from: string; to: string }>) ?? [];
449
+ const changes: Array<{ id: string; field: string; from: string; to: string }> = [];
450
+
451
+ const allEntries = vault.list({});
452
+
453
+ for (const entry of allEntries) {
454
+ // Apply domain rules
455
+ for (const rule of domainRules) {
456
+ if (entry.domain === rule.from) {
457
+ changes.push({ id: entry.id, field: 'domain', from: rule.from, to: rule.to });
458
+ if (!dryRun) vault.update(entry.id, { domain: rule.to });
459
+ }
460
+ }
461
+
462
+ // Apply retag rules
463
+ if (entry.tags) {
464
+ let tagsChanged = false;
465
+ const newTags = [...entry.tags];
466
+ for (const rule of retagRules) {
467
+ const idx = newTags.indexOf(rule.from);
468
+ if (idx !== -1) {
469
+ if (rule.to) {
470
+ changes.push({ id: entry.id, field: 'tag', from: rule.from, to: rule.to });
471
+ newTags[idx] = rule.to;
472
+ } else {
473
+ changes.push({ id: entry.id, field: 'tag', from: rule.from, to: '(removed)' });
474
+ newTags.splice(idx, 1);
475
+ }
476
+ tagsChanged = true;
477
+ }
478
+ }
479
+ if (tagsChanged && !dryRun) {
480
+ vault.update(entry.id, { tags: [...new Set(newTags)] });
481
+ }
482
+ }
483
+ }
484
+
485
+ return {
486
+ dryRun,
487
+ changesFound: changes.length,
488
+ changes: changes.slice(0, 100), // cap output
489
+ entriesScanned: allEntries.length,
490
+ };
491
+ } catch (err) {
492
+ return { error: (err as Error).message };
493
+ }
494
+ },
495
+ },
496
+
497
+ // ─── Temporal (#89) ──────────────────────────────────────────────
498
+ {
499
+ name: 'vault_set_temporal',
500
+ description:
501
+ 'Set valid_from and/or valid_until timestamps on a vault entry for bi-temporal validity windows.',
502
+ auth: 'write',
503
+ schema: z.object({
504
+ id: z.string().describe('Entry ID'),
505
+ validFrom: z.number().optional().describe('Unix epoch — when entry becomes active'),
506
+ validUntil: z.number().optional().describe('Unix epoch — when entry expires'),
507
+ }),
508
+ handler: async (params) => {
509
+ const updated = vault.setTemporal(
510
+ params.id as string,
511
+ params.validFrom as number | undefined,
512
+ params.validUntil as number | undefined,
513
+ );
514
+ if (!updated) return { error: 'Entry not found or no fields to update' };
515
+ const entry = vault.get(params.id as string);
516
+ return {
517
+ updated: true,
518
+ id: params.id,
519
+ validFrom: entry?.validFrom ?? null,
520
+ validUntil: entry?.validUntil ?? null,
521
+ };
522
+ },
523
+ },
524
+ {
525
+ name: 'vault_find_expiring',
526
+ description:
527
+ 'Find vault entries expiring within a given number of days. Useful for proactive knowledge maintenance.',
528
+ auth: 'read',
529
+ schema: z.object({
530
+ withinDays: z.number().describe('Number of days to look ahead'),
531
+ }),
532
+ handler: async (params) => {
533
+ const entries = vault.findExpiring(params.withinDays as number);
534
+ return { entries, count: entries.length };
535
+ },
536
+ },
537
+ {
538
+ name: 'vault_find_expired',
539
+ description: 'List expired vault entries (valid_until in the past). Useful for cleanup.',
540
+ auth: 'read',
541
+ schema: z.object({
542
+ limit: z.number().optional().describe('Max results (default 50)'),
543
+ }),
544
+ handler: async (params) => {
545
+ const entries = vault.findExpired((params.limit as number | undefined) ?? 50);
546
+ return { entries, count: entries.length };
547
+ },
548
+ },
224
549
  ];
225
550
  }
551
+
552
+ /**
553
+ * Parse a markdown file with YAML frontmatter into an IntelligenceEntry.
554
+ * Expected frontmatter fields: id, type, domain, severity, title, tags.
555
+ */
556
+ function parseMarkdownEntry(
557
+ content: string,
558
+ filename: string,
559
+ domainOverride?: string,
560
+ ): IntelligenceEntry | null {
561
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
562
+ if (!match) return null;
563
+
564
+ const frontmatter = match[1];
565
+ const body = match[2].trim();
566
+
567
+ // Simple YAML parser for flat key-value pairs
568
+ const meta: Record<string, string | string[]> = {};
569
+ for (const line of frontmatter.split('\n')) {
570
+ const kvMatch = line.match(/^(\w+):\s*(.+)$/);
571
+ if (kvMatch) {
572
+ const key = kvMatch[1];
573
+ let value = kvMatch[2].trim();
574
+ // Handle quoted strings
575
+ if (
576
+ (value.startsWith('"') && value.endsWith('"')) ||
577
+ (value.startsWith("'") && value.endsWith("'"))
578
+ ) {
579
+ value = value.slice(1, -1);
580
+ }
581
+ // Handle arrays: [a, b, c]
582
+ if (value.startsWith('[') && value.endsWith(']')) {
583
+ meta[key] = value
584
+ .slice(1, -1)
585
+ .split(',')
586
+ .map((s) => s.trim().replace(/^["']|["']$/g, ''));
587
+ } else {
588
+ meta[key] = value;
589
+ }
590
+ }
591
+ }
592
+
593
+ const id = (meta.id as string) || basename(filename, '.md');
594
+ const tags = Array.isArray(meta.tags) ? meta.tags : meta.tags ? [meta.tags as string] : [];
595
+
596
+ return {
597
+ id,
598
+ type: (meta.type as IntelligenceEntry['type']) || 'pattern',
599
+ domain: domainOverride || (meta.domain as string) || 'general',
600
+ title: (meta.title as string) || basename(filename, '.md'),
601
+ severity: (meta.severity as IntelligenceEntry['severity']) || 'suggestion',
602
+ description: body || (meta.description as string) || '',
603
+ context: meta.context as string | undefined,
604
+ tags,
605
+ };
606
+ }
@@ -0,0 +1,87 @@
1
+ import type { IntelligenceEntry } from '../intelligence/types.js';
2
+
3
+ export interface PlaybookStep {
4
+ order: number;
5
+ title: string;
6
+ description: string;
7
+ validation?: string;
8
+ }
9
+
10
+ export interface Playbook {
11
+ id: string;
12
+ title: string;
13
+ domain: string;
14
+ description: string;
15
+ steps: PlaybookStep[];
16
+ tags: string[];
17
+ createdAt: number;
18
+ updatedAt: number;
19
+ }
20
+
21
+ export interface PlaybookValidationResult {
22
+ valid: boolean;
23
+ errors: string[];
24
+ }
25
+
26
+ /**
27
+ * Validate a playbook's structure.
28
+ * Checks: title non-empty, steps.length > 0, each step has order/title/description,
29
+ * orders are sequential starting from 1.
30
+ */
31
+ export function validatePlaybook(playbook: Playbook): PlaybookValidationResult {
32
+ const errors: string[] = [];
33
+
34
+ if (!playbook.title || playbook.title.trim() === '') {
35
+ errors.push('Playbook title must not be empty');
36
+ }
37
+
38
+ if (!playbook.steps || playbook.steps.length === 0) {
39
+ errors.push('Playbook must have at least one step');
40
+ } else {
41
+ for (let i = 0; i < playbook.steps.length; i++) {
42
+ const step = playbook.steps[i];
43
+ const expectedOrder = i + 1;
44
+
45
+ if (step.order !== expectedOrder) {
46
+ errors.push(`Step ${i + 1} has order ${step.order}, expected ${expectedOrder}`);
47
+ }
48
+ if (!step.title || step.title.trim() === '') {
49
+ errors.push(`Step ${expectedOrder} title must not be empty`);
50
+ }
51
+ if (!step.description || step.description.trim() === '') {
52
+ errors.push(`Step ${expectedOrder} description must not be empty`);
53
+ }
54
+ }
55
+ }
56
+
57
+ return { valid: errors.length === 0, errors };
58
+ }
59
+
60
+ /**
61
+ * Parse a Playbook from a vault IntelligenceEntry.
62
+ * Returns null if the entry is not a playbook type or if the context is not valid
63
+ * JSON with a steps array.
64
+ */
65
+ export function parsePlaybookFromEntry(entry: IntelligenceEntry): Playbook | null {
66
+ if (entry.type !== 'playbook') return null;
67
+
68
+ let steps: PlaybookStep[];
69
+ try {
70
+ const parsed = JSON.parse(entry.context ?? '');
71
+ if (!Array.isArray(parsed?.steps)) return null;
72
+ steps = parsed.steps;
73
+ } catch {
74
+ return null;
75
+ }
76
+
77
+ return {
78
+ id: entry.id,
79
+ title: entry.title,
80
+ domain: entry.domain,
81
+ description: entry.description,
82
+ steps,
83
+ tags: entry.tags,
84
+ createdAt: 0, // not available from entry directly
85
+ updatedAt: 0,
86
+ };
87
+ }