acp-sdk 0.1.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 (421) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +11 -0
  3. data/LICENSE.txt +9 -0
  4. data/README.md +81 -0
  5. data/lib/acp/agent_connection/client.rb +39 -0
  6. data/lib/acp/agent_connection/optional_method.rb +28 -0
  7. data/lib/acp/agent_connection.rb +122 -0
  8. data/lib/acp/client_connection.rb +129 -0
  9. data/lib/acp/sdk.rb +13 -0
  10. data/lib/acp/transport/reply.rb +20 -0
  11. data/lib/acp/transport/response_error.rb +28 -0
  12. data/lib/acp/transport/result.rb +35 -0
  13. data/lib/acp/transport/stdio.rb +207 -0
  14. data/lib/acp/transport.rb +4 -0
  15. data/lib/acp/types/agent_auth_capabilities.rb +31 -0
  16. data/lib/acp/types/agent_capabilities.rb +72 -0
  17. data/lib/acp/types/annotations.rb +46 -0
  18. data/lib/acp/types/audio_content.rb +46 -0
  19. data/lib/acp/types/auth_capabilities.rb +31 -0
  20. data/lib/acp/types/auth_method/terminal.rb +66 -0
  21. data/lib/acp/types/auth_method.rb +20 -0
  22. data/lib/acp/types/auth_method_agent.rb +41 -0
  23. data/lib/acp/types/auth_method_terminal.rb +58 -0
  24. data/lib/acp/types/authenticate_request.rb +31 -0
  25. data/lib/acp/types/authenticate_response.rb +26 -0
  26. data/lib/acp/types/available_command.rb +46 -0
  27. data/lib/acp/types/available_command_input.rb +19 -0
  28. data/lib/acp/types/available_commands_update.rb +34 -0
  29. data/lib/acp/types/blob_resource_contents.rb +41 -0
  30. data/lib/acp/types/boolean_config_option_capabilities.rb +26 -0
  31. data/lib/acp/types/boolean_property_schema.rb +41 -0
  32. data/lib/acp/types/cancel_notification.rb +31 -0
  33. data/lib/acp/types/cancel_request_notification.rb +31 -0
  34. data/lib/acp/types/client_capabilities.rb +65 -0
  35. data/lib/acp/types/client_session_capabilities.rb +36 -0
  36. data/lib/acp/types/close_session_request.rb +31 -0
  37. data/lib/acp/types/close_session_response.rb +26 -0
  38. data/lib/acp/types/complete_elicitation_notification.rb +31 -0
  39. data/lib/acp/types/config_option_update.rb +34 -0
  40. data/lib/acp/types/content.rb +31 -0
  41. data/lib/acp/types/content_block/audio.rb +52 -0
  42. data/lib/acp/types/content_block/image.rb +59 -0
  43. data/lib/acp/types/content_block/resource.rb +40 -0
  44. data/lib/acp/types/content_block/resource_link.rb +80 -0
  45. data/lib/acp/types/content_block/text.rb +40 -0
  46. data/lib/acp/types/content_block.rb +26 -0
  47. data/lib/acp/types/content_chunk.rb +40 -0
  48. data/lib/acp/types/cost.rb +36 -0
  49. data/lib/acp/types/create_elicitation_request/mode/form.rb +34 -0
  50. data/lib/acp/types/create_elicitation_request/mode/url.rb +40 -0
  51. data/lib/acp/types/create_elicitation_request/mode.rb +20 -0
  52. data/lib/acp/types/create_elicitation_request.rb +40 -0
  53. data/lib/acp/types/create_elicitation_response/action/accept.rb +26 -0
  54. data/lib/acp/types/create_elicitation_response/action/cancel.rb +21 -0
  55. data/lib/acp/types/create_elicitation_response/action/decline.rb +21 -0
  56. data/lib/acp/types/create_elicitation_response/action.rb +22 -0
  57. data/lib/acp/types/create_elicitation_response.rb +31 -0
  58. data/lib/acp/types/create_terminal_request.rb +72 -0
  59. data/lib/acp/types/create_terminal_response.rb +31 -0
  60. data/lib/acp/types/current_mode_update.rb +31 -0
  61. data/lib/acp/types/delete_session_request.rb +31 -0
  62. data/lib/acp/types/delete_session_response.rb +26 -0
  63. data/lib/acp/types/diff.rb +41 -0
  64. data/lib/acp/types/elicitation_accept_action.rb +26 -0
  65. data/lib/acp/types/elicitation_capabilities.rb +40 -0
  66. data/lib/acp/types/elicitation_form_capabilities.rb +26 -0
  67. data/lib/acp/types/elicitation_form_mode/scope.rb +22 -0
  68. data/lib/acp/types/elicitation_form_mode.rb +34 -0
  69. data/lib/acp/types/elicitation_property_schema/boolean_property_schema.rb +47 -0
  70. data/lib/acp/types/elicitation_property_schema/integer_property_schema.rb +66 -0
  71. data/lib/acp/types/elicitation_property_schema/multi_select_property_schema.rb +73 -0
  72. data/lib/acp/types/elicitation_property_schema/number_property_schema.rb +66 -0
  73. data/lib/acp/types/elicitation_property_schema/string_property_schema.rb +105 -0
  74. data/lib/acp/types/elicitation_property_schema.rb +26 -0
  75. data/lib/acp/types/elicitation_request_scope.rb +26 -0
  76. data/lib/acp/types/elicitation_schema.rb +65 -0
  77. data/lib/acp/types/elicitation_schema_type.rb +7 -0
  78. data/lib/acp/types/elicitation_session_scope.rb +31 -0
  79. data/lib/acp/types/elicitation_url_capabilities.rb +26 -0
  80. data/lib/acp/types/elicitation_url_mode/scope.rb +22 -0
  81. data/lib/acp/types/elicitation_url_mode.rb +40 -0
  82. data/lib/acp/types/embedded_resource.rb +40 -0
  83. data/lib/acp/types/embedded_resource_resource.rb +22 -0
  84. data/lib/acp/types/enum_option.rb +41 -0
  85. data/lib/acp/types/env_variable.rb +36 -0
  86. data/lib/acp/types/file_system_capabilities.rb +36 -0
  87. data/lib/acp/types/http_header.rb +36 -0
  88. data/lib/acp/types/image_content.rb +58 -0
  89. data/lib/acp/types/implementation.rb +41 -0
  90. data/lib/acp/types/initialize_request.rb +51 -0
  91. data/lib/acp/types/initialize_response.rb +58 -0
  92. data/lib/acp/types/integer_property_schema.rb +65 -0
  93. data/lib/acp/types/kill_terminal_request.rb +36 -0
  94. data/lib/acp/types/kill_terminal_response.rb +26 -0
  95. data/lib/acp/types/list_sessions_request.rb +36 -0
  96. data/lib/acp/types/list_sessions_response.rb +40 -0
  97. data/lib/acp/types/load_session_request.rb +58 -0
  98. data/lib/acp/types/load_session_response.rb +40 -0
  99. data/lib/acp/types/logout_capabilities.rb +26 -0
  100. data/lib/acp/types/logout_request.rb +26 -0
  101. data/lib/acp/types/logout_response.rb +26 -0
  102. data/lib/acp/types/mcp_capabilities.rb +36 -0
  103. data/lib/acp/types/mcp_server/http.rb +46 -0
  104. data/lib/acp/types/mcp_server/sse.rb +46 -0
  105. data/lib/acp/types/mcp_server.rb +22 -0
  106. data/lib/acp/types/mcp_server_http.rb +46 -0
  107. data/lib/acp/types/mcp_server_sse.rb +46 -0
  108. data/lib/acp/types/mcp_server_stdio.rb +52 -0
  109. data/lib/acp/types/multi_select_items/string_multi_select_items.rb +31 -0
  110. data/lib/acp/types/multi_select_items.rb +20 -0
  111. data/lib/acp/types/multi_select_property_schema.rb +72 -0
  112. data/lib/acp/types/new_session_request.rb +51 -0
  113. data/lib/acp/types/new_session_response.rb +51 -0
  114. data/lib/acp/types/number_property_schema.rb +65 -0
  115. data/lib/acp/types/permission_option.rb +41 -0
  116. data/lib/acp/types/permission_option_kind.rb +10 -0
  117. data/lib/acp/types/plan.rb +31 -0
  118. data/lib/acp/types/plan_entry.rb +46 -0
  119. data/lib/acp/types/plan_entry_priority.rb +9 -0
  120. data/lib/acp/types/plan_entry_status.rb +9 -0
  121. data/lib/acp/types/prompt_capabilities.rb +41 -0
  122. data/lib/acp/types/prompt_request.rb +40 -0
  123. data/lib/acp/types/prompt_response.rb +31 -0
  124. data/lib/acp/types/read_text_file_request.rb +52 -0
  125. data/lib/acp/types/read_text_file_response.rb +31 -0
  126. data/lib/acp/types/release_terminal_request.rb +36 -0
  127. data/lib/acp/types/release_terminal_response.rb +26 -0
  128. data/lib/acp/types/request_permission_outcome/cancelled.rb +21 -0
  129. data/lib/acp/types/request_permission_outcome/selected.rb +31 -0
  130. data/lib/acp/types/request_permission_outcome.rb +20 -0
  131. data/lib/acp/types/request_permission_request.rb +51 -0
  132. data/lib/acp/types/request_permission_response.rb +31 -0
  133. data/lib/acp/types/resource_link.rb +79 -0
  134. data/lib/acp/types/resume_session_request.rb +58 -0
  135. data/lib/acp/types/resume_session_response.rb +40 -0
  136. data/lib/acp/types/role.rb +8 -0
  137. data/lib/acp/types/selected_permission_outcome.rb +31 -0
  138. data/lib/acp/types/session_additional_directories_capabilities.rb +26 -0
  139. data/lib/acp/types/session_capabilities.rb +67 -0
  140. data/lib/acp/types/session_close_capabilities.rb +26 -0
  141. data/lib/acp/types/session_config_boolean.rb +26 -0
  142. data/lib/acp/types/session_config_option/kind/boolean.rb +26 -0
  143. data/lib/acp/types/session_config_option/kind/select.rb +34 -0
  144. data/lib/acp/types/session_config_option/kind.rb +20 -0
  145. data/lib/acp/types/session_config_option.rb +64 -0
  146. data/lib/acp/types/session_config_option_category.rb +10 -0
  147. data/lib/acp/types/session_config_options_capabilities.rb +34 -0
  148. data/lib/acp/types/session_config_select.rb +34 -0
  149. data/lib/acp/types/session_config_select_group.rb +46 -0
  150. data/lib/acp/types/session_config_select_option.rb +41 -0
  151. data/lib/acp/types/session_config_select_options.rb +22 -0
  152. data/lib/acp/types/session_delete_capabilities.rb +26 -0
  153. data/lib/acp/types/session_info.rb +65 -0
  154. data/lib/acp/types/session_info_update.rb +38 -0
  155. data/lib/acp/types/session_list_capabilities.rb +26 -0
  156. data/lib/acp/types/session_list_request.rb +8 -0
  157. data/lib/acp/types/session_list_response.rb +8 -0
  158. data/lib/acp/types/session_load_request.rb +8 -0
  159. data/lib/acp/types/session_mode.rb +41 -0
  160. data/lib/acp/types/session_mode_state.rb +40 -0
  161. data/lib/acp/types/session_new_request.rb +8 -0
  162. data/lib/acp/types/session_notification.rb +40 -0
  163. data/lib/acp/types/session_prompt_request.rb +8 -0
  164. data/lib/acp/types/session_resume_capabilities.rb +26 -0
  165. data/lib/acp/types/session_update/agent_message_chunk.rb +45 -0
  166. data/lib/acp/types/session_update/agent_thought_chunk.rb +45 -0
  167. data/lib/acp/types/session_update/available_commands_update.rb +38 -0
  168. data/lib/acp/types/session_update/config_option_update.rb +38 -0
  169. data/lib/acp/types/session_update/current_mode_update.rb +31 -0
  170. data/lib/acp/types/session_update/plan.rb +31 -0
  171. data/lib/acp/types/session_update/session_info_update.rb +39 -0
  172. data/lib/acp/types/session_update/tool_call.rb +97 -0
  173. data/lib/acp/types/session_update/tool_call_update.rb +97 -0
  174. data/lib/acp/types/session_update/usage_update.rb +46 -0
  175. data/lib/acp/types/session_update/user_message_chunk.rb +45 -0
  176. data/lib/acp/types/session_update.rb +38 -0
  177. data/lib/acp/types/set_session_config_option_request/value/boolean.rb +26 -0
  178. data/lib/acp/types/set_session_config_option_request/value/value_id.rb +26 -0
  179. data/lib/acp/types/set_session_config_option_request/value.rb +20 -0
  180. data/lib/acp/types/set_session_config_option_request.rb +46 -0
  181. data/lib/acp/types/set_session_config_option_response.rb +34 -0
  182. data/lib/acp/types/set_session_mode_request.rb +36 -0
  183. data/lib/acp/types/set_session_mode_response.rb +26 -0
  184. data/lib/acp/types/stop_reason.rb +11 -0
  185. data/lib/acp/types/string_format.rb +10 -0
  186. data/lib/acp/types/string_multi_select_items.rb +31 -0
  187. data/lib/acp/types/string_property_schema.rb +104 -0
  188. data/lib/acp/types/terminal.rb +31 -0
  189. data/lib/acp/types/terminal_exit_status.rb +36 -0
  190. data/lib/acp/types/terminal_output_request.rb +36 -0
  191. data/lib/acp/types/terminal_output_response.rb +46 -0
  192. data/lib/acp/types/text_content.rb +40 -0
  193. data/lib/acp/types/text_resource_contents.rb +41 -0
  194. data/lib/acp/types/titled_multi_select_items.rb +31 -0
  195. data/lib/acp/types/tool_call.rb +96 -0
  196. data/lib/acp/types/tool_call_content/content.rb +31 -0
  197. data/lib/acp/types/tool_call_content/diff.rb +41 -0
  198. data/lib/acp/types/tool_call_content/terminal.rb +31 -0
  199. data/lib/acp/types/tool_call_content.rb +22 -0
  200. data/lib/acp/types/tool_call_location.rb +36 -0
  201. data/lib/acp/types/tool_call_status.rb +10 -0
  202. data/lib/acp/types/tool_call_update.rb +96 -0
  203. data/lib/acp/types/tool_kind.rb +16 -0
  204. data/lib/acp/types/unstructured_command_input.rb +31 -0
  205. data/lib/acp/types/usage_update.rb +46 -0
  206. data/lib/acp/types/wait_for_terminal_exit_request.rb +36 -0
  207. data/lib/acp/types/wait_for_terminal_exit_response.rb +36 -0
  208. data/lib/acp/types/write_text_file_request.rb +46 -0
  209. data/lib/acp/types/write_text_file_response.rb +26 -0
  210. data/lib/acp/version.rb +5 -0
  211. data/sig/generated/acp/agent_connection/client.rbs +29 -0
  212. data/sig/generated/acp/agent_connection/optional_method.rbs +21 -0
  213. data/sig/generated/acp/agent_connection.rbs +65 -0
  214. data/sig/generated/acp/client_connection.rbs +82 -0
  215. data/sig/generated/acp/sdk.rbs +2 -0
  216. data/sig/generated/acp/transport/reply.rbs +16 -0
  217. data/sig/generated/acp/transport/response_error.rbs +21 -0
  218. data/sig/generated/acp/transport/result.rbs +25 -0
  219. data/sig/generated/acp/transport/stdio.rbs +116 -0
  220. data/sig/generated/acp/transport.rbs +4 -0
  221. data/sig/generated/acp/types/agent_auth_capabilities.rbs +21 -0
  222. data/sig/generated/acp/types/agent_capabilities.rbs +37 -0
  223. data/sig/generated/acp/types/annotations.rbs +29 -0
  224. data/sig/generated/acp/types/audio_content.rbs +29 -0
  225. data/sig/generated/acp/types/auth_capabilities.rbs +21 -0
  226. data/sig/generated/acp/types/auth_method/terminal.rbs +37 -0
  227. data/sig/generated/acp/types/auth_method.rbs +9 -0
  228. data/sig/generated/acp/types/auth_method_agent.rbs +29 -0
  229. data/sig/generated/acp/types/auth_method_terminal.rbs +37 -0
  230. data/sig/generated/acp/types/authenticate_request.rbs +21 -0
  231. data/sig/generated/acp/types/authenticate_response.rbs +17 -0
  232. data/sig/generated/acp/types/available_command.rbs +29 -0
  233. data/sig/generated/acp/types/available_command_input.rbs +9 -0
  234. data/sig/generated/acp/types/available_commands_update.rbs +21 -0
  235. data/sig/generated/acp/types/blob_resource_contents.rbs +29 -0
  236. data/sig/generated/acp/types/boolean_config_option_capabilities.rbs +17 -0
  237. data/sig/generated/acp/types/boolean_property_schema.rbs +29 -0
  238. data/sig/generated/acp/types/cancel_notification.rbs +21 -0
  239. data/sig/generated/acp/types/cancel_request_notification.rbs +21 -0
  240. data/sig/generated/acp/types/client_capabilities.rbs +37 -0
  241. data/sig/generated/acp/types/client_session_capabilities.rbs +21 -0
  242. data/sig/generated/acp/types/close_session_request.rbs +21 -0
  243. data/sig/generated/acp/types/close_session_response.rbs +17 -0
  244. data/sig/generated/acp/types/complete_elicitation_notification.rbs +21 -0
  245. data/sig/generated/acp/types/config_option_update.rbs +21 -0
  246. data/sig/generated/acp/types/content.rbs +21 -0
  247. data/sig/generated/acp/types/content_block/audio.rbs +29 -0
  248. data/sig/generated/acp/types/content_block/image.rbs +33 -0
  249. data/sig/generated/acp/types/content_block/resource.rbs +25 -0
  250. data/sig/generated/acp/types/content_block/resource_link.rbs +45 -0
  251. data/sig/generated/acp/types/content_block/text.rbs +25 -0
  252. data/sig/generated/acp/types/content_block.rbs +9 -0
  253. data/sig/generated/acp/types/content_chunk.rbs +25 -0
  254. data/sig/generated/acp/types/cost.rbs +25 -0
  255. data/sig/generated/acp/types/create_elicitation_request/mode/form.rbs +21 -0
  256. data/sig/generated/acp/types/create_elicitation_request/mode/url.rbs +25 -0
  257. data/sig/generated/acp/types/create_elicitation_request/mode.rbs +9 -0
  258. data/sig/generated/acp/types/create_elicitation_request.rbs +25 -0
  259. data/sig/generated/acp/types/create_elicitation_response/action/accept.rbs +17 -0
  260. data/sig/generated/acp/types/create_elicitation_response/action/cancel.rbs +13 -0
  261. data/sig/generated/acp/types/create_elicitation_response/action/decline.rbs +13 -0
  262. data/sig/generated/acp/types/create_elicitation_response/action.rbs +9 -0
  263. data/sig/generated/acp/types/create_elicitation_response.rbs +21 -0
  264. data/sig/generated/acp/types/create_terminal_request.rbs +41 -0
  265. data/sig/generated/acp/types/create_terminal_response.rbs +21 -0
  266. data/sig/generated/acp/types/current_mode_update.rbs +21 -0
  267. data/sig/generated/acp/types/delete_session_request.rbs +21 -0
  268. data/sig/generated/acp/types/delete_session_response.rbs +17 -0
  269. data/sig/generated/acp/types/diff.rbs +29 -0
  270. data/sig/generated/acp/types/elicitation_accept_action.rbs +17 -0
  271. data/sig/generated/acp/types/elicitation_capabilities.rbs +25 -0
  272. data/sig/generated/acp/types/elicitation_form_capabilities.rbs +17 -0
  273. data/sig/generated/acp/types/elicitation_form_mode/scope.rbs +9 -0
  274. data/sig/generated/acp/types/elicitation_form_mode.rbs +21 -0
  275. data/sig/generated/acp/types/elicitation_property_schema/boolean_property_schema.rbs +29 -0
  276. data/sig/generated/acp/types/elicitation_property_schema/integer_property_schema.rbs +37 -0
  277. data/sig/generated/acp/types/elicitation_property_schema/multi_select_property_schema.rbs +41 -0
  278. data/sig/generated/acp/types/elicitation_property_schema/number_property_schema.rbs +37 -0
  279. data/sig/generated/acp/types/elicitation_property_schema/string_property_schema.rbs +53 -0
  280. data/sig/generated/acp/types/elicitation_property_schema.rbs +9 -0
  281. data/sig/generated/acp/types/elicitation_request_scope.rbs +17 -0
  282. data/sig/generated/acp/types/elicitation_schema.rbs +37 -0
  283. data/sig/generated/acp/types/elicitation_schema_type.rbs +5 -0
  284. data/sig/generated/acp/types/elicitation_session_scope.rbs +21 -0
  285. data/sig/generated/acp/types/elicitation_url_capabilities.rbs +17 -0
  286. data/sig/generated/acp/types/elicitation_url_mode/scope.rbs +9 -0
  287. data/sig/generated/acp/types/elicitation_url_mode.rbs +25 -0
  288. data/sig/generated/acp/types/embedded_resource.rbs +25 -0
  289. data/sig/generated/acp/types/embedded_resource_resource.rbs +9 -0
  290. data/sig/generated/acp/types/enum_option.rbs +29 -0
  291. data/sig/generated/acp/types/env_variable.rbs +25 -0
  292. data/sig/generated/acp/types/file_system_capabilities.rbs +25 -0
  293. data/sig/generated/acp/types/http_header.rbs +25 -0
  294. data/sig/generated/acp/types/image_content.rbs +33 -0
  295. data/sig/generated/acp/types/implementation.rbs +29 -0
  296. data/sig/generated/acp/types/initialize_request.rbs +29 -0
  297. data/sig/generated/acp/types/initialize_response.rbs +33 -0
  298. data/sig/generated/acp/types/integer_property_schema.rbs +37 -0
  299. data/sig/generated/acp/types/kill_terminal_request.rbs +25 -0
  300. data/sig/generated/acp/types/kill_terminal_response.rbs +17 -0
  301. data/sig/generated/acp/types/list_sessions_request.rbs +25 -0
  302. data/sig/generated/acp/types/list_sessions_response.rbs +25 -0
  303. data/sig/generated/acp/types/load_session_request.rbs +33 -0
  304. data/sig/generated/acp/types/load_session_response.rbs +25 -0
  305. data/sig/generated/acp/types/logout_capabilities.rbs +17 -0
  306. data/sig/generated/acp/types/logout_request.rbs +17 -0
  307. data/sig/generated/acp/types/logout_response.rbs +17 -0
  308. data/sig/generated/acp/types/mcp_capabilities.rbs +25 -0
  309. data/sig/generated/acp/types/mcp_server/http.rbs +29 -0
  310. data/sig/generated/acp/types/mcp_server/sse.rbs +29 -0
  311. data/sig/generated/acp/types/mcp_server.rbs +9 -0
  312. data/sig/generated/acp/types/mcp_server_http.rbs +29 -0
  313. data/sig/generated/acp/types/mcp_server_sse.rbs +29 -0
  314. data/sig/generated/acp/types/mcp_server_stdio.rbs +33 -0
  315. data/sig/generated/acp/types/multi_select_items/string_multi_select_items.rbs +21 -0
  316. data/sig/generated/acp/types/multi_select_items.rbs +9 -0
  317. data/sig/generated/acp/types/multi_select_property_schema.rbs +41 -0
  318. data/sig/generated/acp/types/new_session_request.rbs +29 -0
  319. data/sig/generated/acp/types/new_session_response.rbs +29 -0
  320. data/sig/generated/acp/types/number_property_schema.rbs +37 -0
  321. data/sig/generated/acp/types/permission_option.rbs +29 -0
  322. data/sig/generated/acp/types/permission_option_kind.rbs +11 -0
  323. data/sig/generated/acp/types/plan.rbs +21 -0
  324. data/sig/generated/acp/types/plan_entry.rbs +29 -0
  325. data/sig/generated/acp/types/plan_entry_priority.rbs +9 -0
  326. data/sig/generated/acp/types/plan_entry_status.rbs +9 -0
  327. data/sig/generated/acp/types/prompt_capabilities.rbs +29 -0
  328. data/sig/generated/acp/types/prompt_request.rbs +25 -0
  329. data/sig/generated/acp/types/prompt_response.rbs +21 -0
  330. data/sig/generated/acp/types/read_text_file_request.rbs +33 -0
  331. data/sig/generated/acp/types/read_text_file_response.rbs +21 -0
  332. data/sig/generated/acp/types/release_terminal_request.rbs +25 -0
  333. data/sig/generated/acp/types/release_terminal_response.rbs +17 -0
  334. data/sig/generated/acp/types/request_permission_outcome/cancelled.rbs +13 -0
  335. data/sig/generated/acp/types/request_permission_outcome/selected.rbs +21 -0
  336. data/sig/generated/acp/types/request_permission_outcome.rbs +9 -0
  337. data/sig/generated/acp/types/request_permission_request.rbs +29 -0
  338. data/sig/generated/acp/types/request_permission_response.rbs +21 -0
  339. data/sig/generated/acp/types/resource_link.rbs +45 -0
  340. data/sig/generated/acp/types/resume_session_request.rbs +33 -0
  341. data/sig/generated/acp/types/resume_session_response.rbs +25 -0
  342. data/sig/generated/acp/types/role.rbs +7 -0
  343. data/sig/generated/acp/types/selected_permission_outcome.rbs +21 -0
  344. data/sig/generated/acp/types/session_additional_directories_capabilities.rbs +17 -0
  345. data/sig/generated/acp/types/session_capabilities.rbs +37 -0
  346. data/sig/generated/acp/types/session_close_capabilities.rbs +17 -0
  347. data/sig/generated/acp/types/session_config_boolean.rbs +17 -0
  348. data/sig/generated/acp/types/session_config_option/kind/boolean.rbs +17 -0
  349. data/sig/generated/acp/types/session_config_option/kind/select.rbs +21 -0
  350. data/sig/generated/acp/types/session_config_option/kind.rbs +9 -0
  351. data/sig/generated/acp/types/session_config_option.rbs +37 -0
  352. data/sig/generated/acp/types/session_config_option_category.rbs +11 -0
  353. data/sig/generated/acp/types/session_config_options_capabilities.rbs +21 -0
  354. data/sig/generated/acp/types/session_config_select.rbs +21 -0
  355. data/sig/generated/acp/types/session_config_select_group.rbs +29 -0
  356. data/sig/generated/acp/types/session_config_select_option.rbs +29 -0
  357. data/sig/generated/acp/types/session_config_select_options.rbs +9 -0
  358. data/sig/generated/acp/types/session_delete_capabilities.rbs +17 -0
  359. data/sig/generated/acp/types/session_info.rbs +37 -0
  360. data/sig/generated/acp/types/session_info_update.rbs +25 -0
  361. data/sig/generated/acp/types/session_list_capabilities.rbs +17 -0
  362. data/sig/generated/acp/types/session_list_request.rbs +3 -0
  363. data/sig/generated/acp/types/session_list_response.rbs +3 -0
  364. data/sig/generated/acp/types/session_load_request.rbs +3 -0
  365. data/sig/generated/acp/types/session_mode.rbs +29 -0
  366. data/sig/generated/acp/types/session_mode_state.rbs +25 -0
  367. data/sig/generated/acp/types/session_new_request.rbs +3 -0
  368. data/sig/generated/acp/types/session_notification.rbs +25 -0
  369. data/sig/generated/acp/types/session_prompt_request.rbs +3 -0
  370. data/sig/generated/acp/types/session_resume_capabilities.rbs +17 -0
  371. data/sig/generated/acp/types/session_update/agent_message_chunk.rbs +25 -0
  372. data/sig/generated/acp/types/session_update/agent_thought_chunk.rbs +25 -0
  373. data/sig/generated/acp/types/session_update/available_commands_update.rbs +21 -0
  374. data/sig/generated/acp/types/session_update/config_option_update.rbs +21 -0
  375. data/sig/generated/acp/types/session_update/current_mode_update.rbs +21 -0
  376. data/sig/generated/acp/types/session_update/plan.rbs +21 -0
  377. data/sig/generated/acp/types/session_update/session_info_update.rbs +25 -0
  378. data/sig/generated/acp/types/session_update/tool_call.rbs +49 -0
  379. data/sig/generated/acp/types/session_update/tool_call_update.rbs +49 -0
  380. data/sig/generated/acp/types/session_update/usage_update.rbs +29 -0
  381. data/sig/generated/acp/types/session_update/user_message_chunk.rbs +25 -0
  382. data/sig/generated/acp/types/session_update.rbs +9 -0
  383. data/sig/generated/acp/types/set_session_config_option_request/value/boolean.rbs +17 -0
  384. data/sig/generated/acp/types/set_session_config_option_request/value/value_id.rbs +17 -0
  385. data/sig/generated/acp/types/set_session_config_option_request/value.rbs +9 -0
  386. data/sig/generated/acp/types/set_session_config_option_request.rbs +29 -0
  387. data/sig/generated/acp/types/set_session_config_option_response.rbs +21 -0
  388. data/sig/generated/acp/types/set_session_mode_request.rbs +25 -0
  389. data/sig/generated/acp/types/set_session_mode_response.rbs +17 -0
  390. data/sig/generated/acp/types/stop_reason.rbs +13 -0
  391. data/sig/generated/acp/types/string_format.rbs +11 -0
  392. data/sig/generated/acp/types/string_multi_select_items.rbs +21 -0
  393. data/sig/generated/acp/types/string_property_schema.rbs +53 -0
  394. data/sig/generated/acp/types/terminal.rbs +21 -0
  395. data/sig/generated/acp/types/terminal_exit_status.rbs +25 -0
  396. data/sig/generated/acp/types/terminal_output_request.rbs +25 -0
  397. data/sig/generated/acp/types/terminal_output_response.rbs +29 -0
  398. data/sig/generated/acp/types/text_content.rbs +25 -0
  399. data/sig/generated/acp/types/text_resource_contents.rbs +29 -0
  400. data/sig/generated/acp/types/titled_multi_select_items.rbs +21 -0
  401. data/sig/generated/acp/types/tool_call.rbs +49 -0
  402. data/sig/generated/acp/types/tool_call_content/content.rbs +21 -0
  403. data/sig/generated/acp/types/tool_call_content/diff.rbs +29 -0
  404. data/sig/generated/acp/types/tool_call_content/terminal.rbs +21 -0
  405. data/sig/generated/acp/types/tool_call_content.rbs +9 -0
  406. data/sig/generated/acp/types/tool_call_location.rbs +25 -0
  407. data/sig/generated/acp/types/tool_call_status.rbs +11 -0
  408. data/sig/generated/acp/types/tool_call_update.rbs +49 -0
  409. data/sig/generated/acp/types/tool_kind.rbs +23 -0
  410. data/sig/generated/acp/types/unstructured_command_input.rbs +21 -0
  411. data/sig/generated/acp/types/usage_update.rbs +29 -0
  412. data/sig/generated/acp/types/wait_for_terminal_exit_request.rbs +25 -0
  413. data/sig/generated/acp/types/wait_for_terminal_exit_response.rbs +25 -0
  414. data/sig/generated/acp/types/write_text_file_request.rbs +29 -0
  415. data/sig/generated/acp/types/write_text_file_response.rbs +17 -0
  416. data/sig/generated/acp/version.rbs +5 -0
  417. data/sig/manual/acp/agent_connection.rbs +52 -0
  418. data/sig/manual/acp/client_connection.rbs +13 -0
  419. data/sig/manual/acp/transport/stdio.rbs +18 -0
  420. data/sig/manual/acp/types.rbs +2 -0
  421. metadata +477 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: b7ee3b18dbb3a538b575c1b3309f93bd84eab3566e4491d39646dde8f0114ff6
4
+ data.tar.gz: ef1b3bed3a05dea92f076f4ed67e8378a3893d522db9ffe76ad445810b5fb20e
5
+ SHA512:
6
+ metadata.gz: 69232d2be95232917e3f3c25c0eb890cbb2367069378e4ec415765a9606740da333b32631d3430c42fd7b5691b434329ef1ff2f6a0c19433d5149837bf4880d9
7
+ data.tar.gz: 256d27b3b35c9e6e569fe60098885fa878fa6e7df06ffed8330c58d5b611f198a796df6928a62c6f8c2df01e4628312e5a6408efab669511dc4a402a8ab7b6e8
data/CHANGELOG.md ADDED
@@ -0,0 +1,11 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0 (2026-09-30)
4
+
5
+
6
+ ### Features
7
+
8
+ * ACP::AgentConnection with the required methods, session/load and session/list ([#14](https://github.com/bottrall/acp-sdk/issues/14)) ([565798c](https://github.com/bottrall/acp-sdk/commit/565798cffa6a0a3a09c38d28fa2cc25db881650e))
9
+ * ACP::ClientConnection ([#17](https://github.com/bottrall/acp-sdk/issues/17)) ([65fc83a](https://github.com/bottrall/acp-sdk/commit/65fc83a8cda8896c03090fe4bb59a59907418791)), closes [#6](https://github.com/bottrall/acp-sdk/issues/6)
10
+ * JSON-RPC over NDJSON stdio transport with mid-turn multiplexing ([#9](https://github.com/bottrall/acp-sdk/issues/9)) ([ef5f7db](https://github.com/bottrall/acp-sdk/commit/ef5f7db50f4272161f010f14dd99e49f2526d68b))
11
+ * vendor the ACP schema.json and generate the types ([#8](https://github.com/bottrall/acp-sdk/issues/8)) ([69c6ece](https://github.com/bottrall/acp-sdk/commit/69c6ece77ff1b785c8f7f20681e3af71a1cbaffa)), closes [#3](https://github.com/bottrall/acp-sdk/issues/3)
data/LICENSE.txt ADDED
@@ -0,0 +1,9 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2026 Jake Bottrall
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,81 @@
1
+ # acp-sdk
2
+
3
+ A standalone Ruby implementation of the [Agent Client Protocol](https://agentclientprotocol.com) (ACP): the agent side (`ACP::AgentConnection`), for exposing an agent to ACP clients such as Zed and JetBrains, and the client side (`ACP::ClientConnection`), for driving ACP agents from Ruby.
4
+
5
+ Namespace `ACP`. MIT licensed.
6
+
7
+ ## Status
8
+
9
+ Intent only. Nothing is published yet; the `acp-sdk` gem name is claimed at the first release.
10
+
11
+ This repo exists so that [riffer-rig](https://github.com/bottrall/riffer-rig) can depend on it for its `riffer acp` host. Scope for the first release: JSON-RPC over stdio, `mcpServers` and `available_commands` in, permission requests and filesystem methods out.
12
+
13
+ ## Serving an agent
14
+
15
+ An agent is a plain Ruby object. `ACP::AgentConnection` answers `initialize` itself, turns each request into its generated `ACP::Types` object, calls the agent, and sends back what it returns. The factory block receives an `ACP::AgentConnection::Client` handle, which the agent keeps for sending session updates and asking permission. [`examples/echo_agent.rb`](examples/echo_agent.rb) is a complete agent; `ruby examples/echo_agent.rb` serves it on stdio.
16
+
17
+ ```ruby
18
+ connection = ACP::AgentConnection.new(
19
+ transport: ACP::Transport::Stdio.new(input: $stdin, output: $stdout),
20
+ capabilities: ACP::Types::AgentCapabilities.new(load_session: true),
21
+ agent_info: ACP::Types::Implementation.new(name: 'my-agent', version: '1.0.0')
22
+ ) { |client| MyAgent.new(client:) }
23
+ connection.start.join
24
+ ```
25
+
26
+ The agent's contract:
27
+
28
+ - It defines `new_session`, `prompt` and `cancel`. `load_session` and `list_sessions` are required only when `capabilities` advertises `load_session` or `session_capabilities.list`; `start` raises `ArgumentError` when one is advertised but missing, and a method that is not advertised is answered with `-32601`. `session_created`, if defined, runs right after the `session/new` reply is sent.
29
+ - Each request method returns its response type or an `ACP::Transport::ResponseError`, which is sent as the error reply. Params that do not match the schema are answered with `-32602` before the agent sees them.
30
+ - `cancel` runs on the transport's reader thread, so it must return quickly: set a flag and let the prompt notice it.
31
+ - After a cancel, the agent must itself end the turn with `stopReason: cancelled`. `ACP::AgentConnection` does not enforce it.
32
+ - `client.capabilities` is `nil` until the client sends `initialize`.
33
+
34
+ ## Driving an agent
35
+
36
+ `ACP::ClientConnection` is the other side of the same transport, for editors and for tests that drive an agent. Each method takes the request's generated `ACP::Types` object and returns its response type or the agent's `ACP::Transport::ResponseError`. `connect` sends `initialize`, which Ruby reserves for the constructor.
37
+
38
+ ```ruby
39
+ stdin, stdout, = Open3.popen2('ruby', 'examples/echo_agent.rb')
40
+ connection = ACP::ClientConnection.new(
41
+ transport: ACP::Transport::Stdio.new(input: stdout, output: stdin),
42
+ permission: ->(request) { ask_the_user(request) },
43
+ updates: ->(notification) { show(notification) }
44
+ )
45
+ connection.start
46
+ connection.connect(ACP::Types::InitializeRequest.new(protocol_version: 1))
47
+ session = connection.session_new(ACP::Types::NewSessionRequest.new(cwd: Dir.pwd, mcp_servers: []))
48
+ prompt = [ACP::Types::ContentBlock::Text.new(text: 'hello')]
49
+ response = connection.session_prompt(ACP::Types::PromptRequest.new(session_id: session.session_id, prompt:)) do |update|
50
+ print update.content.text if update.is_a?(ACP::Types::SessionUpdate::AgentMessageChunk)
51
+ end
52
+ ```
53
+
54
+ - `session_prompt` and `session_load` yield the session's updates on the calling thread as they arrive and return once the agent replies. Updates outside those calls, such as the available commands after `session/new`, go to `updates`, which runs on the reader thread and must return quickly.
55
+ - `permission` answers `session/request_permission` with an `ACP::Types::RequestPermissionResponse` or an `ACP::Transport::ResponseError`. It runs on its own thread, so it may block while the user decides, and `session_cancel` can be sent meanwhile.
56
+
57
+ ## Development
58
+
59
+ Every project chore is a script in `bin/`. The Rakefile behind them is an implementation detail; you never need to call rake directly.
60
+
61
+ | Script | What it does |
62
+ | --------------- | ---------------------------------------------------------------------------------------------- |
63
+ | `bin/setup` | Install dependencies on a fresh checkout (gems + rbs collection) |
64
+ | `bin/test` | Run the test suite. Pass files and/or Minitest flags: `bin/test test/foo_test.rb -n /pattern/` |
65
+ | `bin/lint` | Run RuboCop. Arguments are forwarded, e.g. `bin/lint -a` |
66
+ | `bin/typecheck` | Check `lib/acp/types`, the rbs collection lockfile and `sig/generated` are current, then type-check with Steep |
67
+ | `bin/rbs` | Regenerate `sig/generated` from the inline annotations in `lib/` |
68
+ | `bin/types` | Regenerate `lib/acp/types` from `schema/schema.json`, then `sig/generated` |
69
+ | `bin/rbs-watch` | Regenerate `sig/generated` whenever `lib/` changes |
70
+ | `bin/ci` | Run everything CI runs, serially. Use before pushing |
71
+ | `bin/build` | Build the gem into `pkg/`; the publish workflow runs this before `gem push` |
72
+
73
+ ## Releasing
74
+
75
+ PR titles are [conventional commits](https://www.conventionalcommits.org/) and are linted in CI: `feat:` bumps the minor version, `fix:` bumps the patch, and `feat!:` marks a breaking change (also a minor bump while we are on 0.x). `chore:`, `docs:`, `ci:`, `refactor:` and `test:` never release. Squash-merging makes the title the commit on `main`.
76
+
77
+ [release-please](https://github.com/googleapis/release-please) keeps a release PR open that bumps `lib/acp/version.rb` and writes `CHANGELOG.md`. Merging that PR tags `vX.Y.Z`, creates the GitHub Release and publishes the gem to RubyGems.org through Trusted Publishing; nothing is pushed by hand. The first release is 0.1.0.
78
+
79
+ ## Maintainer
80
+
81
+ - Jake Bottrall - https://github.com/bottrall
@@ -0,0 +1,39 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ACP::AgentConnection::Client
4
+ # @rbs @peer: ACP::AgentConnection::_Peer
5
+
6
+ # @dynamic capabilities, capabilities=
7
+ attr_accessor :capabilities #: ACP::Types::ClientCapabilities?
8
+
9
+ # @rbs peer: ACP::AgentConnection::_Peer
10
+ # @rbs return: void
11
+ def initialize(peer:)
12
+ @peer = peer
13
+ @capabilities = nil
14
+ end
15
+
16
+ # @rbs session_id: String
17
+ # @rbs update: ACP::Types::SessionUpdate::t
18
+ # @rbs return: void
19
+ def update(session_id, update)
20
+ @peer.notify('session/update', ACP::Types::SessionNotification.new(session_id:, update:).to_h)
21
+ end
22
+
23
+ # @rbs session_id: String
24
+ # @rbs commands: Array[ACP::Types::AvailableCommand]
25
+ # @rbs return: void
26
+ def available_commands(session_id, commands)
27
+ update(session_id, ACP::Types::SessionUpdate::AvailableCommandsUpdate.new(available_commands: commands))
28
+ end
29
+
30
+ # Blocks the calling thread until the client answers or the connection
31
+ # closes.
32
+ #
33
+ # @rbs request: ACP::Types::RequestPermissionRequest
34
+ # @rbs return: ACP::Types::RequestPermissionResponse | ACP::Transport::ResponseError
35
+ def request_permission(request)
36
+ result = @peer.request('session/request_permission', request.to_h)
37
+ result.error || ACP::Types::RequestPermissionResponse.from_h(result.value)
38
+ end
39
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ACP::AgentConnection::OptionalMethod
4
+ # @rbs @capability: ^(ACP::Types::AgentCapabilities) -> boolish
5
+
6
+ # @dynamic rpc_method
7
+ attr_reader :rpc_method #: String
8
+
9
+ # @dynamic agent_method
10
+ attr_reader :agent_method #: Symbol
11
+
12
+ # @rbs rpc_method: String
13
+ # @rbs agent_method: Symbol
14
+ # @rbs &capability: (ACP::Types::AgentCapabilities) -> boolish
15
+ # @rbs return: void
16
+ def initialize(rpc_method:, agent_method:, &capability)
17
+ @rpc_method = rpc_method
18
+ @agent_method = agent_method
19
+ @capability = capability
20
+ freeze
21
+ end
22
+
23
+ # @rbs capabilities: ACP::Types::AgentCapabilities
24
+ # @rbs return: bool
25
+ def advertised?(capabilities)
26
+ @capability.call(capabilities) ? true : false
27
+ end
28
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ACP::AgentConnection
4
+ # @rbs @transport: ACP::AgentConnection::_Transport
5
+ # @rbs @capabilities: ACP::Types::AgentCapabilities
6
+ # @rbs @agent_info: ACP::Types::Implementation?
7
+ # @rbs @auth_methods: Array[ACP::Types::AuthMethod::t]
8
+ # @rbs @factory: ^(ACP::AgentConnection::Client) -> ACP::AgentConnection::_Agent
9
+
10
+ PROTOCOL_VERSION = 1 #: Integer
11
+ INVALID_PARAMS = ACP::Transport::ResponseError.new(code: -32_602, message: 'Invalid params') #: ACP::Transport::ResponseError
12
+ OPTIONAL = [
13
+ ACP::AgentConnection::OptionalMethod.new(rpc_method: 'session/load', agent_method: :load_session, &:load_session),
14
+ ACP::AgentConnection::OptionalMethod.new(rpc_method: 'session/list', agent_method: :list_sessions) do |capabilities|
15
+ capabilities.session_capabilities&.list
16
+ end
17
+ ].freeze #: Array[ACP::AgentConnection::OptionalMethod]
18
+
19
+ # @rbs transport: ACP::AgentConnection::_Transport
20
+ # @rbs capabilities: ACP::Types::AgentCapabilities
21
+ # @rbs agent_info: ACP::Types::Implementation?
22
+ # @rbs auth_methods: Array[ACP::Types::AuthMethod::t]
23
+ # @rbs &factory: (ACP::AgentConnection::Client) -> ACP::AgentConnection::_Agent
24
+ # @rbs return: void
25
+ def initialize(transport:, capabilities:, agent_info: nil, auth_methods: [], &factory)
26
+ @transport = transport
27
+ @capabilities = capabilities
28
+ @agent_info = agent_info
29
+ @auth_methods = auth_methods
30
+ @factory = factory
31
+ end
32
+
33
+ # @rbs return: Thread
34
+ def start
35
+ client = ACP::AgentConnection::Client.new(peer: @transport)
36
+ agent = @factory.call(client)
37
+ advertised, unadvertised = OPTIONAL.partition { |method| method.advertised?(@capabilities) }
38
+ missing = advertised.map(&:agent_method).reject { |name| agent.respond_to?(name) }
39
+ raise ArgumentError, "capabilities advertise methods the agent lacks: #{missing.join(', ')}" unless missing.empty?
40
+
41
+ unrouted = unadvertised.map(&:rpc_method)
42
+ @transport.start(requests: requests(agent, client).except(*unrouted), notifications: notifications(agent))
43
+ end
44
+
45
+ private
46
+
47
+ # @rbs agent: ACP::AgentConnection::_Agent
48
+ # @rbs client: ACP::AgentConnection::Client
49
+ # @rbs return: Hash[String, ^(untyped) -> (ACP::Transport::Result | ACP::Transport::Reply)]
50
+ def requests(agent, client)
51
+ # Safe: start drops the load/list handlers the capabilities do not
52
+ # advertise and checks the agent defines the rest.
53
+ full = agent #: ACP::AgentConnection::_Agent & ACP::AgentConnection::_LoadSession & ACP::AgentConnection::_ListSessions
54
+ {
55
+ 'initialize' => route(ACP::Types::InitializeRequest) { |request| connect(client, request) },
56
+ 'session/new' => route(ACP::Types::NewSessionRequest) { |request| new_session(agent, request) },
57
+ 'session/prompt' => route(ACP::Types::PromptRequest) { |request| respond(agent.prompt(request)) },
58
+ 'session/load' => route(ACP::Types::LoadSessionRequest) { |request| respond(full.load_session(request)) },
59
+ 'session/list' => route(ACP::Types::ListSessionsRequest) { |request| respond(full.list_sessions(request)) }
60
+ }
61
+ end
62
+
63
+ # @rbs agent: ACP::AgentConnection::_Agent
64
+ # @rbs return: Hash[String, ^(untyped) -> void]
65
+ def notifications(agent)
66
+ { 'session/cancel' => ->(params) { agent.cancel(ACP::Types::CancelNotification.from_h(params)) } }
67
+ end
68
+
69
+ # Generated from_h raises on a missing key or a value of the wrong shape.
70
+ #
71
+ # @rbs type: ACP::AgentConnection::_Parser
72
+ # @rbs &handle: (untyped) -> (ACP::Transport::Result | ACP::Transport::Reply)
73
+ # @rbs return: ^(untyped) -> (ACP::Transport::Result | ACP::Transport::Reply)
74
+ def route(type, &)
75
+ lambda do |params|
76
+ request = type.from_h(params)
77
+ rescue KeyError, TypeError, NoMethodError
78
+ ACP::Transport::Result.error(INVALID_PARAMS)
79
+ else
80
+ yield(request)
81
+ end
82
+ end
83
+
84
+ # @rbs response: ACP::AgentConnection::_Response | ACP::Transport::ResponseError
85
+ # @rbs return: ACP::Transport::Result
86
+ def respond(response)
87
+ case response
88
+ when ACP::Transport::ResponseError then ACP::Transport::Result.error(response)
89
+ else ACP::Transport::Result.ok(response.to_h)
90
+ end
91
+ end
92
+
93
+ # @rbs client: ACP::AgentConnection::Client
94
+ # @rbs request: ACP::Types::InitializeRequest
95
+ # @rbs return: ACP::Transport::Result
96
+ def connect(client, request)
97
+ client.capabilities = request.client_capabilities || ACP::Types::ClientCapabilities.new
98
+ respond(
99
+ ACP::Types::InitializeResponse.new(
100
+ protocol_version: PROTOCOL_VERSION,
101
+ agent_capabilities: @capabilities,
102
+ auth_methods: @auth_methods,
103
+ agent_info: @agent_info
104
+ )
105
+ )
106
+ end
107
+
108
+ # session_created runs after the reply because the client must know the
109
+ # session id before updates for it (e.g. available commands) arrive.
110
+ #
111
+ # @rbs agent: ACP::AgentConnection::_Agent
112
+ # @rbs request: ACP::Types::NewSessionRequest
113
+ # @rbs return: ACP::Transport::Result | ACP::Transport::Reply
114
+ def new_session(agent, request)
115
+ response = agent.new_session(request)
116
+ result = respond(response)
117
+ return result unless response.is_a?(ACP::Types::NewSessionResponse) && agent.respond_to?(:session_created)
118
+
119
+ hook = agent #: ACP::AgentConnection::_Agent & ACP::AgentConnection::_SessionCreated
120
+ ACP::Transport::Reply.new(result, after: -> { hook.session_created(response) })
121
+ end
122
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ACP::ClientConnection
4
+ # @rbs @transport: ACP::AgentConnection::_Transport
5
+ # @rbs @permission: ACP::ClientConnection::_PermissionHandler
6
+ # @rbs @updates: ACP::ClientConnection::_UpdateHandler
7
+ # @rbs @lock: Thread::Mutex
8
+ # @rbs @streams: Hash[String, Thread::Queue]
9
+
10
+ IGNORE = ->(_notification) {} #: ^(ACP::Types::SessionNotification) -> void
11
+
12
+ # @rbs transport: ACP::AgentConnection::_Transport
13
+ # @rbs permission: ACP::ClientConnection::_PermissionHandler
14
+ # @rbs updates: ACP::ClientConnection::_UpdateHandler
15
+ # @rbs return: void
16
+ def initialize(transport:, permission:, updates: IGNORE)
17
+ @transport = transport
18
+ @permission = permission
19
+ @updates = updates
20
+ @lock = Mutex.new
21
+ @streams = {}
22
+ end
23
+
24
+ # @rbs return: Thread
25
+ def start
26
+ @transport.start(
27
+ requests: { 'session/request_permission' => method(:request_permission) },
28
+ notifications: { 'session/update' => method(:dispatch) }
29
+ )
30
+ end
31
+
32
+ # Ruby reserves initialize for the constructor, so the initialize request is
33
+ # sent by connect.
34
+ #
35
+ # @rbs request: ACP::Types::InitializeRequest
36
+ # @rbs return: ACP::Types::InitializeResponse | ACP::Transport::ResponseError
37
+ def connect(request)
38
+ parse(ACP::Types::InitializeResponse, @transport.request('initialize', request.to_h))
39
+ end
40
+
41
+ # @rbs request: ACP::Types::NewSessionRequest
42
+ # @rbs return: ACP::Types::NewSessionResponse | ACP::Transport::ResponseError
43
+ def session_new(request)
44
+ parse(ACP::Types::NewSessionResponse, @transport.request('session/new', request.to_h))
45
+ end
46
+
47
+ # @rbs request: ACP::Types::PromptRequest
48
+ # @rbs &block: (ACP::Types::SessionUpdate::t) -> void
49
+ # @rbs return: ACP::Types::PromptResponse | ACP::Transport::ResponseError
50
+ def session_prompt(request, &)
51
+ parse(ACP::Types::PromptResponse, stream(request.session_id, 'session/prompt', request.to_h, &))
52
+ end
53
+
54
+ # @rbs request: ACP::Types::LoadSessionRequest
55
+ # @rbs &block: (ACP::Types::SessionUpdate::t) -> void
56
+ # @rbs return: ACP::Types::LoadSessionResponse | ACP::Transport::ResponseError
57
+ def session_load(request, &)
58
+ parse(ACP::Types::LoadSessionResponse, stream(request.session_id, 'session/load', request.to_h, &))
59
+ end
60
+
61
+ # @rbs request: ACP::Types::ListSessionsRequest
62
+ # @rbs return: ACP::Types::ListSessionsResponse | ACP::Transport::ResponseError
63
+ def session_list(request)
64
+ parse(ACP::Types::ListSessionsResponse, @transport.request('session/list', request.to_h))
65
+ end
66
+
67
+ # @rbs notification: ACP::Types::CancelNotification
68
+ # @rbs return: void
69
+ def session_cancel(notification)
70
+ @transport.notify('session/cancel', notification.to_h)
71
+ end
72
+
73
+ private
74
+
75
+ # @rbs type: ACP::AgentConnection::_Parser
76
+ # @rbs result: ACP::Transport::Result
77
+ # @rbs return: untyped
78
+ def parse(type, result)
79
+ result.error || type.from_h(result.value)
80
+ end
81
+
82
+ # The request waits on its own thread so the caller's thread can yield each
83
+ # update as it arrives. The reader thread queues an update before it settles
84
+ # a reply sent after it, so the queue closes only after the last one.
85
+ #
86
+ # @rbs session_id: String
87
+ # @rbs method: String
88
+ # @rbs params: Hash[String, untyped]
89
+ # @rbs &block: (ACP::Types::SessionUpdate::t) -> void
90
+ # @rbs return: ACP::Transport::Result
91
+ def stream(session_id, method, params)
92
+ queue = Thread::Queue.new
93
+ @lock.synchronize { @streams[session_id] = queue }
94
+ requester = Thread.new do
95
+ @transport.request(method, params)
96
+ ensure
97
+ @lock.synchronize { @streams.delete(session_id)&.close }
98
+ end
99
+ while (update = queue.pop)
100
+ yield update
101
+ end
102
+ requester.value
103
+ end
104
+
105
+ # Pushes under the lock so a stream cannot close its queue between the
106
+ # lookup and the push.
107
+ #
108
+ # @rbs params: untyped
109
+ # @rbs return: void
110
+ def dispatch(params)
111
+ notification = ACP::Types::SessionNotification.from_h(params)
112
+ queue = @lock.synchronize { @streams[notification.session_id]&.push(notification.update) }
113
+ @updates.call(notification) unless queue
114
+ end
115
+
116
+ # @rbs params: untyped
117
+ # @rbs return: ACP::Transport::Result
118
+ def request_permission(params)
119
+ request = ACP::Types::RequestPermissionRequest.from_h(params)
120
+ rescue KeyError, TypeError, NoMethodError
121
+ ACP::Transport::Result.error(ACP::AgentConnection::INVALID_PARAMS)
122
+ else
123
+ response = @permission.call(request)
124
+ case response
125
+ when ACP::Transport::ResponseError then ACP::Transport::Result.error(response)
126
+ else ACP::Transport::Result.ok(response.to_h)
127
+ end
128
+ end
129
+ end
data/lib/acp/sdk.rb ADDED
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'zeitwerk'
4
+ require_relative 'version'
5
+
6
+ # The gem is `acp-sdk` but its namespace is `ACP`, so lib/acp is the root
7
+ # directory for ACP rather than lib for a top-level constant.
8
+ root = __dir__ #: String
9
+ loader = Zeitwerk::Loader.new
10
+ loader.tag = 'acp-sdk'
11
+ loader.push_dir(root, namespace: ACP)
12
+ loader.ignore(__FILE__, "#{root}/version.rb")
13
+ loader.setup
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # A request handler's result plus a callback the transport runs once that
4
+ # result is on the wire, for messages the peer must only see after the reply.
5
+ class ACP::Transport::Reply
6
+ # @dynamic result
7
+ attr_reader :result #: ACP::Transport::Result
8
+
9
+ # @dynamic after
10
+ attr_reader :after #: ^() -> void
11
+
12
+ # @rbs result: ACP::Transport::Result
13
+ # @rbs after: ^() -> void
14
+ # @rbs return: void
15
+ def initialize(result, after:)
16
+ @result = result
17
+ @after = after
18
+ freeze
19
+ end
20
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ACP::Transport::ResponseError
4
+ # @dynamic code
5
+ attr_reader :code #: Integer
6
+
7
+ # @dynamic message
8
+ attr_reader :message #: String
9
+
10
+ # @dynamic data
11
+ attr_reader :data #: untyped
12
+
13
+ # @rbs code: Integer
14
+ # @rbs message: String
15
+ # @rbs data: untyped
16
+ # @rbs return: void
17
+ def initialize(code:, message:, data: nil)
18
+ @code = code
19
+ @message = message
20
+ @data = data
21
+ freeze
22
+ end
23
+
24
+ # @rbs return: Hash[String, untyped]
25
+ def to_h
26
+ { 'code' => code, 'message' => message, 'data' => data }.compact
27
+ end
28
+ end
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ class ACP::Transport::Result
4
+ # @dynamic value
5
+ attr_reader :value #: untyped
6
+
7
+ # @dynamic error
8
+ attr_reader :error #: ACP::Transport::ResponseError?
9
+
10
+ # @rbs value: untyped
11
+ # @rbs return: ACP::Transport::Result
12
+ def self.ok(value)
13
+ new(value:, error: nil)
14
+ end
15
+
16
+ # @rbs error: ACP::Transport::ResponseError
17
+ # @rbs return: ACP::Transport::Result
18
+ def self.error(error)
19
+ new(value: nil, error:)
20
+ end
21
+
22
+ # @rbs value: untyped
23
+ # @rbs error: ACP::Transport::ResponseError?
24
+ # @rbs return: void
25
+ def initialize(value:, error:)
26
+ @value = value
27
+ @error = error
28
+ freeze
29
+ end
30
+
31
+ # @rbs return: bool
32
+ def ok?
33
+ error.nil?
34
+ end
35
+ end