mirrors-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/Gemfile +7 -0
- data/README.md +273 -0
- data/Rakefile +11 -0
- data/lib/mirrors_sdk/agent_context.rb +51 -0
- data/lib/mirrors_sdk/capture.rb +236 -0
- data/lib/mirrors_sdk/circuit.rb +51 -0
- data/lib/mirrors_sdk/client.rb +261 -0
- data/lib/mirrors_sdk/configuration.rb +74 -0
- data/lib/mirrors_sdk/context.rb +281 -0
- data/lib/mirrors_sdk/delivery.rb +74 -0
- data/lib/mirrors_sdk/executor.rb +226 -0
- data/lib/mirrors_sdk/models.rb +104 -0
- data/lib/mirrors_sdk/snapshot.rb +129 -0
- data/lib/mirrors_sdk/transport.rb +223 -0
- data/lib/mirrors_sdk/version.rb +5 -0
- data/lib/mirrors_sdk/wire.rb +238 -0
- data/lib/mirrors_sdk/worker.rb +114 -0
- data/lib/mirrors_sdk.rb +13 -0
- data/mirrors-sdk.gemspec +23 -0
- data/skills/install-mirrors-ruby-sdk/SKILL.md +152 -0
- data/test/capture_test.rb +303 -0
- data/test/configuration_test.rb +64 -0
- data/test/context_test.rb +295 -0
- data/test/executor_test.rb +93 -0
- data/test/fixtures/canonical-events.json +38 -0
- data/test/integration_test.rb +154 -0
- data/test/lifecycle_test.rb +128 -0
- data/test/snapshot_test.rb +88 -0
- data/test/support/runtime_server.rb +101 -0
- data/test/test_helper.rb +30 -0
- data/test/transport_test.rb +182 -0
- data/test/wire_test.rb +128 -0
- metadata +72 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
require "socket"
|
|
5
|
+
require "stringio"
|
|
6
|
+
require_relative "../lib/mirrors_sdk/configuration"
|
|
7
|
+
require_relative "../lib/mirrors_sdk/transport"
|
|
8
|
+
|
|
9
|
+
class TransportTest < Minitest::Test
|
|
10
|
+
def setup
|
|
11
|
+
@requests = Queue.new
|
|
12
|
+
@connections, @handlers = [], []
|
|
13
|
+
@behavior = ->(socket, _request) { respond(socket) }
|
|
14
|
+
@server = TCPServer.new("127.0.0.1", 0)
|
|
15
|
+
@config = MirrorsSDK::Config.new(api_url: "http://127.0.0.1:#{@server.addr[1]}/prefix",
|
|
16
|
+
api_key: "private-token", agent_id: "agent", failure_threshold: 1)
|
|
17
|
+
@transport = MirrorsSDK::Transport.new(@config)
|
|
18
|
+
@acceptor = Thread.new do
|
|
19
|
+
loop do
|
|
20
|
+
socket = @server.accept
|
|
21
|
+
@connections << socket
|
|
22
|
+
@handlers << Thread.new(socket) { |connection| handle(connection) }
|
|
23
|
+
end
|
|
24
|
+
rescue IOError, Errno::EBADF
|
|
25
|
+
nil
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def teardown
|
|
30
|
+
@transport.close if @transport
|
|
31
|
+
@server.close if @server
|
|
32
|
+
@connections.each { |socket| socket.close unless socket.closed? }
|
|
33
|
+
@acceptor.join(0.2) if @acceptor
|
|
34
|
+
@handlers.each { |thread| thread.join(0.2) || thread.kill }
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_exact_wire_binding_and_mode_get
|
|
38
|
+
body = '{"final":false,"text":"original"}'
|
|
39
|
+
assert_equal({ "ok" => true }, post(body))
|
|
40
|
+
request = @requests.pop
|
|
41
|
+
assert_equal "POST /prefix/v1/agents/agent/runtime/trace HTTP/1.1", request[:line]
|
|
42
|
+
assert_equal body, request[:body]
|
|
43
|
+
assert_equal "Bearer private-token", request[:headers]["authorization"]
|
|
44
|
+
assert_equal "3", request[:headers]["x-mirrors-protocol"]
|
|
45
|
+
assert_equal Digest::SHA256.hexdigest("trace\n#{body}"), request[:headers]["idempotency-key"]
|
|
46
|
+
@transport.request("mode", nil, deadline: monotonic + 1) { true }
|
|
47
|
+
request = @requests.pop
|
|
48
|
+
assert request[:line].start_with?("GET ")
|
|
49
|
+
assert_equal "", request[:body]
|
|
50
|
+
assert_equal "1", request[:headers]["x-mirrors-protocol"]
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_total_deadline_bounds_trickling_response
|
|
54
|
+
@behavior = lambda do |socket, _request|
|
|
55
|
+
socket.write("HTTP/1.1 200 OK\r\nContent-Length: 1000\r\nConnection: close\r\n\r\n")
|
|
56
|
+
1000.times { socket.write(" "); sleep 0.005 }
|
|
57
|
+
end
|
|
58
|
+
started = monotonic
|
|
59
|
+
error = assert_raises(MirrorsSDK::TransportError) { post(timeout: 0.05) }
|
|
60
|
+
assert_equal "timeout", error.reason
|
|
61
|
+
assert_operator monotonic - started, :<, 0.3
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def test_redirect_does_not_forward_credentials
|
|
65
|
+
@behavior = ->(socket, _) { respond(socket, status: 302, headers: "Location: /credential-target\r\n") }
|
|
66
|
+
assert_raises(MirrorsSDK::TransportError) { post }
|
|
67
|
+
assert_equal 1, @requests.size
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_raw_and_decompressed_limits_and_valid_gzip
|
|
71
|
+
data = '{"data":"' + ("x" * 70_000) + '"}'
|
|
72
|
+
[nil, "gzip", "deflate"].each do |encoding|
|
|
73
|
+
reset_transport
|
|
74
|
+
payload = encode(data, encoding)
|
|
75
|
+
headers = encoding ? "Content-Encoding: #{encoding}\r\n" : ""
|
|
76
|
+
@behavior = ->(socket, _) { respond(socket, body: payload, headers: headers) }
|
|
77
|
+
error = assert_raises(MirrorsSDK::TransportError) { post }
|
|
78
|
+
assert_equal "response_too_large", error.reason
|
|
79
|
+
end
|
|
80
|
+
reset_transport
|
|
81
|
+
payload = encode('{"ok":true}', "gzip")
|
|
82
|
+
@behavior = ->(socket, _) { respond(socket, body: payload, headers: "Content-Encoding: gzip\r\n") }
|
|
83
|
+
assert_equal({ "ok" => true }, post)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_cancellation_releases_socket_without_opening_circuit
|
|
87
|
+
entered = Queue.new
|
|
88
|
+
@behavior = ->(socket, _) { entered << true; socket.read }
|
|
89
|
+
token = MirrorsSDK::Cancellation.new
|
|
90
|
+
job = Thread.new do
|
|
91
|
+
post(cancel: token)
|
|
92
|
+
rescue MirrorsSDK::TransportError => error
|
|
93
|
+
error
|
|
94
|
+
end
|
|
95
|
+
entered.pop
|
|
96
|
+
token.cancel
|
|
97
|
+
assert job.join(0.5), "Cancellation must interrupt a connected request"
|
|
98
|
+
assert_equal "canceled", job.value.reason
|
|
99
|
+
@behavior = ->(socket, _) { respond(socket) }
|
|
100
|
+
assert_equal({ "ok" => true }, post)
|
|
101
|
+
ensure
|
|
102
|
+
job.kill if job && job.alive?
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def test_final_circuit_reconciles_exact_prefix_independently
|
|
106
|
+
body = '{"final":false,"requestId":"immutable-prefix"}'
|
|
107
|
+
@behavior = ->(socket, _) { respond(socket, status: 503) }
|
|
108
|
+
assert_raises(MirrorsSDK::TransportError) { post(body) }
|
|
109
|
+
first = @requests.pop
|
|
110
|
+
@behavior = ->(socket, _) { respond(socket) }
|
|
111
|
+
value = @transport.request("trace", body, deadline: monotonic + 1, final_recovery: true) { |v| v["ok"] }
|
|
112
|
+
assert_equal({ "ok" => true }, value)
|
|
113
|
+
second = @requests.pop
|
|
114
|
+
assert_equal first[:body], second[:body]
|
|
115
|
+
assert_equal first[:headers]["idempotency-key"], second[:headers]["idempotency-key"]
|
|
116
|
+
assert_equal "circuit_open", assert_raises(MirrorsSDK::TransportError) { post }.reason
|
|
117
|
+
assert_equal({ "ok" => true }, post('{"final":true}'))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def test_invalid_json_and_bindings_fail_closed
|
|
121
|
+
['{"ok":true,"ok":false}', '{"value":NaN}', '{"ok":false}', "\xff".b].each do |body|
|
|
122
|
+
reset_transport
|
|
123
|
+
@behavior = ->(socket, _) { respond(socket, body: body) }
|
|
124
|
+
error = assert_raises(MirrorsSDK::TransportError) { post }
|
|
125
|
+
assert_equal "invalid_response", error.reason
|
|
126
|
+
assert_nil error.cause
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def test_permanent_rejection_has_sanitized_message
|
|
131
|
+
@behavior = ->(socket, _) { respond(socket, status: 400, body: "private-token original history") }
|
|
132
|
+
error = assert_raises(MirrorsSDK::TransportError) { post }
|
|
133
|
+
assert error.permanent
|
|
134
|
+
assert_equal "rejected", error.message
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
private
|
|
138
|
+
|
|
139
|
+
def monotonic
|
|
140
|
+
Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
def post(body = '{"final":false}', timeout: 1, cancel: nil)
|
|
144
|
+
@transport.request("trace", body, deadline: monotonic + timeout, cancel: cancel) { |value| value["ok"] == true }
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def reset_transport
|
|
148
|
+
@transport.close
|
|
149
|
+
@transport = MirrorsSDK::Transport.new(@config)
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
def handle(socket)
|
|
153
|
+
line = socket.gets("\r\n").strip
|
|
154
|
+
headers = {}
|
|
155
|
+
while (header = socket.gets("\r\n")) && header != "\r\n"
|
|
156
|
+
key, value = header.strip.split(":", 2)
|
|
157
|
+
headers[key.downcase] = value.strip
|
|
158
|
+
end
|
|
159
|
+
request = { line: line, headers: headers, body: socket.read(headers.fetch("content-length", "0").to_i) }
|
|
160
|
+
@requests << request
|
|
161
|
+
@behavior.call(socket, request)
|
|
162
|
+
rescue IOError, SystemCallError
|
|
163
|
+
nil
|
|
164
|
+
ensure
|
|
165
|
+
socket.close unless socket.closed?
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
def respond(socket, body: '{"ok":true}', status: 200, headers: "")
|
|
169
|
+
socket.write("HTTP/1.1 #{status} Result\r\nContent-Length: #{body.bytesize}\r\nConnection: close\r\n#{headers}\r\n#{body}")
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
def encode(data, encoding)
|
|
173
|
+
return data unless encoding
|
|
174
|
+
return Zlib::Deflate.deflate(data) if encoding == "deflate"
|
|
175
|
+
|
|
176
|
+
output = StringIO.new
|
|
177
|
+
writer = Zlib::GzipWriter.new(output)
|
|
178
|
+
writer.write(data)
|
|
179
|
+
writer.close
|
|
180
|
+
output.string
|
|
181
|
+
end
|
|
182
|
+
end
|
data/test/wire_test.rb
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "test_helper"
|
|
4
|
+
require "mirrors_sdk/wire"
|
|
5
|
+
|
|
6
|
+
class WireTest < Minitest::Test
|
|
7
|
+
def trace(**extra)
|
|
8
|
+
{ "id" => "run-1", "messages" => [{ "role" => "user", "content" => "Hello" }] }.merge(extra.transform_keys(&:to_s))
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def utc(offset = 0)
|
|
12
|
+
(Time.now + offset).utc.iso8601(3)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def capture(final = false)
|
|
16
|
+
request = { "protocol" => 3, "agentId" => "agent", "requestId" => "request", "ordinal" => 1,
|
|
17
|
+
"trace" => trace, "final" => final, "captureOnly" => true }
|
|
18
|
+
response = { "protocol" => 3, "agentId" => "agent", "traceId" => "run-1", "requestId" => "request",
|
|
19
|
+
"ordinal" => 1, "final" => final, "captureOnly" => true, "decisionId" => "decision",
|
|
20
|
+
"expiresAt" => utc(1), "mode" => "active", "role" => "system", "context" => nil,
|
|
21
|
+
"ingestion" => final ? { "status" => "queued", "jobId" => "job" } : nil }
|
|
22
|
+
[request, response]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def context
|
|
26
|
+
request = { "protocol" => 1, "agentId" => "agent", "requestId" => "request", "trace" => trace }
|
|
27
|
+
response = { "protocol" => 1, "agentId" => "agent", "requestId" => "request", "traceId" => "run-1",
|
|
28
|
+
"decisionId" => "decision", "issuedAt" => utc(-0.1), "expiresAt" => utc(1), "mode" => "active",
|
|
29
|
+
"modeVersion" => "version", "role" => "system", "context" => "fix", "reason" => "selected" }
|
|
30
|
+
[request, response]
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_shared_canonical_fixture_is_preserved
|
|
34
|
+
fixture = JSON.parse(File.read(File.join(__dir__, "fixtures/canonical-events.json")))
|
|
35
|
+
["../../python-sdk/tests/fixtures/canonical-events.json", "../../remote-sdk/test/fixtures/canonical-events.json"].each do |path|
|
|
36
|
+
source = File.expand_path(path, __dir__)
|
|
37
|
+
assert_equal JSON.parse(File.read(source)), fixture if File.exist?(source)
|
|
38
|
+
end
|
|
39
|
+
messages = fixture["events"][0]["data"]
|
|
40
|
+
source = trace(messages: messages)
|
|
41
|
+
assert_equal source, MirrorsSDK::Wire.snapshot(source)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_conversation_user_and_latency_rules
|
|
45
|
+
source = trace(conversationId: "chat", conversationRevision: 7,
|
|
46
|
+
user: { "id" => "\ufeffuser\ufeff", "segment" => " founders " }, turnLatencyMs: 0.25)
|
|
47
|
+
copied = MirrorsSDK::Wire.snapshot(source, final: true, terminal: "failed")
|
|
48
|
+
assert_equal({ "id" => "user", "segment" => "founders" }, copied["user"])
|
|
49
|
+
assert_equal "\ufeffuser\ufeff", source["user"]["id"]
|
|
50
|
+
assert_equal 0.25, copied["turnLatencyMs"]
|
|
51
|
+
[0, -1, 1.5, true, 9_007_199_254_740_992].each do |revision|
|
|
52
|
+
assert_raises(ArgumentError) { MirrorsSDK::Wire.snapshot(trace(conversationId: "chat", conversationRevision: revision)) }
|
|
53
|
+
end
|
|
54
|
+
assert_raises(ArgumentError) { MirrorsSDK::Wire.snapshot(trace(conversationId: "chat")) }
|
|
55
|
+
assert_raises(ArgumentError) { MirrorsSDK::Wire.snapshot(source) }
|
|
56
|
+
assert_raises(ArgumentError) { MirrorsSDK::Wire.snapshot(trace, terminal: "completed") }
|
|
57
|
+
[true, -1, Float::INFINITY].each do |latency|
|
|
58
|
+
assert_raises(ArgumentError) { MirrorsSDK::Wire.snapshot(trace(turnLatencyMs: latency), final: true) }
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_agent_context_matches_prompt_and_skill_limits
|
|
63
|
+
["", " Original prompt\n", "💡" * 50_000].each do |prompt|
|
|
64
|
+
source = trace(agentContext: { "systemPrompt" => prompt, "skills" => [{ "name" => "search", "content" => "Find evidence" }] })
|
|
65
|
+
assert_equal source, MirrorsSDK::Wire.snapshot(source)
|
|
66
|
+
end
|
|
67
|
+
assert_equal({ "skills" => [] }, MirrorsSDK::Wire.snapshot(trace(agentContext: { "skills" => [] }))["agentContext"])
|
|
68
|
+
[{}, nil, { "systemPrompt" => nil }, { "systemPrompt" => "💡" * 50_001 },
|
|
69
|
+
{ "systemPrompt" => "界" * 87_376 }, { "skills" => [{ "name" => " ", "content" => "x" }] },
|
|
70
|
+
{ "skills" => [{ "name" => "a", "content" => "" }] },
|
|
71
|
+
{ "skills" => Array.new(2) { { "name" => "same", "content" => "x" } } },
|
|
72
|
+
{ "skills" => [{ "name" => "a\n", "content" => "x" }] },
|
|
73
|
+
{ "systemPrompt" => "x", "unknown" => true }].each do |agent_context|
|
|
74
|
+
assert_raises(ArgumentError) { MirrorsSDK::Wire.snapshot(trace(agentContext: agent_context)) }
|
|
75
|
+
end
|
|
76
|
+
boundary = trace(agentContext: { "systemPrompt" => "界" * 87_375 })
|
|
77
|
+
assert_equal 262_144, MirrorsSDK::Wire.encode(boundary["agentContext"]).bytesize
|
|
78
|
+
assert_equal boundary, MirrorsSDK::Wire.snapshot(boundary)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def test_invalid_fields_and_unicode_limits_do_not_silently_change_evidence
|
|
82
|
+
[trace(extra: 1), trace(id: "résumé"), trace(entryPoint: "💡" * 129),
|
|
83
|
+
trace(user: { "id" => "a\u001cb" }), trace(user: { "id" => "💡" * 129 }),
|
|
84
|
+
trace(messages: [{ "role" => "assistant", "content" => [{ "type" => "tool-call", "callId" => "c", "name" => "t" }] }]),
|
|
85
|
+
trace(messages: [{ "role" => "tool", "content" => [{ "type" => "tool-result", "callId" => "c", "name" => "t", "value" => nil, "error" => 1 }] }])].each do |source|
|
|
86
|
+
assert_raises(ArgumentError) { MirrorsSDK::Wire.snapshot(source) }
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def test_capture_receipts_require_exact_bindings_and_flags
|
|
91
|
+
[false, true].each do |final|
|
|
92
|
+
request, response = capture(final)
|
|
93
|
+
assert MirrorsSDK::Wire.trace_response(response, request)
|
|
94
|
+
{ "ordinal" => 1.0, "final" => 1, "agentId" => "other", "traceId" => "other", "protocol" => true,
|
|
95
|
+
"context" => "injection", "captureOnly" => 1, "expiresAt" => "2026-02-30T00:00:00.000Z" }.each do |field, invalid|
|
|
96
|
+
refute MirrorsSDK::Wire.trace_response(response.merge(field => invalid), request), field
|
|
97
|
+
end
|
|
98
|
+
%w[context ingestion captureOnly].each do |field|
|
|
99
|
+
refute MirrorsSDK::Wire.trace_response(response.reject { |key, _| key == field }, request)
|
|
100
|
+
end
|
|
101
|
+
bad_ingestion = final ? nil : { "status" => "queued", "jobId" => "job" }
|
|
102
|
+
refute MirrorsSDK::Wire.trace_response(response.merge("ingestion" => bad_ingestion), request)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def test_context_bindings_shadow_and_mode_lease_limits
|
|
107
|
+
request, response = context
|
|
108
|
+
assert MirrorsSDK::Wire.context_response(response, request)
|
|
109
|
+
{ "expiresAt" => utc(6), "issuedAt" => utc(2), "context" => "é" * 16_385,
|
|
110
|
+
"mode" => "shadow", "protocol" => true, "modeVersion" => "bad/value", "shadowOnly" => false }.each do |field, invalid|
|
|
111
|
+
refute MirrorsSDK::Wire.context_response(response.merge(field => invalid), request), field
|
|
112
|
+
end
|
|
113
|
+
assert MirrorsSDK::Wire.context_response(response.merge("shadowOnly" => true, "mode" => "shadow", "context" => nil),
|
|
114
|
+
request.merge("shadowOnly" => true))
|
|
115
|
+
mode = { "protocol" => 1, "agentId" => "agent", "mode" => "active", "version" => "v",
|
|
116
|
+
"issuedAt" => utc(-0.1), "expiresAt" => utc(1) }
|
|
117
|
+
assert MirrorsSDK::Wire.mode_response(mode, "agent")
|
|
118
|
+
refute MirrorsSDK::Wire.mode_response(mode.merge("expiresAt" => utc(4)), "agent")
|
|
119
|
+
refute MirrorsSDK::Wire.mode_response(mode.merge("agentId" => "other"), "agent")
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
def test_dates_are_canonical_calendar_dates_with_milliseconds
|
|
123
|
+
assert_equal 1_767_225_600, MirrorsSDK::Wire.utc("2026-01-01T00:00:00.000Z")
|
|
124
|
+
["2026-01-01T00:00:00Z", "2026-01-01T00:00:00.000+00:00", "2026-02-30T00:00:00.000Z", nil].each do |invalid|
|
|
125
|
+
assert_raises(ArgumentError) { MirrorsSDK::Wire.utc(invalid) }
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: mirrors-sdk
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Mirrors
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Capture canonical conversation history and retrieve bounded, server-selected
|
|
13
|
+
context while your application owns model calls and tools.
|
|
14
|
+
executables: []
|
|
15
|
+
extensions: []
|
|
16
|
+
extra_rdoc_files: []
|
|
17
|
+
files:
|
|
18
|
+
- Gemfile
|
|
19
|
+
- README.md
|
|
20
|
+
- Rakefile
|
|
21
|
+
- lib/mirrors_sdk.rb
|
|
22
|
+
- lib/mirrors_sdk/agent_context.rb
|
|
23
|
+
- lib/mirrors_sdk/capture.rb
|
|
24
|
+
- lib/mirrors_sdk/circuit.rb
|
|
25
|
+
- lib/mirrors_sdk/client.rb
|
|
26
|
+
- lib/mirrors_sdk/configuration.rb
|
|
27
|
+
- lib/mirrors_sdk/context.rb
|
|
28
|
+
- lib/mirrors_sdk/delivery.rb
|
|
29
|
+
- lib/mirrors_sdk/executor.rb
|
|
30
|
+
- lib/mirrors_sdk/models.rb
|
|
31
|
+
- lib/mirrors_sdk/snapshot.rb
|
|
32
|
+
- lib/mirrors_sdk/transport.rb
|
|
33
|
+
- lib/mirrors_sdk/version.rb
|
|
34
|
+
- lib/mirrors_sdk/wire.rb
|
|
35
|
+
- lib/mirrors_sdk/worker.rb
|
|
36
|
+
- mirrors-sdk.gemspec
|
|
37
|
+
- skills/install-mirrors-ruby-sdk/SKILL.md
|
|
38
|
+
- test/capture_test.rb
|
|
39
|
+
- test/configuration_test.rb
|
|
40
|
+
- test/context_test.rb
|
|
41
|
+
- test/executor_test.rb
|
|
42
|
+
- test/fixtures/canonical-events.json
|
|
43
|
+
- test/integration_test.rb
|
|
44
|
+
- test/lifecycle_test.rb
|
|
45
|
+
- test/snapshot_test.rb
|
|
46
|
+
- test/support/runtime_server.rb
|
|
47
|
+
- test/test_helper.rb
|
|
48
|
+
- test/transport_test.rb
|
|
49
|
+
- test/wire_test.rb
|
|
50
|
+
homepage: https://mirrors.dev/docs
|
|
51
|
+
licenses: []
|
|
52
|
+
metadata:
|
|
53
|
+
source_code_uri: https://github.com/runmirrors/sidecar/tree/main/packages/ruby-sdk
|
|
54
|
+
documentation_uri: https://github.com/runmirrors/sidecar/blob/main/packages/ruby-sdk/README.md
|
|
55
|
+
rdoc_options: []
|
|
56
|
+
require_paths:
|
|
57
|
+
- lib
|
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
version: '3.3'
|
|
63
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - ">="
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '0'
|
|
68
|
+
requirements: []
|
|
69
|
+
rubygems_version: 3.6.9
|
|
70
|
+
specification_version: 4
|
|
71
|
+
summary: Background trace capture and optional context for Ruby agents
|
|
72
|
+
test_files: []
|