@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
@@ -219,6 +219,51 @@ function protocolVersionRejection(config: McplServerConfig, error: { code: numbe
219
219
 
220
220
  const errorOf = (value: unknown): Error => (value instanceof Error ? value : new Error(String(value)));
221
221
 
222
+ /**
223
+ * The RFC-014 members of a server's advertisement (`toolHints`, `toolsets`,
224
+ * `surface`), read from it as the server sent it. They are declarations, not
225
+ * §6.2 capabilities: the host's capability mask, which reads every member as
226
+ * a capability, never applies to them. Null when the server advertised none
227
+ * of them, or no MCPL at all.
228
+ */
229
+ export function toolsetDeclarationsOf(advertised: unknown): Record<string, unknown> | null {
230
+ if (advertised === null || typeof advertised !== 'object' || Array.isArray(advertised)) return null;
231
+ const { toolHints, toolsets, surface } = advertised as Record<string, unknown>;
232
+ if (toolHints === undefined && toolsets === undefined && surface === undefined) return null;
233
+ return {
234
+ ...(toolHints !== undefined ? { toolHints } : {}),
235
+ ...(toolsets !== undefined ? { toolsets } : {}),
236
+ ...(surface !== undefined ? { surface } : {}),
237
+ };
238
+ }
239
+
240
+ /** What the RFC-014 read tap is handed as the connection reads a message
241
+ * that can carry hint changes (see McplServerConnection.setReadTap). */
242
+ export type HintRead =
243
+ /** A response to a request that can carry hints (RFC-014 §6.1 carrier 1),
244
+ * or a `tools/list`'s whole inventory (§4.1), its pages joined by
245
+ * sendToolsList, `straddled` when its pages carry different revs.
246
+ * `awaited`: whoever sent it still waits for it; false for a
247
+ * result that arrives after its timeout. A JSON-RPC error carries no state
248
+ * and isn't tapped. */
249
+ | { kind: 'result'; method: string; params: Record<string, unknown>; result: unknown; awaited: boolean; straddled?: boolean }
250
+ /** A push/event (carrier 2), as read, before the plane gate or the grant
251
+ * admit or refuse it. */
252
+ | { kind: 'push'; params: unknown }
253
+ /** A standalone tools/hints (carrier 3). */
254
+ | { kind: 'hints'; params: unknown };
255
+
256
+ /** Requests whose results can carry hint state, remembered for a late answer (RFC-014 §6.1). */
257
+ const HINTED_METHODS: ReadonlySet<string> = new Set(['tools/call', 'toolsets/open', 'toolsets/close', 'toolsets/list']);
258
+
259
+ function hintedParams(method: string, params: Record<string, unknown>): { toolset?: string; instance?: string } {
260
+ if (method !== 'toolsets/open' && method !== 'toolsets/close') return {};
261
+ return {
262
+ ...(typeof params.toolset === 'string' ? { toolset: params.toolset } : {}),
263
+ ...(typeof params.instance === 'string' ? { instance: params.instance } : {}),
264
+ };
265
+ }
266
+
222
267
  /**
223
268
  * A handshake that failed, and whose cleanup couldn't confirm that the child
224
269
  * exited. The transport goes to whoever owns the launch.
@@ -295,6 +340,7 @@ interface PendingRequest {
295
340
  resolve: (result: unknown) => void;
296
341
  reject: (error: Error) => void;
297
342
  method: string;
343
+ onRead?: (result: unknown) => void;
298
344
  /** Per-request timeout timer; cleared on response/close/timeout. */
299
345
  timer?: ReturnType<typeof setTimeout>;
300
346
  }
@@ -304,6 +350,9 @@ interface RequestOptions {
304
350
  timeoutMs?: number;
305
351
  /** Surface a late response even when it carries no checkpoint state. */
306
352
  surfaceOrphanedResponse?: boolean;
353
+ /** Called with a successful result as its response is read, before
354
+ * anything read after it and before the request's promise settles. */
355
+ onRead?: (result: unknown) => void;
307
356
  }
308
357
 
309
358
  /** A server→host message held while its plane is closed. `kept`: it came on
@@ -404,6 +453,49 @@ export class McplServerConnection extends EventEmitter {
404
453
  * the epoch their opening went to, never across a reconnect. */
405
454
  transportEpoch = 0;
406
455
 
456
+ /**
457
+ * RFC-014: the server's `toolHints`, `toolsets` and `surface` members as it
458
+ * advertised them at the last handshake (toolsetDeclarationsOf), or null.
459
+ * Read by the host's toolset controller; they grant nothing.
460
+ */
461
+ toolsetDeclarations: Record<string, unknown> | null = null;
462
+
463
+ /**
464
+ * RFC-014 §6.1: hint changes apply as each message carrying them is read,
465
+ * in rev order (arrival order on stdio and WebSocket), whatever becomes of
466
+ * the message: a push the plane holds or the grant refuses, a result its
467
+ * caller stopped waiting for. The tap sees every such message of the
468
+ * current transport, synchronously, before anything routes it; a late
469
+ * answer's request is remembered past its timeout (hintedRequests, while
470
+ * no more than 1,000 hinted requests await an answer) so its hints still
471
+ * apply. Null (the default) taps nothing, and the connection
472
+ * behaves as it always has.
473
+ */
474
+ private readTap: ((read: HintRead, epoch: number) => void) | null = null;
475
+ /** The hinted requests sent on this transport, by id, kept past a timeout
476
+ * so a late result is still tapped: each one's method, and an open's or
477
+ * close's toolset and instance, which are all the read of its result
478
+ * uses; never a call's arguments. Bounded. Cleared at each boundary,
479
+ * since the next transport numbers its requests afresh. */
480
+ private hintedRequests = new Map<string | number, { method: string; params: { toolset?: string; instance?: string } }>();
481
+
482
+ /** Install (or, with null, remove) the RFC-014 read tap (see readTap). */
483
+ setReadTap(tap: ((read: HintRead, epoch: number) => void) | null): void {
484
+ this.readTap = tap;
485
+ if (!tap) this.hintedRequests.clear();
486
+ }
487
+
488
+ /** Hand a read to the tap, contained: a tap that throws is logged, and the
489
+ * message goes on its way as if there were none. */
490
+ private tap(read: HintRead): void {
491
+ if (!this.readTap) return;
492
+ try {
493
+ this.readTap(read, this.transportEpoch);
494
+ } catch (error) {
495
+ console.error(`[mcpl] ${this.id}: the tool-hints tap failed: ${logDetail(describeCause(error))}`);
496
+ }
497
+ }
498
+
407
499
  /**
408
500
  * §17 manifest tracking (host side of RFC-003): what this host actually
409
501
  * fetched and acted on — never the server's announcement log (§17.10:
@@ -454,6 +546,8 @@ export class McplServerConnection extends EventEmitter {
454
546
  // (connect-failed, reconnect-failed), the server's stderr, and
455
547
  // orphaned-response audits.
456
548
  this.replaceHeld(this.keepAcrossBoundary(this.bufferedEvents));
549
+ // The next transport numbers its requests afresh (RFC-014's tap).
550
+ this.hintedRequests.clear();
457
551
  // RFC-007 §6.5: a filter is per connection epoch — after a reconnect the
458
552
  // server sends it again, and until then receives metadata only.
459
553
  this.toolObserveFilter = null;
@@ -1116,9 +1210,10 @@ export class McplServerConnection extends EventEmitter {
1116
1210
  owner.holdUnreaped(error.transport, error.cleanup);
1117
1211
  throw new McplUnreapedLaunchError(owner, error.failure, error.cleanup);
1118
1212
  }
1119
- const { transport, capabilities, droppedCapabilities, mcpToolsAdvertised, protocolVersion } = established;
1213
+ const { transport, capabilities, droppedCapabilities, mcpToolsAdvertised, protocolVersion, toolsetDeclarations } = established;
1120
1214
 
1121
1215
  const connection = new McplServerConnection(config.id, capabilities, transport);
1216
+ connection.toolsetDeclarations = toolsetDeclarations;
1122
1217
  connection.droppedCapabilities = droppedCapabilities;
1123
1218
  connection.mcpToolsAdvertised = mcpToolsAdvertised;
1124
1219
  connection.protocolVersion = protocolVersion;
@@ -1172,7 +1267,7 @@ export class McplServerConnection extends EventEmitter {
1172
1267
  config: McplServerConfig,
1173
1268
  hostCapabilities: McplHostCapabilities,
1174
1269
  signal?: AbortSignal,
1175
- ): Promise<{ transport: McplTransport; capabilities: McplCapabilities | null; droppedCapabilities: ReadonlySet<string>; mcpToolsAdvertised: boolean; protocolVersion: string }> {
1270
+ ): Promise<{ transport: McplTransport; capabilities: McplCapabilities | null; droppedCapabilities: ReadonlySet<string>; mcpToolsAdvertised: boolean; protocolVersion: string; toolsetDeclarations: Record<string, unknown> | null }> {
1176
1271
  const closedWhileConnecting = () => new Error(`MCPL server "${config.id}" was closed while connecting`);
1177
1272
  if (signal?.aborted) throw closedWhileConnecting();
1178
1273
  let transport: McplTransport;
@@ -1362,7 +1457,10 @@ export class McplServerConnection extends EventEmitter {
1362
1457
  // listener of its own, in the continuation of this return, before any
1363
1458
  // I/O callback can emit.
1364
1459
  transport.off('error', logAfterSettled);
1365
- return { transport, capabilities: scoped, droppedCapabilities: new Set(dropped), mcpToolsAdvertised, protocolVersion };
1460
+ return {
1461
+ transport, capabilities: scoped, droppedCapabilities: new Set(dropped), mcpToolsAdvertised, protocolVersion,
1462
+ toolsetDeclarations: toolsetDeclarationsOf(rawCapabilities),
1463
+ };
1366
1464
  } catch (err) {
1367
1465
  // Never leak the child / socket if the handshake fails. A cleanup that
1368
1466
  // can't confirm the child exited is part of the failure, never
@@ -1538,7 +1636,7 @@ export class McplServerConnection extends EventEmitter {
1538
1636
  }
1539
1637
 
1540
1638
  /** Send `channels/open` request and await result. It keeps a deadline as
1541
- * channels/publish does: a delivery opens a closed channel before it
1639
+ * channels/publish does: a delivery can open a closed channel before it
1542
1640
  * publishes, so an open that is never answered would hold the delivery,
1543
1641
  * and the turn that waits for it, for good. */
1544
1642
  sendChannelsOpen(params: ChannelsOpenParams): Promise<ChannelsOpenResult> {
@@ -1627,22 +1725,40 @@ export class McplServerConnection extends EventEmitter {
1627
1725
  * read it as a complete page). A page that isn't well formed (no `tools` array, a cursor
1628
1726
  * that isn't a string), or cursors that don't terminate (a repeat, or more
1629
1727
  * than {@link MAX_TOOL_LIST_PAGES} pages), are errors, never a partial or
1630
- * empty inventory published as complete.
1728
+ * empty inventory published as complete. The read tap (RFC-014) is handed
1729
+ * the complete inventory once, with the last page's `_meta`, as the last
1730
+ * page's response is read, before anything read after it; a list that
1731
+ * fails or times out hands it nothing. It's `straddled` when its pages
1732
+ * carry different `mcpl/rev`s.
1631
1733
  */
1632
1734
  async sendToolsList(): Promise<{ tools: McpToolDefinition[] }> {
1633
1735
  const tools: McpToolDefinition[] = [];
1634
1736
  const seen = new Set<string>();
1635
1737
  let cursor: string | undefined;
1636
1738
  let first: Record<string, unknown> | undefined;
1739
+ const read: McpToolDefinition[] = [];
1740
+ const revs = new Set<unknown>();
1741
+ const onRead = (result: unknown) => {
1742
+ if (!result || typeof result !== 'object' || !Array.isArray((result as { tools?: unknown }).tools)) return;
1743
+ const r = result as { tools: McpToolDefinition[]; nextCursor?: unknown; _meta?: unknown };
1744
+ for (const t of r.tools) read.push(t);
1745
+ const rev = (r._meta as Record<string, unknown> | undefined)?.['mcpl/rev'];
1746
+ if (typeof rev === 'number') revs.add(rev);
1747
+ if (r.nextCursor !== undefined && r.nextCursor !== null) return;
1748
+ this.tap({
1749
+ kind: 'result', method: 'tools/list', params: {}, ...(revs.size > 1 ? { straddled: true } : {}),
1750
+ result: { tools: read, ...(r._meta !== undefined ? { _meta: r._meta } : {}) }, awaited: true,
1751
+ });
1752
+ };
1637
1753
  for (let page = 0; page < MAX_TOOL_LIST_PAGES; page++) {
1638
- const result = await this.sendRequest('tools/list', cursor === undefined ? {} : { cursor }) as Record<string, unknown> | null;
1754
+ const result = await this.sendRequest('tools/list', cursor === undefined ? {} : { cursor }, { onRead }) as Record<string, unknown> | null;
1639
1755
  const malformed = (why: string) =>
1640
1756
  new Error(`MCPL server "${this.id}" sent a malformed tools/list page ${page + 1}: ${why}`);
1641
1757
  if (!result || typeof result !== 'object' || !Array.isArray(result.tools)) {
1642
1758
  throw malformed('no tools array');
1643
1759
  }
1644
1760
  first ??= result;
1645
- tools.push(...(result.tools as McpToolDefinition[]));
1761
+ for (const t of result.tools as McpToolDefinition[]) tools.push(t);
1646
1762
  if (result.nextCursor === undefined || result.nextCursor === null) {
1647
1763
  // The first page's other fields are kept as they came, so a
1648
1764
  // single-page answer is returned exactly as before.
@@ -1662,18 +1778,39 @@ export class McplServerConnection extends EventEmitter {
1662
1778
  throw new Error(`MCPL server "${this.id}" tools/list did not finish within ${MAX_TOOL_LIST_PAGES} pages`);
1663
1779
  }
1664
1780
 
1665
- /** Send `tools/call` and return the result. Optionally includes state/checkpoint for stateful tools. */
1781
+ /** Send `tools/call` and return the result. Optionally includes
1782
+ * state/checkpoint for stateful tools, and `_meta` (RFC-014 §5.5: the
1783
+ * instance a call is for rides in `_meta["mcpl/instance"]`). */
1666
1784
  sendToolsCall(
1667
1785
  name: string,
1668
1786
  args: Record<string, unknown>,
1669
1787
  stateParams?: { state?: unknown; checkpoint?: string },
1788
+ meta?: Record<string, unknown>,
1670
1789
  ): Promise<McpToolCallResult> {
1671
1790
  const params: Record<string, unknown> = { name, arguments: args };
1672
1791
  if (stateParams?.state !== undefined) params.state = stateParams.state;
1673
1792
  if (stateParams?.checkpoint !== undefined) params.checkpoint = stateParams.checkpoint;
1793
+ if (meta !== undefined) params._meta = meta;
1674
1794
  return this.sendRequest('tools/call', params) as Promise<McpToolCallResult>;
1675
1795
  }
1676
1796
 
1797
+ /** RFC-014 §5.2 `toolsets/open` (Host → Server). The server's refusal
1798
+ * rejects, as any error answer does. */
1799
+ sendToolsetsOpen(params: { toolset: string; instance?: string; options?: Record<string, unknown> }, timeoutMs?: number): Promise<Record<string, unknown> | null> {
1800
+ return this.sendRequest('toolsets/open', { ...params }, timeoutMs === undefined ? {} : { timeoutMs }) as Promise<Record<string, unknown> | null>;
1801
+ }
1802
+
1803
+ /** RFC-014 §5.2 `toolsets/close` (Host → Server). */
1804
+ sendToolsetsClose(params: { toolset: string; instance?: string }, timeoutMs?: number): Promise<Record<string, unknown> | null> {
1805
+ return this.sendRequest('toolsets/close', { ...params }, timeoutMs === undefined ? {} : { timeoutMs }) as Promise<Record<string, unknown> | null>;
1806
+ }
1807
+
1808
+ /** RFC-014 §5.3 `toolsets/list` (Host → Server): what the principal has
1809
+ * open, and the full hint state of its dynamic tools. */
1810
+ sendToolsetsList(timeoutMs?: number): Promise<Record<string, unknown> | null> {
1811
+ return this.sendRequest('toolsets/list', {}, timeoutMs === undefined ? {} : { timeoutMs }) as Promise<Record<string, unknown> | null>;
1812
+ }
1813
+
1677
1814
  /**
1678
1815
  * Send a tools/call with a mandatory request-local deadline. This is used by
1679
1816
  * durable external-side-effect accounting, whose safety bound must remain in
@@ -1940,7 +2077,7 @@ export class McplServerConnection extends EventEmitter {
1940
2077
  if (!permanent) this.scheduleReconnect();
1941
2078
  return;
1942
2079
  }
1943
- const { transport, capabilities, droppedCapabilities, mcpToolsAdvertised, protocolVersion } = established;
2080
+ const { transport, capabilities, droppedCapabilities, mcpToolsAdvertised, protocolVersion, toolsetDeclarations } = established;
1944
2081
 
1945
2082
  if (signal.aborted) {
1946
2083
  // Closed by the host while the handshake finished: this launch never
@@ -1968,6 +2105,7 @@ export class McplServerConnection extends EventEmitter {
1968
2105
  this.resetPolicyForTransportBoundary();
1969
2106
  this.transport = transport;
1970
2107
  this.capabilities = capabilities;
2108
+ this.toolsetDeclarations = toolsetDeclarations;
1971
2109
  this.droppedCapabilities = droppedCapabilities;
1972
2110
  this.mcpToolsAdvertised = mcpToolsAdvertised;
1973
2111
  this.protocolVersion = protocolVersion;
@@ -2073,7 +2211,7 @@ export class McplServerConnection extends EventEmitter {
2073
2211
  }
2074
2212
 
2075
2213
  return new Promise((resolve, reject) => {
2076
- const pending: PendingRequest = { resolve, reject, method };
2214
+ const pending: PendingRequest = { resolve, reject, method, ...(options.onRead ? { onRead: options.onRead } : {}) };
2077
2215
  const timeoutMs = options.timeoutMs ?? this.requestTimeoutMs;
2078
2216
  if (timeoutMs > 0) {
2079
2217
  pending.timer = setTimeout(() => {
@@ -2112,6 +2250,12 @@ export class McplServerConnection extends EventEmitter {
2112
2250
  pending.timer.unref?.();
2113
2251
  }
2114
2252
  this.pendingRequests.set(id, pending);
2253
+ // RFC-014: a request whose result can carry hints is remembered past
2254
+ // its timeout, so a late result's hints still apply (readTap).
2255
+ if (this.readTap && HINTED_METHODS.has(method)) {
2256
+ this.hintedRequests.set(id, { method, params: hintedParams(method, params) });
2257
+ if (this.hintedRequests.size > 1000) this.hintedRequests.delete(this.hintedRequests.keys().next().value!);
2258
+ }
2115
2259
  try {
2116
2260
  this.transport!.writeLine(line);
2117
2261
  } catch (err) {
@@ -2120,6 +2264,7 @@ export class McplServerConnection extends EventEmitter {
2120
2264
  // method, id and the transport's words are the operator's, logged
2121
2265
  // after the refusal, as refused() logs.
2122
2266
  this.pendingRequests.delete(id);
2267
+ this.hintedRequests.delete(id);
2123
2268
  if (pending.timer) clearTimeout(pending.timer);
2124
2269
  reject(new McplRequestError(
2125
2270
  `The request wasn't sent: this host couldn't write it to the "${this.id}" server, so nothing ran. Try again later.`,
@@ -2258,7 +2403,12 @@ export class McplServerConnection extends EventEmitter {
2258
2403
  private dispatchMessage(msg: JsonRpcRequest | JsonRpcResponse, epoch: number): void {
2259
2404
  // Is this a response to one of our outbound requests?
2260
2405
  if ('id' in msg && msg.id != null && !('method' in msg)) {
2261
- this.handleResponse(msg as JsonRpcResponse, epoch !== this.transportEpoch);
2406
+ const late = epoch !== this.transportEpoch;
2407
+ // RFC-014: its hints apply as it's read, whether or not anyone still
2408
+ // waits for it. A dead transport's revs are its own, so a response
2409
+ // read after its boundary is never tapped.
2410
+ if (!late) this.tapResponse(msg as JsonRpcResponse);
2411
+ this.handleResponse(msg as JsonRpcResponse, late);
2262
2412
  return;
2263
2413
  }
2264
2414
 
@@ -2282,6 +2432,17 @@ export class McplServerConnection extends EventEmitter {
2282
2432
  return;
2283
2433
  }
2284
2434
 
2435
+ // RFC-014 §6.1: a push's hints apply as it's read, before the plane gate
2436
+ // holds it or the grant refuses it. A standalone tools/hints is consumed
2437
+ // here when a tap listens (it needs no grant), and answered if it came as
2438
+ // a request; with no tap it stays a method this host doesn't handle.
2439
+ if (request.method === 'push/event') this.tap({ kind: 'push', params: request.params });
2440
+ else if (request.method === 'tools/hints' && this.readTap) {
2441
+ this.tap({ kind: 'hints', params: request.params });
2442
+ if (request.id != null) this.sendResponse(request.id, {});
2443
+ return;
2444
+ }
2445
+
2285
2446
  const eventName = typeof request.method === 'string' && Object.hasOwn(McplServerConnection.METHOD_TO_EVENT, request.method)
2286
2447
  ? McplServerConnection.METHOD_TO_EVENT[request.method] : undefined;
2287
2448
 
@@ -2427,6 +2588,18 @@ export class McplServerConnection extends EventEmitter {
2427
2588
  return true;
2428
2589
  }
2429
2590
 
2591
+ /** Tap a response to a hinted request (RFC-014): its result's hints, or a
2592
+ * list's statement, apply now, awaited or not. */
2593
+ private tapResponse(response: JsonRpcResponse): void {
2594
+ if (!this.readTap) return;
2595
+ const sent = this.hintedRequests.get(response.id);
2596
+ if (!sent) return;
2597
+ this.hintedRequests.delete(response.id);
2598
+ const result = response.result;
2599
+ if (response.error || result === null || typeof result !== 'object' || Array.isArray(result)) return;
2600
+ this.tap({ kind: 'result', method: sent.method, params: sent.params, result, awaited: this.pendingRequests.has(response.id) });
2601
+ }
2602
+
2430
2603
  /**
2431
2604
  * Handle a JSON-RPC response by resolving/rejecting the corresponding pending request.
2432
2605
  */
@@ -2516,6 +2689,11 @@ export class McplServerConnection extends EventEmitter {
2516
2689
  logValueRedacted(response.error.message, redact),
2517
2690
  );
2518
2691
  } else {
2692
+ try {
2693
+ pending.onRead?.(response.result);
2694
+ } catch (error) {
2695
+ console.error(`MCPL server "${this.id}": reading ${pending.method}'s result failed: ${logDetail(describeCause(error))}`);
2696
+ }
2519
2697
  pending.resolve(response.result);
2520
2698
  }
2521
2699
  }
package/src/mcpl/tags.ts CHANGED
@@ -21,8 +21,10 @@ const CORE_IMPLIES: Readonly<Record<string, readonly string[]>> = {
21
21
  /**
22
22
  * RFC-001 core tags that mark a message as being ABOUT the conversation
23
23
  * rather than part of it: reactions (added or removed), edits and deletions
24
- * of an existing message. These are markers, not turns — they must not clear
25
- * explicit-send suppression, count as a new conversational round, or
24
+ * of an existing message, and (revision 3) `chat:system` notices from the
25
+ * platform or the place: a join, a pin, the weather changing in a world.
26
+ * These are markers, not turns — they must not clear explicit-send
27
+ * suppression, count as a new conversational round, or
26
28
  * retarget the default publish channel. An edit can also be the first
27
29
  * version of a message the agent sees, and then counts as that message:
28
30
  * isMarkerTagged reads the host's mark for the case the coalescer knows
@@ -36,6 +38,7 @@ export const NON_CONVERSATIONAL_TAGS: ReadonlySet<string> = new Set([
36
38
  'chat:reaction-remove',
37
39
  'chat:edited',
38
40
  'chat:deleted',
41
+ 'chat:system',
39
42
  ]);
40
43
 
41
44
  /** True when a (possibly untyped) tag list carries a non-conversational marker tag. */
@@ -54,11 +57,19 @@ export function isMarkerTagged(tags: unknown, firstView: boolean): boolean {
54
57
  return hasNonConversationalTag(firstView && Array.isArray(tags) ? tags.filter((t) => t !== 'chat:edited') : tags);
55
58
  }
56
59
 
60
+ /** True when a (possibly untyped) tag list marks a `chat:system` notice: an
61
+ * event about the place, not a message anyone sent (RFC-001 rev 3). */
62
+ export function isSystemNotice(tags: unknown): boolean {
63
+ return Array.isArray(tags) && tags.includes('chat:system');
64
+ }
65
+
57
66
  /**
58
67
  * Expand a raw tag list over the normative core closure, then resolve the
59
68
  * §16.2 mutual exclusion: `chat:addressed` and `chat:ambient` cannot both
60
69
  * stand after expansion, and addressed wins — a message that is both
61
70
  * specifically-to-you and background noise is specifically-to-you.
71
+ * `chat:system` is another facet (kind, not addressing) and stands beside
72
+ * either: a pin in a DM is addressed by its place and still a notice.
62
73
  *
63
74
  * Pure; input order is preserved for the tags that survive, with implied
64
75
  * tags appended in first-cause order. Unknown and producer-namespaced tags
@@ -705,6 +705,27 @@ export class ToolLifecycleEmitter {
705
705
  });
706
706
  }
707
707
 
708
+ /**
709
+ * A call the host routes to another tool before it executes it: RFC-014's
710
+ * `toolsets--act`, which calls a server's dynamic tool. Until it opens, the
711
+ * call is described as that tool, with that tool's arguments, class and
712
+ * provider, so observers see (and narrowing, filters and class rules apply
713
+ * to) what actually runs, as for a direct call of it. Its ids stay.
714
+ */
715
+ retarget(agentName: string, callId: string, call: { name: string; input: unknown }): void {
716
+ const tracked = this.calls.get(callKey(agentName, callId));
717
+ if (!tracked || tracked.opened) return;
718
+ const described = this.host.describe(call.name);
719
+ const { serverId: _serverId, serverTool: _serverTool, ...kept } = tracked.call;
720
+ tracked.call = {
721
+ ...kept,
722
+ tool: call.name,
723
+ class: described.class,
724
+ ...(described.serverId !== undefined ? { serverId: described.serverId, serverTool: described.serverTool } : {}),
725
+ input: call.input,
726
+ };
727
+ }
728
+
708
729
  /**
709
730
  * The host refused the call before executing it — its provider is gone,
710
731
  * host tool policy denies it, a conversation-bound agent reached outside