@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
@@ -0,0 +1,697 @@
1
+ /**
2
+ * MCPL RFC-014 (tool presence, toolsets and surfaces): one server's tool
3
+ * presence as the host keeps it.
4
+ *
5
+ * The server's tool list stays its whole truth (RFC §2 principle 1); hints say
6
+ * which tools are static (the API tool list, as before) and which are dynamic
7
+ * (announced in the conversation as they come and go, and called through the
8
+ * host's `act`). Per server this keeps:
9
+ *
10
+ * - the tool definitions (tools/list) and the toolset declarations (manifest);
11
+ * - which declared toolsets and instances are open (toolsets/open results,
12
+ * toolsets/list, hint `toolsets` entries), and which undeclared groups the
13
+ * host opened itself (RFC §5.4: the server is never told);
14
+ * - the server's visibility statements per instance, and definition
15
+ * overrides, from hint changes applied in `rev` order, each at most once.
16
+ * One apply path serves every carrier: a call's, an open's or a close's
17
+ * result, a push's `hints`, a standalone `tools/hints`. `tools/list`
18
+ * (`mcpl/rev`) and `toolsets/list` are statements later changes build on;
19
+ * - which definitions the model has been shown, so a tool's first sentence
20
+ * is said once.
21
+ *
22
+ * Every mutation is bracketed by view() snapshots, and diff() turns the
23
+ * difference into what the model reads. Nothing here does I/O: the
24
+ * controller (toolset-host.ts) feeds it what the connection read.
25
+ *
26
+ * Ported from mcpl-cc-bridge's presence.ts, the reference host.
27
+ */
28
+ import { objectSignature, renderToolLines, toolDigest } from './tool-signature.js';
29
+ /** Toolset-less dynamic tools live under this key: always "open", visible per the server. */
30
+ export const NO_TOOLSET = '';
31
+ export const keyOf = (toolset, instance) => `${toolset}\u0000${instance ?? ''}`;
32
+ export const splitKey = (key) => {
33
+ const [toolset, instance] = key.split('\u0000');
34
+ return { toolset: toolset ?? '', ...(instance ? { instance } : {}) };
35
+ };
36
+ export const emptyChange = () => ({ changes: [], suggest: [], notes: [] });
37
+ export const isEmptyChange = (c) => !c.changes.length && !c.suggest.length && !c.notes.length && !c.ended?.length;
38
+ /** Whether a change to one key shows that key's tools in a block: what render prints and markShown counts as shown. */
39
+ const showsBlock = (c) => !c.closed && (c.opened || c.appeared.length > 0 || c.redefined.length > 0 || c.gone.length > 0);
40
+ const isObj = (v) => typeof v === 'object' && v !== null && !Array.isArray(v);
41
+ const arr = (v) => (Array.isArray(v) ? v : []);
42
+ const str = (v) => (typeof v === 'string' && v ? v : undefined);
43
+ /** The fields a hint definition may change (RFC §6.1); `name` and all of `_meta` are ignored. */
44
+ export function overrideOf(d) {
45
+ return {
46
+ ...(typeof d.description === 'string' ? { description: d.description } : {}),
47
+ ...(isObj(d.inputSchema) ? { inputSchema: d.inputSchema } : {}),
48
+ ...(isObj(d.annotations) ? { annotations: d.annotations } : {}),
49
+ };
50
+ }
51
+ export class ToolPresence {
52
+ server;
53
+ names;
54
+ allowed;
55
+ listedName;
56
+ published;
57
+ /** Hints are in effect on the current connection: the host advertised toolHints and the server did too. */
58
+ active = false;
59
+ declared = {};
60
+ surface = null;
61
+ /** The last rev read on this connection (RFC §6.1); 0 at each connect. */
62
+ rev = 0;
63
+ tools = new Map();
64
+ open = new Map();
65
+ groups = new Set();
66
+ visible = new Map();
67
+ /** Definition overrides from hints: per key (an instance's own, or a toolset's), per tool. */
68
+ defs = new Map();
69
+ /** `ended` ids told to the agent, kept while the server still reports them (RFC §5.3). */
70
+ toldEnded = new Set();
71
+ /** Moves a tools/list made that the model's tool list doesn't show yet: told once a rebuild has published them. */
72
+ pendingMoves = new Map();
73
+ /** Digests of definitions the model has been shown with their first sentence. */
74
+ shown = new Set();
75
+ constructor(server,
76
+ /** The meta-tools' names, for the hints rendered text gives. */
77
+ names,
78
+ /** The host's tool policy (enabledTools/disabledTools): a tool it refuses is as if unlisted. */
79
+ allowed = () => true,
80
+ /** How a static tool is named in the API tool list, for `look`. */
81
+ listedName = (tool) => tool,
82
+ /** Where the model's published tool list last put a tool, if it's there (shownAs). */
83
+ published = () => undefined) {
84
+ this.server = server;
85
+ this.names = names;
86
+ this.allowed = allowed;
87
+ this.listedName = listedName;
88
+ this.published = published;
89
+ }
90
+ // ── connection lifecycle ──
91
+ /** A new connection: revs start over; hints count only when both sides advertised toolHints. */
92
+ beginConnection(mcpl, hostHints) {
93
+ this.rev = 0;
94
+ // RFC §6.1: hint _meta and tools/hints from a server that did not advertise toolHints are ignored.
95
+ const active = hostHints && !!mcpl && mcpl.toolHints === true;
96
+ if (!active) {
97
+ if (this.active)
98
+ this.deactivate();
99
+ return;
100
+ }
101
+ this.active = true;
102
+ this.setManifest(mcpl);
103
+ }
104
+ /** Toolset declarations and the surface hint, from initialize or a manifest re-fetch. */
105
+ setManifest(mcpl) {
106
+ const prev = this.declared;
107
+ const decl = {};
108
+ if (mcpl && isObj(mcpl.toolsets)) {
109
+ for (const [name, d] of Object.entries(mcpl.toolsets)) {
110
+ if (!isObj(d) || !name)
111
+ continue;
112
+ decl[name] = {
113
+ ...(str(d.title) ? { title: str(d.title) } : {}),
114
+ ...(str(d.summary) ? { summary: str(d.summary) } : {}),
115
+ default: d.default === 'open' ? 'open' : 'closed',
116
+ ...(d.instances === true ? { instances: true } : {}),
117
+ ...(isObj(d.options) ? { options: d.options } : {}),
118
+ };
119
+ }
120
+ }
121
+ this.declared = decl;
122
+ for (const name of Object.keys(decl))
123
+ this.groups.delete(name);
124
+ this.surface = mcpl && isObj(mcpl.surface) ? { title: str(mcpl.surface.title), summary: str(mcpl.surface.summary) } : null;
125
+ // RFC §5.1: removing a toolset, or turning its instances on or off, ends
126
+ // its open instances; one that becomes undeclared starts closed under the
127
+ // host's gating.
128
+ for (const [k, r] of [...this.open]) {
129
+ if (!decl[r.toolset] || !!decl[r.toolset].instances !== !!prev[r.toolset]?.instances)
130
+ this.dropKey(k);
131
+ }
132
+ for (const name of Object.keys(prev)) {
133
+ if (decl[name])
134
+ continue;
135
+ this.groups.delete(name);
136
+ for (const k of new Set([...this.visible.keys(), ...this.defs.keys()]))
137
+ if (splitKey(k).toolset === name)
138
+ this.dropKey(k);
139
+ }
140
+ }
141
+ /** RFC §5.1: a server that withdraws toolHints is hint-less from then on: every tool static, nothing dynamic presented. */
142
+ deactivate() {
143
+ this.active = false;
144
+ this.declared = {};
145
+ this.surface = null;
146
+ this.open.clear();
147
+ this.groups.clear();
148
+ this.visible.clear();
149
+ this.defs.clear();
150
+ this.pendingMoves.clear();
151
+ }
152
+ /** tools/list: every definition, visible or not (RFC §4.1). A tool the host's policy refuses is left out. */
153
+ setTools(list) {
154
+ const seen = new Map([...this.tools.keys()].map((name) => [name, this.presenceOf(name)]));
155
+ const named = list.filter((t) => isObj(t) && typeof t.name === 'string');
156
+ this.tools = new Map();
157
+ for (const t of named)
158
+ if (this.allowed(t.name) && !this.tools.has(t.name))
159
+ this.tools.set(t.name, t);
160
+ for (const set of this.visible.values())
161
+ for (const n of [...set])
162
+ if (!this.tools.has(n))
163
+ set.delete(n);
164
+ // RFC §4.1: the list states each tool's presence, and presence follows
165
+ // it. A changed presence is a move the model's tool list shows at its
166
+ // next rebuild (§4.2; applyMoves); one the list reverses before then
167
+ // never reached that list.
168
+ for (const name of [...this.pendingMoves.keys()])
169
+ if (!this.tools.has(name))
170
+ this.pendingMoves.delete(name);
171
+ for (const name of this.active ? this.tools.keys() : []) {
172
+ const was = seen.get(name);
173
+ const listed = this.listedPresence(name);
174
+ if (this.pendingMoves.has(name)) {
175
+ if (this.pendingMoves.get(name) !== listed)
176
+ this.pendingMoves.delete(name);
177
+ }
178
+ else if (was !== undefined && was !== listed) {
179
+ this.pendingMoves.set(name, listed);
180
+ }
181
+ }
182
+ // The list's `mcpl/visible` is the server's word only where nothing else
183
+ // speaks: toolset-less dynamic tools and undeclared groups. For a declared
184
+ // toolset it's an aggregate ("visible in some open instance"); per-instance
185
+ // state comes from toolsets/list and hint changes (RFC §4.1, §5.3).
186
+ for (const t of this.tools.values()) {
187
+ if (this.listedPresence(t.name) !== 'dynamic')
188
+ continue;
189
+ const ts = this.toolsetOf(t.name);
190
+ if (ts && this.isDeclared(ts))
191
+ continue;
192
+ this.setVisible(keyOf(ts ?? NO_TOOLSET), t.name, t._meta?.['mcpl/visible'] !== false);
193
+ }
194
+ }
195
+ // ── reading ──
196
+ /** Whether the meta-tools speak for this server: hints in effect and something dynamic. */
197
+ get presents() {
198
+ if (!this.active)
199
+ return false;
200
+ if (Object.keys(this.declared).length)
201
+ return true;
202
+ for (const n of this.tools.keys())
203
+ if (this.presenceOf(n) === 'dynamic')
204
+ return true;
205
+ return false;
206
+ }
207
+ get allTools() {
208
+ return [...this.tools.values()];
209
+ }
210
+ tool(name) {
211
+ return this.tools.get(name);
212
+ }
213
+ toolsetOf(name) {
214
+ const m = this.tools.get(name)?._meta;
215
+ return m && typeof m['mcpl/toolset'] === 'string' && m['mcpl/toolset'] ? m['mcpl/toolset'] : undefined;
216
+ }
217
+ /** As the list states it: a toolset's tool is dynamic whatever a hint says (RFC §4.1). */
218
+ presenceOf(name) {
219
+ if (!this.active)
220
+ return 'static';
221
+ return this.listedPresence(name);
222
+ }
223
+ /** RFC §4.2: "Until it applies the move, the tool keeps working where it
224
+ * is." Whether a tool is at `place` in the tool list last published, or
225
+ * in the server's latest tools/list (its presence). Until a rebuild
226
+ * applies a move the two differ, and a call is taken at either. */
227
+ shownAs(name, place) {
228
+ if (!this.active)
229
+ return place === 'static';
230
+ return this.published(name) === place || this.listedPresence(name) === place;
231
+ }
232
+ listedPresence(name) {
233
+ if (this.toolsetOf(name))
234
+ return 'dynamic';
235
+ return this.tools.get(name)?._meta?.['mcpl/presence'] === 'dynamic' ? 'dynamic' : 'static';
236
+ }
237
+ isDeclared(toolset) {
238
+ return Object.hasOwn(this.declared, toolset);
239
+ }
240
+ /** Undeclared toolset names the server's tools are tagged with (RFC §5.4). */
241
+ get undeclaredGroups() {
242
+ const out = new Set();
243
+ for (const n of this.tools.keys()) {
244
+ const ts = this.toolsetOf(n);
245
+ if (ts && !this.isDeclared(ts))
246
+ out.add(ts);
247
+ }
248
+ return [...out];
249
+ }
250
+ openRecords(toolset) {
251
+ return [...this.open.values()].filter((r) => toolset === undefined || r.toolset === toolset);
252
+ }
253
+ isOpen(toolset, instance) {
254
+ return this.isDeclared(toolset) ? this.open.has(keyOf(toolset, instance)) : this.groups.has(toolset);
255
+ }
256
+ get openGroups() {
257
+ return [...this.groups];
258
+ }
259
+ /**
260
+ * The definition the model sees, and the host checks arguments against, for
261
+ * `name` in `key`: the instance's own (a hint definition with `instance`),
262
+ * else the toolset's (one without), else the listed one (RFC §6.1).
263
+ */
264
+ defOf(key, name) {
265
+ const listed = this.tools.get(name);
266
+ const { toolset } = splitKey(key);
267
+ const o = this.defs.get(key)?.get(name) ?? (toolset ? this.defs.get(keyOf(toolset))?.get(name) : undefined);
268
+ return o ? { ...listed, ...o } : listed;
269
+ }
270
+ view() {
271
+ const v = new Map();
272
+ for (const [key, r] of this.open)
273
+ v.set(key, { title: r.title, tools: new Map() });
274
+ for (const g of this.groups)
275
+ if (!this.isDeclared(g))
276
+ v.set(keyOf(g), { tools: new Map() });
277
+ for (const name of this.tools.keys()) {
278
+ if (this.presenceOf(name) !== 'dynamic')
279
+ continue;
280
+ const ts = this.toolsetOf(name);
281
+ if (ts && this.isDeclared(ts)) {
282
+ for (const [key, r] of this.open)
283
+ if (r.toolset === ts && this.visible.get(key)?.has(name))
284
+ v.get(key).tools.set(name, this.defOf(key, name));
285
+ }
286
+ else if (ts) {
287
+ // Two controllers (RFC §4.1, §5.4): the host has the group open AND the server says visible.
288
+ const key = keyOf(ts);
289
+ if (this.groups.has(ts) && this.visible.get(key)?.has(name))
290
+ v.get(key).tools.set(name, this.defOf(key, name));
291
+ }
292
+ else {
293
+ const key = keyOf(NO_TOOLSET);
294
+ if (this.visible.get(key)?.has(name)) {
295
+ if (!v.has(key))
296
+ v.set(key, { tools: new Map() });
297
+ v.get(key).tools.set(name, this.defOf(key, name));
298
+ }
299
+ }
300
+ }
301
+ return v;
302
+ }
303
+ diff(before, after) {
304
+ const out = emptyChange();
305
+ for (const key of new Set([...before.keys(), ...after.keys()])) {
306
+ const b = before.get(key);
307
+ const a = after.get(key);
308
+ const isGroupKey = splitKey(key).toolset !== NO_TOOLSET;
309
+ const appeared = [];
310
+ const redefined = [];
311
+ for (const [n, d] of a?.tools ?? []) {
312
+ const was = b?.tools.get(n);
313
+ if (!was)
314
+ appeared.push(d);
315
+ else if (toolDigest(was) !== toolDigest(d))
316
+ redefined.push(d);
317
+ }
318
+ const gone = [...(b?.tools.keys() ?? [])].filter((n) => !a?.tools.has(n));
319
+ const opened = isGroupKey && !b && !!a;
320
+ const closed = isGroupKey && !!b && !a;
321
+ const retitled = !!a && !!b && !!a.title && a.title !== b.title;
322
+ if (!appeared.length && !redefined.length && !gone.length && !opened && !closed && !retitled)
323
+ continue;
324
+ out.changes.push({ key, ...splitKey(key), title: a?.title ?? b?.title, opened, closed, retitled, appeared, gone, redefined, now: [...(a?.tools.values() ?? [])] });
325
+ }
326
+ return out;
327
+ }
328
+ // ── the one apply path ──
329
+ /** Apply a HintChanges (RFC §6.1) from any of its carriers, as it is read (see ApplyOutcome). */
330
+ apply(h) {
331
+ const none = { applied: false, skipped: false, behind: false, suggest: [], unlisted: false };
332
+ if (!isObj(h) || typeof h.rev !== 'number' || !Number.isFinite(h.rev))
333
+ return none;
334
+ if (h.rev <= this.rev)
335
+ return { ...none, behind: true };
336
+ const skipped = h.rev !== this.rev + 1;
337
+ this.rev = h.rev;
338
+ return { ...none, applied: true, skipped, ...this.applyBody(h) };
339
+ }
340
+ /**
341
+ * tools/list's `_meta["mcpl/rev"]` (RFC §4.1): the last rev the server had
342
+ * sent when it wrote the list, which takes none itself. Read in order it
343
+ * equals the last rev read; a higher one means a message was lost (true).
344
+ */
345
+ noteListRev(rev) {
346
+ if (typeof rev !== 'number' || !Number.isFinite(rev))
347
+ return false;
348
+ const lost = rev > this.rev;
349
+ this.rev = Math.max(this.rev, rev);
350
+ return lost;
351
+ }
352
+ applyBody(h) {
353
+ let unlisted = false;
354
+ for (const e of arr(h.toolsets)) {
355
+ if (!isObj(e) || !str(e.toolset) || !this.isDeclared(e.toolset))
356
+ continue; // undeclared toolsets are host-local
357
+ const ts = e.toolset;
358
+ const withInstances = !!this.declared[ts].instances;
359
+ const instance = withInstances ? str(e.instance) : undefined;
360
+ if (withInstances && !instance)
361
+ continue; // an instance-less statement about a toolset with instances names nothing
362
+ const key = keyOf(ts, instance);
363
+ // "open" opens, confirms, or (with a new title) retitles (RFC §6.1);
364
+ // it may come from another connection of the same principal (§5.6).
365
+ if (e.state === 'open')
366
+ this.open.set(key, { toolset: ts, ...(instance ? { instance } : {}), title: str(e.title) ?? this.open.get(key)?.title });
367
+ else if (e.state === 'closed')
368
+ this.dropKey(key);
369
+ }
370
+ for (const e of arr(h.tools)) {
371
+ if (!isObj(e) || !str(e.name))
372
+ continue;
373
+ const name = e.name;
374
+ if (!this.tools.has(name)) {
375
+ if (this.allowed(name))
376
+ unlisted = true;
377
+ continue;
378
+ }
379
+ // A hint's presence stages nothing: a hinted move takes effect when
380
+ // the server's tools/list states it (RFC §4.1), as the rebuild's own
381
+ // list does. A definition it gives is the tool's current one, kept
382
+ // whatever its presence now, for when it's shown dynamic (§6.1).
383
+ if (isObj(e.definition)) {
384
+ const key = this.defKeyFor(name, str(e.instance));
385
+ if (!this.defs.has(key))
386
+ this.defs.set(key, new Map());
387
+ this.defs.get(key).set(name, overrideOf(e.definition));
388
+ }
389
+ if (typeof e.visible === 'boolean')
390
+ for (const key of this.keysFor(name, str(e.instance)))
391
+ this.setVisible(key, name, e.visible);
392
+ }
393
+ const suggest = [];
394
+ for (const s of arr(h.suggest)) {
395
+ if (!isObj(s) || !str(s.toolset) || (s.action !== 'open' && s.action !== 'close'))
396
+ continue;
397
+ suggest.push({ toolset: s.toolset, ...(str(s.instance) ? { instance: str(s.instance) } : {}), action: s.action, reason: str(s.reason) ?? '' });
398
+ }
399
+ return { suggest, unlisted };
400
+ }
401
+ /**
402
+ * Where a definition lands (RFC §6.1): with `instance`, that instance's own;
403
+ * without, the toolset's (for a toolset without instances, its only one's).
404
+ */
405
+ defKeyFor(name, instance) {
406
+ const ts = this.toolsetOf(name);
407
+ if (!ts)
408
+ return keyOf(NO_TOOLSET);
409
+ return keyOf(ts, this.declared[ts]?.instances ? instance : undefined);
410
+ }
411
+ /** Where a visibility statement lands. For a toolset with instances, `instance` is required; a statement without one is ignored. */
412
+ keysFor(name, instance) {
413
+ const ts = this.toolsetOf(name);
414
+ if (!ts)
415
+ return [keyOf(NO_TOOLSET)];
416
+ const decl = this.declared[ts];
417
+ if (!decl?.instances)
418
+ return [keyOf(ts)];
419
+ return instance ? [keyOf(ts, instance)] : [];
420
+ }
421
+ setVisible(key, name, on) {
422
+ let s = this.visible.get(key);
423
+ if (!s)
424
+ this.visible.set(key, (s = new Set()));
425
+ if (on)
426
+ s.add(name);
427
+ else
428
+ s.delete(name);
429
+ }
430
+ dropKey(key) {
431
+ this.open.delete(key);
432
+ this.visible.delete(key);
433
+ this.defs.delete(key);
434
+ }
435
+ /** The result of a toolsets/open: the instance is open whatever its hints say. */
436
+ noteOpened(toolset, instance, title) {
437
+ if (!this.isDeclared(toolset))
438
+ return;
439
+ const withInstances = !!this.declared[toolset].instances;
440
+ if (withInstances && !instance)
441
+ return;
442
+ const key = keyOf(toolset, withInstances ? instance : undefined);
443
+ this.open.set(key, { toolset, ...(withInstances && instance ? { instance } : {}), title: title ?? this.open.get(key)?.title });
444
+ }
445
+ /** toolsets/close succeeded: closed, whatever the hints said. */
446
+ noteClosed(toolset, instance) {
447
+ this.dropKey(keyOf(toolset, this.declared[toolset]?.instances ? instance : undefined));
448
+ }
449
+ /**
450
+ * toolsets/list (RFC §5.3): what the principal has open, with the full hint
451
+ * state of its dynamic tools, the truth for declared toolsets and dynamic
452
+ * tools outside them. Anything the host believed open and the server doesn't
453
+ * report is closed, never re-opened here: opening can start a new game.
454
+ * Returns what was lost, what the server says ended while no connection was
455
+ * told, its suggestions, and whether its rev skipped one.
456
+ */
457
+ adopt(result) {
458
+ const had = [...this.open.values()];
459
+ // A full statement: a dynamic tool not stated visible is invisible.
460
+ // Undeclared groups stay as the host has them open or closed.
461
+ for (const k of new Set([...this.open.keys(), ...this.visible.keys(), ...this.defs.keys()])) {
462
+ if (this.isDeclared(splitKey(k).toolset))
463
+ this.dropKey(k);
464
+ else {
465
+ this.visible.delete(k);
466
+ this.defs.delete(k);
467
+ }
468
+ }
469
+ const r = isObj(result) ? result : {};
470
+ for (const o of arr(r.open)) {
471
+ if (!isObj(o) || !str(o.toolset))
472
+ continue;
473
+ this.noteOpened(o.toolset, str(o.instance), str(o.title));
474
+ }
475
+ let suggest = [];
476
+ let skipped = false;
477
+ let unlisted = false;
478
+ const h = isObj(r._meta) ? r._meta['mcpl/hints'] : undefined;
479
+ if (isObj(h)) {
480
+ ({ suggest, unlisted } = this.applyBody(h));
481
+ // Its own rev, at its place in order (RFC §6.1).
482
+ if (typeof h.rev === 'number' && Number.isFinite(h.rev)) {
483
+ skipped = h.rev !== this.rev + 1;
484
+ this.rev = Math.max(this.rev, h.rev);
485
+ }
486
+ }
487
+ const ended = [];
488
+ for (const e of arr(r.ended)) {
489
+ if (!isObj(e) || !str(e.toolset))
490
+ continue;
491
+ ended.push({ ...(str(e.id) ? { id: str(e.id) } : {}), toolset: e.toolset, ...(str(e.instance) ? { instance: str(e.instance) } : {}), reason: str(e.reason) ?? '' });
492
+ }
493
+ // RFC §5.3: a record is reported on every toolsets/list until it expires, and its id is never
494
+ // reused, so an id the server no longer reports needn't be remembered.
495
+ const reported = new Set(ended.flatMap((e) => (e.id ? [e.id] : [])));
496
+ for (const id of [...this.toldEnded])
497
+ if (!reported.has(id))
498
+ this.toldEnded.delete(id);
499
+ return { lost: had.filter((o) => !this.open.has(keyOf(o.toolset, o.instance))), ended, suggest, skipped, unlisted };
500
+ }
501
+ /**
502
+ * What the agent should hear about an adoption: what ended while it was
503
+ * away, each `ended` id until it has been told (RFC §5.3; markTold), and
504
+ * what is closed now. An ended record with an id is in `ended`, rendered
505
+ * while its id is untold.
506
+ */
507
+ adoptionNotes(lost, ended) {
508
+ const notes = [];
509
+ const told = [];
510
+ const endedKeys = new Set();
511
+ for (const e of ended) {
512
+ if (!e.id || !this.toldEnded.has(e.id))
513
+ endedKeys.add(keyOf(e.toolset, e.instance));
514
+ const note = `${this.label(keyOf(e.toolset, e.instance))} ended${e.reason ? `: ${e.reason}` : ''}.`;
515
+ if (e.id) {
516
+ if (!this.toldEnded.has(e.id))
517
+ told.push({ id: e.id, note });
518
+ }
519
+ else
520
+ notes.push(note);
521
+ }
522
+ for (const o of lost) {
523
+ if (endedKeys.has(keyOf(o.toolset, o.instance)))
524
+ continue;
525
+ notes.push(`${this.label(keyOf(o.toolset, o.instance))} is closed: ${this.server} no longer has it open. ${this.openHint(o.toolset)} starts a new one.`);
526
+ }
527
+ return { notes, ended: told };
528
+ }
529
+ /** The agent has been told these ended records (a notice written, a result it reads); the next statement forgets any it no longer reports. */
530
+ markTold(ended) {
531
+ for (const { id } of ended ?? [])
532
+ this.toldEnded.add(id);
533
+ }
534
+ /** The definitions a change set's tool blocks describe have reached the model. */
535
+ markShown(cs) {
536
+ for (const c of cs.changes)
537
+ if (showsBlock(c))
538
+ for (const t of c.now)
539
+ this.shown.add(toolDigest(t));
540
+ }
541
+ /** A set's ended notes whose ids are still untold, each once. */
542
+ untoldNotes(cs) {
543
+ const seen = new Set();
544
+ return (cs.ended ?? []).filter(({ id }) => !this.toldEnded.has(id) && !seen.has(id) && !!seen.add(id)).map(({ note }) => note);
545
+ }
546
+ // ── host-made opens of undeclared groups (RFC §5.4) ──
547
+ openGroup(g) {
548
+ this.groups.add(g);
549
+ }
550
+ closeGroup(g) {
551
+ this.groups.delete(g);
552
+ }
553
+ // ── boundaries ──
554
+ /** The static↔dynamic moves the lists made since the last rebuild, still to be shown. */
555
+ moves() {
556
+ return [...this.pendingMoves].map(([name, to]) => ({ name, to }));
557
+ }
558
+ /** At a rebuild of the model's tool list (RFC §4.2): those of `moves` still pending, now shown. */
559
+ applyMoves(moves = this.moves()) {
560
+ const out = moves.filter(({ name, to }) => this.pendingMoves.get(name) === to);
561
+ for (const { name } of out)
562
+ this.pendingMoves.delete(name);
563
+ return out;
564
+ }
565
+ // ── rendering ──
566
+ /** The marker a key's announcements begin with, `[server/toolset:instance]`, which a reminder looks for. */
567
+ label(key) {
568
+ const { toolset, instance } = splitKey(key);
569
+ return `[${this.server}${toolset ? `/${toolset}` : ''}${instance ? `:${instance}` : ''}]`;
570
+ }
571
+ callHint(key) {
572
+ const { instance } = splitKey(key);
573
+ return `${this.names.act} {server:"${this.server}"${instance ? `, instance:"${instance}"` : ''}, tool, args}`;
574
+ }
575
+ closeHint(key) {
576
+ const { toolset, instance } = splitKey(key);
577
+ if (!toolset)
578
+ return null;
579
+ return `${this.names.close} {server:"${this.server}", toolset:"${toolset}"${instance ? `, instance:"${instance}"` : ''}}`;
580
+ }
581
+ openHint(toolset, instance) {
582
+ return `${this.names.open} {server:"${this.server}", toolset:"${toolset}"${instance ? `, instance:"${instance}"` : ''}}`;
583
+ }
584
+ /** The tool block for one key: its label, how to call, and the visible tools (new or changed ones described). */
585
+ toolBlock(key, tools, opts = {}) {
586
+ const head = opts.headed === false ? '' : `${this.label(key)}${opts.title ? ` ${opts.title}` : ''} `;
587
+ if (!tools.length)
588
+ return [`${head}no tools available right now.`];
589
+ return [`${head}tools now — ${this.callHint(key)}:`, ...renderToolLines(tools, opts.seen ?? this.shown, { describeAll: opts.describeAll })];
590
+ }
591
+ /**
592
+ * What the model reads for a change set. `bare`: a key whose header the
593
+ * caller already printed. `mark: false` renders without counting the
594
+ * definitions it describes as shown, for text that may not reach the model
595
+ * (markShown counts them once it has). `describeAll`: every tool with its
596
+ * first sentence, as for a result that may reach no agent.
597
+ */
598
+ render(cs, opts = {}) {
599
+ const local = new Set();
600
+ const seen = opts.mark === false ? { has: (d) => this.shown.has(d) || local.has(d), add: (d) => { local.add(d); } } : this.shown;
601
+ const lines = [];
602
+ for (const c of cs.changes) {
603
+ const bare = opts.bare === c.key;
604
+ if (c.closed) {
605
+ lines.push(`${this.label(c.key)} closed.${c.gone.length ? ` Now unavailable: ${c.gone.join(', ')}.` : ''}`);
606
+ continue;
607
+ }
608
+ const title = (c.opened || c.retitled) && !bare ? c.title : undefined;
609
+ if (showsBlock(c))
610
+ lines.push(...this.toolBlock(c.key, c.now, { headed: !bare, title, seen, describeAll: opts.describeAll }));
611
+ else if (title)
612
+ lines.push(`${this.label(c.key)} ${title}`);
613
+ if (c.gone.length)
614
+ lines.push(`now unavailable: ${c.gone.join(', ')}`);
615
+ if (c.opened && !bare) {
616
+ const close = this.closeHint(c.key);
617
+ if (close)
618
+ lines.push(`${close} closes it.`);
619
+ }
620
+ }
621
+ for (const s of cs.suggest)
622
+ lines.push(this.renderSuggestion(s));
623
+ lines.push(...this.untoldNotes(cs), ...cs.notes);
624
+ return lines.join('\n');
625
+ }
626
+ /** RFC §6.2: a suggestion is shown, never acted on. */
627
+ renderSuggestion(s) {
628
+ const what = `${s.toolset}${s.instance ? `:${s.instance}` : ''}`;
629
+ const how = s.action === 'open' ? this.openHint(s.toolset, s.instance) : this.closeHint(keyOf(s.toolset, s.instance));
630
+ return `${this.server} suggests ${s.action === 'open' ? 'opening' : 'closing'} ${what}${s.reason ? `: ${s.reason}` : ''} — ${how} if you want to (nothing was done).`;
631
+ }
632
+ /** The keys a reminder restates: every open key of this server's view. */
633
+ openKeys() {
634
+ return [...this.view().keys()];
635
+ }
636
+ /** RFC §8.3 reminder: what is open, its current tools (described), how to call and close it. Null when nothing is open. */
637
+ reminder() {
638
+ const v = this.view();
639
+ if (!v.size)
640
+ return null;
641
+ const lines = [];
642
+ for (const [key, e] of v) {
643
+ lines.push(...this.toolBlock(key, [...e.tools.values()], { describeAll: true, title: e.title }));
644
+ const close = this.closeHint(key);
645
+ if (close)
646
+ lines.push(` ${close} closes it.`);
647
+ }
648
+ return lines.join('\n');
649
+ }
650
+ /** look naming this server: what is open (with tools) and what can be
651
+ * opened. `mark: false`, as for render, for a look whose result may
652
+ * reach no agent. */
653
+ catalog(opts = {}) {
654
+ const lines = [];
655
+ lines.push(this.surface ? `${this.server} — ${[this.surface.title, this.surface.summary].filter(Boolean).join(': ')}` : this.server);
656
+ const v = this.view();
657
+ if (v.size) {
658
+ for (const [key, e] of v) {
659
+ lines.push(...this.toolBlock(key, [...e.tools.values()], { describeAll: true, title: e.title, seen: opts.mark === false ? new Set() : undefined }).map((l) => ` ${l}`));
660
+ const close = this.closeHint(key);
661
+ if (close)
662
+ lines.push(` ${close} closes it.`);
663
+ }
664
+ }
665
+ else {
666
+ lines.push(' nothing open');
667
+ }
668
+ const openable = [];
669
+ for (const [name, d] of Object.entries(this.declared)) {
670
+ if (!d.instances && this.isOpen(name))
671
+ continue;
672
+ openable.push(` ${name}${d.title ? ` — ${d.title}` : ''}${d.summary ? `: ${d.summary}` : ''}` +
673
+ (d.instances ? ' (several can be open at once; name each with instance)' : '') +
674
+ (d.options ? ` · options ${objectSignature(d.options)}` : ''));
675
+ }
676
+ for (const g of this.undeclaredGroups) {
677
+ if (this.groups.has(g))
678
+ continue;
679
+ const names = this.allTools.filter((t) => this.toolsetOf(t.name) === g).map((t) => t.name);
680
+ openable.push(` ${g}: ${names.join(', ')}`);
681
+ }
682
+ if (openable.length)
683
+ lines.push(`can open — ${this.names.open} {server:"${this.server}", toolset, instance?, options?}:`, ...openable);
684
+ return lines.join('\n');
685
+ }
686
+ /** Where a tool can be called from now, for `look` naming it. */
687
+ toolStatus(tool) {
688
+ if (this.presenceOf(tool) === 'static')
689
+ return `static — called by name, as ${this.listedName(tool)}`;
690
+ const where = [...this.view().entries()].filter(([, e]) => e.tools.has(tool)).map(([k]) => k);
691
+ const ts = this.toolsetOf(tool);
692
+ return where.length
693
+ ? `available now in ${where.map((k) => this.label(k)).join(', ')} — ${this.names.act} {server:"${this.server}", tool:"${tool}"${where.length > 1 ? ', instance' : ''}, args}`
694
+ : `not available right now${ts ? ` (toolset ${ts})` : ''}`;
695
+ }
696
+ }
697
+ //# sourceMappingURL=tool-presence.js.map