@corbet-labs/ccht 0.2.2 → 0.2.3

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 (388) hide show
  1. package/LICENSE +307 -0
  2. package/LICENSE.md +30 -0
  3. package/LICENSES/GPL-3.0-only.txt +232 -0
  4. package/LICENSES/LGPL-3.0-linking-exception.txt +16 -0
  5. package/LICENSES/LGPL-3.0-only WITH LGPL-3.0-linking-exception.txt +16 -0
  6. package/LICENSES/LGPL-3.0-only.txt +304 -0
  7. package/LICENSES/dependencies/agent-client-protocol-2.1.0/LICENSE +191 -0
  8. package/LICENSES/dependencies/agent-client-protocol-derive-2.1.0/LICENSE +191 -0
  9. package/LICENSES/dependencies/agent-client-protocol-schema-1.7.0/LICENSE +190 -0
  10. package/LICENSES/dependencies/android_system_properties-0.1.6/LICENSE-APACHE +13 -0
  11. package/LICENSES/dependencies/android_system_properties-0.1.6/LICENSE-MIT +20 -0
  12. package/LICENSES/dependencies/anyhow-1.0.104/LICENSE-APACHE +176 -0
  13. package/LICENSES/dependencies/anyhow-1.0.104/LICENSE-MIT +23 -0
  14. package/LICENSES/dependencies/async-channel-2.5.0/LICENSE-APACHE +201 -0
  15. package/LICENSES/dependencies/async-channel-2.5.0/LICENSE-MIT +23 -0
  16. package/LICENSES/dependencies/async-io-2.6.0/LICENSE-APACHE +201 -0
  17. package/LICENSES/dependencies/async-io-2.6.0/LICENSE-MIT +23 -0
  18. package/LICENSES/dependencies/async-lock-3.4.2/LICENSE-APACHE +201 -0
  19. package/LICENSES/dependencies/async-lock-3.4.2/LICENSE-MIT +23 -0
  20. package/LICENSES/dependencies/async-process-2.5.0/LICENSE-APACHE +201 -0
  21. package/LICENSES/dependencies/async-process-2.5.0/LICENSE-MIT +23 -0
  22. package/LICENSES/dependencies/async-signal-0.2.14/LICENSE-APACHE +201 -0
  23. package/LICENSES/dependencies/async-signal-0.2.14/LICENSE-MIT +23 -0
  24. package/LICENSES/dependencies/async-task-4.7.1/LICENSE-APACHE +201 -0
  25. package/LICENSES/dependencies/async-task-4.7.1/LICENSE-MIT +23 -0
  26. package/LICENSES/dependencies/atomic-waker-1.1.2/LICENSE-APACHE +201 -0
  27. package/LICENSES/dependencies/atomic-waker-1.1.2/LICENSE-MIT +23 -0
  28. package/LICENSES/dependencies/atomic-waker-1.1.2/LICENSE-THIRD-PARTY +45 -0
  29. package/LICENSES/dependencies/autocfg-1.5.1/LICENSE-APACHE +201 -0
  30. package/LICENSES/dependencies/autocfg-1.5.1/LICENSE-MIT +25 -0
  31. package/LICENSES/dependencies/base64-0.22.1/LICENSE-APACHE +201 -0
  32. package/LICENSES/dependencies/base64-0.22.1/LICENSE-MIT +21 -0
  33. package/LICENSES/dependencies/base64-0.23.1/LICENSE-APACHE +201 -0
  34. package/LICENSES/dependencies/base64-0.23.1/LICENSE-MIT +21 -0
  35. package/LICENSES/dependencies/bitflags-1.3.2/LICENSE-APACHE +201 -0
  36. package/LICENSES/dependencies/bitflags-1.3.2/LICENSE-MIT +25 -0
  37. package/LICENSES/dependencies/bitflags-2.13.2/LICENSE-APACHE +201 -0
  38. package/LICENSES/dependencies/bitflags-2.13.2/LICENSE-MIT +25 -0
  39. package/LICENSES/dependencies/blocking-1.7.0/LICENSE-APACHE +201 -0
  40. package/LICENSES/dependencies/blocking-1.7.0/LICENSE-MIT +23 -0
  41. package/LICENSES/dependencies/bs58-0.5.1/LICENSE-APACHE +201 -0
  42. package/LICENSES/dependencies/bs58-0.5.1/LICENSE-MIT +20 -0
  43. package/LICENSES/dependencies/bumpalo-3.20.3/LICENSE-APACHE +201 -0
  44. package/LICENSES/dependencies/bumpalo-3.20.3/LICENSE-MIT +25 -0
  45. package/LICENSES/dependencies/bytes-1.12.1/LICENSE +25 -0
  46. package/LICENSES/dependencies/cc-1.4.5/LICENSE-APACHE +201 -0
  47. package/LICENSES/dependencies/cc-1.4.5/LICENSE-MIT +25 -0
  48. package/LICENSES/dependencies/cfg-if-1.0.4/LICENSE-APACHE +201 -0
  49. package/LICENSES/dependencies/cfg-if-1.0.4/LICENSE-MIT +25 -0
  50. package/LICENSES/dependencies/chrono-0.4.45/LICENSE.txt +240 -0
  51. package/LICENSES/dependencies/concurrent-queue-2.5.0/LICENSE-APACHE +201 -0
  52. package/LICENSES/dependencies/concurrent-queue-2.5.0/LICENSE-MIT +23 -0
  53. package/LICENSES/dependencies/convert_case-0.10.0/LICENSE +21 -0
  54. package/LICENSES/dependencies/core-foundation-sys-0.8.7/LICENSE-APACHE +201 -0
  55. package/LICENSES/dependencies/core-foundation-sys-0.8.7/LICENSE-MIT +25 -0
  56. package/LICENSES/dependencies/crossbeam-deque-0.8.8/LICENSE-APACHE +201 -0
  57. package/LICENSES/dependencies/crossbeam-deque-0.8.8/LICENSE-MIT +27 -0
  58. package/LICENSES/dependencies/crossbeam-epoch-0.9.21/LICENSE-APACHE +201 -0
  59. package/LICENSES/dependencies/crossbeam-epoch-0.9.21/LICENSE-MIT +27 -0
  60. package/LICENSES/dependencies/crossbeam-utils-0.8.23/LICENSE-APACHE +201 -0
  61. package/LICENSES/dependencies/crossbeam-utils-0.8.23/LICENSE-MIT +27 -0
  62. package/LICENSES/dependencies/darling-0.24.1/LICENSE +21 -0
  63. package/LICENSES/dependencies/darling_core-0.24.1/LICENSE +21 -0
  64. package/LICENSES/dependencies/darling_macro-0.24.1/LICENSE +21 -0
  65. package/LICENSES/dependencies/defmt-1.1.1/LICENSE-APACHE +201 -0
  66. package/LICENSES/dependencies/defmt-1.1.1/LICENSE-MIT +25 -0
  67. package/LICENSES/dependencies/defmt-macros-1.1.1/LICENSE-APACHE +201 -0
  68. package/LICENSES/dependencies/defmt-macros-1.1.1/LICENSE-MIT +25 -0
  69. package/LICENSES/dependencies/defmt-parser-1.0.0/LICENSE-APACHE +201 -0
  70. package/LICENSES/dependencies/defmt-parser-1.0.0/LICENSE-MIT +25 -0
  71. package/LICENSES/dependencies/deranged-0.5.8/LICENSE-Apache +202 -0
  72. package/LICENSES/dependencies/deranged-0.5.8/LICENSE-MIT +19 -0
  73. package/LICENSES/dependencies/derive_more-2.1.1/LICENSE +21 -0
  74. package/LICENSES/dependencies/derive_more-impl-2.1.1/LICENSE +21 -0
  75. package/LICENSES/dependencies/dyn-clone-1.0.20/LICENSE-APACHE +176 -0
  76. package/LICENSES/dependencies/dyn-clone-1.0.20/LICENSE-MIT +23 -0
  77. package/LICENSES/dependencies/either-1.18.0/LICENSE-APACHE +201 -0
  78. package/LICENSES/dependencies/either-1.18.0/LICENSE-MIT +25 -0
  79. package/LICENSES/dependencies/equivalent-1.0.2/LICENSE-APACHE +201 -0
  80. package/LICENSES/dependencies/equivalent-1.0.2/LICENSE-MIT +25 -0
  81. package/LICENSES/dependencies/errno-0.3.14/LICENSE-APACHE +201 -0
  82. package/LICENSES/dependencies/errno-0.3.14/LICENSE-MIT +25 -0
  83. package/LICENSES/dependencies/event-listener-5.4.2/LICENSE-APACHE +201 -0
  84. package/LICENSES/dependencies/event-listener-5.4.2/LICENSE-MIT +23 -0
  85. package/LICENSES/dependencies/event-listener-strategy-0.5.4/LICENSE-APACHE +201 -0
  86. package/LICENSES/dependencies/event-listener-strategy-0.5.4/LICENSE-MIT +23 -0
  87. package/LICENSES/dependencies/fallible-iterator-0.3.0/LICENSE-APACHE +202 -0
  88. package/LICENSES/dependencies/fallible-iterator-0.3.0/LICENSE-MIT +19 -0
  89. package/LICENSES/dependencies/fastrand-2.5.0/LICENSE-APACHE +201 -0
  90. package/LICENSES/dependencies/fastrand-2.5.0/LICENSE-MIT +23 -0
  91. package/LICENSES/dependencies/find-msvc-tools-0.1.12/LICENSE-APACHE +201 -0
  92. package/LICENSES/dependencies/find-msvc-tools-0.1.12/LICENSE-MIT +25 -0
  93. package/LICENSES/dependencies/fixedbitset-0.5.7/LICENSE-APACHE +201 -0
  94. package/LICENSES/dependencies/fixedbitset-0.5.7/LICENSE-MIT +25 -0
  95. package/LICENSES/dependencies/foldhash-0.2.0/LICENSE +19 -0
  96. package/LICENSES/dependencies/futures-0.3.34/LICENSE-APACHE +202 -0
  97. package/LICENSES/dependencies/futures-0.3.34/LICENSE-MIT +26 -0
  98. package/LICENSES/dependencies/futures-channel-0.3.34/LICENSE-APACHE +202 -0
  99. package/LICENSES/dependencies/futures-channel-0.3.34/LICENSE-MIT +26 -0
  100. package/LICENSES/dependencies/futures-concurrency-7.7.1/LICENSE-APACHE +190 -0
  101. package/LICENSES/dependencies/futures-concurrency-7.7.1/LICENSE-MIT +21 -0
  102. package/LICENSES/dependencies/futures-core-0.3.34/LICENSE-APACHE +202 -0
  103. package/LICENSES/dependencies/futures-core-0.3.34/LICENSE-MIT +26 -0
  104. package/LICENSES/dependencies/futures-executor-0.3.34/LICENSE-APACHE +202 -0
  105. package/LICENSES/dependencies/futures-executor-0.3.34/LICENSE-MIT +26 -0
  106. package/LICENSES/dependencies/futures-io-0.3.34/LICENSE-APACHE +202 -0
  107. package/LICENSES/dependencies/futures-io-0.3.34/LICENSE-MIT +26 -0
  108. package/LICENSES/dependencies/futures-lite-2.6.1/LICENSE-APACHE +201 -0
  109. package/LICENSES/dependencies/futures-lite-2.6.1/LICENSE-MIT +23 -0
  110. package/LICENSES/dependencies/futures-lite-2.6.1/LICENSE-THIRD-PARTY +45 -0
  111. package/LICENSES/dependencies/futures-macro-0.3.34/LICENSE-APACHE +202 -0
  112. package/LICENSES/dependencies/futures-macro-0.3.34/LICENSE-MIT +26 -0
  113. package/LICENSES/dependencies/futures-sink-0.3.34/LICENSE-APACHE +202 -0
  114. package/LICENSES/dependencies/futures-sink-0.3.34/LICENSE-MIT +26 -0
  115. package/LICENSES/dependencies/futures-task-0.3.34/LICENSE-APACHE +202 -0
  116. package/LICENSES/dependencies/futures-task-0.3.34/LICENSE-MIT +26 -0
  117. package/LICENSES/dependencies/futures-util-0.3.34/LICENSE-APACHE +202 -0
  118. package/LICENSES/dependencies/futures-util-0.3.34/LICENSE-MIT +26 -0
  119. package/LICENSES/dependencies/getrandom-0.4.3/LICENSE-APACHE +201 -0
  120. package/LICENSES/dependencies/getrandom-0.4.3/LICENSE-MIT +26 -0
  121. package/LICENSES/dependencies/gimli-0.32.3/LICENSE-APACHE +201 -0
  122. package/LICENSES/dependencies/gimli-0.32.3/LICENSE-MIT +25 -0
  123. package/LICENSES/dependencies/hashbrown-0.12.3/LICENSE-APACHE +201 -0
  124. package/LICENSES/dependencies/hashbrown-0.12.3/LICENSE-MIT +25 -0
  125. package/LICENSES/dependencies/hashbrown-0.16.1/LICENSE-APACHE +201 -0
  126. package/LICENSES/dependencies/hashbrown-0.16.1/LICENSE-MIT +25 -0
  127. package/LICENSES/dependencies/hashbrown-0.17.1/LICENSE-APACHE +201 -0
  128. package/LICENSES/dependencies/hashbrown-0.17.1/LICENSE-MIT +25 -0
  129. package/LICENSES/dependencies/heck-0.5.0/LICENSE-APACHE +201 -0
  130. package/LICENSES/dependencies/heck-0.5.0/LICENSE-MIT +25 -0
  131. package/LICENSES/dependencies/hermit-abi-0.5.3/LICENSE-APACHE +201 -0
  132. package/LICENSES/dependencies/hermit-abi-0.5.3/LICENSE-MIT +23 -0
  133. package/LICENSES/dependencies/hex-0.4.3/LICENSE-APACHE +202 -0
  134. package/LICENSES/dependencies/hex-0.4.3/LICENSE-MIT +20 -0
  135. package/LICENSES/dependencies/iana-time-zone-0.1.65/LICENSE-APACHE +201 -0
  136. package/LICENSES/dependencies/iana-time-zone-0.1.65/LICENSE-MIT +25 -0
  137. package/LICENSES/dependencies/iana-time-zone-haiku-0.1.2/LICENSE-APACHE +201 -0
  138. package/LICENSES/dependencies/iana-time-zone-haiku-0.1.2/LICENSE-MIT +25 -0
  139. package/LICENSES/dependencies/id-arena-2.3.0/LICENSE-APACHE +201 -0
  140. package/LICENSES/dependencies/id-arena-2.3.0/LICENSE-MIT +25 -0
  141. package/LICENSES/dependencies/ident_case-1.0.1/LICENSE +19 -0
  142. package/LICENSES/dependencies/indexmap-1.9.3/LICENSE-APACHE +201 -0
  143. package/LICENSES/dependencies/indexmap-1.9.3/LICENSE-MIT +25 -0
  144. package/LICENSES/dependencies/indexmap-2.14.2/LICENSE-APACHE +201 -0
  145. package/LICENSES/dependencies/indexmap-2.14.2/LICENSE-MIT +25 -0
  146. package/LICENSES/dependencies/itoa-1.0.18/LICENSE-APACHE +176 -0
  147. package/LICENSES/dependencies/itoa-1.0.18/LICENSE-MIT +23 -0
  148. package/LICENSES/dependencies/jiff-0.2.35/COPYING +3 -0
  149. package/LICENSES/dependencies/jiff-0.2.35/LICENSE-MIT +21 -0
  150. package/LICENSES/dependencies/jiff-0.2.35/UNLICENSE +24 -0
  151. package/LICENSES/dependencies/jiff-core-0.1.0/COPYING +3 -0
  152. package/LICENSES/dependencies/jiff-core-0.1.0/LICENSE-MIT +21 -0
  153. package/LICENSES/dependencies/jiff-core-0.1.0/UNLICENSE +24 -0
  154. package/LICENSES/dependencies/jiff-static-0.2.35/COPYING +3 -0
  155. package/LICENSES/dependencies/jiff-static-0.2.35/LICENSE-MIT +21 -0
  156. package/LICENSES/dependencies/jiff-static-0.2.35/UNLICENSE +24 -0
  157. package/LICENSES/dependencies/jiff-tzdb-0.1.8/COPYING +3 -0
  158. package/LICENSES/dependencies/jiff-tzdb-0.1.8/LICENSE-MIT +21 -0
  159. package/LICENSES/dependencies/jiff-tzdb-0.1.8/UNLICENSE +24 -0
  160. package/LICENSES/dependencies/jiff-tzdb-platform-0.1.3/COPYING +3 -0
  161. package/LICENSES/dependencies/jiff-tzdb-platform-0.1.3/LICENSE-MIT +21 -0
  162. package/LICENSES/dependencies/jiff-tzdb-platform-0.1.3/UNLICENSE +24 -0
  163. package/LICENSES/dependencies/js-sys-0.3.105/LICENSE-APACHE +201 -0
  164. package/LICENSES/dependencies/js-sys-0.3.105/LICENSE-MIT +25 -0
  165. package/LICENSES/dependencies/leb128-0.2.7/LICENSE-APACHE +201 -0
  166. package/LICENSES/dependencies/leb128-0.2.7/LICENSE-MIT +25 -0
  167. package/LICENSES/dependencies/leb128fmt-0.1.0/LICENSE-APACHE +201 -0
  168. package/LICENSES/dependencies/leb128fmt-0.1.0/LICENSE-MIT +23 -0
  169. package/LICENSES/dependencies/libc-0.2.189/LICENSE-APACHE +176 -0
  170. package/LICENSES/dependencies/libc-0.2.189/LICENSE-MIT +25 -0
  171. package/LICENSES/dependencies/linux-raw-sys-0.12.1/COPYRIGHT +29 -0
  172. package/LICENSES/dependencies/linux-raw-sys-0.12.1/LICENSE-APACHE +201 -0
  173. package/LICENSES/dependencies/linux-raw-sys-0.12.1/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
  174. package/LICENSES/dependencies/linux-raw-sys-0.12.1/LICENSE-MIT +23 -0
  175. package/LICENSES/dependencies/log-0.4.34/LICENSE-APACHE +201 -0
  176. package/LICENSES/dependencies/log-0.4.34/LICENSE-MIT +25 -0
  177. package/LICENSES/dependencies/memchr-2.8.3/COPYING +3 -0
  178. package/LICENSES/dependencies/memchr-2.8.3/LICENSE-MIT +21 -0
  179. package/LICENSES/dependencies/memchr-2.8.3/UNLICENSE +24 -0
  180. package/LICENSES/dependencies/mio-1.2.3/LICENSE +19 -0
  181. package/LICENSES/dependencies/num-conv-0.2.2/LICENSE-Apache +177 -0
  182. package/LICENSES/dependencies/num-conv-0.2.2/LICENSE-MIT +19 -0
  183. package/LICENSES/dependencies/num-traits-0.2.19/LICENSE-APACHE +201 -0
  184. package/LICENSES/dependencies/num-traits-0.2.19/LICENSE-MIT +25 -0
  185. package/LICENSES/dependencies/once_cell-1.21.4/LICENSE-APACHE +201 -0
  186. package/LICENSES/dependencies/once_cell-1.21.4/LICENSE-MIT +23 -0
  187. package/LICENSES/dependencies/parking-2.2.1/LICENSE-APACHE +201 -0
  188. package/LICENSES/dependencies/parking-2.2.1/LICENSE-MIT +23 -0
  189. package/LICENSES/dependencies/parking-2.2.1/LICENSE-THIRD-PARTY +9 -0
  190. package/LICENSES/dependencies/pin-project-1.1.13/LICENSE-APACHE +177 -0
  191. package/LICENSES/dependencies/pin-project-1.1.13/LICENSE-MIT +23 -0
  192. package/LICENSES/dependencies/pin-project-internal-1.1.13/LICENSE-APACHE +177 -0
  193. package/LICENSES/dependencies/pin-project-internal-1.1.13/LICENSE-MIT +23 -0
  194. package/LICENSES/dependencies/pin-project-lite-0.2.17/LICENSE-APACHE +177 -0
  195. package/LICENSES/dependencies/pin-project-lite-0.2.17/LICENSE-MIT +23 -0
  196. package/LICENSES/dependencies/piper-0.2.5/LICENSE-APACHE +201 -0
  197. package/LICENSES/dependencies/piper-0.2.5/LICENSE-MIT +23 -0
  198. package/LICENSES/dependencies/polling-3.11.0/LICENSE-APACHE +201 -0
  199. package/LICENSES/dependencies/polling-3.11.0/LICENSE-MIT +23 -0
  200. package/LICENSES/dependencies/portable-atomic-1.15.0/LICENSE-APACHE +177 -0
  201. package/LICENSES/dependencies/portable-atomic-1.15.0/LICENSE-MIT +23 -0
  202. package/LICENSES/dependencies/portable-atomic-util-0.2.8/LICENSE-APACHE +177 -0
  203. package/LICENSES/dependencies/portable-atomic-util-0.2.8/LICENSE-MIT +23 -0
  204. package/LICENSES/dependencies/powerfmt-0.2.0/LICENSE-Apache +202 -0
  205. package/LICENSES/dependencies/powerfmt-0.2.0/LICENSE-MIT +19 -0
  206. package/LICENSES/dependencies/proc-macro2-1.0.107/LICENSE-APACHE +176 -0
  207. package/LICENSES/dependencies/proc-macro2-1.0.107/LICENSE-MIT +23 -0
  208. package/LICENSES/dependencies/quote-1.0.47/LICENSE-APACHE +176 -0
  209. package/LICENSES/dependencies/quote-1.0.47/LICENSE-MIT +23 -0
  210. package/LICENSES/dependencies/r-efi-6.0.0/AUTHORS +75 -0
  211. package/LICENSES/dependencies/rayon-1.12.0/LICENSE-APACHE +201 -0
  212. package/LICENSES/dependencies/rayon-1.12.0/LICENSE-MIT +25 -0
  213. package/LICENSES/dependencies/rayon-core-1.13.0/LICENSE-APACHE +201 -0
  214. package/LICENSES/dependencies/rayon-core-1.13.0/LICENSE-MIT +25 -0
  215. package/LICENSES/dependencies/ref-cast-1.0.27/LICENSE-APACHE +176 -0
  216. package/LICENSES/dependencies/ref-cast-1.0.27/LICENSE-MIT +23 -0
  217. package/LICENSES/dependencies/ref-cast-impl-1.0.27/LICENSE-APACHE +176 -0
  218. package/LICENSES/dependencies/ref-cast-impl-1.0.27/LICENSE-MIT +23 -0
  219. package/LICENSES/dependencies/rustc-demangle-0.1.28/LICENSE-APACHE +201 -0
  220. package/LICENSES/dependencies/rustc-demangle-0.1.28/LICENSE-MIT +25 -0
  221. package/LICENSES/dependencies/rustc-hash-2.1.3/LICENSE-APACHE +176 -0
  222. package/LICENSES/dependencies/rustc-hash-2.1.3/LICENSE-MIT +23 -0
  223. package/LICENSES/dependencies/rustc_version-0.4.1/LICENSE-APACHE +201 -0
  224. package/LICENSES/dependencies/rustc_version-0.4.1/LICENSE-MIT +25 -0
  225. package/LICENSES/dependencies/rustix-1.1.4/COPYRIGHT +29 -0
  226. package/LICENSES/dependencies/rustix-1.1.4/LICENSE-APACHE +201 -0
  227. package/LICENSES/dependencies/rustix-1.1.4/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
  228. package/LICENSES/dependencies/rustix-1.1.4/LICENSE-MIT +23 -0
  229. package/LICENSES/dependencies/rustversion-1.0.23/LICENSE-APACHE +176 -0
  230. package/LICENSES/dependencies/rustversion-1.0.23/LICENSE-MIT +23 -0
  231. package/LICENSES/dependencies/schemars-0.9.0/LICENSE +21 -0
  232. package/LICENSES/dependencies/schemars-1.2.2/LICENSE +21 -0
  233. package/LICENSES/dependencies/schemars_derive-1.2.2/LICENSE +21 -0
  234. package/LICENSES/dependencies/semver-1.0.28/LICENSE-APACHE +176 -0
  235. package/LICENSES/dependencies/semver-1.0.28/LICENSE-MIT +23 -0
  236. package/LICENSES/dependencies/serde-1.0.229/LICENSE-APACHE +176 -0
  237. package/LICENSES/dependencies/serde-1.0.229/LICENSE-MIT +23 -0
  238. package/LICENSES/dependencies/serde_core-1.0.229/LICENSE-APACHE +176 -0
  239. package/LICENSES/dependencies/serde_core-1.0.229/LICENSE-MIT +23 -0
  240. package/LICENSES/dependencies/serde_derive-1.0.229/LICENSE-APACHE +176 -0
  241. package/LICENSES/dependencies/serde_derive-1.0.229/LICENSE-MIT +23 -0
  242. package/LICENSES/dependencies/serde_derive_internals-0.30.0/LICENSE-APACHE +176 -0
  243. package/LICENSES/dependencies/serde_derive_internals-0.30.0/LICENSE-MIT +23 -0
  244. package/LICENSES/dependencies/serde_json-1.0.151/LICENSE-APACHE +176 -0
  245. package/LICENSES/dependencies/serde_json-1.0.151/LICENSE-MIT +23 -0
  246. package/LICENSES/dependencies/serde_with-3.23.0/LICENSE-APACHE +201 -0
  247. package/LICENSES/dependencies/serde_with-3.23.0/LICENSE-MIT +25 -0
  248. package/LICENSES/dependencies/serde_with_macros-3.23.0/LICENSE-APACHE +201 -0
  249. package/LICENSES/dependencies/serde_with_macros-3.23.0/LICENSE-MIT +25 -0
  250. package/LICENSES/dependencies/shell-words-1.1.1/LICENSE-APACHE +201 -0
  251. package/LICENSES/dependencies/shell-words-1.1.1/LICENSE-MIT +25 -0
  252. package/LICENSES/dependencies/shlex-2.0.1/LICENSE-APACHE +13 -0
  253. package/LICENSES/dependencies/shlex-2.0.1/LICENSE-MIT +21 -0
  254. package/LICENSES/dependencies/signal-hook-registry-1.4.8/LICENSE-APACHE +201 -0
  255. package/LICENSES/dependencies/signal-hook-registry-1.4.8/LICENSE-MIT +25 -0
  256. package/LICENSES/dependencies/slab-0.4.12/LICENSE +25 -0
  257. package/LICENSES/dependencies/smallvec-1.16.0/LICENSE-APACHE +201 -0
  258. package/LICENSES/dependencies/smallvec-1.16.0/LICENSE-MIT +25 -0
  259. package/LICENSES/dependencies/socket2-0.6.5/LICENSE-APACHE +201 -0
  260. package/LICENSES/dependencies/socket2-0.6.5/LICENSE-MIT +25 -0
  261. package/LICENSES/dependencies/stable_deref_trait-1.2.1/LICENSE-APACHE +201 -0
  262. package/LICENSES/dependencies/stable_deref_trait-1.2.1/LICENSE-MIT +25 -0
  263. package/LICENSES/dependencies/strsim-0.11.1/LICENSE +23 -0
  264. package/LICENSES/dependencies/strum-0.28.0/LICENSE +21 -0
  265. package/LICENSES/dependencies/strum_macros-0.28.0/LICENSE +21 -0
  266. package/LICENSES/dependencies/syn-2.0.119/LICENSE-APACHE +176 -0
  267. package/LICENSES/dependencies/syn-2.0.119/LICENSE-MIT +23 -0
  268. package/LICENSES/dependencies/syn-3.0.5/LICENSE-APACHE +176 -0
  269. package/LICENSES/dependencies/syn-3.0.5/LICENSE-MIT +23 -0
  270. package/LICENSES/dependencies/thiserror-2.0.20/LICENSE-APACHE +176 -0
  271. package/LICENSES/dependencies/thiserror-2.0.20/LICENSE-MIT +23 -0
  272. package/LICENSES/dependencies/thiserror-impl-2.0.20/LICENSE-APACHE +176 -0
  273. package/LICENSES/dependencies/thiserror-impl-2.0.20/LICENSE-MIT +23 -0
  274. package/LICENSES/dependencies/time-0.3.55/LICENSE-Apache +177 -0
  275. package/LICENSES/dependencies/time-0.3.55/LICENSE-MIT +19 -0
  276. package/LICENSES/dependencies/time-core-0.1.9/LICENSE-Apache +177 -0
  277. package/LICENSES/dependencies/time-core-0.1.9/LICENSE-MIT +19 -0
  278. package/LICENSES/dependencies/time-macros-0.2.32/LICENSE-Apache +177 -0
  279. package/LICENSES/dependencies/time-macros-0.2.32/LICENSE-MIT +19 -0
  280. package/LICENSES/dependencies/tinyvec-1.13.2/LICENSE-APACHE.md +202 -0
  281. package/LICENSES/dependencies/tinyvec-1.13.2/LICENSE-MIT.md +5 -0
  282. package/LICENSES/dependencies/tinyvec-1.13.2/LICENSE-ZLIB.md +11 -0
  283. package/LICENSES/dependencies/tinyvec_macros-0.1.1/LICENSE-APACHE.md +202 -0
  284. package/LICENSES/dependencies/tinyvec_macros-0.1.1/LICENSE-MIT.md +21 -0
  285. package/LICENSES/dependencies/tinyvec_macros-0.1.1/LICENSE-ZLIB.md +20 -0
  286. package/LICENSES/dependencies/tokio-1.53.1/LICENSE +21 -0
  287. package/LICENSES/dependencies/tokio-macros-2.7.2/LICENSE +22 -0
  288. package/LICENSES/dependencies/tracing-0.1.44/LICENSE +25 -0
  289. package/LICENSES/dependencies/tracing-attributes-0.1.31/LICENSE +25 -0
  290. package/LICENSES/dependencies/tracing-core-0.1.36/LICENSE +25 -0
  291. package/LICENSES/dependencies/unicode-ident-1.0.24/LICENSE-APACHE +176 -0
  292. package/LICENSES/dependencies/unicode-ident-1.0.24/LICENSE-MIT +23 -0
  293. package/LICENSES/dependencies/unicode-ident-1.0.24/LICENSE-UNICODE +39 -0
  294. package/LICENSES/dependencies/unicode-segmentation-1.13.3/COPYRIGHT +7 -0
  295. package/LICENSES/dependencies/unicode-segmentation-1.13.3/LICENSE-APACHE +201 -0
  296. package/LICENSES/dependencies/unicode-segmentation-1.13.3/LICENSE-MIT +25 -0
  297. package/LICENSES/dependencies/unicode-xid-0.2.6/COPYRIGHT +7 -0
  298. package/LICENSES/dependencies/unicode-xid-0.2.6/LICENSE-APACHE +201 -0
  299. package/LICENSES/dependencies/unicode-xid-0.2.6/LICENSE-MIT +25 -0
  300. package/LICENSES/dependencies/uuid-1.26.1/LICENSE-APACHE +201 -0
  301. package/LICENSES/dependencies/uuid-1.26.1/LICENSE-MIT +26 -0
  302. package/LICENSES/dependencies/walrus-0.26.5/LICENSE-APACHE +201 -0
  303. package/LICENSES/dependencies/walrus-0.26.5/LICENSE-MIT +25 -0
  304. package/LICENSES/dependencies/walrus-macro-0.26.0/LICENSE-APACHE +201 -0
  305. package/LICENSES/dependencies/walrus-macro-0.26.0/LICENSE-MIT +25 -0
  306. package/LICENSES/dependencies/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-APACHE +201 -0
  307. package/LICENSES/dependencies/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
  308. package/LICENSES/dependencies/wasi-0.11.1+wasi-snapshot-preview1/LICENSE-MIT +23 -0
  309. package/LICENSES/dependencies/wasm-bindgen-0.2.128/LICENSE-APACHE +201 -0
  310. package/LICENSES/dependencies/wasm-bindgen-0.2.128/LICENSE-MIT +25 -0
  311. package/LICENSES/dependencies/wasm-bindgen-cli-support-0.2.128/LICENSE-APACHE +201 -0
  312. package/LICENSES/dependencies/wasm-bindgen-cli-support-0.2.128/LICENSE-MIT +25 -0
  313. package/LICENSES/dependencies/wasm-bindgen-macro-0.2.128/LICENSE-APACHE +201 -0
  314. package/LICENSES/dependencies/wasm-bindgen-macro-0.2.128/LICENSE-MIT +25 -0
  315. package/LICENSES/dependencies/wasm-bindgen-macro-support-0.2.128/LICENSE-APACHE +201 -0
  316. package/LICENSES/dependencies/wasm-bindgen-macro-support-0.2.128/LICENSE-MIT +25 -0
  317. package/LICENSES/dependencies/wasm-bindgen-shared-0.2.128/LICENSE-APACHE +201 -0
  318. package/LICENSES/dependencies/wasm-bindgen-shared-0.2.128/LICENSE-MIT +25 -0
  319. package/LICENSES/dependencies/wasm-encoder-0.245.1/LICENSE-APACHE +201 -0
  320. package/LICENSES/dependencies/wasm-encoder-0.245.1/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
  321. package/LICENSES/dependencies/wasm-encoder-0.245.1/LICENSE-MIT +23 -0
  322. package/LICENSES/dependencies/wasmparser-0.245.1/LICENSE-APACHE +201 -0
  323. package/LICENSES/dependencies/wasmparser-0.245.1/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
  324. package/LICENSES/dependencies/wasmparser-0.245.1/LICENSE-MIT +23 -0
  325. package/LICENSES/dependencies/windows-core-0.62.2/license-apache-2.0 +201 -0
  326. package/LICENSES/dependencies/windows-core-0.62.2/license-mit +21 -0
  327. package/LICENSES/dependencies/windows-implement-0.60.2/license-apache-2.0 +201 -0
  328. package/LICENSES/dependencies/windows-implement-0.60.2/license-mit +21 -0
  329. package/LICENSES/dependencies/windows-interface-0.59.3/license-apache-2.0 +201 -0
  330. package/LICENSES/dependencies/windows-interface-0.59.3/license-mit +21 -0
  331. package/LICENSES/dependencies/windows-link-0.2.1/license-apache-2.0 +201 -0
  332. package/LICENSES/dependencies/windows-link-0.2.1/license-mit +21 -0
  333. package/LICENSES/dependencies/windows-result-0.4.1/license-apache-2.0 +201 -0
  334. package/LICENSES/dependencies/windows-result-0.4.1/license-mit +21 -0
  335. package/LICENSES/dependencies/windows-strings-0.5.1/license-apache-2.0 +201 -0
  336. package/LICENSES/dependencies/windows-strings-0.5.1/license-mit +21 -0
  337. package/LICENSES/dependencies/windows-sys-0.61.2/license-apache-2.0 +201 -0
  338. package/LICENSES/dependencies/windows-sys-0.61.2/license-mit +21 -0
  339. package/LICENSES/dependencies/zmij-1.0.23/LICENSE-MIT +23 -0
  340. package/THIRD-PARTY.md +62 -0
  341. package/package.json +37 -9
  342. package/source/.cargo/config.toml +5 -0
  343. package/source/.ci/upstream-notices/PROVENANCE.json +198 -0
  344. package/source/.ci/upstream-notices/agent-client-protocol-2.1.0/LICENSE +191 -0
  345. package/source/.ci/upstream-notices/agent-client-protocol-derive-2.1.0/LICENSE +191 -0
  346. package/source/.ci/upstream-notices/agent-client-protocol-schema-1.7.0/LICENSE +190 -0
  347. package/source/.ci/upstream-notices/defmt-parser-1.0.0/LICENSE-APACHE +201 -0
  348. package/source/.ci/upstream-notices/defmt-parser-1.0.0/LICENSE-MIT +25 -0
  349. package/source/.ci/upstream-notices/r-efi-6.0.0/AUTHORS +75 -0
  350. package/source/.ci/upstream-notices/wasm-encoder-0.245.1/LICENSE-APACHE +201 -0
  351. package/source/.ci/upstream-notices/wasm-encoder-0.245.1/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
  352. package/source/.ci/upstream-notices/wasm-encoder-0.245.1/LICENSE-MIT +23 -0
  353. package/source/.ci/upstream-notices/wasmparser-0.245.1/LICENSE-APACHE +201 -0
  354. package/source/.ci/upstream-notices/wasmparser-0.245.1/LICENSE-Apache-2.0_WITH_LLVM-exception +220 -0
  355. package/source/.ci/upstream-notices/wasmparser-0.245.1/LICENSE-MIT +23 -0
  356. package/source/.ci/wasm-bundle/Cargo.toml +10 -0
  357. package/source/.ci/wasm-bundle/src/main.rs +17 -0
  358. package/source/CHANGELOG.md +85 -0
  359. package/source/Cargo.lock +1630 -0
  360. package/source/Cargo.toml +48 -0
  361. package/source/LICENSE +307 -0
  362. package/source/LICENSE.md +30 -0
  363. package/source/LICENSES/GPL-3.0-only.txt +232 -0
  364. package/source/LICENSES/LGPL-3.0-linking-exception.txt +16 -0
  365. package/source/LICENSES/LGPL-3.0-only WITH LGPL-3.0-linking-exception.txt +16 -0
  366. package/source/LICENSES/LGPL-3.0-only.txt +304 -0
  367. package/source/README.md +289 -0
  368. package/source/THIRD-PARTY.md +62 -0
  369. package/source/dependencies.tar.gz +0 -0
  370. package/source/examples/native.rs +45 -0
  371. package/source/src/auth.rs +435 -0
  372. package/source/src/configuration.rs +140 -0
  373. package/source/src/conversation.rs +594 -0
  374. package/source/src/dock.rs +564 -0
  375. package/source/src/lib.rs +39 -0
  376. package/source/src/native/client.rs +401 -0
  377. package/source/src/native/drivers/codex.rs +506 -0
  378. package/source/src/native/drivers/mod.rs +305 -0
  379. package/source/src/native/drivers/opencode.rs +531 -0
  380. package/source/src/native/env.rs +264 -0
  381. package/source/src/native/fixture.py +191 -0
  382. package/source/src/native/mod.rs +257 -0
  383. package/source/src/native/pool.rs +440 -0
  384. package/source/src/native/session.rs +609 -0
  385. package/source/src/native/tests.rs +667 -0
  386. package/source/src/transport.rs +330 -0
  387. package/source/src/web.rs +35 -0
  388. package/wasm/ccht_bg.wasm +0 -0
@@ -0,0 +1,257 @@
1
+ //! Native ACP sessions driven by the upstream Apache-2.0 Rust SDK.
2
+ //!
3
+ //! The SDK owns process spawning, protocol dispatch, session routing, and process
4
+ //! cleanup. Applications own agent installation, subscription login, workspace
5
+ //! isolation, and authorization policy. Denying permission requests is not a
6
+ //! sandbox: an agent can have tools that do not ask the client for permission.
7
+
8
+ mod client;
9
+ pub mod drivers;
10
+ mod env;
11
+ pub mod pool;
12
+ mod session;
13
+
14
+ #[cfg(test)]
15
+ mod tests;
16
+
17
+ pub use client::NativeClient;
18
+ pub use env::EnvProfile;
19
+ pub use pool::{PoolEvent, SessionKey, SessionPool};
20
+ pub use session::{NativeSession, SessionHandle};
21
+
22
+ use std::collections::BTreeMap;
23
+ use std::path::PathBuf;
24
+ use std::time::Duration;
25
+
26
+ use crate::acp::{McpServer, RequestPermissionOutcome};
27
+
28
+ /// An explicitly installed ACP executable; no implicit shell or installer is used.
29
+ #[derive(Clone)]
30
+ pub struct AgentCommand {
31
+ /// Executable path or name resolved by the operating system.
32
+ pub program: PathBuf,
33
+ /// Arguments passed directly to the executable.
34
+ pub args: Vec<String>,
35
+ /// Additional child environment. Values are excluded from debug output.
36
+ pub env: BTreeMap<String, String>,
37
+ }
38
+
39
+ impl AgentCommand {
40
+ /// Select an existing executable without arguments or environment overrides.
41
+ pub fn new(program: impl Into<PathBuf>) -> Self {
42
+ Self {
43
+ program: program.into(),
44
+ args: Vec::new(),
45
+ env: BTreeMap::new(),
46
+ }
47
+ }
48
+
49
+ /// Set arguments without shell parsing.
50
+ pub fn args(mut self, args: impl IntoIterator<Item = impl Into<String>>) -> Self {
51
+ self.args = args.into_iter().map(Into::into).collect();
52
+ self
53
+ }
54
+
55
+ /// Start in a directory through an installed Linux or macOS `env` launcher.
56
+ ///
57
+ /// This changes the process startup directory, independently of ACP session
58
+ /// `cwd`. The SDK still owns the process and its cleanup. Linux resolves
59
+ /// `env` through the caller's PATH and requires GNU `--chdir`; macOS uses
60
+ /// `/usr/bin/env -C`. An unsupported launcher fails before executing the
61
+ /// agent. No shell parsing is involved. Windows has no implementation here.
62
+ pub fn with_working_directory(self, cwd: impl AsRef<std::path::Path>) -> Result<Self> {
63
+ if !cfg!(any(target_os = "linux", target_os = "macos")) {
64
+ return Err(NativeError::Unsupported(
65
+ "a startup-directory launcher on this platform",
66
+ ));
67
+ }
68
+ let cwd = cwd.as_ref();
69
+ if !cwd.is_absolute() || !cwd.is_dir() {
70
+ return Err(NativeError::InvalidOptions(
71
+ "startup cwd must be an existing absolute directory",
72
+ ));
73
+ }
74
+ let directory = cwd
75
+ .to_str()
76
+ .ok_or(NativeError::InvalidOptions("startup cwd must be UTF-8"))?;
77
+ let program = self
78
+ .program
79
+ .to_str()
80
+ .ok_or(NativeError::InvalidOptions("executable must be UTF-8"))?;
81
+ let (launcher, mut args) = if cfg!(target_os = "macos") {
82
+ (
83
+ "/usr/bin/env",
84
+ vec!["-C".into(), directory.into(), "--".into(), program.into()],
85
+ )
86
+ } else {
87
+ (
88
+ "env",
89
+ vec![format!("--chdir={directory}"), "--".into(), program.into()],
90
+ )
91
+ };
92
+ args.extend(self.args);
93
+ Ok(Self {
94
+ program: launcher.into(),
95
+ args,
96
+ env: self.env,
97
+ })
98
+ }
99
+ }
100
+
101
+ impl std::fmt::Debug for AgentCommand {
102
+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103
+ f.debug_struct("AgentCommand")
104
+ .field("program", &self.program)
105
+ .field("argument_count", &self.args.len())
106
+ .field("environment_keys", &self.env.keys().collect::<Vec<_>>())
107
+ .finish()
108
+ }
109
+ }
110
+
111
+ /// How requests that reach the client are handled; this does not sandbox an agent.
112
+ #[derive(Debug, Clone, Copy, Default, PartialEq, Eq)]
113
+ pub enum PermissionPolicy {
114
+ /// Cancel every permission request without granting authority.
115
+ #[default]
116
+ Deny,
117
+ /// Emit a permission event and await an explicit, valid response.
118
+ Ask,
119
+ }
120
+
121
+ /// A permission response uses ACP's exact selected-option or cancellation type.
122
+ pub type PermissionDecision = RequestPermissionOutcome;
123
+
124
+ /// Resource limits and permission policy for a native connection.
125
+ #[derive(Debug, Clone)]
126
+ pub struct NativeOptions {
127
+ /// Limit for initialization, session creation/restoration, and configuration.
128
+ pub operation_timeout: Duration,
129
+ /// Maximum duration of a prompt, including permission waits.
130
+ pub prompt_timeout: Duration,
131
+ /// Maximum time an unanswered permission request may remain pending.
132
+ pub permission_timeout: Duration,
133
+ /// Grace period for cancellation and SDK connection shutdown.
134
+ pub shutdown_timeout: Duration,
135
+ /// Maximum queued application events per session.
136
+ pub event_capacity: usize,
137
+ /// Policy applied to every incoming permission request.
138
+ pub permissions: PermissionPolicy,
139
+ }
140
+
141
+ impl Default for NativeOptions {
142
+ fn default() -> Self {
143
+ Self {
144
+ operation_timeout: Duration::from_secs(30),
145
+ prompt_timeout: Duration::from_secs(600),
146
+ permission_timeout: Duration::from_secs(120),
147
+ shutdown_timeout: Duration::from_secs(5),
148
+ event_capacity: 256,
149
+ permissions: PermissionPolicy::Deny,
150
+ }
151
+ }
152
+ }
153
+
154
+ /// Application-owned workspace and optional agent session configuration.
155
+ #[derive(Debug, Clone)]
156
+ pub struct SessionOptions {
157
+ /// Absolute existing directory supplied to the agent; not an isolation boundary.
158
+ pub cwd: PathBuf,
159
+ /// Exact advertised model option value, or the agent's default when absent.
160
+ pub model: Option<String>,
161
+ /// Agent-advertised configuration values, applied in order after model selection.
162
+ pub configuration: Vec<(String, crate::acp::SessionConfigOptionValue)>,
163
+ /// Application-owned MCP server descriptors passed to the agent.
164
+ pub mcp_servers: Vec<McpServer>,
165
+ }
166
+
167
+ impl SessionOptions {
168
+ /// Use an explicit workspace and the agent's default model without MCP servers.
169
+ pub fn new(cwd: impl Into<PathBuf>) -> Self {
170
+ Self {
171
+ cwd: cwd.into(),
172
+ model: None,
173
+ configuration: Vec::new(),
174
+ mcp_servers: Vec::new(),
175
+ }
176
+ }
177
+ }
178
+
179
+ /// Native errors deliberately exclude raw agent messages, stderr, and error data.
180
+ #[derive(Debug, Clone, thiserror::Error, PartialEq, Eq)]
181
+ pub enum NativeError {
182
+ /// A Tokio runtime is required to drive the connection.
183
+ #[error("a Tokio runtime is required for native ACP sessions")]
184
+ RuntimeUnavailable,
185
+ /// Configuration is incomplete or inconsistent.
186
+ #[error("invalid native configuration: {0}")]
187
+ InvalidOptions(&'static str),
188
+ /// The operation exceeded its configured deadline.
189
+ #[error("native ACP operation timed out")]
190
+ Timeout,
191
+ /// The connection or session has closed.
192
+ #[error("native ACP connection or session is closed")]
193
+ Closed,
194
+ /// A session already has an active prompt.
195
+ #[error("this session already has an active prompt")]
196
+ Busy,
197
+ /// The agent did not advertise the requested operation or content type.
198
+ #[error("the agent does not support {0}")]
199
+ Unsupported(&'static str),
200
+ /// Agent-owned authentication is required; no alternate billing path is tried.
201
+ #[error("authenticate using the selected agent's native login, then reconnect")]
202
+ AuthenticationRequired,
203
+ /// A protocol error code, without possibly sensitive server text or data.
204
+ #[error("the agent returned ACP error {0}")]
205
+ Protocol(i32),
206
+ /// An application failed to keep up with the bounded event stream.
207
+ #[error("session event buffer is full; the connection was closed")]
208
+ EventBufferFull,
209
+ /// The session's sole event receiver has already been taken.
210
+ #[error("the session event stream has already been taken")]
211
+ EventsTaken,
212
+ /// No pending permission request has the supplied identifier.
213
+ #[error("permission request is absent, expired, or already answered")]
214
+ UnknownPermission,
215
+ /// A selected permission option was not offered by the agent.
216
+ #[error("the selected permission option was not offered")]
217
+ InvalidPermissionOption,
218
+ }
219
+
220
+ impl NativeError {
221
+ /// Stable category suitable for a product's structured error event.
222
+ pub fn code(&self) -> &'static str {
223
+ match self {
224
+ Self::RuntimeUnavailable => "runtime_unavailable",
225
+ Self::InvalidOptions(_) => "invalid_options",
226
+ Self::Timeout => "timeout",
227
+ Self::Closed => "closed",
228
+ Self::Busy => "busy",
229
+ Self::Unsupported(_) => "unsupported",
230
+ Self::AuthenticationRequired => "authentication_required",
231
+ Self::Protocol(_) => "protocol",
232
+ Self::EventBufferFull => "event_buffer_full",
233
+ Self::EventsTaken => "events_taken",
234
+ Self::UnknownPermission => "unknown_permission",
235
+ Self::InvalidPermissionOption => "invalid_permission_option",
236
+ }
237
+ }
238
+ }
239
+
240
+ impl From<agent_client_protocol::Error> for NativeError {
241
+ fn from(error: agent_client_protocol::Error) -> Self {
242
+ if error.code == crate::acp::ErrorCode::AuthRequired {
243
+ Self::AuthenticationRequired
244
+ } else {
245
+ Self::Protocol(error.code.into())
246
+ }
247
+ }
248
+ }
249
+
250
+ /// Result returned by native ACP operations.
251
+ pub type Result<T> = std::result::Result<T, NativeError>;
252
+
253
+ fn lock<T>(mutex: &std::sync::Mutex<T>) -> std::sync::MutexGuard<'_, T> {
254
+ mutex
255
+ .lock()
256
+ .unwrap_or_else(std::sync::PoisonError::into_inner)
257
+ }
@@ -0,0 +1,440 @@
1
+ //! Keyed pool of native agent sessions sharing one client.
2
+ //!
3
+ //! Workspaces, projects or roles map to pool keys; each key owns exactly one
4
+ //! session, created lazily on first use. Turns in different keys run in
5
+ //! parallel automatically, while each key serializes its own turns. Events
6
+ //! arrive tagged so late answers still land where they were asked.
7
+ //!
8
+ //! The pool owns session mechanics only: applications own prompts, permission
9
+ //! decisions, display and storage, and read conversation snapshots back for
10
+ //! replay or persistence.
11
+
12
+ use std::collections::HashMap;
13
+
14
+ use tokio::sync::mpsc;
15
+
16
+ use super::{
17
+ AgentCommand, NativeClient, NativeError, NativeOptions, SessionHandle, SessionOptions,
18
+ };
19
+ use crate::{Conversation, SessionEvent, WireEvent};
20
+
21
+ /// Application-chosen session key: a workspace, project or role name.
22
+ pub type SessionKey = String;
23
+
24
+ /// Streamed pool activity. Raw session events keep their full detail; the
25
+ /// application maps them to display, storage and permission decisions.
26
+ #[derive(Debug)]
27
+ pub enum PoolEvent {
28
+ /// A session event, tagged with its key.
29
+ Session {
30
+ /// Application-chosen session key.
31
+ key: SessionKey,
32
+ /// Raw session event with full detail (boxed: the common case
33
+ /// dwarfs the terminal variant).
34
+ event: Box<SessionEvent>,
35
+ },
36
+ /// The forwarder for a removed session drained. No further events follow.
37
+ Ended {
38
+ /// Application-chosen session key.
39
+ key: SessionKey,
40
+ },
41
+ }
42
+
43
+ struct Session {
44
+ handle: SessionHandle,
45
+ conv: Conversation,
46
+ busy: bool,
47
+ seq: u64,
48
+ }
49
+
50
+ struct Inner {
51
+ client: Option<NativeClient>,
52
+ sessions: HashMap<SessionKey, Session>,
53
+ app_events: mpsc::UnboundedSender<PoolEvent>,
54
+ fwd: Option<mpsc::UnboundedSender<(SessionKey, Option<SessionEvent>)>>,
55
+ }
56
+
57
+ /// Keyed native sessions over one shared client connection.
58
+ #[derive(Clone)]
59
+ pub struct SessionPool {
60
+ inner: std::sync::Arc<tokio::sync::Mutex<Inner>>,
61
+ }
62
+
63
+ impl SessionPool {
64
+ /// Connect the shared client and return the pool plus its event stream.
65
+ /// A failed connection still returns a pool: every operation then
66
+ /// reports the outage instead of panicking, so applications degrade
67
+ /// gracefully (e.g. keep local tools working while the agent is down).
68
+ pub async fn connect(
69
+ command: AgentCommand,
70
+ options: NativeOptions,
71
+ ) -> (Self, mpsc::UnboundedReceiver<PoolEvent>) {
72
+ let (app_tx, app_rx) = mpsc::unbounded_channel();
73
+ let (fwd_tx, fwd_rx) = mpsc::unbounded_channel::<(SessionKey, Option<SessionEvent>)>();
74
+ let client = NativeClient::connect(command, options).await.ok();
75
+ let pool = Self {
76
+ inner: std::sync::Arc::new(tokio::sync::Mutex::new(Inner {
77
+ client,
78
+ sessions: HashMap::new(),
79
+ app_events: app_tx,
80
+ fwd: Some(fwd_tx),
81
+ })),
82
+ };
83
+ let router = pool.clone();
84
+ tokio::spawn(async move {
85
+ Self::route(router, fwd_rx).await;
86
+ });
87
+ (pool, app_rx)
88
+ }
89
+
90
+ /// Send a prompt to a key's session, creating it from `new` on first
91
+ /// use. Resolves when the turn ends; stream events (including the
92
+ /// terminal `Completed`) arrive separately, tagged with the key.
93
+ /// A second prompt while the key is busy fails with [`NativeError::Busy`].
94
+ pub async fn prompt(
95
+ &self,
96
+ key: &str,
97
+ new: SessionOptions,
98
+ text: String,
99
+ ) -> Result<(), NativeError> {
100
+ let handle = {
101
+ let mut inner = self.inner.lock().await;
102
+ Self::ensure(&mut inner, key, new).await?;
103
+ let session = inner.sessions.get_mut(key).expect("just ensured");
104
+ if session.busy {
105
+ return Err(NativeError::Busy);
106
+ }
107
+ session.busy = true;
108
+ session.handle.clone()
109
+ };
110
+ let request_id = format!("ccht-pool-{key}");
111
+ let result = handle.prompt(crate::Prompt::text(request_id, text)).await;
112
+ // Busy clears on the terminal stream event; a failed call has none,
113
+ // so release the slot here to avoid wedging the key.
114
+ if result.is_err()
115
+ && let Some(session) = self.inner.lock().await.sessions.get_mut(key)
116
+ {
117
+ session.busy = false;
118
+ }
119
+ result.map(|_| ())
120
+ }
121
+
122
+ /// Cancel the active turn of one key. Missing keys are a no-op success.
123
+ pub async fn cancel(&self, key: &str) -> Result<(), NativeError> {
124
+ let handle = {
125
+ let inner = self.inner.lock().await;
126
+ match inner.sessions.get(key) {
127
+ Some(session) => session.handle.clone(),
128
+ None => return Ok(()),
129
+ }
130
+ };
131
+ handle.cancel().await
132
+ }
133
+
134
+ /// Switch the model of an existing session, reporting the agent's
135
+ /// resulting configuration. Unknown keys fail without creating sessions:
136
+ /// model selection needs no session of its own.
137
+ pub async fn set_model(
138
+ &self,
139
+ key: &str,
140
+ model: &str,
141
+ ) -> Result<crate::SessionConfiguration, NativeError> {
142
+ let handle = {
143
+ let inner = self.inner.lock().await;
144
+ match inner.sessions.get(key) {
145
+ Some(session) => session.handle.clone(),
146
+ None => return Err(NativeError::Closed),
147
+ }
148
+ };
149
+ handle.set_model(model).await
150
+ }
151
+
152
+ /// Answer a pending permission request of one key's session.
153
+ pub async fn respond_permission(
154
+ &self,
155
+ key: &str,
156
+ request_id: &str,
157
+ decision: crate::acp::RequestPermissionOutcome,
158
+ ) -> Result<(), NativeError> {
159
+ let handle = {
160
+ let inner = self.inner.lock().await;
161
+ match inner.sessions.get(key) {
162
+ Some(session) => session.handle.clone(),
163
+ None => return Err(NativeError::Closed),
164
+ }
165
+ };
166
+ handle.respond_permission(request_id, decision).await
167
+ }
168
+
169
+ /// Drop one key's session and end it agent-side. Unknown keys are a no-op.
170
+ pub async fn close(&self, key: &str) {
171
+ let handle = {
172
+ let mut inner = self.inner.lock().await;
173
+ inner.sessions.remove(key).map(|s| s.handle)
174
+ };
175
+ if let Some(handle) = handle {
176
+ let _ = handle.close().await;
177
+ }
178
+ }
179
+
180
+ /// Whether the key holds a live session with a turn in flight.
181
+ pub async fn is_busy(&self, key: &str) -> bool {
182
+ self.inner
183
+ .lock()
184
+ .await
185
+ .sessions
186
+ .get(key)
187
+ .is_some_and(|s| s.busy)
188
+ }
189
+
190
+ /// Whether the key holds a live session at all.
191
+ pub async fn has_session(&self, key: &str) -> bool {
192
+ self.inner.lock().await.sessions.contains_key(key)
193
+ }
194
+
195
+ /// A snapshot of one key's conversation for replay or persistence.
196
+ /// Applications own storage; the pool only lends the state.
197
+ pub async fn conversation(&self, key: &str) -> Option<Conversation> {
198
+ self.inner
199
+ .lock()
200
+ .await
201
+ .sessions
202
+ .get(key)
203
+ .map(|s| s.conv.clone())
204
+ }
205
+
206
+ /// Shut every session down and close the shared client.
207
+ pub async fn shutdown(&self) {
208
+ let mut inner = self.inner.lock().await;
209
+ let keys: Vec<String> = inner.sessions.keys().cloned().collect();
210
+ for key in keys {
211
+ if let Some(session) = inner.sessions.remove(&key) {
212
+ let _ = session.handle.close().await;
213
+ }
214
+ }
215
+ if let Some(client) = inner.client.take() {
216
+ let _ = client.close().await;
217
+ }
218
+ }
219
+
220
+ async fn ensure(inner: &mut Inner, key: &str, new: SessionOptions) -> Result<(), NativeError> {
221
+ if inner.sessions.contains_key(key) {
222
+ return Ok(());
223
+ }
224
+ let Some(client) = &inner.client else {
225
+ return Err(NativeError::Closed);
226
+ };
227
+ let mut session = client.new_session(new).await?;
228
+ let handle = session.handle();
229
+ let mut events = session.take_events().map_err(|_| NativeError::Closed)?;
230
+ drop(session);
231
+ let key_owned = key.to_string();
232
+ let fwd = inner
233
+ .fwd
234
+ .clone()
235
+ .expect("forwarder channel installed at connect");
236
+ tokio::spawn(async move {
237
+ while let Some(ev) = events.recv().await {
238
+ if fwd.send((key_owned.clone(), Some(ev))).is_err() {
239
+ break;
240
+ }
241
+ }
242
+ let _ = fwd.send((key_owned, None));
243
+ });
244
+ inner.sessions.insert(
245
+ key.to_string(),
246
+ Session {
247
+ handle,
248
+ conv: Conversation::new(format!("pool-{key}")),
249
+ busy: false,
250
+ seq: 0,
251
+ },
252
+ );
253
+ Ok(())
254
+ }
255
+
256
+ /// Single router: applies every streamed event to its key's conversation
257
+ /// (clearing finished turns) and re-emits it tagged for the application.
258
+ async fn route(
259
+ pool: Self,
260
+ mut fwd: mpsc::UnboundedReceiver<(SessionKey, Option<SessionEvent>)>,
261
+ ) {
262
+ while let Some((key, ev)) = fwd.recv().await {
263
+ let mut inner = pool.inner.lock().await;
264
+ let Some(session) = inner.sessions.get_mut(&key) else {
265
+ continue;
266
+ };
267
+ let Some(se) = ev else {
268
+ session.busy = false;
269
+ let _ = inner.app_events.send(PoolEvent::Ended { key });
270
+ continue;
271
+ };
272
+ session.seq += 1;
273
+ let rid = se.request_id.clone().unwrap_or_default();
274
+ let SessionEvent { event, .. } = &se;
275
+ let conv_id = session.conv.id().to_string();
276
+ let _ = session
277
+ .conv
278
+ .apply(WireEvent::new(conv_id, rid, session.seq, event.clone()));
279
+ match &event {
280
+ crate::Event::Completed { .. } | crate::Event::Error { .. } => {
281
+ session.busy = false;
282
+ }
283
+ _ => {}
284
+ }
285
+ let _ = inner.app_events.send(PoolEvent::Session {
286
+ key,
287
+ event: Box::new(se),
288
+ });
289
+ }
290
+ }
291
+ }
292
+
293
+ #[cfg(test)]
294
+ mod tests {
295
+ use super::*;
296
+ use crate::Event;
297
+ use crate::acp::{ContentBlock, SessionUpdate};
298
+ use std::time::Duration;
299
+ use tokio::time::timeout;
300
+
301
+ fn options() -> NativeOptions {
302
+ NativeOptions {
303
+ operation_timeout: Duration::from_secs(5),
304
+ prompt_timeout: Duration::from_secs(5),
305
+ permission_timeout: Duration::from_secs(2),
306
+ shutdown_timeout: Duration::from_secs(2),
307
+ ..NativeOptions::default()
308
+ }
309
+ }
310
+
311
+ fn command(mode: &str) -> AgentCommand {
312
+ AgentCommand::new("python3").args(["-u", "-c", include_str!("fixture.py"), mode])
313
+ }
314
+
315
+ fn session_options() -> SessionOptions {
316
+ SessionOptions::new(std::env::temp_dir())
317
+ }
318
+
319
+ fn text(event: &SessionEvent) -> Option<&str> {
320
+ match &event.event {
321
+ Event::Update {
322
+ update: SessionUpdate::AgentMessageChunk(chunk),
323
+ } => {
324
+ if let ContentBlock::Text(content) = &chunk.content {
325
+ Some(&content.text)
326
+ } else {
327
+ None
328
+ }
329
+ }
330
+ _ => None,
331
+ }
332
+ }
333
+
334
+ /// Collect streamed text for one key until its turn terminates.
335
+ /// Other keys' events are parked and replayed to the caller in order.
336
+ /// Collect streamed text for both keys until each turn terminates,
337
+ /// demultiplexing the single shared event stream.
338
+ async fn collect_two(rx: &mut mpsc::UnboundedReceiver<PoolEvent>) -> (String, String) {
339
+ let (mut a, mut b) = (String::new(), String::new());
340
+ let (mut done_a, mut done_b) = (false, false);
341
+ while !done_a || !done_b {
342
+ let ev = timeout(Duration::from_secs(15), rx.recv())
343
+ .await
344
+ .expect("event in time")
345
+ .expect("stream open");
346
+ if let PoolEvent::Session { key, event } = ev {
347
+ let terminal = matches!(event.event, Event::Completed { .. } | Event::Error { .. });
348
+ if let Some(t) = text(&event) {
349
+ match key.as_str() {
350
+ "a" => a.push_str(t),
351
+ "b" => b.push_str(t),
352
+ _ => {}
353
+ }
354
+ }
355
+ if terminal {
356
+ match key.as_str() {
357
+ "a" => done_a = true,
358
+ "b" => done_b = true,
359
+ _ => {}
360
+ }
361
+ }
362
+ }
363
+ }
364
+ (a, b)
365
+ }
366
+
367
+ #[tokio::test]
368
+ async fn parallel_keys_complete_independently() {
369
+ let (pool, mut rx) = SessionPool::connect(command("normal"), options()).await;
370
+ // Both turns run concurrently; a single shared session could not do this.
371
+ let p1 = pool.clone();
372
+ let t1 =
373
+ tokio::spawn(
374
+ async move { p1.prompt("a", session_options(), "first".to_string()).await },
375
+ );
376
+ let p2 = pool.clone();
377
+ let t2 = tokio::spawn(async move {
378
+ p2.prompt("b", session_options(), "second".to_string())
379
+ .await
380
+ });
381
+ let (r1, r2, texts) = tokio::join!(t1, t2, collect_two(&mut rx));
382
+ r1.expect("task a").expect("turn a");
383
+ r2.expect("task b").expect("turn b");
384
+ // Fixture answers every turn with the same stream.
385
+ assert_eq!(
386
+ texts,
387
+ ("hello world".to_string(), "hello world".to_string())
388
+ );
389
+ assert!(!pool.is_busy("a").await);
390
+ assert!(!pool.is_busy("b").await);
391
+ // Conversations accumulated per key for replay.
392
+ assert!(pool.conversation("a").await.is_some());
393
+ assert!(pool.conversation("b").await.is_some());
394
+ assert!(pool.conversation("ghost").await.is_none());
395
+ pool.shutdown().await;
396
+ }
397
+
398
+ #[tokio::test]
399
+ async fn second_prompt_on_busy_key_fails_cleanly() {
400
+ use crate::native::NativeError;
401
+
402
+ let (pool, _rx) = SessionPool::connect(command("hang"), options()).await;
403
+ // The hang fixture holds the turn open without answering, so the
404
+ // overlapping prompt deterministically hits the busy guard.
405
+ let p = pool.clone();
406
+ let held = tokio::spawn(async move {
407
+ // The hang fixture never answers, so the held turn stays open
408
+ // until the prompt timeout fires; the busy transition in between
409
+ // is what this test observes, not the outcome.
410
+ let _ = p.prompt("k", session_options(), "one".to_string()).await;
411
+ });
412
+ let mut busy_seen = false;
413
+ for _ in 0..100 {
414
+ if pool.is_busy("k").await {
415
+ busy_seen = true;
416
+ break;
417
+ }
418
+ tokio::time::sleep(Duration::from_millis(50)).await;
419
+ }
420
+ assert!(busy_seen, "turn never went busy");
421
+ let second = pool.prompt("k", session_options(), "two".to_string()).await;
422
+ assert!(
423
+ matches!(second, Err(NativeError::Busy)),
424
+ "expected Busy, got {second:?}"
425
+ );
426
+ let _ = held.await;
427
+ assert!(!pool.is_busy("k").await);
428
+ pool.shutdown().await;
429
+ }
430
+
431
+ #[tokio::test]
432
+ async fn missing_keys_fail_without_creating_sessions() {
433
+ let (pool, _rx) = SessionPool::connect(command("normal"), options()).await;
434
+ assert!(pool.set_model("ghost", "m").await.is_err());
435
+ assert!(pool.cancel("ghost").await.is_ok());
436
+ assert!(!pool.has_session("ghost").await);
437
+ pool.close("ghost").await;
438
+ pool.shutdown().await;
439
+ }
440
+ }