@soleri/core 9.2.0 → 9.3.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 (298) hide show
  1. package/data/flows/build.flow.yaml +8 -9
  2. package/data/flows/deliver.flow.yaml +9 -10
  3. package/data/flows/design.flow.yaml +3 -4
  4. package/data/flows/enhance.flow.yaml +5 -6
  5. package/data/flows/explore.flow.yaml +3 -4
  6. package/data/flows/fix.flow.yaml +5 -6
  7. package/data/flows/plan.flow.yaml +4 -5
  8. package/data/flows/review.flow.yaml +3 -4
  9. package/dist/curator/curator.d.ts.map +1 -1
  10. package/dist/curator/curator.js +98 -22
  11. package/dist/curator/curator.js.map +1 -1
  12. package/dist/engine/bin/soleri-engine.js.map +1 -1
  13. package/dist/engine/module-manifest.d.ts.map +1 -1
  14. package/dist/engine/module-manifest.js +21 -1
  15. package/dist/engine/module-manifest.js.map +1 -1
  16. package/dist/engine/register-engine.d.ts.map +1 -1
  17. package/dist/engine/register-engine.js +25 -1
  18. package/dist/engine/register-engine.js.map +1 -1
  19. package/dist/flows/gate-evaluator.js.map +1 -1
  20. package/dist/operator/operator-profile.d.ts.map +1 -1
  21. package/dist/operator/operator-profile.js +11 -5
  22. package/dist/operator/operator-profile.js.map +1 -1
  23. package/dist/operator/operator-signals.d.ts.map +1 -1
  24. package/dist/operator/operator-signals.js.map +1 -1
  25. package/dist/planning/evidence-collector.js.map +1 -1
  26. package/dist/planning/gap-passes.d.ts.map +1 -1
  27. package/dist/planning/gap-passes.js +23 -6
  28. package/dist/planning/gap-passes.js.map +1 -1
  29. package/dist/planning/gap-patterns.d.ts.map +1 -1
  30. package/dist/planning/gap-patterns.js +57 -11
  31. package/dist/planning/gap-patterns.js.map +1 -1
  32. package/dist/planning/github-projection.d.ts.map +1 -1
  33. package/dist/planning/github-projection.js +39 -20
  34. package/dist/planning/github-projection.js.map +1 -1
  35. package/dist/planning/impact-analyzer.d.ts.map +1 -1
  36. package/dist/planning/impact-analyzer.js +20 -18
  37. package/dist/planning/impact-analyzer.js.map +1 -1
  38. package/dist/planning/plan-lifecycle.d.ts.map +1 -1
  39. package/dist/planning/plan-lifecycle.js +22 -9
  40. package/dist/planning/plan-lifecycle.js.map +1 -1
  41. package/dist/planning/planner.d.ts.map +1 -1
  42. package/dist/planning/planner.js +60 -17
  43. package/dist/planning/planner.js.map +1 -1
  44. package/dist/planning/rationalization-detector.d.ts.map +1 -1
  45. package/dist/planning/rationalization-detector.js.map +1 -1
  46. package/dist/planning/reconciliation-engine.d.ts.map +1 -1
  47. package/dist/planning/reconciliation-engine.js.map +1 -1
  48. package/dist/planning/task-verifier.d.ts.map +1 -1
  49. package/dist/planning/task-verifier.js +14 -6
  50. package/dist/planning/task-verifier.js.map +1 -1
  51. package/dist/runtime/admin-setup-ops.d.ts.map +1 -1
  52. package/dist/runtime/admin-setup-ops.js +2 -1
  53. package/dist/runtime/admin-setup-ops.js.map +1 -1
  54. package/dist/runtime/branching-ops.d.ts +12 -0
  55. package/dist/runtime/branching-ops.d.ts.map +1 -0
  56. package/dist/runtime/branching-ops.js +100 -0
  57. package/dist/runtime/branching-ops.js.map +1 -0
  58. package/dist/runtime/context-health.d.ts.map +1 -1
  59. package/dist/runtime/context-health.js.map +1 -1
  60. package/dist/runtime/facades/branching-facade.d.ts +7 -0
  61. package/dist/runtime/facades/branching-facade.d.ts.map +1 -0
  62. package/dist/runtime/facades/branching-facade.js +8 -0
  63. package/dist/runtime/facades/branching-facade.js.map +1 -0
  64. package/dist/runtime/facades/chat-service-ops.d.ts.map +1 -1
  65. package/dist/runtime/facades/chat-service-ops.js +3 -1
  66. package/dist/runtime/facades/chat-service-ops.js.map +1 -1
  67. package/dist/runtime/facades/chat-transport-ops.d.ts.map +1 -1
  68. package/dist/runtime/facades/chat-transport-ops.js.map +1 -1
  69. package/dist/runtime/facades/index.d.ts.map +1 -1
  70. package/dist/runtime/facades/index.js +42 -0
  71. package/dist/runtime/facades/index.js.map +1 -1
  72. package/dist/runtime/facades/intake-facade.d.ts +9 -0
  73. package/dist/runtime/facades/intake-facade.d.ts.map +1 -0
  74. package/dist/runtime/facades/intake-facade.js +11 -0
  75. package/dist/runtime/facades/intake-facade.js.map +1 -0
  76. package/dist/runtime/facades/links-facade.d.ts +9 -0
  77. package/dist/runtime/facades/links-facade.d.ts.map +1 -0
  78. package/dist/runtime/facades/links-facade.js +10 -0
  79. package/dist/runtime/facades/links-facade.js.map +1 -0
  80. package/dist/runtime/facades/operator-facade.d.ts.map +1 -1
  81. package/dist/runtime/facades/operator-facade.js.map +1 -1
  82. package/dist/runtime/facades/plan-facade.d.ts.map +1 -1
  83. package/dist/runtime/facades/plan-facade.js +4 -1
  84. package/dist/runtime/facades/plan-facade.js.map +1 -1
  85. package/dist/runtime/facades/tier-facade.d.ts +7 -0
  86. package/dist/runtime/facades/tier-facade.d.ts.map +1 -0
  87. package/dist/runtime/facades/tier-facade.js +8 -0
  88. package/dist/runtime/facades/tier-facade.js.map +1 -0
  89. package/dist/runtime/facades/vault-facade.d.ts +9 -1
  90. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  91. package/dist/runtime/facades/vault-facade.js +44 -187
  92. package/dist/runtime/facades/vault-facade.js.map +1 -1
  93. package/dist/runtime/github-integration.d.ts.map +1 -1
  94. package/dist/runtime/github-integration.js +11 -4
  95. package/dist/runtime/github-integration.js.map +1 -1
  96. package/dist/runtime/orchestrate-ops.d.ts.map +1 -1
  97. package/dist/runtime/orchestrate-ops.js +32 -10
  98. package/dist/runtime/orchestrate-ops.js.map +1 -1
  99. package/dist/runtime/planning-extra-ops.d.ts.map +1 -1
  100. package/dist/runtime/planning-extra-ops.js.map +1 -1
  101. package/dist/runtime/runtime.d.ts.map +1 -1
  102. package/dist/runtime/runtime.js +3 -1
  103. package/dist/runtime/runtime.js.map +1 -1
  104. package/dist/runtime/session-briefing.d.ts.map +1 -1
  105. package/dist/runtime/session-briefing.js +5 -1
  106. package/dist/runtime/session-briefing.js.map +1 -1
  107. package/dist/runtime/tier-ops.d.ts +13 -0
  108. package/dist/runtime/tier-ops.d.ts.map +1 -0
  109. package/dist/runtime/tier-ops.js +110 -0
  110. package/dist/runtime/tier-ops.js.map +1 -0
  111. package/dist/skills/sync-skills.d.ts.map +1 -1
  112. package/dist/skills/sync-skills.js +1 -1
  113. package/dist/skills/sync-skills.js.map +1 -1
  114. package/dist/vault/linking.d.ts.map +1 -1
  115. package/dist/vault/linking.js +41 -5
  116. package/dist/vault/linking.js.map +1 -1
  117. package/dist/vault/vault-entries.d.ts.map +1 -1
  118. package/dist/vault/vault-entries.js +68 -26
  119. package/dist/vault/vault-entries.js.map +1 -1
  120. package/dist/vault/vault-maintenance.d.ts.map +1 -1
  121. package/dist/vault/vault-maintenance.js +6 -2
  122. package/dist/vault/vault-maintenance.js.map +1 -1
  123. package/dist/vault/vault-markdown-sync.d.ts.map +1 -1
  124. package/dist/vault/vault-markdown-sync.js.map +1 -1
  125. package/dist/vault/vault-memories.d.ts.map +1 -1
  126. package/dist/vault/vault-memories.js +3 -1
  127. package/dist/vault/vault-memories.js.map +1 -1
  128. package/dist/vault/vault-schema.js +36 -10
  129. package/dist/vault/vault-schema.js.map +1 -1
  130. package/dist/vault/vault.d.ts.map +1 -1
  131. package/dist/vault/vault.js +5 -1
  132. package/dist/vault/vault.js.map +1 -1
  133. package/package.json +7 -7
  134. package/src/agency/agency-manager.test.ts +60 -40
  135. package/src/agency/default-rules.test.ts +17 -9
  136. package/src/capabilities/registry.test.ts +2 -12
  137. package/src/chat/agent-loop.test.ts +33 -43
  138. package/src/chat/mcp-bridge.test.ts +7 -2
  139. package/src/claudemd/inject.test.ts +2 -12
  140. package/src/context/context-engine.test.ts +96 -51
  141. package/src/control/intent-router.test.ts +3 -3
  142. package/src/curator/classifier.test.ts +14 -8
  143. package/src/curator/contradiction-detector.test.ts +30 -5
  144. package/src/curator/curator.ts +278 -56
  145. package/src/curator/duplicate-detector.test.ts +77 -15
  146. package/src/curator/quality-gate.test.ts +71 -31
  147. package/src/curator/tag-manager.test.ts +12 -4
  148. package/src/domain-packs/knowledge-installer.test.ts +2 -10
  149. package/src/domain-packs/token-resolver.test.ts +1 -3
  150. package/src/domain-packs/types.test.ts +16 -2
  151. package/src/enforcement/registry.test.ts +2 -8
  152. package/src/engine/bin/soleri-engine.ts +3 -1
  153. package/src/engine/module-manifest.test.ts +5 -4
  154. package/src/engine/module-manifest.ts +21 -1
  155. package/src/engine/register-engine.test.ts +6 -1
  156. package/src/engine/register-engine.ts +26 -3
  157. package/src/errors/classify.test.ts +6 -2
  158. package/src/errors/retry.test.ts +1 -4
  159. package/src/facades/facade-factory.test.ts +110 -64
  160. package/src/flows/epilogue.test.ts +16 -10
  161. package/src/flows/gate-evaluator.test.ts +12 -6
  162. package/src/flows/gate-evaluator.ts +1 -3
  163. package/src/governance/governance.test.ts +137 -21
  164. package/src/health/health-registry.test.ts +8 -1
  165. package/src/intake/content-classifier.test.ts +121 -51
  166. package/src/intake/dedup-gate.test.ts +38 -22
  167. package/src/intake/intake-pipeline.test.ts +5 -3
  168. package/src/intake/text-ingester.test.ts +26 -20
  169. package/src/llm/key-pool.test.ts +1 -3
  170. package/src/llm/llm-client.test.ts +1 -4
  171. package/src/llm/oauth-discovery.test.ts +16 -16
  172. package/src/llm/utils.test.ts +62 -18
  173. package/src/logging/logger.test.ts +4 -1
  174. package/src/loop/loop-manager.test.ts +2 -6
  175. package/src/migrations/migration-runner.edge-cases.test.ts +2 -7
  176. package/src/operator/operator-profile-extended.test.ts +15 -5
  177. package/src/operator/operator-profile.test.ts +26 -8
  178. package/src/operator/operator-profile.ts +38 -22
  179. package/src/operator/operator-signals-extended.test.ts +35 -23
  180. package/src/operator/operator-signals.test.ts +6 -10
  181. package/src/operator/operator-signals.ts +2 -1
  182. package/src/operator/prompts/hook-precompact-operator-dispatch.md +10 -6
  183. package/src/operator/prompts/subagent-soft-signal-extractor.md +5 -0
  184. package/src/operator/prompts/subagent-synthesis-cognition.md +19 -10
  185. package/src/operator/prompts/subagent-synthesis-communication.md +13 -7
  186. package/src/operator/prompts/subagent-synthesis-technical.md +19 -9
  187. package/src/operator/prompts/subagent-synthesis-trust.md +27 -21
  188. package/src/persona/defaults.test.ts +1 -5
  189. package/src/planning/evidence-collector.test.ts +147 -38
  190. package/src/planning/evidence-collector.ts +1 -4
  191. package/src/planning/gap-analysis-alternatives.test.ts +41 -11
  192. package/src/planning/gap-passes.test.ts +215 -33
  193. package/src/planning/gap-passes.ts +115 -46
  194. package/src/planning/gap-patterns.test.ts +87 -13
  195. package/src/planning/gap-patterns.ts +114 -31
  196. package/src/planning/github-projection.test.ts +6 -1
  197. package/src/planning/github-projection.ts +41 -20
  198. package/src/planning/impact-analyzer.test.ts +10 -23
  199. package/src/planning/impact-analyzer.ts +33 -46
  200. package/src/planning/plan-lifecycle.test.ts +103 -36
  201. package/src/planning/plan-lifecycle.ts +49 -18
  202. package/src/planning/planner.test.ts +12 -2
  203. package/src/planning/planner.ts +198 -58
  204. package/src/planning/rationalization-detector.test.ts +5 -20
  205. package/src/planning/rationalization-detector.ts +14 -16
  206. package/src/planning/reconciliation-engine.test.ts +20 -3
  207. package/src/planning/reconciliation-engine.ts +1 -2
  208. package/src/planning/task-verifier.test.ts +59 -27
  209. package/src/planning/task-verifier.ts +15 -9
  210. package/src/playbooks/playbook-executor.test.ts +1 -3
  211. package/src/plugins/plugin-loader.test.ts +19 -14
  212. package/src/plugins/plugin-registry.test.ts +45 -33
  213. package/src/project/project-registry.test.ts +23 -12
  214. package/src/prompts/template-manager.test.ts +4 -1
  215. package/src/queue/job-queue.test.ts +10 -14
  216. package/src/runtime/admin-extra-ops.test.ts +5 -19
  217. package/src/runtime/admin-ops.test.ts +1 -3
  218. package/src/runtime/admin-setup-ops.test.ts +3 -4
  219. package/src/runtime/admin-setup-ops.ts +9 -2
  220. package/src/runtime/archive-ops.test.ts +4 -1
  221. package/src/runtime/branching-ops.test.ts +144 -0
  222. package/src/runtime/branching-ops.ts +107 -0
  223. package/src/runtime/capture-ops.test.ts +7 -21
  224. package/src/runtime/chain-ops.test.ts +16 -6
  225. package/src/runtime/claude-md-helpers.test.ts +1 -3
  226. package/src/runtime/context-health.test.ts +1 -3
  227. package/src/runtime/context-health.ts +1 -3
  228. package/src/runtime/curator-extra-ops.test.ts +3 -1
  229. package/src/runtime/domain-ops.test.ts +46 -36
  230. package/src/runtime/facades/admin-facade.test.ts +1 -4
  231. package/src/runtime/facades/archive-facade.test.ts +21 -7
  232. package/src/runtime/facades/brain-facade.test.ts +176 -72
  233. package/src/runtime/facades/branching-facade.test.ts +43 -0
  234. package/src/runtime/facades/branching-facade.ts +11 -0
  235. package/src/runtime/facades/chat-facade.test.ts +81 -28
  236. package/src/runtime/facades/chat-service-ops.test.ts +178 -73
  237. package/src/runtime/facades/chat-service-ops.ts +3 -1
  238. package/src/runtime/facades/chat-session-ops.test.ts +25 -10
  239. package/src/runtime/facades/chat-transport-ops.test.ts +101 -34
  240. package/src/runtime/facades/chat-transport-ops.ts +0 -1
  241. package/src/runtime/facades/context-facade.test.ts +19 -4
  242. package/src/runtime/facades/control-facade.test.ts +3 -3
  243. package/src/runtime/facades/index.ts +42 -0
  244. package/src/runtime/facades/intake-facade.test.ts +215 -0
  245. package/src/runtime/facades/intake-facade.ts +14 -0
  246. package/src/runtime/facades/links-facade.test.ts +203 -0
  247. package/src/runtime/facades/links-facade.ts +13 -0
  248. package/src/runtime/facades/loop-facade.test.ts +22 -5
  249. package/src/runtime/facades/memory-facade.test.ts +19 -5
  250. package/src/runtime/facades/operator-facade.test.ts +17 -4
  251. package/src/runtime/facades/operator-facade.ts +11 -3
  252. package/src/runtime/facades/orchestrate-facade.test.ts +7 -1
  253. package/src/runtime/facades/plan-facade.test.ts +29 -12
  254. package/src/runtime/facades/plan-facade.ts +7 -2
  255. package/src/runtime/facades/tier-facade.test.ts +47 -0
  256. package/src/runtime/facades/tier-facade.ts +11 -0
  257. package/src/runtime/facades/vault-facade.test.ts +174 -242
  258. package/src/runtime/facades/vault-facade.ts +55 -199
  259. package/src/runtime/github-integration.ts +11 -8
  260. package/src/runtime/grading-ops.test.ts +39 -8
  261. package/src/runtime/intake-ops.test.ts +69 -16
  262. package/src/runtime/loop-ops.test.ts +16 -6
  263. package/src/runtime/memory-cross-project-ops.test.ts +25 -14
  264. package/src/runtime/orchestrate-ops.ts +54 -27
  265. package/src/runtime/pack-ops.test.ts +23 -6
  266. package/src/runtime/planning-extra-ops.test.ts +17 -7
  267. package/src/runtime/planning-extra-ops.ts +3 -1
  268. package/src/runtime/playbook-ops.test.ts +26 -3
  269. package/src/runtime/plugin-ops.test.ts +83 -25
  270. package/src/runtime/project-ops.test.ts +26 -6
  271. package/src/runtime/runtime.ts +3 -1
  272. package/src/runtime/session-briefing.test.ts +183 -54
  273. package/src/runtime/session-briefing.ts +8 -2
  274. package/src/runtime/sync-ops.test.ts +3 -12
  275. package/src/runtime/telemetry-ops.test.ts +31 -6
  276. package/src/runtime/tier-ops.test.ts +159 -0
  277. package/src/runtime/tier-ops.ts +119 -0
  278. package/src/runtime/vault-extra-ops.test.ts +32 -8
  279. package/src/runtime/vault-sharing-ops.test.ts +1 -4
  280. package/src/skills/sync-skills.ts +2 -12
  281. package/src/transport/ws-server.test.ts +7 -4
  282. package/src/vault/__tests__/vault-characterization.test.ts +492 -81
  283. package/src/vault/linking.test.ts +50 -17
  284. package/src/vault/linking.ts +48 -7
  285. package/src/vault/obsidian-sync.test.ts +6 -3
  286. package/src/vault/scope-detector.test.ts +1 -3
  287. package/src/vault/vault-branching.test.ts +9 -7
  288. package/src/vault/vault-entries.ts +209 -65
  289. package/src/vault/vault-maintenance.ts +7 -12
  290. package/src/vault/vault-manager.test.ts +10 -10
  291. package/src/vault/vault-markdown-sync.ts +4 -1
  292. package/src/vault/vault-memories.ts +7 -7
  293. package/src/vault/vault-schema.ts +72 -15
  294. package/src/vault/vault.ts +55 -9
  295. package/src/brain/strength-scorer.ts +0 -404
  296. package/src/engine/index.ts +0 -21
  297. package/src/persona/index.ts +0 -9
  298. package/src/vault/vault-interfaces.ts +0 -56
@@ -15,14 +15,33 @@ export interface AutoLinkConfig {
15
15
  }
16
16
 
17
17
  /** Updatable fields on an entry. */
18
- export type EntryUpdateFields = Partial<Pick<IntelligenceEntry,
19
- 'title' | 'description' | 'context' | 'example' | 'counterExample' | 'why' |
20
- 'tags' | 'appliesTo' | 'severity' | 'type' | 'domain' | 'validFrom' | 'validUntil'>>;
18
+ export type EntryUpdateFields = Partial<
19
+ Pick<
20
+ IntelligenceEntry,
21
+ | 'title'
22
+ | 'description'
23
+ | 'context'
24
+ | 'example'
25
+ | 'counterExample'
26
+ | 'why'
27
+ | 'tags'
28
+ | 'appliesTo'
29
+ | 'severity'
30
+ | 'type'
31
+ | 'domain'
32
+ | 'validFrom'
33
+ | 'validUntil'
34
+ >
35
+ >;
21
36
 
22
37
  /** Search/list filter options. */
23
38
  export interface EntryFilterOptions {
24
- domain?: string; type?: string; severity?: string;
25
- origin?: 'agent' | 'pack' | 'user'; limit?: number; includeExpired?: boolean;
39
+ domain?: string;
40
+ type?: string;
41
+ severity?: string;
42
+ origin?: 'agent' | 'pack' | 'user';
43
+ limit?: number;
44
+ includeExpired?: boolean;
26
45
  }
27
46
 
28
47
  export function autoLink(entryId: string, config: AutoLinkConfig): void {
@@ -32,10 +51,16 @@ export function autoLink(entryId: string, config: AutoLinkConfig): void {
32
51
  for (const s of suggestions) {
33
52
  config.linkManager.addLink(entryId, s.entryId, s.suggestedType, `auto: ${s.reason}`);
34
53
  }
35
- } catch { /* best-effort */ }
54
+ } catch {
55
+ /* best-effort */
56
+ }
36
57
  }
37
58
 
38
- export function seed(provider: PersistenceProvider, entries: IntelligenceEntry[], alc: AutoLinkConfig): number {
59
+ export function seed(
60
+ provider: PersistenceProvider,
61
+ entries: IntelligenceEntry[],
62
+ alc: AutoLinkConfig,
63
+ ): number {
39
64
  const sql = `
40
65
  INSERT INTO entries (id,type,domain,title,severity,description,context,example,counter_example,why,tags,applies_to,valid_from,valid_until,content_hash,tier,origin)
41
66
  VALUES (@id,@type,@domain,@title,@severity,@description,@context,@example,@counterExample,@why,@tags,@appliesTo,@validFrom,@validUntil,@contentHash,@tier,@origin)
@@ -48,13 +73,23 @@ export function seed(provider: PersistenceProvider, entries: IntelligenceEntry[]
48
73
  let count = 0;
49
74
  for (const entry of entries) {
50
75
  provider.run(sql, {
51
- id: entry.id, type: entry.type, domain: entry.domain, title: entry.title,
52
- severity: entry.severity, description: entry.description,
53
- context: entry.context ?? null, example: entry.example ?? null,
54
- counterExample: entry.counterExample ?? null, why: entry.why ?? null,
55
- tags: JSON.stringify(entry.tags), appliesTo: JSON.stringify(entry.appliesTo ?? []),
56
- validFrom: entry.validFrom ?? null, validUntil: entry.validUntil ?? null,
57
- contentHash: computeContentHash(entry), tier: entry.tier ?? 'agent', origin: entry.origin ?? 'agent',
76
+ id: entry.id,
77
+ type: entry.type,
78
+ domain: entry.domain,
79
+ title: entry.title,
80
+ severity: entry.severity,
81
+ description: entry.description,
82
+ context: entry.context ?? null,
83
+ example: entry.example ?? null,
84
+ counterExample: entry.counterExample ?? null,
85
+ why: entry.why ?? null,
86
+ tags: JSON.stringify(entry.tags),
87
+ appliesTo: JSON.stringify(entry.appliesTo ?? []),
88
+ validFrom: entry.validFrom ?? null,
89
+ validUntil: entry.validUntil ?? null,
90
+ contentHash: computeContentHash(entry),
91
+ tier: entry.tier ?? 'agent',
92
+ origin: entry.origin ?? 'agent',
58
93
  });
59
94
  count++;
60
95
  }
@@ -67,10 +102,13 @@ export function seed(provider: PersistenceProvider, entries: IntelligenceEntry[]
67
102
  }
68
103
 
69
104
  export function seedDedup(
70
- provider: PersistenceProvider, entries: IntelligenceEntry[], alc: AutoLinkConfig,
105
+ provider: PersistenceProvider,
106
+ entries: IntelligenceEntry[],
107
+ alc: AutoLinkConfig,
71
108
  ): Array<{ id: string; action: 'inserted' | 'duplicate'; existingId?: string }> {
72
109
  return provider.transaction(() => {
73
- const results: Array<{ id: string; action: 'inserted' | 'duplicate'; existingId?: string }> = [];
110
+ const results: Array<{ id: string; action: 'inserted' | 'duplicate'; existingId?: string }> =
111
+ [];
74
112
  for (const entry of entries) {
75
113
  const hash = computeContentHash(entry);
76
114
  const existing = findByContentHash(provider, hash);
@@ -86,24 +124,44 @@ export function seedDedup(
86
124
  }
87
125
 
88
126
  export function installPack(
89
- provider: PersistenceProvider, entries: IntelligenceEntry[], alc: AutoLinkConfig,
127
+ provider: PersistenceProvider,
128
+ entries: IntelligenceEntry[],
129
+ alc: AutoLinkConfig,
90
130
  ): { installed: number; skipped: number } {
91
- let installed = 0, skipped = 0;
131
+ let installed = 0,
132
+ skipped = 0;
92
133
  const tagged = entries.map((e) => ({ ...e, origin: 'pack' as const }));
93
134
  for (const r of seedDedup(provider, tagged, alc)) {
94
- if (r.action === 'inserted') installed++; else skipped++;
135
+ if (r.action === 'inserted') installed++;
136
+ else skipped++;
95
137
  }
96
138
  return { installed, skipped };
97
139
  }
98
140
 
99
- export function search(provider: PersistenceProvider, query: string, options?: EntryFilterOptions): SearchResult[] {
141
+ export function search(
142
+ provider: PersistenceProvider,
143
+ query: string,
144
+ options?: EntryFilterOptions,
145
+ ): SearchResult[] {
100
146
  const limit = options?.limit ?? 10;
101
147
  const filters: string[] = [];
102
148
  const fp: Record<string, unknown> = {};
103
- if (options?.domain) { filters.push('e.domain = @domain'); fp.domain = options.domain; }
104
- if (options?.type) { filters.push('e.type = @type'); fp.type = options.type; }
105
- if (options?.severity) { filters.push('e.severity = @severity'); fp.severity = options.severity; }
106
- if (options?.origin) { filters.push('e.origin = @origin'); fp.origin = options.origin; }
149
+ if (options?.domain) {
150
+ filters.push('e.domain = @domain');
151
+ fp.domain = options.domain;
152
+ }
153
+ if (options?.type) {
154
+ filters.push('e.type = @type');
155
+ fp.type = options.type;
156
+ }
157
+ if (options?.severity) {
158
+ filters.push('e.severity = @severity');
159
+ fp.severity = options.severity;
160
+ }
161
+ if (options?.origin) {
162
+ filters.push('e.origin = @origin');
163
+ fp.origin = options.origin;
164
+ }
107
165
  if (!options?.includeExpired) {
108
166
  const now = Math.floor(Date.now() / 1000);
109
167
  filters.push('(e.valid_until IS NULL OR e.valid_until > @now)');
@@ -125,7 +183,9 @@ export function search(provider: PersistenceProvider, query: string, options?: E
125
183
  { query, limit, ...fp },
126
184
  );
127
185
  return rows.map(rowToSearchResult);
128
- } catch { return []; }
186
+ } catch {
187
+ return [];
188
+ }
129
189
  }
130
190
  }
131
191
 
@@ -140,12 +200,27 @@ export function list(
140
200
  ): IntelligenceEntry[] {
141
201
  const filters: string[] = [];
142
202
  const params: Record<string, unknown> = {};
143
- if (options?.domain) { filters.push('domain = @domain'); params.domain = options.domain; }
144
- if (options?.type) { filters.push('type = @type'); params.type = options.type; }
145
- if (options?.severity) { filters.push('severity = @severity'); params.severity = options.severity; }
146
- if (options?.origin) { filters.push('origin = @origin'); params.origin = options.origin; }
203
+ if (options?.domain) {
204
+ filters.push('domain = @domain');
205
+ params.domain = options.domain;
206
+ }
207
+ if (options?.type) {
208
+ filters.push('type = @type');
209
+ params.type = options.type;
210
+ }
211
+ if (options?.severity) {
212
+ filters.push('severity = @severity');
213
+ params.severity = options.severity;
214
+ }
215
+ if (options?.origin) {
216
+ filters.push('origin = @origin');
217
+ params.origin = options.origin;
218
+ }
147
219
  if (options?.tags?.length) {
148
- const c = options.tags.map((t, i) => { params[`tag${i}`] = `%"${t}"%`; return `tags LIKE @tag${i}`; });
220
+ const c = options.tags.map((t, i) => {
221
+ params[`tag${i}`] = `%"${t}"%`;
222
+ return `tags LIKE @tag${i}`;
223
+ });
149
224
  filters.push(`(${c.join(' OR ')})`);
150
225
  }
151
226
  if (!options?.includeExpired) {
@@ -164,10 +239,19 @@ export function list(
164
239
 
165
240
  export function stats(provider: PersistenceProvider): VaultStats {
166
241
  const total = provider.get<{ count: number }>('SELECT COUNT(*) as count FROM entries')!.count;
167
- return { totalEntries: total, byType: gc(provider, 'type'), byDomain: gc(provider, 'domain'), bySeverity: gc(provider, 'severity') };
242
+ return {
243
+ totalEntries: total,
244
+ byType: gc(provider, 'type'),
245
+ byDomain: gc(provider, 'domain'),
246
+ bySeverity: gc(provider, 'severity'),
247
+ };
168
248
  }
169
249
 
170
- export function add(provider: PersistenceProvider, entry: IntelligenceEntry, alc: AutoLinkConfig): void {
250
+ export function add(
251
+ provider: PersistenceProvider,
252
+ entry: IntelligenceEntry,
253
+ alc: AutoLinkConfig,
254
+ ): void {
171
255
  seed(provider, [entry], alc);
172
256
  }
173
257
 
@@ -175,38 +259,64 @@ export function remove(provider: PersistenceProvider, id: string): boolean {
175
259
  return provider.run('DELETE FROM entries WHERE id = ?', [id]).changes > 0;
176
260
  }
177
261
 
178
- export function update(provider: PersistenceProvider, id: string, fields: EntryUpdateFields, alc: AutoLinkConfig): IntelligenceEntry | null {
262
+ export function update(
263
+ provider: PersistenceProvider,
264
+ id: string,
265
+ fields: EntryUpdateFields,
266
+ alc: AutoLinkConfig,
267
+ ): IntelligenceEntry | null {
179
268
  const existing = get(provider, id);
180
269
  if (!existing) return null;
181
270
  seed(provider, [{ ...existing, ...fields }], alc);
182
271
  return get(provider, id);
183
272
  }
184
273
 
185
- export function setTemporal(provider: PersistenceProvider, id: string, validFrom?: number, validUntil?: number): boolean {
274
+ export function setTemporal(
275
+ provider: PersistenceProvider,
276
+ id: string,
277
+ validFrom?: number,
278
+ validUntil?: number,
279
+ ): boolean {
186
280
  const sets: string[] = [];
187
281
  const params: Record<string, unknown> = { id };
188
- if (validFrom !== undefined) { sets.push('valid_from = @validFrom'); params.validFrom = validFrom; }
189
- if (validUntil !== undefined) { sets.push('valid_until = @validUntil'); params.validUntil = validUntil; }
282
+ if (validFrom !== undefined) {
283
+ sets.push('valid_from = @validFrom');
284
+ params.validFrom = validFrom;
285
+ }
286
+ if (validUntil !== undefined) {
287
+ sets.push('valid_until = @validUntil');
288
+ params.validUntil = validUntil;
289
+ }
190
290
  if (sets.length === 0) return false;
191
291
  sets.push('updated_at = unixepoch()');
192
292
  return provider.run(`UPDATE entries SET ${sets.join(', ')} WHERE id = @id`, params).changes > 0;
193
293
  }
194
294
 
195
- export function findExpiring(provider: PersistenceProvider, withinDays: number): IntelligenceEntry[] {
295
+ export function findExpiring(
296
+ provider: PersistenceProvider,
297
+ withinDays: number,
298
+ ): IntelligenceEntry[] {
196
299
  const now = Math.floor(Date.now() / 1000);
197
300
  const cutoff = now + withinDays * 86400;
198
- return provider.all<Record<string, unknown>>(
199
- 'SELECT * FROM entries WHERE valid_until IS NOT NULL AND valid_until > @now AND valid_until <= @cutoff ORDER BY valid_until ASC',
200
- { now, cutoff },
201
- ).map(rowToEntry);
301
+ return provider
302
+ .all<Record<string, unknown>>(
303
+ 'SELECT * FROM entries WHERE valid_until IS NOT NULL AND valid_until > @now AND valid_until <= @cutoff ORDER BY valid_until ASC',
304
+ { now, cutoff },
305
+ )
306
+ .map(rowToEntry);
202
307
  }
203
308
 
204
- export function findExpired(provider: PersistenceProvider, limit: number = 50): IntelligenceEntry[] {
309
+ export function findExpired(
310
+ provider: PersistenceProvider,
311
+ limit: number = 50,
312
+ ): IntelligenceEntry[] {
205
313
  const now = Math.floor(Date.now() / 1000);
206
- return provider.all<Record<string, unknown>>(
207
- 'SELECT * FROM entries WHERE valid_until IS NOT NULL AND valid_until <= @now ORDER BY valid_until DESC LIMIT @limit',
208
- { now, limit },
209
- ).map(rowToEntry);
314
+ return provider
315
+ .all<Record<string, unknown>>(
316
+ 'SELECT * FROM entries WHERE valid_until IS NOT NULL AND valid_until <= @now ORDER BY valid_until DESC LIMIT @limit',
317
+ { now, limit },
318
+ )
319
+ .map(rowToEntry);
210
320
  }
211
321
 
212
322
  export function bulkRemove(provider: PersistenceProvider, ids: string[]): number {
@@ -225,45 +335,75 @@ export function getTags(provider: PersistenceProvider): Array<{ tag: string; cou
225
335
  counts.set(tag, (counts.get(tag) ?? 0) + 1);
226
336
  }
227
337
  }
228
- return Array.from(counts.entries()).map(([tag, count]) => ({ tag, count })).sort((a, b) => b.count - a.count);
338
+ return Array.from(counts.entries())
339
+ .map(([tag, count]) => ({ tag, count }))
340
+ .sort((a, b) => b.count - a.count);
229
341
  }
230
342
 
231
- export function getDomains(provider: PersistenceProvider): Array<{ domain: string; count: number }> {
232
- return provider.all('SELECT domain, COUNT(*) as count FROM entries GROUP BY domain ORDER BY count DESC');
343
+ export function getDomains(
344
+ provider: PersistenceProvider,
345
+ ): Array<{ domain: string; count: number }> {
346
+ return provider.all(
347
+ 'SELECT domain, COUNT(*) as count FROM entries GROUP BY domain ORDER BY count DESC',
348
+ );
233
349
  }
234
350
 
235
351
  export function getRecent(provider: PersistenceProvider, limit: number = 20): IntelligenceEntry[] {
236
- return provider.all<Record<string, unknown>>('SELECT * FROM entries ORDER BY updated_at DESC LIMIT ?', [limit]).map(rowToEntry);
352
+ return provider
353
+ .all<Record<string, unknown>>('SELECT * FROM entries ORDER BY updated_at DESC LIMIT ?', [limit])
354
+ .map(rowToEntry);
237
355
  }
238
356
 
239
357
  export function findByContentHash(provider: PersistenceProvider, hash: string): string | null {
240
- return provider.get<{ id: string }>('SELECT id FROM entries WHERE content_hash = @hash', { hash })?.id ?? null;
358
+ return (
359
+ provider.get<{ id: string }>('SELECT id FROM entries WHERE content_hash = @hash', { hash })
360
+ ?.id ?? null
361
+ );
241
362
  }
242
363
 
243
- export function contentHashStats(provider: PersistenceProvider): { total: number; hashed: number; uniqueHashes: number } {
364
+ export function contentHashStats(provider: PersistenceProvider): {
365
+ total: number;
366
+ hashed: number;
367
+ uniqueHashes: number;
368
+ } {
244
369
  const total = provider.get<{ c: number }>('SELECT COUNT(*) as c FROM entries')?.c ?? 0;
245
- const hashed = provider.get<{ c: number }>('SELECT COUNT(*) as c FROM entries WHERE content_hash IS NOT NULL')?.c ?? 0;
246
- const uniqueHashes = provider.get<{ c: number }>('SELECT COUNT(DISTINCT content_hash) as c FROM entries WHERE content_hash IS NOT NULL')?.c ?? 0;
370
+ const hashed =
371
+ provider.get<{ c: number }>('SELECT COUNT(*) as c FROM entries WHERE content_hash IS NOT NULL')
372
+ ?.c ?? 0;
373
+ const uniqueHashes =
374
+ provider.get<{ c: number }>(
375
+ 'SELECT COUNT(DISTINCT content_hash) as c FROM entries WHERE content_hash IS NOT NULL',
376
+ )?.c ?? 0;
247
377
  return { total, hashed, uniqueHashes };
248
378
  }
249
379
 
250
380
  // ── Helpers ──────────────────────────────────────────────────────────────
251
381
 
252
382
  function gc(provider: PersistenceProvider, col: string): Record<string, number> {
253
- const rows = provider.all<{ key: string; count: number }>(`SELECT ${col} as key, COUNT(*) as count FROM entries GROUP BY ${col}`);
383
+ const rows = provider.all<{ key: string; count: number }>(
384
+ `SELECT ${col} as key, COUNT(*) as count FROM entries GROUP BY ${col}`,
385
+ );
254
386
  return Object.fromEntries(rows.map((r) => [r.key, r.count]));
255
387
  }
256
388
 
257
389
  export function rowToEntry(row: Record<string, unknown>): IntelligenceEntry {
258
390
  return {
259
- id: row.id as string, type: row.type as IntelligenceEntry['type'],
260
- domain: row.domain as IntelligenceEntry['domain'], title: row.title as string,
261
- severity: row.severity as IntelligenceEntry['severity'], description: row.description as string,
262
- context: (row.context as string) ?? undefined, example: (row.example as string) ?? undefined,
263
- counterExample: (row.counter_example as string) ?? undefined, why: (row.why as string) ?? undefined,
264
- tags: JSON.parse((row.tags as string) || '[]'), appliesTo: JSON.parse((row.applies_to as string) || '[]'),
265
- tier: (row.tier as IntelligenceEntry['tier']) ?? undefined, origin: (row.origin as IntelligenceEntry['origin']) ?? undefined,
266
- validFrom: (row.valid_from as number) ?? undefined, validUntil: (row.valid_until as number) ?? undefined,
391
+ id: row.id as string,
392
+ type: row.type as IntelligenceEntry['type'],
393
+ domain: row.domain as IntelligenceEntry['domain'],
394
+ title: row.title as string,
395
+ severity: row.severity as IntelligenceEntry['severity'],
396
+ description: row.description as string,
397
+ context: (row.context as string) ?? undefined,
398
+ example: (row.example as string) ?? undefined,
399
+ counterExample: (row.counter_example as string) ?? undefined,
400
+ why: (row.why as string) ?? undefined,
401
+ tags: JSON.parse((row.tags as string) || '[]'),
402
+ appliesTo: JSON.parse((row.applies_to as string) || '[]'),
403
+ tier: (row.tier as IntelligenceEntry['tier']) ?? undefined,
404
+ origin: (row.origin as IntelligenceEntry['origin']) ?? undefined,
405
+ validFrom: (row.valid_from as number) ?? undefined,
406
+ validUntil: (row.valid_until as number) ?? undefined,
267
407
  };
268
408
  }
269
409
 
@@ -274,8 +414,12 @@ export function rowToSearchResult(row: Record<string, unknown>): SearchResult {
274
414
 
275
415
  /** Build FTS5 query from natural language: terms joined with OR for broad matching. */
276
416
  export function buildFtsQuery(query: string): string {
277
- const terms = query.toLowerCase().split(/\s+/)
278
- .filter((t) => t.length >= 2).map((t) => t.replace(/[^a-z0-9]/g, '')).filter(Boolean);
417
+ const terms = query
418
+ .toLowerCase()
419
+ .split(/\s+/)
420
+ .filter((t) => t.length >= 2)
421
+ .map((t) => t.replace(/[^a-z0-9]/g, ''))
422
+ .filter(Boolean);
279
423
  if (terms.length === 0) return query;
280
424
  if (terms.length === 1) return terms[0];
281
425
  return terms.join(' OR ');
@@ -111,10 +111,9 @@ export function archive(
111
111
  const reason = options.reason ?? `Archived: older than ${options.olderThanDays} days`;
112
112
 
113
113
  return provider.transaction(() => {
114
- const candidates = provider.all<{ id: string }>(
115
- 'SELECT id FROM entries WHERE updated_at < ?',
116
- [cutoff],
117
- );
114
+ const candidates = provider.all<{ id: string }>('SELECT id FROM entries WHERE updated_at < ?', [
115
+ cutoff,
116
+ ]);
118
117
 
119
118
  if (candidates.length === 0) return { archived: 0 };
120
119
 
@@ -173,14 +172,10 @@ export function registerProject(
173
172
  return getProject(provider, path)!;
174
173
  }
175
174
 
176
- export function getProject(
177
- provider: PersistenceProvider,
178
- path: string,
179
- ): ProjectInfo | null {
180
- const row = provider.get<Record<string, unknown>>(
181
- 'SELECT * FROM projects WHERE path = ?',
182
- [path],
183
- );
175
+ export function getProject(provider: PersistenceProvider, path: string): ProjectInfo | null {
176
+ const row = provider.get<Record<string, unknown>>('SELECT * FROM projects WHERE path = ?', [
177
+ path,
178
+ ]);
184
179
  if (!row) return null;
185
180
  return {
186
181
  path: row.path as string,
@@ -7,13 +7,17 @@ vi.mock('./vault.js', () => {
7
7
  const MockVault = function (this: Record<string, unknown>, path: string) {
8
8
  this._path = path;
9
9
  this.search = vi.fn().mockReturnValue([]);
10
- this.stats = vi.fn().mockReturnValue({ totalEntries: 0, byType: {}, byDomain: {}, bySeverity: {} });
10
+ this.stats = vi
11
+ .fn()
12
+ .mockReturnValue({ totalEntries: 0, byType: {}, byDomain: {}, bySeverity: {} });
11
13
  this.close = vi.fn();
12
- } as unknown as typeof import('./vault.js')['Vault'];
14
+ } as unknown as (typeof import('./vault.js'))['Vault'];
13
15
  return { Vault: MockVault };
14
16
  });
15
17
 
16
- function makeManager(weights?: Partial<Record<'agent' | 'project' | 'team', number>>): VaultManager {
18
+ function makeManager(
19
+ weights?: Partial<Record<'agent' | 'project' | 'team', number>>,
20
+ ): VaultManager {
17
21
  return new VaultManager({ agentId: 'test-agent', weights });
18
22
  }
19
23
 
@@ -74,7 +78,7 @@ describe('VaultManager', () => {
74
78
 
75
79
  it('throws when connecting duplicate named vault', () => {
76
80
  mgr.connect('shared', '/tmp/shared.db');
77
- expect(() => mgr.connect('shared', '/tmp/other.db')).toThrow("already connected");
81
+ expect(() => mgr.connect('shared', '/tmp/other.db')).toThrow('already connected');
78
82
  });
79
83
 
80
84
  it('disconnects a named vault', () => {
@@ -122,14 +126,10 @@ describe('VaultManager', () => {
122
126
  it('deduplicates entries keeping highest weighted score', () => {
123
127
  const entry = { id: 'e1', title: 'Shared' };
124
128
  const agentVault = mgr.open('agent', '/tmp/a.db');
125
- (agentVault.search as ReturnType<typeof vi.fn>).mockReturnValue([
126
- { entry, score: 0.5 },
127
- ]);
129
+ (agentVault.search as ReturnType<typeof vi.fn>).mockReturnValue([{ entry, score: 0.5 }]);
128
130
 
129
131
  const teamVault = mgr.open('team', '/tmp/t.db');
130
- (teamVault.search as ReturnType<typeof vi.fn>).mockReturnValue([
131
- { entry, score: 0.8 },
132
- ]);
132
+ (teamVault.search as ReturnType<typeof vi.fn>).mockReturnValue([{ entry, score: 0.8 }]);
133
133
 
134
134
  const results = mgr.search('test');
135
135
  // Agent: 0.5 * 1.0 = 0.5, Team: 0.8 * 0.6 = 0.48 → agent wins
@@ -105,7 +105,10 @@ export async function syncAllToMarkdown(
105
105
  for (const entry of entries) {
106
106
  const domain = entry.domain || '_general';
107
107
  const slug = titleToSlug(entry.title);
108
- if (!slug) { skipped++; continue; }
108
+ if (!slug) {
109
+ skipped++;
110
+ continue;
111
+ }
109
112
 
110
113
  const filePath = join(knowledgeDir, 'vault', domain, `${slug}.md`);
111
114
  if (existsSync(filePath)) {
@@ -228,16 +228,16 @@ export function pruneMemories(
228
228
  olderThanDays: number,
229
229
  ): { pruned: number } {
230
230
  const cutoff = Math.floor(Date.now() / 1000) - olderThanDays * 86400;
231
- const result = provider.run(
232
- 'DELETE FROM memories WHERE created_at < ? AND archived_at IS NULL',
233
- [cutoff],
234
- );
231
+ const result = provider.run('DELETE FROM memories WHERE created_at < ? AND archived_at IS NULL', [
232
+ cutoff,
233
+ ]);
235
234
  return { pruned: result.changes };
236
235
  }
237
236
 
238
- export function deduplicateMemories(
239
- provider: PersistenceProvider,
240
- ): { removed: number; groups: Array<{ kept: string; removed: string[] }> } {
237
+ export function deduplicateMemories(provider: PersistenceProvider): {
238
+ removed: number;
239
+ groups: Array<{ kept: string; removed: string[] }>;
240
+ } {
241
241
  const dupeRows = provider.all<{ id1: string; id2: string }>(`
242
242
  SELECT m1.id as id1, m2.id as id2
243
243
  FROM memories m1
@@ -87,7 +87,9 @@ function createCoreTables(provider: PersistenceProvider): void {
87
87
  );`);
88
88
 
89
89
  // Add memory columns if missing
90
- const memCols = provider.all<{ name: string }>('PRAGMA table_info(memories)').map((r: { name: string }) => r.name);
90
+ const memCols = provider
91
+ .all<{ name: string }>('PRAGMA table_info(memories)')
92
+ .map((r: { name: string }) => r.name);
91
93
  if (!memCols.includes('intent')) {
92
94
  provider.execSql(`
93
95
  ALTER TABLE memories ADD COLUMN intent TEXT;
@@ -134,8 +136,12 @@ function migrateBrainSchema(provider: PersistenceProvider): void {
134
136
  const hasSource = columns.some((c: { name: string }) => c.name === 'source');
135
137
  if (!hasSource && columns.length > 0) {
136
138
  provider.transaction(() => {
137
- provider.run(`CREATE TABLE brain_feedback_new (id INTEGER PRIMARY KEY AUTOINCREMENT, query TEXT NOT NULL, entry_id TEXT NOT NULL, action TEXT NOT NULL CHECK(action IN ('accepted', 'dismissed', 'modified', 'failed')), source TEXT NOT NULL DEFAULT 'search', confidence REAL NOT NULL DEFAULT 0.6, duration INTEGER, context TEXT NOT NULL DEFAULT '{}', reason TEXT, created_at INTEGER NOT NULL DEFAULT (unixepoch()))`);
138
- provider.run(`INSERT INTO brain_feedback_new (id, query, entry_id, action, created_at) SELECT id, query, entry_id, action, created_at FROM brain_feedback`);
139
+ provider.run(
140
+ `CREATE TABLE brain_feedback_new (id INTEGER PRIMARY KEY AUTOINCREMENT, query TEXT NOT NULL, entry_id TEXT NOT NULL, action TEXT NOT NULL CHECK(action IN ('accepted', 'dismissed', 'modified', 'failed')), source TEXT NOT NULL DEFAULT 'search', confidence REAL NOT NULL DEFAULT 0.6, duration INTEGER, context TEXT NOT NULL DEFAULT '{}', reason TEXT, created_at INTEGER NOT NULL DEFAULT (unixepoch()))`,
141
+ );
142
+ provider.run(
143
+ `INSERT INTO brain_feedback_new (id, query, entry_id, action, created_at) SELECT id, query, entry_id, action, created_at FROM brain_feedback`,
144
+ );
139
145
  provider.run('DROP TABLE brain_feedback');
140
146
  provider.run('ALTER TABLE brain_feedback_new RENAME TO brain_feedback');
141
147
  provider.run('CREATE INDEX IF NOT EXISTS idx_brain_feedback_query ON brain_feedback(query)');
@@ -143,39 +149,90 @@ function migrateBrainSchema(provider: PersistenceProvider): void {
143
149
  }
144
150
  try {
145
151
  const sessionCols = provider.all<{ name: string }>('PRAGMA table_info(brain_sessions)');
146
- if (sessionCols.length > 0 && !sessionCols.some((c: { name: string }) => c.name === 'extracted_at')) {
152
+ if (
153
+ sessionCols.length > 0 &&
154
+ !sessionCols.some((c: { name: string }) => c.name === 'extracted_at')
155
+ ) {
147
156
  provider.run('ALTER TABLE brain_sessions ADD COLUMN extracted_at TEXT');
148
157
  }
149
- } catch { /* brain_sessions doesn't exist yet */ }
158
+ } catch {
159
+ /* brain_sessions doesn't exist yet */
160
+ }
150
161
  }
151
162
 
152
163
  function migrateTemporalSchema(provider: PersistenceProvider): void {
153
- try { provider.run('ALTER TABLE entries ADD COLUMN valid_from INTEGER'); } catch { /* exists */ }
154
- try { provider.run('ALTER TABLE entries ADD COLUMN valid_until INTEGER'); } catch { /* exists */ }
164
+ try {
165
+ provider.run('ALTER TABLE entries ADD COLUMN valid_from INTEGER');
166
+ } catch {
167
+ /* exists */
168
+ }
169
+ try {
170
+ provider.run('ALTER TABLE entries ADD COLUMN valid_until INTEGER');
171
+ } catch {
172
+ /* exists */
173
+ }
155
174
  }
156
175
 
157
176
  function migrateOriginColumn(provider: PersistenceProvider): void {
158
- try { provider.run("ALTER TABLE entries ADD COLUMN origin TEXT NOT NULL DEFAULT 'user' CHECK(origin IN ('agent', 'pack', 'user'))"); } catch { /* exists */ }
177
+ try {
178
+ provider.run(
179
+ "ALTER TABLE entries ADD COLUMN origin TEXT NOT NULL DEFAULT 'user' CHECK(origin IN ('agent', 'pack', 'user'))",
180
+ );
181
+ } catch {
182
+ /* exists */
183
+ }
159
184
  provider.execSql('CREATE INDEX IF NOT EXISTS idx_entries_origin ON entries(origin)');
160
185
  }
161
186
 
162
187
  function migrateContentHash(provider: PersistenceProvider): void {
163
- try { provider.run('ALTER TABLE entries ADD COLUMN content_hash TEXT'); } catch { /* exists */ }
164
- provider.execSql('CREATE INDEX IF NOT EXISTS idx_entries_content_hash ON entries(content_hash) WHERE content_hash IS NOT NULL');
165
- const unhashed = provider.all<{ id: string; type: string; domain: string; title: string; description: string; tags: string; example: string | null; counter_example: string | null }>(
188
+ try {
189
+ provider.run('ALTER TABLE entries ADD COLUMN content_hash TEXT');
190
+ } catch {
191
+ /* exists */
192
+ }
193
+ provider.execSql(
194
+ 'CREATE INDEX IF NOT EXISTS idx_entries_content_hash ON entries(content_hash) WHERE content_hash IS NOT NULL',
195
+ );
196
+ const unhashed = provider.all<{
197
+ id: string;
198
+ type: string;
199
+ domain: string;
200
+ title: string;
201
+ description: string;
202
+ tags: string;
203
+ example: string | null;
204
+ counter_example: string | null;
205
+ }>(
166
206
  'SELECT id, type, domain, title, description, tags, example, counter_example FROM entries WHERE content_hash IS NULL',
167
207
  );
168
208
  if (unhashed.length > 0) {
169
209
  provider.transaction(() => {
170
210
  for (const row of unhashed) {
171
- const hash = computeContentHash({ type: row.type, domain: row.domain, title: row.title, description: row.description, tags: JSON.parse(row.tags), example: row.example ?? undefined, counterExample: row.counter_example ?? undefined });
172
- provider.run('UPDATE entries SET content_hash = @hash WHERE id = @id', { hash, id: row.id });
211
+ const hash = computeContentHash({
212
+ type: row.type,
213
+ domain: row.domain,
214
+ title: row.title,
215
+ description: row.description,
216
+ tags: JSON.parse(row.tags),
217
+ example: row.example ?? undefined,
218
+ counterExample: row.counter_example ?? undefined,
219
+ });
220
+ provider.run('UPDATE entries SET content_hash = @hash WHERE id = @id', {
221
+ hash,
222
+ id: row.id,
223
+ });
173
224
  }
174
225
  });
175
226
  }
176
227
  }
177
228
 
178
229
  function migrateTierColumn(provider: PersistenceProvider): void {
179
- try { provider.run("ALTER TABLE entries ADD COLUMN tier TEXT DEFAULT 'agent'"); } catch { /* exists */ }
180
- provider.execSql('CREATE INDEX IF NOT EXISTS idx_entries_tier ON entries(tier) WHERE tier IS NOT NULL');
230
+ try {
231
+ provider.run("ALTER TABLE entries ADD COLUMN tier TEXT DEFAULT 'agent'");
232
+ } catch {
233
+ /* exists */
234
+ }
235
+ provider.execSql(
236
+ 'CREATE INDEX IF NOT EXISTS idx_entries_tier ON entries(tier) WHERE tier IS NOT NULL',
237
+ );
181
238
  }