@soleri/core 2.10.0 β†’ 2.11.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 (499) hide show
  1. package/dist/agency/agency-manager.d.ts +47 -0
  2. package/dist/agency/agency-manager.d.ts.map +1 -0
  3. package/dist/agency/agency-manager.js +281 -0
  4. package/dist/agency/agency-manager.js.map +1 -0
  5. package/dist/agency/index.d.ts +3 -0
  6. package/dist/agency/index.d.ts.map +1 -0
  7. package/dist/agency/index.js +2 -0
  8. package/dist/agency/index.js.map +1 -0
  9. package/dist/agency/types.d.ts +69 -0
  10. package/dist/agency/types.d.ts.map +1 -0
  11. package/dist/agency/types.js +5 -0
  12. package/dist/agency/types.js.map +1 -0
  13. package/dist/brain/brain.d.ts +0 -1
  14. package/dist/brain/brain.d.ts.map +1 -1
  15. package/dist/brain/brain.js +14 -0
  16. package/dist/brain/brain.js.map +1 -1
  17. package/dist/brain/intelligence.d.ts +5 -1
  18. package/dist/brain/intelligence.d.ts.map +1 -1
  19. package/dist/brain/intelligence.js +83 -0
  20. package/dist/brain/intelligence.js.map +1 -1
  21. package/dist/brain/types.d.ts +21 -0
  22. package/dist/brain/types.d.ts.map +1 -1
  23. package/dist/chat/agent-loop-types.d.ts +82 -0
  24. package/dist/chat/agent-loop-types.d.ts.map +1 -0
  25. package/dist/chat/agent-loop-types.js +8 -0
  26. package/dist/chat/agent-loop-types.js.map +1 -0
  27. package/dist/chat/agent-loop.d.ts +19 -0
  28. package/dist/chat/agent-loop.d.ts.map +1 -0
  29. package/dist/chat/agent-loop.js +261 -0
  30. package/dist/chat/agent-loop.js.map +1 -0
  31. package/dist/chat/auth-manager.d.ts +49 -0
  32. package/dist/chat/auth-manager.d.ts.map +1 -0
  33. package/dist/chat/auth-manager.js +152 -0
  34. package/dist/chat/auth-manager.js.map +1 -0
  35. package/dist/chat/browser-session.d.ts +86 -0
  36. package/dist/chat/browser-session.d.ts.map +1 -0
  37. package/dist/chat/browser-session.js +143 -0
  38. package/dist/chat/browser-session.js.map +1 -0
  39. package/dist/chat/cancellation.d.ts +54 -0
  40. package/dist/chat/cancellation.d.ts.map +1 -0
  41. package/dist/chat/cancellation.js +80 -0
  42. package/dist/chat/cancellation.js.map +1 -0
  43. package/dist/chat/chat-session.d.ts +86 -0
  44. package/dist/chat/chat-session.d.ts.map +1 -0
  45. package/dist/chat/chat-session.js +252 -0
  46. package/dist/chat/chat-session.js.map +1 -0
  47. package/dist/chat/file-handler.d.ts +63 -0
  48. package/dist/chat/file-handler.d.ts.map +1 -0
  49. package/dist/chat/file-handler.js +182 -0
  50. package/dist/chat/file-handler.js.map +1 -0
  51. package/dist/chat/fragment-buffer.d.ts +49 -0
  52. package/dist/chat/fragment-buffer.d.ts.map +1 -0
  53. package/dist/chat/fragment-buffer.js +130 -0
  54. package/dist/chat/fragment-buffer.js.map +1 -0
  55. package/dist/chat/index.d.ts +24 -0
  56. package/dist/chat/index.d.ts.map +1 -0
  57. package/dist/chat/index.js +15 -0
  58. package/dist/chat/index.js.map +1 -0
  59. package/dist/chat/mcp-bridge.d.ts +60 -0
  60. package/dist/chat/mcp-bridge.d.ts.map +1 -0
  61. package/dist/chat/mcp-bridge.js +111 -0
  62. package/dist/chat/mcp-bridge.js.map +1 -0
  63. package/dist/chat/notifications.d.ts +82 -0
  64. package/dist/chat/notifications.d.ts.map +1 -0
  65. package/dist/chat/notifications.js +119 -0
  66. package/dist/chat/notifications.js.map +1 -0
  67. package/dist/chat/output-compressor.d.ts +30 -0
  68. package/dist/chat/output-compressor.d.ts.map +1 -0
  69. package/dist/chat/output-compressor.js +95 -0
  70. package/dist/chat/output-compressor.js.map +1 -0
  71. package/dist/chat/queue.d.ts +91 -0
  72. package/dist/chat/queue.d.ts.map +1 -0
  73. package/dist/chat/queue.js +146 -0
  74. package/dist/chat/queue.js.map +1 -0
  75. package/dist/chat/response-chunker.d.ts +29 -0
  76. package/dist/chat/response-chunker.d.ts.map +1 -0
  77. package/dist/chat/response-chunker.js +163 -0
  78. package/dist/chat/response-chunker.js.map +1 -0
  79. package/dist/chat/self-update.d.ts +62 -0
  80. package/dist/chat/self-update.d.ts.map +1 -0
  81. package/dist/chat/self-update.js +90 -0
  82. package/dist/chat/self-update.js.map +1 -0
  83. package/dist/chat/types.d.ts +105 -0
  84. package/dist/chat/types.d.ts.map +1 -0
  85. package/dist/chat/types.js +8 -0
  86. package/dist/chat/types.js.map +1 -0
  87. package/dist/chat/voice.d.ts +39 -0
  88. package/dist/chat/voice.d.ts.map +1 -0
  89. package/dist/chat/voice.js +80 -0
  90. package/dist/chat/voice.js.map +1 -0
  91. package/dist/claudemd/compose.d.ts +31 -0
  92. package/dist/claudemd/compose.d.ts.map +1 -0
  93. package/dist/claudemd/compose.js +105 -0
  94. package/dist/claudemd/compose.js.map +1 -0
  95. package/dist/claudemd/index.d.ts +5 -0
  96. package/dist/claudemd/index.d.ts.map +1 -0
  97. package/dist/claudemd/index.js +3 -0
  98. package/dist/claudemd/index.js.map +1 -0
  99. package/dist/claudemd/inject.d.ts +31 -0
  100. package/dist/claudemd/inject.d.ts.map +1 -0
  101. package/dist/claudemd/inject.js +157 -0
  102. package/dist/claudemd/inject.js.map +1 -0
  103. package/dist/claudemd/types.d.ts +41 -0
  104. package/dist/claudemd/types.d.ts.map +1 -0
  105. package/dist/claudemd/types.js +5 -0
  106. package/dist/claudemd/types.js.map +1 -0
  107. package/dist/context/context-engine.d.ts +31 -0
  108. package/dist/context/context-engine.d.ts.map +1 -0
  109. package/dist/context/context-engine.js +245 -0
  110. package/dist/context/context-engine.js.map +1 -0
  111. package/dist/context/index.d.ts +3 -0
  112. package/dist/context/index.d.ts.map +1 -0
  113. package/dist/context/index.js +2 -0
  114. package/dist/context/index.js.map +1 -0
  115. package/dist/context/types.d.ts +54 -0
  116. package/dist/context/types.d.ts.map +1 -0
  117. package/dist/context/types.js +5 -0
  118. package/dist/context/types.js.map +1 -0
  119. package/dist/enforcement/adapters/claude-code.d.ts +18 -0
  120. package/dist/enforcement/adapters/claude-code.d.ts.map +1 -0
  121. package/dist/enforcement/adapters/claude-code.js +106 -0
  122. package/dist/enforcement/adapters/claude-code.js.map +1 -0
  123. package/dist/enforcement/adapters/index.d.ts +2 -0
  124. package/dist/enforcement/adapters/index.d.ts.map +1 -0
  125. package/dist/enforcement/adapters/index.js +2 -0
  126. package/dist/enforcement/adapters/index.js.map +1 -0
  127. package/dist/enforcement/index.d.ts +4 -0
  128. package/dist/enforcement/index.d.ts.map +1 -0
  129. package/dist/enforcement/index.js +3 -0
  130. package/dist/enforcement/index.js.map +1 -0
  131. package/dist/enforcement/registry.d.ts +23 -0
  132. package/dist/enforcement/registry.d.ts.map +1 -0
  133. package/dist/enforcement/registry.js +63 -0
  134. package/dist/enforcement/registry.js.map +1 -0
  135. package/dist/enforcement/types.d.ts +51 -0
  136. package/dist/enforcement/types.d.ts.map +1 -0
  137. package/dist/enforcement/types.js +8 -0
  138. package/dist/enforcement/types.js.map +1 -0
  139. package/dist/facades/facade-factory.d.ts +10 -3
  140. package/dist/facades/facade-factory.d.ts.map +1 -1
  141. package/dist/facades/facade-factory.js +94 -5
  142. package/dist/facades/facade-factory.js.map +1 -1
  143. package/dist/facades/types.d.ts +15 -1
  144. package/dist/facades/types.d.ts.map +1 -1
  145. package/dist/facades/types.js +6 -0
  146. package/dist/facades/types.js.map +1 -1
  147. package/dist/health/health-registry.d.ts +40 -0
  148. package/dist/health/health-registry.d.ts.map +1 -0
  149. package/dist/health/health-registry.js +134 -0
  150. package/dist/health/health-registry.js.map +1 -0
  151. package/dist/health/index.d.ts +5 -0
  152. package/dist/health/index.d.ts.map +1 -0
  153. package/dist/health/index.js +3 -0
  154. package/dist/health/index.js.map +1 -0
  155. package/dist/health/vault-integrity.d.ts +13 -0
  156. package/dist/health/vault-integrity.d.ts.map +1 -0
  157. package/dist/health/vault-integrity.js +49 -0
  158. package/dist/health/vault-integrity.js.map +1 -0
  159. package/dist/index.d.ts +67 -6
  160. package/dist/index.d.ts.map +1 -1
  161. package/dist/index.js +51 -3
  162. package/dist/index.js.map +1 -1
  163. package/dist/intake/intake-pipeline.d.ts +0 -7
  164. package/dist/intake/intake-pipeline.d.ts.map +1 -1
  165. package/dist/intake/intake-pipeline.js +1 -1
  166. package/dist/intake/intake-pipeline.js.map +1 -1
  167. package/dist/intelligence/types.d.ts +1 -0
  168. package/dist/intelligence/types.d.ts.map +1 -1
  169. package/dist/migrations/index.d.ts +6 -0
  170. package/dist/migrations/index.d.ts.map +1 -0
  171. package/dist/migrations/index.js +5 -0
  172. package/dist/migrations/index.js.map +1 -0
  173. package/dist/migrations/migration-runner.d.ts +51 -0
  174. package/dist/migrations/migration-runner.d.ts.map +1 -0
  175. package/dist/migrations/migration-runner.js +141 -0
  176. package/dist/migrations/migration-runner.js.map +1 -0
  177. package/dist/packs/index.d.ts +10 -0
  178. package/dist/packs/index.d.ts.map +1 -0
  179. package/dist/packs/index.js +8 -0
  180. package/dist/packs/index.js.map +1 -0
  181. package/dist/packs/lockfile.d.ts +97 -0
  182. package/dist/packs/lockfile.d.ts.map +1 -0
  183. package/dist/packs/lockfile.js +129 -0
  184. package/dist/packs/lockfile.js.map +1 -0
  185. package/dist/packs/pack-installer.d.ts +41 -0
  186. package/dist/packs/pack-installer.d.ts.map +1 -0
  187. package/dist/packs/pack-installer.js +253 -0
  188. package/dist/packs/pack-installer.js.map +1 -0
  189. package/dist/packs/resolver.d.ts +51 -0
  190. package/dist/packs/resolver.d.ts.map +1 -0
  191. package/dist/packs/resolver.js +195 -0
  192. package/dist/packs/resolver.js.map +1 -0
  193. package/dist/packs/types.d.ts +186 -0
  194. package/dist/packs/types.d.ts.map +1 -0
  195. package/dist/packs/types.js +69 -0
  196. package/dist/packs/types.js.map +1 -0
  197. package/dist/persistence/postgres-provider.d.ts +42 -7
  198. package/dist/persistence/postgres-provider.d.ts.map +1 -1
  199. package/dist/persistence/postgres-provider.js +187 -46
  200. package/dist/persistence/postgres-provider.js.map +1 -1
  201. package/dist/playbooks/index.d.ts +2 -0
  202. package/dist/playbooks/index.d.ts.map +1 -1
  203. package/dist/playbooks/index.js +2 -0
  204. package/dist/playbooks/index.js.map +1 -1
  205. package/dist/playbooks/playbook-executor.d.ts +100 -0
  206. package/dist/playbooks/playbook-executor.d.ts.map +1 -0
  207. package/dist/playbooks/playbook-executor.js +207 -0
  208. package/dist/playbooks/playbook-executor.js.map +1 -0
  209. package/dist/plugins/index.d.ts +7 -0
  210. package/dist/plugins/index.d.ts.map +1 -0
  211. package/dist/plugins/index.js +7 -0
  212. package/dist/plugins/index.js.map +1 -0
  213. package/dist/plugins/plugin-loader.d.ts +28 -0
  214. package/dist/plugins/plugin-loader.d.ts.map +1 -0
  215. package/dist/plugins/plugin-loader.js +150 -0
  216. package/dist/plugins/plugin-loader.js.map +1 -0
  217. package/dist/plugins/plugin-registry.d.ts +58 -0
  218. package/dist/plugins/plugin-registry.d.ts.map +1 -0
  219. package/dist/plugins/plugin-registry.js +157 -0
  220. package/dist/plugins/plugin-registry.js.map +1 -0
  221. package/dist/plugins/types.d.ts +180 -0
  222. package/dist/plugins/types.d.ts.map +1 -0
  223. package/dist/plugins/types.js +48 -0
  224. package/dist/plugins/types.js.map +1 -0
  225. package/dist/runtime/admin-extra-ops.d.ts.map +1 -1
  226. package/dist/runtime/admin-extra-ops.js +181 -8
  227. package/dist/runtime/admin-extra-ops.js.map +1 -1
  228. package/dist/runtime/capture-ops.d.ts.map +1 -1
  229. package/dist/runtime/capture-ops.js +106 -7
  230. package/dist/runtime/capture-ops.js.map +1 -1
  231. package/dist/runtime/deprecation.d.ts +33 -0
  232. package/dist/runtime/deprecation.d.ts.map +1 -0
  233. package/dist/runtime/deprecation.js +41 -0
  234. package/dist/runtime/deprecation.js.map +1 -0
  235. package/dist/runtime/facades/admin-facade.d.ts.map +1 -1
  236. package/dist/runtime/facades/admin-facade.js +12 -1
  237. package/dist/runtime/facades/admin-facade.js.map +1 -1
  238. package/dist/runtime/facades/agency-facade.d.ts +7 -0
  239. package/dist/runtime/facades/agency-facade.d.ts.map +1 -0
  240. package/dist/runtime/facades/agency-facade.js +103 -0
  241. package/dist/runtime/facades/agency-facade.js.map +1 -0
  242. package/dist/runtime/facades/brain-facade.d.ts.map +1 -1
  243. package/dist/runtime/facades/brain-facade.js +58 -0
  244. package/dist/runtime/facades/brain-facade.js.map +1 -1
  245. package/dist/runtime/facades/chat-facade.d.ts +7 -0
  246. package/dist/runtime/facades/chat-facade.d.ts.map +1 -0
  247. package/dist/runtime/facades/chat-facade.js +808 -0
  248. package/dist/runtime/facades/chat-facade.js.map +1 -0
  249. package/dist/runtime/facades/context-facade.d.ts +7 -0
  250. package/dist/runtime/facades/context-facade.d.ts.map +1 -0
  251. package/dist/runtime/facades/context-facade.js +45 -0
  252. package/dist/runtime/facades/context-facade.js.map +1 -0
  253. package/dist/runtime/facades/index.d.ts.map +1 -1
  254. package/dist/runtime/facades/index.js +18 -0
  255. package/dist/runtime/facades/index.js.map +1 -1
  256. package/dist/runtime/facades/vault-facade.d.ts.map +1 -1
  257. package/dist/runtime/facades/vault-facade.js +247 -1
  258. package/dist/runtime/facades/vault-facade.js.map +1 -1
  259. package/dist/runtime/feature-flags.d.ts +18 -0
  260. package/dist/runtime/feature-flags.d.ts.map +1 -0
  261. package/dist/runtime/feature-flags.js +90 -0
  262. package/dist/runtime/feature-flags.js.map +1 -0
  263. package/dist/runtime/pack-ops.d.ts +9 -0
  264. package/dist/runtime/pack-ops.d.ts.map +1 -0
  265. package/dist/runtime/pack-ops.js +76 -0
  266. package/dist/runtime/pack-ops.js.map +1 -0
  267. package/dist/runtime/playbook-ops.d.ts +3 -7
  268. package/dist/runtime/playbook-ops.d.ts.map +1 -1
  269. package/dist/runtime/playbook-ops.js +101 -10
  270. package/dist/runtime/playbook-ops.js.map +1 -1
  271. package/dist/runtime/plugin-ops.d.ts +9 -0
  272. package/dist/runtime/plugin-ops.d.ts.map +1 -0
  273. package/dist/runtime/plugin-ops.js +235 -0
  274. package/dist/runtime/plugin-ops.js.map +1 -0
  275. package/dist/runtime/runtime.d.ts.map +1 -1
  276. package/dist/runtime/runtime.js +72 -5
  277. package/dist/runtime/runtime.js.map +1 -1
  278. package/dist/runtime/telemetry-ops.d.ts +10 -0
  279. package/dist/runtime/telemetry-ops.d.ts.map +1 -0
  280. package/dist/runtime/telemetry-ops.js +53 -0
  281. package/dist/runtime/telemetry-ops.js.map +1 -0
  282. package/dist/runtime/types.d.ts +35 -0
  283. package/dist/runtime/types.d.ts.map +1 -1
  284. package/dist/runtime/vault-sharing-ops.d.ts +13 -0
  285. package/dist/runtime/vault-sharing-ops.d.ts.map +1 -0
  286. package/dist/runtime/vault-sharing-ops.js +345 -0
  287. package/dist/runtime/vault-sharing-ops.js.map +1 -0
  288. package/dist/streams/index.d.ts +1 -1
  289. package/dist/streams/index.d.ts.map +1 -1
  290. package/dist/streams/index.js +1 -1
  291. package/dist/streams/index.js.map +1 -1
  292. package/dist/streams/replayable-stream.d.ts +13 -1
  293. package/dist/streams/replayable-stream.d.ts.map +1 -1
  294. package/dist/streams/replayable-stream.js +27 -3
  295. package/dist/streams/replayable-stream.js.map +1 -1
  296. package/dist/text/similarity.d.ts +0 -1
  297. package/dist/text/similarity.d.ts.map +1 -1
  298. package/dist/text/similarity.js +1 -1
  299. package/dist/text/similarity.js.map +1 -1
  300. package/dist/transport/http-server.d.ts +56 -0
  301. package/dist/transport/http-server.d.ts.map +1 -0
  302. package/dist/transport/http-server.js +210 -0
  303. package/dist/transport/http-server.js.map +1 -0
  304. package/dist/transport/index.d.ts +11 -0
  305. package/dist/transport/index.d.ts.map +1 -0
  306. package/dist/transport/index.js +10 -0
  307. package/dist/transport/index.js.map +1 -0
  308. package/dist/transport/lsp-server.d.ts +140 -0
  309. package/dist/transport/lsp-server.d.ts.map +1 -0
  310. package/dist/transport/lsp-server.js +239 -0
  311. package/dist/transport/lsp-server.js.map +1 -0
  312. package/dist/transport/rate-limiter.d.ts +35 -0
  313. package/dist/transport/rate-limiter.d.ts.map +1 -0
  314. package/dist/transport/rate-limiter.js +72 -0
  315. package/dist/transport/rate-limiter.js.map +1 -0
  316. package/dist/transport/session-manager.d.ts +49 -0
  317. package/dist/transport/session-manager.d.ts.map +1 -0
  318. package/dist/transport/session-manager.js +83 -0
  319. package/dist/transport/session-manager.js.map +1 -0
  320. package/dist/transport/token-auth.d.ts +29 -0
  321. package/dist/transport/token-auth.d.ts.map +1 -0
  322. package/dist/transport/token-auth.js +84 -0
  323. package/dist/transport/token-auth.js.map +1 -0
  324. package/dist/transport/types.d.ts +61 -0
  325. package/dist/transport/types.d.ts.map +1 -0
  326. package/dist/transport/types.js +5 -0
  327. package/dist/transport/types.js.map +1 -0
  328. package/dist/transport/ws-server.d.ts +78 -0
  329. package/dist/transport/ws-server.d.ts.map +1 -0
  330. package/dist/transport/ws-server.js +342 -0
  331. package/dist/transport/ws-server.js.map +1 -0
  332. package/dist/vault/git-vault-sync.d.ts +107 -0
  333. package/dist/vault/git-vault-sync.d.ts.map +1 -0
  334. package/dist/vault/git-vault-sync.js +251 -0
  335. package/dist/vault/git-vault-sync.js.map +1 -0
  336. package/dist/vault/knowledge-review.d.ts +67 -0
  337. package/dist/vault/knowledge-review.d.ts.map +1 -0
  338. package/dist/vault/knowledge-review.js +133 -0
  339. package/dist/vault/knowledge-review.js.map +1 -0
  340. package/dist/vault/obsidian-sync.d.ts +94 -0
  341. package/dist/vault/obsidian-sync.d.ts.map +1 -0
  342. package/dist/vault/obsidian-sync.js +247 -0
  343. package/dist/vault/obsidian-sync.js.map +1 -0
  344. package/dist/vault/scope-detector.d.ts +31 -0
  345. package/dist/vault/scope-detector.d.ts.map +1 -0
  346. package/dist/vault/scope-detector.js +182 -0
  347. package/dist/vault/scope-detector.js.map +1 -0
  348. package/dist/vault/vault-branching.d.ts +71 -0
  349. package/dist/vault/vault-branching.d.ts.map +1 -0
  350. package/dist/vault/vault-branching.js +180 -0
  351. package/dist/vault/vault-branching.js.map +1 -0
  352. package/dist/vault/vault-manager.d.ts +89 -0
  353. package/dist/vault/vault-manager.d.ts.map +1 -0
  354. package/dist/vault/vault-manager.js +199 -0
  355. package/dist/vault/vault-manager.js.map +1 -0
  356. package/dist/vault/vault-types.d.ts +30 -0
  357. package/dist/vault/vault-types.d.ts.map +1 -0
  358. package/dist/vault/vault-types.js +10 -0
  359. package/dist/vault/vault-types.js.map +1 -0
  360. package/dist/vault/vault.d.ts +10 -0
  361. package/dist/vault/vault.d.ts.map +1 -1
  362. package/dist/vault/vault.js +36 -3
  363. package/dist/vault/vault.js.map +1 -1
  364. package/package.json +1 -1
  365. package/src/__tests__/admin-extra-ops.test.ts +31 -11
  366. package/src/__tests__/agency-manager.test.ts +374 -0
  367. package/src/__tests__/agent-loop.test.ts +256 -0
  368. package/src/__tests__/capture-ops.test.ts +275 -0
  369. package/src/__tests__/chat-differentiators.test.ts +251 -0
  370. package/src/__tests__/chat-enhanced.test.ts +390 -0
  371. package/src/__tests__/chat-transport.test.ts +665 -0
  372. package/src/__tests__/claudemd.test.ts +282 -0
  373. package/src/__tests__/context-engine.test.ts +256 -0
  374. package/src/__tests__/core-ops.test.ts +97 -5
  375. package/src/__tests__/deprecation.test.ts +78 -0
  376. package/src/__tests__/enforcement.test.ts +153 -0
  377. package/src/__tests__/facade-factory.test.ts +271 -0
  378. package/src/__tests__/feature-flags.test.ts +138 -0
  379. package/src/__tests__/git-vault-sync.test.ts +230 -0
  380. package/src/__tests__/health-registry.test.ts +173 -0
  381. package/src/__tests__/knowledge-review.test.ts +104 -0
  382. package/src/__tests__/lsp-transport.test.ts +442 -0
  383. package/src/__tests__/migration-runner.test.ts +170 -0
  384. package/src/__tests__/normalize.test.ts +10 -0
  385. package/src/__tests__/obsidian-sync.test.ts +354 -0
  386. package/src/__tests__/pack-lockfile.test.ts +261 -0
  387. package/src/__tests__/pack-ops.test.ts +146 -0
  388. package/src/__tests__/pack-system.test.ts +423 -0
  389. package/src/__tests__/playbook-executor.test.ts +249 -0
  390. package/src/__tests__/playbook-ops-execution.test.ts +189 -0
  391. package/src/__tests__/plugin-ops.test.ts +411 -0
  392. package/src/__tests__/plugin-system.test.ts +509 -0
  393. package/src/__tests__/postgres-provider.test.ts +64 -6
  394. package/src/__tests__/replayable-stream.test.ts +112 -1
  395. package/src/__tests__/scope-detector.test.ts +121 -0
  396. package/src/__tests__/session-lifecycle.test.ts +259 -0
  397. package/src/__tests__/transport.test.ts +758 -0
  398. package/src/__tests__/vault-branching.test.ts +274 -0
  399. package/src/__tests__/vault-connect.test.ts +179 -0
  400. package/src/__tests__/vault-integrity.test.ts +71 -0
  401. package/src/__tests__/vault-manager.test.ts +238 -0
  402. package/src/__tests__/vault-scaling.test.ts +281 -0
  403. package/src/__tests__/vault-sharing.test.ts +270 -0
  404. package/src/__tests__/ws-transport.test.ts +479 -0
  405. package/src/agency/agency-manager.ts +326 -0
  406. package/src/agency/index.ts +13 -0
  407. package/src/agency/types.ts +88 -0
  408. package/src/brain/brain.ts +15 -11
  409. package/src/brain/intelligence.ts +103 -0
  410. package/src/brain/types.ts +26 -0
  411. package/src/chat/agent-loop-types.ts +99 -0
  412. package/src/chat/agent-loop.ts +357 -0
  413. package/src/chat/auth-manager.ts +171 -0
  414. package/src/chat/browser-session.ts +188 -0
  415. package/src/chat/cancellation.ts +99 -0
  416. package/src/chat/chat-session.ts +283 -0
  417. package/src/chat/file-handler.ts +230 -0
  418. package/src/chat/fragment-buffer.ts +160 -0
  419. package/src/chat/index.ts +72 -0
  420. package/src/chat/mcp-bridge.ts +135 -0
  421. package/src/chat/notifications.ts +164 -0
  422. package/src/chat/output-compressor.ts +116 -0
  423. package/src/chat/queue.ts +208 -0
  424. package/src/chat/response-chunker.ts +200 -0
  425. package/src/chat/self-update.ts +117 -0
  426. package/src/chat/types.ts +126 -0
  427. package/src/chat/voice.ts +134 -0
  428. package/src/claudemd/compose.ts +142 -0
  429. package/src/claudemd/index.ts +17 -0
  430. package/src/claudemd/inject.ts +170 -0
  431. package/src/claudemd/types.ts +45 -0
  432. package/src/context/context-engine.ts +302 -0
  433. package/src/context/index.ts +11 -0
  434. package/src/context/types.ts +69 -0
  435. package/src/enforcement/adapters/claude-code.ts +135 -0
  436. package/src/enforcement/adapters/index.ts +1 -0
  437. package/src/enforcement/index.ts +10 -0
  438. package/src/enforcement/registry.ts +82 -0
  439. package/src/enforcement/types.ts +56 -0
  440. package/src/facades/facade-factory.ts +138 -5
  441. package/src/facades/types.ts +21 -0
  442. package/src/health/health-registry.ts +165 -0
  443. package/src/health/index.ts +11 -0
  444. package/src/health/vault-integrity.ts +66 -0
  445. package/src/index.ts +294 -2
  446. package/src/intake/intake-pipeline.ts +1 -1
  447. package/src/intelligence/types.ts +1 -0
  448. package/src/migrations/index.ts +6 -0
  449. package/src/migrations/migration-runner.ts +185 -0
  450. package/src/packs/index.ts +20 -0
  451. package/src/packs/lockfile.ts +180 -0
  452. package/src/packs/pack-installer.ts +289 -0
  453. package/src/packs/resolver.ts +237 -0
  454. package/src/packs/types.ts +125 -0
  455. package/src/persistence/postgres-provider.ts +211 -58
  456. package/src/playbooks/index.ts +11 -0
  457. package/src/playbooks/playbook-executor.ts +301 -0
  458. package/src/plugins/index.ts +19 -0
  459. package/src/plugins/plugin-loader.ts +183 -0
  460. package/src/plugins/plugin-registry.ts +187 -0
  461. package/src/plugins/types.ts +119 -0
  462. package/src/runtime/admin-extra-ops.ts +193 -8
  463. package/src/runtime/capture-ops.ts +113 -8
  464. package/src/runtime/deprecation.ts +58 -0
  465. package/src/runtime/facades/admin-facade.ts +16 -1
  466. package/src/runtime/facades/agency-facade.ts +111 -0
  467. package/src/runtime/facades/brain-facade.ts +60 -0
  468. package/src/runtime/facades/chat-facade.ts +918 -0
  469. package/src/runtime/facades/context-facade.ts +55 -0
  470. package/src/runtime/facades/index.ts +22 -1
  471. package/src/runtime/facades/vault-facade.ts +261 -1
  472. package/src/runtime/feature-flags.ts +101 -0
  473. package/src/runtime/pack-ops.ts +85 -0
  474. package/src/runtime/playbook-ops.ts +113 -9
  475. package/src/runtime/plugin-ops.ts +258 -0
  476. package/src/runtime/runtime.ts +84 -5
  477. package/src/runtime/telemetry-ops.ts +57 -0
  478. package/src/runtime/types.ts +35 -0
  479. package/src/runtime/vault-sharing-ops.ts +372 -0
  480. package/src/streams/index.ts +1 -1
  481. package/src/streams/replayable-stream.ts +34 -3
  482. package/src/text/similarity.ts +1 -1
  483. package/src/transport/http-server.ts +269 -0
  484. package/src/transport/index.ts +48 -0
  485. package/src/transport/lsp-server.ts +401 -0
  486. package/src/transport/rate-limiter.ts +97 -0
  487. package/src/transport/session-manager.ts +120 -0
  488. package/src/transport/token-auth.ts +96 -0
  489. package/src/transport/types.ts +66 -0
  490. package/src/transport/ws-server.ts +415 -0
  491. package/src/vault/git-vault-sync.ts +318 -0
  492. package/src/vault/knowledge-review.ts +221 -0
  493. package/src/vault/obsidian-sync.ts +346 -0
  494. package/src/vault/scope-detector.ts +219 -0
  495. package/src/vault/vault-branching.ts +264 -0
  496. package/src/vault/vault-manager.ts +237 -0
  497. package/src/vault/vault-types.ts +50 -0
  498. package/src/vault/vault.ts +41 -3
  499. package/src/governance/index.ts +0 -18
@@ -1,5 +1,5 @@
1
1
  import { describe, it, expect } from 'vitest';
2
- import { ReplayableStream } from '../streams/replayable-stream.js';
2
+ import { ReplayableStream, fanOut } from '../streams/replayable-stream.js';
3
3
 
4
4
  async function* generate(items: number[]): AsyncIterable<number> {
5
5
  for (const item of items) yield item;
@@ -63,4 +63,115 @@ describe('ReplayableStream', () => {
63
63
  for await (const item of stream) items.push(item);
64
64
  expect(items).toEqual([5, 6]);
65
65
  });
66
+
67
+ it('works with slow async source', async () => {
68
+ async function* slow(): AsyncIterable<number> {
69
+ for (let i = 1; i <= 3; i++) {
70
+ await new Promise((r) => setTimeout(r, 10));
71
+ yield i;
72
+ }
73
+ }
74
+ const stream = new ReplayableStream(slow());
75
+ expect(await stream.collect()).toEqual([1, 2, 3]);
76
+ });
77
+
78
+ it('concurrent consumers on slow source all see identical results', async () => {
79
+ async function* slow(): AsyncIterable<number> {
80
+ for (let i = 1; i <= 5; i++) {
81
+ await new Promise((r) => setTimeout(r, 5));
82
+ yield i;
83
+ }
84
+ }
85
+ const stream = new ReplayableStream(slow());
86
+ const [a, b, c] = await Promise.all([stream.collect(), stream.collect(), stream.collect()]);
87
+ expect(a).toEqual([1, 2, 3, 4, 5]);
88
+ expect(b).toEqual([1, 2, 3, 4, 5]);
89
+ expect(c).toEqual([1, 2, 3, 4, 5]);
90
+ });
91
+
92
+ it('late consumer replays full stream after completion', async () => {
93
+ const stream = new ReplayableStream(generate([10, 20, 30]));
94
+ // Drain the stream first
95
+ await stream.collect();
96
+ expect(stream.isDone).toBe(true);
97
+ // Late consumer should still see everything
98
+ expect(await stream.collect()).toEqual([10, 20, 30]);
99
+ });
100
+
101
+ it('maxBuffer evicts oldest entries', async () => {
102
+ const stream = new ReplayableStream(generate([1, 2, 3, 4, 5]), {
103
+ maxBuffer: 3,
104
+ });
105
+ await stream.collect();
106
+ // Buffer should only hold the last 3 items
107
+ expect(stream.bufferedCount).toBe(3);
108
+ });
109
+
110
+ it('late consumer throws when evicted items are needed', async () => {
111
+ async function* slow(): AsyncIterable<number> {
112
+ for (let i = 1; i <= 5; i++) {
113
+ await new Promise((r) => setTimeout(r, 2));
114
+ yield i;
115
+ }
116
+ }
117
+ const stream = new ReplayableStream(slow(), { maxBuffer: 2 });
118
+ // Drain the stream fully β€” evicts items 1, 2, 3
119
+ await stream.collect();
120
+ // Late consumer starts at index 0 but items 0-2 are evicted
121
+ await expect(stream.collect()).rejects.toThrow('consumer fell behind');
122
+ });
123
+ });
124
+
125
+ describe('fanOut', () => {
126
+ it('feeds one source to multiple consumers in parallel', async () => {
127
+ async function* source(): AsyncIterable<string> {
128
+ yield 'plan-result';
129
+ yield 'session-data';
130
+ yield 'extraction';
131
+ }
132
+
133
+ const brainLog: string[] = [];
134
+ const vaultLog: string[] = [];
135
+ const cogneeLog: string[] = [];
136
+
137
+ await fanOut(source(), [
138
+ async (items) => {
139
+ for await (const item of items) brainLog.push(`brain:${item}`);
140
+ },
141
+ async (items) => {
142
+ for await (const item of items) vaultLog.push(`vault:${item}`);
143
+ },
144
+ async (items) => {
145
+ for await (const item of items) cogneeLog.push(`cognee:${item}`);
146
+ },
147
+ ]);
148
+
149
+ const expected = ['plan-result', 'session-data', 'extraction'];
150
+ expect(brainLog).toEqual(expected.map((e) => `brain:${e}`));
151
+ expect(vaultLog).toEqual(expected.map((e) => `vault:${e}`));
152
+ expect(cogneeLog).toEqual(expected.map((e) => `cognee:${e}`));
153
+ });
154
+
155
+ it('source executes only once with fanOut', async () => {
156
+ let callCount = 0;
157
+ async function* tracked(): AsyncIterable<number> {
158
+ callCount++;
159
+ yield 1;
160
+ yield 2;
161
+ }
162
+
163
+ const results: number[][] = [[], []];
164
+ await fanOut(tracked(), [
165
+ async (items) => {
166
+ for await (const item of items) results[0].push(item);
167
+ },
168
+ async (items) => {
169
+ for await (const item of items) results[1].push(item);
170
+ },
171
+ ]);
172
+
173
+ expect(callCount).toBe(1);
174
+ expect(results[0]).toEqual([1, 2]);
175
+ expect(results[1]).toEqual([1, 2]);
176
+ });
66
177
  });
@@ -0,0 +1,121 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { detectScope } from '../vault/scope-detector.js';
3
+ import type { ScopeInput } from '../vault/scope-detector.js';
4
+
5
+ describe('detectScope', () => {
6
+ it('classifies project-specific paths as project tier with HIGH confidence', () => {
7
+ const input: ScopeInput = {
8
+ title: 'Monorepo package structure',
9
+ description: 'In packages/core/src/vault.ts, we use SQLite for persistence.',
10
+ tags: ['project-specific'],
11
+ };
12
+ const result = detectScope(input);
13
+ expect(result.tier).toBe('project');
14
+ expect(result.confidence).toBe('HIGH');
15
+ });
16
+
17
+ it('classifies universal TypeScript patterns as team tier', () => {
18
+ const input: ScopeInput = {
19
+ title: 'Type safety best practice',
20
+ description: 'Always use type guards instead of type assertions for runtime safety.',
21
+ tags: ['typescript', 'best-practice'],
22
+ };
23
+ const result = detectScope(input);
24
+ expect(result.tier).toBe('team');
25
+ });
26
+
27
+ it('classifies personal preferences as agent tier', () => {
28
+ const input: ScopeInput = {
29
+ title: 'My editor setup',
30
+ description: 'I prefer using vim keybindings in my IDE. My workflow involves ~/projects.',
31
+ tags: ['preference'],
32
+ };
33
+ const result = detectScope(input);
34
+ expect(result.tier).toBe('agent');
35
+ });
36
+
37
+ it('classifies accessibility concepts as team tier', () => {
38
+ const input: ScopeInput = {
39
+ title: 'WCAG contrast requirements',
40
+ description: 'All text must meet WCAG AA contrast ratio of 4.5:1 for accessibility.',
41
+ category: 'accessibility',
42
+ };
43
+ const result = detectScope(input);
44
+ expect(result.tier).toBe('team');
45
+ expect(result.confidence).toBe('HIGH');
46
+ });
47
+
48
+ it('returns LOW confidence for mixed signals', () => {
49
+ const input: ScopeInput = {
50
+ title: 'Clean code in our codebase',
51
+ description: 'Our project follows clean code principles. In packages/core we enforce SOLID.',
52
+ tags: ['best-practice', 'project-specific'],
53
+ };
54
+ const result = detectScope(input);
55
+ // Should have LOW or MEDIUM confidence due to conflicting signals
56
+ expect(['LOW', 'MEDIUM']).toContain(result.confidence);
57
+ });
58
+
59
+ it('defaults to agent tier with LOW confidence when no signals', () => {
60
+ const input: ScopeInput = {
61
+ title: 'Random note',
62
+ description: 'Something happened today.',
63
+ };
64
+ const result = detectScope(input);
65
+ expect(result.tier).toBe('agent');
66
+ expect(result.confidence).toBe('LOW');
67
+ expect(result.reason).toContain('No clear signals');
68
+ });
69
+
70
+ it('detects scoped packages as project-specific', () => {
71
+ const input: ScopeInput = {
72
+ title: 'Package usage',
73
+ description: 'Use @soleri/core for all vault operations, not direct SQLite.',
74
+ };
75
+ const result = detectScope(input);
76
+ expect(result.tier).toBe('project');
77
+ });
78
+
79
+ it('detects security patterns as team tier', () => {
80
+ const input: ScopeInput = {
81
+ title: 'XSS prevention',
82
+ description: 'Sanitize all user input to prevent XSS injection attacks.',
83
+ category: 'security',
84
+ tags: ['security'],
85
+ };
86
+ const result = detectScope(input);
87
+ expect(result.tier).toBe('team');
88
+ expect(result.confidence).toBe('HIGH');
89
+ });
90
+
91
+ it('includes signals in result', () => {
92
+ const input: ScopeInput = {
93
+ title: 'Focus ring pattern',
94
+ description:
95
+ 'All interactive elements must have a visible focus ring for keyboard navigation.',
96
+ };
97
+ const result = detectScope(input);
98
+ expect(result.signals.length).toBeGreaterThan(0);
99
+ expect(result.signals.some((s) => s.source === 'content')).toBe(true);
100
+ });
101
+
102
+ it('category signal boosts classification', () => {
103
+ const input: ScopeInput = {
104
+ title: 'Color palette choice',
105
+ description: 'Use harmonious color palettes for UI consistency.',
106
+ category: 'design',
107
+ };
108
+ const result = detectScope(input);
109
+ expect(result.tier).toBe('team');
110
+ });
111
+
112
+ it('agent tags classify as agent', () => {
113
+ const input: ScopeInput = {
114
+ title: 'Terminal config',
115
+ description: 'Set up zsh with custom prompt.',
116
+ tags: ['personal', 'workflow'],
117
+ };
118
+ const result = detectScope(input);
119
+ expect(result.tier).toBe('agent');
120
+ });
121
+ });
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Session Lifecycle Tests β€” list, get, quality scoring, replay.
3
+ *
4
+ * Covers the #178 additions to BrainIntelligence: public session query,
5
+ * 4-dimension quality scoring, and session replay.
6
+ */
7
+
8
+ import { describe, test, expect, beforeEach } from 'vitest';
9
+ import { Vault } from '../vault/vault.js';
10
+ import { Brain } from '../brain/brain.js';
11
+ import { BrainIntelligence } from '../brain/intelligence.js';
12
+
13
+ describe('Session Lifecycle', () => {
14
+ let vault: Vault;
15
+ let brain: Brain;
16
+ let intelligence: BrainIntelligence;
17
+
18
+ beforeEach(() => {
19
+ vault = new Vault(':memory:');
20
+ brain = new Brain(vault);
21
+ intelligence = new BrainIntelligence(vault, brain);
22
+ });
23
+
24
+ // ─── getSessionById ─────────────────────────────────────────────
25
+
26
+ test('getSessionById returns null for non-existent session', () => {
27
+ expect(intelligence.getSessionById('non-existent')).toBeNull();
28
+ });
29
+
30
+ test('getSessionById returns session after lifecycle start', () => {
31
+ const session = intelligence.lifecycle({ action: 'start', domain: 'design' });
32
+ const found = intelligence.getSessionById(session.id);
33
+ expect(found).not.toBeNull();
34
+ expect(found!.id).toBe(session.id);
35
+ expect(found!.domain).toBe('design');
36
+ });
37
+
38
+ // ─── listSessions ──────────────────────────────────────────────
39
+
40
+ test('listSessions returns all sessions', () => {
41
+ intelligence.lifecycle({ action: 'start', domain: 'design' });
42
+ intelligence.lifecycle({ action: 'start', domain: 'a11y' });
43
+ intelligence.lifecycle({ action: 'start', domain: 'performance' });
44
+
45
+ const sessions = intelligence.listSessions();
46
+ expect(sessions.length).toBe(3);
47
+ });
48
+
49
+ test('listSessions filters by domain', () => {
50
+ intelligence.lifecycle({ action: 'start', domain: 'design' });
51
+ intelligence.lifecycle({ action: 'start', domain: 'a11y' });
52
+ intelligence.lifecycle({ action: 'start', domain: 'design' });
53
+
54
+ const sessions = intelligence.listSessions({ domain: 'design' });
55
+ expect(sessions.length).toBe(2);
56
+ expect(sessions.every((s) => s.domain === 'design')).toBe(true);
57
+ });
58
+
59
+ test('listSessions filters active sessions', () => {
60
+ const s1 = intelligence.lifecycle({ action: 'start', domain: 'design' });
61
+ intelligence.lifecycle({ action: 'start', domain: 'a11y' });
62
+ intelligence.lifecycle({ action: 'end', sessionId: s1.id });
63
+
64
+ const active = intelligence.listSessions({ active: true });
65
+ expect(active.length).toBe(1);
66
+ expect(active[0].domain).toBe('a11y');
67
+
68
+ const completed = intelligence.listSessions({ active: false });
69
+ expect(completed.length).toBe(1);
70
+ expect(completed[0].id).toBe(s1.id);
71
+ });
72
+
73
+ test('listSessions filters extracted sessions', () => {
74
+ const s1 = intelligence.lifecycle({
75
+ action: 'start',
76
+ domain: 'design',
77
+ toolsUsed: ['tool1', 'tool1', 'tool1'],
78
+ });
79
+ intelligence.lifecycle({ action: 'start', domain: 'a11y' });
80
+ // End s1 β€” triggers auto-extract since it has tool usage
81
+ intelligence.lifecycle({ action: 'end', sessionId: s1.id });
82
+
83
+ const extracted = intelligence.listSessions({ extracted: true });
84
+ expect(extracted.length).toBe(1);
85
+ expect(extracted[0].id).toBe(s1.id);
86
+
87
+ const notExtracted = intelligence.listSessions({ extracted: false });
88
+ expect(notExtracted.length).toBe(1);
89
+ expect(notExtracted[0].domain).toBe('a11y');
90
+ });
91
+
92
+ test('listSessions respects limit and offset', () => {
93
+ for (let i = 0; i < 10; i++) {
94
+ intelligence.lifecycle({ action: 'start', domain: `d${i}` });
95
+ }
96
+
97
+ const page1 = intelligence.listSessions({ limit: 3, offset: 0 });
98
+ expect(page1.length).toBe(3);
99
+
100
+ const page2 = intelligence.listSessions({ limit: 3, offset: 3 });
101
+ expect(page2.length).toBe(3);
102
+
103
+ // No overlap
104
+ const ids1 = new Set(page1.map((s) => s.id));
105
+ const ids2 = new Set(page2.map((s) => s.id));
106
+ expect([...ids1].some((id) => ids2.has(id))).toBe(false);
107
+ });
108
+
109
+ // ─── computeSessionQuality ─────────────────────────────────────
110
+
111
+ test('computeSessionQuality throws for non-existent session', () => {
112
+ expect(() => intelligence.computeSessionQuality('nope')).toThrow('Session not found');
113
+ });
114
+
115
+ test('minimal session scores low', () => {
116
+ const session = intelligence.lifecycle({ action: 'start' });
117
+ const quality = intelligence.computeSessionQuality(session.id);
118
+
119
+ expect(quality.sessionId).toBe(session.id);
120
+ expect(quality.overall).toBe(0);
121
+ expect(quality.completeness).toBe(0);
122
+ expect(quality.artifactDensity).toBe(0);
123
+ expect(quality.toolEngagement).toBe(0);
124
+ expect(quality.outcomeClarity).toBe(0);
125
+ });
126
+
127
+ test('completed session with context and domain scores higher', () => {
128
+ const session = intelligence.lifecycle({
129
+ action: 'start',
130
+ domain: 'design',
131
+ context: 'Building a card component',
132
+ });
133
+ intelligence.lifecycle({ action: 'end', sessionId: session.id });
134
+
135
+ const quality = intelligence.computeSessionQuality(session.id);
136
+ // ended (10) + context (8) + domain (7) = 25
137
+ expect(quality.completeness).toBe(25);
138
+ expect(quality.overall).toBeGreaterThan(0);
139
+ });
140
+
141
+ test('files modified boost artifact density', () => {
142
+ const session = intelligence.lifecycle({
143
+ action: 'start',
144
+ filesModified: ['a.ts', 'b.ts', 'c.ts'],
145
+ });
146
+ const quality = intelligence.computeSessionQuality(session.id);
147
+ // 3 files * 5 = 15
148
+ expect(quality.artifactDensity).toBe(15);
149
+ });
150
+
151
+ test('artifact density caps at 25', () => {
152
+ const session = intelligence.lifecycle({
153
+ action: 'start',
154
+ filesModified: Array.from({ length: 20 }, (_, i) => `file-${i}.ts`),
155
+ });
156
+ const quality = intelligence.computeSessionQuality(session.id);
157
+ expect(quality.artifactDensity).toBe(25);
158
+ });
159
+
160
+ test('unique tools boost tool engagement', () => {
161
+ const session = intelligence.lifecycle({
162
+ action: 'start',
163
+ toolsUsed: ['search', 'create', 'validate', 'search', 'search'],
164
+ });
165
+ const quality = intelligence.computeSessionQuality(session.id);
166
+ // 3 unique tools * 5 = 15
167
+ expect(quality.toolEngagement).toBe(15);
168
+ });
169
+
170
+ test('plan outcome boosts outcome clarity', () => {
171
+ const session = intelligence.lifecycle({
172
+ action: 'start',
173
+ planId: 'plan-123',
174
+ });
175
+ intelligence.lifecycle({
176
+ action: 'end',
177
+ sessionId: session.id,
178
+ planOutcome: 'completed',
179
+ });
180
+
181
+ const quality = intelligence.computeSessionQuality(session.id);
182
+ // planId (8) + completed (10) + extractedAt (7, auto-extract fires because planId+planOutcome)
183
+ expect(quality.outcomeClarity).toBe(25);
184
+ });
185
+
186
+ test('overall is sum of all dimensions, max 100', () => {
187
+ const session = intelligence.lifecycle({
188
+ action: 'start',
189
+ domain: 'design',
190
+ context: 'Full session',
191
+ toolsUsed: ['t1', 't2', 't3', 't4', 't5'],
192
+ filesModified: ['f1', 'f2', 'f3', 'f4', 'f5'],
193
+ planId: 'plan-1',
194
+ });
195
+ intelligence.lifecycle({
196
+ action: 'end',
197
+ sessionId: session.id,
198
+ planOutcome: 'completed',
199
+ });
200
+
201
+ const quality = intelligence.computeSessionQuality(session.id);
202
+ expect(quality.overall).toBe(
203
+ quality.completeness +
204
+ quality.artifactDensity +
205
+ quality.toolEngagement +
206
+ quality.outcomeClarity,
207
+ );
208
+ expect(quality.overall).toBeLessThanOrEqual(100);
209
+ });
210
+
211
+ // ─── replaySession ─────────────────────────────────────────────
212
+
213
+ test('replaySession throws for non-existent session', () => {
214
+ expect(() => intelligence.replaySession('nope')).toThrow('Session not found');
215
+ });
216
+
217
+ test('replaySession returns complete session data', () => {
218
+ const session = intelligence.lifecycle({
219
+ action: 'start',
220
+ domain: 'design',
221
+ context: 'Test replay',
222
+ toolsUsed: ['search', 'search', 'search', 'create'],
223
+ filesModified: ['a.ts', 'b.ts', 'c.ts'],
224
+ planId: 'plan-replay',
225
+ });
226
+ intelligence.lifecycle({
227
+ action: 'end',
228
+ sessionId: session.id,
229
+ planOutcome: 'completed',
230
+ });
231
+
232
+ const replay = intelligence.replaySession(session.id);
233
+
234
+ expect(replay.session.id).toBe(session.id);
235
+ expect(replay.session.domain).toBe('design');
236
+ expect(replay.quality.overall).toBeGreaterThan(0);
237
+ expect(replay.durationMinutes).not.toBeNull();
238
+ // Auto-extract should have generated proposals
239
+ expect(replay.proposals.length).toBeGreaterThan(0);
240
+ });
241
+
242
+ test('replaySession returns null duration for active session', () => {
243
+ const session = intelligence.lifecycle({ action: 'start' });
244
+ const replay = intelligence.replaySession(session.id);
245
+ expect(replay.durationMinutes).toBeNull();
246
+ });
247
+
248
+ test('replaySession includes quality scores', () => {
249
+ const session = intelligence.lifecycle({
250
+ action: 'start',
251
+ domain: 'perf',
252
+ context: 'Optimizing queries',
253
+ });
254
+
255
+ const replay = intelligence.replaySession(session.id);
256
+ expect(replay.quality.sessionId).toBe(session.id);
257
+ expect(replay.quality.completeness).toBeGreaterThan(0);
258
+ });
259
+ });