@soleri/core 2.1.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 (377) hide show
  1. package/dist/brain/brain.d.ts +10 -1
  2. package/dist/brain/brain.d.ts.map +1 -1
  3. package/dist/brain/brain.js +116 -13
  4. package/dist/brain/brain.js.map +1 -1
  5. package/dist/brain/intelligence.d.ts +36 -1
  6. package/dist/brain/intelligence.d.ts.map +1 -1
  7. package/dist/brain/intelligence.js +119 -14
  8. package/dist/brain/intelligence.js.map +1 -1
  9. package/dist/brain/types.d.ts +34 -2
  10. package/dist/brain/types.d.ts.map +1 -1
  11. package/dist/cognee/client.d.ts +3 -0
  12. package/dist/cognee/client.d.ts.map +1 -1
  13. package/dist/cognee/client.js +17 -0
  14. package/dist/cognee/client.js.map +1 -1
  15. package/dist/cognee/sync-manager.d.ts +94 -0
  16. package/dist/cognee/sync-manager.d.ts.map +1 -0
  17. package/dist/cognee/sync-manager.js +293 -0
  18. package/dist/cognee/sync-manager.js.map +1 -0
  19. package/dist/control/identity-manager.d.ts +22 -0
  20. package/dist/control/identity-manager.d.ts.map +1 -0
  21. package/dist/control/identity-manager.js +233 -0
  22. package/dist/control/identity-manager.js.map +1 -0
  23. package/dist/control/intent-router.d.ts +32 -0
  24. package/dist/control/intent-router.d.ts.map +1 -0
  25. package/dist/control/intent-router.js +242 -0
  26. package/dist/control/intent-router.js.map +1 -0
  27. package/dist/control/types.d.ts +68 -0
  28. package/dist/control/types.d.ts.map +1 -0
  29. package/dist/control/types.js +9 -0
  30. package/dist/control/types.js.map +1 -0
  31. package/dist/curator/curator.d.ts +37 -1
  32. package/dist/curator/curator.d.ts.map +1 -1
  33. package/dist/curator/curator.js +199 -1
  34. package/dist/curator/curator.js.map +1 -1
  35. package/dist/errors/classify.d.ts +13 -0
  36. package/dist/errors/classify.d.ts.map +1 -0
  37. package/dist/errors/classify.js +97 -0
  38. package/dist/errors/classify.js.map +1 -0
  39. package/dist/errors/index.d.ts +6 -0
  40. package/dist/errors/index.d.ts.map +1 -0
  41. package/dist/errors/index.js +4 -0
  42. package/dist/errors/index.js.map +1 -0
  43. package/dist/errors/retry.d.ts +40 -0
  44. package/dist/errors/retry.d.ts.map +1 -0
  45. package/dist/errors/retry.js +97 -0
  46. package/dist/errors/retry.js.map +1 -0
  47. package/dist/errors/types.d.ts +48 -0
  48. package/dist/errors/types.d.ts.map +1 -0
  49. package/dist/errors/types.js +59 -0
  50. package/dist/errors/types.js.map +1 -0
  51. package/dist/facades/types.d.ts +1 -1
  52. package/dist/governance/governance.d.ts +42 -0
  53. package/dist/governance/governance.d.ts.map +1 -0
  54. package/dist/governance/governance.js +488 -0
  55. package/dist/governance/governance.js.map +1 -0
  56. package/dist/governance/index.d.ts +3 -0
  57. package/dist/governance/index.d.ts.map +1 -0
  58. package/dist/governance/index.js +2 -0
  59. package/dist/governance/index.js.map +1 -0
  60. package/dist/governance/types.d.ts +102 -0
  61. package/dist/governance/types.d.ts.map +1 -0
  62. package/dist/governance/types.js +3 -0
  63. package/dist/governance/types.js.map +1 -0
  64. package/dist/index.d.ts +52 -3
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +47 -1
  67. package/dist/index.js.map +1 -1
  68. package/dist/intake/content-classifier.d.ts +14 -0
  69. package/dist/intake/content-classifier.d.ts.map +1 -0
  70. package/dist/intake/content-classifier.js +125 -0
  71. package/dist/intake/content-classifier.js.map +1 -0
  72. package/dist/intake/dedup-gate.d.ts +17 -0
  73. package/dist/intake/dedup-gate.d.ts.map +1 -0
  74. package/dist/intake/dedup-gate.js +66 -0
  75. package/dist/intake/dedup-gate.js.map +1 -0
  76. package/dist/intake/intake-pipeline.d.ts +63 -0
  77. package/dist/intake/intake-pipeline.d.ts.map +1 -0
  78. package/dist/intake/intake-pipeline.js +373 -0
  79. package/dist/intake/intake-pipeline.js.map +1 -0
  80. package/dist/intake/types.d.ts +65 -0
  81. package/dist/intake/types.d.ts.map +1 -0
  82. package/dist/intake/types.js +3 -0
  83. package/dist/intake/types.js.map +1 -0
  84. package/dist/intelligence/loader.js +1 -1
  85. package/dist/intelligence/loader.js.map +1 -1
  86. package/dist/intelligence/types.d.ts +3 -1
  87. package/dist/intelligence/types.d.ts.map +1 -1
  88. package/dist/logging/logger.d.ts +37 -0
  89. package/dist/logging/logger.d.ts.map +1 -0
  90. package/dist/logging/logger.js +145 -0
  91. package/dist/logging/logger.js.map +1 -0
  92. package/dist/logging/types.d.ts +19 -0
  93. package/dist/logging/types.d.ts.map +1 -0
  94. package/dist/logging/types.js +2 -0
  95. package/dist/logging/types.js.map +1 -0
  96. package/dist/loop/loop-manager.d.ts +100 -0
  97. package/dist/loop/loop-manager.d.ts.map +1 -0
  98. package/dist/loop/loop-manager.js +379 -0
  99. package/dist/loop/loop-manager.js.map +1 -0
  100. package/dist/loop/types.d.ts +103 -0
  101. package/dist/loop/types.d.ts.map +1 -0
  102. package/dist/loop/types.js +11 -0
  103. package/dist/loop/types.js.map +1 -0
  104. package/dist/persistence/index.d.ts +3 -0
  105. package/dist/persistence/index.d.ts.map +1 -0
  106. package/dist/persistence/index.js +2 -0
  107. package/dist/persistence/index.js.map +1 -0
  108. package/dist/persistence/sqlite-provider.d.ts +25 -0
  109. package/dist/persistence/sqlite-provider.d.ts.map +1 -0
  110. package/dist/persistence/sqlite-provider.js +59 -0
  111. package/dist/persistence/sqlite-provider.js.map +1 -0
  112. package/dist/persistence/types.d.ts +36 -0
  113. package/dist/persistence/types.d.ts.map +1 -0
  114. package/dist/persistence/types.js +8 -0
  115. package/dist/persistence/types.js.map +1 -0
  116. package/dist/planning/gap-analysis.d.ts +72 -0
  117. package/dist/planning/gap-analysis.d.ts.map +1 -0
  118. package/dist/planning/gap-analysis.js +442 -0
  119. package/dist/planning/gap-analysis.js.map +1 -0
  120. package/dist/planning/gap-types.d.ts +29 -0
  121. package/dist/planning/gap-types.d.ts.map +1 -0
  122. package/dist/planning/gap-types.js +28 -0
  123. package/dist/planning/gap-types.js.map +1 -0
  124. package/dist/planning/planner.d.ts +421 -4
  125. package/dist/planning/planner.d.ts.map +1 -1
  126. package/dist/planning/planner.js +949 -21
  127. package/dist/planning/planner.js.map +1 -1
  128. package/dist/playbooks/generic/brainstorming.d.ts +9 -0
  129. package/dist/playbooks/generic/brainstorming.d.ts.map +1 -0
  130. package/dist/playbooks/generic/brainstorming.js +105 -0
  131. package/dist/playbooks/generic/brainstorming.js.map +1 -0
  132. package/dist/playbooks/generic/code-review.d.ts +11 -0
  133. package/dist/playbooks/generic/code-review.d.ts.map +1 -0
  134. package/dist/playbooks/generic/code-review.js +176 -0
  135. package/dist/playbooks/generic/code-review.js.map +1 -0
  136. package/dist/playbooks/generic/subagent-execution.d.ts +9 -0
  137. package/dist/playbooks/generic/subagent-execution.d.ts.map +1 -0
  138. package/dist/playbooks/generic/subagent-execution.js +68 -0
  139. package/dist/playbooks/generic/subagent-execution.js.map +1 -0
  140. package/dist/playbooks/generic/systematic-debugging.d.ts +9 -0
  141. package/dist/playbooks/generic/systematic-debugging.d.ts.map +1 -0
  142. package/dist/playbooks/generic/systematic-debugging.js +87 -0
  143. package/dist/playbooks/generic/systematic-debugging.js.map +1 -0
  144. package/dist/playbooks/generic/tdd.d.ts +9 -0
  145. package/dist/playbooks/generic/tdd.d.ts.map +1 -0
  146. package/dist/playbooks/generic/tdd.js +70 -0
  147. package/dist/playbooks/generic/tdd.js.map +1 -0
  148. package/dist/playbooks/generic/verification.d.ts +9 -0
  149. package/dist/playbooks/generic/verification.d.ts.map +1 -0
  150. package/dist/playbooks/generic/verification.js +74 -0
  151. package/dist/playbooks/generic/verification.js.map +1 -0
  152. package/dist/playbooks/index.d.ts +4 -0
  153. package/dist/playbooks/index.d.ts.map +1 -0
  154. package/dist/playbooks/index.js +5 -0
  155. package/dist/playbooks/index.js.map +1 -0
  156. package/dist/playbooks/playbook-registry.d.ts +42 -0
  157. package/dist/playbooks/playbook-registry.d.ts.map +1 -0
  158. package/dist/playbooks/playbook-registry.js +227 -0
  159. package/dist/playbooks/playbook-registry.js.map +1 -0
  160. package/dist/playbooks/playbook-seeder.d.ts +47 -0
  161. package/dist/playbooks/playbook-seeder.d.ts.map +1 -0
  162. package/dist/playbooks/playbook-seeder.js +104 -0
  163. package/dist/playbooks/playbook-seeder.js.map +1 -0
  164. package/dist/playbooks/playbook-types.d.ts +132 -0
  165. package/dist/playbooks/playbook-types.d.ts.map +1 -0
  166. package/dist/playbooks/playbook-types.js +12 -0
  167. package/dist/playbooks/playbook-types.js.map +1 -0
  168. package/dist/project/project-registry.d.ts +79 -0
  169. package/dist/project/project-registry.d.ts.map +1 -0
  170. package/dist/project/project-registry.js +274 -0
  171. package/dist/project/project-registry.js.map +1 -0
  172. package/dist/project/types.d.ts +28 -0
  173. package/dist/project/types.d.ts.map +1 -0
  174. package/dist/project/types.js +5 -0
  175. package/dist/project/types.js.map +1 -0
  176. package/dist/prompts/index.d.ts +4 -0
  177. package/dist/prompts/index.d.ts.map +1 -0
  178. package/dist/prompts/index.js +3 -0
  179. package/dist/prompts/index.js.map +1 -0
  180. package/dist/prompts/parser.d.ts +17 -0
  181. package/dist/prompts/parser.d.ts.map +1 -0
  182. package/dist/prompts/parser.js +47 -0
  183. package/dist/prompts/parser.js.map +1 -0
  184. package/dist/prompts/template-manager.d.ts +25 -0
  185. package/dist/prompts/template-manager.d.ts.map +1 -0
  186. package/dist/prompts/template-manager.js +71 -0
  187. package/dist/prompts/template-manager.js.map +1 -0
  188. package/dist/prompts/types.d.ts +26 -0
  189. package/dist/prompts/types.d.ts.map +1 -0
  190. package/dist/prompts/types.js +5 -0
  191. package/dist/prompts/types.js.map +1 -0
  192. package/dist/runtime/admin-extra-ops.d.ts +15 -0
  193. package/dist/runtime/admin-extra-ops.d.ts.map +1 -0
  194. package/dist/runtime/admin-extra-ops.js +595 -0
  195. package/dist/runtime/admin-extra-ops.js.map +1 -0
  196. package/dist/runtime/admin-ops.d.ts +15 -0
  197. package/dist/runtime/admin-ops.d.ts.map +1 -0
  198. package/dist/runtime/admin-ops.js +329 -0
  199. package/dist/runtime/admin-ops.js.map +1 -0
  200. package/dist/runtime/capture-ops.d.ts +15 -0
  201. package/dist/runtime/capture-ops.d.ts.map +1 -0
  202. package/dist/runtime/capture-ops.js +363 -0
  203. package/dist/runtime/capture-ops.js.map +1 -0
  204. package/dist/runtime/cognee-sync-ops.d.ts +12 -0
  205. package/dist/runtime/cognee-sync-ops.d.ts.map +1 -0
  206. package/dist/runtime/cognee-sync-ops.js +55 -0
  207. package/dist/runtime/cognee-sync-ops.js.map +1 -0
  208. package/dist/runtime/core-ops.d.ts +9 -3
  209. package/dist/runtime/core-ops.d.ts.map +1 -1
  210. package/dist/runtime/core-ops.js +693 -10
  211. package/dist/runtime/core-ops.js.map +1 -1
  212. package/dist/runtime/curator-extra-ops.d.ts +9 -0
  213. package/dist/runtime/curator-extra-ops.d.ts.map +1 -0
  214. package/dist/runtime/curator-extra-ops.js +71 -0
  215. package/dist/runtime/curator-extra-ops.js.map +1 -0
  216. package/dist/runtime/domain-ops.d.ts.map +1 -1
  217. package/dist/runtime/domain-ops.js +61 -15
  218. package/dist/runtime/domain-ops.js.map +1 -1
  219. package/dist/runtime/grading-ops.d.ts +14 -0
  220. package/dist/runtime/grading-ops.d.ts.map +1 -0
  221. package/dist/runtime/grading-ops.js +105 -0
  222. package/dist/runtime/grading-ops.js.map +1 -0
  223. package/dist/runtime/intake-ops.d.ts +14 -0
  224. package/dist/runtime/intake-ops.d.ts.map +1 -0
  225. package/dist/runtime/intake-ops.js +110 -0
  226. package/dist/runtime/intake-ops.js.map +1 -0
  227. package/dist/runtime/loop-ops.d.ts +14 -0
  228. package/dist/runtime/loop-ops.d.ts.map +1 -0
  229. package/dist/runtime/loop-ops.js +251 -0
  230. package/dist/runtime/loop-ops.js.map +1 -0
  231. package/dist/runtime/memory-cross-project-ops.d.ts +12 -0
  232. package/dist/runtime/memory-cross-project-ops.d.ts.map +1 -0
  233. package/dist/runtime/memory-cross-project-ops.js +165 -0
  234. package/dist/runtime/memory-cross-project-ops.js.map +1 -0
  235. package/dist/runtime/memory-extra-ops.d.ts +13 -0
  236. package/dist/runtime/memory-extra-ops.d.ts.map +1 -0
  237. package/dist/runtime/memory-extra-ops.js +173 -0
  238. package/dist/runtime/memory-extra-ops.js.map +1 -0
  239. package/dist/runtime/orchestrate-ops.d.ts +17 -0
  240. package/dist/runtime/orchestrate-ops.d.ts.map +1 -0
  241. package/dist/runtime/orchestrate-ops.js +246 -0
  242. package/dist/runtime/orchestrate-ops.js.map +1 -0
  243. package/dist/runtime/planning-extra-ops.d.ts +25 -0
  244. package/dist/runtime/planning-extra-ops.d.ts.map +1 -0
  245. package/dist/runtime/planning-extra-ops.js +663 -0
  246. package/dist/runtime/planning-extra-ops.js.map +1 -0
  247. package/dist/runtime/playbook-ops.d.ts +14 -0
  248. package/dist/runtime/playbook-ops.d.ts.map +1 -0
  249. package/dist/runtime/playbook-ops.js +141 -0
  250. package/dist/runtime/playbook-ops.js.map +1 -0
  251. package/dist/runtime/project-ops.d.ts +15 -0
  252. package/dist/runtime/project-ops.d.ts.map +1 -0
  253. package/dist/runtime/project-ops.js +186 -0
  254. package/dist/runtime/project-ops.js.map +1 -0
  255. package/dist/runtime/runtime.d.ts.map +1 -1
  256. package/dist/runtime/runtime.js +65 -3
  257. package/dist/runtime/runtime.js.map +1 -1
  258. package/dist/runtime/types.d.ts +29 -0
  259. package/dist/runtime/types.d.ts.map +1 -1
  260. package/dist/runtime/vault-extra-ops.d.ts +10 -0
  261. package/dist/runtime/vault-extra-ops.d.ts.map +1 -0
  262. package/dist/runtime/vault-extra-ops.js +536 -0
  263. package/dist/runtime/vault-extra-ops.js.map +1 -0
  264. package/dist/telemetry/telemetry.d.ts +48 -0
  265. package/dist/telemetry/telemetry.d.ts.map +1 -0
  266. package/dist/telemetry/telemetry.js +87 -0
  267. package/dist/telemetry/telemetry.js.map +1 -0
  268. package/dist/vault/playbook.d.ts +34 -0
  269. package/dist/vault/playbook.d.ts.map +1 -0
  270. package/dist/vault/playbook.js +60 -0
  271. package/dist/vault/playbook.js.map +1 -0
  272. package/dist/vault/vault.d.ts +97 -4
  273. package/dist/vault/vault.d.ts.map +1 -1
  274. package/dist/vault/vault.js +424 -65
  275. package/dist/vault/vault.js.map +1 -1
  276. package/package.json +7 -3
  277. package/src/__tests__/admin-extra-ops.test.ts +467 -0
  278. package/src/__tests__/admin-ops.test.ts +271 -0
  279. package/src/__tests__/brain-intelligence.test.ts +205 -0
  280. package/src/__tests__/brain.test.ts +134 -3
  281. package/src/__tests__/capture-ops.test.ts +509 -0
  282. package/src/__tests__/cognee-integration.test.ts +80 -0
  283. package/src/__tests__/cognee-sync-manager.test.ts +103 -0
  284. package/src/__tests__/core-ops.test.ts +292 -2
  285. package/src/__tests__/curator-extra-ops.test.ts +381 -0
  286. package/src/__tests__/domain-ops.test.ts +66 -0
  287. package/src/__tests__/errors.test.ts +388 -0
  288. package/src/__tests__/governance.test.ts +522 -0
  289. package/src/__tests__/grading-ops.test.ts +361 -0
  290. package/src/__tests__/identity-manager.test.ts +243 -0
  291. package/src/__tests__/intake-pipeline.test.ts +162 -0
  292. package/src/__tests__/intent-router.test.ts +222 -0
  293. package/src/__tests__/logger.test.ts +200 -0
  294. package/src/__tests__/loop-ops.test.ts +469 -0
  295. package/src/__tests__/memory-cross-project-ops.test.ts +248 -0
  296. package/src/__tests__/memory-extra-ops.test.ts +352 -0
  297. package/src/__tests__/orchestrate-ops.test.ts +289 -0
  298. package/src/__tests__/persistence.test.ts +225 -0
  299. package/src/__tests__/planner.test.ts +416 -7
  300. package/src/__tests__/planning-extra-ops.test.ts +706 -0
  301. package/src/__tests__/playbook-registry.test.ts +326 -0
  302. package/src/__tests__/playbook-seeder.test.ts +163 -0
  303. package/src/__tests__/playbook.test.ts +389 -0
  304. package/src/__tests__/project-ops.test.ts +381 -0
  305. package/src/__tests__/template-manager.test.ts +222 -0
  306. package/src/__tests__/vault-extra-ops.test.ts +482 -0
  307. package/src/brain/brain.ts +185 -16
  308. package/src/brain/intelligence.ts +179 -10
  309. package/src/brain/types.ts +40 -2
  310. package/src/cognee/client.ts +18 -0
  311. package/src/cognee/sync-manager.ts +389 -0
  312. package/src/control/identity-manager.ts +354 -0
  313. package/src/control/intent-router.ts +326 -0
  314. package/src/control/types.ts +102 -0
  315. package/src/curator/curator.ts +295 -1
  316. package/src/errors/classify.ts +102 -0
  317. package/src/errors/index.ts +5 -0
  318. package/src/errors/retry.ts +132 -0
  319. package/src/errors/types.ts +81 -0
  320. package/src/governance/governance.ts +698 -0
  321. package/src/governance/index.ts +18 -0
  322. package/src/governance/types.ts +111 -0
  323. package/src/index.ts +213 -2
  324. package/src/intake/content-classifier.ts +146 -0
  325. package/src/intake/dedup-gate.ts +92 -0
  326. package/src/intake/intake-pipeline.ts +503 -0
  327. package/src/intake/types.ts +69 -0
  328. package/src/intelligence/loader.ts +1 -1
  329. package/src/intelligence/types.ts +3 -1
  330. package/src/logging/logger.ts +154 -0
  331. package/src/logging/types.ts +21 -0
  332. package/src/loop/loop-manager.ts +448 -0
  333. package/src/loop/types.ts +115 -0
  334. package/src/persistence/index.ts +7 -0
  335. package/src/persistence/sqlite-provider.ts +62 -0
  336. package/src/persistence/types.ts +44 -0
  337. package/src/planning/gap-analysis.ts +775 -0
  338. package/src/planning/gap-types.ts +61 -0
  339. package/src/planning/planner.ts +1273 -24
  340. package/src/playbooks/generic/brainstorming.ts +110 -0
  341. package/src/playbooks/generic/code-review.ts +181 -0
  342. package/src/playbooks/generic/subagent-execution.ts +74 -0
  343. package/src/playbooks/generic/systematic-debugging.ts +92 -0
  344. package/src/playbooks/generic/tdd.ts +75 -0
  345. package/src/playbooks/generic/verification.ts +79 -0
  346. package/src/playbooks/index.ts +27 -0
  347. package/src/playbooks/playbook-registry.ts +284 -0
  348. package/src/playbooks/playbook-seeder.ts +119 -0
  349. package/src/playbooks/playbook-types.ts +162 -0
  350. package/src/project/project-registry.ts +370 -0
  351. package/src/project/types.ts +31 -0
  352. package/src/prompts/index.ts +3 -0
  353. package/src/prompts/parser.ts +59 -0
  354. package/src/prompts/template-manager.ts +77 -0
  355. package/src/prompts/types.ts +28 -0
  356. package/src/runtime/admin-extra-ops.ts +652 -0
  357. package/src/runtime/admin-ops.ts +340 -0
  358. package/src/runtime/capture-ops.ts +404 -0
  359. package/src/runtime/cognee-sync-ops.ts +63 -0
  360. package/src/runtime/core-ops.ts +787 -9
  361. package/src/runtime/curator-extra-ops.ts +85 -0
  362. package/src/runtime/domain-ops.ts +67 -15
  363. package/src/runtime/grading-ops.ts +130 -0
  364. package/src/runtime/intake-ops.ts +126 -0
  365. package/src/runtime/loop-ops.ts +277 -0
  366. package/src/runtime/memory-cross-project-ops.ts +191 -0
  367. package/src/runtime/memory-extra-ops.ts +186 -0
  368. package/src/runtime/orchestrate-ops.ts +278 -0
  369. package/src/runtime/planning-extra-ops.ts +718 -0
  370. package/src/runtime/playbook-ops.ts +169 -0
  371. package/src/runtime/project-ops.ts +202 -0
  372. package/src/runtime/runtime.ts +77 -3
  373. package/src/runtime/types.ts +29 -0
  374. package/src/runtime/vault-extra-ops.ts +606 -0
  375. package/src/telemetry/telemetry.ts +118 -0
  376. package/src/vault/playbook.ts +87 -0
  377. package/src/vault/vault.ts +575 -98
@@ -0,0 +1,154 @@
1
+ /**
2
+ * Structured logger for Soleri agents.
3
+ *
4
+ * All output routes through stderr (stdout is reserved for MCP JSON-RPC).
5
+ * Supports optional file logging with daily rotation and 7-day retention.
6
+ *
7
+ * Ported from Salvador MCP's src/utils/logger.ts.
8
+ */
9
+
10
+ import { appendFileSync, mkdirSync, readdirSync, unlinkSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ import type { LogLevel, LogContext, LoggerConfig } from './types.js';
13
+
14
+ const LEVEL_ORDER: LogLevel[] = ['debug', 'info', 'warn', 'error'];
15
+
16
+ // MCP stdio servers must keep stdout clean for JSON-RPC.
17
+ // Route ALL log levels through stderr (console.error/console.warn).
18
+ const LEVEL_METHODS: Record<LogLevel, 'warn' | 'error'> = {
19
+ debug: 'error',
20
+ info: 'error',
21
+ warn: 'warn',
22
+ error: 'error',
23
+ };
24
+
25
+ /** Max age for log files before auto-pruning (7 days) */
26
+ const LOG_RETENTION_DAYS = 7;
27
+
28
+ export class Logger {
29
+ private readonly prefix: string;
30
+ private readonly minLevel: number;
31
+ private fileLogDir: string | null = null;
32
+ private fileLogPrefix: string;
33
+ private currentLogDate: string | null = null;
34
+ private currentLogPath: string | null = null;
35
+
36
+ constructor(config?: LoggerConfig) {
37
+ this.prefix = config?.prefix ?? '[Soleri]';
38
+ this.fileLogPrefix = 'agent';
39
+ const envLevel = process.env.SOLERI_LOG_LEVEL as LogLevel | undefined;
40
+ const level = config?.level ?? envLevel ?? 'info';
41
+ const levelIndex = LEVEL_ORDER.indexOf(level);
42
+ this.minLevel = levelIndex >= 0 ? levelIndex : LEVEL_ORDER.indexOf('info');
43
+
44
+ if (config?.fileLogDir) {
45
+ this.enableFileLog(config.fileLogDir);
46
+ }
47
+ }
48
+
49
+ /**
50
+ * Enable file-based logging. Output is teed to
51
+ * {dir}/{prefix}-YYYY-MM-DD.log in addition to stderr.
52
+ * Automatically prunes log files older than 7 days.
53
+ */
54
+ enableFileLog(dir: string, prefix?: string): void {
55
+ mkdirSync(dir, { recursive: true });
56
+ this.fileLogDir = dir;
57
+ if (prefix) this.fileLogPrefix = prefix;
58
+ this.rotateFileIfNeeded();
59
+ this.pruneOldLogs();
60
+ }
61
+
62
+ debug(message: string, context?: LogContext): void {
63
+ this.log('debug', message, context);
64
+ }
65
+
66
+ info(message: string, context?: LogContext): void {
67
+ this.log('info', message, context);
68
+ }
69
+
70
+ warn(message: string, context?: LogContext): void {
71
+ this.log('warn', message, context);
72
+ }
73
+
74
+ error(message: string, context?: LogContext): void {
75
+ this.log('error', message, context);
76
+ }
77
+
78
+ private log(level: LogLevel, message: string, context?: LogContext): void {
79
+ if (LEVEL_ORDER.indexOf(level) < this.minLevel) return;
80
+ const method = LEVEL_METHODS[level];
81
+ const tag = level.toUpperCase();
82
+
83
+ // Console output (stderr)
84
+ if (context) {
85
+ console[method](`${this.prefix}[${tag}] ${message}`, context);
86
+ } else {
87
+ console[method](`${this.prefix}[${tag}] ${message}`);
88
+ }
89
+
90
+ // File output (if enabled)
91
+ if (this.fileLogDir) {
92
+ this.writeToFile(tag, message, context);
93
+ }
94
+ }
95
+
96
+ private writeToFile(tag: string, message: string, context?: LogContext): void {
97
+ this.rotateFileIfNeeded();
98
+ if (!this.currentLogPath) return;
99
+
100
+ const ts = new Date().toISOString();
101
+ let line = `${ts} [${tag}] ${message}`;
102
+ if (context) {
103
+ try {
104
+ line += ' ' + JSON.stringify(context);
105
+ } catch {
106
+ // Skip unserializable context
107
+ }
108
+ }
109
+ try {
110
+ appendFileSync(this.currentLogPath, line + '\n');
111
+ } catch {
112
+ // Silently fail — file logging should never break the app
113
+ }
114
+ }
115
+
116
+ /** Switch to a new log file if the date has changed */
117
+ private rotateFileIfNeeded(): void {
118
+ if (!this.fileLogDir) return;
119
+ const today = new Date().toISOString().slice(0, 10); // YYYY-MM-DD
120
+ if (today !== this.currentLogDate) {
121
+ this.currentLogDate = today;
122
+ this.currentLogPath = join(this.fileLogDir, `${this.fileLogPrefix}-${today}.log`);
123
+ }
124
+ }
125
+
126
+ /** Remove log files older than LOG_RETENTION_DAYS */
127
+ private pruneOldLogs(): void {
128
+ if (!this.fileLogDir) return;
129
+ const prefix = this.fileLogPrefix;
130
+ try {
131
+ const cutoff = Date.now() - LOG_RETENTION_DAYS * 24 * 60 * 60 * 1000;
132
+ const files = readdirSync(this.fileLogDir);
133
+ for (const file of files) {
134
+ if (!file.startsWith(`${prefix}-`) || !file.endsWith('.log')) continue;
135
+ const dateStr = file.slice(`${prefix}-`.length, -'.log'.length);
136
+ const fileDate = new Date(dateStr).getTime();
137
+ if (!isNaN(fileDate) && fileDate < cutoff) {
138
+ try {
139
+ unlinkSync(join(this.fileLogDir, file));
140
+ } catch {
141
+ // Ignore individual file deletion errors
142
+ }
143
+ }
144
+ }
145
+ } catch {
146
+ // Ignore pruning errors
147
+ }
148
+ }
149
+ }
150
+
151
+ /** Factory function for creating a logger. */
152
+ export function createLogger(config?: LoggerConfig): Logger {
153
+ return new Logger(config);
154
+ }
@@ -0,0 +1,21 @@
1
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
2
+
3
+ export interface LogContext {
4
+ [key: string]: unknown;
5
+ }
6
+
7
+ export interface LogEntry {
8
+ level: LogLevel;
9
+ message: string;
10
+ context?: LogContext;
11
+ timestamp: string;
12
+ }
13
+
14
+ export interface LoggerConfig {
15
+ /** Minimum log level. Default: 'info' (or SOLERI_LOG_LEVEL env var) */
16
+ level?: LogLevel;
17
+ /** Prefix for log messages. Default: '[Soleri]' */
18
+ prefix?: string;
19
+ /** Directory for file logging. If set, enables file output on construction. */
20
+ fileLogDir?: string;
21
+ }
@@ -0,0 +1,448 @@
1
+ /**
2
+ * Loop manager — iterative validation loop state tracking with
3
+ * output scanning and gate decision system.
4
+ *
5
+ * Ported from Salvador's loop.facade.ts with:
6
+ * - Promise tag extraction (<promise>...</promise>)
7
+ * - 5-tier heuristic completion detection
8
+ * - Gate decision pattern (allow/block) for Stop hook integration
9
+ * - Knowledge tracking for brain session recording
10
+ *
11
+ * Session-scoped (in-memory). Persistence is optional via external store.
12
+ */
13
+
14
+ import type {
15
+ LoopConfig,
16
+ LoopIteration,
17
+ LoopMode,
18
+ LoopState,
19
+ LoopKnowledge,
20
+ LoopHistoryEntry,
21
+ LoopIterateDecision,
22
+ } from './types.js';
23
+
24
+ // ─── Grade ordering for plan-iteration mode ──────────────────────
25
+ const GRADE_ORDER = ['A+', 'A', 'B', 'C', 'D', 'F'];
26
+
27
+ // ─── Output Scanning ─────────────────────────────────────────────
28
+
29
+ /**
30
+ * Extract text between <promise>...</promise> tags from output.
31
+ * Ported from Salvador's extractPromise.
32
+ */
33
+ export function extractPromise(text: string): string | null {
34
+ const match = /<promise>([\s\S]*?)<\/promise>/.exec(text);
35
+ if (!match) return null;
36
+ return match[1].trim().replace(/\s+/g, ' ');
37
+ }
38
+
39
+ /**
40
+ * 5-tier heuristic completion detection.
41
+ * Returns a reason string if completion is detected, null otherwise.
42
+ * Ported from Salvador's detectImplicitCompletion.
43
+ *
44
+ * Tiers:
45
+ * 1. Validation tool score (token-migration, component-build)
46
+ * 2. Contrast mode — all PASS, no FAIL
47
+ * 3. Plan mode — grade meets target
48
+ * 4. Completion language + file modification signals (requires BOTH)
49
+ * 5. Test pass signals
50
+ */
51
+ export function detectImplicitCompletion(lastOutput: string, config: LoopConfig): string | null {
52
+ const text = lastOutput;
53
+
54
+ // 1. Validation tool score (token-migration, component-build)
55
+ if (config.mode === 'token-migration' || config.mode === 'component-build') {
56
+ const scoreMatch = /"score"\s*:\s*(\d+(?:\.\d+)?)/.exec(text);
57
+ if (scoreMatch) {
58
+ const score = parseFloat(scoreMatch[1]);
59
+ const target = config.targetScore ?? (config.mode === 'token-migration' ? 95 : 90);
60
+ if (score >= target) {
61
+ return `Auto-detected: validation score ${score} >= target ${target}`;
62
+ }
63
+ }
64
+ }
65
+
66
+ // 2. Contrast mode — all PASS, no FAIL
67
+ if (config.mode === 'contrast-fix') {
68
+ const hasPass = /\bPASS\b/i.test(text);
69
+ const hasFail = /\bFAIL\b/i.test(text);
70
+ if (hasPass && !hasFail) {
71
+ return 'Auto-detected: all contrast checks PASS, no FAIL';
72
+ }
73
+ }
74
+
75
+ // 3. Plan mode — grade meets target
76
+ if (config.mode === 'plan-iteration') {
77
+ const gradeMatch = /"grade"\s*:\s*"([A-F][+-]?)"/.exec(text);
78
+ if (gradeMatch) {
79
+ const grade = gradeMatch[1];
80
+ const target = config.targetGrade ?? 'A';
81
+ const gradeIdx = GRADE_ORDER.indexOf(grade);
82
+ const targetIdx = GRADE_ORDER.indexOf(target);
83
+ if (gradeIdx >= 0 && targetIdx >= 0 && gradeIdx <= targetIdx) {
84
+ return `Auto-detected: plan grade ${grade} >= target ${target}`;
85
+ }
86
+ }
87
+ }
88
+
89
+ // 4. Completion language + file modification signals (requires BOTH)
90
+ const completionPhrases =
91
+ /\b(task complete|implementation finished|implementation complete|work complete|all done|changes complete|finished implementing|done implementing)\b/i;
92
+ const fileModPhrases =
93
+ /\b(file modified|wrote to|saved to|created file|updated file|edited file|changes written)\b/i;
94
+ if (completionPhrases.test(text) && fileModPhrases.test(text)) {
95
+ return 'Auto-detected: completion language with file modification evidence';
96
+ }
97
+
98
+ // 5. Test pass signals
99
+ const testPass =
100
+ /(\d+)\s+tests?\s+passed[,\s]+0\s+fail/i.test(text) ||
101
+ /\ball\s+tests?\s+(passing|passed)\b/i.test(text);
102
+ if (testPass) {
103
+ return 'Auto-detected: test suite passing';
104
+ }
105
+
106
+ return null;
107
+ }
108
+
109
+ // ─── Anomaly Detection ──────────────────────────────────────────
110
+
111
+ /**
112
+ * Minimum expected duration (ms) per mode.
113
+ * Iterations finishing faster than this with low scores are flagged as anomalous.
114
+ */
115
+ const MIN_DURATION_MS: Record<LoopMode, number> = {
116
+ 'token-migration': 5000,
117
+ 'contrast-fix': 2000,
118
+ 'component-build': 5000,
119
+ 'plan-iteration': 3000,
120
+ custom: 0,
121
+ };
122
+
123
+ /**
124
+ * Detect anomalous loop iteration patterns.
125
+ * Flags fast + low-score combos that suggest the agent is spinning without making progress.
126
+ */
127
+ export function detectAnomaly(iteration: LoopIteration, mode: LoopMode): string | null {
128
+ const minDuration = MIN_DURATION_MS[mode];
129
+ if (minDuration === 0) return null; // custom mode — no threshold
130
+
131
+ const duration = iteration.durationMs ?? 0;
132
+ const score = iteration.validationScore ?? 0;
133
+
134
+ // Flag: iteration completed very fast with a low score
135
+ if (duration > 0 && duration < minDuration && !iteration.passed && score < 50) {
136
+ return `Anomaly: iteration ${iteration.iteration} completed in ${duration}ms (min expected: ${minDuration}ms) with score ${score} — possible no-op loop`;
137
+ }
138
+
139
+ return null;
140
+ }
141
+
142
+ // ─── LoopManager ─────────────────────────────────────────────────
143
+
144
+ export class LoopManager {
145
+ private activeLoop: LoopState | null = null;
146
+ private completedLoops: LoopState[] = [];
147
+ private historyEntries: LoopHistoryEntry[] = [];
148
+
149
+ /**
150
+ * Start a new validation loop.
151
+ * Throws if a loop is already active.
152
+ */
153
+ startLoop(config: LoopConfig): LoopState {
154
+ if (this.activeLoop) {
155
+ throw new Error(
156
+ `Loop already active: ${this.activeLoop.id} (mode: ${this.activeLoop.config.mode}). ` +
157
+ 'Cancel or complete it first.',
158
+ );
159
+ }
160
+
161
+ const loop: LoopState = {
162
+ id: `loop-${Date.now()}`,
163
+ config,
164
+ iterations: [],
165
+ status: 'active',
166
+ startedAt: new Date().toISOString(),
167
+ };
168
+
169
+ this.activeLoop = loop;
170
+ return loop;
171
+ }
172
+
173
+ /**
174
+ * Record an iteration result on the active loop (simple API).
175
+ * If the iteration passes, the loop status is NOT automatically changed —
176
+ * call completeLoop() explicitly when validation is confirmed.
177
+ * If max iterations reached and this iteration fails, status becomes 'max-iterations'.
178
+ */
179
+ iterate(result: {
180
+ validationScore?: number;
181
+ validationResult?: string;
182
+ passed: boolean;
183
+ }): LoopIteration {
184
+ if (!this.activeLoop) {
185
+ throw new Error('No active loop. Start one first.');
186
+ }
187
+
188
+ const iteration: LoopIteration = {
189
+ iteration: this.activeLoop.iterations.length + 1,
190
+ timestamp: new Date().toISOString(),
191
+ validationScore: result.validationScore,
192
+ validationResult: result.validationResult,
193
+ passed: result.passed,
194
+ };
195
+
196
+ this.activeLoop.iterations.push(iteration);
197
+
198
+ // Auto-transition to max-iterations if limit reached and not passing
199
+ if (
200
+ !result.passed &&
201
+ this.activeLoop.iterations.length >= this.activeLoop.config.maxIterations
202
+ ) {
203
+ this.activeLoop.status = 'max-iterations';
204
+ this.activeLoop.completedAt = new Date().toISOString();
205
+ this.moveToHistory('max_iterations');
206
+ this.completedLoops.push(this.activeLoop);
207
+ this.activeLoop = null;
208
+ }
209
+
210
+ return iteration;
211
+ }
212
+
213
+ /**
214
+ * Gate-based iteration — accepts LLM output and returns allow/block decision.
215
+ * Ported from Salvador's loop_iterate handler.
216
+ *
217
+ * This is the primary method for Stop hook integration:
218
+ * - Scans output for completion promise tags
219
+ * - Runs 5-tier heuristic completion detection
220
+ * - Returns 'block' with injected prompt/systemMessage for next iteration
221
+ * - Returns 'allow' when loop ends (completed, max_iterations)
222
+ *
223
+ * @param lastOutput - The LLM's last response to scan for completion signals
224
+ * @param knowledge - Optional knowledge items discovered during this iteration
225
+ */
226
+ iterateWithGate(
227
+ lastOutput: string,
228
+ knowledge?: LoopKnowledge,
229
+ durationMs?: number,
230
+ ): LoopIterateDecision {
231
+ if (!this.activeLoop) {
232
+ return { decision: 'allow', reason: 'No active loop' };
233
+ }
234
+
235
+ const config = this.activeLoop.config;
236
+
237
+ // Accumulate knowledge
238
+ if (knowledge) {
239
+ if (!this.activeLoop.knowledge) {
240
+ this.activeLoop.knowledge = {};
241
+ }
242
+ if (knowledge.items) {
243
+ this.activeLoop.knowledge.items = [
244
+ ...(this.activeLoop.knowledge.items ?? []),
245
+ ...knowledge.items,
246
+ ];
247
+ }
248
+ if (knowledge.patternsApplied) {
249
+ this.activeLoop.knowledge.patternsApplied = [
250
+ ...(this.activeLoop.knowledge.patternsApplied ?? []),
251
+ ...knowledge.patternsApplied,
252
+ ];
253
+ }
254
+ if (knowledge.antiPatternsAvoided) {
255
+ this.activeLoop.knowledge.antiPatternsAvoided = [
256
+ ...(this.activeLoop.knowledge.antiPatternsAvoided ?? []),
257
+ ...knowledge.antiPatternsAvoided,
258
+ ];
259
+ }
260
+ }
261
+
262
+ // Check for completion promise
263
+ if (config.completionPromise) {
264
+ const promiseText = extractPromise(lastOutput);
265
+ if (promiseText && promiseText === config.completionPromise) {
266
+ const iterCount = this.activeLoop.iterations.length;
267
+ this.activeLoop.status = 'completed';
268
+ this.activeLoop.completedAt = new Date().toISOString();
269
+ this.moveToHistory('completed');
270
+ this.completedLoops.push(this.activeLoop);
271
+ this.activeLoop = null;
272
+ return {
273
+ decision: 'allow',
274
+ reason: `Loop completed — promise "${config.completionPromise}" detected`,
275
+ outcome: 'completed',
276
+ iteration: iterCount,
277
+ };
278
+ }
279
+ }
280
+
281
+ // Heuristic completion detection
282
+ const autoReason = detectImplicitCompletion(lastOutput, config);
283
+ if (autoReason) {
284
+ const iterCount = this.activeLoop.iterations.length;
285
+ this.activeLoop.status = 'completed';
286
+ this.activeLoop.completedAt = new Date().toISOString();
287
+ this.moveToHistory('completed');
288
+ this.completedLoops.push(this.activeLoop);
289
+ this.activeLoop = null;
290
+ return {
291
+ decision: 'allow',
292
+ reason: autoReason,
293
+ outcome: 'completed',
294
+ iteration: iterCount,
295
+ autoCompleted: true,
296
+ };
297
+ }
298
+
299
+ // Check max iterations
300
+ if (config.maxIterations > 0 && this.activeLoop.iterations.length >= config.maxIterations) {
301
+ const iterCount = this.activeLoop.iterations.length;
302
+ this.activeLoop.status = 'max-iterations';
303
+ this.activeLoop.completedAt = new Date().toISOString();
304
+ this.moveToHistory('max_iterations');
305
+ this.completedLoops.push(this.activeLoop);
306
+ this.activeLoop = null;
307
+ return {
308
+ decision: 'allow',
309
+ reason: `Max iterations (${config.maxIterations}) reached`,
310
+ outcome: 'max_iterations',
311
+ iteration: iterCount,
312
+ };
313
+ }
314
+
315
+ // Continue loop — increment iteration
316
+ const nextIteration = this.activeLoop.iterations.length + 1;
317
+ const iterationEntry: LoopIteration = {
318
+ iteration: nextIteration,
319
+ timestamp: new Date().toISOString(),
320
+ passed: false,
321
+ ...(durationMs !== undefined && { durationMs }),
322
+ };
323
+ this.activeLoop.iterations.push(iterationEntry);
324
+
325
+ // Run anomaly detection
326
+ const anomalyWarning = detectAnomaly(iterationEntry, config.mode);
327
+
328
+ // Build validation hint for system message
329
+ let validationHint = '';
330
+ switch (config.mode) {
331
+ case 'token-migration':
332
+ validationHint = `Run validation (tokens check). Target: score >= ${config.targetScore ?? 95}`;
333
+ break;
334
+ case 'contrast-fix':
335
+ validationHint = 'Run contrast check on all color pairs. Target: all PASS';
336
+ break;
337
+ case 'component-build':
338
+ validationHint = `Run validation (full check). Target: score >= ${config.targetScore ?? 90}`;
339
+ break;
340
+ case 'plan-iteration':
341
+ validationHint = `Run plan grading. Target: grade >= ${config.targetGrade ?? 'A'}`;
342
+ break;
343
+ case 'custom':
344
+ validationHint = 'Complete the task and validate your work';
345
+ break;
346
+ }
347
+
348
+ const maxDisplay = config.maxIterations > 0 ? String(config.maxIterations) : 'unlimited';
349
+ const systemMessage =
350
+ `[Loop — Iteration ${nextIteration}/${maxDisplay} | Mode: ${config.mode}] ${validationHint}` +
351
+ (config.completionPromise
352
+ ? ` | Output <promise>${config.completionPromise}</promise> ONLY when validation passes`
353
+ : '');
354
+
355
+ // Build full prompt with validation instructions
356
+ const fullPrompt = config.validationInstructions
357
+ ? `${config.prompt}\n\n${config.validationInstructions}`
358
+ : config.prompt;
359
+
360
+ return {
361
+ decision: 'block',
362
+ reason: fullPrompt,
363
+ prompt: fullPrompt,
364
+ systemMessage,
365
+ iteration: nextIteration,
366
+ ...(anomalyWarning && { anomalyWarning }),
367
+ };
368
+ }
369
+
370
+ /**
371
+ * Mark the active loop as completed (validation passed).
372
+ */
373
+ completeLoop(): LoopState {
374
+ if (!this.activeLoop) {
375
+ throw new Error('No active loop to complete.');
376
+ }
377
+
378
+ this.activeLoop.status = 'completed';
379
+ this.activeLoop.completedAt = new Date().toISOString();
380
+ const completed = this.activeLoop;
381
+ this.moveToHistory('completed');
382
+ this.completedLoops.push(completed);
383
+ this.activeLoop = null;
384
+ return completed;
385
+ }
386
+
387
+ /**
388
+ * Cancel the active loop.
389
+ */
390
+ cancelLoop(): LoopState {
391
+ if (!this.activeLoop) {
392
+ throw new Error('No active loop to cancel.');
393
+ }
394
+
395
+ this.activeLoop.status = 'cancelled';
396
+ this.activeLoop.completedAt = new Date().toISOString();
397
+ const cancelled = this.activeLoop;
398
+ this.moveToHistory('cancelled');
399
+ this.completedLoops.push(cancelled);
400
+ this.activeLoop = null;
401
+ return cancelled;
402
+ }
403
+
404
+ /**
405
+ * Get current loop status. Returns null if no active loop.
406
+ */
407
+ getStatus(): LoopState | null {
408
+ return this.activeLoop;
409
+ }
410
+
411
+ /**
412
+ * Get history of all completed/cancelled/max-iterations loops.
413
+ */
414
+ getHistory(): LoopState[] {
415
+ return [...this.completedLoops];
416
+ }
417
+
418
+ /**
419
+ * Get structured history entries (for brain session recording).
420
+ */
421
+ getHistoryEntries(): LoopHistoryEntry[] {
422
+ return [...this.historyEntries];
423
+ }
424
+
425
+ /**
426
+ * Check if a loop is currently active.
427
+ */
428
+ isActive(): boolean {
429
+ return this.activeLoop !== null;
430
+ }
431
+
432
+ /**
433
+ * Move active loop to history entries.
434
+ */
435
+ private moveToHistory(outcome: 'completed' | 'cancelled' | 'max_iterations'): void {
436
+ if (!this.activeLoop) return;
437
+ this.historyEntries.push({
438
+ id: this.activeLoop.id,
439
+ mode: this.activeLoop.config.mode,
440
+ intent: this.activeLoop.config.intent,
441
+ prompt: this.activeLoop.config.prompt,
442
+ iterations: this.activeLoop.iterations.length,
443
+ outcome,
444
+ startedAt: this.activeLoop.startedAt,
445
+ completedAt: this.activeLoop.completedAt ?? new Date().toISOString(),
446
+ });
447
+ }
448
+ }