acp-sdk 0.7.0 → 0.9.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 (446) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +20 -0
  3. data/README.md +14 -1
  4. data/lib/acp/agent_connection/client.rb +1 -1
  5. data/lib/acp/agent_connection.rb +3 -1
  6. data/lib/acp/client_connection.rb +93 -4
  7. data/lib/acp/request_error.rb +15 -2
  8. data/lib/acp/sdk.rb +3 -1
  9. data/lib/acp/transport/cancellation.rb +25 -0
  10. data/lib/acp/transport/stdio.rb +67 -1
  11. data/lib/acp/types/agent_auth_capabilities.rb +4 -1
  12. data/lib/acp/types/agent_capabilities.rb +20 -5
  13. data/lib/acp/types/annotations.rb +6 -3
  14. data/lib/acp/types/audio_content.rb +8 -3
  15. data/lib/acp/types/auth_capabilities.rb +1 -1
  16. data/lib/acp/types/auth_method/terminal.rb +11 -5
  17. data/lib/acp/types/auth_method_agent.rb +6 -1
  18. data/lib/acp/types/auth_method_terminal.rb +11 -5
  19. data/lib/acp/types/authenticate_request.rb +1 -1
  20. data/lib/acp/types/available_command.rb +3 -3
  21. data/lib/acp/types/available_commands_update.rb +7 -4
  22. data/lib/acp/types/blob_resource_contents.rb +6 -1
  23. data/lib/acp/types/boolean_property_schema.rb +6 -1
  24. data/lib/acp/types/cancel_notification.rb +4 -1
  25. data/lib/acp/types/cancel_request_notification.rb +9 -1
  26. data/lib/acp/types/client_capabilities.rb +15 -5
  27. data/lib/acp/types/client_session_capabilities.rb +6 -3
  28. data/lib/acp/types/close_session_request.rb +4 -1
  29. data/lib/acp/types/complete_elicitation_notification.rb +4 -1
  30. data/lib/acp/types/config_option_update.rb +7 -4
  31. data/lib/acp/types/content.rb +4 -1
  32. data/lib/acp/types/content_block/audio.rb +8 -3
  33. data/lib/acp/types/content_block/image.rb +9 -4
  34. data/lib/acp/types/content_block/resource.rb +11 -2
  35. data/lib/acp/types/content_block/resource_link.rb +12 -7
  36. data/lib/acp/types/content_block/text.rb +7 -2
  37. data/lib/acp/types/content_chunk.rb +2 -2
  38. data/lib/acp/types/cost.rb +5 -1
  39. data/lib/acp/types/create_elicitation_request/mode/form.rb +5 -1
  40. data/lib/acp/types/create_elicitation_request/mode/url.rb +2 -2
  41. data/lib/acp/types/create_elicitation_request.rb +1 -1
  42. data/lib/acp/types/create_elicitation_response/action/accept.rb +6 -1
  43. data/lib/acp/types/create_terminal_request.rb +12 -6
  44. data/lib/acp/types/create_terminal_response.rb +4 -1
  45. data/lib/acp/types/current_mode_update.rb +4 -1
  46. data/lib/acp/types/delete_session_request.rb +4 -1
  47. data/lib/acp/types/diff.rb +6 -1
  48. data/lib/acp/types/elicitation_accept_action.rb +6 -1
  49. data/lib/acp/types/elicitation_capabilities.rb +2 -2
  50. data/lib/acp/types/elicitation_form_mode.rb +5 -1
  51. data/lib/acp/types/elicitation_property_schema/boolean_property_schema.rb +6 -1
  52. data/lib/acp/types/elicitation_property_schema/integer_property_schema.rb +5 -5
  53. data/lib/acp/types/elicitation_property_schema/multi_select_property_schema.rb +9 -6
  54. data/lib/acp/types/elicitation_property_schema/number_property_schema.rb +5 -5
  55. data/lib/acp/types/elicitation_property_schema/string_property_schema.rb +15 -9
  56. data/lib/acp/types/elicitation_request_scope.rb +8 -1
  57. data/lib/acp/types/elicitation_schema.rb +11 -5
  58. data/lib/acp/types/elicitation_session_scope.rb +4 -1
  59. data/lib/acp/types/elicitation_url_mode.rb +2 -2
  60. data/lib/acp/types/embedded_resource.rb +11 -2
  61. data/lib/acp/types/enum_option.rb +6 -1
  62. data/lib/acp/types/env_variable.rb +5 -1
  63. data/lib/acp/types/file_system_capabilities.rb +5 -1
  64. data/lib/acp/types/http_header.rb +5 -1
  65. data/lib/acp/types/image_content.rb +9 -4
  66. data/lib/acp/types/implementation.rb +6 -1
  67. data/lib/acp/types/initialize_request.rb +18 -3
  68. data/lib/acp/types/initialize_response.rb +17 -4
  69. data/lib/acp/types/integer_property_schema.rb +5 -5
  70. data/lib/acp/types/kill_terminal_request.rb +5 -1
  71. data/lib/acp/types/list_sessions_request.rb +5 -1
  72. data/lib/acp/types/list_sessions_response.rb +5 -2
  73. data/lib/acp/types/load_session_request.rb +14 -4
  74. data/lib/acp/types/load_session_response.rb +5 -2
  75. data/lib/acp/types/mcp_capabilities.rb +5 -1
  76. data/lib/acp/types/mcp_server/http.rb +6 -3
  77. data/lib/acp/types/mcp_server/sse.rb +6 -3
  78. data/lib/acp/types/mcp_server_http.rb +6 -3
  79. data/lib/acp/types/mcp_server_sse.rb +6 -3
  80. data/lib/acp/types/mcp_server_stdio.rb +10 -4
  81. data/lib/acp/types/multi_select_items/string_multi_select_items.rb +4 -1
  82. data/lib/acp/types/multi_select_property_schema.rb +9 -6
  83. data/lib/acp/types/new_session_request.rb +13 -3
  84. data/lib/acp/types/new_session_response.rb +6 -3
  85. data/lib/acp/types/number_property_schema.rb +5 -5
  86. data/lib/acp/types/permission_option.rb +10 -1
  87. data/lib/acp/types/plan.rb +4 -1
  88. data/lib/acp/types/plan_entry.rb +7 -3
  89. data/lib/acp/types/prompt_capabilities.rb +6 -1
  90. data/lib/acp/types/prompt_request.rb +5 -2
  91. data/lib/acp/types/prompt_response.rb +8 -1
  92. data/lib/acp/types/read_text_file_request.rb +4 -4
  93. data/lib/acp/types/read_text_file_response.rb +1 -1
  94. data/lib/acp/types/release_terminal_request.rb +5 -1
  95. data/lib/acp/types/request_permission_outcome/selected.rb +1 -1
  96. data/lib/acp/types/request_permission_request.rb +10 -3
  97. data/lib/acp/types/request_permission_response.rb +8 -1
  98. data/lib/acp/types/resource_link.rb +12 -7
  99. data/lib/acp/types/resume_session_request.rb +14 -4
  100. data/lib/acp/types/resume_session_response.rb +5 -2
  101. data/lib/acp/types/selected_permission_outcome.rb +1 -1
  102. data/lib/acp/types/session_capabilities.rb +20 -7
  103. data/lib/acp/types/session_config_boolean.rb +1 -1
  104. data/lib/acp/types/session_config_option/kind/boolean.rb +1 -1
  105. data/lib/acp/types/session_config_option/kind/select.rb +6 -2
  106. data/lib/acp/types/session_config_option.rb +3 -3
  107. data/lib/acp/types/session_config_options_capabilities.rb +6 -1
  108. data/lib/acp/types/session_config_select.rb +6 -2
  109. data/lib/acp/types/session_config_select_group.rb +6 -3
  110. data/lib/acp/types/session_config_select_option.rb +6 -1
  111. data/lib/acp/types/session_config_select_options.rb +6 -2
  112. data/lib/acp/types/session_info.rb +12 -5
  113. data/lib/acp/types/session_info_update.rb +10 -1
  114. data/lib/acp/types/session_mode.rb +6 -1
  115. data/lib/acp/types/session_mode_state.rb +8 -2
  116. data/lib/acp/types/session_notification.rb +2 -2
  117. data/lib/acp/types/session_update/agent_message_chunk.rb +2 -2
  118. data/lib/acp/types/session_update/agent_thought_chunk.rb +2 -2
  119. data/lib/acp/types/session_update/available_commands_update.rb +7 -4
  120. data/lib/acp/types/session_update/config_option_update.rb +7 -4
  121. data/lib/acp/types/session_update/current_mode_update.rb +4 -1
  122. data/lib/acp/types/session_update/plan.rb +4 -1
  123. data/lib/acp/types/session_update/session_info_update.rb +10 -1
  124. data/lib/acp/types/session_update/tool_call.rb +23 -7
  125. data/lib/acp/types/session_update/tool_call_update.rb +23 -7
  126. data/lib/acp/types/session_update/usage_update.rb +3 -3
  127. data/lib/acp/types/session_update/user_message_chunk.rb +2 -2
  128. data/lib/acp/types/set_session_config_option_request/value/boolean.rb +1 -1
  129. data/lib/acp/types/set_session_config_option_request/value/value_id.rb +1 -1
  130. data/lib/acp/types/set_session_config_option_request.rb +2 -2
  131. data/lib/acp/types/set_session_config_option_response.rb +7 -4
  132. data/lib/acp/types/set_session_mode_request.rb +5 -1
  133. data/lib/acp/types/string_multi_select_items.rb +4 -1
  134. data/lib/acp/types/string_property_schema.rb +15 -9
  135. data/lib/acp/types/terminal.rb +4 -1
  136. data/lib/acp/types/terminal_exit_status.rb +5 -1
  137. data/lib/acp/types/terminal_output_request.rb +5 -1
  138. data/lib/acp/types/terminal_output_response.rb +8 -3
  139. data/lib/acp/types/text_content.rb +7 -2
  140. data/lib/acp/types/text_resource_contents.rb +6 -1
  141. data/lib/acp/types/titled_multi_select_items.rb +4 -1
  142. data/lib/acp/types/tool_call.rb +23 -7
  143. data/lib/acp/types/tool_call_content/content.rb +4 -1
  144. data/lib/acp/types/tool_call_content/diff.rb +6 -1
  145. data/lib/acp/types/tool_call_content/terminal.rb +4 -1
  146. data/lib/acp/types/tool_call_location.rb +5 -1
  147. data/lib/acp/types/tool_call_update.rb +23 -7
  148. data/lib/acp/types/unstable/accept_nes_notification.rb +41 -0
  149. data/lib/acp/types/unstable/agent_capabilities.rb +122 -0
  150. data/lib/acp/types/unstable/client_capabilities.rb +127 -0
  151. data/lib/acp/types/unstable/client_nes_capabilities.rb +62 -0
  152. data/lib/acp/types/unstable/client_session_capabilities.rb +57 -0
  153. data/lib/acp/types/unstable/close_nes_request.rb +35 -0
  154. data/lib/acp/types/unstable/close_nes_response.rb +27 -0
  155. data/lib/acp/types/unstable/compaction_status.rb +11 -0
  156. data/lib/acp/types/unstable/compaction_summary_chunk.rb +41 -0
  157. data/lib/acp/types/unstable/compaction_update.rb +62 -0
  158. data/lib/acp/types/unstable/did_change_document_notification.rb +65 -0
  159. data/lib/acp/types/unstable/did_close_document_notification.rb +41 -0
  160. data/lib/acp/types/unstable/did_focus_document_notification.rb +74 -0
  161. data/lib/acp/types/unstable/did_open_document_notification.rb +66 -0
  162. data/lib/acp/types/unstable/did_save_document_notification.rb +41 -0
  163. data/lib/acp/types/unstable/disable_provider_request.rb +35 -0
  164. data/lib/acp/types/unstable/disable_provider_response.rb +27 -0
  165. data/lib/acp/types/unstable/fork_session_request.rb +69 -0
  166. data/lib/acp/types/unstable/fork_session_response.rb +55 -0
  167. data/lib/acp/types/unstable/idle_state_update.rb +46 -0
  168. data/lib/acp/types/unstable/initialize_response.rb +72 -0
  169. data/lib/acp/types/unstable/list_providers_request.rb +27 -0
  170. data/lib/acp/types/unstable/list_providers_response.rb +35 -0
  171. data/lib/acp/types/unstable/llm_protocol.rb +12 -0
  172. data/lib/acp/types/unstable/mcp_capabilities.rb +47 -0
  173. data/lib/acp/types/unstable/mcp_error.rb +41 -0
  174. data/lib/acp/types/unstable/mcp_server/acp.rb +41 -0
  175. data/lib/acp/types/unstable/mcp_server/http.rb +50 -0
  176. data/lib/acp/types/unstable/mcp_server/sse.rb +50 -0
  177. data/lib/acp/types/unstable/mcp_server.rb +25 -0
  178. data/lib/acp/types/unstable/mcp_server_acp.rb +41 -0
  179. data/lib/acp/types/unstable/message_mcp_notification.rb +59 -0
  180. data/lib/acp/types/unstable/message_mcp_request.rb +59 -0
  181. data/lib/acp/types/unstable/message_mcp_response/error.rb +35 -0
  182. data/lib/acp/types/unstable/message_mcp_response/result.rb +32 -0
  183. data/lib/acp/types/unstable/message_mcp_response.rb +23 -0
  184. data/lib/acp/types/unstable/nes_capabilities.rb +51 -0
  185. data/lib/acp/types/unstable/nes_context_capabilities.rb +111 -0
  186. data/lib/acp/types/unstable/nes_diagnostic.rb +57 -0
  187. data/lib/acp/types/unstable/nes_diagnostic_severity.rb +11 -0
  188. data/lib/acp/types/unstable/nes_diagnostics_capabilities.rb +27 -0
  189. data/lib/acp/types/unstable/nes_document_did_change_capabilities.rb +35 -0
  190. data/lib/acp/types/unstable/nes_document_did_close_capabilities.rb +27 -0
  191. data/lib/acp/types/unstable/nes_document_did_focus_capabilities.rb +27 -0
  192. data/lib/acp/types/unstable/nes_document_did_open_capabilities.rb +27 -0
  193. data/lib/acp/types/unstable/nes_document_did_save_capabilities.rb +27 -0
  194. data/lib/acp/types/unstable/nes_document_event_capabilities.rb +91 -0
  195. data/lib/acp/types/unstable/nes_edit_history_capabilities.rb +35 -0
  196. data/lib/acp/types/unstable/nes_edit_history_entry.rb +41 -0
  197. data/lib/acp/types/unstable/nes_edit_suggestion.rb +67 -0
  198. data/lib/acp/types/unstable/nes_event_capabilities.rb +40 -0
  199. data/lib/acp/types/unstable/nes_excerpt.rb +47 -0
  200. data/lib/acp/types/unstable/nes_jump_capabilities.rb +27 -0
  201. data/lib/acp/types/unstable/nes_jump_suggestion.rb +51 -0
  202. data/lib/acp/types/unstable/nes_open_file.rb +64 -0
  203. data/lib/acp/types/unstable/nes_open_files_capabilities.rb +27 -0
  204. data/lib/acp/types/unstable/nes_recent_file.rb +47 -0
  205. data/lib/acp/types/unstable/nes_recent_files_capabilities.rb +35 -0
  206. data/lib/acp/types/unstable/nes_reject_reason.rb +11 -0
  207. data/lib/acp/types/unstable/nes_related_snippet.rb +40 -0
  208. data/lib/acp/types/unstable/nes_related_snippets_capabilities.rb +27 -0
  209. data/lib/acp/types/unstable/nes_rename_capabilities.rb +27 -0
  210. data/lib/acp/types/unstable/nes_rename_suggestion.rb +57 -0
  211. data/lib/acp/types/unstable/nes_repository.rb +47 -0
  212. data/lib/acp/types/unstable/nes_search_and_replace_capabilities.rb +27 -0
  213. data/lib/acp/types/unstable/nes_search_and_replace_suggestion.rb +66 -0
  214. data/lib/acp/types/unstable/nes_suggest_context.rb +95 -0
  215. data/lib/acp/types/unstable/nes_suggestion/edit.rb +68 -0
  216. data/lib/acp/types/unstable/nes_suggestion/jump.rb +51 -0
  217. data/lib/acp/types/unstable/nes_suggestion/rename.rb +64 -0
  218. data/lib/acp/types/unstable/nes_suggestion/searchandreplace.rb +67 -0
  219. data/lib/acp/types/unstable/nes_suggestion.rb +25 -0
  220. data/lib/acp/types/unstable/nes_text_edit.rb +41 -0
  221. data/lib/acp/types/unstable/nes_trigger_kind.rb +10 -0
  222. data/lib/acp/types/unstable/nes_user_action.rb +63 -0
  223. data/lib/acp/types/unstable/nes_user_actions_capabilities.rb +35 -0
  224. data/lib/acp/types/unstable/notice.rb +47 -0
  225. data/lib/acp/types/unstable/notice_severity.rb +10 -0
  226. data/lib/acp/types/unstable/plan_capabilities.rb +27 -0
  227. data/lib/acp/types/unstable/plan_file.rb +41 -0
  228. data/lib/acp/types/unstable/plan_items.rb +44 -0
  229. data/lib/acp/types/unstable/plan_markdown.rb +41 -0
  230. data/lib/acp/types/unstable/plan_removed.rb +32 -0
  231. data/lib/acp/types/unstable/plan_update.rb +39 -0
  232. data/lib/acp/types/unstable/plan_update_content/file.rb +41 -0
  233. data/lib/acp/types/unstable/plan_update_content/items.rb +44 -0
  234. data/lib/acp/types/unstable/plan_update_content/markdown.rb +41 -0
  235. data/lib/acp/types/unstable/plan_update_content.rb +23 -0
  236. data/lib/acp/types/unstable/position.rb +41 -0
  237. data/lib/acp/types/unstable/position_encoding_kind.rb +10 -0
  238. data/lib/acp/types/unstable/prompt_response.rb +45 -0
  239. data/lib/acp/types/unstable/provider_current_config.rb +41 -0
  240. data/lib/acp/types/unstable/provider_info.rb +64 -0
  241. data/lib/acp/types/unstable/providers_capabilities.rb +27 -0
  242. data/lib/acp/types/unstable/range.rb +41 -0
  243. data/lib/acp/types/unstable/reject_nes_notification.rb +52 -0
  244. data/lib/acp/types/unstable/requires_action_state_update.rb +27 -0
  245. data/lib/acp/types/unstable/running_state_update.rb +27 -0
  246. data/lib/acp/types/unstable/session_cancel_capabilities.rb +27 -0
  247. data/lib/acp/types/unstable/session_capabilities.rb +93 -0
  248. data/lib/acp/types/unstable/session_fork_capabilities.rb +27 -0
  249. data/lib/acp/types/unstable/session_message.rb +62 -0
  250. data/lib/acp/types/unstable/session_message_chunk.rb +59 -0
  251. data/lib/acp/types/unstable/session_update/agent_message_chunk.rb +46 -0
  252. data/lib/acp/types/unstable/session_update/agent_thought_chunk.rb +46 -0
  253. data/lib/acp/types/unstable/session_update/available_commands_update.rb +42 -0
  254. data/lib/acp/types/unstable/session_update/compaction_summary_chunk.rb +46 -0
  255. data/lib/acp/types/unstable/session_update/compaction_update.rb +63 -0
  256. data/lib/acp/types/unstable/session_update/config_option_update.rb +42 -0
  257. data/lib/acp/types/unstable/session_update/current_mode_update.rb +35 -0
  258. data/lib/acp/types/unstable/session_update/notice.rb +53 -0
  259. data/lib/acp/types/unstable/session_update/plan.rb +35 -0
  260. data/lib/acp/types/unstable/session_update/plan_removed.rb +32 -0
  261. data/lib/acp/types/unstable/session_update/plan_update.rb +39 -0
  262. data/lib/acp/types/unstable/session_update/session_info_update.rb +49 -0
  263. data/lib/acp/types/unstable/session_update/session_message.rb +63 -0
  264. data/lib/acp/types/unstable/session_update/session_message_chunk.rb +60 -0
  265. data/lib/acp/types/unstable/session_update/subagent_update.rb +72 -0
  266. data/lib/acp/types/unstable/session_update/tool_call.rb +122 -0
  267. data/lib/acp/types/unstable/session_update/tool_call_update.rb +122 -0
  268. data/lib/acp/types/unstable/session_update/usage_update.rb +47 -0
  269. data/lib/acp/types/unstable/session_update/user_message_chunk.rb +46 -0
  270. data/lib/acp/types/unstable/session_update.rb +55 -0
  271. data/lib/acp/types/unstable/set_provider_request.rb +62 -0
  272. data/lib/acp/types/unstable/set_provider_response.rb +27 -0
  273. data/lib/acp/types/unstable/start_nes_request.rb +64 -0
  274. data/lib/acp/types/unstable/start_nes_response.rb +35 -0
  275. data/lib/acp/types/unstable/state_update/idle.rb +46 -0
  276. data/lib/acp/types/unstable/state_update/requires_action.rb +27 -0
  277. data/lib/acp/types/unstable/state_update/running.rb +27 -0
  278. data/lib/acp/types/unstable/state_update/unknown.rb +27 -0
  279. data/lib/acp/types/unstable/state_update.rb +25 -0
  280. data/lib/acp/types/unstable/subagent_capabilities.rb +27 -0
  281. data/lib/acp/types/unstable/subagent_session_capabilities.rb +40 -0
  282. data/lib/acp/types/unstable/subagent_update.rb +71 -0
  283. data/lib/acp/types/unstable/suggest_nes_request.rb +93 -0
  284. data/lib/acp/types/unstable/suggest_nes_response.rb +35 -0
  285. data/lib/acp/types/unstable/text_document_content_change_event.rb +41 -0
  286. data/lib/acp/types/unstable/text_document_sync_kind.rb +9 -0
  287. data/lib/acp/types/unstable/unknown_state_update.rb +27 -0
  288. data/lib/acp/types/unstable/usage.rb +91 -0
  289. data/lib/acp/types/unstable/workspace_folder.rb +41 -0
  290. data/lib/acp/types/unstable.rb +155 -0
  291. data/lib/acp/types/unstructured_command_input.rb +1 -1
  292. data/lib/acp/types/usage_update.rb +3 -3
  293. data/lib/acp/types/wait_for_terminal_exit_request.rb +5 -1
  294. data/lib/acp/types/wait_for_terminal_exit_response.rb +5 -1
  295. data/lib/acp/types/write_text_file_request.rb +3 -3
  296. data/lib/acp/types.rb +247 -0
  297. data/lib/acp/version.rb +1 -1
  298. data/sig/generated/acp/client_connection.rbs +39 -1
  299. data/sig/generated/acp/request_error.rbs +12 -1
  300. data/sig/generated/acp/transport/cancellation.rbs +19 -0
  301. data/sig/generated/acp/transport/stdio.rbs +38 -0
  302. data/sig/generated/acp/types/unstable/accept_nes_notification.rbs +25 -0
  303. data/sig/generated/acp/types/unstable/agent_capabilities.rbs +49 -0
  304. data/sig/generated/acp/types/unstable/client_capabilities.rbs +53 -0
  305. data/sig/generated/acp/types/unstable/client_nes_capabilities.rbs +29 -0
  306. data/sig/generated/acp/types/unstable/client_session_capabilities.rbs +29 -0
  307. data/sig/generated/acp/types/unstable/close_nes_request.rbs +21 -0
  308. data/sig/generated/acp/types/unstable/close_nes_response.rbs +17 -0
  309. data/sig/generated/acp/types/unstable/compaction_status.rbs +11 -0
  310. data/sig/generated/acp/types/unstable/compaction_summary_chunk.rbs +25 -0
  311. data/sig/generated/acp/types/unstable/compaction_update.rbs +33 -0
  312. data/sig/generated/acp/types/unstable/did_change_document_notification.rbs +33 -0
  313. data/sig/generated/acp/types/unstable/did_close_document_notification.rbs +25 -0
  314. data/sig/generated/acp/types/unstable/did_focus_document_notification.rbs +37 -0
  315. data/sig/generated/acp/types/unstable/did_open_document_notification.rbs +37 -0
  316. data/sig/generated/acp/types/unstable/did_save_document_notification.rbs +25 -0
  317. data/sig/generated/acp/types/unstable/disable_provider_request.rbs +21 -0
  318. data/sig/generated/acp/types/unstable/disable_provider_response.rbs +17 -0
  319. data/sig/generated/acp/types/unstable/fork_session_request.rbs +33 -0
  320. data/sig/generated/acp/types/unstable/fork_session_response.rbs +29 -0
  321. data/sig/generated/acp/types/unstable/idle_state_update.rbs +25 -0
  322. data/sig/generated/acp/types/unstable/initialize_response.rbs +33 -0
  323. data/sig/generated/acp/types/unstable/list_providers_request.rbs +17 -0
  324. data/sig/generated/acp/types/unstable/list_providers_response.rbs +21 -0
  325. data/sig/generated/acp/types/unstable/llm_protocol.rbs +13 -0
  326. data/sig/generated/acp/types/unstable/mcp_capabilities.rbs +29 -0
  327. data/sig/generated/acp/types/unstable/mcp_error.rbs +25 -0
  328. data/sig/generated/acp/types/unstable/mcp_server/acp.rbs +25 -0
  329. data/sig/generated/acp/types/unstable/mcp_server/http.rbs +29 -0
  330. data/sig/generated/acp/types/unstable/mcp_server/sse.rbs +29 -0
  331. data/sig/generated/acp/types/unstable/mcp_server.rbs +9 -0
  332. data/sig/generated/acp/types/unstable/mcp_server_acp.rbs +25 -0
  333. data/sig/generated/acp/types/unstable/message_mcp_notification.rbs +33 -0
  334. data/sig/generated/acp/types/unstable/message_mcp_request.rbs +33 -0
  335. data/sig/generated/acp/types/unstable/message_mcp_response/error.rbs +21 -0
  336. data/sig/generated/acp/types/unstable/message_mcp_response/result.rbs +21 -0
  337. data/sig/generated/acp/types/unstable/message_mcp_response.rbs +9 -0
  338. data/sig/generated/acp/types/unstable/nes_capabilities.rbs +25 -0
  339. data/sig/generated/acp/types/unstable/nes_context_capabilities.rbs +41 -0
  340. data/sig/generated/acp/types/unstable/nes_diagnostic.rbs +33 -0
  341. data/sig/generated/acp/types/unstable/nes_diagnostic_severity.rbs +11 -0
  342. data/sig/generated/acp/types/unstable/nes_diagnostics_capabilities.rbs +17 -0
  343. data/sig/generated/acp/types/unstable/nes_document_did_change_capabilities.rbs +21 -0
  344. data/sig/generated/acp/types/unstable/nes_document_did_close_capabilities.rbs +17 -0
  345. data/sig/generated/acp/types/unstable/nes_document_did_focus_capabilities.rbs +17 -0
  346. data/sig/generated/acp/types/unstable/nes_document_did_open_capabilities.rbs +17 -0
  347. data/sig/generated/acp/types/unstable/nes_document_did_save_capabilities.rbs +17 -0
  348. data/sig/generated/acp/types/unstable/nes_document_event_capabilities.rbs +37 -0
  349. data/sig/generated/acp/types/unstable/nes_edit_history_capabilities.rbs +21 -0
  350. data/sig/generated/acp/types/unstable/nes_edit_history_entry.rbs +25 -0
  351. data/sig/generated/acp/types/unstable/nes_edit_suggestion.rbs +33 -0
  352. data/sig/generated/acp/types/unstable/nes_event_capabilities.rbs +21 -0
  353. data/sig/generated/acp/types/unstable/nes_excerpt.rbs +29 -0
  354. data/sig/generated/acp/types/unstable/nes_jump_capabilities.rbs +17 -0
  355. data/sig/generated/acp/types/unstable/nes_jump_suggestion.rbs +29 -0
  356. data/sig/generated/acp/types/unstable/nes_open_file.rbs +33 -0
  357. data/sig/generated/acp/types/unstable/nes_open_files_capabilities.rbs +17 -0
  358. data/sig/generated/acp/types/unstable/nes_recent_file.rbs +29 -0
  359. data/sig/generated/acp/types/unstable/nes_recent_files_capabilities.rbs +21 -0
  360. data/sig/generated/acp/types/unstable/nes_reject_reason.rbs +11 -0
  361. data/sig/generated/acp/types/unstable/nes_related_snippet.rbs +25 -0
  362. data/sig/generated/acp/types/unstable/nes_related_snippets_capabilities.rbs +17 -0
  363. data/sig/generated/acp/types/unstable/nes_rename_capabilities.rbs +17 -0
  364. data/sig/generated/acp/types/unstable/nes_rename_suggestion.rbs +33 -0
  365. data/sig/generated/acp/types/unstable/nes_repository.rbs +29 -0
  366. data/sig/generated/acp/types/unstable/nes_search_and_replace_capabilities.rbs +17 -0
  367. data/sig/generated/acp/types/unstable/nes_search_and_replace_suggestion.rbs +37 -0
  368. data/sig/generated/acp/types/unstable/nes_suggest_context.rbs +41 -0
  369. data/sig/generated/acp/types/unstable/nes_suggestion/edit.rbs +33 -0
  370. data/sig/generated/acp/types/unstable/nes_suggestion/jump.rbs +29 -0
  371. data/sig/generated/acp/types/unstable/nes_suggestion/rename.rbs +33 -0
  372. data/sig/generated/acp/types/unstable/nes_suggestion/searchandreplace.rbs +37 -0
  373. data/sig/generated/acp/types/unstable/nes_suggestion.rbs +9 -0
  374. data/sig/generated/acp/types/unstable/nes_text_edit.rbs +25 -0
  375. data/sig/generated/acp/types/unstable/nes_trigger_kind.rbs +9 -0
  376. data/sig/generated/acp/types/unstable/nes_user_action.rbs +33 -0
  377. data/sig/generated/acp/types/unstable/nes_user_actions_capabilities.rbs +21 -0
  378. data/sig/generated/acp/types/unstable/notice.rbs +29 -0
  379. data/sig/generated/acp/types/unstable/notice_severity.rbs +9 -0
  380. data/sig/generated/acp/types/unstable/plan_capabilities.rbs +17 -0
  381. data/sig/generated/acp/types/unstable/plan_file.rbs +25 -0
  382. data/sig/generated/acp/types/unstable/plan_items.rbs +25 -0
  383. data/sig/generated/acp/types/unstable/plan_markdown.rbs +25 -0
  384. data/sig/generated/acp/types/unstable/plan_removed.rbs +21 -0
  385. data/sig/generated/acp/types/unstable/plan_update.rbs +21 -0
  386. data/sig/generated/acp/types/unstable/plan_update_content/file.rbs +25 -0
  387. data/sig/generated/acp/types/unstable/plan_update_content/items.rbs +25 -0
  388. data/sig/generated/acp/types/unstable/plan_update_content/markdown.rbs +25 -0
  389. data/sig/generated/acp/types/unstable/plan_update_content.rbs +9 -0
  390. data/sig/generated/acp/types/unstable/position.rbs +25 -0
  391. data/sig/generated/acp/types/unstable/position_encoding_kind.rbs +9 -0
  392. data/sig/generated/acp/types/unstable/prompt_response.rbs +25 -0
  393. data/sig/generated/acp/types/unstable/provider_current_config.rbs +25 -0
  394. data/sig/generated/acp/types/unstable/provider_info.rbs +33 -0
  395. data/sig/generated/acp/types/unstable/providers_capabilities.rbs +17 -0
  396. data/sig/generated/acp/types/unstable/range.rbs +25 -0
  397. data/sig/generated/acp/types/unstable/reject_nes_notification.rbs +29 -0
  398. data/sig/generated/acp/types/unstable/requires_action_state_update.rbs +17 -0
  399. data/sig/generated/acp/types/unstable/running_state_update.rbs +17 -0
  400. data/sig/generated/acp/types/unstable/session_cancel_capabilities.rbs +17 -0
  401. data/sig/generated/acp/types/unstable/session_capabilities.rbs +41 -0
  402. data/sig/generated/acp/types/unstable/session_fork_capabilities.rbs +17 -0
  403. data/sig/generated/acp/types/unstable/session_message.rbs +33 -0
  404. data/sig/generated/acp/types/unstable/session_message_chunk.rbs +33 -0
  405. data/sig/generated/acp/types/unstable/session_update/agent_message_chunk.rbs +25 -0
  406. data/sig/generated/acp/types/unstable/session_update/agent_thought_chunk.rbs +25 -0
  407. data/sig/generated/acp/types/unstable/session_update/available_commands_update.rbs +21 -0
  408. data/sig/generated/acp/types/unstable/session_update/compaction_summary_chunk.rbs +25 -0
  409. data/sig/generated/acp/types/unstable/session_update/compaction_update.rbs +33 -0
  410. data/sig/generated/acp/types/unstable/session_update/config_option_update.rbs +21 -0
  411. data/sig/generated/acp/types/unstable/session_update/current_mode_update.rbs +21 -0
  412. data/sig/generated/acp/types/unstable/session_update/notice.rbs +29 -0
  413. data/sig/generated/acp/types/unstable/session_update/plan.rbs +21 -0
  414. data/sig/generated/acp/types/unstable/session_update/plan_removed.rbs +21 -0
  415. data/sig/generated/acp/types/unstable/session_update/plan_update.rbs +21 -0
  416. data/sig/generated/acp/types/unstable/session_update/session_info_update.rbs +25 -0
  417. data/sig/generated/acp/types/unstable/session_update/session_message.rbs +33 -0
  418. data/sig/generated/acp/types/unstable/session_update/session_message_chunk.rbs +33 -0
  419. data/sig/generated/acp/types/unstable/session_update/subagent_update.rbs +37 -0
  420. data/sig/generated/acp/types/unstable/session_update/tool_call.rbs +53 -0
  421. data/sig/generated/acp/types/unstable/session_update/tool_call_update.rbs +53 -0
  422. data/sig/generated/acp/types/unstable/session_update/usage_update.rbs +29 -0
  423. data/sig/generated/acp/types/unstable/session_update/user_message_chunk.rbs +25 -0
  424. data/sig/generated/acp/types/unstable/session_update.rbs +9 -0
  425. data/sig/generated/acp/types/unstable/set_provider_request.rbs +33 -0
  426. data/sig/generated/acp/types/unstable/set_provider_response.rbs +17 -0
  427. data/sig/generated/acp/types/unstable/start_nes_request.rbs +29 -0
  428. data/sig/generated/acp/types/unstable/start_nes_response.rbs +21 -0
  429. data/sig/generated/acp/types/unstable/state_update/idle.rbs +25 -0
  430. data/sig/generated/acp/types/unstable/state_update/requires_action.rbs +17 -0
  431. data/sig/generated/acp/types/unstable/state_update/running.rbs +17 -0
  432. data/sig/generated/acp/types/unstable/state_update/unknown.rbs +17 -0
  433. data/sig/generated/acp/types/unstable/state_update.rbs +9 -0
  434. data/sig/generated/acp/types/unstable/subagent_capabilities.rbs +17 -0
  435. data/sig/generated/acp/types/unstable/subagent_session_capabilities.rbs +21 -0
  436. data/sig/generated/acp/types/unstable/subagent_update.rbs +37 -0
  437. data/sig/generated/acp/types/unstable/suggest_nes_request.rbs +45 -0
  438. data/sig/generated/acp/types/unstable/suggest_nes_response.rbs +21 -0
  439. data/sig/generated/acp/types/unstable/text_document_content_change_event.rbs +25 -0
  440. data/sig/generated/acp/types/unstable/text_document_sync_kind.rbs +7 -0
  441. data/sig/generated/acp/types/unstable/unknown_state_update.rbs +17 -0
  442. data/sig/generated/acp/types/unstable/usage.rbs +41 -0
  443. data/sig/generated/acp/types/unstable/workspace_folder.rbs +25 -0
  444. data/sig/generated/acp/types/unstable.rbs +4 -0
  445. data/sig/generated/acp/types.rbs +143 -0
  446. metadata +289 -1
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 978b245a94b3cd60d3858ee42c3deaf9b687c8a6a1701ce54c684a5e434b2bce
4
- data.tar.gz: b83adfe811bc7873455f9f81993061e7b2ea66d0a75b78645807fc44e08cd58b
3
+ metadata.gz: e8a4bb93508ff8a9737b9b5b044e4b63e19ba87393490f10f522b9b63aee4396
4
+ data.tar.gz: 9e62dd726471fd718c2a234b9ea65bc90ca473c67d979e207b2702edd33b30b5
5
5
  SHA512:
6
- metadata.gz: 2907a71594882b7afd3ed8fe89b2e9520f139dc1c235a4e43bf836a2f127ae6264ce948d86885577daa6cd320078c6da831f4b8df89225201287276e17710c4e
7
- data.tar.gz: 9191c0362932d37aaeb55666fb0b892505e448949f13e4173d0d9a0196ccebd16e56324f42db33f6c0d47fbec658bce6aac6e1bec4e4e9d4bdd4d6a1237ffc11
6
+ metadata.gz: 50ef3954c1104ef512b757421bb0df10d3eb0be7f245c5a26c8641e815fa54d160a1d4d2c8369b9b41eb4b64affce206c584627650a5ce3446be09f88f2239b4
7
+ data.tar.gz: 0e505cd00fd0fef743dc1c047892db07b1ea4bf0999b3c0bff402fc10f73609a23fcbd4310b4a8deb7822fd06d8a64d1bab517426302004c205b3eee2972a104
data/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.0](https://github.com/bottrall/acp-sdk/compare/v0.8.0...v0.9.0) (2026-10-07)
4
+
5
+
6
+ ### Features
7
+
8
+ * $/cancel_request in ACP::Transport::Stdio ([#82](https://github.com/bottrall/acp-sdk/issues/82)) ([8679883](https://github.com/bottrall/acp-sdk/commit/86798837ce9e35949c7cc3770e52f3191d81bc6f))
9
+ * opt-in types for the unstable schema ([#83](https://github.com/bottrall/acp-sdk/issues/83)) ([a49a5cc](https://github.com/bottrall/acp-sdk/commit/a49a5ccba84e599fc8fae95fe2336a4eb3169835))
10
+
11
+ ## [0.8.0](https://github.com/bottrall/acp-sdk/compare/v0.7.0...v0.8.0) (2026-10-06)
12
+
13
+
14
+ ### Features
15
+
16
+ * elicitation handlers on ACP::ClientConnection ([#78](https://github.com/bottrall/acp-sdk/issues/78)) ([94ee653](https://github.com/bottrall/acp-sdk/commit/94ee65374a0b463ccbd7ed57e05fe1ad76b08a62))
17
+
18
+
19
+ ### Bug Fixes
20
+
21
+ * validate primitive and enum types when parsing params ([#79](https://github.com/bottrall/acp-sdk/issues/79)) ([624be63](https://github.com/bottrall/acp-sdk/commit/624be63f667468fba7c1a61064b84f43c226b060))
22
+
3
23
  ## [0.7.0](https://github.com/bottrall/acp-sdk/compare/v0.6.0...v0.7.0) (2026-10-05)
4
24
 
5
25
 
data/README.md CHANGED
@@ -49,7 +49,7 @@ connection.start.join
49
49
  The agent's contract:
50
50
 
51
51
  - It defines `new_session`, `prompt` and `cancel`. `load_session`, `list_sessions`, `resume_session`, `close_session` and `delete_session` are required only when `capabilities` advertises `load_session` or `session_capabilities.list`, `.resume`, `.close` or `.delete`, and `authenticate` only when `auth_methods` is non-empty, and `logout` only when `capabilities` advertises `auth.logout`; `start` raises `ArgumentError` when one is advertised but missing, and a method that is not advertised is answered with `-32601`. `change_session_mode` and `change_session_config_option` are optional, since modes and config options are offered per session rather than in `initialize`; `session/set_mode` and `session/set_config_option` are answered with `-32601` when the agent does not define them. `session_created`, if defined, runs right after the `session/new` reply is sent.
52
- - Each request method returns its response type or an `ACP::RequestError`, which is sent as the error reply. Params that do not match the schema are answered with `-32602` before the agent sees them.
52
+ - Each request method returns its response type or an `ACP::RequestError`, which is sent as the error reply. Params that do not match the schema are answered with `-32602` before the agent sees them, with `data` naming the failing path, e.g. `{ errors: ['sessionId: expected String, got Integer'] }`.
53
53
  - A handler that raises is answered with `-32603` and the message `Internal error`. Nothing from the exception reaches the peer — its class and message are logged to the transport's logger instead, since messages can interpolate paths, queries or credentials. Return an `ACP::RequestError` from the handler when the peer should see a real error message.
54
54
  - `cancel` runs on the transport's reader thread, so it must return quickly: set a flag and let the prompt notice it.
55
55
  - After a cancel, the agent must itself end the turn with `stopReason: cancelled`. `ACP::AgentConnection` does not enforce it.
@@ -107,9 +107,22 @@ connection = ACP::ClientConnection.new(
107
107
  - `permission` answers `session/request_permission` with an `ACP::Types::RequestPermissionResponse` or an `ACP::RequestError`. It runs on its own thread, so it may block while the user decides, and `session_cancel` can be sent meanwhile.
108
108
  - `read_text_file` and `write_text_file` answer the agent's `fs/read_text_file` and `fs/write_text_file` with an `ACP::Types::ReadTextFileResponse`, an `ACP::Types::WriteTextFileResponse` or an `ACP::RequestError`. They are routed by the `fs` capabilities `connect` sends: a method the client does not advertise is answered with `-32601` without reaching the handler, and `connect` raises `ArgumentError` — before sending `initialize` — when the request advertises a capability that has no handler.
109
109
  - `create_terminal`, `terminal_output`, `wait_for_terminal_exit`, `kill_terminal` and `release_terminal` answer the agent's `terminal/create`, `terminal/output`, `terminal/wait_for_exit`, `terminal/kill` and `terminal/release` with the matching `ACP::Types` response or an `ACP::RequestError`. They are routed by the `terminal` capability `connect` sends, the same way as `fs`: a method the client does not advertise is answered with `-32601` without reaching the handler, and `connect` raises `ArgumentError` — before sending `initialize` — when the request advertises `terminal` with any of the five missing. The transport serves each request on its own thread, so `wait_for_terminal_exit` may block without holding up the others.
110
+ - `elicitation` answers the agent's `elicitation/create` with an `ACP::Types::CreateElicitationResponse` or an `ACP::RequestError`, serving both form and url mode, and `complete_elicitation` receives the agent's `elicitation/complete` notification. They are routed by the `elicitation` capabilities `connect` sends: a request whose mode the client does not advertise is answered with `-32602` without reaching the handler, and `connect` raises `ArgumentError` — before sending `initialize` — when the request advertises `elicitation.form` or `elicitation.url` with no `elicitation` handler. The client tracks the elicitation ids of the url-mode requests it serves, so a completion for an unknown or already-completed id is ignored. `complete_elicitation` runs on the reader thread and must return quickly; it may be omitted, in which case completions are only tracked.
110
111
 
111
112
  Extensions work the same way in both directions: `extension_requests` and `extension_notifications` are hashes keyed by the raw `_`-prefixed wire name, `ext_request` sends one to the agent and returns its result as-is, and `ext_notify` sends an extension notification. A handler name without the `_` prefix raises `ArgumentError`, and an extension request nothing answers gets `-32601`.
112
113
 
114
+ ## Unstable types
115
+
116
+ Some protocol features are not part of the spec yet, so their schema lives in a separate unstable file: draft session updates (`plan_update`, `plan_removed`, `notice`, `compaction_update`, `compaction_summary_chunk`, `subagent_update`, `session_message`, `session_message_chunk`), `PromptResponse#usage`, the NE (next edit) and provider methods, and the matching capabilities. They are generated under `ACP::Types::Unstable` and load only when you opt in:
117
+
118
+ ```ruby
119
+ require 'acp/types/unstable'
120
+ ```
121
+
122
+ Until then, referencing `ACP::Types::Unstable` is a `NameError`, and the stable `ACP::Types` namespace is byte-identical either way: types the unstable schema shares with the stable one resolve to the same generated classes, so opting in adds names without changing or duplicating any stable one.
123
+
124
+ These types are **unstable**: they are not part of the ACP spec, agents and clients must not rely on the peer supporting them, and any of them may change or disappear between releases without a major version bump. When a stable schema absorbs one of them, the unstable twin goes away and the stable name stays.
125
+
113
126
  ## Contributing
114
127
 
115
128
  Bug reports and pull requests are welcome on [GitHub](https://github.com/bottrall/acp-sdk). See [CONTRIBUTING.md](CONTRIBUTING.md) for setup and workflow. Everyone interacting in the project is expected to follow the [code of conduct](CODE_OF_CONDUCT.md).
@@ -136,7 +136,7 @@ class ACP::AgentConnection::Client
136
136
  return result if result.is_a?(ACP::RequestError)
137
137
 
138
138
  type.from_h(result)
139
- rescue KeyError, TypeError, NoMethodError
139
+ rescue ACP::Types::ParseError, KeyError, TypeError, NoMethodError
140
140
  ACP::RequestError.invalid_response(result)
141
141
  end
142
142
 
@@ -117,7 +117,7 @@ class ACP::AgentConnection
117
117
  # @rbs return: void
118
118
  def cancel(agent, params)
119
119
  notification = ACP::Types::CancelNotification.from_h(params)
120
- rescue KeyError, TypeError, NoMethodError => e
120
+ rescue ACP::Types::ParseError, KeyError, TypeError, NoMethodError => e
121
121
  @logger.warn("dropped malformed session/cancel: #{e.class}: #{e.message}")
122
122
  else
123
123
  agent.cancel(notification)
@@ -131,6 +131,8 @@ class ACP::AgentConnection
131
131
  def route(type, &)
132
132
  lambda do |params|
133
133
  request = type.from_h(params)
134
+ rescue ACP::Types::ParseError => e
135
+ ACP::RequestError.invalid_params([e.message])
134
136
  rescue KeyError, TypeError, NoMethodError
135
137
  ACP::RequestError.invalid_params
136
138
  else
@@ -11,14 +11,19 @@ class ACP::ClientConnection
11
11
  # (ACP::Types::WaitForTerminalExitResponse | ACP::RequestError))?
12
12
  # @rbs @kill_terminal: (^(ACP::Types::KillTerminalRequest) -> (ACP::Types::KillTerminalResponse | ACP::RequestError))?
13
13
  # @rbs @release_terminal: (^(ACP::Types::ReleaseTerminalRequest) -> (ACP::Types::ReleaseTerminalResponse | ACP::RequestError))?
14
+ # @rbs @elicitation: (^(ACP::Types::CreateElicitationRequest) ->
15
+ # (ACP::Types::CreateElicitationResponse | ACP::RequestError))?
16
+ # @rbs @complete_elicitation: (^(ACP::Types::CompleteElicitationNotification) -> void)?
14
17
  # @rbs @updates: ACP::ClientConnection::_UpdateHandler
15
18
  # @rbs @fs_capabilities: ACP::Types::FileSystemCapabilities?
16
19
  # @rbs @terminal: bool?
20
+ # @rbs @elicitation_capabilities: ACP::Types::ElicitationCapabilities?
17
21
  # @rbs @extension_requests: Hash[String, ^(untyped) -> untyped]
18
22
  # @rbs @extension_notifications: Hash[String, ^(untyped) -> void]
19
23
  # @rbs @lock: Thread::Mutex
20
24
  # @rbs @streams: Hash[String, Thread::Queue]
21
25
  # @rbs @pending_permissions: Hash[String, Array[Thread::Queue]]
26
+ # @rbs @outstanding_elicitations: Hash[String, bool]
22
27
  # @rbs @logger: ACP::Transport::_Logger
23
28
 
24
29
  PROTOCOL_VERSION = ACP::AgentConnection::PROTOCOL_VERSION #: Integer
@@ -39,6 +44,9 @@ class ACP::ClientConnection
39
44
  # (ACP::Types::WaitForTerminalExitResponse | ACP::RequestError))?
40
45
  # @rbs kill_terminal: (^(ACP::Types::KillTerminalRequest) -> (ACP::Types::KillTerminalResponse | ACP::RequestError))?
41
46
  # @rbs release_terminal: (^(ACP::Types::ReleaseTerminalRequest) -> (ACP::Types::ReleaseTerminalResponse | ACP::RequestError))?
47
+ # @rbs elicitation: (^(ACP::Types::CreateElicitationRequest) ->
48
+ # (ACP::Types::CreateElicitationResponse | ACP::RequestError))?
49
+ # @rbs complete_elicitation: (^(ACP::Types::CompleteElicitationNotification) -> void)?
42
50
  # @rbs updates: ACP::ClientConnection::_UpdateHandler
43
51
  # @rbs extension_requests: Hash[String, ^(untyped) -> untyped]
44
52
  # @rbs extension_notifications: Hash[String, ^(untyped) -> void]
@@ -54,6 +62,8 @@ class ACP::ClientConnection
54
62
  wait_for_terminal_exit: nil,
55
63
  kill_terminal: nil,
56
64
  release_terminal: nil,
65
+ elicitation: nil,
66
+ complete_elicitation: nil,
57
67
  updates: IGNORE,
58
68
  extension_requests: {},
59
69
  extension_notifications: {},
@@ -68,9 +78,12 @@ class ACP::ClientConnection
68
78
  @wait_for_terminal_exit = wait_for_terminal_exit
69
79
  @kill_terminal = kill_terminal
70
80
  @release_terminal = release_terminal
81
+ @elicitation = elicitation
82
+ @complete_elicitation = complete_elicitation
71
83
  @updates = updates
72
84
  @fs_capabilities = nil
73
85
  @terminal = nil
86
+ @elicitation_capabilities = nil
74
87
  @extension_requests = extension_requests
75
88
  @extension_notifications = extension_notifications
76
89
  extension_requests.each_key { |name| ACP::Extensions.validate_name(name) }
@@ -78,6 +91,7 @@ class ACP::ClientConnection
78
91
  @lock = Mutex.new
79
92
  @streams = {}
80
93
  @pending_permissions = {}
94
+ @outstanding_elicitations = {}
81
95
  @logger = logger
82
96
  end
83
97
 
@@ -92,9 +106,11 @@ class ACP::ClientConnection
92
106
  'terminal/output' => method(:terminal_output),
93
107
  'terminal/wait_for_exit' => method(:wait_for_terminal_exit),
94
108
  'terminal/kill' => method(:kill_terminal),
95
- 'terminal/release' => method(:release_terminal)
109
+ 'terminal/release' => method(:release_terminal),
110
+ 'elicitation/create' => method(:create_elicitation)
96
111
  }.merge(@extension_requests),
97
- notifications: { 'session/update' => method(:dispatch) }.merge(@extension_notifications)
112
+ notifications: { 'session/update' => method(:dispatch),
113
+ 'elicitation/complete' => method(:complete_elicitation) }.merge(@extension_notifications)
98
114
  )
99
115
  end
100
116
 
@@ -116,8 +132,15 @@ class ACP::ClientConnection
116
132
  raise ArgumentError, "initialize advertises terminal methods no handler serves: #{unserved.join(', ')}"
117
133
  end
118
134
 
135
+ elicitation = request.client_capabilities&.elicitation
136
+ unserved = unserved_elicitation_modes(elicitation)
137
+ unless unserved.empty?
138
+ raise ArgumentError, "initialize advertises elicitation modes no handler serves: #{unserved.join(', ')}"
139
+ end
140
+
119
141
  @fs_capabilities = capabilities
120
142
  @terminal = terminal
143
+ @elicitation_capabilities = elicitation
121
144
  parse(ACP::Types::InitializeResponse, @transport.request('initialize', request.to_h)).then do |response|
122
145
  next response if response.is_a?(ACP::RequestError)
123
146
 
@@ -246,7 +269,7 @@ class ACP::ClientConnection
246
269
  return result if result.is_a?(ACP::RequestError)
247
270
 
248
271
  type.from_h(result)
249
- rescue KeyError, TypeError, NoMethodError
272
+ rescue ACP::Types::ParseError, KeyError, TypeError, NoMethodError
250
273
  ACP::RequestError.invalid_response(result)
251
274
  end
252
275
 
@@ -295,7 +318,7 @@ class ACP::ClientConnection
295
318
  # @rbs return: void
296
319
  def dispatch(params)
297
320
  notification = ACP::Types::SessionNotification.from_h(params)
298
- rescue KeyError, TypeError, NoMethodError => e
321
+ rescue ACP::Types::ParseError, KeyError, TypeError, NoMethodError => e
299
322
  @logger.warn("dropped malformed session/update: #{e.class}: #{e.message}")
300
323
  else
301
324
  queue = @lock.synchronize { @streams[notification.session_id]&.push(notification.update) }
@@ -310,6 +333,8 @@ class ACP::ClientConnection
310
333
  # @rbs return: (ACP::Types::RequestPermissionResponse | ACP::RequestError)
311
334
  def request_permission(params)
312
335
  request = ACP::Types::RequestPermissionRequest.from_h(params)
336
+ rescue ACP::Types::ParseError => e
337
+ ACP::RequestError.invalid_params([e.message])
313
338
  rescue KeyError, TypeError, NoMethodError
314
339
  ACP::RequestError.invalid_params
315
340
  else
@@ -363,6 +388,8 @@ class ACP::ClientConnection
363
388
  return ACP::RequestError.unadvertised('fs.readTextFile') unless @fs_capabilities&.read_text_file
364
389
 
365
390
  request = ACP::Types::ReadTextFileRequest.from_h(params)
391
+ rescue ACP::Types::ParseError => e
392
+ ACP::RequestError.invalid_params([e.message])
366
393
  rescue KeyError, TypeError, NoMethodError
367
394
  ACP::RequestError.invalid_params
368
395
  else
@@ -377,6 +404,8 @@ class ACP::ClientConnection
377
404
  return ACP::RequestError.unadvertised('fs.writeTextFile') unless @fs_capabilities&.write_text_file
378
405
 
379
406
  request = ACP::Types::WriteTextFileRequest.from_h(params)
407
+ rescue ACP::Types::ParseError => e
408
+ ACP::RequestError.invalid_params([e.message])
380
409
  rescue KeyError, TypeError, NoMethodError
381
410
  ACP::RequestError.invalid_params
382
411
  else
@@ -461,6 +490,54 @@ class ACP::ClientConnection
461
490
  handler.call(request)
462
491
  end
463
492
 
493
+ # The routes are registered at start, before connect records the advertised
494
+ # capabilities, so a request for an unadvertised mode answers -32602.
495
+ #
496
+ # @rbs params: untyped
497
+ # @rbs return: (ACP::Types::CreateElicitationResponse | ACP::RequestError)
498
+ def create_elicitation(params)
499
+ request = ACP::Types::CreateElicitationRequest.from_h(params)
500
+ rescue KeyError, TypeError, NoMethodError
501
+ ACP::RequestError.invalid_params
502
+ else
503
+ case request.mode
504
+ when ACP::Types::CreateElicitationRequest::Mode::Form
505
+ return ACP::RequestError.unadvertised_mode('elicitation.form') unless @elicitation_capabilities&.form
506
+ when ACP::Types::CreateElicitationRequest::Mode::Url
507
+ return ACP::RequestError.unadvertised_mode('elicitation.url') unless @elicitation_capabilities&.url
508
+
509
+ register_elicitation(request.mode.elicitation_id)
510
+ else
511
+ return ACP::RequestError.unadvertised_mode("elicitation.#{request.mode['mode']}")
512
+ end
513
+
514
+ # Safe: connect refuses an advertised mode no handler serves.
515
+ handler = @elicitation #: ^(ACP::Types::CreateElicitationRequest) -> (ACP::Types::CreateElicitationResponse | ACP::RequestError)
516
+ handler.call(request)
517
+ end
518
+
519
+ # The spec requires a completion for an unknown or already-completed
520
+ # elicitation id to be ignored, so only an id registered by a served
521
+ # url-mode request reaches the handler. It runs on the transport's reader
522
+ # thread, so it must return quickly.
523
+ #
524
+ # @rbs params: untyped
525
+ # @rbs return: void
526
+ def complete_elicitation(params)
527
+ notification = ACP::Types::CompleteElicitationNotification.from_h(params)
528
+ rescue KeyError, TypeError, NoMethodError => e
529
+ @logger.warn("dropped malformed elicitation/complete: #{e.class}: #{e.message}")
530
+ else
531
+ known = @lock.synchronize { @outstanding_elicitations.delete(notification.elicitation_id) }
532
+ @complete_elicitation&.call(notification) if known
533
+ end
534
+
535
+ # @rbs elicitation_id: String
536
+ # @rbs return: void
537
+ def register_elicitation(elicitation_id)
538
+ @lock.synchronize { @outstanding_elicitations[elicitation_id] = true }
539
+ end
540
+
464
541
  # The fs capabilities initialize advertises that no injected handler serves.
465
542
  #
466
543
  # @rbs capabilities: ACP::Types::FileSystemCapabilities?
@@ -486,4 +563,16 @@ class ACP::ClientConnection
486
563
  'terminal.release' => capabilities && !@release_terminal
487
564
  }.select { |_, unserved| unserved }.keys
488
565
  end
566
+
567
+ # The elicitation capabilities initialize advertises that no injected
568
+ # handler serves.
569
+ #
570
+ # @rbs capabilities: ACP::Types::ElicitationCapabilities?
571
+ # @rbs return: Array[String]
572
+ def unserved_elicitation_modes(capabilities)
573
+ {
574
+ 'elicitation.form' => capabilities&.form && !@elicitation,
575
+ 'elicitation.url' => capabilities&.url && !@elicitation
576
+ }.select { |_, unserved| unserved }.keys
577
+ end
489
578
  end
@@ -54,9 +54,10 @@ class ACP::RequestError
54
54
  new(code: METHOD_NOT_FOUND, message: 'Method not found', data: method && { method: method })
55
55
  end
56
56
 
57
+ # @rbs errors: Array[String]?
57
58
  # @rbs return: ACP::RequestError
58
- def self.invalid_params
59
- new(code: INVALID_PARAMS, message: 'Invalid params')
59
+ def self.invalid_params(errors = nil)
60
+ new(code: INVALID_PARAMS, message: 'Invalid params', data: errors && { errors: errors })
60
61
  end
61
62
 
62
63
  # The method-not-found code for a method the client's capabilities do not
@@ -69,6 +70,18 @@ class ACP::RequestError
69
70
  new(code: METHOD_NOT_FOUND, message: "Client does not advertise #{capability}")
70
71
  end
71
72
 
73
+ # The invalid-params code for a request whose mode the client's
74
+ # capabilities do not advertise, with a message that names the mode so it
75
+ # can be told apart from malformed params. The spec gives elicitation a
76
+ # mode in its params, so an unadvertised one is invalid params rather than
77
+ # method not found.
78
+ #
79
+ # @rbs mode: String
80
+ # @rbs return: ACP::RequestError
81
+ def self.unadvertised_mode(mode)
82
+ new(code: INVALID_PARAMS, message: "Client does not advertise #{mode}")
83
+ end
84
+
72
85
  # @rbs return: ACP::RequestError
73
86
  def self.internal_error
74
87
  new(code: INTERNAL_ERROR, message: 'Internal error')
data/lib/acp/sdk.rb CHANGED
@@ -9,5 +9,7 @@ root = __dir__ #: String
9
9
  loader = Zeitwerk::Loader.new
10
10
  loader.tag = 'acp-sdk'
11
11
  loader.push_dir(root, namespace: ACP)
12
- loader.ignore(__FILE__, "#{root}/version.rb")
12
+ # The unstable types load only through `require 'acp/types/unstable'`, so
13
+ # Zeitwerk must not map them.
14
+ loader.ignore(__FILE__, "#{root}/version.rb", "#{root}/types/unstable.rb", "#{root}/types/unstable")
13
15
  loader.setup
@@ -0,0 +1,25 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Whether the peer cancelled one request. The transport creates one per served
4
+ # request, marks it when the peer's $/cancel_request arrives, and hands it to
5
+ # the serving thread, whose handler can observe it and end the request early.
6
+ class ACP::Transport::Cancellation
7
+ # @rbs @lock: Thread::Mutex
8
+ # @rbs @cancelled: bool
9
+
10
+ # @rbs return: void
11
+ def initialize
12
+ @lock = Mutex.new
13
+ @cancelled = false
14
+ end
15
+
16
+ # @rbs return: void
17
+ def cancel
18
+ @lock.synchronize { @cancelled = true }
19
+ end
20
+
21
+ # @rbs return: bool
22
+ def cancelled?
23
+ @lock.synchronize { @cancelled }
24
+ end
25
+ end
@@ -13,6 +13,7 @@ class ACP::Transport::Stdio
13
13
  # @rbs @write_lock: Thread::Mutex
14
14
  # @rbs @lock: Thread::Mutex
15
15
  # @rbs @pending: Hash[untyped, Thread::Queue]
16
+ # @rbs @serving: Hash[untyped, ACP::Transport::Cancellation]
16
17
  # @rbs @next_id: Integer
17
18
  # @rbs @closed: bool
18
19
  # @rbs @logger: ACP::Transport::_Logger
@@ -21,6 +22,9 @@ class ACP::Transport::Stdio
21
22
  code: ACP::RequestError::INTERNAL_ERROR, message: 'Connection closed'
22
23
  ) #: ACP::RequestError
23
24
 
25
+ SERVING_CANCEL = :acp_serving_cancellation #: Symbol
26
+ private_constant :SERVING_CANCEL
27
+
24
28
  # @rbs input: _Reader
25
29
  # @rbs output: _Writer
26
30
  # @rbs logger: ACP::Transport::_Logger
@@ -31,6 +35,7 @@ class ACP::Transport::Stdio
31
35
  @write_lock = Mutex.new
32
36
  @lock = Mutex.new
33
37
  @pending = {}
38
+ @serving = {}
34
39
  @next_id = 0
35
40
  @closed = false
36
41
  @logger = logger
@@ -70,6 +75,26 @@ class ACP::Transport::Stdio
70
75
  write({ 'jsonrpc' => '2.0', 'method' => method, 'params' => params }.compact)
71
76
  end
72
77
 
78
+ # The waiting caller is released only by the peer's answer, which the spec
79
+ # requires to be either a result or the -32800 error.
80
+ #
81
+ # @rbs id: untyped
82
+ # @rbs return: void
83
+ def cancel(id)
84
+ pending = @lock.synchronize { @pending.key?(id) }
85
+ notify('$/cancel_request', { 'requestId' => id }) if pending
86
+ end
87
+
88
+ # The cancellation of the request the calling thread is serving, or nil off
89
+ # a serve thread. A handler observes the peer's $/cancel_request through it
90
+ # and can end early with ACP::RequestError.request_cancelled; whatever it
91
+ # returns, the transport sends exactly one response.
92
+ #
93
+ # @rbs return: ACP::Transport::Cancellation?
94
+ def cancellation
95
+ Thread.current[SERVING_CANCEL] #: ACP::Transport::Cancellation?
96
+ end
97
+
73
98
  private
74
99
 
75
100
  # @rbs line: String
@@ -91,7 +116,11 @@ class ACP::Transport::Stdio
91
116
  def route(message, requests, notifications)
92
117
  method = message['method']
93
118
  error = message['error']
94
- if method.is_a?(String) && message.key?('id')
119
+ if method == '$/cancel_request' && !message.key?('id')
120
+ # Cancellation is transport-owned: the notification targets a request
121
+ # the transport is serving, not application state.
122
+ cancel_request(message['params'])
123
+ elsif method.is_a?(String) && message.key?('id')
95
124
  serve(requests[method], message['id'], message['params'], method)
96
125
  elsif method.is_a?(String)
97
126
  # Run on the reader thread so notifications (session/update) keep their
@@ -136,7 +165,9 @@ class ACP::Transport::Stdio
136
165
  # @rbs method: String
137
166
  # @rbs return: void
138
167
  def serve(handler, id, params, method)
168
+ cancellation = register_serving(id)
139
169
  Thread.new do
170
+ Thread.current[SERVING_CANCEL] = cancellation
140
171
  outcome = handler ? invoke(handler, params) : ACP::RequestError.method_not_found(method)
141
172
  case outcome
142
173
  when ACP::Transport::Reply
@@ -144,9 +175,44 @@ class ACP::Transport::Stdio
144
175
  quietly('reply after callback', &outcome.after)
145
176
  else reply(id, outcome)
146
177
  end
178
+ ensure
179
+ unregister_serving(id)
147
180
  end
148
181
  end
149
182
 
183
+ # Registered before the serve thread starts so a cancel racing the request
184
+ # cannot be missed, and dropped once the response is on the wire so later
185
+ # cancels for the id are ignored.
186
+ #
187
+ # @rbs id: untyped
188
+ # @rbs return: ACP::Transport::Cancellation
189
+ def register_serving(id)
190
+ cancellation = ACP::Transport::Cancellation.new
191
+ @lock.synchronize { @serving[id] = cancellation }
192
+ cancellation
193
+ end
194
+
195
+ # @rbs id: untyped
196
+ # @rbs return: void
197
+ def unregister_serving(id)
198
+ @lock.synchronize { @serving.delete(id) }
199
+ end
200
+
201
+ # A notification has no reply to carry a malformed params failure, so one
202
+ # is warned and dropped rather than answered.
203
+ #
204
+ # @rbs params: untyped
205
+ # @rbs return: void
206
+ def cancel_request(params)
207
+ id = params.is_a?(Hash) ? params['requestId'] : nil
208
+ unless id.is_a?(String) || id.is_a?(Integer)
209
+ return @logger.warn('dropped malformed $/cancel_request: missing requestId')
210
+ end
211
+
212
+ cancellation = @lock.synchronize { @serving[id] }
213
+ cancellation&.cancel
214
+ end
215
+
150
216
  # For code with no reply to carry an error: a notification handler, or a
151
217
  # Reply's after once the reply is on the wire. An exception must not kill
152
218
  # the reader thread or print a thread report.
@@ -21,7 +21,10 @@ class ACP::Types::AgentAuthCapabilities
21
21
  # @rbs hash: Hash[String, untyped]
22
22
  # @rbs return: ACP::Types::AgentAuthCapabilities
23
23
  def self.from_h(hash)
24
- new(logout: hash['logout']&.then { |value| ACP::Types::LogoutCapabilities.from_h(value) }, meta: hash['_meta'])
24
+ new(
25
+ logout: ACP::Types::Check.object(hash['logout'], 'logout', ACP::Types::LogoutCapabilities, allow_nil: true),
26
+ meta: hash['_meta']
27
+ )
25
28
  end
26
29
 
27
30
  # @rbs return: Hash[String, untyped]
@@ -49,11 +49,26 @@ class ACP::Types::AgentCapabilities
49
49
  # @rbs return: ACP::Types::AgentCapabilities
50
50
  def self.from_h(hash)
51
51
  new(
52
- load_session: hash['loadSession'],
53
- prompt_capabilities: hash['promptCapabilities']&.then { |value| ACP::Types::PromptCapabilities.from_h(value) },
54
- mcp_capabilities: hash['mcpCapabilities']&.then { |value| ACP::Types::McpCapabilities.from_h(value) },
55
- session_capabilities: hash['sessionCapabilities']&.then { |value| ACP::Types::SessionCapabilities.from_h(value) },
56
- auth: hash['auth']&.then { |value| ACP::Types::AgentAuthCapabilities.from_h(value) },
52
+ load_session: ACP::Types::Check.boolean(hash['loadSession'], 'loadSession', allow_nil: true),
53
+ prompt_capabilities: ACP::Types::Check.object(
54
+ hash['promptCapabilities'],
55
+ 'promptCapabilities',
56
+ ACP::Types::PromptCapabilities,
57
+ allow_nil: true
58
+ ),
59
+ mcp_capabilities: ACP::Types::Check.object(
60
+ hash['mcpCapabilities'],
61
+ 'mcpCapabilities',
62
+ ACP::Types::McpCapabilities,
63
+ allow_nil: true
64
+ ),
65
+ session_capabilities: ACP::Types::Check.object(
66
+ hash['sessionCapabilities'],
67
+ 'sessionCapabilities',
68
+ ACP::Types::SessionCapabilities,
69
+ allow_nil: true
70
+ ),
71
+ auth: ACP::Types::Check.object(hash['auth'], 'auth', ACP::Types::AgentAuthCapabilities, allow_nil: true),
57
72
  meta: hash['_meta']
58
73
  )
59
74
  end
@@ -31,10 +31,13 @@ class ACP::Types::Annotations
31
31
  # @rbs hash: Hash[String, untyped]
32
32
  # @rbs return: ACP::Types::Annotations
33
33
  def self.from_h(hash)
34
+ audience = ACP::Types::Check.array(hash['audience'], 'audience', allow_nil: true) do |item, path|
35
+ ACP::Types::Check.enum(item, path, %w[assistant user])
36
+ end
34
37
  new(
35
- audience: hash['audience'],
36
- last_modified: hash['lastModified'],
37
- priority: hash['priority'],
38
+ audience:,
39
+ last_modified: ACP::Types::Check.string(hash['lastModified'], 'lastModified', allow_nil: true),
40
+ priority: ACP::Types::Check.number(hash['priority'], 'priority', allow_nil: true),
38
41
  meta: hash['_meta']
39
42
  )
40
43
  end
@@ -32,9 +32,14 @@ class ACP::Types::AudioContent
32
32
  # @rbs return: ACP::Types::AudioContent
33
33
  def self.from_h(hash)
34
34
  new(
35
- annotations: hash['annotations']&.then { |value| ACP::Types::Annotations.from_h(value) },
36
- data: hash.fetch('data'),
37
- mime_type: hash.fetch('mimeType'),
35
+ annotations: ACP::Types::Check.object(
36
+ hash['annotations'],
37
+ 'annotations',
38
+ ACP::Types::Annotations,
39
+ allow_nil: true
40
+ ),
41
+ data: ACP::Types::Check.string(ACP::Types::Check.key(hash, 'data'), 'data'),
42
+ mime_type: ACP::Types::Check.string(ACP::Types::Check.key(hash, 'mimeType'), 'mimeType'),
38
43
  meta: hash['_meta']
39
44
  )
40
45
  end
@@ -21,7 +21,7 @@ class ACP::Types::AuthCapabilities
21
21
  # @rbs hash: Hash[String, untyped]
22
22
  # @rbs return: ACP::Types::AuthCapabilities
23
23
  def self.from_h(hash)
24
- new(terminal: hash['terminal'], meta: hash['_meta'])
24
+ new(terminal: ACP::Types::Check.boolean(hash['terminal'], 'terminal', allow_nil: true), meta: hash['_meta'])
25
25
  end
26
26
 
27
27
  # @rbs return: Hash[String, untyped]
@@ -41,12 +41,18 @@ class ACP::Types::AuthMethod::Terminal
41
41
  # @rbs hash: Hash[String, untyped]
42
42
  # @rbs return: ACP::Types::AuthMethod::Terminal
43
43
  def self.from_h(hash)
44
+ args = ACP::Types::Check.array(hash['args'], 'args', allow_nil: true) do |item, path|
45
+ ACP::Types::Check.string(item, path)
46
+ end
47
+ env = ACP::Types::Check.hash(hash['env'], 'env', allow_nil: true) do |value, path|
48
+ ACP::Types::Check.string(value, path)
49
+ end
44
50
  new(
45
- id: hash.fetch('id'),
46
- name: hash.fetch('name'),
47
- description: hash['description'],
48
- args: hash['args'],
49
- env: hash['env'],
51
+ id: ACP::Types::Check.string(ACP::Types::Check.key(hash, 'id'), 'id'),
52
+ name: ACP::Types::Check.string(ACP::Types::Check.key(hash, 'name'), 'name'),
53
+ description: ACP::Types::Check.string(hash['description'], 'description', allow_nil: true),
54
+ args:,
55
+ env:,
50
56
  meta: hash['_meta']
51
57
  )
52
58
  end
@@ -31,7 +31,12 @@ class ACP::Types::AuthMethodAgent
31
31
  # @rbs hash: Hash[String, untyped]
32
32
  # @rbs return: ACP::Types::AuthMethodAgent
33
33
  def self.from_h(hash)
34
- new(id: hash.fetch('id'), name: hash.fetch('name'), description: hash['description'], meta: hash['_meta'])
34
+ new(
35
+ id: ACP::Types::Check.string(ACP::Types::Check.key(hash, 'id'), 'id'),
36
+ name: ACP::Types::Check.string(ACP::Types::Check.key(hash, 'name'), 'name'),
37
+ description: ACP::Types::Check.string(hash['description'], 'description', allow_nil: true),
38
+ meta: hash['_meta']
39
+ )
35
40
  end
36
41
 
37
42
  # @rbs return: Hash[String, untyped]
@@ -41,12 +41,18 @@ class ACP::Types::AuthMethodTerminal
41
41
  # @rbs hash: Hash[String, untyped]
42
42
  # @rbs return: ACP::Types::AuthMethodTerminal
43
43
  def self.from_h(hash)
44
+ args = ACP::Types::Check.array(hash['args'], 'args', allow_nil: true) do |item, path|
45
+ ACP::Types::Check.string(item, path)
46
+ end
47
+ env = ACP::Types::Check.hash(hash['env'], 'env', allow_nil: true) do |value, path|
48
+ ACP::Types::Check.string(value, path)
49
+ end
44
50
  new(
45
- id: hash.fetch('id'),
46
- name: hash.fetch('name'),
47
- description: hash['description'],
48
- args: hash['args'],
49
- env: hash['env'],
51
+ id: ACP::Types::Check.string(ACP::Types::Check.key(hash, 'id'), 'id'),
52
+ name: ACP::Types::Check.string(ACP::Types::Check.key(hash, 'name'), 'name'),
53
+ description: ACP::Types::Check.string(hash['description'], 'description', allow_nil: true),
54
+ args:,
55
+ env:,
50
56
  meta: hash['_meta']
51
57
  )
52
58
  end