Skip to content

Commit cd62c67

Browse files
nficanoclaude
andcommitted
update lib and specs
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 7b8b2de commit cd62c67

19 files changed

Lines changed: 91 additions & 74 deletions

lib/arcp/client.rb

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,9 @@ def handshake!(auth:, client_name:, client_version:, capabilities: nil, resume:
9595
end
9696

9797
start_reader!
98-
start_heartbeat! if @session.supports?(Arcp::Session::Feature::HEARTBEAT) && @session.heartbeat_interval_sec
98+
if @session.supports?(Arcp::Session::Feature::HEARTBEAT) && @session.heartbeat_interval_sec
99+
start_heartbeat!
100+
end
99101
@session
100102
end
101103

@@ -250,7 +252,7 @@ def request(type:, expect:, payload:)
250252
end
251253

252254
def start_reader!
253-
@reader_task = Async do |task|
255+
@reader_task = Async do |_task|
254256
loop do
255257
env = @transport.receive
256258
break if env.nil?
@@ -296,7 +298,8 @@ def dispatch(env)
296298
when Arcp::MessageTypes::SESSION_PING
297299
ping = Arcp::Session::Ping.from_h(env.payload)
298300
send_envelope(type: Arcp::MessageTypes::SESSION_PONG,
299-
payload: Arcp::Session::Pong.new(ping_nonce: ping.nonce, received_at: @clock.now.iso8601).to_h)
301+
payload: Arcp::Session::Pong.new(ping_nonce: ping.nonce,
302+
received_at: @clock.now.iso8601).to_h)
300303
when Arcp::MessageTypes::SESSION_PONG
301304
# noop — receipt of any inbound resets timer (implicit)
302305
else
@@ -307,9 +310,7 @@ def dispatch(env)
307310
def feed_job_stream(env, end_stream: false)
308311
queue = @mutex.synchronize { @job_streams[env.job_id] ||= Async::Queue.new }
309312

310-
if env.type == Arcp::MessageTypes::JOB_EVENT
311-
queue.enqueue(Arcp::Job::Event.from_h(env.payload))
312-
end
313+
queue.enqueue(Arcp::Job::Event.from_h(env.payload)) if env.type == Arcp::MessageTypes::JOB_EVENT
313314

314315
queue.enqueue(:__arcp_end__) if end_stream
315316
end
@@ -324,7 +325,11 @@ def feed_result(env)
324325

325326
def feed_pending(env)
326327
reply_to = env.payload.is_a?(Hash) ? env.payload['reply_to'] : nil
327-
key = reply_to || @mutex.synchronize { @pending.keys.find { |k| @pending[k].is_a?(Array) && @pending[k][0] == env.type } }
328+
key = reply_to || @mutex.synchronize do
329+
@pending.keys.find do |k|
330+
@pending[k].is_a?(Array) && @pending[k][0] == env.type
331+
end
332+
end
328333
return unless key
329334

330335
pair = @mutex.synchronize { @pending.delete(key) }

lib/arcp/envelope.rb

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module Arcp
1616
HEX32 = /\A[0-9a-f]{32}\z/
1717

1818
def self.build(type:, session_id:, payload:, trace_id: nil, job_id: nil, event_seq: nil, id: nil)
19-
raise Arcp::Errors::InvalidRequest, "trace_id must be 32 hex chars" if trace_id && trace_id !~ HEX32
19+
raise Arcp::Errors::InvalidRequest, 'trace_id must be 32 hex chars' if trace_id && trace_id !~ HEX32
2020

2121
new(
2222
arcp: Arcp::PROTOCOL_VERSION,
@@ -35,16 +35,24 @@ def self.from_h(hash)
3535

3636
h = hash.transform_keys(&:to_s)
3737
arcp = h['arcp']
38-
raise Arcp::Errors::InvalidRequest, "unsupported arcp version: #{arcp.inspect}" unless arcp == Arcp::PROTOCOL_VERSION
38+
unless arcp == Arcp::PROTOCOL_VERSION
39+
raise Arcp::Errors::InvalidRequest, "unsupported arcp version: #{arcp.inspect}"
40+
end
3941

4042
type = h['type']
4143
raise Arcp::Errors::InvalidRequest, 'envelope type must be a String' unless type.is_a?(String)
4244

4345
session_id = h['session_id']
44-
raise Arcp::Errors::InvalidRequest, 'envelope session_id must be a String' unless session_id.is_a?(String)
46+
unless session_id.is_a?(String)
47+
raise Arcp::Errors::InvalidRequest,
48+
'envelope session_id must be a String'
49+
end
4550

4651
event_seq = h['event_seq']
47-
raise Arcp::Errors::InvalidRequest, 'event_seq must be an Integer' unless event_seq.nil? || event_seq.is_a?(Integer)
52+
unless event_seq.nil? || event_seq.is_a?(Integer)
53+
raise Arcp::Errors::InvalidRequest,
54+
'event_seq must be an Integer'
55+
end
4856

4957
trace_id = h['trace_id']
5058
raise Arcp::Errors::InvalidRequest, 'trace_id must be 32 hex chars' if trace_id && trace_id !~ HEX32
@@ -72,17 +80,15 @@ def self.deep_freeze(value)
7280
case value
7381
when Hash
7482
value.each_value { |v| deep_freeze(v) }
75-
value.freeze
7683
when Array
7784
value.each { |v| deep_freeze(v) }
78-
value.freeze
79-
else
80-
value.freeze
8185
end
86+
value.freeze
8287
end
8388

8489
def to_h
85-
h = { 'arcp' => arcp, 'id' => id, 'type' => type, 'session_id' => session_id, 'payload' => stringify(payload) }
90+
h = { 'arcp' => arcp, 'id' => id, 'type' => type, 'session_id' => session_id,
91+
'payload' => stringify(payload) }
8692
h['trace_id'] = trace_id if trace_id
8793
h['job_id'] = job_id if job_id
8894
h['event_seq'] = event_seq if event_seq

lib/arcp/errors.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,7 @@ class UnnegotiatedFeature < Arcp::Error
118118

119119
WIRE_CODES = ALL.map { |c| c::CODE }.freeze
120120

121-
BY_CODE = ALL.each_with_object({}) { |klass, h| h[klass::CODE] = klass }.freeze
121+
BY_CODE = ALL.to_h { |klass| [klass::CODE, klass] }.freeze
122122

123123
RETRYABLE_BY_DEFAULT = ALL.select { |k| k.new.retryable? }.map { |k| k::CODE }.freeze
124124
NON_RETRYABLE_BY_DEFAULT = (WIRE_CODES - RETRYABLE_BY_DEFAULT).freeze

lib/arcp/job/agent_ref.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ def self.parse(ref)
77
return nil if ref.nil?
88

99
name, version = ref.to_s.split('@', 2)
10-
raise Arcp::Errors::InvalidRequest, "agent name must be non-empty" if name.nil? || name.empty?
10+
raise Arcp::Errors::InvalidRequest, 'agent name must be non-empty' if name.nil? || name.empty?
1111

1212
new(name: name, version: version)
1313
end

lib/arcp/job/event.rb

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,16 +35,16 @@ module EventKind
3535
end
3636

3737
BODY_CLASSES = {
38-
EventKind::PROGRESS => EventBody::Progress,
38+
EventKind::PROGRESS => EventBody::Progress,
3939
EventKind::RESULT_CHUNK => EventBody::ResultChunk,
40-
EventKind::LOG => EventBody::Log,
41-
EventKind::THOUGHT => EventBody::Thought,
42-
EventKind::TOOL_CALL => EventBody::ToolCall,
43-
EventKind::TOOL_RESULT => EventBody::ToolResult,
44-
EventKind::STATUS => EventBody::Status,
45-
EventKind::METRIC => EventBody::Metric,
46-
EventKind::TRACE_SPAN => EventBody::TraceSpan,
47-
EventKind::DELEGATE => EventBody::Delegate
40+
EventKind::LOG => EventBody::Log,
41+
EventKind::THOUGHT => EventBody::Thought,
42+
EventKind::TOOL_CALL => EventBody::ToolCall,
43+
EventKind::TOOL_RESULT => EventBody::ToolResult,
44+
EventKind::STATUS => EventBody::Status,
45+
EventKind::METRIC => EventBody::Metric,
46+
EventKind::TRACE_SPAN => EventBody::TraceSpan,
47+
EventKind::DELEGATE => EventBody::Delegate
4848
}.freeze
4949

5050
Event = Data.define(:kind, :body) do

lib/arcp/job/event_body/result_chunk.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,10 @@ module EventBody
1111
def self.from_h(h)
1212
h = h.transform_keys(&:to_s)
1313
encoding = h.fetch('encoding')
14-
raise Arcp::Errors::InvalidRequest, "unknown encoding: #{encoding.inspect}" unless ENCODINGS.include?(encoding)
14+
unless ENCODINGS.include?(encoding)
15+
raise Arcp::Errors::InvalidRequest,
16+
"unknown encoding: #{encoding.inspect}"
17+
end
1518

1619
new(
1720
result_id: h.fetch('result_id'),

lib/arcp/lease.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@ def self.parse(entries)
3535
h = {}
3636
Array(entries).each do |entry|
3737
ccy, amount = entry.to_s.split(':', 2)
38-
raise Arcp::Errors::InvalidRequest, "malformed budget entry: #{entry.inspect}" if ccy.nil? || amount.nil?
38+
if ccy.nil? || amount.nil?
39+
raise Arcp::Errors::InvalidRequest,
40+
"malformed budget entry: #{entry.inspect}"
41+
end
3942

4043
h[ccy] = BigDecimal(amount)
4144
end
@@ -69,7 +72,7 @@ def get(currency) = @remaining[currency] || BigDecimal('0')
6972
def negative?(currency) = (@remaining[currency] || BigDecimal('0')).negative?
7073

7174
def snapshot
72-
@remaining.transform_values { |v| v.dup }.freeze
75+
@remaining.transform_values(&:dup).freeze
7376
end
7477
end
7578

lib/arcp/runtime/job_context.rb

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,8 @@ def log(level:, message:, **fields)
3939

4040
def progress(current:, total: nil, units: nil, message: nil)
4141
emit(kind: Arcp::Job::EventKind::PROGRESS,
42-
body: Arcp::Job::EventBody::Progress.new(current: current, total: total, units: units, message: message))
42+
body: Arcp::Job::EventBody::Progress.new(current: current, total: total, units: units,
43+
message: message))
4344
end
4445

4546
def metric(name:, value:, unit: nil)
@@ -67,8 +68,6 @@ def stream_result(encoding: 'utf8', &block)
6768

6869
@chunked = true
6970
@result_id = Arcp::Ids.result_id
70-
seq = 0
71-
size = 0
7271

7372
writer = ChunkWriter.new(ctx: self, encoding: encoding, result_id: @result_id)
7473
if block
@@ -117,8 +116,6 @@ def fail!(code:, message: nil, retryable: false, details: {})
117116

118117
# @api private
119118
class ChunkWriter
120-
attr_reader :totals
121-
122119
def initialize(ctx:, encoding:, result_id:)
123120
@ctx = ctx
124121
@encoding = encoding

lib/arcp/runtime/job_manager.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ module Runtime
1010

1111
JobRecord = Data.define(:job_id, :agent, :principal_id, :status, :created_at,
1212
:input, :submitter_session_id, :task) do
13-
def with(**kw) = self.class.new(**to_h.merge(kw))
13+
def with(**kw) = self.class.new(**to_h, **kw)
1414
end
1515

1616
# Owns agent registry + per-job lifecycle. Submitted jobs run as
@@ -135,7 +135,7 @@ def list(principal_id:, filter: {}, limit: 50, cursor: nil)
135135
end
136136

137137
page = rows[offset, limit] || []
138-
next_cursor = ((offset + page.size) < rows.size) ? (offset + page.size).to_s : nil
138+
next_cursor = (offset + page.size) < rows.size ? (offset + page.size).to_s : nil
139139

140140
summaries = page.map do |r|
141141
lease = @leases.get(r.job_id)
@@ -167,7 +167,10 @@ def publish_event(job_id, event)
167167
end
168168

169169
def publish_result(job_id, result)
170-
record = @mutex.synchronize { @jobs[job_id] = @jobs[job_id].with(status: 'succeeded') if @jobs[job_id]; @jobs[job_id] }
170+
record = @mutex.synchronize do
171+
@jobs[job_id] = @jobs[job_id].with(status: 'succeeded') if @jobs[job_id]
172+
@jobs[job_id]
173+
end
171174
env = Arcp::Envelope.build(
172175
type: Arcp::MessageTypes::JOB_RESULT,
173176
session_id: record&.submitter_session_id || '',
@@ -180,7 +183,10 @@ def publish_result(job_id, result)
180183
end
181184

182185
def publish_error(job_id, error)
183-
record = @mutex.synchronize { @jobs[job_id] = @jobs[job_id].with(status: error.final_status) if @jobs[job_id]; @jobs[job_id] }
186+
record = @mutex.synchronize do
187+
@jobs[job_id] = @jobs[job_id].with(status: error.final_status) if @jobs[job_id]
188+
@jobs[job_id]
189+
end
184190
env = Arcp::Envelope.build(
185191
type: Arcp::MessageTypes::JOB_ERROR,
186192
session_id: record&.submitter_session_id || '',
@@ -212,7 +218,7 @@ def run_agent(task, reg, job_id, submit, lease)
212218
lease: lease, sink: self
213219
)
214220
if submit.max_runtime_sec
215-
deadline = task.async do
221+
task.async do
216222
task.sleep(submit.max_runtime_sec)
217223
ctx.fail!(code: 'TIMEOUT', message: 'max_runtime_sec elapsed', retryable: true)
218224
task.stop

lib/arcp/runtime/lease_manager.rb

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,12 @@ def check!(job_id, capability:)
3434
)
3535
end
3636

37-
unless lease.capabilities.include?(capability)
38-
raise Arcp::Errors::PermissionDenied.new(
39-
"capability #{capability.inspect} not in lease #{lease.id}",
40-
details: { 'capability' => capability, 'lease_id' => lease.id }
41-
)
42-
end
37+
return if lease.capabilities.include?(capability)
38+
39+
raise Arcp::Errors::PermissionDenied.new(
40+
"capability #{capability.inspect} not in lease #{lease.id}",
41+
details: { 'capability' => capability, 'lease_id' => lease.id }
42+
)
4343
end
4444

4545
# Try to decrement the bound budget. Returns true on success, raises

0 commit comments

Comments
 (0)