@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
@@ -9,18 +9,148 @@
9
9
  * `maxChars` slice and re-introduces, one turn deferred, exactly the silent
10
10
  * hallucination this feature exists to kill.
11
11
  *
12
- * For MCP-shaped content arrays, this helper keeps text blocks verbatim,
13
- * apart from a lone UTF-16 surrogate, which becomes U+FFFD (#695), and
14
- * replaces image blocks with a short `[image: mimeType, ~NKB]` placeholder.
15
- * A string is already text and is kept as it is. For anything else it falls
16
- * back to JSON, written by RFC-005's rules at every depth (historyJson): a
17
- * reference is shown as its stub wherever it sits in the data, so its uri
18
- * never reaches history, and inline data that claims a uri or a disposition
19
- * is withheld.
12
+ * A content array (toolResultBlocks) is shown block by block, each block as
13
+ * both copies show it: text verbatim, apart from a lone UTF-16 surrogate,
14
+ * which becomes U+FFFD (#695), an image as a short
15
+ * `[image: mimeType, ~NKB]` placeholder, a reference as its stub, and any
16
+ * other block as a marker saying what it was and why it isn't shown. Anything
17
+ * else falls back to JSON, written by RFC-005's rules at every depth
18
+ * (historyJson): a reference is shown as its stub wherever it sits in the
19
+ * data, so its uri reaches history only as a link's line shows it where the
20
+ * host can't fetch the link and its uri can't carry a credential
21
+ * (shownLinkUri), and inline data that claims a uri or a disposition is
22
+ * withheld. A string is already text and is kept as it is.
20
23
  */
21
- import { INLINE_WITHHELD_TEXT, isInlineContradiction, referenceBlockForScript, referenceStubOrNull } from './mcpl/references.js';
22
- import { NON_STRING_TEXT_PLACEHOLDER } from './mcpl/visible-content.js';
24
+ import { AUDIO_NO_DATA_TEXT, AUDIO_NO_MEDIA_TYPE_TEXT, IMAGE_NO_DATA_TEXT, IMAGE_NO_MEDIA_TYPE_TEXT, INLINE_WITHHELD_TEXT, buildReferenceStub, classifyBlock, contentClaims, inlineNeverUri, referenceBlockForScript, referenceStubOrNull, referenceTypeLabel, resultClaims, } from './mcpl/references.js';
23
25
  import { formatToolImagePlaceholder, toolResultCutAt } from './tool-image-ledger.js';
26
+ /**
27
+ * Whether a tool result's live copy goes as native blocks, an image among
28
+ * them, rather than as its stored text. It does when the result holds an
29
+ * image and its links' blocks and markers come to no more than `maxChars`
30
+ * together. Past that, the live copy is the stored text, cut at the cap, so
31
+ * no number of links and markers carries a result past it. A reference's
32
+ * stub and a withheld line go whole beside an image whatever their length,
33
+ * and past its cut the stored copy keeps the newest 64 image slots the live
34
+ * copy showed (#403). Text blocks don't count either: the native copy cuts
35
+ * each at the cap on its own. Both copies decide by this, from each block's
36
+ * kind and the length of its text.
37
+ */
38
+ export function goesNative(blocks, maxChars) {
39
+ if (!blocks.some((block) => block.kind === 'image'))
40
+ return false;
41
+ if (!maxChars)
42
+ return true;
43
+ let bounded = 0;
44
+ for (const block of blocks)
45
+ if (block.kind === 'link' || block.kind === 'marker')
46
+ bounded += block.length;
47
+ return bounded <= maxChars;
48
+ }
49
+ /** The block types MCP defines for a tool result's content. */
50
+ const MCP_BLOCK_TYPES = new Set(['text', 'image', 'audio', 'resource', 'resource_link']);
51
+ /**
52
+ * Each element of `data` as both copies of a tool result show it, or null
53
+ * when `data` isn't a content array, so the result is data, written as JSON.
54
+ * An array is content when the result says so (`dataIsContent`, which the
55
+ * framework sets on an MCPL tool's result: every element of a protocol
56
+ * result's content is a block, of a type the host shows or not). Otherwise
57
+ * it's content only when every element is a block of one of MCP's own types,
58
+ * so a list of records of other types stays data. A module's array of
59
+ * records that all carry MCP's type names, such as `[{ type: 'image', url }]`,
60
+ * is read as content, and shows a marker for each record that isn't a block
61
+ * the lane can show.
62
+ */
63
+ export function toolResultBlocks(data, dataIsContent, claims) {
64
+ if (!isContentArray(data, dataIsContent))
65
+ return null;
66
+ // The result's claims, read as one (ResultClaims): what any of its
67
+ // references says of a uri (a `disposition: never`, an expiry) holds for
68
+ // each block that names it, whatever their order.
69
+ const read = claims ?? contentClaims(data);
70
+ // Each text well formed, as the context store makes it when it writes, so
71
+ // the copy sent and the copy stored carry the same text (#695): a stub's
72
+ // labels and a link's uri come from the block too.
73
+ return data.map((raw) => {
74
+ const block = toolResultBlock(raw, read);
75
+ return 'text' in block ? { ...block, text: block.text.toWellFormed() } : block;
76
+ });
77
+ }
78
+ /** Whether a tool result's data is a content array, which toolResultBlocks
79
+ * shows block by block. */
80
+ export function isContentArray(data, dataIsContent) {
81
+ return Array.isArray(data)
82
+ && (dataIsContent === true || data.every((element) => isPlainRecord(element) && MCP_BLOCK_TYPES.has(element.type)));
83
+ }
84
+ /**
85
+ * The claims of a tool result's data (ResultClaims), as showing it reads
86
+ * them: a content array's from its elements (contentClaims), and any other
87
+ * data's by the walk serialization takes (claimsInHistory). For a decision
88
+ * made about the result outside showing it, such as which of its references
89
+ * to fetch, or for showing part of it as the rest of it claims. Null where a
90
+ * value stops the walk, so what the data claims is unknown (claimsInHistory).
91
+ */
92
+ export function resultClaimsOf(data, dataIsContent) {
93
+ return isContentArray(data, dataIsContent) ? contentClaims(data) : claimsInHistory(data);
94
+ }
95
+ /** resultClaimsOf, throwing what stops the walk, for a caller that answers
96
+ * a stop itself: a script's text route, which fails then, as its view does
97
+ * (Ada-1017, room-203 #200374). */
98
+ export function resultClaimsOrThrow(data, dataIsContent) {
99
+ return isContentArray(data, dataIsContent) ? contentClaims(data) : claimsInHistoryOrThrow(data);
100
+ }
101
+ /** One element of a content array, as toolResultBlocks gives it. Only an
102
+ * image with data and a media type is shown as itself, as the push lane's
103
+ * converter shows one, and only text and images can be: this host's tool
104
+ * results carry nothing else, so inline audio is a marker here. */
105
+ function toolResultBlock(raw, claims) {
106
+ const c = classifyBlock(raw);
107
+ switch (c.kind) {
108
+ case 'text':
109
+ return { kind: 'text', text: raw.text };
110
+ case 'reference':
111
+ // A bounded stub, never the block's JSON, which would show its uri. A
112
+ // stub that shows an image type occupies a slot in save_recent_image's
113
+ // inventory (a failing one: its bytes are behind fetch_reference), so
114
+ // the index never slides past an image the resident saw. A link the
115
+ // host can't fetch, shown by its uri, is no slot, as no link was before
116
+ // links were references, when the result's JSON showed it. A link's
117
+ // block, stub or line, is `link`, counted toward the cap (goesNative).
118
+ return {
119
+ kind: c.testimony.fromLink ? 'link' : 'reference',
120
+ text: buildReferenceStub(c.testimony, 'from tool result', undefined, { unfetchedLinkUri: true, claims }),
121
+ };
122
+ case 'invalid':
123
+ return { kind: 'marker', text: c.shown };
124
+ case 'embedded':
125
+ // MCP's embedded resource shows as the block its contents make would
126
+ // show here. Its uri never shows.
127
+ if (c.contents.kind === 'block')
128
+ return toolResultBlock(c.contents.block, claims);
129
+ return c.contents.kind === 'withheld'
130
+ ? { kind: 'withheld', text: INLINE_WITHHELD_TEXT }
131
+ : { kind: 'marker', text: c.contents.text };
132
+ case 'inline': {
133
+ if (c.contradiction)
134
+ return { kind: 'withheld', text: INLINE_WITHHELD_TEXT };
135
+ const b = raw;
136
+ const image = b.type === 'image';
137
+ if (b.data === '')
138
+ return { kind: 'marker', text: image ? IMAGE_NO_DATA_TEXT : AUDIO_NO_DATA_TEXT };
139
+ if (typeof b.mimeType !== 'string' || b.mimeType === '') {
140
+ return { kind: 'marker', text: image ? IMAGE_NO_MEDIA_TYPE_TEXT : AUDIO_NO_MEDIA_TYPE_TEXT };
141
+ }
142
+ if (image)
143
+ return { kind: 'image', data: b.data, mimeType: b.mimeType };
144
+ return { kind: 'marker', text: toolResultAudioText(b.mimeType, b.data) };
145
+ }
146
+ }
147
+ }
148
+ /** What a tool result's inline audio shows as: its type and size, and that
149
+ * this host's tool results carry only text and images. */
150
+ function toolResultAudioText(mimeType, data) {
151
+ const what = [referenceTypeLabel(mimeType), formatSize(Math.floor(data.length * 3 / 4))].filter(Boolean).join(', ');
152
+ return `[Audio not shown: this host's tool results carry only text and images (${what}).]`;
153
+ }
24
154
  /**
25
155
  * House-safe default inline cap (chars) for tool results, error results, and
26
156
  * script wake payloads (issue #89: a resident should never eat a
@@ -52,7 +182,7 @@ export function toolResultDataToHistoryString(data, maxChars, options) {
52
182
  // RFC-005 shows only objects otherwise (shownInHistory), so a string has
53
183
  // nothing for historyJson to stub or withhold.
54
184
  const str = fromArray
55
- ?? (typeof data === 'string' ? data.toWellFormed() : historyJson(data))
185
+ ?? (typeof data === 'string' ? data.toWellFormed() : historyJson(data, options?.claims))
56
186
  ?? '';
57
187
  return maxChars ? truncateForHistory(str, maxChars) : str;
58
188
  }
@@ -64,6 +194,11 @@ export function toolResultDataToHistoryString(data, maxChars, options) {
64
194
  *
65
195
  * Each field is read once: the direct Agent API passes its caller's own
66
196
  * result here, unchecked, and a getter needn't answer the same way twice.
197
+ * Whatever stops reading or showing the data is UNCHECKED_RESULT_TEXT, with
198
+ * a log line naming the tool (`options.toolName`) and the error: the model's
199
+ * lane has no failure answer that doesn't wedge the round.
200
+ * Whether an array is content is the result's own `dataIsContent`, read for
201
+ * its truth as a result's flags are, whatever `options` says.
67
202
  *
68
203
  * Each text a tool result's copies carry is made well formed where it's
69
204
  * taken from the result: a lone UTF-16 surrogate becomes U+FFFD, as the
@@ -74,6 +209,40 @@ export function toolResultDataToHistoryString(data, maxChars, options) {
74
209
  * as before; a cut at a lone high half no longer steps back over it.
75
210
  */
76
211
  export function nonErrorResultText(result, maxChars, options) {
212
+ // The blocks go to the caller only once the text is made from them, so a
213
+ // result that shows the notice has no live copy of its own: the notice is
214
+ // both copies' text.
215
+ let blocks;
216
+ const onBlocks = options?.onBlocks;
217
+ try {
218
+ // The result's own fields are read inside the try too: the direct Agent
219
+ // API's result is its caller's own object, whose `data` can be a getter.
220
+ const text = nonErrorResultTextOrThrow(result, maxChars, {
221
+ ...options,
222
+ ...(onBlocks ? { onBlocks: (spans, made) => { blocks = [spans, made]; } } : {}),
223
+ });
224
+ if (onBlocks && blocks)
225
+ onBlocks(blocks[0], blocks[1]);
226
+ return text;
227
+ }
228
+ catch (error) {
229
+ // The model's lane has no failure answer that doesn't wedge the round, so
230
+ // whatever stops reading or showing a result, such as a getter that
231
+ // throws in a content array's block or a render deeper than the claims
232
+ // walk got (a script's routes call the serializer directly, and fail),
233
+ // is the notice, with a line naming the tool and the error, since it can
234
+ // also be a fault of this host's (Ada-1017, room-203 #201013).
235
+ console.error(`[tool-results] ${options?.toolName ? `tool '${options.toolName}'` : 'a tool'}: its result couldn't be read or shown, so the model reads that it was withheld: ${thrownLine(error)}`);
236
+ return maxChars ? truncateForHistory(UNCHECKED_RESULT_TEXT, maxChars) : UNCHECKED_RESULT_TEXT;
237
+ }
238
+ }
239
+ /**
240
+ * nonErrorResultText's text, throwing whatever stops reading or showing the
241
+ * result, such as a stopped claims walk (historyJson), for a caller that
242
+ * answers a stop itself: a script's routes, which fail naming the tool
243
+ * (Ada-1017, room-203 #200374), never the model lane's notice.
244
+ */
245
+ export function nonErrorResultTextOrThrow(result, maxChars, options) {
77
246
  const data = result.data;
78
247
  if (data === undefined || data === null) {
79
248
  const error = result.error;
@@ -82,7 +251,16 @@ export function nonErrorResultText(result, maxChars, options) {
82
251
  return maxChars ? truncateForHistory(text, maxChars) : text;
83
252
  }
84
253
  }
85
- return toolResultDataToHistoryString(data, maxChars, options);
254
+ return toolResultDataToHistoryString(data, maxChars, { ...options, dataIsContent: Boolean(result.dataIsContent) });
255
+ }
256
+ /** The first line of what was thrown, for a log line. */
257
+ function thrownLine(error) {
258
+ try {
259
+ return (error instanceof Error ? error.message : String(error)).split('\n')[0] || 'no reason given';
260
+ }
261
+ catch {
262
+ return 'a value that can\'t be read';
263
+ }
86
264
  }
87
265
  /** Bounded copy of an arbitrary string with the standard truncation notice,
88
266
  * cut at the cap where a tool result's stored copy is cut at that cap
@@ -98,53 +276,20 @@ export function truncateForHistory(str, maxChars) {
98
276
  return str.slice(0, toolResultCutAt(str, maxChars)) + '\n\n[truncated — original was ' + str.length + ' chars]';
99
277
  }
100
278
  /**
101
- * If `data` is an MCP content array whose every block has a shape we
102
- * recognize, return a history-safe string (images → placeholders). Otherwise
103
- * return `null` to defer to the fallback, historyJson, which shows a
104
- * reference anywhere in the data as its stub.
279
+ * A content array's history text (toolResultBlocks): each block's text, an
280
+ * image as its placeholder, joined by newlines. Null when `data` isn't a
281
+ * content array, for the fallback, historyJson.
105
282
  */
106
283
  function tryHistoryStringFromContentArray(data, options) {
107
- if (!Array.isArray(data))
284
+ const blocks = toolResultBlocks(data, options?.dataIsContent, options?.claims);
285
+ if (blocks === null)
108
286
  return null;
109
- const parts = [];
110
- for (const [blockIndex, raw] of data.entries()) {
111
- if (!raw || typeof raw !== 'object')
112
- return null;
113
- const b = raw;
114
- if (b.type === 'text') {
115
- // A text that isn't a string shows as the converters' placeholder,
116
- // rather than sending the whole array to the JSON fallback.
117
- // Read once: a direct Agent API result is unchecked.
118
- const text = b.text;
119
- parts.push({ kind: 'text', text: typeof text === 'string' ? text.toWellFormed() : NON_STRING_TEXT_PLACEHOLDER });
120
- }
121
- else if (isInlineContradiction(b)) {
122
- // RFC-005 vector 2: checked before the data branch so inline data
123
- // claiming bulk disposition is withheld, not rendered.
124
- parts.push({ kind: 'withheld', text: INLINE_WITHHELD_TEXT });
125
- }
126
- else if (b.type === 'image' && typeof b.data === 'string' && typeof b.mimeType === 'string') {
127
- // Decoded byte estimate from base64 length (3/4 ratio, rounded).
128
- const approxBytes = Math.floor(b.data.length * 3 / 4);
129
- const ref = options?.imageRef?.(blockIndex, { data: b.data, mimeType: b.mimeType }) ?? null;
130
- parts.push({ kind: 'image', text: formatToolImagePlaceholder(b.mimeType, formatSize(approxBytes), ref) });
131
- }
132
- else if (b.type === 'resource' || ((b.type === 'image' || b.type === 'audio') && typeof b.uri === 'string')) {
133
- // RFC-005 reference block: a bounded stub, never the raw block JSON
134
- // (which used to inline the URI — and the whole array — into history).
135
- // A stub that shows an image type occupies a slot in
136
- // save_recent_image's inventory (a failing one — bytes live behind
137
- // fetch_reference), so the index never slides past an image the
138
- // resident saw.
139
- const stub = referenceStubOrNull(b, 'from tool result');
140
- if (stub === null)
141
- return null;
142
- parts.push({ kind: 'reference', text: stub });
143
- }
144
- else {
145
- return null;
146
- }
147
- }
287
+ const parts = blocks.map((block, blockIndex) => {
288
+ if (block.kind !== 'image')
289
+ return block;
290
+ const ref = options?.imageRef?.(blockIndex, { data: block.data, mimeType: block.mimeType }) ?? null;
291
+ return { kind: 'image', text: storedText(block, ref) };
292
+ });
148
293
  if (options?.onBlocks) {
149
294
  const spans = [];
150
295
  let start = 0;
@@ -152,23 +297,47 @@ function tryHistoryStringFromContentArray(data, options) {
152
297
  spans.push({ kind: part.kind, start, end: start + part.text.length });
153
298
  start += part.text.length + 1; // the newline that joins them
154
299
  }
155
- options.onBlocks(spans);
300
+ options.onBlocks(spans, blocks);
156
301
  }
157
302
  return parts.map((part) => part.text).join('\n');
158
303
  }
159
304
  /**
160
305
  * Whether RFC-005 governs a value of a tool result's data, wherever it sits.
161
- * A reference is a `resource` with a uri, at its top level or under
162
- * `resource` (where MCP embeds a resource's uri), or an `image` or `audio`
163
- * given by uri; inline data is an `image` or `audio` whose data is a string.
164
- * A uri that is null, absent or empty counts as none, and data that isn't a
165
- * string isn't inline data, so a record that only shares one of those type
166
- * names is data. A uri of any other value, string or not, counts, so a
167
- * credential fails closed.
306
+ * A reference is a `resource` with a uri at its top level, or an `image` or
307
+ * `audio` given by uri; inline data is an `image` or `audio` whose data is a
308
+ * string, or MCP's embedded resource, a `resource` with a uri under
309
+ * `resource` beside its contents. There a uri that is null, absent or empty
310
+ * counts as none, except under `resource`, where a string, an empty one
311
+ * included, tells an embedded resource, as the classifier tells it. Data
312
+ * that isn't a string isn't inline data, so a record that only shares one of
313
+ * those type names is data. A uri of any other value, string or not, counts,
314
+ * so a credential fails closed. MCP's `resource_link` (RFC-005 §8.1) is
315
+ * governed whatever value its uri has, so one whose uri isn't usable is its
316
+ * marker; one that names no uri is data here, where as a block of a content
317
+ * array, which the classifier reads, it's the marker.
168
318
  */
169
319
  function governedByRfc005(b) {
170
- if (b.type === 'resource')
171
- return hasUri(b.uri) || (isPlainRecord(b.resource) && hasUri(b.resource.uri));
320
+ if (b.type === 'resource') {
321
+ // MCP's embedded resource is told by a string uri under `resource`, ''
322
+ // included, as classifyBlock tells it; a uri of any other value there
323
+ // counts too, failing closed.
324
+ return hasUri(b.uri) || (isPlainRecord(b.resource) && (typeof b.resource.uri === 'string' || hasUri(b.resource.uri)));
325
+ }
326
+ // A link that names a uri is governed whatever its value, so one without
327
+ // a usable uri (empty, null, not a string) is its marker here, as on the
328
+ // content lanes, never its fields. One that names none is data at depth,
329
+ // on the model's lanes and a script's, as main has every link at depth:
330
+ // its fields hold no uri RFC-005 governs, though a value in them, such as
331
+ // an untyped object's `uri`, is data as anywhere else. As a block of a
332
+ // content array on the model's line, the classifier reads it, and there
333
+ // it's the marker; a script's view of a module's array, which reads every
334
+ // block here, shows its fields, as module data reached a script on main
335
+ // (Ada-1017, room-203 #202138).
336
+ // That's the object the script lane makes of a link whose uri it leaves
337
+ // out (referenceBlockForScript), which a script's view, given it again,
338
+ // leaves as it is, as it leaves a reference's stub.
339
+ if (b.type === 'resource_link')
340
+ return b.uri !== undefined;
172
341
  if (b.type === 'image' || b.type === 'audio')
173
342
  return typeof b.data === 'string' || hasUri(b.uri);
174
343
  return false;
@@ -183,14 +352,36 @@ function isPlainRecord(value) {
183
352
  * A value of a tool result's data as history shows it, by RFC-005's rules,
184
353
  * wherever it sits in the data. A value RFC-005 governs (governedByRfc005) is
185
354
  * shown as the reference lanes show it: a reference as its stub, an invalid
186
- * one (such as a uri that isn't a string, or MCP's embedded resource, whose
187
- * uri isn't at its top level) as an unrecognized-block line, and inline data
188
- * that also claims a uri or a disposition as withheld, so no uri is ever
189
- * shown. Inline data that claims neither is itself, as is any other value:
190
- * a block of another type keeps its fields, a `resource_link`'s uri among
355
+ * one (such as a uri that isn't a string) as the marker saying why it isn't
356
+ * shown, MCP's embedded resource as the text its stored copy shows (its
357
+ * text, an image's placeholder or a marker), never its fields, which carry
358
+ * its uri, and inline data that also claims a uri or a disposition as
359
+ * withheld. So of the uris RFC-005 governs, the only one shown is a link's,
360
+ * in its line, where the host can't fetch the link and its uri can't carry a
361
+ * credential (shownLinkUri). Inline data that claims neither is itself, as is
362
+ * any other value: a block of another type keeps its fields, a url among
191
363
  * them, since RFC-005 governs references.
192
364
  */
193
- function shownInHistory(value) {
365
+ function shownInHistory(value, claims) {
366
+ return walkedInHistory(value, (b) => {
367
+ // MCP's embedded resource is the text its stored copy shows: its text,
368
+ // an image's placeholder or its marker. As JSON it would carry its uri.
369
+ // Made well formed, as a content array's texts are (toolResultBlocks),
370
+ // since at the root this text is the result's whole text, as it's sent.
371
+ if (classifyBlock(b).kind === 'embedded')
372
+ return storedText(toolResultBlock(b, claims ?? resultClaims([]))).toWellFormed();
373
+ return referenceStubOrNull(b, 'from tool result', undefined, { unfetchedLinkUri: true, claims });
374
+ });
375
+ }
376
+ /**
377
+ * The one walk of a tool result's data that showing it and reading its
378
+ * claims both take: a value RFC-005 governs is text, from `asText`, except
379
+ * inline data that claims no uri or disposition, which is itself, as is any
380
+ * other value, so serialization descends into the same values each time, so
381
+ * long as each value's toJSON and getters answer the same each time they're
382
+ * read. `asText` gives a governed value's text, or null where it's itself.
383
+ */
384
+ function walkedInHistory(value, asText) {
194
385
  if (!value || typeof value !== 'object' || Array.isArray(value))
195
386
  return value;
196
387
  const b = value;
@@ -203,22 +394,92 @@ function shownInHistory(value) {
203
394
  // carries none (hasUri).
204
395
  return hasUri(b.uri) || b.disposition !== undefined ? INLINE_WITHHELD_TEXT : value;
205
396
  }
206
- return referenceStubOrNull(b, 'from tool result') ?? value;
397
+ return asText(b) ?? value;
398
+ }
399
+ /**
400
+ * What a tool result's data claims of each uri its references name
401
+ * (ResultClaims), read by the walk serialization takes (walkedInHistory),
402
+ * through toJSON, the fields of inline data and every depth it shows: so
403
+ * every reference whose stub the result shows counts, and nothing it doesn't
404
+ * show does. A value the walk can't read or write, such as a cycle or a
405
+ * getter that throws, stops it, and then it's null, since what the rest of
406
+ * the data claims is unknown. It's a walk of its own before the one that
407
+ * shows the data, so a value's toJSON and getters run in both: one that
408
+ * answers differently the second time can claim what the shown text doesn't
409
+ * show, or show a reference the claims never read. Only data no check has
410
+ * read through JSON can do either: a module's result is parsed JSON from its
411
+ * dispatch, and an MCPL peer's blocks are parsed wire JSON, so this is the
412
+ * direct Agent API's, whose results a host gives unchecked.
413
+ */
414
+ function claimsInHistory(data) {
415
+ try {
416
+ return claimsInHistoryOrThrow(data);
417
+ }
418
+ catch {
419
+ // What the rest of the data claims is unknown, so a withheld result's
420
+ // kept fields keep nothing (keptWhenWithheld).
421
+ return null;
422
+ }
423
+ }
424
+ /** claimsInHistory's walk, which throws what stops it. */
425
+ function claimsInHistoryOrThrow(data) {
426
+ const testimonies = [];
427
+ // Inline data's never, which walkedInHistory withholds before it asks
428
+ // `read`, and an embedded resource's, which `read` notes (inlineNeverUri).
429
+ const nevers = [];
430
+ const noteInline = (value) => {
431
+ if (!isPlainRecord(value) || (value.type !== 'image' && value.type !== 'audio'))
432
+ return;
433
+ const never = inlineNeverUri(value);
434
+ if (never !== null)
435
+ nevers.push(never);
436
+ };
437
+ const read = (b) => {
438
+ const c = classifyBlock(b);
439
+ if (c.kind === 'reference')
440
+ testimonies.push(c.testimony);
441
+ const never = inlineNeverUri(b, c);
442
+ if (never !== null)
443
+ nevers.push(never);
444
+ // Text ('') wherever showing gives text (shownInHistory), an embedded
445
+ // resource included, so the walk stops where showing stops, never
446
+ // reading such a value's fields, and descends only where showing does.
447
+ // Inline data never reaches here: walkedInHistory decides it first.
448
+ return c.kind === 'text' ? null : '';
449
+ };
450
+ noteInline(data);
451
+ const root = walkedInHistory(data, read);
452
+ // Its text is thrown away, so a string is written as 0, which keeps a
453
+ // long one from being copied for nothing; the walk descends as before.
454
+ if (root === data || typeof root !== 'string') {
455
+ JSON.stringify(data, (_key, value) => {
456
+ if (value !== data)
457
+ noteInline(value);
458
+ const walked = walkedInHistory(value, read);
459
+ return typeof walked === 'string' ? 0 : walked;
460
+ });
461
+ }
462
+ return resultClaims(testimonies, nevers);
207
463
  }
208
464
  /**
209
465
  * A value of a tool result's data as a script reads it, by history's rules
210
466
  * (shownInHistory), as a JSON replacer applies them at every depth: what
211
467
  * RFC-005 governs is shown as the script lane shows it, a reference as its
212
- * stub's facts without its uri and an invalid one as the notice
213
- * (referenceBlockForScript), and inline data that also claims a uri of any
214
- * type or a disposition as the withheld notice, each as a text block. A
215
- * null, absent or empty uri claims none (hasUri), so inline data beside one
216
- * is itself. Any
217
- * other value is itself. What it gives is either a stub or a notice, which
218
- * RFC-005 doesn't govern, or the value itself, which it gives again, so
219
- * applying it twice gives what applying it once gave.
468
+ * stub's facts, a link's uri among them only where the model's line for it
469
+ * would show it, an invalid one as the notice, and MCP's embedded resource as
470
+ * the block its contents make (referenceBlockForScript), and inline data
471
+ * that also claims a uri of any type or a disposition as the withheld
472
+ * notice, each notice as a text block. A null, absent or empty uri claims
473
+ * none (hasUri), so inline data beside one is itself. Any other value is
474
+ * itself. `claims` are those of everything the script's view shows
475
+ * (scriptViewClaims), and `line` the model's line for the result, its claims
476
+ * and its records as it reads them (ScriptLine), which decide where a link's
477
+ * uri is shown. What it gives is either a stub, a notice or a text block,
478
+ * which RFC-005 doesn't govern, or inline data that claims nothing, or the
479
+ * value itself, each of which it gives again, so applying it twice gives
480
+ * what applying it once gave.
220
481
  */
221
- export function shownToScript(value) {
482
+ export function shownToScript(value, claims, line) {
222
483
  if (!value || typeof value !== 'object' || Array.isArray(value))
223
484
  return value;
224
485
  const b = value;
@@ -227,7 +488,28 @@ export function shownToScript(value) {
227
488
  if ((b.type === 'image' || b.type === 'audio') && typeof b.data === 'string') {
228
489
  return hasUri(b.uri) || b.disposition !== undefined ? { type: 'text', text: INLINE_WITHHELD_TEXT } : value;
229
490
  }
230
- return referenceBlockForScript(b);
491
+ return referenceBlockForScript(b, undefined, claims, line);
492
+ }
493
+ /**
494
+ * The claims (ResultClaims) of everything a script's view of a content array
495
+ * shows, nested fields included (Ada-1017, room-203 #200094), by the walk
496
+ * history's JSON takes (claimsInHistory): a script is shown the references
497
+ * nested in a block's fields, each as its stub, so a `never` among them
498
+ * withholds that uri from every block in the view. A link's uri is still
499
+ * shown only where the model's line shows it, by the claims the line reads
500
+ * (contentClaims, which reads a block and not its fields): reading more
501
+ * claims can loosen as well as tighten, since an expiry makes a link one the
502
+ * host can't fetch (referenceBlockForScript's `line`). An MCPL peer's
503
+ * top-level block the classifier rejects is
504
+ * its notice, its fields unshown, so they don't count (`fromServer`). It
505
+ * throws what stops the walk, such as a value too deep to convert or a
506
+ * getter that throws, and the script's view then isn't written: the
507
+ * treatment never runs without the claims it reads (Ada-1017, room-203
508
+ * #200250), whether or not its own walk, with other replacers and so other
509
+ * stack depths, would stop at the same value.
510
+ */
511
+ export function scriptViewClaims(content, fromServer) {
512
+ return claimsInHistoryOrThrow(fromServer ? content.filter((block) => classifyBlock(block).kind !== 'invalid') : content);
231
513
  }
232
514
  /**
233
515
  * A value nested in an MCPL peer's content block as a script reads it, as a
@@ -238,32 +520,70 @@ export function shownToScript(value) {
238
520
  * peer's block (referenceBlockForScript), its reference bound to the server
239
521
  * that sent it, and any other value is itself. A stub or a notice isn't
240
522
  * governed, so a block the top-level pass already treated is left as it is.
523
+ * `claims` are those of the script's whole view (scriptViewClaims).
241
524
  */
242
- export function shownToScriptFromServer(value, serverId) {
525
+ export function shownToScriptFromServer(value, serverId, claims, line) {
243
526
  if (!value || typeof value !== 'object' || Array.isArray(value))
244
527
  return value;
245
528
  const b = value;
246
- return governedByRfc005(b) ? referenceBlockForScript(b, serverId) : value;
529
+ return governedByRfc005(b) ? referenceBlockForScript(b, serverId, claims, line) : value;
247
530
  }
248
531
  /**
249
532
  * The text RFC-005 shows a value of a tool result's data as, when it shows it
250
533
  * as text rather than as its fields: a reference's stub, an invalid one's
251
- * unrecognized-block line, or inline data withheld. Null for any other value.
534
+ * marker, inline data withheld, or what an embedded resource's stored copy
535
+ * shows. Null for any other value.
252
536
  */
253
537
  export function rfc005TextOf(value) {
538
+ // A single value claims only what it says itself, which its stub reads.
254
539
  const shown = shownInHistory(value);
255
540
  return shown !== value && typeof shown === 'string' ? shown : null;
256
541
  }
542
+ /**
543
+ * What the model's lane shows for a tool result it couldn't read through,
544
+ * check for references and show (nonErrorResultText, the lane's one catch,
545
+ * which logs the tool and the error): the tool ran, and its result isn't
546
+ * shown, rather than shown with fewer claims, where a link could show a uri a
547
+ * reference in the result withholds, and rather than a throw, which would
548
+ * leave the round unstored and unsent. Where the walk of its claims stops
549
+ * (historyJson throws then), the claims walk meets data no JSON can hold,
550
+ * such as a cycle or a BigInt, or data too deep to write, before the render
551
+ * does, so such data shows this, where the render threw (Desmond-2579's
552
+ * run); so does a render that stops after the walk, or a getter that throws
553
+ * (Ada-1017, room-203 #200344, #201013). The round completes, as a withheld
554
+ * result's kept fields keep nothing then (keptWhenWithheld). A script never
555
+ * gets it: its routes call the serializer, or nonErrorResultTextOrThrow,
556
+ * directly, whose walk throws where it stops, and fail naming the tool
557
+ * (#200374). An error result's own error field is read as before, and so
558
+ * are the fields the tool-result guard keeps of a result it withholds.
559
+ */
560
+ export const UNCHECKED_RESULT_TEXT = "[Result not shown: the tool ran, but its result couldn't be read through, checked for references and shown, so this host withheld it.]";
257
561
  /**
258
562
  * JSON of a tool result's data with each value in it shown as history shows
259
- * it (shownInHistory), at any depth. Data that is itself shown as text, such
260
- * as a bare reference, is that text.
563
+ * it (shownInHistory), at any depth, by the claims `given` or else its own
564
+ * (claimsInHistoryOrThrow), throwing where that walk stops. Data that is
565
+ * itself shown as text, such as a bare reference, is that text.
261
566
  */
262
- function historyJson(data) {
263
- const root = shownInHistory(data);
567
+ function historyJson(data, given) {
568
+ // A walk that stopped can't say what the data claims, and showing it with
569
+ // no claims would let a link show a uri a reference withholds, should the
570
+ // render's own walk, with other replacers, get past the same value.
571
+ // Where the walk stops this throws: the model's lane answers with
572
+ // UNCHECKED_RESULT_TEXT and a log line (nonErrorResultText), a script's
573
+ // routes with a failure naming the tool.
574
+ const claims = given ?? claimsInHistoryOrThrow(data);
575
+ const root = shownInHistory(data, claims);
264
576
  if (root !== data && typeof root === 'string')
265
577
  return root;
266
- return JSON.stringify(data, (_key, value) => shownInHistory(value));
578
+ return JSON.stringify(data, (_key, value) => shownInHistory(value, claims));
579
+ }
580
+ /** A block's text in the stored copy: an image as its placeholder, with the
581
+ * retention ref given, and any other block as its text. */
582
+ function storedText(block, ref = null) {
583
+ if (block.kind !== 'image')
584
+ return block.text;
585
+ // Decoded byte estimate from base64 length (3/4 ratio, rounded).
586
+ return formatToolImagePlaceholder(block.mimeType, formatSize(Math.floor(block.data.length * 3 / 4)), ref);
267
587
  }
268
588
  function formatSize(bytes) {
269
589
  if (bytes < 1024)