@animalabs/agent-framework 0.26.2 → 0.28.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 (371) hide show
  1. package/README.md +99 -8
  2. package/dist/src/agent.d.ts +291 -23
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +596 -61
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/api/server.d.ts.map +1 -1
  7. package/dist/src/api/server.js +5 -4
  8. package/dist/src/api/server.js.map +1 -1
  9. package/dist/src/auto-continue.d.ts +34 -0
  10. package/dist/src/auto-continue.d.ts.map +1 -0
  11. package/dist/src/auto-continue.js +36 -0
  12. package/dist/src/auto-continue.js.map +1 -0
  13. package/dist/src/code-execution/isolate/scripts.d.ts +11 -0
  14. package/dist/src/code-execution/isolate/scripts.d.ts.map +1 -1
  15. package/dist/src/code-execution/isolate/scripts.js +5 -1
  16. package/dist/src/code-execution/isolate/scripts.js.map +1 -1
  17. package/dist/src/cut-off-round.d.ts +24 -0
  18. package/dist/src/cut-off-round.d.ts.map +1 -0
  19. package/dist/src/cut-off-round.js +87 -0
  20. package/dist/src/cut-off-round.js.map +1 -0
  21. package/dist/src/framework.d.ts +1038 -193
  22. package/dist/src/framework.d.ts.map +1 -1
  23. package/dist/src/framework.js +5967 -1514
  24. package/dist/src/framework.js.map +1 -1
  25. package/dist/src/gate/event-gate.d.ts +405 -46
  26. package/dist/src/gate/event-gate.d.ts.map +1 -1
  27. package/dist/src/gate/event-gate.js +2646 -399
  28. package/dist/src/gate/event-gate.js.map +1 -1
  29. package/dist/src/gate/index.d.ts +3 -2
  30. package/dist/src/gate/index.d.ts.map +1 -1
  31. package/dist/src/gate/index.js +2 -1
  32. package/dist/src/gate/index.js.map +1 -1
  33. package/dist/src/gate/recipe-reconcile.d.ts +103 -0
  34. package/dist/src/gate/recipe-reconcile.d.ts.map +1 -0
  35. package/dist/src/gate/recipe-reconcile.js +325 -0
  36. package/dist/src/gate/recipe-reconcile.js.map +1 -0
  37. package/dist/src/gate/reply-judge.d.ts +116 -0
  38. package/dist/src/gate/reply-judge.d.ts.map +1 -0
  39. package/dist/src/gate/reply-judge.js +467 -0
  40. package/dist/src/gate/reply-judge.js.map +1 -0
  41. package/dist/src/gate/types.d.ts +229 -8
  42. package/dist/src/gate/types.d.ts.map +1 -1
  43. package/dist/src/heartbeat/heartbeat.d.ts +184 -0
  44. package/dist/src/heartbeat/heartbeat.d.ts.map +1 -0
  45. package/dist/src/heartbeat/heartbeat.js +752 -0
  46. package/dist/src/heartbeat/heartbeat.js.map +1 -0
  47. package/dist/src/heartbeat/reminder-time.d.ts +5 -0
  48. package/dist/src/heartbeat/reminder-time.d.ts.map +1 -0
  49. package/dist/src/heartbeat/reminder-time.js +107 -0
  50. package/dist/src/heartbeat/reminder-time.js.map +1 -0
  51. package/dist/src/index.d.ts +10 -4
  52. package/dist/src/index.d.ts.map +1 -1
  53. package/dist/src/index.js +7 -2
  54. package/dist/src/index.js.map +1 -1
  55. package/dist/src/mcpl/capability-mask.d.ts.map +1 -1
  56. package/dist/src/mcpl/capability-mask.js +3 -2
  57. package/dist/src/mcpl/capability-mask.js.map +1 -1
  58. package/dist/src/mcpl/channel-registry.d.ts +433 -113
  59. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  60. package/dist/src/mcpl/channel-registry.js +1154 -435
  61. package/dist/src/mcpl/channel-registry.js.map +1 -1
  62. package/dist/src/mcpl/conversation-router.d.ts +16 -8
  63. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  64. package/dist/src/mcpl/conversation-router.js +29 -15
  65. package/dist/src/mcpl/conversation-router.js.map +1 -1
  66. package/dist/src/mcpl/convert-block.d.ts.map +1 -1
  67. package/dist/src/mcpl/convert-block.js +14 -2
  68. package/dist/src/mcpl/convert-block.js.map +1 -1
  69. package/dist/src/mcpl/hook-orchestrator.d.ts +7 -1
  70. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  71. package/dist/src/mcpl/hook-orchestrator.js +32 -7
  72. package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
  73. package/dist/src/mcpl/inbound-source.d.ts +26 -10
  74. package/dist/src/mcpl/inbound-source.d.ts.map +1 -1
  75. package/dist/src/mcpl/inbound-source.js +40 -69
  76. package/dist/src/mcpl/inbound-source.js.map +1 -1
  77. package/dist/src/mcpl/index.d.ts +5 -1
  78. package/dist/src/mcpl/index.d.ts.map +1 -1
  79. package/dist/src/mcpl/index.js +4 -0
  80. package/dist/src/mcpl/index.js.map +1 -1
  81. package/dist/src/mcpl/inference-router.d.ts +14 -2
  82. package/dist/src/mcpl/inference-router.d.ts.map +1 -1
  83. package/dist/src/mcpl/inference-router.js +73 -28
  84. package/dist/src/mcpl/inference-router.js.map +1 -1
  85. package/dist/src/mcpl/prose-grammar.d.ts +4 -0
  86. package/dist/src/mcpl/prose-grammar.d.ts.map +1 -1
  87. package/dist/src/mcpl/prose-grammar.js +6 -0
  88. package/dist/src/mcpl/prose-grammar.js.map +1 -1
  89. package/dist/src/mcpl/protocol-family.d.ts.map +1 -1
  90. package/dist/src/mcpl/protocol-family.js +3 -0
  91. package/dist/src/mcpl/protocol-family.js.map +1 -1
  92. package/dist/src/mcpl/push-coalescer.d.ts +20 -2
  93. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  94. package/dist/src/mcpl/push-coalescer.js +33 -11
  95. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  96. package/dist/src/mcpl/reference-fetcher.d.ts +63 -42
  97. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  98. package/dist/src/mcpl/reference-fetcher.js +177 -145
  99. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  100. package/dist/src/mcpl/references.d.ts +380 -46
  101. package/dist/src/mcpl/references.d.ts.map +1 -1
  102. package/dist/src/mcpl/references.js +736 -77
  103. package/dist/src/mcpl/references.js.map +1 -1
  104. package/dist/src/mcpl/server-connection.d.ts +93 -4
  105. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  106. package/dist/src/mcpl/server-connection.js +176 -11
  107. package/dist/src/mcpl/server-connection.js.map +1 -1
  108. package/dist/src/mcpl/tags.d.ts +9 -2
  109. package/dist/src/mcpl/tags.d.ts.map +1 -1
  110. package/dist/src/mcpl/tags.js +12 -2
  111. package/dist/src/mcpl/tags.js.map +1 -1
  112. package/dist/src/mcpl/tool-lifecycle.d.ts +11 -0
  113. package/dist/src/mcpl/tool-lifecycle.d.ts.map +1 -1
  114. package/dist/src/mcpl/tool-lifecycle.js +21 -0
  115. package/dist/src/mcpl/tool-lifecycle.js.map +1 -1
  116. package/dist/src/mcpl/tool-presence.d.ts +311 -0
  117. package/dist/src/mcpl/tool-presence.d.ts.map +1 -0
  118. package/dist/src/mcpl/tool-presence.js +697 -0
  119. package/dist/src/mcpl/tool-presence.js.map +1 -0
  120. package/dist/src/mcpl/tool-result-normalize.d.ts +9 -3
  121. package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -1
  122. package/dist/src/mcpl/tool-result-normalize.js +14 -6
  123. package/dist/src/mcpl/tool-result-normalize.js.map +1 -1
  124. package/dist/src/mcpl/tool-signature.d.ts +46 -0
  125. package/dist/src/mcpl/tool-signature.d.ts.map +1 -0
  126. package/dist/src/mcpl/tool-signature.js +321 -0
  127. package/dist/src/mcpl/tool-signature.js.map +1 -0
  128. package/dist/src/mcpl/toolset-host.d.ts +251 -0
  129. package/dist/src/mcpl/toolset-host.d.ts.map +1 -0
  130. package/dist/src/mcpl/toolset-host.js +720 -0
  131. package/dist/src/mcpl/toolset-host.js.map +1 -0
  132. package/dist/src/mcpl/toolsets-module.d.ts +83 -0
  133. package/dist/src/mcpl/toolsets-module.d.ts.map +1 -0
  134. package/dist/src/mcpl/toolsets-module.js +523 -0
  135. package/dist/src/mcpl/toolsets-module.js.map +1 -0
  136. package/dist/src/mcpl/transport.d.ts +2 -2
  137. package/dist/src/mcpl/transport.d.ts.map +1 -1
  138. package/dist/src/mcpl/transport.js +3 -2
  139. package/dist/src/mcpl/transport.js.map +1 -1
  140. package/dist/src/mcpl/types.d.ts +24 -2
  141. package/dist/src/mcpl/types.d.ts.map +1 -1
  142. package/dist/src/mcpl/types.js.map +1 -1
  143. package/dist/src/mcpl/visible-content.d.ts +7 -9
  144. package/dist/src/mcpl/visible-content.d.ts.map +1 -1
  145. package/dist/src/mcpl/visible-content.js +21 -9
  146. package/dist/src/mcpl/visible-content.js.map +1 -1
  147. package/dist/src/module-registry.d.ts +27 -9
  148. package/dist/src/module-registry.d.ts.map +1 -1
  149. package/dist/src/module-registry.js +77 -17
  150. package/dist/src/module-registry.js.map +1 -1
  151. package/dist/src/modules/discord/index.js +1 -1
  152. package/dist/src/modules/discord/index.js.map +1 -1
  153. package/dist/src/modules/history/index.d.ts +2 -1
  154. package/dist/src/modules/history/index.d.ts.map +1 -1
  155. package/dist/src/modules/history/index.js +73 -28
  156. package/dist/src/modules/history/index.js.map +1 -1
  157. package/dist/src/modules/history/semantic.d.ts +108 -6
  158. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  159. package/dist/src/modules/history/semantic.js +273 -57
  160. package/dist/src/modules/history/semantic.js.map +1 -1
  161. package/dist/src/modules/workspace/answers.d.ts +22 -0
  162. package/dist/src/modules/workspace/answers.d.ts.map +1 -0
  163. package/dist/src/modules/workspace/answers.js +123 -0
  164. package/dist/src/modules/workspace/answers.js.map +1 -0
  165. package/dist/src/modules/workspace/branch-intent.d.ts +5 -3
  166. package/dist/src/modules/workspace/branch-intent.d.ts.map +1 -1
  167. package/dist/src/modules/workspace/branch-intent.js +6 -2
  168. package/dist/src/modules/workspace/branch-intent.js.map +1 -1
  169. package/dist/src/modules/workspace/direct-write.d.ts +184 -0
  170. package/dist/src/modules/workspace/direct-write.d.ts.map +1 -0
  171. package/dist/src/modules/workspace/direct-write.js +417 -0
  172. package/dist/src/modules/workspace/direct-write.js.map +1 -0
  173. package/dist/src/modules/workspace/disk-agreement.d.ts +33 -11
  174. package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
  175. package/dist/src/modules/workspace/disk-agreement.js +48 -17
  176. package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
  177. package/dist/src/modules/workspace/edit-text.d.ts +27 -12
  178. package/dist/src/modules/workspace/edit-text.d.ts.map +1 -1
  179. package/dist/src/modules/workspace/edit-text.js +61 -29
  180. package/dist/src/modules/workspace/edit-text.js.map +1 -1
  181. package/dist/src/modules/workspace/effects.d.ts +10 -0
  182. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  183. package/dist/src/modules/workspace/effects.js +21 -14
  184. package/dist/src/modules/workspace/effects.js.map +1 -1
  185. package/dist/src/modules/workspace/index.d.ts +308 -78
  186. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  187. package/dist/src/modules/workspace/index.js +2103 -720
  188. package/dist/src/modules/workspace/index.js.map +1 -1
  189. package/dist/src/modules/workspace/observe.d.ts +75 -63
  190. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  191. package/dist/src/modules/workspace/observe.js +366 -180
  192. package/dist/src/modules/workspace/observe.js.map +1 -1
  193. package/dist/src/modules/workspace/read-page.d.ts +41 -0
  194. package/dist/src/modules/workspace/read-page.d.ts.map +1 -0
  195. package/dist/src/modules/workspace/read-page.js +219 -0
  196. package/dist/src/modules/workspace/read-page.js.map +1 -0
  197. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  198. package/dist/src/modules/workspace/reconcile.js +26 -16
  199. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  200. package/dist/src/modules/workspace/result-text.d.ts +68 -0
  201. package/dist/src/modules/workspace/result-text.d.ts.map +1 -0
  202. package/dist/src/modules/workspace/result-text.js +94 -0
  203. package/dist/src/modules/workspace/result-text.js.map +1 -0
  204. package/dist/src/modules/workspace/sync.d.ts +9 -0
  205. package/dist/src/modules/workspace/sync.d.ts.map +1 -1
  206. package/dist/src/modules/workspace/sync.js +14 -0
  207. package/dist/src/modules/workspace/sync.js.map +1 -1
  208. package/dist/src/modules/workspace/text.d.ts +29 -12
  209. package/dist/src/modules/workspace/text.d.ts.map +1 -1
  210. package/dist/src/modules/workspace/text.js +35 -33
  211. package/dist/src/modules/workspace/text.js.map +1 -1
  212. package/dist/src/modules/workspace/types.d.ts +137 -60
  213. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  214. package/dist/src/modules/workspace/types.js.map +1 -1
  215. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  216. package/dist/src/modules/workspace/watcher.js +8 -3
  217. package/dist/src/modules/workspace/watcher.js.map +1 -1
  218. package/dist/src/prose-drafts.d.ts +4 -3
  219. package/dist/src/prose-drafts.d.ts.map +1 -1
  220. package/dist/src/prose-drafts.js +3 -2
  221. package/dist/src/prose-drafts.js.map +1 -1
  222. package/dist/src/reasoning-loss-notice.d.ts +23 -0
  223. package/dist/src/reasoning-loss-notice.d.ts.map +1 -0
  224. package/dist/src/reasoning-loss-notice.js +50 -0
  225. package/dist/src/reasoning-loss-notice.js.map +1 -0
  226. package/dist/src/reasoning-loss.d.ts +24 -0
  227. package/dist/src/reasoning-loss.d.ts.map +1 -0
  228. package/dist/src/reasoning-loss.js +102 -0
  229. package/dist/src/reasoning-loss.js.map +1 -0
  230. package/dist/src/send-tools.d.ts +3 -2
  231. package/dist/src/send-tools.d.ts.map +1 -1
  232. package/dist/src/send-tools.js +3 -2
  233. package/dist/src/send-tools.js.map +1 -1
  234. package/dist/src/sent-rows.d.ts +59 -0
  235. package/dist/src/sent-rows.d.ts.map +1 -0
  236. package/dist/src/sent-rows.js +145 -0
  237. package/dist/src/sent-rows.js.map +1 -0
  238. package/dist/src/silent-heartbeat.d.ts +17 -10
  239. package/dist/src/silent-heartbeat.d.ts.map +1 -1
  240. package/dist/src/silent-heartbeat.js.map +1 -1
  241. package/dist/src/stable-hash.d.ts +3 -0
  242. package/dist/src/stable-hash.d.ts.map +1 -0
  243. package/dist/src/stable-hash.js +15 -0
  244. package/dist/src/stable-hash.js.map +1 -0
  245. package/dist/src/stored-injections.d.ts +99 -0
  246. package/dist/src/stored-injections.d.ts.map +1 -0
  247. package/dist/src/stored-injections.js +106 -0
  248. package/dist/src/stored-injections.js.map +1 -0
  249. package/dist/src/text-as-stored.d.ts +4 -3
  250. package/dist/src/text-as-stored.d.ts.map +1 -1
  251. package/dist/src/text-as-stored.js +4 -3
  252. package/dist/src/text-as-stored.js.map +1 -1
  253. package/dist/src/thinking-drops.d.ts +74 -0
  254. package/dist/src/thinking-drops.d.ts.map +1 -0
  255. package/dist/src/thinking-drops.js +96 -0
  256. package/dist/src/thinking-drops.js.map +1 -0
  257. package/dist/src/tool-args.d.ts +12 -0
  258. package/dist/src/tool-args.d.ts.map +1 -1
  259. package/dist/src/tool-args.js +35 -1
  260. package/dist/src/tool-args.js.map +1 -1
  261. package/dist/src/tool-image-ledger.d.ts +9 -8
  262. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  263. package/dist/src/tool-image-ledger.js +9 -8
  264. package/dist/src/tool-image-ledger.js.map +1 -1
  265. package/dist/src/tool-presentation.d.ts.map +1 -1
  266. package/dist/src/tool-presentation.js +3 -1
  267. package/dist/src/tool-presentation.js.map +1 -1
  268. package/dist/src/tool-result-guard.d.ts +12 -6
  269. package/dist/src/tool-result-guard.d.ts.map +1 -1
  270. package/dist/src/tool-result-guard.js +44 -17
  271. package/dist/src/tool-result-guard.js.map +1 -1
  272. package/dist/src/tool-result-history.d.ts +176 -24
  273. package/dist/src/tool-result-history.d.ts.map +1 -1
  274. package/dist/src/tool-result-history.js +411 -91
  275. package/dist/src/tool-result-history.js.map +1 -1
  276. package/dist/src/tune-out/coordinator.d.ts +17 -9
  277. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  278. package/dist/src/tune-out/coordinator.js +84 -37
  279. package/dist/src/tune-out/coordinator.js.map +1 -1
  280. package/dist/src/tune-out/tools.d.ts +4 -1
  281. package/dist/src/tune-out/tools.d.ts.map +1 -1
  282. package/dist/src/types/agent.d.ts +217 -7
  283. package/dist/src/types/agent.d.ts.map +1 -1
  284. package/dist/src/types/events.d.ts +33 -9
  285. package/dist/src/types/events.d.ts.map +1 -1
  286. package/dist/src/types/framework.d.ts +39 -3
  287. package/dist/src/types/framework.d.ts.map +1 -1
  288. package/dist/src/types/module.d.ts +95 -9
  289. package/dist/src/types/module.d.ts.map +1 -1
  290. package/dist/src/types/trace.d.ts +91 -3
  291. package/dist/src/types/trace.d.ts.map +1 -1
  292. package/package.json +5 -4
  293. package/src/agent.ts +661 -67
  294. package/src/api/server.ts +5 -4
  295. package/src/auto-continue.ts +43 -0
  296. package/src/code-execution/isolate/scripts.ts +11 -3
  297. package/src/cut-off-round.ts +98 -0
  298. package/src/framework.ts +6168 -1540
  299. package/src/gate/event-gate.ts +2895 -405
  300. package/src/gate/index.ts +10 -1
  301. package/src/gate/recipe-reconcile.ts +355 -0
  302. package/src/gate/reply-judge.ts +512 -0
  303. package/src/gate/types.ts +234 -9
  304. package/src/heartbeat/heartbeat.ts +804 -0
  305. package/src/heartbeat/reminder-time.ts +107 -0
  306. package/src/index.ts +11 -4
  307. package/src/mcpl/capability-mask.ts +3 -2
  308. package/src/mcpl/channel-registry.ts +1222 -468
  309. package/src/mcpl/conversation-router.ts +34 -16
  310. package/src/mcpl/convert-block.ts +14 -1
  311. package/src/mcpl/hook-orchestrator.ts +33 -6
  312. package/src/mcpl/inbound-source.ts +42 -69
  313. package/src/mcpl/index.ts +6 -1
  314. package/src/mcpl/inference-router.ts +79 -34
  315. package/src/mcpl/prose-grammar.ts +7 -0
  316. package/src/mcpl/protocol-family.ts +2 -0
  317. package/src/mcpl/push-coalescer.ts +51 -13
  318. package/src/mcpl/reference-fetcher.ts +201 -162
  319. package/src/mcpl/references.ts +851 -77
  320. package/src/mcpl/server-connection.ts +189 -11
  321. package/src/mcpl/tags.ts +13 -2
  322. package/src/mcpl/tool-lifecycle.ts +21 -0
  323. package/src/mcpl/tool-presence.ts +716 -0
  324. package/src/mcpl/tool-result-normalize.ts +18 -6
  325. package/src/mcpl/tool-signature.ts +316 -0
  326. package/src/mcpl/toolset-host.ts +727 -0
  327. package/src/mcpl/toolsets-module.ts +545 -0
  328. package/src/mcpl/transport.ts +3 -2
  329. package/src/mcpl/types.ts +25 -2
  330. package/src/mcpl/visible-content.ts +22 -9
  331. package/src/module-registry.ts +85 -26
  332. package/src/modules/discord/index.ts +1 -1
  333. package/src/modules/history/index.ts +70 -30
  334. package/src/modules/history/semantic.ts +308 -54
  335. package/src/modules/workspace/answers.ts +133 -0
  336. package/src/modules/workspace/branch-intent.ts +10 -5
  337. package/src/modules/workspace/direct-write.ts +460 -0
  338. package/src/modules/workspace/disk-agreement.ts +52 -21
  339. package/src/modules/workspace/edit-text.ts +84 -35
  340. package/src/modules/workspace/effects.ts +22 -13
  341. package/src/modules/workspace/index.ts +2142 -723
  342. package/src/modules/workspace/observe.ts +381 -177
  343. package/src/modules/workspace/read-page.ts +228 -0
  344. package/src/modules/workspace/reconcile.ts +27 -15
  345. package/src/modules/workspace/result-text.ts +121 -0
  346. package/src/modules/workspace/sync.ts +16 -0
  347. package/src/modules/workspace/text.ts +58 -32
  348. package/src/modules/workspace/types.ts +137 -60
  349. package/src/modules/workspace/watcher.ts +8 -3
  350. package/src/prose-drafts.ts +4 -3
  351. package/src/reasoning-loss-notice.ts +62 -0
  352. package/src/reasoning-loss.ts +120 -0
  353. package/src/send-tools.ts +3 -2
  354. package/src/sent-rows.ts +162 -0
  355. package/src/silent-heartbeat.ts +17 -10
  356. package/src/stable-hash.ts +14 -0
  357. package/src/stored-injections.ts +131 -0
  358. package/src/text-as-stored.ts +4 -3
  359. package/src/thinking-drops.ts +135 -0
  360. package/src/tool-args.ts +34 -1
  361. package/src/tool-image-ledger.ts +9 -8
  362. package/src/tool-presentation.ts +3 -1
  363. package/src/tool-result-guard.ts +45 -17
  364. package/src/tool-result-history.ts +471 -90
  365. package/src/tune-out/coordinator.ts +80 -37
  366. package/src/tune-out/tools.ts +4 -1
  367. package/src/types/agent.ts +220 -7
  368. package/src/types/events.ts +33 -9
  369. package/src/types/framework.ts +36 -3
  370. package/src/types/module.ts +91 -9
  371. package/src/types/trace.ts +94 -3
package/src/gate/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- export { EventGate, wakeProvenance, type WakeProvenance } from './event-gate.js';
1
+ export { EventGate, wakeProvenance, wakeLineText, type WakeProvenance, type WritePlacement } from './event-gate.js';
2
2
  export type {
3
3
  GateConfig,
4
4
  GateOptions,
@@ -9,4 +9,13 @@ export type {
9
9
  GateEventInfo,
10
10
  GateStatus,
11
11
  GatePolicyStats,
12
+ WakeLine,
13
+ JudgeConfig,
14
+ JudgeEventSummary,
15
+ JudgeFn,
16
+ JudgeRequest,
17
+ JudgeVerdict,
18
+ ReplyJudgeOptions,
19
+ UpcomingWake,
12
20
  } from './types.js';
21
+ export { ReplyJudge, DEFAULT_JUDGE_MODEL, DEFAULT_JUDGE_MAX_INPUT_TOKENS, MIN_JUDGE_INPUT_TOKENS, type ReplyJudgeDeps } from './reply-judge.js';
@@ -0,0 +1,355 @@
1
+ /**
2
+ * What a start does to an existing gate.json, given the recipe's gate config.
3
+ *
4
+ * The gate keeps the recipe it last applied in a file of its own beside
5
+ * gate.json (`gate.recipe.json` for `gate.json`), written by the gate. A start
6
+ * merges the recipe into gate.json three ways, rule by rule, against it:
7
+ * - a rule as the last recipe left it follows the new recipe: it takes the
8
+ * new definition, or goes when the new recipe no longer declares it, so a
9
+ * renamed rule goes under its old name and comes in under its new one;
10
+ * - a rule edited since the last recipe keeps its definition, unless it now
11
+ * agrees with the recipe's;
12
+ * - a rule both recipes declare, removed from gate.json since, comes back;
13
+ * - a rule no recipe declared (added at run time, or by hand) stays, and if
14
+ * the recipe now declares it as it stands, it's the recipe's from then on;
15
+ * - a rule new in the recipe comes in.
16
+ *
17
+ * Order is an edit of its own, apart from a rule's definition. When gate.json
18
+ * holds the rules both recipes declare in the last recipe's order, the recipe's
19
+ * rules take the new recipe's order, in the places gate.json gives them, and
20
+ * every other rule keeps its place. When one of them was moved since
21
+ * (wake_add_rule's position moves one), gate.json's order stays. Either way, a
22
+ * recipe rule new to gate.json goes before the recipe rule that follows it
23
+ * there, or after the one before it when none follows.
24
+ * `default` follows the same rule as a definition.
25
+ *
26
+ * Without a last recipe, as at the first start after this change, nothing is
27
+ * removed or redefined: the rules that match the recipe by name and definition
28
+ * take its order, every other rule stays, and the line names those that differ
29
+ * from the recipe or aren't in it. A rule that differs can be removed, and the
30
+ * next start restores it from the recipe.
31
+ *
32
+ * Rules are compared by what they mean and kept as written. Two rules agree
33
+ * when the gate reads them the same, so a rule `wake_add_rule` wrote as the
34
+ * gate reads it agrees with the recipe's rule as written, and a change to a
35
+ * field the gate doesn't read alone isn't an edit; the caller gives the
36
+ * meaning (`meaningOf`). A rule the gate leaves out has no reading, so it is
37
+ * compared as written, a change to any of its fields an edit. A rule that agrees with the recipe stays as gate.json
38
+ * holds it, and one that follows the recipe takes the recipe's rule as
39
+ * written. A rule with no name stays where gate.json holds it, and a recipe's
40
+ * rule with no name is never merged: the merge keys rules by name.
41
+ */
42
+
43
+ /** What a start changed, by rule name, for its trace and its log line. */
44
+ export interface RecipeChanges {
45
+ added: string[];
46
+ removed: string[];
47
+ updated: string[];
48
+ restored: string[];
49
+ moved: string[];
50
+ /** Edited since the last recipe, and kept, though the new recipe changed or dropped it. */
51
+ keptEdited: string[];
52
+ /** Kept, though the recipe declares the name with another definition. */
53
+ differsFromRecipe: string[];
54
+ /** Kept, and in no recipe: named at a start without a last recipe only. */
55
+ outsideRecipe: string[];
56
+ /** gate.json's order was kept, though the recipe changed its order, since a recipe rule was moved in it. */
57
+ orderKept: boolean;
58
+ defaultChange?: { from: string; to: string };
59
+ /** The live `default`, kept as edited, though the recipe changed it. */
60
+ defaultKept?: string;
61
+ /** At a start without a last recipe: the live `default`, kept, and the recipe's, which differs. */
62
+ defaultDiffers?: { kept: string; recipe: string };
63
+ /** Why the recorded recipe couldn't be read, when it exists and doesn't read. */
64
+ recordUnreadable?: string;
65
+ }
66
+
67
+ /** The file beside gate.json that holds the recipe the gate last applied. */
68
+ export function lastRecipePath(configPath: string): string {
69
+ return `${configPath.replace(/\.json$/, '')}.recipe.json`;
70
+ }
71
+
72
+ function sortKeys(value: unknown): unknown {
73
+ if (Array.isArray(value)) return value.map(sortKeys);
74
+ if (value && typeof value === 'object') {
75
+ // No prototype, so a key such as "__proto__", which JSON.parse gives as an
76
+ // own field, is copied as one rather than setting the prototype.
77
+ const out: Record<string, unknown> = Object.create(null);
78
+ for (const key of Object.keys(value as Record<string, unknown>).sort()) {
79
+ out[key] = sortKeys((value as Record<string, unknown>)[key]);
80
+ }
81
+ return out;
82
+ }
83
+ return value;
84
+ }
85
+
86
+ /** A config or policy as text that doesn't depend on its keys' order. */
87
+ export function canonical(value: unknown): string {
88
+ return JSON.stringify(sortKeys(value));
89
+ }
90
+
91
+ const same = (a: unknown, b: unknown): boolean => canonical(a) === canonical(b);
92
+
93
+ /** Changes with nothing in them, for a start to fill. */
94
+ export function noChanges(): RecipeChanges {
95
+ return {
96
+ added: [], removed: [], updated: [], restored: [], moved: [],
97
+ keptEdited: [], differsFromRecipe: [], outsideRecipe: [], orderKept: false,
98
+ };
99
+ }
100
+
101
+ /** A gate config as written: its rule list and its `default`, each as given. */
102
+ export interface WrittenConfig { rules: readonly unknown[]; default: unknown }
103
+
104
+ /** A rule's name as written, when it has a usable one. */
105
+ export function ruleName(rule: unknown): string | undefined {
106
+ if (!rule || typeof rule !== 'object' || Array.isArray(rule)) return undefined;
107
+ const name = (rule as { name?: unknown }).name;
108
+ return typeof name === 'string' && name ? name : undefined;
109
+ }
110
+
111
+ /** A rule name the list gives more than once, if any: the merge keys rules by name. */
112
+ export function duplicateName(rules: readonly unknown[]): string | undefined {
113
+ const seen = new Set<string>();
114
+ for (const rule of rules) {
115
+ const name = ruleName(rule);
116
+ if (name === undefined) continue;
117
+ if (seen.has(name)) return name;
118
+ seen.add(name);
119
+ }
120
+ return undefined;
121
+ }
122
+
123
+ /** A `default` as the gate reads one: left out, null or "" is "always". */
124
+ function behaviorOf(value: unknown): unknown {
125
+ return value === undefined || value === null || value === '' ? 'always' : value;
126
+ }
127
+
128
+ /** The names in one longest sequence that `a` and `b` share in order. */
129
+ function longestShared(a: string[], b: string[]): Set<string> {
130
+ const rows = a.length + 1;
131
+ const cols = b.length + 1;
132
+ const table: number[][] = Array.from({ length: rows }, () => new Array<number>(cols).fill(0));
133
+ for (let i = a.length - 1; i >= 0; i--) {
134
+ for (let j = b.length - 1; j >= 0; j--) {
135
+ table[i][j] = a[i] === b[j] ? table[i + 1][j + 1] + 1 : Math.max(table[i + 1][j], table[i][j + 1]);
136
+ }
137
+ }
138
+ const shared = new Set<string>();
139
+ let i = 0;
140
+ let j = 0;
141
+ while (i < a.length && j < b.length) {
142
+ if (a[i] === b[j]) {
143
+ shared.add(a[i]);
144
+ i++;
145
+ j++;
146
+ } else if (table[i + 1][j] > table[i][j + 1]) {
147
+ i++;
148
+ } else {
149
+ // On a tie, the result's rule is the one named as moved: the rule a
150
+ // swap brought earlier, such as one the old appending left behind a
151
+ // catch-all.
152
+ j++;
153
+ }
154
+ }
155
+ return shared;
156
+ }
157
+
158
+ /**
159
+ * Merge `recipe` into `live` against `last`, the recipe last applied (null
160
+ * when there is none), each as written. Pure: the caller reads and writes the
161
+ * files, and gives `meaningOf`, a rule's meaning as a string two rules share
162
+ * when they mean the same (as written, unless given). Returns the rule list
163
+ * and `default` to write, each as written, and whether `default` changed.
164
+ * Throws when `live` or `recipe` names a rule twice, since the merge keys
165
+ * rules by name.
166
+ */
167
+ export function reconcileWithRecipe(
168
+ live: WrittenConfig,
169
+ recipe: WrittenConfig,
170
+ last: WrittenConfig | null,
171
+ meaningOf: (rule: unknown) => string = canonical,
172
+ ): { rules: unknown[]; default: unknown; defaultChanged: boolean; changes: RecipeChanges } {
173
+ const agree = (a: unknown, b: unknown): boolean => meaningOf(a) === meaningOf(b);
174
+ const liveDuplicate = duplicateName(live.rules);
175
+ if (liveDuplicate !== undefined) {
176
+ throw new Error(`gate.json names the rule "${liveDuplicate}" twice`);
177
+ }
178
+ const recipeDuplicate = duplicateName(recipe.rules);
179
+ if (recipeDuplicate !== undefined) {
180
+ throw new Error(`the recipe names the rule "${recipeDuplicate}" twice`);
181
+ }
182
+
183
+ const changes = noChanges();
184
+ // Each rule by its name; a rule with no name by a key no name can be, so
185
+ // it keeps its place and matches nothing.
186
+ const keyOf = (rule: unknown, index: number): string => ruleName(rule) ?? `\u0000nameless ${index}`;
187
+ const byName = (rules: readonly unknown[]) => new Map(rules.flatMap((rule) => {
188
+ const name = ruleName(rule);
189
+ return name === undefined ? [] : [[name, rule] as const];
190
+ }));
191
+ const inLive = new Map(live.rules.map((rule, index) => [keyOf(rule, index), rule] as const));
192
+ const inRecipe = byName(recipe.rules);
193
+ const inLast = last ? byName(last.rules) : null;
194
+
195
+ // Each rule gate.json keeps, with its definition, and which of them take
196
+ // the recipe's order (`ordered`); the rest keep their places.
197
+ const definitions = new Map<string, unknown>();
198
+ const ordered = new Set<string>();
199
+ live.rules.forEach((rule, index) => {
200
+ const name = keyOf(rule, index);
201
+ const declared = inRecipe.get(name);
202
+ const before = inLast?.get(name);
203
+ if (declared !== undefined && agree(rule, declared)) {
204
+ // As the recipe has it: kept as written, whatever the record says, and
205
+ // nothing to change or name.
206
+ definitions.set(name, rule);
207
+ ordered.add(name);
208
+ } else if (ruleName(rule) === undefined) {
209
+ definitions.set(name, rule);
210
+ } else if (!inLast) {
211
+ definitions.set(name, rule);
212
+ (declared !== undefined ? changes.differsFromRecipe : changes.outsideRecipe).push(name);
213
+ } else if (before !== undefined) {
214
+ const edited = !agree(rule, before);
215
+ if (declared === undefined) {
216
+ if (edited) {
217
+ definitions.set(name, rule);
218
+ changes.keptEdited.push(name);
219
+ } else {
220
+ changes.removed.push(name);
221
+ }
222
+ } else {
223
+ // Both recipes declare it: it's a recipe rule, so it takes the recipe's order.
224
+ ordered.add(name);
225
+ if (edited) {
226
+ definitions.set(name, rule);
227
+ if (!agree(declared, before)) changes.keptEdited.push(name);
228
+ } else {
229
+ definitions.set(name, declared);
230
+ changes.updated.push(name);
231
+ }
232
+ }
233
+ } else {
234
+ // No recipe declared it as it stands: a run-time rule, or one the recipe
235
+ // now declares otherwise.
236
+ definitions.set(name, rule);
237
+ if (declared !== undefined) changes.differsFromRecipe.push(name);
238
+ }
239
+ });
240
+
241
+ const liveOrder = live.rules.map(keyOf);
242
+ const recipeOrder = recipe.rules.flatMap((rule) => {
243
+ const name = ruleName(rule);
244
+ return name === undefined ? [] : [name];
245
+ });
246
+ const staying = liveOrder.filter((name) => definitions.has(name));
247
+
248
+ // Whether gate.json holds the rules both recipes declare in the last recipe's order.
249
+ const shared = (order: string[]) => order.filter((name) => inLast?.has(name) && inRecipe.has(name) && inLive.has(name));
250
+ const lastOrder = last ? last.rules.flatMap((rule) => {
251
+ const name = ruleName(rule);
252
+ return name === undefined ? [] : [name];
253
+ }) : [];
254
+ const followsLast = canonical(shared(liveOrder)) === canonical(shared(lastOrder));
255
+
256
+ let order: string[];
257
+ if (followsLast) {
258
+ // The recipe's rules fill the places its rules hold, in the recipe's order.
259
+ const recipeRules = recipeOrder.filter((name) => ordered.has(name));
260
+ let next = 0;
261
+ order = staying.map((name) => (ordered.has(name) ? recipeRules[next++] : name));
262
+ } else {
263
+ order = staying;
264
+ // Named only when it holds something back: the recipe reordered these
265
+ // rules, and gate.json doesn't already hold the recipe's order.
266
+ changes.orderKept = canonical(shared(recipeOrder)) !== canonical(shared(lastOrder))
267
+ && canonical(shared(liveOrder)) !== canonical(shared(recipeOrder));
268
+ }
269
+ // A recipe rule new to gate.json goes before the recipe rule that follows it
270
+ // there, or after the one before it when none follows.
271
+ recipeOrder.forEach((name, index) => {
272
+ if (inLive.has(name)) return;
273
+ definitions.set(name, inRecipe.get(name)!);
274
+ (inLast?.has(name) ? changes.restored : changes.added).push(name);
275
+ const following = recipeOrder.slice(index + 1).find((later) => order.includes(later));
276
+ if (following !== undefined) {
277
+ order.splice(order.indexOf(following), 0, name);
278
+ return;
279
+ }
280
+ const preceding = recipeOrder.slice(0, index).reverse().find((earlier) => order.includes(earlier));
281
+ if (preceding !== undefined) order.splice(order.indexOf(preceding) + 1, 0, name);
282
+ else order.push(name);
283
+ });
284
+
285
+ // Only the recipe's rules move; every other rule keeps its place.
286
+ const stayedInOrder = longestShared(staying.filter((name) => ordered.has(name)), order.filter((name) => ordered.has(name)));
287
+ changes.moved = order.filter((name) => ordered.has(name) && !stayedInOrder.has(name));
288
+
289
+ // `default` as the gate reads it, compared as written once read.
290
+ const liveDefault = behaviorOf(live.default);
291
+ const recipeDefault = behaviorOf(recipe.default);
292
+ let defaultBehavior = live.default;
293
+ let defaultChanged = false;
294
+ const shownBehavior = (value: unknown): string => (typeof value === 'string' ? value : JSON.stringify(value));
295
+ if (!last) {
296
+ if (!same(liveDefault, recipeDefault)) changes.defaultDiffers = { kept: shownBehavior(liveDefault), recipe: shownBehavior(recipeDefault) };
297
+ } else {
298
+ const lastDefault = behaviorOf(last.default);
299
+ if (same(liveDefault, lastDefault) || same(liveDefault, recipeDefault)) {
300
+ if (!same(recipeDefault, liveDefault)) {
301
+ defaultBehavior = recipe.default;
302
+ defaultChanged = true;
303
+ changes.defaultChange = { from: shownBehavior(liveDefault), to: shownBehavior(recipeDefault) };
304
+ }
305
+ } else if (!same(recipeDefault, lastDefault)) {
306
+ changes.defaultKept = shownBehavior(liveDefault);
307
+ }
308
+ }
309
+
310
+ return {
311
+ rules: order.map((name) => definitions.get(name)),
312
+ default: defaultBehavior,
313
+ defaultChanged,
314
+ changes,
315
+ };
316
+ }
317
+
318
+ /** The log line a start gives its changes, or null when it changed nothing worth saying. */
319
+ export function reconcileLine(changes: RecipeChanges): string | null {
320
+ const parts: string[] = [];
321
+ if (changes.recordUnreadable !== undefined) {
322
+ parts.push(`the recorded recipe couldn't be read (${changes.recordUnreadable}), so this start removed and redefined nothing`);
323
+ }
324
+ const list = (label: string, names: string[]) => {
325
+ if (names.length > 0) parts.push(`${label} ${names.join(', ')}`);
326
+ };
327
+ list('added', changes.added);
328
+ list('removed', changes.removed);
329
+ list('updated', changes.updated);
330
+ list('restored', changes.restored);
331
+ list('moved', changes.moved);
332
+ list('kept as edited, though the recipe changed or dropped it:', changes.keptEdited);
333
+ if (changes.differsFromRecipe.length > 0) {
334
+ parts.push(
335
+ `kept, though the recipe defines it otherwise: ${changes.differsFromRecipe.join(', ')} `
336
+ + '(remove such a rule, and the next start restores it from the recipe)',
337
+ );
338
+ }
339
+ if (changes.outsideRecipe.length > 0) {
340
+ parts.push(
341
+ `kept, not in the recipe: ${changes.outsideRecipe.join(', ')} `
342
+ + "(a stale one can be removed, and no start brings it back)",
343
+ );
344
+ }
345
+ if (changes.defaultChange) parts.push(`default ${changes.defaultChange.from} → ${changes.defaultChange.to}`);
346
+ if (changes.defaultKept) parts.push(`default kept as edited (${changes.defaultKept}), though the recipe changed it`);
347
+ if (changes.defaultDiffers) {
348
+ parts.push(
349
+ `default kept as gate.json has it (${changes.defaultDiffers.kept}), though the recipe's is ${changes.defaultDiffers.recipe} `
350
+ + "(set it to the recipe's, and later starts follow the recipe)",
351
+ );
352
+ }
353
+ if (changes.orderKept) parts.push("gate.json's order kept, though the recipe reordered its rules, since a recipe rule was moved in it");
354
+ return parts.length > 0 ? `[gate] the recipe was applied to gate.json: ${parts.join('; ')}` : null;
355
+ }