@animalabs/agent-framework 0.22.2 → 0.23.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 (997) hide show
  1. package/README.md +108 -4
  2. package/dist/src/agent.d.ts +108 -17
  3. package/dist/src/agent.d.ts.map +1 -1
  4. package/dist/src/agent.js +207 -67
  5. package/dist/src/agent.js.map +1 -1
  6. package/dist/src/api/mcp-server-core.js +1 -1
  7. package/dist/src/api/mcp-server-core.js.map +1 -1
  8. package/dist/src/api/server.d.ts.map +1 -1
  9. package/dist/src/api/server.js +5 -1
  10. package/dist/src/api/server.js.map +1 -1
  11. package/dist/src/api/types.d.ts +1 -1
  12. package/dist/src/api/types.d.ts.map +1 -1
  13. package/dist/src/branch-names.d.ts +14 -0
  14. package/dist/src/branch-names.d.ts.map +1 -0
  15. package/dist/src/branch-names.js +22 -0
  16. package/dist/src/branch-names.js.map +1 -0
  17. package/dist/src/closest-names.d.ts +28 -0
  18. package/dist/src/closest-names.d.ts.map +1 -0
  19. package/dist/src/closest-names.js +98 -0
  20. package/dist/src/closest-names.js.map +1 -0
  21. package/dist/src/code-execution/index.d.ts +1 -1
  22. package/dist/src/code-execution/index.d.ts.map +1 -1
  23. package/dist/src/code-execution/index.js.map +1 -1
  24. package/dist/src/code-execution/py-runner.d.ts +50 -7
  25. package/dist/src/code-execution/py-runner.d.ts.map +1 -1
  26. package/dist/src/code-execution/py-runner.js +258 -90
  27. package/dist/src/code-execution/py-runner.js.map +1 -1
  28. package/dist/src/code-execution/runtime-py.d.ts +3 -2
  29. package/dist/src/code-execution/runtime-py.d.ts.map +1 -1
  30. package/dist/src/code-execution/runtime-py.js +94 -8
  31. package/dist/src/code-execution/runtime-py.js.map +1 -1
  32. package/dist/src/code-execution/tool-definition.d.ts +17 -1
  33. package/dist/src/code-execution/tool-definition.d.ts.map +1 -1
  34. package/dist/src/code-execution/tool-definition.js +72 -6
  35. package/dist/src/code-execution/tool-definition.js.map +1 -1
  36. package/dist/src/config-checks.d.ts +28 -0
  37. package/dist/src/config-checks.d.ts.map +1 -0
  38. package/dist/src/config-checks.js +56 -0
  39. package/dist/src/config-checks.js.map +1 -0
  40. package/dist/src/deadline-timer.d.ts +21 -0
  41. package/dist/src/deadline-timer.d.ts.map +1 -0
  42. package/dist/src/deadline-timer.js +43 -0
  43. package/dist/src/deadline-timer.js.map +1 -0
  44. package/dist/src/error-message.d.ts +30 -0
  45. package/dist/src/error-message.d.ts.map +1 -0
  46. package/dist/src/error-message.js +50 -0
  47. package/dist/src/error-message.js.map +1 -0
  48. package/dist/src/framework.d.ts +1529 -165
  49. package/dist/src/framework.d.ts.map +1 -1
  50. package/dist/src/framework.js +8264 -1939
  51. package/dist/src/framework.js.map +1 -1
  52. package/dist/src/gate/event-gate.d.ts +8 -0
  53. package/dist/src/gate/event-gate.d.ts.map +1 -1
  54. package/dist/src/gate/event-gate.js +36 -5
  55. package/dist/src/gate/event-gate.js.map +1 -1
  56. package/dist/src/held-writes.d.ts +16 -0
  57. package/dist/src/held-writes.d.ts.map +1 -0
  58. package/dist/src/held-writes.js +56 -0
  59. package/dist/src/held-writes.js.map +1 -0
  60. package/dist/src/index.d.ts +10 -4
  61. package/dist/src/index.d.ts.map +1 -1
  62. package/dist/src/index.js +11 -5
  63. package/dist/src/index.js.map +1 -1
  64. package/dist/src/mcpl/capability-grant.d.ts +4 -0
  65. package/dist/src/mcpl/capability-grant.d.ts.map +1 -1
  66. package/dist/src/mcpl/capability-grant.js +1 -1
  67. package/dist/src/mcpl/capability-grant.js.map +1 -1
  68. package/dist/src/mcpl/channel-registry.d.ts +233 -18
  69. package/dist/src/mcpl/channel-registry.d.ts.map +1 -1
  70. package/dist/src/mcpl/channel-registry.js +975 -386
  71. package/dist/src/mcpl/channel-registry.js.map +1 -1
  72. package/dist/src/mcpl/checkpoint-manager.d.ts +21 -1
  73. package/dist/src/mcpl/checkpoint-manager.d.ts.map +1 -1
  74. package/dist/src/mcpl/checkpoint-manager.js +91 -30
  75. package/dist/src/mcpl/checkpoint-manager.js.map +1 -1
  76. package/dist/src/mcpl/conversation-router.d.ts +19 -3
  77. package/dist/src/mcpl/conversation-router.d.ts.map +1 -1
  78. package/dist/src/mcpl/conversation-router.js +23 -8
  79. package/dist/src/mcpl/conversation-router.js.map +1 -1
  80. package/dist/src/mcpl/convert-block.d.ts +20 -0
  81. package/dist/src/mcpl/convert-block.d.ts.map +1 -0
  82. package/dist/src/mcpl/convert-block.js +88 -0
  83. package/dist/src/mcpl/convert-block.js.map +1 -0
  84. package/dist/src/mcpl/errors.d.ts +13 -0
  85. package/dist/src/mcpl/errors.d.ts.map +1 -1
  86. package/dist/src/mcpl/errors.js +28 -0
  87. package/dist/src/mcpl/errors.js.map +1 -1
  88. package/dist/src/mcpl/hook-orchestrator.d.ts +23 -0
  89. package/dist/src/mcpl/hook-orchestrator.d.ts.map +1 -1
  90. package/dist/src/mcpl/hook-orchestrator.js +83 -72
  91. package/dist/src/mcpl/hook-orchestrator.js.map +1 -1
  92. package/dist/src/mcpl/inbound-source.d.ts +114 -0
  93. package/dist/src/mcpl/inbound-source.d.ts.map +1 -0
  94. package/dist/src/mcpl/inbound-source.js +83 -0
  95. package/dist/src/mcpl/inbound-source.js.map +1 -0
  96. package/dist/src/mcpl/index.d.ts +7 -1
  97. package/dist/src/mcpl/index.d.ts.map +1 -1
  98. package/dist/src/mcpl/index.js +5 -1
  99. package/dist/src/mcpl/index.js.map +1 -1
  100. package/dist/src/mcpl/inference-router.d.ts +19 -13
  101. package/dist/src/mcpl/inference-router.d.ts.map +1 -1
  102. package/dist/src/mcpl/inference-router.js +155 -87
  103. package/dist/src/mcpl/inference-router.js.map +1 -1
  104. package/dist/src/mcpl/log-value.d.ts +7 -0
  105. package/dist/src/mcpl/log-value.d.ts.map +1 -1
  106. package/dist/src/mcpl/log-value.js +9 -0
  107. package/dist/src/mcpl/log-value.js.map +1 -1
  108. package/dist/src/mcpl/modern-connection.d.ts +227 -0
  109. package/dist/src/mcpl/modern-connection.d.ts.map +1 -0
  110. package/dist/src/mcpl/modern-connection.js +1088 -0
  111. package/dist/src/mcpl/modern-connection.js.map +1 -0
  112. package/dist/src/mcpl/prefix-rule.d.ts +18 -0
  113. package/dist/src/mcpl/prefix-rule.d.ts.map +1 -0
  114. package/dist/src/mcpl/prefix-rule.js +25 -0
  115. package/dist/src/mcpl/prefix-rule.js.map +1 -0
  116. package/dist/src/mcpl/protocol-family.d.ts +179 -0
  117. package/dist/src/mcpl/protocol-family.d.ts.map +1 -0
  118. package/dist/src/mcpl/protocol-family.js +697 -0
  119. package/dist/src/mcpl/protocol-family.js.map +1 -0
  120. package/dist/src/mcpl/push-coalescer.d.ts +58 -17
  121. package/dist/src/mcpl/push-coalescer.d.ts.map +1 -1
  122. package/dist/src/mcpl/push-coalescer.js +93 -32
  123. package/dist/src/mcpl/push-coalescer.js.map +1 -1
  124. package/dist/src/mcpl/push-handler.d.ts +16 -9
  125. package/dist/src/mcpl/push-handler.d.ts.map +1 -1
  126. package/dist/src/mcpl/push-handler.js +18 -83
  127. package/dist/src/mcpl/push-handler.js.map +1 -1
  128. package/dist/src/mcpl/reference-fetcher.d.ts +76 -13
  129. package/dist/src/mcpl/reference-fetcher.d.ts.map +1 -1
  130. package/dist/src/mcpl/reference-fetcher.js +275 -66
  131. package/dist/src/mcpl/reference-fetcher.js.map +1 -1
  132. package/dist/src/mcpl/references.d.ts +98 -18
  133. package/dist/src/mcpl/references.d.ts.map +1 -1
  134. package/dist/src/mcpl/references.js +170 -32
  135. package/dist/src/mcpl/references.js.map +1 -1
  136. package/dist/src/mcpl/server-connection.d.ts +406 -31
  137. package/dist/src/mcpl/server-connection.d.ts.map +1 -1
  138. package/dist/src/mcpl/server-connection.js +1331 -272
  139. package/dist/src/mcpl/server-connection.js.map +1 -1
  140. package/dist/src/mcpl/server-registry.d.ts +35 -6
  141. package/dist/src/mcpl/server-registry.d.ts.map +1 -1
  142. package/dist/src/mcpl/server-registry.js +63 -13
  143. package/dist/src/mcpl/server-registry.js.map +1 -1
  144. package/dist/src/mcpl/skip-reply-wake.d.ts +36 -0
  145. package/dist/src/mcpl/skip-reply-wake.d.ts.map +1 -0
  146. package/dist/src/mcpl/skip-reply-wake.js +55 -0
  147. package/dist/src/mcpl/skip-reply-wake.js.map +1 -0
  148. package/dist/src/mcpl/tool-result-normalize.d.ts +54 -0
  149. package/dist/src/mcpl/tool-result-normalize.d.ts.map +1 -0
  150. package/dist/src/mcpl/tool-result-normalize.js +232 -0
  151. package/dist/src/mcpl/tool-result-normalize.js.map +1 -0
  152. package/dist/src/mcpl/transport.d.ts +104 -12
  153. package/dist/src/mcpl/transport.d.ts.map +1 -1
  154. package/dist/src/mcpl/transport.js +315 -85
  155. package/dist/src/mcpl/transport.js.map +1 -1
  156. package/dist/src/mcpl/types.d.ts +76 -19
  157. package/dist/src/mcpl/types.d.ts.map +1 -1
  158. package/dist/src/mcpl/types.js.map +1 -1
  159. package/dist/src/mcpl/visible-content.d.ts +16 -0
  160. package/dist/src/mcpl/visible-content.d.ts.map +1 -1
  161. package/dist/src/mcpl/visible-content.js +26 -0
  162. package/dist/src/mcpl/visible-content.js.map +1 -1
  163. package/dist/src/module-registry.d.ts +34 -6
  164. package/dist/src/module-registry.d.ts.map +1 -1
  165. package/dist/src/module-registry.js +119 -31
  166. package/dist/src/module-registry.js.map +1 -1
  167. package/dist/src/modules/api/index.d.ts +1 -1
  168. package/dist/src/modules/api/index.d.ts.map +1 -1
  169. package/dist/src/modules/api/index.js +3 -2
  170. package/dist/src/modules/api/index.js.map +1 -1
  171. package/dist/src/modules/discord/discord-js-client.d.ts +43 -2
  172. package/dist/src/modules/discord/discord-js-client.d.ts.map +1 -1
  173. package/dist/src/modules/discord/discord-js-client.js +286 -67
  174. package/dist/src/modules/discord/discord-js-client.js.map +1 -1
  175. package/dist/src/modules/discord/index.d.ts +53 -5
  176. package/dist/src/modules/discord/index.d.ts.map +1 -1
  177. package/dist/src/modules/discord/index.js +712 -186
  178. package/dist/src/modules/discord/index.js.map +1 -1
  179. package/dist/src/modules/discord/refusal.d.ts +14 -0
  180. package/dist/src/modules/discord/refusal.d.ts.map +1 -0
  181. package/dist/src/modules/discord/refusal.js +14 -0
  182. package/dist/src/modules/discord/refusal.js.map +1 -0
  183. package/dist/src/modules/discord/types.d.ts +27 -4
  184. package/dist/src/modules/discord/types.d.ts.map +1 -1
  185. package/dist/src/modules/health/index.d.ts.map +1 -1
  186. package/dist/src/modules/health/index.js +8 -3
  187. package/dist/src/modules/health/index.js.map +1 -1
  188. package/dist/src/modules/history/index.d.ts +3 -1
  189. package/dist/src/modules/history/index.d.ts.map +1 -1
  190. package/dist/src/modules/history/index.js +359 -127
  191. package/dist/src/modules/history/index.js.map +1 -1
  192. package/dist/src/modules/history/semantic.d.ts +28 -1
  193. package/dist/src/modules/history/semantic.d.ts.map +1 -1
  194. package/dist/src/modules/history/semantic.js +177 -32
  195. package/dist/src/modules/history/semantic.js.map +1 -1
  196. package/dist/src/modules/index.d.ts +1 -1
  197. package/dist/src/modules/index.d.ts.map +1 -1
  198. package/dist/src/modules/index.js +1 -1
  199. package/dist/src/modules/index.js.map +1 -1
  200. package/dist/src/modules/unknown-tool.d.ts +10 -0
  201. package/dist/src/modules/unknown-tool.d.ts.map +1 -0
  202. package/dist/src/modules/unknown-tool.js +17 -0
  203. package/dist/src/modules/unknown-tool.js.map +1 -0
  204. package/dist/src/modules/workspace/disk-agreement.d.ts +57 -4
  205. package/dist/src/modules/workspace/disk-agreement.d.ts.map +1 -1
  206. package/dist/src/modules/workspace/disk-agreement.js +75 -7
  207. package/dist/src/modules/workspace/disk-agreement.js.map +1 -1
  208. package/dist/src/modules/workspace/effects.d.ts +66 -7
  209. package/dist/src/modules/workspace/effects.d.ts.map +1 -1
  210. package/dist/src/modules/workspace/effects.js +303 -46
  211. package/dist/src/modules/workspace/effects.js.map +1 -1
  212. package/dist/src/modules/workspace/index.d.ts +313 -45
  213. package/dist/src/modules/workspace/index.d.ts.map +1 -1
  214. package/dist/src/modules/workspace/index.js +1404 -304
  215. package/dist/src/modules/workspace/index.js.map +1 -1
  216. package/dist/src/modules/workspace/observe.d.ts +315 -16
  217. package/dist/src/modules/workspace/observe.d.ts.map +1 -1
  218. package/dist/src/modules/workspace/observe.js +727 -37
  219. package/dist/src/modules/workspace/observe.js.map +1 -1
  220. package/dist/src/modules/workspace/reconcile.d.ts +58 -7
  221. package/dist/src/modules/workspace/reconcile.d.ts.map +1 -1
  222. package/dist/src/modules/workspace/reconcile.js +521 -56
  223. package/dist/src/modules/workspace/reconcile.js.map +1 -1
  224. package/dist/src/modules/workspace/types.d.ts +28 -5
  225. package/dist/src/modules/workspace/types.d.ts.map +1 -1
  226. package/dist/src/modules/workspace/types.js.map +1 -1
  227. package/dist/src/modules/workspace/watcher.d.ts +49 -2
  228. package/dist/src/modules/workspace/watcher.d.ts.map +1 -1
  229. package/dist/src/modules/workspace/watcher.js +158 -29
  230. package/dist/src/modules/workspace/watcher.js.map +1 -1
  231. package/dist/src/prose-drafts.d.ts +240 -0
  232. package/dist/src/prose-drafts.d.ts.map +1 -0
  233. package/dist/src/prose-drafts.js +362 -0
  234. package/dist/src/prose-drafts.js.map +1 -0
  235. package/dist/src/prose-segments.d.ts +21 -2
  236. package/dist/src/prose-segments.d.ts.map +1 -1
  237. package/dist/src/prose-segments.js +46 -9
  238. package/dist/src/prose-segments.js.map +1 -1
  239. package/dist/src/recovery/offline-branch.d.ts +1 -0
  240. package/dist/src/recovery/offline-branch.d.ts.map +1 -1
  241. package/dist/src/recovery/offline-branch.js +3 -1
  242. package/dist/src/recovery/offline-branch.js.map +1 -1
  243. package/dist/src/safe-slice.d.ts +5 -1
  244. package/dist/src/safe-slice.d.ts.map +1 -1
  245. package/dist/src/safe-slice.js +9 -1
  246. package/dist/src/safe-slice.js.map +1 -1
  247. package/dist/src/silent-heartbeat.d.ts +112 -39
  248. package/dist/src/silent-heartbeat.d.ts.map +1 -1
  249. package/dist/src/silent-heartbeat.js +100 -29
  250. package/dist/src/silent-heartbeat.js.map +1 -1
  251. package/dist/src/tool-args.d.ts +40 -0
  252. package/dist/src/tool-args.d.ts.map +1 -0
  253. package/dist/src/tool-args.js +207 -0
  254. package/dist/src/tool-args.js.map +1 -0
  255. package/dist/src/tool-image-ledger.d.ts +87 -19
  256. package/dist/src/tool-image-ledger.d.ts.map +1 -1
  257. package/dist/src/tool-image-ledger.js +135 -32
  258. package/dist/src/tool-image-ledger.js.map +1 -1
  259. package/dist/src/tool-presentation.d.ts +9 -0
  260. package/dist/src/tool-presentation.d.ts.map +1 -1
  261. package/dist/src/tool-presentation.js +266 -62
  262. package/dist/src/tool-presentation.js.map +1 -1
  263. package/dist/src/tool-result-guard.d.ts.map +1 -1
  264. package/dist/src/tool-result-guard.js +7 -10
  265. package/dist/src/tool-result-guard.js.map +1 -1
  266. package/dist/src/tool-result-history.d.ts +32 -3
  267. package/dist/src/tool-result-history.d.ts.map +1 -1
  268. package/dist/src/tool-result-history.js +106 -19
  269. package/dist/src/tool-result-history.js.map +1 -1
  270. package/dist/src/tune-out/coordinator.d.ts +25 -1
  271. package/dist/src/tune-out/coordinator.d.ts.map +1 -1
  272. package/dist/src/tune-out/coordinator.js +260 -65
  273. package/dist/src/tune-out/coordinator.js.map +1 -1
  274. package/dist/src/types/agent.d.ts +44 -22
  275. package/dist/src/types/agent.d.ts.map +1 -1
  276. package/dist/src/types/events.d.ts +25 -0
  277. package/dist/src/types/events.d.ts.map +1 -1
  278. package/dist/src/types/framework.d.ts +38 -7
  279. package/dist/src/types/framework.d.ts.map +1 -1
  280. package/dist/src/types/inference-log.d.ts +1 -1
  281. package/dist/src/types/inference-log.d.ts.map +1 -1
  282. package/dist/src/types/module.d.ts +77 -14
  283. package/dist/src/types/module.d.ts.map +1 -1
  284. package/dist/src/types/trace.d.ts +97 -8
  285. package/dist/src/types/trace.d.ts.map +1 -1
  286. package/dist/src/usage/usage-tracker.d.ts +5 -0
  287. package/dist/src/usage/usage-tracker.d.ts.map +1 -1
  288. package/dist/src/usage/usage-tracker.js +35 -8
  289. package/dist/src/usage/usage-tracker.js.map +1 -1
  290. package/package.json +5 -4
  291. package/src/agent.ts +257 -72
  292. package/src/api/mcp-server-core.ts +1 -1
  293. package/src/api/server.ts +4 -1
  294. package/src/api/types.ts +1 -1
  295. package/src/branch-names.ts +19 -0
  296. package/src/closest-names.ts +94 -0
  297. package/src/code-execution/index.ts +1 -0
  298. package/src/code-execution/py-runner.ts +304 -93
  299. package/src/code-execution/runtime-py.ts +94 -8
  300. package/src/code-execution/tool-definition.ts +85 -7
  301. package/src/config-checks.ts +50 -0
  302. package/src/deadline-timer.ts +44 -0
  303. package/src/error-message.ts +48 -0
  304. package/src/framework.ts +8625 -1984
  305. package/src/gate/event-gate.ts +38 -5
  306. package/src/held-writes.ts +59 -0
  307. package/src/index.ts +29 -5
  308. package/src/mcpl/capability-grant.ts +1 -1
  309. package/src/mcpl/channel-registry.ts +1095 -411
  310. package/src/mcpl/checkpoint-manager.ts +104 -29
  311. package/src/mcpl/conversation-router.ts +28 -8
  312. package/src/mcpl/convert-block.ts +105 -0
  313. package/src/mcpl/errors.ts +28 -0
  314. package/src/mcpl/hook-orchestrator.ts +84 -77
  315. package/src/mcpl/inbound-source.ts +156 -0
  316. package/src/mcpl/index.ts +23 -1
  317. package/src/mcpl/inference-router.ts +179 -103
  318. package/src/mcpl/log-value.ts +10 -0
  319. package/src/mcpl/modern-connection.ts +1182 -0
  320. package/src/mcpl/prefix-rule.ts +32 -0
  321. package/src/mcpl/protocol-family.ts +691 -0
  322. package/src/mcpl/push-coalescer.ts +110 -36
  323. package/src/mcpl/push-handler.ts +20 -90
  324. package/src/mcpl/reference-fetcher.ts +318 -67
  325. package/src/mcpl/references.ts +183 -34
  326. package/src/mcpl/server-connection.ts +1404 -280
  327. package/src/mcpl/server-registry.ts +63 -14
  328. package/src/mcpl/skip-reply-wake.ts +73 -0
  329. package/src/mcpl/tool-result-normalize.ts +294 -0
  330. package/src/mcpl/transport.ts +320 -76
  331. package/src/mcpl/types.ts +77 -19
  332. package/src/mcpl/visible-content.ts +28 -0
  333. package/src/module-registry.ts +126 -36
  334. package/src/modules/api/index.ts +3 -2
  335. package/src/modules/discord/discord-js-client.ts +306 -74
  336. package/src/modules/discord/index.ts +737 -201
  337. package/src/modules/discord/refusal.ts +13 -0
  338. package/src/modules/discord/types.ts +27 -6
  339. package/src/modules/health/index.ts +8 -3
  340. package/src/modules/history/index.ts +410 -120
  341. package/src/modules/history/semantic.ts +189 -32
  342. package/src/modules/index.ts +1 -1
  343. package/src/modules/unknown-tool.ts +16 -0
  344. package/src/modules/workspace/disk-agreement.ts +97 -8
  345. package/src/modules/workspace/effects.ts +291 -36
  346. package/src/modules/workspace/index.ts +1469 -321
  347. package/src/modules/workspace/observe.ts +784 -41
  348. package/src/modules/workspace/reconcile.ts +619 -64
  349. package/src/modules/workspace/types.ts +28 -5
  350. package/src/modules/workspace/watcher.ts +154 -30
  351. package/src/prose-drafts.ts +481 -0
  352. package/src/prose-segments.ts +51 -8
  353. package/src/recovery/offline-branch.ts +4 -1
  354. package/src/safe-slice.ts +9 -1
  355. package/src/silent-heartbeat.ts +184 -59
  356. package/src/tool-args.ts +213 -0
  357. package/src/tool-image-ledger.ts +153 -34
  358. package/src/tool-presentation.ts +233 -42
  359. package/src/tool-result-guard.ts +7 -8
  360. package/src/tool-result-history.ts +124 -20
  361. package/src/tune-out/coordinator.ts +259 -65
  362. package/src/types/agent.ts +44 -24
  363. package/src/types/events.ts +25 -0
  364. package/src/types/framework.ts +38 -7
  365. package/src/types/inference-log.ts +1 -1
  366. package/src/types/module.ts +79 -14
  367. package/src/types/trace.ts +103 -8
  368. package/src/usage/usage-tracker.ts +32 -8
  369. package/dist/test/activation-request-sanitize.test.d.ts +0 -2
  370. package/dist/test/activation-request-sanitize.test.d.ts.map +0 -1
  371. package/dist/test/activation-request-sanitize.test.js +0 -89
  372. package/dist/test/activation-request-sanitize.test.js.map +0 -1
  373. package/dist/test/agent-cache-ttl.test.d.ts +0 -2
  374. package/dist/test/agent-cache-ttl.test.d.ts.map +0 -1
  375. package/dist/test/agent-cache-ttl.test.js +0 -41
  376. package/dist/test/agent-cache-ttl.test.js.map +0 -1
  377. package/dist/test/agent-runtime-settings.test.d.ts +0 -2
  378. package/dist/test/agent-runtime-settings.test.d.ts.map +0 -1
  379. package/dist/test/agent-runtime-settings.test.js +0 -1598
  380. package/dist/test/agent-runtime-settings.test.js.map +0 -1
  381. package/dist/test/api-admin-gate.test.d.ts +0 -2
  382. package/dist/test/api-admin-gate.test.d.ts.map +0 -1
  383. package/dist/test/api-admin-gate.test.js +0 -116
  384. package/dist/test/api-admin-gate.test.js.map +0 -1
  385. package/dist/test/assistant-persist-refusal.test.d.ts +0 -2
  386. package/dist/test/assistant-persist-refusal.test.d.ts.map +0 -1
  387. package/dist/test/assistant-persist-refusal.test.js +0 -63
  388. package/dist/test/assistant-persist-refusal.test.js.map +0 -1
  389. package/dist/test/batched-wake-locus.test.d.ts +0 -2
  390. package/dist/test/batched-wake-locus.test.d.ts.map +0 -1
  391. package/dist/test/batched-wake-locus.test.js +0 -166
  392. package/dist/test/batched-wake-locus.test.js.map +0 -1
  393. package/dist/test/busy-agent-scheduler.test.d.ts +0 -2
  394. package/dist/test/busy-agent-scheduler.test.d.ts.map +0 -1
  395. package/dist/test/busy-agent-scheduler.test.js +0 -92
  396. package/dist/test/busy-agent-scheduler.test.js.map +0 -1
  397. package/dist/test/channel-incoming-targeting.test.d.ts +0 -2
  398. package/dist/test/channel-incoming-targeting.test.d.ts.map +0 -1
  399. package/dist/test/channel-incoming-targeting.test.js +0 -81
  400. package/dist/test/channel-incoming-targeting.test.js.map +0 -1
  401. package/dist/test/channel-label-history.test.d.ts +0 -2
  402. package/dist/test/channel-label-history.test.d.ts.map +0 -1
  403. package/dist/test/channel-label-history.test.js +0 -424
  404. package/dist/test/channel-label-history.test.js.map +0 -1
  405. package/dist/test/channel-lifecycle-provenance.test.d.ts +0 -2
  406. package/dist/test/channel-lifecycle-provenance.test.d.ts.map +0 -1
  407. package/dist/test/channel-lifecycle-provenance.test.js +0 -47
  408. package/dist/test/channel-lifecycle-provenance.test.js.map +0 -1
  409. package/dist/test/channel-registry-routing.test.d.ts +0 -2
  410. package/dist/test/channel-registry-routing.test.d.ts.map +0 -1
  411. package/dist/test/channel-registry-routing.test.js +0 -446
  412. package/dist/test/channel-registry-routing.test.js.map +0 -1
  413. package/dist/test/channel-registry-typing.test.d.ts +0 -2
  414. package/dist/test/channel-registry-typing.test.d.ts.map +0 -1
  415. package/dist/test/channel-registry-typing.test.js +0 -91
  416. package/dist/test/channel-registry-typing.test.js.map +0 -1
  417. package/dist/test/channels-changed-respond-first.test.d.ts +0 -2
  418. package/dist/test/channels-changed-respond-first.test.d.ts.map +0 -1
  419. package/dist/test/channels-changed-respond-first.test.js +0 -74
  420. package/dist/test/channels-changed-respond-first.test.js.map +0 -1
  421. package/dist/test/checkpoint-attribution.test.d.ts +0 -2
  422. package/dist/test/checkpoint-attribution.test.d.ts.map +0 -1
  423. package/dist/test/checkpoint-attribution.test.js +0 -200
  424. package/dist/test/checkpoint-attribution.test.js.map +0 -1
  425. package/dist/test/code-execution.test.d.ts +0 -11
  426. package/dist/test/code-execution.test.d.ts.map +0 -1
  427. package/dist/test/code-execution.test.js +0 -1272
  428. package/dist/test/code-execution.test.js.map +0 -1
  429. package/dist/test/component-defaults.test.d.ts +0 -2
  430. package/dist/test/component-defaults.test.d.ts.map +0 -1
  431. package/dist/test/component-defaults.test.js +0 -54
  432. package/dist/test/component-defaults.test.js.map +0 -1
  433. package/dist/test/conversation-fork-injection-scoping.test.d.ts +0 -2
  434. package/dist/test/conversation-fork-injection-scoping.test.d.ts.map +0 -1
  435. package/dist/test/conversation-fork-injection-scoping.test.js +0 -200
  436. package/dist/test/conversation-fork-injection-scoping.test.js.map +0 -1
  437. package/dist/test/conversation-router.test.d.ts +0 -2
  438. package/dist/test/conversation-router.test.d.ts.map +0 -1
  439. package/dist/test/conversation-router.test.js +0 -174
  440. package/dist/test/conversation-router.test.js.map +0 -1
  441. package/dist/test/conversation-routing.test.d.ts +0 -7
  442. package/dist/test/conversation-routing.test.d.ts.map +0 -1
  443. package/dist/test/conversation-routing.test.js +0 -621
  444. package/dist/test/conversation-routing.test.js.map +0 -1
  445. package/dist/test/deferred-wake-integrity.test.d.ts +0 -29
  446. package/dist/test/deferred-wake-integrity.test.d.ts.map +0 -1
  447. package/dist/test/deferred-wake-integrity.test.js +0 -320
  448. package/dist/test/deferred-wake-integrity.test.js.map +0 -1
  449. package/dist/test/discord-awareness-outbox.test.d.ts +0 -2
  450. package/dist/test/discord-awareness-outbox.test.d.ts.map +0 -1
  451. package/dist/test/discord-awareness-outbox.test.js +0 -371
  452. package/dist/test/discord-awareness-outbox.test.js.map +0 -1
  453. package/dist/test/discord-history-delete-window.test.d.ts +0 -2
  454. package/dist/test/discord-history-delete-window.test.d.ts.map +0 -1
  455. package/dist/test/discord-history-delete-window.test.js +0 -81
  456. package/dist/test/discord-history-delete-window.test.js.map +0 -1
  457. package/dist/test/discord-history-sync.test.d.ts +0 -2
  458. package/dist/test/discord-history-sync.test.d.ts.map +0 -1
  459. package/dist/test/discord-history-sync.test.js +0 -56
  460. package/dist/test/discord-history-sync.test.js.map +0 -1
  461. package/dist/test/discord-typing-keepalive.test.d.ts +0 -2
  462. package/dist/test/discord-typing-keepalive.test.d.ts.map +0 -1
  463. package/dist/test/discord-typing-keepalive.test.js +0 -77
  464. package/dist/test/discord-typing-keepalive.test.js.map +0 -1
  465. package/dist/test/ephemeral-settle.test.d.ts +0 -2
  466. package/dist/test/ephemeral-settle.test.d.ts.map +0 -1
  467. package/dist/test/ephemeral-settle.test.js +0 -354
  468. package/dist/test/ephemeral-settle.test.js.map +0 -1
  469. package/dist/test/event-gate-passthrough.test.d.ts +0 -15
  470. package/dist/test/event-gate-passthrough.test.d.ts.map +0 -1
  471. package/dist/test/event-gate-passthrough.test.js +0 -289
  472. package/dist/test/event-gate-passthrough.test.js.map +0 -1
  473. package/dist/test/event-gate-policy-mutation.test.d.ts +0 -2
  474. package/dist/test/event-gate-policy-mutation.test.d.ts.map +0 -1
  475. package/dist/test/event-gate-policy-mutation.test.js +0 -137
  476. package/dist/test/event-gate-policy-mutation.test.js.map +0 -1
  477. package/dist/test/event-gate-rate-limit.test.d.ts +0 -2
  478. package/dist/test/event-gate-rate-limit.test.d.ts.map +0 -1
  479. package/dist/test/event-gate-rate-limit.test.js +0 -627
  480. package/dist/test/event-gate-rate-limit.test.js.map +0 -1
  481. package/dist/test/event-gate-script.test.d.ts +0 -2
  482. package/dist/test/event-gate-script.test.d.ts.map +0 -1
  483. package/dist/test/event-gate-script.test.js +0 -95
  484. package/dist/test/event-gate-script.test.js.map +0 -1
  485. package/dist/test/event-gate-selfwake.test.d.ts +0 -15
  486. package/dist/test/event-gate-selfwake.test.d.ts.map +0 -1
  487. package/dist/test/event-gate-selfwake.test.js +0 -125
  488. package/dist/test/event-gate-selfwake.test.js.map +0 -1
  489. package/dist/test/event-gate-tags.test.d.ts +0 -2
  490. package/dist/test/event-gate-tags.test.d.ts.map +0 -1
  491. package/dist/test/event-gate-tags.test.js +0 -76
  492. package/dist/test/event-gate-tags.test.js.map +0 -1
  493. package/dist/test/event-gate.test.d.ts +0 -2
  494. package/dist/test/event-gate.test.d.ts.map +0 -1
  495. package/dist/test/event-gate.test.js +0 -919
  496. package/dist/test/event-gate.test.js.map +0 -1
  497. package/dist/test/explicit-prose-routing.test.d.ts +0 -11
  498. package/dist/test/explicit-prose-routing.test.d.ts.map +0 -1
  499. package/dist/test/explicit-prose-routing.test.js +0 -1715
  500. package/dist/test/explicit-prose-routing.test.js.map +0 -1
  501. package/dist/test/framework.test.d.ts +0 -5
  502. package/dist/test/framework.test.d.ts.map +0 -1
  503. package/dist/test/framework.test.js +0 -1048
  504. package/dist/test/framework.test.js.map +0 -1
  505. package/dist/test/gather-context-timeout.test.d.ts +0 -2
  506. package/dist/test/gather-context-timeout.test.d.ts.map +0 -1
  507. package/dist/test/gather-context-timeout.test.js +0 -120
  508. package/dist/test/gather-context-timeout.test.js.map +0 -1
  509. package/dist/test/health-snapshot.test.d.ts +0 -2
  510. package/dist/test/health-snapshot.test.d.ts.map +0 -1
  511. package/dist/test/health-snapshot.test.js +0 -74
  512. package/dist/test/health-snapshot.test.js.map +0 -1
  513. package/dist/test/helpers/coalescing-fixture.d.ts +0 -74
  514. package/dist/test/helpers/coalescing-fixture.d.ts.map +0 -1
  515. package/dist/test/helpers/coalescing-fixture.js +0 -152
  516. package/dist/test/helpers/coalescing-fixture.js.map +0 -1
  517. package/dist/test/helpers/coalescing-kill-child.d.ts +0 -2
  518. package/dist/test/helpers/coalescing-kill-child.d.ts.map +0 -1
  519. package/dist/test/helpers/coalescing-kill-child.js +0 -24
  520. package/dist/test/helpers/coalescing-kill-child.js.map +0 -1
  521. package/dist/test/helpers/mock-membrane.d.ts +0 -53
  522. package/dist/test/helpers/mock-membrane.d.ts.map +0 -1
  523. package/dist/test/helpers/mock-membrane.js +0 -168
  524. package/dist/test/helpers/mock-membrane.js.map +0 -1
  525. package/dist/test/helpers/pytc-stop-rows.d.ts +0 -11
  526. package/dist/test/helpers/pytc-stop-rows.d.ts.map +0 -1
  527. package/dist/test/helpers/pytc-stop-rows.js +0 -143
  528. package/dist/test/helpers/pytc-stop-rows.js.map +0 -1
  529. package/dist/test/helpers/quiesce-crash-child.d.ts +0 -2
  530. package/dist/test/helpers/quiesce-crash-child.d.ts.map +0 -1
  531. package/dist/test/helpers/quiesce-crash-child.js +0 -156
  532. package/dist/test/helpers/quiesce-crash-child.js.map +0 -1
  533. package/dist/test/hide-then-turn.test.d.ts +0 -2
  534. package/dist/test/hide-then-turn.test.d.ts.map +0 -1
  535. package/dist/test/hide-then-turn.test.js +0 -101
  536. package/dist/test/hide-then-turn.test.js.map +0 -1
  537. package/dist/test/history-module-overview.test.d.ts +0 -2
  538. package/dist/test/history-module-overview.test.d.ts.map +0 -1
  539. package/dist/test/history-module-overview.test.js +0 -711
  540. package/dist/test/history-module-overview.test.js.map +0 -1
  541. package/dist/test/history-module-realstore.test.d.ts +0 -2
  542. package/dist/test/history-module-realstore.test.d.ts.map +0 -1
  543. package/dist/test/history-module-realstore.test.js +0 -363
  544. package/dist/test/history-module-realstore.test.js.map +0 -1
  545. package/dist/test/history-module-semantic.test.d.ts +0 -2
  546. package/dist/test/history-module-semantic.test.d.ts.map +0 -1
  547. package/dist/test/history-module-semantic.test.js +0 -213
  548. package/dist/test/history-module-semantic.test.js.map +0 -1
  549. package/dist/test/history-module-ux.test.d.ts +0 -2
  550. package/dist/test/history-module-ux.test.d.ts.map +0 -1
  551. package/dist/test/history-module-ux.test.js +0 -304
  552. package/dist/test/history-module-ux.test.js.map +0 -1
  553. package/dist/test/history-module.test.d.ts +0 -2
  554. package/dist/test/history-module.test.d.ts.map +0 -1
  555. package/dist/test/history-module.test.js +0 -619
  556. package/dist/test/history-module.test.js.map +0 -1
  557. package/dist/test/history-semantic-review-findings.test.d.ts +0 -2
  558. package/dist/test/history-semantic-review-findings.test.d.ts.map +0 -1
  559. package/dist/test/history-semantic-review-findings.test.js +0 -338
  560. package/dist/test/history-semantic-review-findings.test.js.map +0 -1
  561. package/dist/test/history-semantic-sync-converges.test.d.ts +0 -2
  562. package/dist/test/history-semantic-sync-converges.test.d.ts.map +0 -1
  563. package/dist/test/history-semantic-sync-converges.test.js +0 -109
  564. package/dist/test/history-semantic-sync-converges.test.js.map +0 -1
  565. package/dist/test/host-quiesce.test.d.ts +0 -2
  566. package/dist/test/host-quiesce.test.d.ts.map +0 -1
  567. package/dist/test/host-quiesce.test.js +0 -988
  568. package/dist/test/host-quiesce.test.js.map +0 -1
  569. package/dist/test/inference-exhaustion-rewind.test.d.ts +0 -2
  570. package/dist/test/inference-exhaustion-rewind.test.d.ts.map +0 -1
  571. package/dist/test/inference-exhaustion-rewind.test.js +0 -387
  572. package/dist/test/inference-exhaustion-rewind.test.js.map +0 -1
  573. package/dist/test/inference-failure-bounds.test.d.ts +0 -2
  574. package/dist/test/inference-failure-bounds.test.d.ts.map +0 -1
  575. package/dist/test/inference-failure-bounds.test.js +0 -1202
  576. package/dist/test/inference-failure-bounds.test.js.map +0 -1
  577. package/dist/test/inference-failure-observability.test.d.ts +0 -2
  578. package/dist/test/inference-failure-observability.test.d.ts.map +0 -1
  579. package/dist/test/inference-failure-observability.test.js +0 -84
  580. package/dist/test/inference-failure-observability.test.js.map +0 -1
  581. package/dist/test/journal-tool.test.d.ts +0 -2
  582. package/dist/test/journal-tool.test.d.ts.map +0 -1
  583. package/dist/test/journal-tool.test.js +0 -33
  584. package/dist/test/journal-tool.test.js.map +0 -1
  585. package/dist/test/kv-unified-open-flight-retry.test.d.ts +0 -2
  586. package/dist/test/kv-unified-open-flight-retry.test.d.ts.map +0 -1
  587. package/dist/test/kv-unified-open-flight-retry.test.js +0 -189
  588. package/dist/test/kv-unified-open-flight-retry.test.js.map +0 -1
  589. package/dist/test/kv-unified-wiring.test.d.ts +0 -2
  590. package/dist/test/kv-unified-wiring.test.d.ts.map +0 -1
  591. package/dist/test/kv-unified-wiring.test.js +0 -95
  592. package/dist/test/kv-unified-wiring.test.js.map +0 -1
  593. package/dist/test/late-events-after-stop.test.d.ts +0 -2
  594. package/dist/test/late-events-after-stop.test.d.ts.map +0 -1
  595. package/dist/test/late-events-after-stop.test.js +0 -122
  596. package/dist/test/late-events-after-stop.test.js.map +0 -1
  597. package/dist/test/liveness-watchdog.test.d.ts +0 -2
  598. package/dist/test/liveness-watchdog.test.d.ts.map +0 -1
  599. package/dist/test/liveness-watchdog.test.js +0 -94
  600. package/dist/test/liveness-watchdog.test.js.map +0 -1
  601. package/dist/test/machine-close-provenance.test.d.ts +0 -2
  602. package/dist/test/machine-close-provenance.test.d.ts.map +0 -1
  603. package/dist/test/machine-close-provenance.test.js +0 -116
  604. package/dist/test/machine-close-provenance.test.js.map +0 -1
  605. package/dist/test/maintenance-timer.test.d.ts +0 -2
  606. package/dist/test/maintenance-timer.test.d.ts.map +0 -1
  607. package/dist/test/maintenance-timer.test.js +0 -1827
  608. package/dist/test/maintenance-timer.test.js.map +0 -1
  609. package/dist/test/mcp-server-bin.test.d.ts +0 -2
  610. package/dist/test/mcp-server-bin.test.d.ts.map +0 -1
  611. package/dist/test/mcp-server-bin.test.js +0 -142
  612. package/dist/test/mcp-server-bin.test.js.map +0 -1
  613. package/dist/test/mcpl-access-provider.test.d.ts +0 -2
  614. package/dist/test/mcpl-access-provider.test.d.ts.map +0 -1
  615. package/dist/test/mcpl-access-provider.test.js +0 -20
  616. package/dist/test/mcpl-access-provider.test.js.map +0 -1
  617. package/dist/test/mcpl-awareness-barrier.test.d.ts +0 -2
  618. package/dist/test/mcpl-awareness-barrier.test.d.ts.map +0 -1
  619. package/dist/test/mcpl-awareness-barrier.test.js +0 -769
  620. package/dist/test/mcpl-awareness-barrier.test.js.map +0 -1
  621. package/dist/test/mcpl-capability-grant.test.d.ts +0 -10
  622. package/dist/test/mcpl-capability-grant.test.d.ts.map +0 -1
  623. package/dist/test/mcpl-capability-grant.test.js +0 -111
  624. package/dist/test/mcpl-capability-grant.test.js.map +0 -1
  625. package/dist/test/mcpl-capability-scoping.test.d.ts +0 -2
  626. package/dist/test/mcpl-capability-scoping.test.d.ts.map +0 -1
  627. package/dist/test/mcpl-capability-scoping.test.js +0 -266
  628. package/dist/test/mcpl-capability-scoping.test.js.map +0 -1
  629. package/dist/test/mcpl-child-env.test.d.ts +0 -2
  630. package/dist/test/mcpl-child-env.test.d.ts.map +0 -1
  631. package/dist/test/mcpl-child-env.test.js +0 -158
  632. package/dist/test/mcpl-child-env.test.js.map +0 -1
  633. package/dist/test/mcpl-coalesced-content-parity.test.d.ts +0 -2
  634. package/dist/test/mcpl-coalesced-content-parity.test.d.ts.map +0 -1
  635. package/dist/test/mcpl-coalesced-content-parity.test.js +0 -532
  636. package/dist/test/mcpl-coalesced-content-parity.test.js.map +0 -1
  637. package/dist/test/mcpl-coalescing.test.d.ts +0 -2
  638. package/dist/test/mcpl-coalescing.test.d.ts.map +0 -1
  639. package/dist/test/mcpl-coalescing.test.js +0 -1125
  640. package/dist/test/mcpl-coalescing.test.js.map +0 -1
  641. package/dist/test/mcpl-config-diagnostics.test.d.ts +0 -2
  642. package/dist/test/mcpl-config-diagnostics.test.d.ts.map +0 -1
  643. package/dist/test/mcpl-config-diagnostics.test.js +0 -452
  644. package/dist/test/mcpl-config-diagnostics.test.js.map +0 -1
  645. package/dist/test/mcpl-empty-content.test.d.ts +0 -2
  646. package/dist/test/mcpl-empty-content.test.d.ts.map +0 -1
  647. package/dist/test/mcpl-empty-content.test.js +0 -368
  648. package/dist/test/mcpl-empty-content.test.js.map +0 -1
  649. package/dist/test/mcpl-gate-roundtrip.test.d.ts +0 -2
  650. package/dist/test/mcpl-gate-roundtrip.test.d.ts.map +0 -1
  651. package/dist/test/mcpl-gate-roundtrip.test.js +0 -285
  652. package/dist/test/mcpl-gate-roundtrip.test.js.map +0 -1
  653. package/dist/test/mcpl-list-status.test.d.ts +0 -2
  654. package/dist/test/mcpl-list-status.test.d.ts.map +0 -1
  655. package/dist/test/mcpl-list-status.test.js +0 -82
  656. package/dist/test/mcpl-list-status.test.js.map +0 -1
  657. package/dist/test/mcpl-manifest-changed.test.d.ts +0 -2
  658. package/dist/test/mcpl-manifest-changed.test.d.ts.map +0 -1
  659. package/dist/test/mcpl-manifest-changed.test.js +0 -178
  660. package/dist/test/mcpl-manifest-changed.test.js.map +0 -1
  661. package/dist/test/mcpl-push-convert.test.d.ts +0 -2
  662. package/dist/test/mcpl-push-convert.test.d.ts.map +0 -1
  663. package/dist/test/mcpl-push-convert.test.js +0 -42
  664. package/dist/test/mcpl-push-convert.test.js.map +0 -1
  665. package/dist/test/mcpl-reconnect-events.test.d.ts +0 -2
  666. package/dist/test/mcpl-reconnect-events.test.d.ts.map +0 -1
  667. package/dist/test/mcpl-reconnect-events.test.js +0 -144
  668. package/dist/test/mcpl-reconnect-events.test.js.map +0 -1
  669. package/dist/test/mcpl-reconnect-reregistration.test.d.ts +0 -2
  670. package/dist/test/mcpl-reconnect-reregistration.test.d.ts.map +0 -1
  671. package/dist/test/mcpl-reconnect-reregistration.test.js +0 -238
  672. package/dist/test/mcpl-reconnect-reregistration.test.js.map +0 -1
  673. package/dist/test/mcpl-reference-fetcher.test.d.ts +0 -2
  674. package/dist/test/mcpl-reference-fetcher.test.d.ts.map +0 -1
  675. package/dist/test/mcpl-reference-fetcher.test.js +0 -427
  676. package/dist/test/mcpl-reference-fetcher.test.js.map +0 -1
  677. package/dist/test/mcpl-reference-fields.test.d.ts +0 -2
  678. package/dist/test/mcpl-reference-fields.test.d.ts.map +0 -1
  679. package/dist/test/mcpl-reference-fields.test.js +0 -149
  680. package/dist/test/mcpl-reference-fields.test.js.map +0 -1
  681. package/dist/test/mcpl-rejection-log-lines.test.d.ts +0 -2
  682. package/dist/test/mcpl-rejection-log-lines.test.d.ts.map +0 -1
  683. package/dist/test/mcpl-rejection-log-lines.test.js +0 -349
  684. package/dist/test/mcpl-rejection-log-lines.test.js.map +0 -1
  685. package/dist/test/mcpl-request-timeout.test.d.ts +0 -2
  686. package/dist/test/mcpl-request-timeout.test.d.ts.map +0 -1
  687. package/dist/test/mcpl-request-timeout.test.js +0 -138
  688. package/dist/test/mcpl-request-timeout.test.js.map +0 -1
  689. package/dist/test/mcpl-runtime-lifecycle.test.d.ts +0 -7
  690. package/dist/test/mcpl-runtime-lifecycle.test.d.ts.map +0 -1
  691. package/dist/test/mcpl-runtime-lifecycle.test.js +0 -142
  692. package/dist/test/mcpl-runtime-lifecycle.test.js.map +0 -1
  693. package/dist/test/mcpl-stderr-tail.test.d.ts +0 -2
  694. package/dist/test/mcpl-stderr-tail.test.d.ts.map +0 -1
  695. package/dist/test/mcpl-stderr-tail.test.js +0 -210
  696. package/dist/test/mcpl-stderr-tail.test.js.map +0 -1
  697. package/dist/test/mcpl-tool-class-listing.test.d.ts +0 -2
  698. package/dist/test/mcpl-tool-class-listing.test.d.ts.map +0 -1
  699. package/dist/test/mcpl-tool-class-listing.test.js +0 -147
  700. package/dist/test/mcpl-tool-class-listing.test.js.map +0 -1
  701. package/dist/test/mcpl-tool-classes.test.d.ts +0 -7
  702. package/dist/test/mcpl-tool-classes.test.d.ts.map +0 -1
  703. package/dist/test/mcpl-tool-classes.test.js +0 -80
  704. package/dist/test/mcpl-tool-classes.test.js.map +0 -1
  705. package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts +0 -2
  706. package/dist/test/mcpl-tool-lifecycle-e2e.test.d.ts.map +0 -1
  707. package/dist/test/mcpl-tool-lifecycle-e2e.test.js +0 -237
  708. package/dist/test/mcpl-tool-lifecycle-e2e.test.js.map +0 -1
  709. package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts +0 -11
  710. package/dist/test/mcpl-tool-lifecycle-emitter.test.d.ts.map +0 -1
  711. package/dist/test/mcpl-tool-lifecycle-emitter.test.js +0 -412
  712. package/dist/test/mcpl-tool-lifecycle-emitter.test.js.map +0 -1
  713. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts +0 -2
  714. package/dist/test/mcpl-tool-lifecycle-script.test.d.ts.map +0 -1
  715. package/dist/test/mcpl-tool-lifecycle-script.test.js +0 -288
  716. package/dist/test/mcpl-tool-lifecycle-script.test.js.map +0 -1
  717. package/dist/test/mcpl-tool-lifecycle.test.d.ts +0 -11
  718. package/dist/test/mcpl-tool-lifecycle.test.d.ts.map +0 -1
  719. package/dist/test/mcpl-tool-lifecycle.test.js +0 -270
  720. package/dist/test/mcpl-tool-lifecycle.test.js.map +0 -1
  721. package/dist/test/mcpl-transport-close.test.d.ts +0 -2
  722. package/dist/test/mcpl-transport-close.test.d.ts.map +0 -1
  723. package/dist/test/mcpl-transport-close.test.js +0 -373
  724. package/dist/test/mcpl-transport-close.test.js.map +0 -1
  725. package/dist/test/mcpl-transport-spawn.test.d.ts +0 -2
  726. package/dist/test/mcpl-transport-spawn.test.d.ts.map +0 -1
  727. package/dist/test/mcpl-transport-spawn.test.js +0 -58
  728. package/dist/test/mcpl-transport-spawn.test.js.map +0 -1
  729. package/dist/test/mcpl-ws-transport.test.d.ts +0 -2
  730. package/dist/test/mcpl-ws-transport.test.d.ts.map +0 -1
  731. package/dist/test/mcpl-ws-transport.test.js +0 -259
  732. package/dist/test/mcpl-ws-transport.test.js.map +0 -1
  733. package/dist/test/module-context-integration.test.d.ts +0 -18
  734. package/dist/test/module-context-integration.test.d.ts.map +0 -1
  735. package/dist/test/module-context-integration.test.js +0 -181
  736. package/dist/test/module-context-integration.test.js.map +0 -1
  737. package/dist/test/offline-recovery-branch.test.d.ts +0 -2
  738. package/dist/test/offline-recovery-branch.test.d.ts.map +0 -1
  739. package/dist/test/offline-recovery-branch.test.js +0 -570
  740. package/dist/test/offline-recovery-branch.test.js.map +0 -1
  741. package/dist/test/operator-surgery.test.d.ts +0 -2
  742. package/dist/test/operator-surgery.test.d.ts.map +0 -1
  743. package/dist/test/operator-surgery.test.js +0 -595
  744. package/dist/test/operator-surgery.test.js.map +0 -1
  745. package/dist/test/ops-alert.test.d.ts +0 -2
  746. package/dist/test/ops-alert.test.d.ts.map +0 -1
  747. package/dist/test/ops-alert.test.js +0 -153
  748. package/dist/test/ops-alert.test.js.map +0 -1
  749. package/dist/test/overbudget-drain-breaker.test.d.ts +0 -2
  750. package/dist/test/overbudget-drain-breaker.test.d.ts.map +0 -1
  751. package/dist/test/overbudget-drain-breaker.test.js +0 -198
  752. package/dist/test/overbudget-drain-breaker.test.js.map +0 -1
  753. package/dist/test/pack-installs-alone.test.d.ts +0 -2
  754. package/dist/test/pack-installs-alone.test.d.ts.map +0 -1
  755. package/dist/test/pack-installs-alone.test.js +0 -284
  756. package/dist/test/pack-installs-alone.test.js.map +0 -1
  757. package/dist/test/per-agent-delivery.test.d.ts +0 -2
  758. package/dist/test/per-agent-delivery.test.d.ts.map +0 -1
  759. package/dist/test/per-agent-delivery.test.js +0 -108
  760. package/dist/test/per-agent-delivery.test.js.map +0 -1
  761. package/dist/test/physical-window-restart.test.d.ts +0 -16
  762. package/dist/test/physical-window-restart.test.d.ts.map +0 -1
  763. package/dist/test/physical-window-restart.test.js +0 -487
  764. package/dist/test/physical-window-restart.test.js.map +0 -1
  765. package/dist/test/placed-reaction-baseline.test.d.ts +0 -2
  766. package/dist/test/placed-reaction-baseline.test.d.ts.map +0 -1
  767. package/dist/test/placed-reaction-baseline.test.js +0 -312
  768. package/dist/test/placed-reaction-baseline.test.js.map +0 -1
  769. package/dist/test/present-while-acting.test.d.ts +0 -19
  770. package/dist/test/present-while-acting.test.d.ts.map +0 -1
  771. package/dist/test/present-while-acting.test.js +0 -1649
  772. package/dist/test/present-while-acting.test.js.map +0 -1
  773. package/dist/test/prose-segments.test.d.ts +0 -2
  774. package/dist/test/prose-segments.test.d.ts.map +0 -1
  775. package/dist/test/prose-segments.test.js +0 -52
  776. package/dist/test/prose-segments.test.js.map +0 -1
  777. package/dist/test/prose-stream-router.test.d.ts +0 -2
  778. package/dist/test/prose-stream-router.test.d.ts.map +0 -1
  779. package/dist/test/prose-stream-router.test.js +0 -171
  780. package/dist/test/prose-stream-router.test.js.map +0 -1
  781. package/dist/test/provider-acceleration-cooldown.test.d.ts +0 -2
  782. package/dist/test/provider-acceleration-cooldown.test.d.ts.map +0 -1
  783. package/dist/test/provider-acceleration-cooldown.test.js +0 -435
  784. package/dist/test/provider-acceleration-cooldown.test.js.map +0 -1
  785. package/dist/test/provider-waits.test.d.ts +0 -2
  786. package/dist/test/provider-waits.test.d.ts.map +0 -1
  787. package/dist/test/provider-waits.test.js +0 -2259
  788. package/dist/test/provider-waits.test.js.map +0 -1
  789. package/dist/test/puppet-tool-call.test.d.ts +0 -2
  790. package/dist/test/puppet-tool-call.test.d.ts.map +0 -1
  791. package/dist/test/puppet-tool-call.test.js +0 -226
  792. package/dist/test/puppet-tool-call.test.js.map +0 -1
  793. package/dist/test/puppet-turn-reservation.test.d.ts +0 -2
  794. package/dist/test/puppet-turn-reservation.test.d.ts.map +0 -1
  795. package/dist/test/puppet-turn-reservation.test.js +0 -139
  796. package/dist/test/puppet-turn-reservation.test.js.map +0 -1
  797. package/dist/test/reaction-baseline-platform.test.d.ts +0 -2
  798. package/dist/test/reaction-baseline-platform.test.d.ts.map +0 -1
  799. package/dist/test/reaction-baseline-platform.test.js +0 -124
  800. package/dist/test/reaction-baseline-platform.test.js.map +0 -1
  801. package/dist/test/record-journal.test.d.ts +0 -6
  802. package/dist/test/record-journal.test.d.ts.map +0 -1
  803. package/dist/test/record-journal.test.js +0 -269
  804. package/dist/test/record-journal.test.js.map +0 -1
  805. package/dist/test/refusal-reaction-baseline.test.d.ts +0 -2
  806. package/dist/test/refusal-reaction-baseline.test.d.ts.map +0 -1
  807. package/dist/test/refusal-reaction-baseline.test.js +0 -81
  808. package/dist/test/refusal-reaction-baseline.test.js.map +0 -1
  809. package/dist/test/refusal-rewind.test.d.ts +0 -2
  810. package/dist/test/refusal-rewind.test.d.ts.map +0 -1
  811. package/dist/test/refusal-rewind.test.js +0 -117
  812. package/dist/test/refusal-rewind.test.js.map +0 -1
  813. package/dist/test/refusal-single-call.test.d.ts +0 -2
  814. package/dist/test/refusal-single-call.test.d.ts.map +0 -1
  815. package/dist/test/refusal-single-call.test.js +0 -177
  816. package/dist/test/refusal-single-call.test.js.map +0 -1
  817. package/dist/test/release-bump.test.d.ts +0 -2
  818. package/dist/test/release-bump.test.d.ts.map +0 -1
  819. package/dist/test/release-bump.test.js +0 -248
  820. package/dist/test/release-bump.test.js.map +0 -1
  821. package/dist/test/release-changelog.test.d.ts +0 -2
  822. package/dist/test/release-changelog.test.d.ts.map +0 -1
  823. package/dist/test/release-changelog.test.js +0 -159
  824. package/dist/test/release-changelog.test.js.map +0 -1
  825. package/dist/test/release-holds.test.d.ts +0 -2
  826. package/dist/test/release-holds.test.d.ts.map +0 -1
  827. package/dist/test/release-holds.test.js +0 -456
  828. package/dist/test/release-holds.test.js.map +0 -1
  829. package/dist/test/same-round-think-policy.test.d.ts +0 -2
  830. package/dist/test/same-round-think-policy.test.d.ts.map +0 -1
  831. package/dist/test/same-round-think-policy.test.js +0 -853
  832. package/dist/test/same-round-think-policy.test.js.map +0 -1
  833. package/dist/test/save-recent-image.test.d.ts +0 -26
  834. package/dist/test/save-recent-image.test.d.ts.map +0 -1
  835. package/dist/test/save-recent-image.test.js +0 -974
  836. package/dist/test/save-recent-image.test.js.map +0 -1
  837. package/dist/test/shutdown-errors.test.d.ts +0 -2
  838. package/dist/test/shutdown-errors.test.d.ts.map +0 -1
  839. package/dist/test/shutdown-errors.test.js +0 -662
  840. package/dist/test/shutdown-errors.test.js.map +0 -1
  841. package/dist/test/silent-heartbeat-rows.test.d.ts +0 -2
  842. package/dist/test/silent-heartbeat-rows.test.d.ts.map +0 -1
  843. package/dist/test/silent-heartbeat-rows.test.js +0 -451
  844. package/dist/test/silent-heartbeat-rows.test.js.map +0 -1
  845. package/dist/test/silent-heartbeat.test.d.ts +0 -2
  846. package/dist/test/silent-heartbeat.test.d.ts.map +0 -1
  847. package/dist/test/silent-heartbeat.test.js +0 -165
  848. package/dist/test/silent-heartbeat.test.js.map +0 -1
  849. package/dist/test/state-scaling-e2e.test.d.ts +0 -22
  850. package/dist/test/state-scaling-e2e.test.d.ts.map +0 -1
  851. package/dist/test/state-scaling-e2e.test.js +0 -206
  852. package/dist/test/state-scaling-e2e.test.js.map +0 -1
  853. package/dist/test/state-scaling.test.d.ts +0 -12
  854. package/dist/test/state-scaling.test.d.ts.map +0 -1
  855. package/dist/test/state-scaling.test.js +0 -251
  856. package/dist/test/state-scaling.test.js.map +0 -1
  857. package/dist/test/stop-with-host-operations.test.d.ts +0 -20
  858. package/dist/test/stop-with-host-operations.test.d.ts.map +0 -1
  859. package/dist/test/stop-with-host-operations.test.js +0 -1174
  860. package/dist/test/stop-with-host-operations.test.js.map +0 -1
  861. package/dist/test/stop-with-work-in-flight.test.d.ts +0 -23
  862. package/dist/test/stop-with-work-in-flight.test.d.ts.map +0 -1
  863. package/dist/test/stop-with-work-in-flight.test.js +0 -784
  864. package/dist/test/stop-with-work-in-flight.test.js.map +0 -1
  865. package/dist/test/surgery-marks-receipt.test.d.ts +0 -2
  866. package/dist/test/surgery-marks-receipt.test.d.ts.map +0 -1
  867. package/dist/test/surgery-marks-receipt.test.js +0 -540
  868. package/dist/test/surgery-marks-receipt.test.js.map +0 -1
  869. package/dist/test/thinking-token-stamp.test.d.ts +0 -12
  870. package/dist/test/thinking-token-stamp.test.d.ts.map +0 -1
  871. package/dist/test/thinking-token-stamp.test.js +0 -129
  872. package/dist/test/thinking-token-stamp.test.js.map +0 -1
  873. package/dist/test/timezone.test.d.ts +0 -2
  874. package/dist/test/timezone.test.d.ts.map +0 -1
  875. package/dist/test/timezone.test.js +0 -18
  876. package/dist/test/timezone.test.js.map +0 -1
  877. package/dist/test/tool-batch-hook.test.d.ts +0 -8
  878. package/dist/test/tool-batch-hook.test.d.ts.map +0 -1
  879. package/dist/test/tool-batch-hook.test.js +0 -275
  880. package/dist/test/tool-batch-hook.test.js.map +0 -1
  881. package/dist/test/tool-policy.test.d.ts +0 -2
  882. package/dist/test/tool-policy.test.d.ts.map +0 -1
  883. package/dist/test/tool-policy.test.js +0 -51
  884. package/dist/test/tool-policy.test.js.map +0 -1
  885. package/dist/test/tool-presentation.test.d.ts +0 -2
  886. package/dist/test/tool-presentation.test.d.ts.map +0 -1
  887. package/dist/test/tool-presentation.test.js +0 -268
  888. package/dist/test/tool-presentation.test.js.map +0 -1
  889. package/dist/test/tool-result-content.test.d.ts +0 -2
  890. package/dist/test/tool-result-content.test.d.ts.map +0 -1
  891. package/dist/test/tool-result-content.test.js +0 -37
  892. package/dist/test/tool-result-content.test.js.map +0 -1
  893. package/dist/test/tool-result-guard.test.d.ts +0 -2
  894. package/dist/test/tool-result-guard.test.d.ts.map +0 -1
  895. package/dist/test/tool-result-guard.test.js +0 -3729
  896. package/dist/test/tool-result-guard.test.js.map +0 -1
  897. package/dist/test/tool-result-history.test.d.ts +0 -7
  898. package/dist/test/tool-result-history.test.d.ts.map +0 -1
  899. package/dist/test/tool-result-history.test.js +0 -156
  900. package/dist/test/tool-result-history.test.js.map +0 -1
  901. package/dist/test/tool-result-spill.test.d.ts +0 -13
  902. package/dist/test/tool-result-spill.test.d.ts.map +0 -1
  903. package/dist/test/tool-result-spill.test.js +0 -586
  904. package/dist/test/tool-result-spill.test.js.map +0 -1
  905. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts +0 -2
  906. package/dist/test/tool-wrapper-prose-guard-integration.test.d.ts.map +0 -1
  907. package/dist/test/tool-wrapper-prose-guard-integration.test.js +0 -485
  908. package/dist/test/tool-wrapper-prose-guard-integration.test.js.map +0 -1
  909. package/dist/test/tool-wrapper-prose-guard.test.d.ts +0 -2
  910. package/dist/test/tool-wrapper-prose-guard.test.d.ts.map +0 -1
  911. package/dist/test/tool-wrapper-prose-guard.test.js +0 -34
  912. package/dist/test/tool-wrapper-prose-guard.test.js.map +0 -1
  913. package/dist/test/tools-observe-parser-conformance.test.d.ts +0 -2
  914. package/dist/test/tools-observe-parser-conformance.test.d.ts.map +0 -1
  915. package/dist/test/tools-observe-parser-conformance.test.js +0 -129
  916. package/dist/test/tools-observe-parser-conformance.test.js.map +0 -1
  917. package/dist/test/trunk-channel-routing.test.d.ts +0 -19
  918. package/dist/test/trunk-channel-routing.test.d.ts.map +0 -1
  919. package/dist/test/trunk-channel-routing.test.js +0 -652
  920. package/dist/test/trunk-channel-routing.test.js.map +0 -1
  921. package/dist/test/tune-out-e2e.test.d.ts +0 -2
  922. package/dist/test/tune-out-e2e.test.d.ts.map +0 -1
  923. package/dist/test/tune-out-e2e.test.js +0 -142
  924. package/dist/test/tune-out-e2e.test.js.map +0 -1
  925. package/dist/test/tune-out-gate-composition.test.d.ts +0 -2
  926. package/dist/test/tune-out-gate-composition.test.d.ts.map +0 -1
  927. package/dist/test/tune-out-gate-composition.test.js +0 -221
  928. package/dist/test/tune-out-gate-composition.test.js.map +0 -1
  929. package/dist/test/tune-out-lifecycle.test.d.ts +0 -2
  930. package/dist/test/tune-out-lifecycle.test.d.ts.map +0 -1
  931. package/dist/test/tune-out-lifecycle.test.js +0 -121
  932. package/dist/test/tune-out-lifecycle.test.js.map +0 -1
  933. package/dist/test/turn-trigger-provenance.test.d.ts +0 -2
  934. package/dist/test/turn-trigger-provenance.test.d.ts.map +0 -1
  935. package/dist/test/turn-trigger-provenance.test.js +0 -137
  936. package/dist/test/turn-trigger-provenance.test.js.map +0 -1
  937. package/dist/test/unconvertible-tool-result.test.d.ts +0 -2
  938. package/dist/test/unconvertible-tool-result.test.d.ts.map +0 -1
  939. package/dist/test/unconvertible-tool-result.test.js +0 -358
  940. package/dist/test/unconvertible-tool-result.test.js.map +0 -1
  941. package/dist/test/unsent-compiles.test.d.ts +0 -2
  942. package/dist/test/unsent-compiles.test.d.ts.map +0 -1
  943. package/dist/test/unsent-compiles.test.js +0 -139
  944. package/dist/test/unsent-compiles.test.js.map +0 -1
  945. package/dist/test/usage-accounting.test.d.ts +0 -2
  946. package/dist/test/usage-accounting.test.d.ts.map +0 -1
  947. package/dist/test/usage-accounting.test.js +0 -13
  948. package/dist/test/usage-accounting.test.js.map +0 -1
  949. package/dist/test/usage-tracker.test.d.ts +0 -2
  950. package/dist/test/usage-tracker.test.d.ts.map +0 -1
  951. package/dist/test/usage-tracker.test.js +0 -129
  952. package/dist/test/usage-tracker.test.js.map +0 -1
  953. package/dist/test/utils-surface.test.d.ts +0 -2
  954. package/dist/test/utils-surface.test.d.ts.map +0 -1
  955. package/dist/test/utils-surface.test.js +0 -145
  956. package/dist/test/utils-surface.test.js.map +0 -1
  957. package/dist/test/workspace-branch-continuation.test.d.ts +0 -15
  958. package/dist/test/workspace-branch-continuation.test.d.ts.map +0 -1
  959. package/dist/test/workspace-branch-continuation.test.js +0 -183
  960. package/dist/test/workspace-branch-continuation.test.js.map +0 -1
  961. package/dist/test/workspace-materialize-freshness.test.d.ts +0 -16
  962. package/dist/test/workspace-materialize-freshness.test.d.ts.map +0 -1
  963. package/dist/test/workspace-materialize-freshness.test.js +0 -252
  964. package/dist/test/workspace-materialize-freshness.test.js.map +0 -1
  965. package/dist/test/workspace-path-containment.test.d.ts +0 -19
  966. package/dist/test/workspace-path-containment.test.d.ts.map +0 -1
  967. package/dist/test/workspace-path-containment.test.js +0 -91
  968. package/dist/test/workspace-path-containment.test.js.map +0 -1
  969. package/dist/test/workspace-read-file-from-disk.test.d.ts +0 -17
  970. package/dist/test/workspace-read-file-from-disk.test.d.ts.map +0 -1
  971. package/dist/test/workspace-read-file-from-disk.test.js +0 -174
  972. package/dist/test/workspace-read-file-from-disk.test.js.map +0 -1
  973. package/dist/test/workspace-read-image.test.d.ts +0 -2
  974. package/dist/test/workspace-read-image.test.d.ts.map +0 -1
  975. package/dist/test/workspace-read-image.test.js +0 -597
  976. package/dist/test/workspace-read-image.test.js.map +0 -1
  977. package/dist/test/workspace-read-text.test.d.ts +0 -2
  978. package/dist/test/workspace-read-text.test.d.ts.map +0 -1
  979. package/dist/test/workspace-read-text.test.js +0 -101
  980. package/dist/test/workspace-read-text.test.js.map +0 -1
  981. package/dist/test/workspace-restore-order.test.d.ts +0 -16
  982. package/dist/test/workspace-restore-order.test.d.ts.map +0 -1
  983. package/dist/test/workspace-restore-order.test.js +0 -140
  984. package/dist/test/workspace-restore-order.test.js.map +0 -1
  985. package/dist/test/workspace-three-way.test.d.ts +0 -12
  986. package/dist/test/workspace-three-way.test.d.ts.map +0 -1
  987. package/dist/test/workspace-three-way.test.js +0 -4094
  988. package/dist/test/workspace-three-way.test.js.map +0 -1
  989. package/dist/test/workspace-watcher.test.d.ts +0 -2
  990. package/dist/test/workspace-watcher.test.d.ts.map +0 -1
  991. package/dist/test/workspace-watcher.test.js +0 -281
  992. package/dist/test/workspace-watcher.test.js.map +0 -1
  993. package/dist/test/xml-round-persist.test.d.ts +0 -19
  994. package/dist/test/xml-round-persist.test.d.ts.map +0 -1
  995. package/dist/test/xml-round-persist.test.js +0 -235
  996. package/dist/test/xml-round-persist.test.js.map +0 -1
  997. package/dist/tsconfig.tsbuildinfo +0 -1
@@ -49,7 +49,9 @@ import type { ToolDefinition, ToolCall, ToolResult, ProcessEvent } from '../../t
49
49
  import type { EventResponse, ProcessState } from '../../types/module.js';
50
50
  import type { SearchWorkerMessage, SearchWorkerMatch } from './search-regex-worker.js';
51
51
  import type { ChannelRegistry } from '../../mcpl/channel-registry.js';
52
- import { SemanticIndexClient, SemanticIndexer, messageIndexText, type SemanticIndexConfig, type SyncReport, type PendingChanges } from './semantic.js';
52
+ import { EmbedServiceError, SemanticIndexClient, SemanticIndexer, messageIndexText, type SearchResponse, type SemanticIndexConfig, type SyncReport, type PendingChanges } from './semantic.js';
53
+ import { unknownToolMessage } from '../unknown-tool.js';
54
+ import { safeSlice } from '../../safe-slice.js';
53
55
 
54
56
  // ============================================================================
55
57
  // Tool input shapes
@@ -177,10 +179,15 @@ const AROUND_MAX = 100;
177
179
  * see a handful; past this the call fails loudly rather than guessing. */
178
180
  const AROUND_TIE_CAP = 5000;
179
181
 
180
- function tieCapError(anchorId: string, n: number): Error {
182
+ function tieCapError(anchorId: string, ms: number, channelId: string | undefined): Error {
183
+ const at = new Date(ms).toISOString();
184
+ // The channel is the agent's to send back, so it's quoted whole: a cut one
185
+ // couldn't be sent. It's one the store holds, with all those messages in it.
186
+ const inChannel = channelId !== undefined ? ` and "channelId" set to ${JSON.stringify(channelId)}` : '';
181
187
  return new Error(
182
- `Message ${anchorId} shares its timestamp with ${n} messages (more than ${AROUND_TIE_CAP}), too many to order ` +
183
- 'around it. Use extract with from/to set to that instant instead.',
188
+ `Nothing was read: more than ${AROUND_TIE_CAP} messages share the timestamp of message ${shown(anchorId)}, ${at}, too ` +
189
+ `many for aroundId to put in order. To read them, call history--extract with "from" and "to" both set to ${at}` +
190
+ `${inChannel}; it pages through them with "offset" and "limit".`,
184
191
  );
185
192
  }
186
193
 
@@ -208,9 +215,13 @@ const SEMANTIC_DEFAULT_LIMIT = 10;
208
215
  const SEMANTIC_MAX_LIMIT = 50;
209
216
  /** Largest k sent to the embed-service when over-fetching past dropped hits (its own cap is 200). */
210
217
  const SEMANTIC_MAX_FETCH = 200;
211
- /** Local snippet: the leading SEMANTIC_SNIPPET_CHARS of the current text. */
218
+ /**
219
+ * Local snippet: the leading SEMANTIC_SNIPPET_CHARS of the current text, one
220
+ * fewer where the last would split a surrogate pair, since the snippet reaches
221
+ * the agent's context.
222
+ */
212
223
  function snippetOf(text: string): string {
213
- return text.length <= SEMANTIC_SNIPPET_CHARS ? text : `${text.slice(0, SEMANTIC_SNIPPET_CHARS)}…`;
224
+ return text.length <= SEMANTIC_SNIPPET_CHARS ? text : `${safeSlice(text, 0, SEMANTIC_SNIPPET_CHARS)}…`;
214
225
  }
215
226
  const SEMANTIC_SNIPPET_CHARS = 400;
216
227
 
@@ -336,16 +347,20 @@ export class HistoryModule implements Module {
336
347
  * you're trying to deliver a message, nonsense advice from a read-only
337
348
  * history tool. This module always uses its own read-only-appropriate
338
349
  * wording instead, regardless of which branch of the live resolver
339
- * produced the miss.
350
+ * produced the miss. Its refusals open with "Nothing was read", which
351
+ * holds because every tool that resolves a channel here only reads (see
352
+ * the note above the helpers).
340
353
  */
341
354
  private resolveChannel(input: string | undefined): string | undefined {
342
355
  if (!input || !this.channelRegistry) return input;
343
356
  const resolved = this.channelRegistry.resolveProseTargetDurable(input);
344
357
  if (!('error' in resolved)) return resolved.channelId;
345
358
  if (input.startsWith('#') || input.startsWith('@') || DM_MENTION_RE.test(input)) {
346
- const suggestions = resolved.candidates?.length ? ` Did you mean: ${resolved.candidates.join(', ')}?` : '';
359
+ const suggestions = resolved.candidates?.length ? ` Did you mean ${quotedList(resolved.candidates, 'or')}?` : '';
347
360
  throw new Error(
348
- `No channel history found for "${input}" — it may never have been seen by this resident, or predates this feature.${suggestions}`,
361
+ `Nothing was read: no channel history matches ${shown(input)}. This host may never have seen a channel by that ` +
362
+ `name, or saw it before it kept channel names.${suggestions} Each message history--search returns carries ` +
363
+ 'its "channelId", so searching for something said in that channel shows its id; pass that id as "channelId".',
349
364
  );
350
365
  }
351
366
  return input;
@@ -390,9 +405,15 @@ export class HistoryModule implements Module {
390
405
  ? cm.queryMessagesByTimeAndChannel({ fromMs: ms, toMs: ms, channelId, limit: 0 }).matchedCount
391
406
  : cm.queryMessagesByTime({ fromMs: ms, toMs: ms, limit: AROUND_TIE_CAP + 1 }).messages.length;
392
407
  if (n > AROUND_TIE_CAP) {
408
+ // The count is by channel alone, so an author filter can't bring it down.
409
+ const at = new Date(ms).toISOString();
393
410
  throw new Error(
394
- `More than ${AROUND_TIE_CAP} messages share the instant ${new Date(ms).toISOString()}; a continuation can't step ` +
395
- 'through them. Narrow the search with channelId/author instead.',
411
+ `Nothing was searched: more than ${AROUND_TIE_CAP} messages share the instant ${at}, too many for a ` +
412
+ `continuation to step past. To read them, call history--extract with "from" and "to" both set to ${at}` +
413
+ (channelId !== undefined
414
+ ? ' and the same "channelId"; it pages through them with "offset" and "limit".'
415
+ : '; it pages through them with "offset" and "limit". Searching one channel, with "channelId", may also ' +
416
+ 'bring them under the limit.'),
396
417
  );
397
418
  }
398
419
  return n;
@@ -450,13 +471,19 @@ export class HistoryModule implements Module {
450
471
  q: { fromMs?: number; toMs?: number; channelId?: string },
451
472
  ): { windowOffset: number; shift: number } {
452
473
  if (windowOffset === 0) {
453
- if (afterId != null) throw new Error('"afterId" only applies together with a windowOffset > 0.');
474
+ if (afterId != null) {
475
+ throw new Error(
476
+ 'Nothing was read: "afterId" goes with a "windowOffset" above 0, both copied from a truncated response\'s ' +
477
+ '"resume". For a first page, leave afterId out; to continue, copy the fields of that "resume" into the call.',
478
+ );
479
+ }
454
480
  return { windowOffset, shift: 0 };
455
481
  }
456
482
  if (afterId == null) {
457
483
  throw new Error(
458
- '"windowOffset" needs the "afterId" from the same `resume` object — without it a message added or removed ' +
459
- 'since the previous call would be silently skipped. Pass the whole `resume` object back.',
484
+ 'Nothing was read: "windowOffset" needs the "afterId" from the same "resume", or a message added or removed ' +
485
+ 'since the previous call would be skipped unseen. Copy the fields of the previous response\'s "resume" into ' +
486
+ 'the call.',
460
487
  );
461
488
  }
462
489
  const cm = this.cm as ContextManager;
@@ -467,9 +494,9 @@ export class HistoryModule implements Module {
467
494
  const i = around.findIndex((m) => String(m.id) === afterId);
468
495
  if (i === -1) {
469
496
  throw new Error(
470
- `The window changed since the previous call: message ${afterId}, where that scan stopped, is no longer ` +
471
- `within ${RELOCATE_RADIUS} positions of windowOffset ${windowOffset} (it may have been deleted). ` +
472
- 'Restart the scan without windowOffset/afterId, or narrow it with from/to.',
497
+ `Nothing was read: the window changed since the previous call. Message ${shown(afterId)}, where that scan stopped, ` +
498
+ `is no longer within ${RELOCATE_RADIUS} positions of windowOffset ${windowOffset}; it may have been deleted. ` +
499
+ 'Restart the scan without "windowOffset" and "afterId", over the same range or one narrowed with "from" and "to".',
473
500
  );
474
501
  }
475
502
  const relocated = lo + i + 1;
@@ -717,7 +744,7 @@ export class HistoryModule implements Module {
717
744
  excludeAuthor: EXCLUDE_AUTHOR_SCHEMA,
718
745
  order: { type: 'string', enum: ['oldest', 'newest'], description: 'Scan and return oldest-first (default) or newest-first.' },
719
746
  limit: { type: 'number', description: `Max matches to return (default ${SEARCH_DEFAULT_LIMIT}, hard cap ${SEARCH_MAX_LIMIT}). Must be a non-negative integer.` },
720
- maxScan: { type: 'number', description: `Max candidate messages to scan (default ${SEARCH_DEFAULT_MAX_SCAN}, hard cap ${SEARCH_MAX_MAX_SCAN}). Must be a non-negative integer.` },
747
+ maxScan: { type: 'number', description: `Max candidate messages to scan (default ${SEARCH_DEFAULT_MAX_SCAN}, hard cap ${SEARCH_MAX_MAX_SCAN}). Must be a whole number, 1 or more.` },
721
748
  skipSequences: {
722
749
  type: 'array',
723
750
  items: { type: 'number' },
@@ -793,7 +820,10 @@ export class HistoryModule implements Module {
793
820
  'Purely a read: nothing is written to your history. Hits are checked against your current branch ' +
794
821
  'before they come back: a message you undid or a summary from a branch you left is dropped and ' +
795
822
  'counted in index.droppedOffBranch; a message edited down to nothing searchable is dropped and ' +
796
- 'counted in index.droppedStale. Message snippets always show the current text of the message.',
823
+ 'counted in index.droppedStale. When that leaves fewer than `limit` hits and the index may hold more, it ' +
824
+ 'searches again, deeper, down to its first 200 hits; if a search after the first fails, the hits found so ' +
825
+ 'far come back, with why in index.searchAgainError. Message snippets ' +
826
+ 'always show the current text of the message.',
797
827
  inputSchema: {
798
828
  type: 'object' as const,
799
829
  properties: {
@@ -814,7 +844,13 @@ export class HistoryModule implements Module {
814
844
  async handleToolCall(call: ToolCall): Promise<ToolResult> {
815
845
  try {
816
846
  if (!this.cm) {
817
- throw new Error('HistoryModule not bound — host must call bind(contextManager) before tool dispatch.');
847
+ // The host's mistake, named for whoever runs it; the agent reads what
848
+ // it can do about it, which is nothing.
849
+ console.error('[history] a tool was called before the host bound the module: call bind(contextManager) before tool dispatch');
850
+ throw new Error(
851
+ "The history tools aren't available: this host hasn't finished setting up the history module, so nothing " +
852
+ "was read. Calling them again won't change that; ask whoever runs this host.",
853
+ );
818
854
  }
819
855
  switch (call.name) {
820
856
  case 'stats':
@@ -828,7 +864,7 @@ export class HistoryModule implements Module {
828
864
  case 'semantic_search':
829
865
  return await this.handleSemanticSearch((call.input ?? {}) as SemanticSearchInput);
830
866
  default:
831
- return { success: false, isError: true, error: `Unknown tool: ${call.name}` };
867
+ return { success: false, isError: true, error: unknownToolMessage(this.name, call.name, this.getTools()) };
832
868
  }
833
869
  } catch (error) {
834
870
  // Catches our own validation errors (bad ISO date, invalid regex,
@@ -897,20 +933,33 @@ export class HistoryModule implements Module {
897
933
 
898
934
  private handleExtract(input: ExtractInput): ToolResult {
899
935
  if (input.aroundId !== undefined) return this.handleExtractAround(input);
900
- if (input.before !== undefined || input.after !== undefined || input.allChannels !== undefined) {
901
- throw new Error('"before"/"after"/"allChannels" only apply together with "aroundId".');
936
+ const aroundOnly = (['before', 'after', 'allChannels'] as const).filter((k) => input[k] !== undefined);
937
+ if (aroundOnly.length > 0) {
938
+ const one = aroundOnly.length === 1;
939
+ throw new Error(
940
+ `Nothing was read: ${quotedList(aroundOnly, 'and')} ${one ? 'goes' : 'go'} with "aroundId", which reads the ` +
941
+ `conversation around one message. Pass aroundId too, or leave ${one ? 'it' : 'them'} out to read a range ` +
942
+ 'with "from", "to" and "channelId".',
943
+ );
902
944
  }
903
945
  const channelId = this.resolveChannel(input.channelId);
904
946
  const fromMs = parseIsoDate(input.from, 'from');
905
947
  const toMs = parseIsoDate(input.to, 'to');
906
- const limit = clampCount(input.limit, EXTRACT_DEFAULT_LIMIT, EXTRACT_MAX_LIMIT, 'limit');
907
- const offset = clampCount(input.offset, 0, NATIVE_OFFSET_MAX, 'offset');
948
+ const limit = clampCount(input.limit, EXTRACT_DEFAULT_LIMIT, EXTRACT_MAX_LIMIT, 'limit', 'read');
949
+ const offset = clampCount(input.offset, 0, NATIVE_OFFSET_MAX, 'offset', 'read');
908
950
  const format = input.format ?? 'text';
909
951
  const authorFilter = buildAuthorFilter(input.author, input.excludeAuthor);
910
952
 
911
953
  const cm = this.cm as ContextManager;
912
- if (!authorFilter && (input.windowOffset !== undefined || input.afterId !== undefined || input.seqMark !== undefined)) {
913
- throw new Error('"windowOffset"/"afterId" only apply together with author/excludeAuthor (they resume a filtered scan). Use offset.');
954
+ const resumeOnly = (['windowOffset', 'afterId', 'seqMark'] as const).filter((k) => input[k] !== undefined);
955
+ if (!authorFilter && resumeOnly.length > 0) {
956
+ const one = resumeOnly.length === 1;
957
+ throw new Error(
958
+ `Nothing was read: ${quotedList(resumeOnly, 'and')} ${one ? 'resumes' : 'resume'} an author-filtered ` +
959
+ `extract, so ${one ? 'it goes' : 'they go'} with the "author" or "excludeAuthor" of the call that returned ` +
960
+ `${one ? 'it' : 'them'}: repeat that call with the fields of its "resume". To page through a range without an ` +
961
+ 'author filter, use "offset".',
962
+ );
914
963
  }
915
964
 
916
965
  if (authorFilter) {
@@ -924,9 +973,11 @@ export class HistoryModule implements Module {
924
973
  // channel-scoped window pages in append order, and Discord catch-up
925
974
  // appends old-timestamped messages late, so "continue from the last
926
975
  // timestamp seen" would skip them.
927
- const maxScan = clampCount(input.maxScan, EXTRACT_FILTER_DEFAULT_MAX_SCAN, EXTRACT_FILTER_MAX_MAX_SCAN, 'maxScan');
928
- if (maxScan === 0) throw new Error('"maxScan" must be at least 1 for an author-filtered extract.');
929
- const requestedOffset = clampCount(input.windowOffset, 0, NATIVE_OFFSET_MAX, 'windowOffset');
976
+ // An author-filtered extract has to examine at least 1 message.
977
+ const maxScan = clampCount(
978
+ input.maxScan, EXTRACT_FILTER_DEFAULT_MAX_SCAN, EXTRACT_FILTER_MAX_MAX_SCAN, 'maxScan', 'read', 1,
979
+ );
980
+ const requestedOffset = clampCount(input.windowOffset, 0, NATIVE_OFFSET_MAX, 'windowOffset', 'read');
930
981
  // A position is only meaningful against the window it was taken in:
931
982
  // a removal before it (every discord:delete, host hide, /undo) shifts
932
983
  // later positions left, and a message inserted before it (a
@@ -951,7 +1002,8 @@ export class HistoryModule implements Module {
951
1002
  if (channelId === undefined && windowOffset > 0) {
952
1003
  if (typeof input.seqMark !== 'number') {
953
1004
  throw new Error(
954
- '"windowOffset" without channelId needs the "seqMark" from the same `resume` object. Pass the whole `resume` object back.',
1005
+ 'Nothing was read: "windowOffset" without "channelId" needs the "seqMark" from the same "resume". Copy the ' +
1006
+ 'fields of the previous response\'s "resume" into the call.',
955
1007
  );
956
1008
  }
957
1009
  const cursor = cm.queryMessagesByTimeAndChannel({ fromMs, toMs, limit: 1, offset: windowOffset - 1 }).messages[0];
@@ -1128,25 +1180,27 @@ export class HistoryModule implements Module {
1128
1180
  * via edgeWindow, which is exact by timestamp).
1129
1181
  */
1130
1182
  private handleExtractAround(input: ExtractInput): ToolResult {
1131
- if (
1132
- input.from !== undefined ||
1133
- input.to !== undefined ||
1134
- input.offset !== undefined ||
1135
- input.windowOffset !== undefined ||
1136
- input.limit !== undefined
1137
- ) {
1183
+ const windowFields = (['from', 'to', 'offset', 'windowOffset', 'limit'] as const).filter((k) => input[k] !== undefined);
1184
+ if (windowFields.length > 0) {
1138
1185
  throw new Error(
1139
- '"aroundId" cannot be combined with from/to/offset/windowOffset/limit — it picks its own window; size it with before/after.',
1186
+ `Nothing was read: "aroundId" picks its own window, so it doesn't take ${quotedList(windowFields, 'or')}. Size ` +
1187
+ 'the window with "before" and "after", or leave out "aroundId" to read a range.',
1140
1188
  );
1141
1189
  }
1142
1190
  if (input.author !== undefined || input.excludeAuthor !== undefined) {
1143
- throw new Error('"aroundId" returns the whole conversation around a message; author filters do not apply. Use search/extract with author instead.');
1191
+ throw new Error(
1192
+ 'Nothing was read: "aroundId" reads the whole conversation around one message, so author filters don\'t ' +
1193
+ 'apply to it. To find someone\'s messages, use history--search or history--extract with "author".',
1194
+ );
1144
1195
  }
1145
1196
  if (input.allChannels && input.channelId !== undefined) {
1146
- throw new Error('Pass either "channelId" or "allChannels", not both.');
1197
+ throw new Error(
1198
+ 'Nothing was read: "channelId" and "allChannels" can\'t go together. Leave out "channelId" to read around the ' +
1199
+ 'message in every channel, or leave out both to read around it in its own channel.',
1200
+ );
1147
1201
  }
1148
- const before = clampCount(input.before, AROUND_DEFAULT, AROUND_MAX, 'before');
1149
- const after = clampCount(input.after, AROUND_DEFAULT, AROUND_MAX, 'after');
1202
+ const before = clampCount(input.before, AROUND_DEFAULT, AROUND_MAX, 'before', 'read');
1203
+ const after = clampCount(input.after, AROUND_DEFAULT, AROUND_MAX, 'after', 'read');
1150
1204
  const format = input.format ?? 'text';
1151
1205
  const cm = this.cm as ContextManager;
1152
1206
 
@@ -1155,12 +1209,20 @@ export class HistoryModule implements Module {
1155
1209
  const rawId = String(input.aroundId);
1156
1210
  if (/^sum:/.test(rawId)) {
1157
1211
  throw new Error(
1158
- `${rawId} is a compression summary, not a message — use overview for summaries, or aroundId with a msg: hit.`,
1212
+ `Nothing was read: ${shown(rawId)} is a compression summary, not a message. history--overview reads ` +
1213
+ 'summaries; "aroundId" takes a message, such as ' +
1214
+ (this.semanticCfg
1215
+ ? 'the "messageId" of a history--semantic_search hit.'
1216
+ : 'the "id" of a history--search or history--extract result.'),
1159
1217
  );
1160
1218
  }
1161
1219
  const anchor = cm.getMessage(rawId.replace(/^msg:/, ''));
1162
1220
  if (!anchor) {
1163
- throw new Error(`No message with id ${JSON.stringify(input.aroundId)} in this history.`);
1221
+ throw new Error(
1222
+ `Nothing was read: no message in your history has the id ${shown(input.aroundId)}. Pass the "id" ` +
1223
+ 'of a history--search or history--extract result' +
1224
+ (this.semanticCfg ? ', or the "messageId" of a history--semantic_search hit.' : '.'),
1225
+ );
1164
1226
  }
1165
1227
  const anchorId = String(anchor.id);
1166
1228
  const anchorChannel = getChannelId(anchor);
@@ -1175,11 +1237,11 @@ export class HistoryModule implements Module {
1175
1237
  let ties: StoredMessage[];
1176
1238
  if (channelId !== undefined) {
1177
1239
  const n = cm.queryMessagesByTimeAndChannel({ fromMs: t, toMs: t, channelId, limit: 0 }).matchedCount;
1178
- if (n > AROUND_TIE_CAP) throw tieCapError(anchorId, n);
1240
+ if (n > AROUND_TIE_CAP) throw tieCapError(anchorId, t, channelId);
1179
1241
  ties = cm.queryMessagesByTimeAndChannel({ fromMs: t, toMs: t, channelId, limit: n }).messages;
1180
1242
  } else {
1181
1243
  ties = cm.queryMessagesByTime({ fromMs: t, toMs: t, limit: AROUND_TIE_CAP + 1 }).messages;
1182
- if (ties.length > AROUND_TIE_CAP) throw tieCapError(anchorId, ties.length);
1244
+ if (ties.length > AROUND_TIE_CAP) throw tieCapError(anchorId, t, undefined);
1183
1245
  }
1184
1246
  ties = [...ties].sort((x, y) => x.sequence - y.sequence);
1185
1247
  const idx = ties.findIndex((m) => String(m.id) === anchorId);
@@ -1187,9 +1249,9 @@ export class HistoryModule implements Module {
1187
1249
  // The anchor is in the store and every message of its millisecond in
1188
1250
  // `channelId` was fetched, so only an explicit channelId excludes it.
1189
1251
  throw new Error(
1190
- `Message ${anchorId} is not in channel ${JSON.stringify(input.channelId)}` +
1191
- (anchorChannel ? ` (it is in ${anchorChannel}).` : '.') +
1192
- ' Omit channelId to use the anchor\'s own channel.',
1252
+ `Nothing was read: message ${shown(anchorId)} isn't in channel ${shown(input.channelId)}` +
1253
+ (anchorChannel ? ` (it's in ${shown(anchorChannel)}).` : '.') +
1254
+ ' Leave out "channelId" to read around it in its own channel.',
1193
1255
  );
1194
1256
  }
1195
1257
  const tiesBefore = ties.slice(Math.max(0, idx - before), idx);
@@ -1219,22 +1281,35 @@ export class HistoryModule implements Module {
1219
1281
  // ==========================================================================
1220
1282
 
1221
1283
  private async handleSearch(input: SearchInput): Promise<ToolResult> {
1222
- if (!input.query) {
1223
- throw new Error('search requires a non-empty "query".');
1284
+ // A finite number is searched as its text, as a regular expression made
1285
+ // from it reads it; any other value that isn't text is refused.
1286
+ const given: unknown = input.query;
1287
+ const query = typeof given === 'number' && Number.isFinite(given) ? String(given) : given;
1288
+ if (typeof query !== 'string' || query === '') {
1289
+ throw new Error(
1290
+ 'Nothing was searched: "query" must be the text to look for; ' +
1291
+ `${given === undefined ? 'this call gave none' : `it's ${shown(given)}`}.`,
1292
+ );
1224
1293
  }
1225
1294
  const channelId = this.resolveChannel(input.channelId);
1226
1295
  const fromMs = parseIsoDate(input.from, 'from');
1227
1296
  const toMs = parseIsoDate(input.to, 'to');
1228
- const limit = clampCount(input.limit, SEARCH_DEFAULT_LIMIT, SEARCH_MAX_LIMIT, 'limit');
1229
- const maxScan = clampCount(input.maxScan, SEARCH_DEFAULT_MAX_SCAN, SEARCH_MAX_MAX_SCAN, 'maxScan');
1297
+ const limit = clampCount(input.limit, SEARCH_DEFAULT_LIMIT, SEARCH_MAX_LIMIT, 'limit', 'searched');
1298
+ // A search has to scan at least 1 message.
1299
+ const maxScan = clampCount(input.maxScan, SEARCH_DEFAULT_MAX_SCAN, SEARCH_MAX_MAX_SCAN, 'maxScan', 'searched', 1);
1230
1300
  const caseSensitive = input.caseSensitive ?? false;
1231
1301
  const flags = caseSensitive ? '' : 'i';
1232
1302
  const order = input.order ?? 'oldest';
1233
1303
  if (order !== 'oldest' && order !== 'newest') {
1234
- throw new Error(`"order" must be "oldest" or "newest", got ${JSON.stringify(input.order)}.`);
1304
+ throw new Error(
1305
+ `Nothing was searched: "order" must be "oldest" or "newest", or left out for "oldest"; it's ${shown(input.order)}.`,
1306
+ );
1235
1307
  }
1236
1308
  if (input.wholeWord && input.regex) {
1237
- throw new Error('"wholeWord" applies to substring search only; in regex mode write \\b around the pattern instead.');
1309
+ throw new Error(
1310
+ 'Nothing was searched: "wholeWord" applies to a search for text, not a regular expression. In a regular ' +
1311
+ 'expression, write \\b around the pattern instead, or set "regex" to false.',
1312
+ );
1238
1313
  }
1239
1314
  const authorFilter = buildAuthorFilter(input.author, input.excludeAuthor);
1240
1315
 
@@ -1245,12 +1320,18 @@ export class HistoryModule implements Module {
1245
1320
  // than here.
1246
1321
  if (input.regex) {
1247
1322
  try {
1248
- new RegExp(input.query, flags);
1323
+ new RegExp(query, flags);
1249
1324
  } catch (error) {
1250
- throw new Error(`Invalid regex "${input.query}": ${error instanceof Error ? error.message : String(error)}`);
1325
+ // V8's message restates the pattern; the reason alone follows it.
1326
+ const reason = (error instanceof Error ? error.message : String(error))
1327
+ .replace(/^Invalid regular expression: \/.*\/[a-z]*: /s, '');
1328
+ throw new Error(
1329
+ `Nothing was searched: "query" ${shown(query)} isn't a valid regular expression (${reason}). ` +
1330
+ 'Fix the pattern, or set "regex" to false to search for the text as written.',
1331
+ );
1251
1332
  }
1252
1333
  }
1253
- const needle = caseSensitive ? input.query : input.query.toLowerCase();
1334
+ const needle = caseSensitive ? query : query.toLowerCase();
1254
1335
 
1255
1336
  const cm = this.cm as ContextManager;
1256
1337
  // The candidate window is the oldest (order:"oldest") or newest
@@ -1259,7 +1340,7 @@ export class HistoryModule implements Module {
1259
1340
  // detected up front rather than
1260
1341
  // silently scanning maxScan candidates and returning as if that were the
1261
1342
  // whole window. See the tool description's `truncated` contract.
1262
- if (maxScan === 0) throw new Error('"maxScan" must be at least 1 — a zero-message scan can never make progress.');
1343
+ //
1263
1344
  // Continuation: `skipSequences` names what the previous call already
1264
1345
  // scanned at exactly the resume bound's millisecond (the bound is
1265
1346
  // inclusive). Identified by sequence range rather than a count, so a
@@ -1269,7 +1350,11 @@ export class HistoryModule implements Module {
1269
1350
  const skipRange = parseSkipSequences(input.skipSequences);
1270
1351
  const edgeMs = order === 'oldest' ? fromMs : toMs;
1271
1352
  if (skipRange && edgeMs === undefined) {
1272
- throw new Error(`"skipSequences" only applies together with "${order === 'oldest' ? 'from' : 'to'}" — pass the whole \`resume\` object back.`);
1353
+ throw new Error(
1354
+ `Nothing was searched: "skipSequences" goes with the "${order === 'oldest' ? 'from' : 'to'}" from the same ` +
1355
+ '"resume". Copy the fields of the previous response\'s "resume" into the call, or leave "skipSequences" ' +
1356
+ 'out for a new search.',
1357
+ );
1273
1358
  }
1274
1359
  const isSkipped = (m: StoredMessage) =>
1275
1360
  !!skipRange && m.timestamp.getTime() === edgeMs && m.sequence >= skipRange[0] && m.sequence <= skipRange[1];
@@ -1350,7 +1435,7 @@ export class HistoryModule implements Module {
1350
1435
  // through a worker thread instead, which can be forcibly terminated
1351
1436
  // on a deadline. See search-regex-worker.ts's header for the full
1352
1437
  // rationale.
1353
- const { matches, scanned } = await this.searchWithRegexWorker(candidates, input.query, flags, limit);
1438
+ const { matches, scanned } = await this.searchWithRegexWorker(candidates, query, flags, limit);
1354
1439
  return { success: true, data: { scanned, ...poolInfo, truncated, ...resumeInfo(scanned), matches } };
1355
1440
  }
1356
1441
 
@@ -1416,22 +1501,32 @@ export class HistoryModule implements Module {
1416
1501
  const timer = setTimeout(() => {
1417
1502
  reject(
1418
1503
  new Error(
1419
- `search timed out after ${SEARCH_REGEX_TIMEOUT_MS}ms while matching regex "${pattern}" against ` +
1420
- `up to ${texts.length} candidate(s) — the pattern may be catastrophically slow (exponential ` +
1421
- 'backtracking) against this data; try a simpler pattern, a literal substring search ' +
1422
- '(regex:false), or a smaller maxScan.',
1504
+ `The search stopped, and returned nothing: matching the regular expression ${shown(pattern)} ` +
1505
+ `against up to ${texts.length} ${texts.length === 1 ? 'message' : 'messages'} took longer than ` +
1506
+ `${SEARCH_REGEX_TIMEOUT_MS} ms, as a ` +
1507
+ 'pattern that backtracks heavily can. Try a simpler pattern, set "regex" to false to search for the ' +
1508
+ 'text as written, or lower "maxScan".',
1423
1509
  ),
1424
1510
  );
1425
1511
  }, SEARCH_REGEX_TIMEOUT_MS);
1426
1512
  timer.unref?.();
1427
1513
  worker.once('message', (msg: SearchWorkerMessage) => {
1428
1514
  clearTimeout(timer);
1429
- if (msg.type === 'error') reject(new Error(msg.error));
1430
- else resolve({ matches: msg.matches, scanned: msg.scanned });
1515
+ if (msg.type === 'error') {
1516
+ reject(new Error(
1517
+ `The search stopped, and returned nothing: matching the regular expression ${shown(pattern)} ` +
1518
+ `failed (${msg.error}). Try a simpler pattern, or set "regex" to false to search for the text as written.`,
1519
+ ));
1520
+ } else {
1521
+ resolve({ matches: msg.matches, scanned: msg.scanned });
1522
+ }
1431
1523
  });
1432
1524
  worker.once('error', (err) => {
1433
1525
  clearTimeout(timer);
1434
- reject(err);
1526
+ reject(new Error(
1527
+ `The search stopped, and returned nothing: the worker matching the regular expression ${shown(pattern)} ` +
1528
+ `failed (${err.message}). Set "regex" to false to search for the text as written.`,
1529
+ ));
1435
1530
  });
1436
1531
  },
1437
1532
  );
@@ -1463,30 +1558,54 @@ export class HistoryModule implements Module {
1463
1558
 
1464
1559
  private async handleSemanticSearch(input: SemanticSearchInput): Promise<ToolResult> {
1465
1560
  if (!this.semanticCfg || !this.semanticClient) {
1466
- throw new Error('semantic_search is not configured for this resident (no embed-service in the recipe).');
1561
+ throw new Error(
1562
+ "Nothing was searched: this host has no semantic index set up, so history--semantic_search isn't available " +
1563
+ "here, and calling it again won't change that. history--search finds messages by their words.",
1564
+ );
1565
+ }
1566
+ if (typeof input.query !== 'string' || !input.query.trim()) {
1567
+ throw new Error(
1568
+ 'Nothing was searched: "query" must be a sentence describing what you\'re looking for; ' +
1569
+ `${input.query === undefined ? 'this call gave none' : `it's ${shown(input.query)}`}.`,
1570
+ );
1467
1571
  }
1468
- if (typeof input.query !== 'string' || !input.query.trim()) throw new Error('query must be a non-empty string');
1469
- const limit = clampCount(input.limit, SEMANTIC_DEFAULT_LIMIT, SEMANTIC_MAX_LIMIT, 'limit');
1470
- if (limit < 1) throw new Error('limit must be at least 1');
1572
+ // A search has to return at least 1 hit.
1573
+ const limit = clampCount(input.limit, SEMANTIC_DEFAULT_LIMIT, SEMANTIC_MAX_LIMIT, 'limit', 'searched', 1);
1471
1574
  const fromMs = parseIsoDate(input.from, 'from');
1472
1575
  const toMs = parseIsoDate(input.to, 'to');
1473
- if (fromMs !== undefined && toMs !== undefined && fromMs > toMs) throw new Error('`from` must not be after `to`');
1576
+ assertOrderedRange(fromMs, toMs, input.from, input.to);
1474
1577
  const channelId = this.resolveChannel(input.channelId);
1475
1578
  // Summaries carry no channel, so a channel filter on a summaries-only
1476
1579
  // search can only ever match nothing. Say so instead of returning [].
1477
1580
  const wantsSummariesOnly = input.level !== undefined || input.kinds === 'summaries';
1478
1581
  if (channelId && wantsSummariesOnly) {
1479
- throw new Error('channelId cannot be combined with kinds="summaries" or level: summaries are not per-channel. Drop channelId, or search kinds="messages".');
1582
+ const picks = [
1583
+ ...(input.kinds === 'summaries' ? ['"kinds": "summaries"'] : []),
1584
+ ...(input.level !== undefined ? ['"level"'] : []),
1585
+ ];
1586
+ const fields = [...(input.kinds === 'summaries' ? ['kinds'] : []), ...(input.level !== undefined ? ['level'] : [])];
1587
+ throw new Error(
1588
+ `Nothing was searched: summaries aren't kept by channel, so "channelId" can't go with ${picks.join(' or ')}. ` +
1589
+ `Leave out "channelId" to search summaries, or leave out ${quotedList(fields, 'and')} to search that ` +
1590
+ 'channel\'s messages.',
1591
+ );
1480
1592
  }
1481
1593
  if (input.level !== undefined && input.kinds === 'messages') {
1482
- throw new Error('level applies to summaries only and cannot be combined with kinds="messages".');
1594
+ throw new Error(
1595
+ 'Nothing was searched: "level" picks summaries, and "kinds" is "messages", so the two can\'t go together. ' +
1596
+ 'Leave out "kinds" to search summaries of that level, or leave out "level" to search messages.',
1597
+ );
1483
1598
  }
1484
1599
  let kinds: string[] | undefined;
1485
1600
  if (wantsSummariesOnly) kinds = ['summary'];
1486
1601
  else if (input.kinds === 'messages' || channelId) kinds = ['message'];
1487
- if (input.level !== undefined && (!Number.isInteger(input.level) || input.level < 0)) throw new Error('level must be a non-negative integer');
1602
+ if (input.level !== undefined && (!Number.isInteger(input.level) || input.level < 0)) {
1603
+ throw new Error(`Nothing was searched: "level" must be a whole number, 0 or more, such as 1; it's ${shown(input.level)}.`);
1604
+ }
1488
1605
  if (input.minScore !== undefined && (typeof input.minScore !== 'number' || input.minScore < -1 || input.minScore > 1)) {
1489
- throw new Error('minScore must be a number in -1..1');
1606
+ throw new Error(
1607
+ `Nothing was searched: "minScore" must be a number from -1 to 1, or left out; it's ${shown(input.minScore)}.`,
1608
+ );
1490
1609
  }
1491
1610
 
1492
1611
  // Bounded catch-up so the newest messages are searchable; never block on a backlog.
@@ -1501,6 +1620,48 @@ export class HistoryModule implements Module {
1501
1620
  ts_to: toMs === undefined ? undefined : toMs / 1000,
1502
1621
  channel: channelId, kinds, level: input.level, min_score: input.minScore, snippet: SEMANTIC_SNIPPET_CHARS,
1503
1622
  });
1623
+ // The client says what went wrong with the service; what that means for
1624
+ // this call, and what the agent can do, is said here. The service's own
1625
+ // source isn't in this repository, so a 404's advice holds whether it
1626
+ // means this store's index doesn't exist yet or a route the service lacks.
1627
+ const failure = (e: unknown): { reason: string; advice: string; mayPass: boolean } => {
1628
+ const status = e instanceof EmbedServiceError ? e.status : null;
1629
+ const reason = e instanceof Error ? e.message : String(e);
1630
+ const later = {
1631
+ reason, mayPass: true,
1632
+ advice: 'You can try again later; if it keeps happening, ask whoever runs this host',
1633
+ };
1634
+ if (status === 404) {
1635
+ return {
1636
+ reason, mayPass: true,
1637
+ advice: "If this store's index doesn't exist yet, the first messages indexed create it, and a later call " +
1638
+ 'can work; if it keeps happening, ask whoever runs this host',
1639
+ };
1640
+ }
1641
+ if (status === 408 || status === 429 || (status !== null && status >= 500)) return later;
1642
+ // An answer without what a search reads: a page shown while the service
1643
+ // restarts clears, and an address that points at something else doesn't.
1644
+ if (status !== null && status >= 200 && status < 300) {
1645
+ return {
1646
+ reason, mayPass: true,
1647
+ advice: 'If the service was restarting, a later call can work; if its address points at something else, ' +
1648
+ "none will until whoever runs this host fixes the semantic index's settings",
1649
+ };
1650
+ }
1651
+ if (status !== null) {
1652
+ return {
1653
+ reason, mayPass: false,
1654
+ advice: "Calling it again the same way won't change that; if it keeps happening, ask whoever runs this host",
1655
+ };
1656
+ }
1657
+ // No answer: the service down or slow, or the connection dropped, which
1658
+ // clears, or its address or certificate wrong, which doesn't.
1659
+ return {
1660
+ reason, mayPass: true,
1661
+ advice: 'If the service is down or slow, or the connection dropped, a later call can work; if its address ' +
1662
+ "or certificate is wrong, none will until whoever runs this host fixes the semantic index's settings",
1663
+ };
1664
+ };
1504
1665
  // The remote index is append-only and knows nothing about branches: a
1505
1666
  // message removed from the agent's reach by /undo, /checkout, /restore or
1506
1667
  // /newtopic keeps its id (a new message gets a fresh one) and stays
@@ -1525,7 +1686,7 @@ export class HistoryModule implements Module {
1525
1686
  if (msgId !== undefined) {
1526
1687
  const m = cm.getMessage(msgId);
1527
1688
  if (!m) return 'offBranch';
1528
- const text = messageIndexText(m, includePrivate).slice(0, this.semanticCfg!.maxChars ?? 32_000);
1689
+ const text = safeSlice(messageIndexText(m, includePrivate), 0, this.semanticCfg!.maxChars ?? 32_000);
1529
1690
  if (!text) return 'stale';
1530
1691
  currentText.set(h.id, text);
1531
1692
  return 'ok';
@@ -1534,11 +1695,21 @@ export class HistoryModule implements Module {
1534
1695
  return 'ok';
1535
1696
  };
1536
1697
  let k = limit;
1537
- let res = await runSearch(k);
1698
+ let res: SearchResponse;
1699
+ try {
1700
+ res = await runSearch(k);
1701
+ } catch (e) {
1702
+ const { reason, advice } = failure(e);
1703
+ throw new Error(`Nothing was searched: ${reason}. ${advice}. history--search finds messages by their words meanwhile.`);
1704
+ }
1538
1705
  let droppedOffBranch = 0;
1539
1706
  let droppedStale = 0;
1707
+ // Why searching again for more hits failed, when it did. The search
1708
+ // before it answered, so its hits on this branch are returned.
1709
+ let searchAgainError: string | null = null;
1540
1710
  let onBranch: typeof res.hits = [];
1541
- for (;;) {
1711
+ // The last answer's hits on this branch, checked as they are now.
1712
+ const check = (): void => {
1542
1713
  droppedOffBranch = 0; droppedStale = 0;
1543
1714
  onBranch = res.hits.filter((h) => {
1544
1715
  const v = keep(h);
@@ -1546,25 +1717,58 @@ export class HistoryModule implements Module {
1546
1717
  else if (v === 'stale') droppedStale++;
1547
1718
  return v === 'ok';
1548
1719
  });
1720
+ };
1721
+ for (;;) {
1722
+ check();
1549
1723
  if (onBranch.length >= limit || res.hits.length < k || k >= SEMANTIC_MAX_FETCH) break;
1550
1724
  k = Math.min(SEMANTIC_MAX_FETCH, k * 4);
1551
- res = await runSearch(k);
1725
+ try {
1726
+ res = await runSearch(k);
1727
+ } catch (e) {
1728
+ const { reason, mayPass } = failure(e);
1729
+ searchAgainError = `searching again for more hits failed: ${reason}. ` +
1730
+ (mayPass ? 'Calling again may bring more.' : "Calling again the same way won't change that.");
1731
+ // A message may have been edited, deleted or taken off this branch
1732
+ // while that search waited, so the hits are checked again.
1733
+ check();
1734
+ break;
1735
+ }
1552
1736
  }
1553
1737
  onBranch = onBranch.slice(0, limit);
1554
- const hits = onBranch.map((h) => ({
1555
- id: h.id,
1556
- /** Raw message id for a `msg:` hit (null for summaries) — pass it to extract({ aroundId }). */
1557
- messageId: h.id.startsWith('msg:') ? h.id.slice(4) : null,
1558
- kind: h.kind,
1559
- level: h.level,
1560
- score: h.score,
1561
- timestamp: h.ts === null ? null : new Date(h.ts * 1000).toISOString(),
1562
- channelId: h.channel,
1563
- participant: (h.meta as { participant?: unknown }).participant ?? null,
1564
- author: (h.meta as { author?: unknown }).author ?? null,
1565
- snippet: currentText.has(h.id) ? snippetOf(currentText.get(h.id)!) : (h.text ?? ''),
1566
- chars: currentText.has(h.id) ? currentText.get(h.id)!.length : h.chars,
1567
- }));
1738
+ // A ts that makes no date reads as no timestamp, as a null one does, and a
1739
+ // meta that isn't an object as no participant or author. The hit is still
1740
+ // returned, but the index holds data written wrong, so the host is told.
1741
+ // The index writes ts as a number of seconds, and text holding one reads
1742
+ // as main read it; nothing else makes a date, and reading it can't throw.
1743
+ const unread: string[] = [];
1744
+ const hits = onBranch.map((h) => {
1745
+ const ts: unknown = h.ts;
1746
+ const seconds = typeof ts === 'number' ? ts : typeof ts === 'string' && ts.trim() !== '' ? Number(ts) : NaN;
1747
+ const at = new Date(seconds * 1000);
1748
+ const timestamp = Number.isNaN(at.getTime()) ? null : at.toISOString();
1749
+ const meta = typeof h.meta === 'object' && h.meta !== null && !Array.isArray(h.meta)
1750
+ ? h.meta as { participant?: unknown; author?: unknown }
1751
+ : null;
1752
+ const missing = [...(h.ts !== null && timestamp === null ? ['ts'] : []), ...(meta === null ? ['meta'] : [])];
1753
+ if (missing.length > 0) unread.push(`${shown(h.id)} (${missing.join(' and ')})`);
1754
+ return {
1755
+ id: h.id,
1756
+ /** Raw message id for a `msg:` hit (null for summaries) — pass it to extract({ aroundId }). */
1757
+ messageId: h.id.startsWith('msg:') ? h.id.slice(4) : null,
1758
+ kind: h.kind,
1759
+ level: h.level,
1760
+ score: h.score,
1761
+ timestamp,
1762
+ channelId: h.channel,
1763
+ participant: meta?.participant ?? null,
1764
+ author: meta?.author ?? null,
1765
+ snippet: currentText.has(h.id) ? snippetOf(currentText.get(h.id)!) : (h.text ?? ''),
1766
+ chars: currentText.has(h.id) ? currentText.get(h.id)!.length : h.chars,
1767
+ };
1768
+ });
1769
+ if (unread.length > 0) {
1770
+ this.indexer?.warn(`a search returned hits whose ts or meta couldn't be read, without them: ${unread.join(', ')}`);
1771
+ }
1568
1772
  return {
1569
1773
  success: true,
1570
1774
  data: {
@@ -1576,8 +1780,10 @@ export class HistoryModule implements Module {
1576
1780
  lastError: this.indexer?.lastError ?? null,
1577
1781
  /** Hits the index returned for messages/summaries not on the current branch (undone, checked out past). */
1578
1782
  droppedOffBranch,
1579
- /** Hits for messages whose current text has nothing indexable (edited blank; the index delete is pending). */
1783
+ /** Hits for messages whose current text has nothing indexable (edited blank; the index keeps such a message until its delete goes through, and on a service without /delete it never does). */
1580
1784
  droppedStale,
1785
+ /** When too few hits were on this branch and searching again for more failed: why, and whether calling again can help. */
1786
+ searchAgainError,
1581
1787
  },
1582
1788
  timingMs: res.timing_ms,
1583
1789
  },
@@ -1589,7 +1795,7 @@ export class HistoryModule implements Module {
1589
1795
  const fromMs = parseIsoDate(input.from, 'from');
1590
1796
  const toMs = parseIsoDate(input.to, 'to');
1591
1797
  assertOrderedRange(fromMs, toMs, input.from, input.to);
1592
- const limit = clampCount(input.limit, OVERVIEW_DEFAULT_LIMIT, OVERVIEW_MAX_LIMIT, 'limit');
1798
+ const limit = clampCount(input.limit, OVERVIEW_DEFAULT_LIMIT, OVERVIEW_MAX_LIMIT, 'limit', 'read');
1593
1799
  const cm = this.cm as ContextManager;
1594
1800
 
1595
1801
  let entries: TimeRangeSummaryEntry[];
@@ -1959,6 +2165,12 @@ export class HistoryModule implements Module {
1959
2165
  // Helpers
1960
2166
  // ============================================================================
1961
2167
 
2168
+ // The checks here (and resolveChannel) are shared by several tools. Their
2169
+ // refusals open with "Nothing was read", or for clampCount with the outcome
2170
+ // its caller names, and "Nothing was read" holds because every history tool
2171
+ // only reads. A tool that changes something should give its refusals an
2172
+ // opener of its own rather than reach these.
2173
+
1962
2174
  /** Parse an ISO 8601 date string to Unix ms. Throws a clear error (not a
1963
2175
  * crash) on an unparseable string; returns undefined for an omitted field
1964
2176
  * (open-ended bound). */
@@ -1966,7 +2178,10 @@ function parseIsoDate(value: string | undefined, field: string): number | undefi
1966
2178
  if (value === undefined) return undefined;
1967
2179
  const ms = Date.parse(value);
1968
2180
  if (Number.isNaN(ms)) {
1969
- throw new Error(`Invalid ISO 8601 date for "${field}": ${JSON.stringify(value)}`);
2181
+ throw new Error(
2182
+ `Nothing was read: "${field}" is ${shown(value)}, which isn't an ISO 8601 date. Write it like 2026-03-01 or ` +
2183
+ '2026-03-01T09:00:00Z.',
2184
+ );
1970
2185
  }
1971
2186
  return ms;
1972
2187
  }
@@ -1980,7 +2195,10 @@ function parseIsoDate(value: string | undefined, field: string): number | undefi
1980
2195
  */
1981
2196
  function assertOrderedRange(fromMs: number | undefined, toMs: number | undefined, fromRaw: string | undefined, toRaw: string | undefined): void {
1982
2197
  if (fromMs !== undefined && toMs !== undefined && fromMs > toMs) {
1983
- throw new Error(`"from" (${fromRaw}) must not be after "to" (${toRaw}).`);
2198
+ throw new Error(
2199
+ `Nothing was read: "from" (${shown(fromRaw)}) is after "to" (${shown(toRaw)}), so the range holds nothing. Swap them, ` +
2200
+ 'or correct the one that\'s wrong.',
2201
+ );
1984
2202
  }
1985
2203
  }
1986
2204
 
@@ -1992,24 +2210,54 @@ function assertOrderedRange(fromMs: number | undefined, toMs: number | undefined
1992
2210
  * "hard cap" and reach a native chronicle call expecting an unsigned
1993
2211
  * pagination argument (observed: `extract({limit:-1})` returning every
1994
2212
  * message in the store instead of being capped). Rejects (rather than
1995
- * silently clamping) anything that isn't a finite non-negative integer, so
1996
- * a caller mistake is surfaced as a clean tool error instead of silently
1997
- * doing something other than what was asked.
2213
+ * silently clamping) anything that isn't a whole number of at least `min`,
2214
+ * so a caller mistake is surfaced as a clean tool error instead of silently
2215
+ * doing something other than what was asked. The refusal opens with what
2216
+ * didn't happen for the calling tool (`outcome`), and gives the range and
2217
+ * the default.
1998
2218
  */
1999
- function clampCount(value: number | undefined, def: number, max: number, field: string): number {
2219
+ function clampCount(
2220
+ value: number | undefined, def: number, max: number, field: string, outcome: 'read' | 'searched', min = 0,
2221
+ ): number {
2000
2222
  if (value === undefined) return def;
2001
- if (typeof value !== 'number' || !Number.isFinite(value)) {
2002
- throw new Error(`"${field}" must be a finite number, got ${JSON.stringify(value)}.`);
2003
- }
2004
- if (!Number.isInteger(value)) {
2005
- throw new Error(`"${field}" must be an integer, got ${value}.`);
2006
- }
2007
- if (value < 0) {
2008
- throw new Error(`"${field}" must be >= 0, got ${value}.`);
2223
+ if (typeof value !== 'number' || !Number.isInteger(value) || value < min) {
2224
+ throw new Error(
2225
+ `Nothing was ${outcome}: "${field}" is ${shown(value)}, which isn't a whole number from ${min} to ${max}. `
2226
+ + `Pass one, or leave it out for ${def}.`,
2227
+ );
2009
2228
  }
2010
2229
  return Math.min(value, max);
2011
2230
  }
2012
2231
 
2232
+ /**
2233
+ * A value a message names, the call's or the store's, as the message quotes
2234
+ * it: its JSON, or what String() gives where JSON can't give the value itself,
2235
+ * cut to 80 UTF-16 units, or one fewer where the 80th would split a surrogate
2236
+ * pair: the message stays in the agent's context, and half a pair isn't
2237
+ * well-formed text. Inside a list or an object, JSON writes Infinity and NaN
2238
+ * as null, and so does the quote. A value the message gives the agent to send
2239
+ * is quoted whole instead, since a cut one couldn't be sent.
2240
+ */
2241
+ function shown(value: unknown): string {
2242
+ let text: string | undefined;
2243
+ // A value JSON can't give, such as a cycle, or Infinity or NaN as the whole
2244
+ // value, which JSON.stringify gives as null, is shown as String() shows it.
2245
+ if (typeof value !== 'number' || Number.isFinite(value)) {
2246
+ try { text = JSON.stringify(value); } catch { /* String() below */ }
2247
+ }
2248
+ text ??= String(value);
2249
+ return text.length <= 80 ? text : `${safeSlice(text, 0, 80)}…`;
2250
+ }
2251
+
2252
+ /**
2253
+ * Names in prose, each quoted whole: "a"; "a" and "b"; "a", "b" and "c" (or
2254
+ * "or"). They're the tools' own field names, or names the agent can send.
2255
+ */
2256
+ function quotedList(names: readonly string[], joiner: 'and' | 'or'): string {
2257
+ const quoted = names.map((name) => JSON.stringify(name));
2258
+ return quoted.length <= 1 ? quoted.join('') : `${quoted.slice(0, -1).join(', ')} ${joiner} ${quoted[quoted.length - 1]}`;
2259
+ }
2260
+
2013
2261
  /** channelId lives at metadata.external.channelId — same path context-manager's
2014
2262
  * own native index reads from (message-store.ts's CHANNEL_FIELD). Metadata's
2015
2263
  * index-signature type means this needs an explicit narrow, same as
@@ -2219,7 +2467,9 @@ function codePointBefore(s: string, i: number): string | undefined {
2219
2467
  }
2220
2468
 
2221
2469
  /**
2222
- * First occurrence of `needle` in `text`. With `wholeWord`, an occurrence
2470
+ * First occurrence of `needle` in `text`, as the units of `text` it covers.
2471
+ * Without `caseSensitive`, `needle` is lower case already, and is found in
2472
+ * `text.toLowerCase()`. With `wholeWord`, an occurrence
2223
2473
  * only counts when it isn't glued to a letter/digit/underscore on either
2224
2474
  * side (Unicode-aware, so Cyrillic words work too) — "mission" no longer
2225
2475
  * hits inside "uncommissioned". Word-ness is checked only on the sides
@@ -2228,6 +2478,13 @@ function codePointBefore(s: string, i: number): string | undefined {
2228
2478
  */
2229
2479
  function matchSubstring(text: string, needle: string, caseSensitive: boolean, wholeWord = false): MatchHit | null {
2230
2480
  const haystack = caseSensitive ? text : text.toLowerCase();
2481
+ const hit = firstOccurrence(haystack, needle, wholeWord);
2482
+ // Lowering can lengthen a character, as it does `İ` (one unit to two). Then
2483
+ // the lowered text's units aren't the text's, and the snippet cuts the text.
2484
+ return hit !== null && haystack.length !== text.length ? inTextUnits(text, hit) : hit;
2485
+ }
2486
+
2487
+ function firstOccurrence(haystack: string, needle: string, wholeWord: boolean): MatchHit | null {
2231
2488
  if (!wholeWord) {
2232
2489
  const index = haystack.indexOf(needle);
2233
2490
  return index === -1 ? null : { index, length: needle.length };
@@ -2245,6 +2502,26 @@ function matchSubstring(text: string, needle: string, caseSensitive: boolean, wh
2245
2502
  }
2246
2503
  }
2247
2504
 
2505
+ /**
2506
+ * The units of `text` that a hit in `text.toLowerCase()` covers. Each code
2507
+ * point of `text` takes as many units there as its own lower case has, so a
2508
+ * hit that starts or ends inside a character's lower case takes in the whole
2509
+ * character.
2510
+ */
2511
+ function inTextUnits(text: string, hit: MatchHit): MatchHit {
2512
+ const end = hit.index + hit.length;
2513
+ let start = -1;
2514
+ let lowered = 0;
2515
+ let at = 0;
2516
+ while (at < text.length && lowered < end) {
2517
+ const cp = text.codePointAt(at)!;
2518
+ lowered += String.fromCodePoint(cp).toLowerCase().length;
2519
+ if (start === -1 && lowered > hit.index) start = at;
2520
+ at += cp > 0xffff ? 2 : 1;
2521
+ }
2522
+ return { index: start, length: at - start };
2523
+ }
2524
+
2248
2525
  // ============================================================================
2249
2526
  // author filtering
2250
2527
  // ============================================================================
@@ -2270,7 +2547,11 @@ function parseSkipSequences(v: unknown): [number, number] | null {
2270
2547
  !v.every((x) => typeof x === 'number' && Number.isInteger(x) && x >= 0) ||
2271
2548
  v[0] > v[1]
2272
2549
  ) {
2273
- throw new Error('"skipSequences" must be [lo, hi], two non-negative integers with lo <= hi — copy it from `resume`.');
2550
+ throw new Error(
2551
+ 'Nothing was searched: "skipSequences" must be two whole numbers, 0 or more, the first no larger than the ' +
2552
+ `second, as a search's "resume" gives it; it's ${shown(v)}. Copy it from "resume" with its "from" or "to", ` +
2553
+ 'or leave it out for a new search.',
2554
+ );
2274
2555
  }
2275
2556
  return [v[0], v[1]];
2276
2557
  }
@@ -2295,11 +2576,20 @@ function normalizeAuthorSpec(spec: string): string {
2295
2576
  function toAuthorSet(spec: AuthorSpec | undefined, field: string): Set<string> | null {
2296
2577
  if (spec === undefined) return null;
2297
2578
  const list = Array.isArray(spec) ? spec : [spec];
2298
- if (list.some((s) => typeof s !== 'string')) {
2299
- throw new Error(`"${field}" must be a string or an array of strings.`);
2579
+ const odd = list.findIndex((s) => typeof s !== 'string');
2580
+ if (odd !== -1) {
2581
+ throw new Error(
2582
+ `Nothing was read: "${field}" must list names or ids as text, such as ["alice", "1234"], and ` +
2583
+ `${shown(list[odd])} isn't text.`,
2584
+ );
2300
2585
  }
2301
2586
  const set = new Set(list.map(normalizeAuthorSpec).filter((s) => s.length > 0));
2302
- if (set.size === 0) throw new Error(`"${field}" must name at least one author.`);
2587
+ if (set.size === 0) {
2588
+ throw new Error(
2589
+ `Nothing was read: "${field}" names no one: ${list.length > 0 ? 'its entries are blank' : 'the list is empty'}. ` +
2590
+ `List at least one name or id, such as ["alice"], or leave "${field}" out.`,
2591
+ );
2592
+ }
2303
2593
  return set;
2304
2594
  }
2305
2595
 
@@ -2331,12 +2621,12 @@ function buildAuthorFilter(include: AuthorSpec | undefined, exclude: AuthorSpec
2331
2621
  /** ~SNIPPET_CONTEXT_CHARS of surrounding context on each side of a match, or
2332
2622
  * the first ~SNIPPET_FALLBACK_CHARS of content when no meaningful match
2333
2623
  * position is given (kept simple on purpose — this is a readability aid,
2334
- * not a highlighting engine). */
2624
+ * not a highlighting engine). Neither end splits a surrogate pair. */
2335
2625
  function snippetAround(text: string, index: number, length: number): string {
2336
- if (index < 0) return text.slice(0, SNIPPET_FALLBACK_CHARS);
2626
+ if (index < 0) return safeSlice(text, 0, SNIPPET_FALLBACK_CHARS);
2337
2627
  const start = Math.max(0, index - SNIPPET_CONTEXT_CHARS);
2338
2628
  const end = Math.min(text.length, index + length + SNIPPET_CONTEXT_CHARS);
2339
2629
  const prefix = start > 0 ? '…' : '';
2340
2630
  const suffix = end < text.length ? '…' : '';
2341
- return prefix + text.slice(start, end) + suffix;
2631
+ return prefix + safeSlice(text, start, end) + suffix;
2342
2632
  }