rcrewai-rails 0.7.0 → 0.8.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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +93 -1
  3. data/README.md +122 -3
  4. data/app/jobs/rcrewai/rails/crew_execution_job.rb +24 -2
  5. data/app/models/rcrewai/rails/active_record_checkpoint_store.rb +53 -0
  6. data/app/models/rcrewai/rails/agent.rb +4 -0
  7. data/app/models/rcrewai/rails/checkpoint.rb +37 -0
  8. data/app/models/rcrewai/rails/crew.rb +25 -0
  9. data/db/migrate/002_add_config_to_rcrewai_agents.rb +1 -1
  10. data/db/migrate/003_add_output_processing_to_rcrewai_tasks.rb +1 -1
  11. data/db/migrate/004_add_lifecycle_to_rcrewai_crews.rb +1 -1
  12. data/db/migrate/005_add_batch_id_to_rcrewai_executions.rb +1 -1
  13. data/db/migrate/006_create_rcrewai_knowledge_sources.rb +1 -1
  14. data/db/migrate/007_create_rcrewai_flows.rb +1 -1
  15. data/db/migrate/008_add_consensus_agents_to_rcrewai_crews.rb +1 -1
  16. data/db/migrate/010_create_rcrewai_spans.rb +1 -1
  17. data/db/migrate/011_add_observation_rollups_to_rcrewai_executions.rb +1 -1
  18. data/db/migrate/012_create_rcrewai_checkpoints.rb +22 -0
  19. data/lib/generators/rcrewai/rails/install/templates/create_rcrewai_tables.rb +18 -1
  20. data/lib/generators/rcrewai/rails/install/templates/rcrewai.rb +36 -2
  21. data/lib/rcrewai/rails/agent_builder.rb +4 -0
  22. data/lib/rcrewai/rails/configuration.rb +13 -1
  23. data/lib/rcrewai/rails/interceptors.rb +52 -0
  24. data/lib/rcrewai/rails/observation/collector.rb +11 -9
  25. data/lib/rcrewai/rails/observation/span_stack.rb +25 -8
  26. data/lib/rcrewai/rails/version.rb +1 -1
  27. data/lib/rcrewai/rails.rb +1 -0
  28. data/rcrewai-rails.gemspec +1 -1
  29. metadata +7 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e21a3a1057f9221cca3d280f2307b219d15a636995284c781404c4ed3d283fa6
4
- data.tar.gz: 9b715b0cc17839eb5fb23f578905d962c4b6450d0aae2b8b1f494d549497a8b3
3
+ metadata.gz: 7987750789244c90f421923541a2132e3aab1f6891f6b706ed42544ea4ae2507
4
+ data.tar.gz: 99fe0af08b0ad8f1a4c5bccf93edd2285e32f5966f6c3d93c2f152c239074ffe
5
5
  SHA512:
6
- metadata.gz: 1be59f83c847e48c36b0ac61527ca9dedc61b903763be6145d6dd26354575a74d2dee3fc807a804e6f847660347c8368a28d70f33df6cb57d75d2b952b98657f
7
- data.tar.gz: 0f37c9c304ef0264afeb363f07ca1b23c72045d9686869fe9325e336a79f30bc1930d08f97470a6235ad7d4dc8d1e5ebab4c78eb3de9d9ae1129ea42cb48dca8
6
+ metadata.gz: edafeb7cbf6461861bee821981911a5262d0dff09ec034cbb4cb2de123fe5a31a135e25685b7ba756931c665a908cf41e369b474aa33bb7a4beab7b99fcc17a3
7
+ data.tar.gz: f2edb449ac6b178e62392e809b677c103456e1ec6141e35b3dcac52b15c3254c5af0abe7e3165ba2b5f95137b87fcbcae1cf9dfc2cb0082afe3da795af470c3b
data/CHANGELOG.md CHANGED
@@ -7,6 +7,86 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.8.0] - 2026-09-10
11
+
12
+ Tracks **rcrewai 0.8.0**, which is backward compatible for this engine — the
13
+ 0.7.1 suite passed against it unchanged. This release is about surfacing the
14
+ new capabilities in Rails terms: durable **checkpointing** with resume, **LLM
15
+ interceptors**, and the **event hierarchy**, plus the four new providers.
16
+
17
+ ### Added
18
+ - **Checkpointing.** `RcrewAI::Rails::ActiveRecordCheckpointStore` persists
19
+ rcrewai crew checkpoints to the new `rcrewai_checkpoints` table, implementing
20
+ the gem's store contract (`save`/`load`/`list`/`delete`). Enable globally with
21
+ `config.checkpoint_enabled = true`, or per crew via the new
22
+ `checkpoint_enabled` column. Records are stored verbatim as JSON, so a record
23
+ written by any store implementation round-trips identically.
24
+ - **Resume.** `crew.resume_async(execution)` / `crew.resume_sync(execution)`
25
+ re-run a checkpointed execution, replaying tasks that already completed
26
+ instead of paying for them again. Accepts an `Execution` or a bare run id.
27
+ `crew.resumable_executions` lists what can be resumed.
28
+ - **Run lineage.** Executions record `run_id` and `parent_run_id`, so a resumed
29
+ run links back to the one it continues and `RCrewAI::Checkpoint.lineage`
30
+ walks the chain. `RcrewAI::Rails::Checkpoint` exposes `children`, `roots`,
31
+ `completed_task_names` and `failed_task_names`.
32
+ - **LLM interceptors.** `config.llm_before_request` and
33
+ `config.llm_after_response` attach rcrewai 0.8 hooks to every client the
34
+ engine builds. The gem forwards hooks only through `LLMClient.for_provider`,
35
+ and agents resolve their client via `LLMClient.resolve`, which does not — so
36
+ the engine registers them on the built client, covering per-agent LLMs too.
37
+ - **New providers.** `openai_compatible`, `bedrock`, `snowflake` and
38
+ `openai_responses` resolve through an agent's `llm_config` and are documented
39
+ in the generated initializer. (No engine change was required — provider
40
+ values pass through to the gem — but they are now covered by specs.)
41
+
42
+ ### Fixed
43
+ - **Crew inputs never reached the gem.** `CrewExecutionJob` persisted the
44
+ execution's `inputs` but called `rcrew.execute` without them, so
45
+ `before_kickoff` hooks and `Crew#last_inputs` always saw `{}` and no task
46
+ could interpolate an input. Inputs are now forwarded. Pre-existing, unrelated
47
+ to the upgrade.
48
+ - **Concurrent runs of the same agent shared one span stack.** The observation
49
+ collector keyed open spans by agent name, so when one agent ran twice
50
+ concurrently, one run's tool call could nest under the other run's iteration
51
+ (and its usage, text and errors could land on the wrong span). rcrewai 0.8
52
+ stamps every event with the id of its enclosing run span; the collector now
53
+ scopes its bookkeeping by that id. Events without one — older streams, and
54
+ the engine's own crew/agent spans — keep the previous behavior.
55
+
56
+ ### Changed
57
+ - Requires `rcrewai ~> 0.8.0`.
58
+ - `SpanStack.key_for` takes an optional second argument (the run id) and
59
+ `push`/`pop`/`current` take an optional `run_id:`. Existing calls without it
60
+ behave as before.
61
+
62
+ ### Upgrading
63
+ Existing installs need the new migration:
64
+
65
+ ```bash
66
+ $ rails rcrew_ai_rails:install:migrations
67
+ $ rails db:migrate
68
+ ```
69
+
70
+ This adds `rcrewai_checkpoints`, `rcrewai_crews.checkpoint_enabled`, and
71
+ `run_id`/`parent_run_id` on `rcrewai_executions`. Checkpointing is off by
72
+ default, so nothing changes until you enable it.
73
+
74
+ ## [0.7.1] - 2026-08-13
75
+
76
+ ### Fixed
77
+ - **`rails db:migrate` raised `NameError` on 0.7.0.** The engine registers an
78
+ `RcrewAI` acronym inflection (Zeitwerk needs it to resolve
79
+ `rcrewai/rails/span.rb` to `RcrewAI::Rails::Span`), so Rails camelizes
80
+ `create_rcrewai_tables` to `CreateRcrewAITables` — but the shipped migrations
81
+ declared `CreateRcrewaiTables`. Rails resolves a migration's class from its
82
+ filename, so following the documented install verbatim failed. Nine migrations
83
+ in `db/migrate` and the install template were affected; all now declare the
84
+ name Rails derives. Thanks to the user who reported this from a real install.
85
+
86
+ **If you already installed 0.7.0** and renamed the class by hand, no action is
87
+ needed — your migration works. Otherwise, delete the copied migration, upgrade,
88
+ and re-run the install (or `rcrew_ai_rails:install:migrations`).
89
+
10
90
  ## [0.7.0] - 2026-08-13
11
91
 
12
92
  ### Added
@@ -36,6 +116,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
36
116
  - `rcrewai >= 0.7.1` for agent-level tracing. On earlier 0.7.x versions traces
37
117
  contain only crew-level spans.
38
118
 
119
+ ### Upgrading
120
+ - **Existing installs need two new migrations.** Do not run the install
121
+ generator (it creates every table and will fail on duplicates). Instead run
122
+ `rails rcrew_ai_rails:install:migrations && rails db:migrate` to copy only
123
+ what is missing: `010_create_rcrewai_spans` (the trace tree) and
124
+ `011_add_observation_rollups_to_rcrewai_executions` (cost/token totals). Both
125
+ are additive — nothing existing is altered or dropped. Observation is enabled
126
+ by default once the tables exist; set `config.observation_enabled = false` to
127
+ upgrade without turning tracing on.
128
+
39
129
  ## [0.6.1] - 2026-07-07
40
130
 
41
131
  ### Fixed
@@ -152,7 +242,9 @@ existing agents, tasks, and crews build unchanged.
152
242
  ### Changed
153
243
  - Rename generators from `rcrew_a_i` to `rcrewai` namespacing.
154
244
 
155
- [Unreleased]: https://github.com/gkosmo/rcrewai-rails/compare/v0.7.0...HEAD
245
+ [Unreleased]: https://github.com/gkosmo/rcrewai-rails/compare/v0.8.0...HEAD
246
+ [0.8.0]: https://github.com/gkosmo/rcrewai-rails/compare/v0.7.1...v0.8.0
247
+ [0.7.1]: https://github.com/gkosmo/rcrewai-rails/compare/v0.7.0...v0.7.1
156
248
  [0.7.0]: https://github.com/gkosmo/rcrewai-rails/compare/v0.6.1...v0.7.0
157
249
  [0.6.1]: https://github.com/gkosmo/rcrewai-rails/compare/v0.6.0...v0.6.1
158
250
  [0.6.0]: https://github.com/gkosmo/rcrewai-rails/compare/v0.5.1...v0.6.0
data/README.md CHANGED
@@ -10,11 +10,12 @@ Rails engine for integrating [RcrewAI](https://github.com/gkosmo/rcrewai-rails)
10
10
  - **Web UI**: Monitor and manage crews through a built-in interface
11
11
  - **Rails-Specific Tools**: Pre-built tools for ActiveRecord, ActionMailer, Rails cache, and more
12
12
  - **Configuration**: Flexible configuration through Rails initializers
13
- - **Full rcrewai 0.7 feature coverage** (see [rcrewai 0.7 capabilities](#rcrewai-07-capabilities)):
13
+ - **Full rcrewai 0.8 feature coverage** (see [rcrewai 0.8 capabilities](#rcrewai-08-capabilities) and [rcrewai 0.7 capabilities](#rcrewai-07-capabilities)):
14
14
  - Agent config: reasoning, per-agent LLM, rate limiting, context-window trimming, cognitive memory
15
15
  - Task output: structured output schemas, guardrails, file output, multimodal attachments
16
16
  - Crew: `before_kickoff`/`after_kickoff` hooks, planning, the `consensual` process, batch execution
17
17
  - Knowledge (RAG) sources and Flow persistence
18
+ - Checkpointing with resume, LLM interceptors, and the Bedrock / Snowflake / OpenAI-compatible providers
18
19
 
19
20
  ## Observation Engine
20
21
 
@@ -86,6 +87,46 @@ This will:
86
87
  - Add an initializer file for configuration
87
88
  - Mount the engine routes in `config/routes.rb`
88
89
 
90
+ ### Upgrading an existing install
91
+
92
+ The install generator above is for **new** installs — it creates every table, so
93
+ running it against an app that already has the RcrewAI tables will fail on
94
+ duplicates.
95
+
96
+ If you are already running rcrewai-rails, pull in only the migrations you are
97
+ missing using the standard Rails engine task:
98
+
99
+ ```bash
100
+ $ rails rcrew_ai_rails:install:migrations
101
+ $ rails db:migrate
102
+ ```
103
+
104
+ (The task name comes from the engine's railtie name, `rcrew_ai_rails`.)
105
+
106
+ Rails copies only the migrations your app does not already have. Upgrading to
107
+ 0.8.0 from 0.7.x adds one:
108
+
109
+ | Migration | Purpose |
110
+ |---|---|
111
+ | `012_create_rcrewai_checkpoints` | `rcrewai_checkpoints`, `rcrewai_crews.checkpoint_enabled`, and `run_id`/`parent_run_id` on executions — checkpointing and resume |
112
+
113
+ Upgrading from 0.6.x adds two more:
114
+
115
+ | Migration | Purpose |
116
+ |---|---|
117
+ | `010_create_rcrewai_spans` | `rcrewai_spans` and `rcrewai_span_events` — the observation engine's trace tree |
118
+ | `011_add_observation_rollups_to_rcrewai_executions` | `total_cost_usd`, `total_tokens`, `span_count`, `error_count` on executions |
119
+
120
+ All are additive: no existing column or table is changed, and nothing is
121
+ dropped. Existing crews, agents, tasks, and executions are unaffected, and
122
+ observation is enabled by default once the tables exist. To upgrade the gem
123
+ without turning tracing on, set `config.observation_enabled = false` in
124
+ `config/initializers/rcrewai.rb` before migrating.
125
+
126
+ If your schema was created by hand (the install generator did not copy a
127
+ migration before 0.7.0, so this is likely), review the copied migrations before
128
+ running `db:migrate` and delete any whose tables you already have.
129
+
89
130
  ### Manual Routes Setup
90
131
 
91
132
  If you need to mount the routes manually, add this to your `config/routes.rb`:
@@ -235,10 +276,86 @@ crew.execute_sync(inputs)
235
276
  CrewExecutionJob.set(wait: 5.minutes).perform_later(crew, inputs)
236
277
  ```
237
278
 
279
+ ## rcrewai 0.8 capabilities
280
+
281
+ This engine tracks [rcrewai](https://github.com/gkosmo/rcrewAI) `~> 0.8`.
282
+
283
+ ### Checkpointing and resume
284
+
285
+ A crew run can record durable per-task state, so an interrupted run resumes
286
+ instead of re-executing (and re-paying for) the tasks that already finished.
287
+ Checkpoints are written to `rcrewai_checkpoints` after each task settles.
288
+
289
+ ```ruby
290
+ # Globally, in config/initializers/rcrewai.rb
291
+ config.checkpoint_enabled = true
292
+
293
+ # ...or per crew
294
+ crew.update!(checkpoint_enabled: true)
295
+
296
+ execution = crew.executions.order(:id).last
297
+ execution.run_id # => the checkpointed run
298
+
299
+ # Resume it: completed tasks are replayed, the rest execute.
300
+ crew.resume_sync(execution) # or resume_async(execution)
301
+ crew.resumable_executions # executions that recorded a run id
302
+ ```
303
+
304
+ A resumed run gets its own `run_id` and records the original in
305
+ `parent_run_id`, leaving the first run's record intact:
306
+
307
+ ```ruby
308
+ store = RcrewAI::Rails::ActiveRecordCheckpointStore.new
309
+ RCrewAI::Checkpoint.lineage(store, resumed.run_id)
310
+ # => ["<original run id>", "<resumed run id>"]
311
+ ```
312
+
313
+ Any object responding to `save`/`load`/`list`/`delete` can replace the store via
314
+ `config.checkpoint_store`.
315
+
316
+ ### LLM interceptors
317
+
318
+ Hooks that run around every LLM request the engine's agents make — useful for
319
+ logging, request tagging, or signing (AWS SigV4 for Bedrock, for instance).
320
+ Return a replacement value to modify the payload or result, or `nil` to leave it
321
+ untouched. A hook that raises is reported and skipped, so instrumentation can
322
+ never break a run.
323
+
324
+ ```ruby
325
+ config.llm_before_request = lambda do |payload, context|
326
+ Rails.logger.info("[llm] -> #{context[:provider]}/#{context[:model]}")
327
+ payload
328
+ end
329
+
330
+ config.llm_after_response = lambda do |result, context|
331
+ Rails.logger.info("[llm] <- #{context[:duration_ms]}ms")
332
+ result
333
+ end
334
+ ```
335
+
336
+ ### Providers
337
+
338
+ Alongside `openai`, `anthropic`, `google`, `azure` and `ollama`, rcrewai 0.8
339
+ adds four, set as an agent's `llm_config` provider (or
340
+ `config.default_llm_provider`):
341
+
342
+ | Provider | Notes |
343
+ |---|---|
344
+ | `openai_compatible` | Any OpenAI-format endpoint (Groq, Together, Fireworks, vLLM, OpenRouter, a self-hosted gateway). Requires RCrewAI's `base_url`. |
345
+ | `bedrock` | AWS Bedrock via the Converse API. Requires `aws_region`. |
346
+ | `snowflake` | Snowflake Cortex inference. Requires `snowflake_account`. |
347
+ | `openai_responses` | OpenAI's Responses API. Non-streaming only. |
348
+
349
+ ### Tracing accuracy
350
+
351
+ rcrewai 0.8 stamps every event with the id of its enclosing run span, and the
352
+ observation collector uses it to keep concurrent runs of the *same* agent apart.
353
+ Previously both runs shared one span stack, so one run's tool call could nest
354
+ under the other's iteration.
355
+
238
356
  ## rcrewai 0.7 capabilities
239
357
 
240
- This engine tracks [rcrewai](https://github.com/gkosmo/rcrewAI) `~> 0.7`. The
241
- following capabilities are configured through columns on the persisted models and
358
+ These capabilities are configured through columns on the persisted models and
242
359
  forwarded to the core objects at build time. All are **off/absent by default**, so
243
360
  existing records are unaffected — set only what you need.
244
361
 
@@ -342,6 +459,8 @@ The gem provides these ActiveRecord models:
342
459
  - `RcrewAI::Rails::KnowledgeSource` - Knowledge (RAG) sources, owned by an agent or a crew
343
460
  - `RcrewAI::Rails::FlowState` - Persisted rcrewai Flow state (resume flows across restarts)
344
461
  - `RcrewAI::Rails::FlowRun` - Flow-run tracking (status, inputs, result, timing)
462
+ - `RcrewAI::Rails::Span` / `SpanEvent` - The observation engine's trace tree
463
+ - `RcrewAI::Rails::Checkpoint` - Persisted crew checkpoints (resume and lineage)
345
464
 
346
465
  ## API Endpoints
347
466
 
@@ -5,7 +5,7 @@ module RcrewAI
5
5
 
6
6
  retry_on StandardError, wait: :exponentially_longer, attempts: 3
7
7
 
8
- def perform(crew, inputs = {}, batch_id: nil)
8
+ def perform(crew, inputs = {}, batch_id: nil, resume_run_id: nil)
9
9
  execution = crew.executions.create!(
10
10
  status: "pending",
11
11
  inputs: inputs,
@@ -22,7 +22,16 @@ module RcrewAI
22
22
  collector = collector_for(execution)
23
23
  collector&.start_crew_span(crew_name: crew.name)
24
24
 
25
- result = rcrew.execute(stream: collector)
25
+ store = checkpoint_store_for(crew)
26
+ result = if resume_run_id
27
+ execution.update!(parent_run_id: resume_run_id)
28
+ rcrew.resume(resume_run_id, checkpoint: store, stream: collector, inputs: inputs)
29
+ else
30
+ rcrew.execute(stream: collector, inputs: inputs, checkpoint: store)
31
+ end
32
+ # The gem assigns the run id during execute, so it can only be
33
+ # recorded once the run has opened.
34
+ execution.update!(run_id: rcrew.run_id) if rcrew.run_id
26
35
 
27
36
  # Close agent spans as successful before finish!, which treats
28
37
  # anything still open as an aborted run.
@@ -50,6 +59,19 @@ module RcrewAI
50
59
 
51
60
  private
52
61
 
62
+ # The checkpoint store for this run, or nil when checkpointing is off.
63
+ # A crew may opt in per-record; otherwise the engine default applies.
64
+ def checkpoint_store_for(crew)
65
+ enabled = if crew.respond_to?(:checkpoint_enabled) && !crew.checkpoint_enabled.nil?
66
+ crew.checkpoint_enabled
67
+ else
68
+ RcrewAI::Rails.config.checkpoint_enabled
69
+ end
70
+ return nil unless enabled
71
+
72
+ RcrewAI::Rails.config.checkpoint_store || ActiveRecordCheckpointStore.new
73
+ end
74
+
53
75
  # Translates rcrewai events into the span tree. Returns nil when
54
76
  # observation is disabled so no sink is attached at all.
55
77
  def collector_for(execution)
@@ -0,0 +1,53 @@
1
+ module RcrewAI
2
+ module Rails
3
+ # Persists rcrewai crew checkpoints to the DB. Implements the core
4
+ # checkpoint-store contract: save(id, record) / load(id) => record or nil /
5
+ # list => ids / delete(id). Pass an instance as +checkpoint:+ to
6
+ # RCrewAI::Crew#execute or #resume.
7
+ #
8
+ # Records are plain JSON-shaped hashes with string keys (the gem's own
9
+ # stores round-trip through JSON, and Crew reads them back with string
10
+ # keys), so they are stored verbatim rather than mapped onto columns.
11
+ # run_id/parent_run_id/crew_name are mirrored into columns for querying.
12
+ #
13
+ # Unlike the gem's FileStore, an id needs no path sanitizing here -- it is
14
+ # bound as a query parameter, never used to build a filesystem path.
15
+ class ActiveRecordCheckpointStore
16
+ def save(id, record)
17
+ checkpoint = Checkpoint.find_or_initialize_by(run_id: id.to_s)
18
+ checkpoint.data = record
19
+ checkpoint.parent_run_id = record["parent_run_id"] || record[:parent_run_id]
20
+ checkpoint.crew_name = record["crew"] || record[:crew]
21
+ checkpoint.checkpoint_updated_at = parse_time(record["updated_at"] || record[:updated_at])
22
+ checkpoint.save!
23
+ record
24
+ end
25
+
26
+ def load(id)
27
+ Checkpoint.find_by(run_id: id.to_s)&.data
28
+ end
29
+
30
+ def list
31
+ Checkpoint.order(:id).pluck(:run_id)
32
+ end
33
+
34
+ def delete(id)
35
+ Checkpoint.where(run_id: id.to_s).delete_all
36
+ nil
37
+ end
38
+
39
+ private
40
+
41
+ # The gem stamps an ISO8601 string. A malformed value must not take down
42
+ # the run -- the column is a convenience mirror, the authoritative copy
43
+ # lives in +data+.
44
+ def parse_time(value)
45
+ return nil if value.blank?
46
+
47
+ Time.zone ? Time.zone.parse(value.to_s) : Time.parse(value.to_s)
48
+ rescue ArgumentError, TypeError
49
+ nil
50
+ end
51
+ end
52
+ end
53
+ end
@@ -17,6 +17,10 @@ module RcrewAI
17
17
  scope :active, -> { where(active: true) }
18
18
 
19
19
  def to_rcrew_agent
20
+ Interceptors.apply_to_agent(build_rcrew_agent)
21
+ end
22
+
23
+ def build_rcrew_agent
20
24
  RCrewAI::Agent.new(
21
25
  name: name,
22
26
  role: role,
@@ -0,0 +1,37 @@
1
+ module RcrewAI
2
+ module Rails
3
+ # One persisted rcrewai checkpoint run record.
4
+ #
5
+ # The gem writes a plain JSON-shaped hash per run (see
6
+ # RCrewAI::Checkpoint.record_for); +data+ holds it verbatim so a record
7
+ # written by any store implementation round-trips identically. run_id and
8
+ # parent_run_id are also mirrored into columns so lineage can be queried in
9
+ # SQL rather than by deserializing every row.
10
+ class Checkpoint < ApplicationRecord
11
+ self.table_name = "rcrewai_checkpoints"
12
+
13
+ serialize :data, coder: JSON
14
+
15
+ validates :run_id, presence: true, uniqueness: true
16
+
17
+ scope :roots, -> { where(parent_run_id: nil) }
18
+
19
+ def children
20
+ self.class.where(parent_run_id: run_id)
21
+ end
22
+
23
+ # The per-task entries of the stored record, keyed by task name.
24
+ def tasks
25
+ (data || {})["tasks"] || {}
26
+ end
27
+
28
+ def completed_task_names
29
+ tasks.select { |_name, entry| entry["status"] == "completed" }.keys
30
+ end
31
+
32
+ def failed_task_names
33
+ tasks.select { |_name, entry| entry["status"] == "failed" }.keys
34
+ end
35
+ end
36
+ end
37
+ end
@@ -61,6 +61,22 @@ module RcrewAI
61
61
  CrewExecutionJob.perform_now(self, inputs)
62
62
  end
63
63
 
64
+ # Re-runs a checkpointed execution, replaying tasks that already
65
+ # completed instead of paying for them again. Accepts either a run id or
66
+ # an Execution that recorded one.
67
+ def resume_async(run, inputs = {})
68
+ CrewExecutionJob.perform_later(self, inputs, resume_run_id: run_id_for(run))
69
+ end
70
+
71
+ def resume_sync(run, inputs = {})
72
+ CrewExecutionJob.perform_now(self, inputs, resume_run_id: run_id_for(run))
73
+ end
74
+
75
+ # Executions of this crew that recorded a checkpoint run id, newest first.
76
+ def resumable_executions
77
+ executions.where.not(run_id: nil).order(created_at: :desc)
78
+ end
79
+
64
80
  def execute_batch_async(inputs_list)
65
81
  batch_id = SecureRandom.uuid
66
82
  normalize_batch_inputs(inputs_list).each do |inputs|
@@ -97,6 +113,15 @@ module RcrewAI
97
113
 
98
114
  private
99
115
 
116
+ # Accepts an Execution or a bare run id, so callers can pass whichever
117
+ # they have without unwrapping it themselves.
118
+ def run_id_for(run)
119
+ id = run.respond_to?(:run_id) ? run.run_id : run
120
+ raise ArgumentError, "no checkpoint run id to resume from" if id.blank?
121
+
122
+ id
123
+ end
124
+
100
125
  # Wraps a single inputs hash into a one-element array; leaves an array of
101
126
  # hashes as-is. Avoids Array()'s hash-destructuring (Array({a:1}) => [[:a,1]]).
102
127
  def normalize_batch_inputs(inputs_list)
@@ -1,4 +1,4 @@
1
- class AddConfigToRcrewaiAgents < ActiveRecord::Migration[7.0]
1
+ class AddConfigToRcrewAIAgents < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  # max_rpm and llm_config already exist on rcrewai_agents from the
4
4
  # original create table; only the 0.5.0 additions are new.
@@ -1,4 +1,4 @@
1
- class AddOutputProcessingToRcrewaiTasks < ActiveRecord::Migration[7.0]
1
+ class AddOutputProcessingToRcrewAITasks < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  # output_file already exists on rcrewai_tasks from the original create
4
4
  # table; only the new 0.4/0.5 output-processing options are added here.
@@ -1,4 +1,4 @@
1
- class AddLifecycleToRcrewaiCrews < ActiveRecord::Migration[7.0]
1
+ class AddLifecycleToRcrewAICrews < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  add_column :rcrewai_crews, :planning, :boolean, default: false, null: false
4
4
  add_column :rcrewai_crews, :planning_llm, :string
@@ -1,4 +1,4 @@
1
- class AddBatchIdToRcrewaiExecutions < ActiveRecord::Migration[7.0]
1
+ class AddBatchIdToRcrewAIExecutions < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  add_column :rcrewai_executions, :batch_id, :string
4
4
  add_index :rcrewai_executions, :batch_id
@@ -1,4 +1,4 @@
1
- class CreateRcrewaiKnowledgeSources < ActiveRecord::Migration[7.0]
1
+ class CreateRcrewAIKnowledgeSources < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  create_table :rcrewai_knowledge_sources do |t|
4
4
  t.references :owner, polymorphic: true, null: false
@@ -1,4 +1,4 @@
1
- class CreateRcrewaiFlows < ActiveRecord::Migration[7.0]
1
+ class CreateRcrewAIFlows < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  create_table :rcrewai_flow_states do |t|
4
4
  t.string :state_id, null: false
@@ -1,4 +1,4 @@
1
- class AddConsensusAgentsToRcrewaiCrews < ActiveRecord::Migration[7.0]
1
+ class AddConsensusAgentsToRcrewAICrews < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  add_column :rcrewai_crews, :consensus_agents, :integer
4
4
  end
@@ -1,4 +1,4 @@
1
- class CreateRcrewaiSpans < ActiveRecord::Migration[7.0]
1
+ class CreateRcrewAISpans < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  create_table :rcrewai_spans do |t|
4
4
  t.references :execution, null: false, foreign_key: { to_table: :rcrewai_executions }
@@ -1,4 +1,4 @@
1
- class AddObservationRollupsToRcrewaiExecutions < ActiveRecord::Migration[7.0]
1
+ class AddObservationRollupsToRcrewAIExecutions < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  add_column :rcrewai_executions, :total_cost_usd, :decimal, precision: 12, scale: 6
4
4
  add_column :rcrewai_executions, :total_tokens, :integer
@@ -0,0 +1,22 @@
1
+ class CreateRcrewAICheckpoints < ActiveRecord::Migration[7.0]
2
+ def change
3
+ create_table :rcrewai_checkpoints do |t|
4
+ t.string :run_id, null: false
5
+ t.string :parent_run_id
6
+ t.string :crew_name
7
+ t.text :data, null: false
8
+ t.datetime :checkpoint_updated_at
9
+
10
+ t.timestamps
11
+ end
12
+
13
+ add_index :rcrewai_checkpoints, :run_id, unique: true
14
+ add_index :rcrewai_checkpoints, :parent_run_id
15
+
16
+ add_column :rcrewai_crews, :checkpoint_enabled, :boolean
17
+
18
+ add_column :rcrewai_executions, :run_id, :string
19
+ add_column :rcrewai_executions, :parent_run_id, :string
20
+ add_index :rcrewai_executions, :run_id
21
+ end
22
+ end
@@ -1,4 +1,4 @@
1
- class CreateRcrewaiTables < ActiveRecord::Migration[7.0]
1
+ class CreateRcrewAITables < ActiveRecord::Migration[7.0]
2
2
  def change
3
3
  create_table :rcrewai_crews do |t|
4
4
  t.string :name, null: false
@@ -16,6 +16,7 @@ class CreateRcrewaiTables < ActiveRecord::Migration[7.0]
16
16
  t.string :after_kickoff_class
17
17
  t.string :after_kickoff_method
18
18
  t.text :config
19
+ t.boolean :checkpoint_enabled
19
20
  t.boolean :active, default: true
20
21
 
21
22
  t.timestamps
@@ -112,6 +113,8 @@ class CreateRcrewaiTables < ActiveRecord::Migration[7.0]
112
113
  t.integer :total_tokens
113
114
  t.integer :span_count, default: 0, null: false
114
115
  t.integer :error_count, default: 0, null: false
116
+ t.string :run_id
117
+ t.string :parent_run_id
115
118
 
116
119
  t.timestamps
117
120
  end
@@ -119,6 +122,7 @@ class CreateRcrewaiTables < ActiveRecord::Migration[7.0]
119
122
  add_index :rcrewai_executions, :status
120
123
  add_index :rcrewai_executions, :created_at
121
124
  add_index :rcrewai_executions, :batch_id
125
+ add_index :rcrewai_executions, :run_id
122
126
 
123
127
  create_table :rcrewai_execution_logs do |t|
124
128
  t.references :execution, null: false, foreign_key: { to_table: :rcrewai_executions }
@@ -204,6 +208,19 @@ class CreateRcrewaiTables < ActiveRecord::Migration[7.0]
204
208
  add_index :rcrewai_spans, :status
205
209
  add_index :rcrewai_spans, %i[execution_id sequence]
206
210
 
211
+ create_table :rcrewai_checkpoints do |t|
212
+ t.string :run_id, null: false
213
+ t.string :parent_run_id
214
+ t.string :crew_name
215
+ t.text :data, null: false
216
+ t.datetime :checkpoint_updated_at
217
+
218
+ t.timestamps
219
+ end
220
+
221
+ add_index :rcrewai_checkpoints, :run_id, unique: true
222
+ add_index :rcrewai_checkpoints, :parent_run_id
223
+
207
224
  create_table :rcrewai_span_events do |t|
208
225
  t.references :span, null: false, foreign_key: { to_table: :rcrewai_spans }
209
226
  t.string :level, null: false, default: "info"
@@ -16,7 +16,13 @@ RcrewAI::Rails.configure do |config|
16
16
  config.persistence_backend = :active_record
17
17
 
18
18
  # Default LLM provider
19
- # Options: "openai", "anthropic", "cohere", "groq", etc.
19
+ # Options: "openai", "anthropic", "google", "azure", "ollama", and (rcrewai
20
+ # 0.8+) "openai_compatible", "bedrock", "snowflake", "openai_responses".
21
+ # openai_compatible - any OpenAI-format endpoint (Groq, Together,
22
+ # Fireworks, vLLM, OpenRouter); needs RCrewAI's
23
+ # base_url set.
24
+ # bedrock - AWS Bedrock Converse API; needs aws_region.
25
+ # snowflake - Snowflake Cortex; needs snowflake_account.
20
26
  config.default_llm_provider = ENV.fetch("RCREWAI_LLM_PROVIDER", "openai")
21
27
 
22
28
  # Default LLM model
@@ -67,4 +73,32 @@ RcrewAI.configure do |config|
67
73
  # Configure other RcrewAI settings
68
74
  # config.default_model = "gpt-4"
69
75
  # config.temperature = 0.7
70
- end
76
+
77
+ # --- Checkpointing (rcrewai 0.8+) -----------------------------------------
78
+ # Records durable per-task state during a crew run so an interrupted run can
79
+ # be resumed instead of re-executing (and re-paying for) completed tasks.
80
+ # Off by default; a crew record can also opt in individually via its
81
+ # checkpoint_enabled column.
82
+ # config.checkpoint_enabled = true
83
+
84
+ # Where checkpoints are stored. Defaults to the bundled ActiveRecord store
85
+ # (the rcrewai_checkpoints table). Any object responding to
86
+ # save/load/list/delete works.
87
+ # config.checkpoint_store = RcrewAI::Rails::ActiveRecordCheckpointStore.new
88
+
89
+ # --- LLM interceptors (rcrewai 0.8+) --------------------------------------
90
+ # Hooks run around every LLM request the engine's agents make. Each receives
91
+ # (payload, context) / (result, context); return a replacement to modify it,
92
+ # or nil to leave it untouched. A hook that raises is reported and skipped,
93
+ # so instrumentation can never break a run.
94
+ #
95
+ # config.llm_before_request = lambda do |payload, context|
96
+ # Rails.logger.info("[llm] -> #{context[:provider]}/#{context[:model]}")
97
+ # payload
98
+ # end
99
+ #
100
+ # config.llm_after_response = lambda do |result, context|
101
+ # Rails.logger.info("[llm] <- #{context[:duration_ms]}ms")
102
+ # result
103
+ # end
104
+ end
@@ -76,6 +76,10 @@ module RcrewAI
76
76
  protected
77
77
 
78
78
  def build_agent
79
+ RcrewAI::Rails::Interceptors.apply_to_agent(new_rcrew_agent)
80
+ end
81
+
82
+ def new_rcrew_agent
79
83
  RCrewAI::Agent.new(
80
84
  name: @attributes[:name] || default_agent_name,
81
85
  role: @attributes[:role] || self.class.agent_role,
@@ -7,7 +7,9 @@ module RcrewAI
7
7
  :default_memory_embedder, :default_memory_store,
8
8
  :observation_enabled, :observation_capture_prompts,
9
9
  :observation_prompt_max_bytes, :observation_flush_mode,
10
- :observation_flush_every, :observation_retention_days
10
+ :observation_flush_every, :observation_retention_days,
11
+ :checkpoint_enabled, :checkpoint_store,
12
+ :llm_before_request, :llm_after_response
11
13
 
12
14
  def initialize
13
15
  @job_queue_name = "default"
@@ -28,6 +30,16 @@ module RcrewAI
28
30
  @observation_flush_mode = :batched # :batched | :immediate
29
31
  @observation_flush_every = 25
30
32
  @observation_retention_days = 30
33
+ # Checkpointing (rcrewai 0.8+). Off by default: it writes a row per
34
+ # task settlement, which an app should opt into rather than inherit.
35
+ @checkpoint_enabled = false
36
+ # Defaults to ActiveRecordCheckpointStore when checkpointing is on.
37
+ # Set to any object responding to save/load/list/delete to override.
38
+ @checkpoint_store = nil
39
+ # LLM interceptor hooks (rcrewai 0.8+). Each is a callable, or an
40
+ # array of callables, applied to every client the engine builds.
41
+ @llm_before_request = nil
42
+ @llm_after_response = nil
31
43
  end
32
44
  end
33
45
  end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RcrewAI
4
+ module Rails
5
+ # Applies the configured LLM interceptor hooks (rcrewai 0.8+) to a client.
6
+ #
7
+ # The gem forwards +before_request:+/+after_response:+ only through
8
+ # LLMClient.for_provider. Agents build their client via LLMClient.resolve,
9
+ # which does not, so hooks configured on the engine would never reach a
10
+ # per-agent client. Registering them on the built client instead covers
11
+ # every path uniformly.
12
+ module Interceptors
13
+ module_function
14
+
15
+ # Registers the configured hooks on +client+ and returns it.
16
+ #
17
+ # A client the host supplied ready-made (anything not speaking the hook
18
+ # API, e.g. a bare double or a custom object responding only to #chat)
19
+ # is returned untouched rather than raising -- the gem accepts any
20
+ # object responding to #chat as an llm, and instrumentation must never
21
+ # be the reason a run fails.
22
+ def apply(client, config = RcrewAI::Rails.config)
23
+ return client unless client
24
+
25
+ Array(config.llm_before_request).each do |hook|
26
+ client.before_request(hook) if client.respond_to?(:before_request)
27
+ end
28
+
29
+ Array(config.llm_after_response).each do |hook|
30
+ client.after_response(hook) if client.respond_to?(:after_response)
31
+ end
32
+
33
+ client
34
+ end
35
+
36
+ # True when any interceptor is configured, so callers can skip the work
37
+ # entirely in the common case where none are.
38
+ def configured?(config = RcrewAI::Rails.config)
39
+ Array(config.llm_before_request).any? || Array(config.llm_after_response).any?
40
+ end
41
+
42
+ # Registers the hooks on an already-built agent's client.
43
+ def apply_to_agent(agent, config = RcrewAI::Rails.config)
44
+ return agent unless configured?(config)
45
+ return agent unless agent.respond_to?(:llm_client)
46
+
47
+ apply(agent.llm_client, config)
48
+ agent
49
+ end
50
+ end
51
+ end
52
+ end
@@ -106,11 +106,12 @@ module RcrewAI
106
106
  end
107
107
 
108
108
  def on_iteration_start(event)
109
+ run = SpanStack.run_id_for(event)
109
110
  id = open_span(
110
111
  kind: "llm_call", name: "iteration #{event.iteration_index}",
111
112
  parent_span_id: agent_span_for(event.agent), agent: event.agent
112
113
  )
113
- @stack.push(agent: event.agent, key: :iteration, id: id)
114
+ @stack.push(agent: event.agent, key: :iteration, id: id, run_id: run)
114
115
  end
115
116
 
116
117
  # Returns the agent span that events from +agent+ belong under,
@@ -126,7 +127,7 @@ module RcrewAI
126
127
  end
127
128
 
128
129
  def on_iteration_end(event)
129
- id = @stack.pop(agent: event.agent, key: :iteration)
130
+ id = @stack.pop(agent: event.agent, key: :iteration, run_id: SpanStack.run_id_for(event))
130
131
  return unless id
131
132
 
132
133
  merge_attributes(id, "finish_reason" => event.finish_reason.to_s)
@@ -136,7 +137,8 @@ module RcrewAI
136
137
  def on_tool_start(event)
137
138
  id = open_span(
138
139
  kind: "tool_call", name: event.tool.to_s,
139
- parent_span_id: @stack.current(agent: event.agent), agent: event.agent,
140
+ parent_span_id: @stack.current(agent: event.agent, run_id: SpanStack.run_id_for(event)),
141
+ agent: event.agent,
140
142
  attributes: { "args" => event.args }
141
143
  )
142
144
  @stack.register_call(call_id: event.call_id, span_id: id)
@@ -160,7 +162,7 @@ module RcrewAI
160
162
  end
161
163
 
162
164
  def on_usage(event)
163
- id = @stack.current(agent: event.agent)
165
+ id = @stack.current(agent: event.agent, run_id: SpanStack.run_id_for(event))
164
166
  return unless id
165
167
 
166
168
  @writer.update_span(id,
@@ -176,16 +178,16 @@ module RcrewAI
176
178
  def on_text_delta(event)
177
179
  return if config.observation_capture_prompts == :none
178
180
 
179
- @text_buffers[SpanStack.key_for(event.agent)] << event.text.to_s
181
+ @text_buffers[SpanStack.key_for(event.agent, SpanStack.run_id_for(event))] << event.text.to_s
180
182
  end
181
183
 
182
184
  # Prefers the event's own text, falling back to the accumulated
183
185
  # deltas when the provider sends TextDone without a payload.
184
186
  def on_text_done(event)
185
- buffered = @text_buffers.delete(SpanStack.key_for(event.agent))
187
+ buffered = @text_buffers.delete(SpanStack.key_for(event.agent, SpanStack.run_id_for(event)))
186
188
  return if config.observation_capture_prompts == :none
187
189
 
188
- id = @stack.current(agent: event.agent)
190
+ id = @stack.current(agent: event.agent, run_id: SpanStack.run_id_for(event))
189
191
  return unless id
190
192
 
191
193
  text = event.text.to_s
@@ -196,14 +198,14 @@ module RcrewAI
196
198
  def on_thinking(event)
197
199
  return if config.observation_capture_prompts == :none
198
200
 
199
- id = @stack.current(agent: event.agent)
201
+ id = @stack.current(agent: event.agent, run_id: SpanStack.run_id_for(event))
200
202
  return unless id
201
203
 
202
204
  merge_attributes(id, "thinking" => truncate(event.text.to_s))
203
205
  end
204
206
 
205
207
  def on_error(event)
206
- id = @stack.current(agent: event.agent)
208
+ id = @stack.current(agent: event.agent, run_id: SpanStack.run_id_for(event))
207
209
  return unless id
208
210
 
209
211
  merge_attributes(id, "error" => event.error.to_s)
@@ -23,17 +23,34 @@ module RcrewAI
23
23
 
24
24
  # Normalizes an agent key. Blank/nil agents share a single reserved
25
25
  # bucket that cannot collide with a real agent name.
26
- def self.key_for(agent)
26
+ def self.key_for(agent, run_id = nil)
27
27
  key = agent.to_s
28
- key.empty? ? UNTAGGED : key
28
+ key = UNTAGGED if key.empty?
29
+ # rcrewai 0.8+ stamps every event with the id of the enclosing run
30
+ # span. Scoping the stack by it keeps two concurrent runs of the
31
+ # *same* agent apart -- keyed on name alone they share one stack, so
32
+ # one run's tool call nests under the other's iteration. Events from
33
+ # older streams (and the engine's own crew/agent spans) carry no run
34
+ # id and keep the previous name-only key.
35
+ run = run_id.to_s
36
+ run.empty? ? key : "#{key}\u0000#{run}"
37
+ end
38
+
39
+ # The run-span id an event belongs to, or nil for events that predate
40
+ # the 0.8 event hierarchy or are emitted outside a runner span.
41
+ def self.run_id_for(event)
42
+ return nil unless event.respond_to?(:parent_id)
43
+
44
+ id = event.parent_id
45
+ id.nil? || id.to_s.empty? ? nil : id
29
46
  end
30
47
 
31
48
  def next_sequence
32
49
  @mutex.synchronize { @sequence += 1 }
33
50
  end
34
51
 
35
- def push(agent:, key:, id:)
36
- @mutex.synchronize { @stacks[self.class.key_for(agent)] << { key: key, id: id } }
52
+ def push(agent:, key:, id:, run_id: nil)
53
+ @mutex.synchronize { @stacks[self.class.key_for(agent, run_id)] << { key: key, id: id } }
37
54
  id
38
55
  end
39
56
 
@@ -41,9 +58,9 @@ module RcrewAI
41
58
  #
42
59
  # Reads use +fetch+ rather than +[]+: the default block auto-vivifies
43
60
  # on read, so querying unknown agents would retain a key forever.
44
- def pop(agent:, key:)
61
+ def pop(agent:, key:, run_id: nil)
45
62
  @mutex.synchronize do
46
- stack = @stacks.fetch(self.class.key_for(agent), nil)
63
+ stack = @stacks.fetch(self.class.key_for(agent, run_id), nil)
47
64
  next nil if stack.nil?
48
65
 
49
66
  index = stack.rindex { |frame| frame[:key] == key }
@@ -53,8 +70,8 @@ module RcrewAI
53
70
  end
54
71
  end
55
72
 
56
- def current(agent:)
57
- @mutex.synchronize { @stacks.fetch(self.class.key_for(agent), nil)&.last&.fetch(:id) }
73
+ def current(agent:, run_id: nil)
74
+ @mutex.synchronize { @stacks.fetch(self.class.key_for(agent, run_id), nil)&.last&.fetch(:id) }
58
75
  end
59
76
 
60
77
  def register_call(call_id:, span_id:)
@@ -1,6 +1,6 @@
1
1
  module RcrewAI
2
2
  module Rails
3
- VERSION = "0.7.0"
3
+ VERSION = "0.8.0"
4
4
  Version = VERSION
5
5
  end
6
6
  end
data/lib/rcrewai/rails.rb CHANGED
@@ -8,6 +8,7 @@ require "stimulus-rails"
8
8
  require_relative "rails/version"
9
9
  require_relative "rails/engine"
10
10
  require_relative "rails/configuration"
11
+ require_relative "rails/interceptors"
11
12
  require_relative "rails/crew_builder"
12
13
  require_relative "rails/agent_builder"
13
14
  require_relative "rails/observation/span_stack"
@@ -45,7 +45,7 @@ Gem::Specification.new do |spec|
45
45
  spec.require_paths = ["lib"]
46
46
 
47
47
  # Core dependency
48
- spec.add_dependency "rcrewai", "~> 0.7.1"
48
+ spec.add_dependency "rcrewai", "~> 0.8.0"
49
49
 
50
50
  # Rails dependencies
51
51
  spec.add_dependency "rails", ">= 7.0.0"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rcrewai-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.0
4
+ version: 0.8.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - gkosmo
@@ -15,14 +15,14 @@ dependencies:
15
15
  requirements:
16
16
  - - "~>"
17
17
  - !ruby/object:Gem::Version
18
- version: 0.7.1
18
+ version: 0.8.0
19
19
  type: :runtime
20
20
  prerelease: false
21
21
  version_requirements: !ruby/object:Gem::Requirement
22
22
  requirements:
23
23
  - - "~>"
24
24
  - !ruby/object:Gem::Version
25
- version: 0.7.1
25
+ version: 0.8.0
26
26
  - !ruby/object:Gem::Dependency
27
27
  name: rails
28
28
  requirement: !ruby/object:Gem::Requirement
@@ -210,9 +210,11 @@ files:
210
210
  - app/controllers/rcrewai/rails/tools_controller.rb
211
211
  - app/jobs/rcrewai/rails/crew_execution_job.rb
212
212
  - app/jobs/rcrewai/rails/task_execution_job.rb
213
+ - app/models/rcrewai/rails/active_record_checkpoint_store.rb
213
214
  - app/models/rcrewai/rails/active_record_state_store.rb
214
215
  - app/models/rcrewai/rails/agent.rb
215
216
  - app/models/rcrewai/rails/application_record.rb
217
+ - app/models/rcrewai/rails/checkpoint.rb
216
218
  - app/models/rcrewai/rails/crew.rb
217
219
  - app/models/rcrewai/rails/execution.rb
218
220
  - app/models/rcrewai/rails/execution_log.rb
@@ -259,6 +261,7 @@ files:
259
261
  - db/migrate/009_add_memory_config_to_agents_and_clean_crews.rb
260
262
  - db/migrate/010_create_rcrewai_spans.rb
261
263
  - db/migrate/011_add_observation_rollups_to_rcrewai_executions.rb
264
+ - db/migrate/012_create_rcrewai_checkpoints.rb
262
265
  - docs/api.html
263
266
  - docs/capabilities.html
264
267
  - docs/examples.html
@@ -275,6 +278,7 @@ files:
275
278
  - lib/rcrewai/rails/configuration.rb
276
279
  - lib/rcrewai/rails/crew_builder.rb
277
280
  - lib/rcrewai/rails/engine.rb
281
+ - lib/rcrewai/rails/interceptors.rb
278
282
  - lib/rcrewai/rails/observation/collector.rb
279
283
  - lib/rcrewai/rails/observation/pruner.rb
280
284
  - lib/rcrewai/rails/observation/rollup.rb