agent_c 2.71828

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 (62) hide show
  1. checksums.yaml +7 -0
  2. data/.rubocop.yml +10 -0
  3. data/.ruby-version +1 -0
  4. data/CLAUDE.md +21 -0
  5. data/README.md +360 -0
  6. data/Rakefile +16 -0
  7. data/TODO.md +12 -0
  8. data/agent_c.gemspec +38 -0
  9. data/docs/chat-methods.md +157 -0
  10. data/docs/cost-reporting.md +86 -0
  11. data/docs/pipeline-tips-and-tricks.md +71 -0
  12. data/docs/session-configuration.md +274 -0
  13. data/docs/testing.md +747 -0
  14. data/docs/tools.md +103 -0
  15. data/docs/versioned-store.md +840 -0
  16. data/lib/agent_c/agent/chat.rb +211 -0
  17. data/lib/agent_c/agent/chat_response.rb +32 -0
  18. data/lib/agent_c/agent/chats/anthropic_bedrock.rb +48 -0
  19. data/lib/agent_c/batch.rb +102 -0
  20. data/lib/agent_c/configs/repo.rb +90 -0
  21. data/lib/agent_c/context.rb +56 -0
  22. data/lib/agent_c/costs/data.rb +39 -0
  23. data/lib/agent_c/costs/report.rb +219 -0
  24. data/lib/agent_c/db/store.rb +162 -0
  25. data/lib/agent_c/errors.rb +19 -0
  26. data/lib/agent_c/pipeline.rb +188 -0
  27. data/lib/agent_c/processor.rb +98 -0
  28. data/lib/agent_c/prompts.yml +53 -0
  29. data/lib/agent_c/schema.rb +85 -0
  30. data/lib/agent_c/session.rb +207 -0
  31. data/lib/agent_c/store.rb +72 -0
  32. data/lib/agent_c/test_helpers.rb +173 -0
  33. data/lib/agent_c/tools/dir_glob.rb +46 -0
  34. data/lib/agent_c/tools/edit_file.rb +112 -0
  35. data/lib/agent_c/tools/file_metadata.rb +43 -0
  36. data/lib/agent_c/tools/grep.rb +119 -0
  37. data/lib/agent_c/tools/paths.rb +36 -0
  38. data/lib/agent_c/tools/read_file.rb +94 -0
  39. data/lib/agent_c/tools/run_rails_test.rb +87 -0
  40. data/lib/agent_c/tools.rb +60 -0
  41. data/lib/agent_c/utils/git.rb +75 -0
  42. data/lib/agent_c/utils/shell.rb +58 -0
  43. data/lib/agent_c/version.rb +5 -0
  44. data/lib/agent_c.rb +32 -0
  45. data/lib/versioned_store/base.rb +314 -0
  46. data/lib/versioned_store/config.rb +26 -0
  47. data/lib/versioned_store/stores/schema.rb +127 -0
  48. data/lib/versioned_store/version.rb +5 -0
  49. data/lib/versioned_store.rb +5 -0
  50. data/template/Gemfile +9 -0
  51. data/template/Gemfile.lock +152 -0
  52. data/template/README.md +61 -0
  53. data/template/Rakefile +50 -0
  54. data/template/bin/rake +27 -0
  55. data/template/lib/autoload.rb +10 -0
  56. data/template/lib/config.rb +59 -0
  57. data/template/lib/pipeline.rb +19 -0
  58. data/template/lib/prompts.yml +57 -0
  59. data/template/lib/store.rb +17 -0
  60. data/template/test/pipeline_test.rb +221 -0
  61. data/template/test/test_helper.rb +18 -0
  62. metadata +191 -0
data/lib/agent_c.rb ADDED
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "ruby_llm"
4
+ require "active_support/all"
5
+
6
+ # this shows an annyoing warning
7
+ begin
8
+ old_stderr = $stderr
9
+ $stderr = StringIO.new
10
+ require "async"
11
+ require "async/semaphore"
12
+ $stderr = old_stderr
13
+ ensure
14
+ end
15
+
16
+ require "zeitwerk"
17
+ loader = Zeitwerk::Loader.for_gem(warn_on_extra_files: false)
18
+ loader.setup
19
+
20
+ # Configure i18n how I like it:
21
+
22
+ require "i18n"
23
+ MissingTranslation = Class.new(StandardError)
24
+ I18n.singleton_class.prepend(Module.new do
25
+ def t(*a, **p)
26
+ super(*a, __force_exception_raising__: true, **p)
27
+ end
28
+ end)
29
+ I18n.exception_handler = ->(_, _, key, _) { raise MissingTranslation.new(key.inspect) }
30
+ I18n.load_path << File.join(__dir__, "./agent_c/prompts.yml")
31
+
32
+ module AgentC; end
@@ -0,0 +1,314 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_record"
4
+
5
+ module VersionedStore
6
+ class Base
7
+ class << self
8
+ def inherited(subclass)
9
+ super
10
+ # If parent has a schema, duplicate it for the child
11
+ # Otherwise create a new empty schema
12
+ parent_schema = @schema
13
+ if parent_schema
14
+ subclass.instance_variable_set(:@schema, parent_schema.dup)
15
+ else
16
+ subclass.instance_variable_set(:@schema, Stores::Schema.new)
17
+ end
18
+ end
19
+
20
+ def schema
21
+ @schema ||= Stores::Schema.new
22
+ end
23
+
24
+ def record(name, table: nil, &block)
25
+ schema.record(name, table: table, &block)
26
+ end
27
+
28
+ def migrate(version = nil, &block)
29
+ if version.nil?
30
+ schema.migrate(&block)
31
+ else
32
+ schema.migrate(version, &block)
33
+ end
34
+ end
35
+ end
36
+
37
+ attr_reader :schema, :version, :config
38
+ def initialize(version: :root, **config)
39
+ @schema = self.class.schema
40
+ @schema.add_table_migrations!
41
+ @version = version
42
+ @config = Config.new(**config)
43
+ @klasses = {}
44
+
45
+ if File.exist?(@config.db_filename)
46
+ @config&.logger&.info("using existing store at: #{@config.db_filename}")
47
+ else
48
+ @config&.logger&.info("creating store at: #{@config.db_filename}")
49
+ end
50
+
51
+
52
+ # Define accessor methods for each record type
53
+ schema.records.each do |name, spec|
54
+ singleton_class.define_method(name) { class_for(spec) }
55
+ end
56
+
57
+ # eagerly construct classes
58
+ schema.records.each_value { class_for(_1) }
59
+
60
+ # Run post-initialization hooks for setting up associations
61
+ schema.post_init_hooks.each { |hook| hook.call(self) }
62
+ end
63
+
64
+ def transaction(&)
65
+ base_class.transaction(&)
66
+ end
67
+
68
+ def dir
69
+ @config.dir
70
+ end
71
+
72
+ def versions
73
+ return [self] unless root?
74
+
75
+ version_files = Dir.glob(File.join(versions_dir, "*.sqlite3")).sort
76
+ version_files.map do |file|
77
+ version_num = File.basename(file, ".sqlite3").to_i
78
+ self.class.new(version: version_num, **config.to_h)
79
+ end
80
+ end
81
+
82
+ def restore(name = nil)
83
+ if name
84
+ # Named snapshot restore
85
+ raise "Can only restore from root store" unless root?
86
+
87
+ snapshot_path = File.join(snapshots_dir, "#{name}.sqlite3")
88
+ raise "Snapshot '#{name}' does not exist" unless File.exist?(snapshot_path)
89
+
90
+ # Copy the snapshot to the main database
91
+ main_db = File.join(dir, config.db_filename)
92
+ FileUtils.cp(snapshot_path, main_db)
93
+
94
+ # Create a new version snapshot of the restored state
95
+ timestamp = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
96
+ version_file = File.join(versions_dir, "#{timestamp}.sqlite3")
97
+ FileUtils.mkdir_p(File.dirname(version_file))
98
+ FileUtils.cp(main_db, version_file)
99
+
100
+ # Return a new root store for the restored state
101
+ self.class.new(version:, **config.to_h)
102
+ else
103
+ # Version-based restore (existing behavior)
104
+ raise "Can only restore from a version snapshot, not from root" if root?
105
+
106
+ # Copy the version database to the main database
107
+ main_db = File.join(dir, config.db_filename)
108
+ FileUtils.cp(db_path, main_db)
109
+
110
+ # Renumber versions: keep all versions up to and including this one,
111
+ # then create a new version snapshot for the restore
112
+ version_files = Dir.glob(File.join(versions_dir, "*.sqlite3")).sort
113
+ version_files.each do |file|
114
+ file_version = File.basename(file, ".sqlite3").to_i
115
+ if file_version > version
116
+ FileUtils.rm(file)
117
+ end
118
+ end
119
+
120
+ # Create a new version snapshot of the restored state
121
+ new_version_num = version + 1
122
+ version_file = File.join(versions_dir, "#{new_version_num}.sqlite3")
123
+ FileUtils.cp(main_db, version_file)
124
+
125
+ # Return a new root store for the restored state
126
+ self.class.new(version: :root, **config.to_h)
127
+ end
128
+ end
129
+
130
+ def snapshot(name)
131
+ raise "Can only create snapshots from root store" unless root?
132
+
133
+ # Create snapshots directory if it doesn't exist
134
+ FileUtils.mkdir_p(snapshots_dir)
135
+
136
+ # Copy current database to named snapshot
137
+ snapshot_path = File.join(snapshots_dir, "#{name}.sqlite3")
138
+ FileUtils.cp(db_path, snapshot_path)
139
+ end
140
+
141
+ private
142
+
143
+ def schema_root_mod_name
144
+ @schema_root_mod_name ||+ "SchemaRoot_#{schema.object_id}_#{object_id}"
145
+ end
146
+
147
+ def schema_root_mod
148
+ @schema_root_mod ||= (
149
+ if Base.const_defined?(schema_root_mod_name)
150
+ Base.const_get(schema_root_mod_name)
151
+ else
152
+ Module.new.tap { Base.const_set(schema_root_mod_name, _1) }
153
+ end
154
+ )
155
+ end
156
+
157
+ def base_class
158
+ @base_class ||= (
159
+ me = self
160
+ klass = Class.new(ActiveRecord::Base) do
161
+ define_singleton_method(:transaction_mutex) do
162
+ @transaction_mutex ||= Mutex.new
163
+ end
164
+
165
+ define_singleton_method(:class_name) do |name|
166
+ "#{me.send(:schema_root_mod)}::Record_#{name}"
167
+ end
168
+
169
+ define_singleton_method(:store) do
170
+ me
171
+ end
172
+
173
+ define_method(:store) do
174
+ self.class.store
175
+ end
176
+
177
+ define_singleton_method(:transaction) do |**options, &block|
178
+ # If already in a transaction, just call super without creating a backup
179
+ return super(**options, &block) if connection.transaction_open?
180
+
181
+ transaction_mutex.synchronize do
182
+ result = super(**options, &block)
183
+
184
+ if me.config.versioned
185
+ timestamp = Process.clock_gettime(Process::CLOCK_REALTIME, :nanosecond)
186
+ backup_path = File.join(me.send(:versions_dir), "#{timestamp}.sqlite3")
187
+ FileUtils.mkdir_p(File.dirname(backup_path))
188
+ FileUtils.cp(me.send(:db_path), backup_path)
189
+ end
190
+
191
+ result
192
+ end
193
+ end
194
+ end
195
+
196
+ # ActiveRecord doesn't let this be anonymous?
197
+ schema_root_mod.const_set(
198
+ "Base",
199
+ klass
200
+ )
201
+
202
+ klass.abstract_class = true
203
+ klass.establish_connection(
204
+ adapter: 'sqlite3',
205
+ database: db_path
206
+ )
207
+
208
+ # Configure SQLite to use DELETE journal mode instead of WAL
209
+ # This avoids creating -wal and -shm files
210
+ klass.connection.execute("PRAGMA journal_mode=DELETE")
211
+ klass.connection.execute("PRAGMA locking_mode=NORMAL")
212
+
213
+ # Only run migrations for the root store, not for version snapshots
214
+ if root? && schema.migrations.any?
215
+ # Ensure schema_migrations table exists
216
+ unless klass.connection.table_exists?(:schema_migrations)
217
+ klass.connection.create_table(:schema_migrations, id: false) do |t|
218
+ t.string :version, null: false
219
+ end
220
+ klass.connection.add_index(:schema_migrations, :version, unique: true)
221
+ end
222
+
223
+ # Run any migrations that haven't been run yet
224
+ schema.migrations.each do |migration|
225
+ version = migration.version.to_s
226
+
227
+ # Check if migration has already been run using quote method for SQL safety
228
+ existing = klass.connection.select_value(
229
+ "SELECT 1 FROM schema_migrations WHERE version = #{klass.connection.quote(version)} LIMIT 1"
230
+ )
231
+ next if existing
232
+
233
+ # Run the migration
234
+ klass.connection.instance_exec(&migration.block)
235
+
236
+ # Record that this migration has been run
237
+ klass.connection.execute(
238
+ "INSERT INTO schema_migrations (version) VALUES (#{klass.connection.quote(version)})"
239
+ )
240
+ end
241
+ end
242
+
243
+ klass
244
+ )
245
+ end
246
+
247
+ def class_for(spec)
248
+ @klasses[spec.name] ||= (
249
+ store = self
250
+ Class.new(base_class) do
251
+ self.table_name = spec.table
252
+
253
+ # Execute all blocks if present, but define a schema method to ignore schema calls
254
+ if spec.blocks && !spec.blocks.empty?
255
+ define_singleton_method(:schema) { |*args, &blk| }
256
+ spec.blocks.each do |blk|
257
+ class_exec(&blk) if blk
258
+ end
259
+ singleton_class.remove_method(:schema) rescue nil
260
+ end
261
+
262
+ # Make all records readonly if this is a version snapshot
263
+ if not store.send(:root?)
264
+ define_method(:readonly?) { true }
265
+ end
266
+
267
+ # Override polymorphic_name to return simple string name (both instance and class methods)
268
+ simple_name = spec.name.to_s
269
+
270
+ define_method(:polymorphic_name) do
271
+ simple_name
272
+ end
273
+
274
+ define_singleton_method(:polymorphic_name) do
275
+ simple_name
276
+ end
277
+
278
+ # Override polymorphic_class_for to resolve simple names back to classes
279
+ define_singleton_method(:polymorphic_class_for) do |name|
280
+ store.send(:class_for, store.schema.records.fetch(name.to_sym))
281
+ end
282
+ end
283
+ ).tap {
284
+ schema_root_mod.const_set("Record_#{spec.name}", _1)
285
+ }
286
+ end
287
+
288
+ def db_prefix
289
+ @db_prefix ||= config.db_filename.sub(/\.sqlite3$/, "")
290
+ end
291
+
292
+ def versions_dir
293
+ @versions_dir ||= File.join(dir, "#{db_prefix}_versions")
294
+ end
295
+
296
+ def snapshots_dir
297
+ @snapshots_dir ||= File.join(dir, "#{db_prefix}_snapshots")
298
+ end
299
+
300
+ def db_path
301
+ @db_path ||= (
302
+ if root?
303
+ File.join(dir, config.db_filename)
304
+ else
305
+ File.join(versions_dir, "#{version}.sqlite3")
306
+ end
307
+ )
308
+ end
309
+
310
+ def root?
311
+ version == :root
312
+ end
313
+ end
314
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VersionedStore
4
+ Config = Data.define(:dir, :db_filename, :logger, :versioned) do
5
+ def initialize(dir: nil, path: nil, logger: nil, versioned: true, db_filename: nil)
6
+ raise ArgumentError, "Must provide either dir: or path:, not both" if dir && path
7
+ raise ArgumentError, "Must provide either dir: or path:" unless dir || path
8
+
9
+ db_filename ||= (
10
+ if path
11
+ dir = File.dirname(path)
12
+ db_filename = File.basename(path)
13
+ else
14
+ db_filename = "db.sqlite3"
15
+ end
16
+ )
17
+
18
+ super(
19
+ dir:,
20
+ db_filename:,
21
+ logger:,
22
+ versioned:
23
+ )
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "set"
4
+ module VersionedStore
5
+ module Stores
6
+ class Schema
7
+ Record = Data.define(:name, :table, :blocks)
8
+ Migration = Data.define(:version, :block)
9
+
10
+ class RecordContext
11
+ attr_reader :schema_instance, :table_name, :record_name
12
+
13
+ def initialize(schema_instance, record_name)
14
+ @schema_instance = schema_instance
15
+ @record_name = record_name
16
+ @table_name = nil
17
+ end
18
+
19
+ def schema(table_name = nil, &block)
20
+ # If no table name provided, infer from record name
21
+ if table_name.nil?
22
+ name_str = record_name.to_s
23
+ table_name = (name_str.end_with?('s') ? name_str : "#{name_str}s").to_sym
24
+ end
25
+
26
+ # Store the table name for later use
27
+ @table_name = table_name
28
+
29
+ # Collect all schema blocks for a table
30
+ schema_instance.schema_blocks[table_name] ||= []
31
+ schema_instance.schema_blocks[table_name] << block if block
32
+ end
33
+
34
+ def method_missing(...)
35
+ end
36
+
37
+ def respond_to_missing?(...)
38
+ true
39
+ end
40
+ end
41
+
42
+ attr_reader :migrations, :records, :migrated_tables, :schema_blocks, :post_init_hooks
43
+ def initialize
44
+ @migrations = []
45
+ @records = {}
46
+ @migration_counter = 1
47
+ @migrated_tables = Set.new
48
+ @schema_blocks = {}
49
+ @post_init_hooks = []
50
+ end
51
+
52
+ def dup
53
+ new_schema = Schema.new
54
+ new_schema.instance_variable_set(:@migrations, @migrations.dup)
55
+ new_schema.instance_variable_set(:@records, @records.dup)
56
+ new_schema.instance_variable_set(:@migration_counter, @migration_counter)
57
+ new_schema.instance_variable_set(:@migrated_tables, @migrated_tables.dup)
58
+ new_schema.instance_variable_set(:@schema_blocks, @schema_blocks.dup)
59
+ new_schema.instance_variable_set(:@post_init_hooks, @post_init_hooks.dup)
60
+ new_schema.instance_variable_set(:@dir, @dir)
61
+ new_schema
62
+ end
63
+
64
+ def dir(path = nil)
65
+ @dir = path if path
66
+ @dir
67
+ end
68
+
69
+ def migrate(version = @migration_counter += 1, &block)
70
+ migrations << Migration.new(version: version, block: block)
71
+ end
72
+
73
+ def prepend_migration(version, &block)
74
+ migrations.unshift(Migration.new(version: version, block: block))
75
+ end
76
+
77
+ def record(name, table: nil, &block)
78
+ # If block is given, execute it in RecordContext to extract schema calls
79
+ extracted_table = table
80
+ if block
81
+ context = RecordContext.new(self, name)
82
+ context.instance_exec(&block)
83
+ extracted_table ||= context.table_name
84
+ end
85
+
86
+ prev = records[name]
87
+ blocks = prev ? prev.blocks.dup : []
88
+ blocks << block if block
89
+ # Prefer the first non-nil table name, otherwise default to name + "s"
90
+ table_name = prev&.table || extracted_table
91
+ if table_name.nil?
92
+ name_str = name.to_s
93
+ table_name = (name_str.end_with?('s') ? name_str : "#{name_str}s").to_sym
94
+ end
95
+
96
+ records[name] = Record.new(
97
+ name: name,
98
+ table: table_name,
99
+ blocks: blocks
100
+ )
101
+ end
102
+
103
+ # Add a migration for each table with collected schema blocks (called after all record blocks)
104
+ def add_table_migrations!
105
+ schema_blocks.each do |table_name, blocks|
106
+ next if migrated_tables.include?(table_name) || table_name.nil?
107
+ blocks_to_eval = blocks.dup
108
+ migration_block = Proc.new do
109
+ create_table(table_name) do |t|
110
+ blocks_to_eval.each { |blk| blk.call(t) }
111
+ end
112
+ end
113
+ version = "table_#{table_name}"
114
+ prepend_migration(version, &migration_block)
115
+ migrated_tables.add(table_name)
116
+ end
117
+ end
118
+
119
+ def self.call(&block)
120
+ schema = new
121
+ schema.instance_exec(&block) if block
122
+ schema.add_table_migrations!
123
+ schema
124
+ end
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VersionedStore
4
+ VERSION = "0.1.0"
5
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VersionedStore
4
+ class Error < StandardError; end
5
+ end
data/template/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gem "agent_c", path: "../../agent_c"
6
+
7
+ gem "rake"
8
+ gem "minitest"
9
+ gem "debug"
@@ -0,0 +1,152 @@
1
+ PATH
2
+ remote: ..
3
+ specs:
4
+ agent_c (2.71828)
5
+ activerecord
6
+ async
7
+ json-schema
8
+ ruby_llm
9
+ sqlite3
10
+ zeitwerk
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ activemodel (8.1.2)
16
+ activesupport (= 8.1.2)
17
+ activerecord (8.1.2)
18
+ activemodel (= 8.1.2)
19
+ activesupport (= 8.1.2)
20
+ timeout (>= 0.4.0)
21
+ activesupport (8.1.2)
22
+ base64
23
+ bigdecimal
24
+ concurrent-ruby (~> 1.0, >= 1.3.1)
25
+ connection_pool (>= 2.2.5)
26
+ drb
27
+ i18n (>= 1.6, < 2)
28
+ json
29
+ logger (>= 1.4.2)
30
+ minitest (>= 5.1)
31
+ securerandom (>= 0.3)
32
+ tzinfo (~> 2.0, >= 2.0.5)
33
+ uri (>= 0.13.1)
34
+ addressable (2.8.8)
35
+ public_suffix (>= 2.0.2, < 8.0)
36
+ async (2.36.0)
37
+ console (~> 1.29)
38
+ fiber-annotation
39
+ io-event (~> 1.11)
40
+ metrics (~> 0.12)
41
+ traces (~> 0.18)
42
+ base64 (0.3.0)
43
+ bigdecimal (4.0.1)
44
+ concurrent-ruby (1.3.6)
45
+ connection_pool (3.0.2)
46
+ console (1.34.2)
47
+ fiber-annotation
48
+ fiber-local (~> 1.1)
49
+ json
50
+ date (3.5.1)
51
+ debug (1.11.1)
52
+ irb (~> 1.10)
53
+ reline (>= 0.3.8)
54
+ drb (2.2.3)
55
+ erb (6.0.1)
56
+ event_stream_parser (1.0.0)
57
+ faraday (2.14.0)
58
+ faraday-net_http (>= 2.0, < 3.5)
59
+ json
60
+ logger
61
+ faraday-multipart (1.2.0)
62
+ multipart-post (~> 2.0)
63
+ faraday-net_http (3.4.2)
64
+ net-http (~> 0.5)
65
+ faraday-retry (2.4.0)
66
+ faraday (~> 2.0)
67
+ fiber-annotation (0.2.0)
68
+ fiber-local (1.1.0)
69
+ fiber-storage
70
+ fiber-storage (1.0.1)
71
+ i18n (1.14.8)
72
+ concurrent-ruby (~> 1.0)
73
+ io-console (0.8.2)
74
+ io-event (1.14.2)
75
+ irb (1.16.0)
76
+ pp (>= 0.6.0)
77
+ rdoc (>= 4.0.0)
78
+ reline (>= 0.4.2)
79
+ json (2.18.0)
80
+ json-schema (6.1.0)
81
+ addressable (~> 2.8)
82
+ bigdecimal (>= 3.1, < 5)
83
+ logger (1.7.0)
84
+ marcel (1.1.0)
85
+ metrics (0.15.0)
86
+ minitest (6.0.1)
87
+ prism (~> 1.5)
88
+ multipart-post (2.4.1)
89
+ net-http (0.9.1)
90
+ uri (>= 0.11.1)
91
+ pp (0.6.3)
92
+ prettyprint
93
+ prettyprint (0.2.0)
94
+ prism (1.8.0)
95
+ psych (5.3.1)
96
+ date
97
+ stringio
98
+ public_suffix (7.0.2)
99
+ rake (13.3.1)
100
+ rdoc (7.1.0)
101
+ erb
102
+ psych (>= 4.0.0)
103
+ tsort
104
+ reline (0.6.3)
105
+ io-console (~> 0.5)
106
+ ruby_llm (1.11.0)
107
+ base64
108
+ event_stream_parser (~> 1)
109
+ faraday (>= 1.10.0)
110
+ faraday-multipart (>= 1)
111
+ faraday-net_http (>= 1)
112
+ faraday-retry (>= 1)
113
+ marcel (~> 1.0)
114
+ ruby_llm-schema (~> 0.2.1)
115
+ zeitwerk (~> 2)
116
+ ruby_llm-schema (0.2.5)
117
+ securerandom (0.4.1)
118
+ sqlite3 (2.9.0-aarch64-linux-gnu)
119
+ sqlite3 (2.9.0-aarch64-linux-musl)
120
+ sqlite3 (2.9.0-arm-linux-gnu)
121
+ sqlite3 (2.9.0-arm-linux-musl)
122
+ sqlite3 (2.9.0-arm64-darwin)
123
+ sqlite3 (2.9.0-x86_64-darwin)
124
+ sqlite3 (2.9.0-x86_64-linux-gnu)
125
+ sqlite3 (2.9.0-x86_64-linux-musl)
126
+ stringio (3.2.0)
127
+ timeout (0.6.0)
128
+ traces (0.18.2)
129
+ tsort (0.2.0)
130
+ tzinfo (2.0.6)
131
+ concurrent-ruby (~> 1.0)
132
+ uri (1.1.1)
133
+ zeitwerk (2.7.4)
134
+
135
+ PLATFORMS
136
+ aarch64-linux-gnu
137
+ aarch64-linux-musl
138
+ arm-linux-gnu
139
+ arm-linux-musl
140
+ arm64-darwin
141
+ x86_64-darwin
142
+ x86_64-linux-gnu
143
+ x86_64-linux-musl
144
+
145
+ DEPENDENCIES
146
+ agent_c!
147
+ debug
148
+ minitest
149
+ rake
150
+
151
+ BUNDLED WITH
152
+ 2.6.5