acp-sdk 0.3.0 → 0.5.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.
@@ -15,6 +15,7 @@ class ACP::Transport::Stdio
15
15
  # @rbs @pending: Hash[untyped, Thread::Queue]
16
16
  # @rbs @next_id: Integer
17
17
  # @rbs @closed: bool
18
+ # @rbs @logger: ACP::Transport::_Logger
18
19
 
19
20
  CONNECTION_CLOSED = ACP::RequestError.new(
20
21
  code: ACP::RequestError::INTERNAL_ERROR, message: 'Connection closed'
@@ -22,8 +23,9 @@ class ACP::Transport::Stdio
22
23
 
23
24
  # @rbs input: _Reader
24
25
  # @rbs output: _Writer
26
+ # @rbs logger: ACP::Transport::_Logger
25
27
  # @rbs return: void
26
- def initialize(input:, output:)
28
+ def initialize(input:, output:, logger: ACP::Transport::StderrLogger.new)
27
29
  @input = input
28
30
  @output = output
29
31
  @write_lock = Mutex.new
@@ -31,6 +33,7 @@ class ACP::Transport::Stdio
31
33
  @pending = {}
32
34
  @next_id = 0
33
35
  @closed = false
36
+ @logger = logger
34
37
  end
35
38
 
36
39
  # Returns the reader thread, which ends at EOF on input after releasing any
@@ -94,7 +97,7 @@ class ACP::Transport::Stdio
94
97
  # Run on the reader thread so notifications (session/update) keep their
95
98
  # order; a JSON-RPC notification has no reply to carry a handler error.
96
99
  handler = notifications[method]
97
- quietly { handler.call(message['params']) } if handler
100
+ quietly('notification handler') { handler.call(message['params']) } if handler
98
101
  elsif message.key?('result')
99
102
  settle(message['id'], message['result'])
100
103
  elsif error.is_a?(Hash) && error['code'].is_a?(Integer) && error['message'].is_a?(String)
@@ -126,7 +129,7 @@ class ACP::Transport::Stdio
126
129
  case outcome
127
130
  when ACP::Transport::Reply
128
131
  reply(id, outcome.result)
129
- quietly(&outcome.after)
132
+ quietly('reply after callback', &outcome.after)
130
133
  else reply(id, outcome)
131
134
  end
132
135
  end
@@ -136,12 +139,13 @@ class ACP::Transport::Stdio
136
139
  # Reply's after once the reply is on the wire. An exception must not kill
137
140
  # the reader thread or print a thread report.
138
141
  #
142
+ # @rbs label: String
139
143
  # @rbs &block: () -> void
140
144
  # @rbs return: void
141
- def quietly
145
+ def quietly(label)
142
146
  yield
143
- rescue StandardError
144
- nil
147
+ rescue StandardError => e
148
+ @logger.error("#{label} raised #{e.class}: #{e.message}")
145
149
  end
146
150
 
147
151
  # Handlers are application code at the protocol boundary: an exception
@@ -198,6 +202,9 @@ class ACP::Transport::Stdio
198
202
  end
199
203
  end
200
204
 
205
+ # A closed peer must not kill the thread writing the reply: the reader has
206
+ # to keep draining input so pending requests still settle at EOF.
207
+ #
201
208
  # @rbs message: Hash[String, untyped]
202
209
  # @rbs return: void
203
210
  def write(message)
@@ -206,5 +213,7 @@ class ACP::Transport::Stdio
206
213
  @output.write(line)
207
214
  @output.flush
208
215
  end
216
+ rescue Errno::EPIPE => e
217
+ @logger.warn("write to closed peer: #{e.class}: #{e.message}")
209
218
  end
210
219
  end
@@ -9,6 +9,9 @@ class ACP::Types::SessionUpdate::ToolCall
9
9
  # @dynamic title
10
10
  attr_reader :title #: String
11
11
 
12
+ # @dynamic name
13
+ attr_reader :name #: String?
14
+
12
15
  # @dynamic kind
13
16
  attr_reader :kind #: String?
14
17
 
@@ -32,6 +35,7 @@ class ACP::Types::SessionUpdate::ToolCall
32
35
 
33
36
  # @rbs tool_call_id: String
34
37
  # @rbs title: String
38
+ # @rbs name: String?
35
39
  # @rbs kind: String?
36
40
  # @rbs status: String?
37
41
  # @rbs content: Array[ACP::Types::ToolCallContent::t]?
@@ -43,6 +47,7 @@ class ACP::Types::SessionUpdate::ToolCall
43
47
  def initialize(
44
48
  tool_call_id:,
45
49
  title:,
50
+ name: nil,
46
51
  kind: nil,
47
52
  status: nil,
48
53
  content: nil,
@@ -53,6 +58,7 @@ class ACP::Types::SessionUpdate::ToolCall
53
58
  )
54
59
  @tool_call_id = tool_call_id
55
60
  @title = title
61
+ @name = name
56
62
  @kind = kind
57
63
  @status = status
58
64
  @content = content
@@ -69,6 +75,7 @@ class ACP::Types::SessionUpdate::ToolCall
69
75
  new(
70
76
  tool_call_id: hash.fetch('toolCallId'),
71
77
  title: hash.fetch('title'),
78
+ name: hash['name'],
72
79
  kind: hash['kind'],
73
80
  status: hash['status'],
74
81
  content: hash['content']&.map { |item| ACP::Types::ToolCallContent.from_h(item) },
@@ -85,6 +92,7 @@ class ACP::Types::SessionUpdate::ToolCall
85
92
  'sessionUpdate' => 'tool_call',
86
93
  'toolCallId' => tool_call_id,
87
94
  'title' => title,
95
+ 'name' => name,
88
96
  'kind' => kind,
89
97
  'status' => status,
90
98
  'content' => content&.map(&:to_h),
@@ -15,6 +15,9 @@ class ACP::Types::SessionUpdate::ToolCallUpdate
15
15
  # @dynamic title
16
16
  attr_reader :title #: String?
17
17
 
18
+ # @dynamic name
19
+ attr_reader :name #: String?
20
+
18
21
  # @dynamic content
19
22
  attr_reader :content #: Array[ACP::Types::ToolCallContent::t]?
20
23
 
@@ -34,6 +37,7 @@ class ACP::Types::SessionUpdate::ToolCallUpdate
34
37
  # @rbs kind: String?
35
38
  # @rbs status: String?
36
39
  # @rbs title: String?
40
+ # @rbs name: String?
37
41
  # @rbs content: Array[ACP::Types::ToolCallContent::t]?
38
42
  # @rbs locations: Array[ACP::Types::ToolCallLocation]?
39
43
  # @rbs raw_input: untyped
@@ -45,6 +49,7 @@ class ACP::Types::SessionUpdate::ToolCallUpdate
45
49
  kind: nil,
46
50
  status: nil,
47
51
  title: nil,
52
+ name: nil,
48
53
  content: nil,
49
54
  locations: nil,
50
55
  raw_input: nil,
@@ -55,6 +60,7 @@ class ACP::Types::SessionUpdate::ToolCallUpdate
55
60
  @kind = kind
56
61
  @status = status
57
62
  @title = title
63
+ @name = name
58
64
  @content = content
59
65
  @locations = locations
60
66
  @raw_input = raw_input
@@ -71,6 +77,7 @@ class ACP::Types::SessionUpdate::ToolCallUpdate
71
77
  kind: hash['kind'],
72
78
  status: hash['status'],
73
79
  title: hash['title'],
80
+ name: hash['name'],
74
81
  content: hash['content']&.map { |item| ACP::Types::ToolCallContent.from_h(item) },
75
82
  locations: hash['locations']&.map { |item| ACP::Types::ToolCallLocation.from_h(item) },
76
83
  raw_input: hash['rawInput'],
@@ -87,6 +94,7 @@ class ACP::Types::SessionUpdate::ToolCallUpdate
87
94
  'kind' => kind,
88
95
  'status' => status,
89
96
  'title' => title,
97
+ 'name' => name,
90
98
  'content' => content&.map(&:to_h),
91
99
  'locations' => locations&.map(&:to_h),
92
100
  'rawInput' => raw_input,
@@ -9,6 +9,9 @@ class ACP::Types::ToolCall
9
9
  # @dynamic title
10
10
  attr_reader :title #: String
11
11
 
12
+ # @dynamic name
13
+ attr_reader :name #: String?
14
+
12
15
  # @dynamic kind
13
16
  attr_reader :kind #: String?
14
17
 
@@ -32,6 +35,7 @@ class ACP::Types::ToolCall
32
35
 
33
36
  # @rbs tool_call_id: String
34
37
  # @rbs title: String
38
+ # @rbs name: String?
35
39
  # @rbs kind: String?
36
40
  # @rbs status: String?
37
41
  # @rbs content: Array[ACP::Types::ToolCallContent::t]?
@@ -43,6 +47,7 @@ class ACP::Types::ToolCall
43
47
  def initialize(
44
48
  tool_call_id:,
45
49
  title:,
50
+ name: nil,
46
51
  kind: nil,
47
52
  status: nil,
48
53
  content: nil,
@@ -53,6 +58,7 @@ class ACP::Types::ToolCall
53
58
  )
54
59
  @tool_call_id = tool_call_id
55
60
  @title = title
61
+ @name = name
56
62
  @kind = kind
57
63
  @status = status
58
64
  @content = content
@@ -69,6 +75,7 @@ class ACP::Types::ToolCall
69
75
  new(
70
76
  tool_call_id: hash.fetch('toolCallId'),
71
77
  title: hash.fetch('title'),
78
+ name: hash['name'],
72
79
  kind: hash['kind'],
73
80
  status: hash['status'],
74
81
  content: hash['content']&.map { |item| ACP::Types::ToolCallContent.from_h(item) },
@@ -84,6 +91,7 @@ class ACP::Types::ToolCall
84
91
  {
85
92
  'toolCallId' => tool_call_id,
86
93
  'title' => title,
94
+ 'name' => name,
87
95
  'kind' => kind,
88
96
  'status' => status,
89
97
  'content' => content&.map(&:to_h),
@@ -15,6 +15,9 @@ class ACP::Types::ToolCallUpdate
15
15
  # @dynamic title
16
16
  attr_reader :title #: String?
17
17
 
18
+ # @dynamic name
19
+ attr_reader :name #: String?
20
+
18
21
  # @dynamic content
19
22
  attr_reader :content #: Array[ACP::Types::ToolCallContent::t]?
20
23
 
@@ -34,6 +37,7 @@ class ACP::Types::ToolCallUpdate
34
37
  # @rbs kind: String?
35
38
  # @rbs status: String?
36
39
  # @rbs title: String?
40
+ # @rbs name: String?
37
41
  # @rbs content: Array[ACP::Types::ToolCallContent::t]?
38
42
  # @rbs locations: Array[ACP::Types::ToolCallLocation]?
39
43
  # @rbs raw_input: untyped
@@ -45,6 +49,7 @@ class ACP::Types::ToolCallUpdate
45
49
  kind: nil,
46
50
  status: nil,
47
51
  title: nil,
52
+ name: nil,
48
53
  content: nil,
49
54
  locations: nil,
50
55
  raw_input: nil,
@@ -55,6 +60,7 @@ class ACP::Types::ToolCallUpdate
55
60
  @kind = kind
56
61
  @status = status
57
62
  @title = title
63
+ @name = name
58
64
  @content = content
59
65
  @locations = locations
60
66
  @raw_input = raw_input
@@ -71,6 +77,7 @@ class ACP::Types::ToolCallUpdate
71
77
  kind: hash['kind'],
72
78
  status: hash['status'],
73
79
  title: hash['title'],
80
+ name: hash['name'],
74
81
  content: hash['content']&.map { |item| ACP::Types::ToolCallContent.from_h(item) },
75
82
  locations: hash['locations']&.map { |item| ACP::Types::ToolCallLocation.from_h(item) },
76
83
  raw_input: hash['rawInput'],
@@ -86,6 +93,7 @@ class ACP::Types::ToolCallUpdate
86
93
  'kind' => kind,
87
94
  'status' => status,
88
95
  'title' => title,
96
+ 'name' => name,
89
97
  'content' => content&.map(&:to_h),
90
98
  'locations' => locations&.map(&:to_h),
91
99
  'rawInput' => raw_input,
data/lib/acp/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ACP
4
- VERSION = '0.3.0'
4
+ VERSION = '0.5.0'
5
5
  end
@@ -55,6 +55,14 @@ class ACP::AgentConnection::Client
55
55
  # @rbs return: ACP::Types::ReleaseTerminalResponse | ACP::RequestError
56
56
  def release_terminal: (ACP::Types::ReleaseTerminalRequest request) -> (ACP::Types::ReleaseTerminalResponse | ACP::RequestError)
57
57
 
58
+ # @rbs request: ACP::Types::CreateElicitationRequest
59
+ # @rbs return: ACP::Types::CreateElicitationResponse | ACP::RequestError
60
+ def create_elicitation: (ACP::Types::CreateElicitationRequest request) -> (ACP::Types::CreateElicitationResponse | ACP::RequestError)
61
+
62
+ # @rbs notification: ACP::Types::CompleteElicitationNotification
63
+ # @rbs return: void
64
+ def complete_elicitation: (ACP::Types::CompleteElicitationNotification notification) -> void
65
+
58
66
  private
59
67
 
60
68
  # @rbs rpc_method: String
@@ -68,11 +76,4 @@ class ACP::AgentConnection::Client
68
76
  # @rbs request: ACP::AgentConnection::_Response
69
77
  # @rbs return: untyped
70
78
  def terminal_call: (String rpc_method, ACP::AgentConnection::_Parser type, ACP::AgentConnection::_Response request) -> untyped
71
-
72
- # Same method-not-found code a peer would send, but with a message that says
73
- # the refusal was local, so logs can tell the two cases apart.
74
- #
75
- # @rbs capability: String
76
- # @rbs return: ACP::RequestError
77
- def unadvertised: (String capability) -> ACP::RequestError
78
79
  end
@@ -0,0 +1,13 @@
1
+ # Generated from lib/acp/agent_connection/optional_method/logout.rb with RBS::Inline
2
+
3
+ module ACP::AgentConnection::OptionalMethod::Logout
4
+ # @rbs return: String
5
+ def rpc_method: () -> String
6
+
7
+ # @rbs return: Symbol
8
+ def agent_method: () -> Symbol
9
+
10
+ # @rbs initialize_response: ACP::Types::InitializeResponse
11
+ # @rbs return: bool
12
+ def advertised?: (ACP::Types::InitializeResponse initialize_response) -> bool
13
+ end
@@ -1,12 +1,18 @@
1
1
  # Generated from lib/acp/agent_connection.rb with RBS::Inline
2
2
 
3
3
  class ACP::AgentConnection
4
- @transport: ACP::AgentConnection::_Transport
5
-
6
- @initialize_response: ACP::Types::InitializeResponse
4
+ @logger: ACP::Transport::_Logger
7
5
 
8
6
  @factory: ^(ACP::AgentConnection::Client) -> ACP::AgentConnection::_Agent
9
7
 
8
+ @auth_methods: Array[ACP::Types::AuthMethod::t]
9
+
10
+ @agent_info: ACP::Types::Implementation?
11
+
12
+ @capabilities: ACP::Types::AgentCapabilities
13
+
14
+ @transport: ACP::AgentConnection::_Transport
15
+
10
16
  PROTOCOL_VERSION: Integer
11
17
 
12
18
  OPTIONAL: Array[ACP::AgentConnection::_OptionalMethod]
@@ -20,9 +26,10 @@ class ACP::AgentConnection
20
26
  # @rbs capabilities: ACP::Types::AgentCapabilities
21
27
  # @rbs agent_info: ACP::Types::Implementation?
22
28
  # @rbs auth_methods: Array[ACP::Types::AuthMethod::t]
29
+ # @rbs logger: ACP::Transport::_Logger
23
30
  # @rbs &factory: (ACP::AgentConnection::Client) -> ACP::AgentConnection::_Agent
24
31
  # @rbs return: void
25
- def initialize: (transport: ACP::AgentConnection::_Transport, capabilities: ACP::Types::AgentCapabilities, ?agent_info: ACP::Types::Implementation?, ?auth_methods: Array[ACP::Types::AuthMethod::t]) { (ACP::AgentConnection::Client) -> ACP::AgentConnection::_Agent } -> void
32
+ def initialize: (transport: ACP::AgentConnection::_Transport, capabilities: ACP::Types::AgentCapabilities, ?agent_info: ACP::Types::Implementation?, ?auth_methods: Array[ACP::Types::AuthMethod::t], ?logger: ACP::Transport::_Logger) { (ACP::AgentConnection::Client) -> ACP::AgentConnection::_Agent } -> void
26
33
 
27
34
  # @rbs return: Thread
28
35
  def start: () -> Thread
@@ -38,6 +45,13 @@ class ACP::AgentConnection
38
45
  # @rbs return: Hash[String, ^(untyped) -> void]
39
46
  def notifications: (ACP::AgentConnection::_Agent agent) -> Hash[String, ^(untyped) -> void]
40
47
 
48
+ # A notification has no reply to carry a parse failure.
49
+ #
50
+ # @rbs agent: ACP::AgentConnection::_Agent
51
+ # @rbs params: untyped
52
+ # @rbs return: void
53
+ def cancel: (ACP::AgentConnection::_Agent agent, untyped params) -> void
54
+
41
55
  # Generated from_h raises on a missing key or a value of the wrong shape.
42
56
  #
43
57
  # @rbs type: ACP::AgentConnection::_Parser
@@ -50,6 +64,18 @@ class ACP::AgentConnection
50
64
  # @rbs return: ACP::Types::InitializeResponse
51
65
  def connect: (ACP::AgentConnection::Client client, ACP::Types::InitializeRequest request) -> ACP::Types::InitializeResponse
52
66
 
67
+ # @rbs auth_methods: Array[ACP::Types::AuthMethod::t]
68
+ # @rbs return: ACP::Types::InitializeResponse
69
+ def initialize_response: (Array[ACP::Types::AuthMethod::t] auth_methods) -> ACP::Types::InitializeResponse
70
+
71
+ # @rbs capabilities: ACP::Types::ClientCapabilities
72
+ # @rbs return: Array[ACP::Types::AuthMethod::t]
73
+ def auth_methods: (ACP::Types::ClientCapabilities capabilities) -> Array[ACP::Types::AuthMethod::t]
74
+
75
+ # @rbs method: ACP::Types::AuthMethod::t
76
+ # @rbs return: bool
77
+ def terminal_auth_method?: (ACP::Types::AuthMethod::t method) -> bool
78
+
53
79
  # session_created runs after the reply because the client must know the
54
80
  # session id before updates for it (e.g. available commands) arrive.
55
81
  #
@@ -1,23 +1,36 @@
1
1
  # Generated from lib/acp/client_connection.rb with RBS::Inline
2
2
 
3
3
  class ACP::ClientConnection
4
+ @logger: ACP::Transport::_Logger
5
+
4
6
  @streams: Hash[String, Thread::Queue]
5
7
 
6
8
  @lock: Thread::Mutex
7
9
 
10
+ @fs_capabilities: ACP::Types::FileSystemCapabilities?
11
+
8
12
  @updates: ACP::ClientConnection::_UpdateHandler
9
13
 
14
+ @write_text_file: (^(ACP::Types::WriteTextFileRequest) -> (ACP::Types::WriteTextFileResponse | ACP::RequestError))?
15
+
16
+ @read_text_file: (^(ACP::Types::ReadTextFileRequest) -> (ACP::Types::ReadTextFileResponse | ACP::RequestError))?
17
+
10
18
  @permission: ACP::ClientConnection::_PermissionHandler
11
19
 
12
20
  @transport: ACP::AgentConnection::_Transport
13
21
 
22
+ PROTOCOL_VERSION: Integer
23
+
14
24
  IGNORE: ^(ACP::Types::SessionNotification) -> void
15
25
 
16
26
  # @rbs transport: ACP::AgentConnection::_Transport
17
27
  # @rbs permission: ACP::ClientConnection::_PermissionHandler
28
+ # @rbs read_text_file: (^(ACP::Types::ReadTextFileRequest) -> (ACP::Types::ReadTextFileResponse | ACP::RequestError))?
29
+ # @rbs write_text_file: (^(ACP::Types::WriteTextFileRequest) -> (ACP::Types::WriteTextFileResponse | ACP::RequestError))?
18
30
  # @rbs updates: ACP::ClientConnection::_UpdateHandler
31
+ # @rbs logger: ACP::Transport::_Logger
19
32
  # @rbs return: void
20
- def initialize: (transport: ACP::AgentConnection::_Transport, permission: ACP::ClientConnection::_PermissionHandler, ?updates: ACP::ClientConnection::_UpdateHandler) -> void
33
+ def initialize: (transport: ACP::AgentConnection::_Transport, permission: ACP::ClientConnection::_PermissionHandler, ?read_text_file: (^(ACP::Types::ReadTextFileRequest) -> (ACP::Types::ReadTextFileResponse | ACP::RequestError))?, ?write_text_file: (^(ACP::Types::WriteTextFileRequest) -> (ACP::Types::WriteTextFileResponse | ACP::RequestError))?, ?updates: ACP::ClientConnection::_UpdateHandler, ?logger: ACP::Transport::_Logger) -> void
21
34
 
22
35
  # @rbs return: Thread
23
36
  def start: () -> Thread
@@ -47,10 +60,44 @@ class ACP::ClientConnection
47
60
  # @rbs return: ACP::Types::ListSessionsResponse | ACP::RequestError
48
61
  def session_list: (ACP::Types::ListSessionsRequest request) -> (ACP::Types::ListSessionsResponse | ACP::RequestError)
49
62
 
63
+ # The agent does not replay history on resume, so unlike session_load there
64
+ # is nothing to stream and no block is taken.
65
+ #
66
+ # @rbs request: ACP::Types::ResumeSessionRequest
67
+ # @rbs return: ACP::Types::ResumeSessionResponse | ACP::RequestError
68
+ def session_resume: (ACP::Types::ResumeSessionRequest request) -> (ACP::Types::ResumeSessionResponse | ACP::RequestError)
69
+
70
+ # @rbs request: ACP::Types::CloseSessionRequest
71
+ # @rbs return: ACP::Types::CloseSessionResponse | ACP::RequestError
72
+ def session_close: (ACP::Types::CloseSessionRequest request) -> (ACP::Types::CloseSessionResponse | ACP::RequestError)
73
+
74
+ # @rbs request: ACP::Types::DeleteSessionRequest
75
+ # @rbs return: ACP::Types::DeleteSessionResponse | ACP::RequestError
76
+ def session_delete: (ACP::Types::DeleteSessionRequest request) -> (ACP::Types::DeleteSessionResponse | ACP::RequestError)
77
+
50
78
  # @rbs notification: ACP::Types::CancelNotification
51
79
  # @rbs return: void
52
80
  def session_cancel: (ACP::Types::CancelNotification notification) -> void
53
81
 
82
+ # @rbs request: ACP::Types::SetSessionModeRequest
83
+ # @rbs return: ACP::Types::SetSessionModeResponse | ACP::RequestError
84
+ def session_set_mode: (ACP::Types::SetSessionModeRequest request) -> (ACP::Types::SetSessionModeResponse | ACP::RequestError)
85
+
86
+ # The reply carries the session's complete option list, so it replaces any
87
+ # state held from an earlier reply rather than merging into it.
88
+ #
89
+ # @rbs request: ACP::Types::SetSessionConfigOptionRequest
90
+ # @rbs return: ACP::Types::SetSessionConfigOptionResponse | ACP::RequestError
91
+ def session_set_config_option: (ACP::Types::SetSessionConfigOptionRequest request) -> (ACP::Types::SetSessionConfigOptionResponse | ACP::RequestError)
92
+
93
+ # @rbs request: ACP::Types::AuthenticateRequest
94
+ # @rbs return: ACP::Types::AuthenticateResponse | ACP::RequestError
95
+ def authenticate: (ACP::Types::AuthenticateRequest request) -> (ACP::Types::AuthenticateResponse | ACP::RequestError)
96
+
97
+ # @rbs request: ACP::Types::LogoutRequest
98
+ # @rbs return: ACP::Types::LogoutResponse | ACP::RequestError
99
+ def logout: (ACP::Types::LogoutRequest request) -> (ACP::Types::LogoutResponse | ACP::RequestError)
100
+
54
101
  private
55
102
 
56
103
  # @rbs type: ACP::AgentConnection::_Parser
@@ -70,7 +117,8 @@ class ACP::ClientConnection
70
117
  def stream: (String session_id, String method, Hash[String, untyped] params) { (ACP::Types::SessionUpdate::t) -> void } -> (Hash[String, untyped] | ACP::RequestError)
71
118
 
72
119
  # Pushes under the lock so a stream cannot close its queue between the
73
- # lookup and the push.
120
+ # lookup and the push. A malformed notification has no reply to carry the
121
+ # failure.
74
122
  #
75
123
  # @rbs params: untyped
76
124
  # @rbs return: void
@@ -79,4 +127,21 @@ class ACP::ClientConnection
79
127
  # @rbs params: untyped
80
128
  # @rbs return: (ACP::Types::RequestPermissionResponse | ACP::RequestError)
81
129
  def request_permission: (untyped params) -> (ACP::Types::RequestPermissionResponse | ACP::RequestError)
130
+
131
+ # The routes are registered at start, before connect records the advertised
132
+ # capabilities, so each one answers -32601 until then.
133
+ #
134
+ # @rbs params: untyped
135
+ # @rbs return: (ACP::Types::ReadTextFileResponse | ACP::RequestError)
136
+ def read_text_file: (untyped params) -> (ACP::Types::ReadTextFileResponse | ACP::RequestError)
137
+
138
+ # @rbs params: untyped
139
+ # @rbs return: (ACP::Types::WriteTextFileResponse | ACP::RequestError)
140
+ def write_text_file: (untyped params) -> (ACP::Types::WriteTextFileResponse | ACP::RequestError)
141
+
142
+ # The fs capabilities initialize advertises that no injected handler serves.
143
+ #
144
+ # @rbs capabilities: ACP::Types::FileSystemCapabilities?
145
+ # @rbs return: Array[String]
146
+ def unserved_fs_methods: (ACP::Types::FileSystemCapabilities? capabilities) -> Array[String]
82
147
  end
@@ -50,9 +50,33 @@ class ACP::RequestError
50
50
  # @rbs return: ACP::RequestError
51
51
  def self.invalid_params: () -> ACP::RequestError
52
52
 
53
+ # The method-not-found code for a method the client's capabilities do not
54
+ # advertise, with a message that names the capability so it can be told
55
+ # apart from an unknown method.
56
+ #
57
+ # @rbs capability: String
58
+ # @rbs return: ACP::RequestError
59
+ def self.unadvertised: (String capability) -> ACP::RequestError
60
+
53
61
  # @rbs return: ACP::RequestError
54
62
  def self.internal_error: () -> ACP::RequestError
55
63
 
64
+ # The message tells a malformed peer reply apart from an internal error a
65
+ # handler raised, though both use the -32603 code.
66
+ #
67
+ # @rbs response: untyped
68
+ # @rbs return: ACP::RequestError
69
+ def self.invalid_response: (untyped response) -> ACP::RequestError
70
+
71
+ # A local refusal of an initialize reply that names a version the SDK does
72
+ # not speak; the message names both versions so logs can tell it from a
73
+ # peer's error.
74
+ #
75
+ # @rbs requested: Integer
76
+ # @rbs returned: Integer
77
+ # @rbs return: ACP::RequestError
78
+ def self.unsupported_protocol_version: (requested: Integer, returned: Integer) -> ACP::RequestError
79
+
56
80
  # @rbs return: ACP::RequestError
57
81
  def self.request_cancelled: () -> ACP::RequestError
58
82
 
@@ -0,0 +1,13 @@
1
+ # Generated from lib/acp/transport/stderr_logger.rb with RBS::Inline
2
+
3
+ # The default sink for dropped messages and swallowed errors: the spec
4
+ # sanctions agents logging to stderr.
5
+ class ACP::Transport::StderrLogger
6
+ # @rbs message: String
7
+ # @rbs return: void
8
+ def warn: (String message) -> void
9
+
10
+ # @rbs message: String
11
+ # @rbs return: void
12
+ def error: (String message) -> void
13
+ end
@@ -6,6 +6,8 @@
6
6
  # outbound request mid-turn and still see notifications (e.g. session/cancel)
7
7
  # that arrive meanwhile.
8
8
  class ACP::Transport::Stdio
9
+ @logger: ACP::Transport::_Logger
10
+
9
11
  @closed: bool
10
12
 
11
13
  @next_id: Integer
@@ -24,8 +26,9 @@ class ACP::Transport::Stdio
24
26
 
25
27
  # @rbs input: _Reader
26
28
  # @rbs output: _Writer
29
+ # @rbs logger: ACP::Transport::_Logger
27
30
  # @rbs return: void
28
- def initialize: (input: _Reader, output: _Writer) -> void
31
+ def initialize: (input: _Reader, output: _Writer, ?logger: ACP::Transport::_Logger) -> void
29
32
 
30
33
  # Returns the reader thread, which ends at EOF on input after releasing any
31
34
  # pending outbound requests.
@@ -73,9 +76,10 @@ class ACP::Transport::Stdio
73
76
  # Reply's after once the reply is on the wire. An exception must not kill
74
77
  # the reader thread or print a thread report.
75
78
  #
79
+ # @rbs label: String
76
80
  # @rbs &block: () -> void
77
81
  # @rbs return: void
78
- def quietly: () { () -> void } -> void
82
+ def quietly: (String label) { () -> void } -> void
79
83
 
80
84
  # Handlers are application code at the protocol boundary: an exception
81
85
  # becomes an error response instead of killing the thread serving it.
@@ -105,6 +109,9 @@ class ACP::Transport::Stdio
105
109
  # @rbs return: void
106
110
  def close: () -> void
107
111
 
112
+ # A closed peer must not kill the thread writing the reply: the reader has
113
+ # to keep draining input so pending requests still settle at EOF.
114
+ #
108
115
  # @rbs message: Hash[String, untyped]
109
116
  # @rbs return: void
110
117
  def write: (Hash[String, untyped] message) -> void
@@ -7,6 +7,9 @@ class ACP::Types::SessionUpdate::ToolCall
7
7
  # @dynamic title
8
8
  attr_reader title: String
9
9
 
10
+ # @dynamic name
11
+ attr_reader name: String?
12
+
10
13
  # @dynamic kind
11
14
  attr_reader kind: String?
12
15
 
@@ -30,6 +33,7 @@ class ACP::Types::SessionUpdate::ToolCall
30
33
 
31
34
  # @rbs tool_call_id: String
32
35
  # @rbs title: String
36
+ # @rbs name: String?
33
37
  # @rbs kind: String?
34
38
  # @rbs status: String?
35
39
  # @rbs content: Array[ACP::Types::ToolCallContent::t]?
@@ -38,7 +42,7 @@ class ACP::Types::SessionUpdate::ToolCall
38
42
  # @rbs raw_output: untyped
39
43
  # @rbs meta: Hash[String, untyped]?
40
44
  # @rbs return: void
41
- def initialize: (tool_call_id: String, title: String, ?kind: String?, ?status: String?, ?content: Array[ACP::Types::ToolCallContent::t]?, ?locations: Array[ACP::Types::ToolCallLocation]?, ?raw_input: untyped, ?raw_output: untyped, ?meta: Hash[String, untyped]?) -> void
45
+ def initialize: (tool_call_id: String, title: String, ?name: String?, ?kind: String?, ?status: String?, ?content: Array[ACP::Types::ToolCallContent::t]?, ?locations: Array[ACP::Types::ToolCallLocation]?, ?raw_input: untyped, ?raw_output: untyped, ?meta: Hash[String, untyped]?) -> void
42
46
 
43
47
  # @rbs hash: Hash[String, untyped]
44
48
  # @rbs return: ACP::Types::SessionUpdate::ToolCall
@@ -13,6 +13,9 @@ class ACP::Types::SessionUpdate::ToolCallUpdate
13
13
  # @dynamic title
14
14
  attr_reader title: String?
15
15
 
16
+ # @dynamic name
17
+ attr_reader name: String?
18
+
16
19
  # @dynamic content
17
20
  attr_reader content: Array[ACP::Types::ToolCallContent::t]?
18
21
 
@@ -32,13 +35,14 @@ class ACP::Types::SessionUpdate::ToolCallUpdate
32
35
  # @rbs kind: String?
33
36
  # @rbs status: String?
34
37
  # @rbs title: String?
38
+ # @rbs name: String?
35
39
  # @rbs content: Array[ACP::Types::ToolCallContent::t]?
36
40
  # @rbs locations: Array[ACP::Types::ToolCallLocation]?
37
41
  # @rbs raw_input: untyped
38
42
  # @rbs raw_output: untyped
39
43
  # @rbs meta: Hash[String, untyped]?
40
44
  # @rbs return: void
41
- def initialize: (tool_call_id: String, ?kind: String?, ?status: String?, ?title: String?, ?content: Array[ACP::Types::ToolCallContent::t]?, ?locations: Array[ACP::Types::ToolCallLocation]?, ?raw_input: untyped, ?raw_output: untyped, ?meta: Hash[String, untyped]?) -> void
45
+ def initialize: (tool_call_id: String, ?kind: String?, ?status: String?, ?title: String?, ?name: String?, ?content: Array[ACP::Types::ToolCallContent::t]?, ?locations: Array[ACP::Types::ToolCallLocation]?, ?raw_input: untyped, ?raw_output: untyped, ?meta: Hash[String, untyped]?) -> void
42
46
 
43
47
  # @rbs hash: Hash[String, untyped]
44
48
  # @rbs return: ACP::Types::SessionUpdate::ToolCallUpdate
@@ -7,6 +7,9 @@ class ACP::Types::ToolCall
7
7
  # @dynamic title
8
8
  attr_reader title: String
9
9
 
10
+ # @dynamic name
11
+ attr_reader name: String?
12
+
10
13
  # @dynamic kind
11
14
  attr_reader kind: String?
12
15
 
@@ -30,6 +33,7 @@ class ACP::Types::ToolCall
30
33
 
31
34
  # @rbs tool_call_id: String
32
35
  # @rbs title: String
36
+ # @rbs name: String?
33
37
  # @rbs kind: String?
34
38
  # @rbs status: String?
35
39
  # @rbs content: Array[ACP::Types::ToolCallContent::t]?
@@ -38,7 +42,7 @@ class ACP::Types::ToolCall
38
42
  # @rbs raw_output: untyped
39
43
  # @rbs meta: Hash[String, untyped]?
40
44
  # @rbs return: void
41
- def initialize: (tool_call_id: String, title: String, ?kind: String?, ?status: String?, ?content: Array[ACP::Types::ToolCallContent::t]?, ?locations: Array[ACP::Types::ToolCallLocation]?, ?raw_input: untyped, ?raw_output: untyped, ?meta: Hash[String, untyped]?) -> void
45
+ def initialize: (tool_call_id: String, title: String, ?name: String?, ?kind: String?, ?status: String?, ?content: Array[ACP::Types::ToolCallContent::t]?, ?locations: Array[ACP::Types::ToolCallLocation]?, ?raw_input: untyped, ?raw_output: untyped, ?meta: Hash[String, untyped]?) -> void
42
46
 
43
47
  # @rbs hash: Hash[String, untyped]
44
48
  # @rbs return: ACP::Types::ToolCall