nexo_ai 0.11.0 → 0.12.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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/.rdoc_options +5 -0
  3. data/CHANGELOG.md +14 -0
  4. data/README.md +2 -1
  5. data/Rakefile +5 -0
  6. data/docs/durable-workflows.md +2 -2
  7. data/docs/getting-started.md +1 -1
  8. data/docs/loops.md +2 -1
  9. data/docs/rails.md +1 -1
  10. data/docs/workflows.md +2 -2
  11. data/lib/generators/nexo/artifacts/artifacts_generator.rb +9 -7
  12. data/lib/generators/nexo/artifacts/templates/add_artifacts_to_nexo_workflow_runs.rb +1 -1
  13. data/lib/generators/nexo/install/install_generator.rb +7 -5
  14. data/lib/generators/nexo/skill/skill_generator.rb +10 -8
  15. data/lib/generators/nexo/state/state_generator.rb +9 -7
  16. data/lib/generators/nexo/state/templates/add_state_to_nexo_workflow_runs.rb +1 -1
  17. data/lib/generators/nexo/workflows/templates/create_nexo_workflow_runs.rb +3 -3
  18. data/lib/generators/nexo/workflows/workflows_generator.rb +7 -5
  19. data/lib/nexo/agent.rb +145 -133
  20. data/lib/nexo/concurrent.rb +17 -15
  21. data/lib/nexo/configuration.rb +21 -21
  22. data/lib/nexo/engine.rb +1 -1
  23. data/lib/nexo/loop.rb +6 -6
  24. data/lib/nexo/loops/agent_sdk.rb +11 -11
  25. data/lib/nexo/loops/ruby_llm.rb +10 -10
  26. data/lib/nexo/mcp/gated_tool.rb +16 -16
  27. data/lib/nexo/mcp.rb +37 -37
  28. data/lib/nexo/output_truncator.rb +9 -9
  29. data/lib/nexo/permissions.rb +44 -44
  30. data/lib/nexo/read_tracker.rb +4 -4
  31. data/lib/nexo/run_store.rb +30 -28
  32. data/lib/nexo/sandbox.rb +29 -27
  33. data/lib/nexo/sandboxes/container.rb +75 -75
  34. data/lib/nexo/sandboxes/local.rb +15 -15
  35. data/lib/nexo/sandboxes/remote.rb +25 -23
  36. data/lib/nexo/sandboxes/virtual.rb +2 -2
  37. data/lib/nexo/sandboxes.rb +9 -9
  38. data/lib/nexo/session.rb +31 -29
  39. data/lib/nexo/skills.rb +30 -28
  40. data/lib/nexo/tools/fetch.rb +20 -20
  41. data/lib/nexo/tools/glob.rb +5 -5
  42. data/lib/nexo/tools/read_file.rb +7 -7
  43. data/lib/nexo/tools/shell.rb +6 -6
  44. data/lib/nexo/tools/web_search.rb +15 -15
  45. data/lib/nexo/tools/write_file.rb +9 -9
  46. data/lib/nexo/turbo_broadcaster.rb +1 -1
  47. data/lib/nexo/version.rb +2 -2
  48. data/lib/nexo/workflow.rb +197 -188
  49. data/lib/nexo/workflow_job.rb +6 -6
  50. data/lib/nexo/workflow_run.rb +10 -10
  51. data/lib/nexo.rb +26 -22
  52. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2a372c4ac5c253a76cbf930d99b134bbdc62c596dc8bde4414da26f08c99ad39
4
- data.tar.gz: 8329003fd163b8b4131f4608b18ecb5604717b7dd6995699328216cb22143320
3
+ metadata.gz: 6739e4be2c75c08ffb553133324b96b29d13e0212e21cc23c4cd6287316b3249
4
+ data.tar.gz: d051ce3a0916cd5fb9328df9b893e5aad15b6f60c8d5c23bb9be8b3126da7285
5
5
  SHA512:
6
- metadata.gz: 3e3de8747793d6c7b052d9272f6e23345e33d0749e66cd17a4247429eec1999f1329830c1c21841ac399bc04743f2d6595126496d8a24d340b537ad5ee4de026
7
- data.tar.gz: 271699e28b6c236f618250b06f75ed8c4fc5d0dec269d24d0f4bac145366e1cedc0f6fee37926117b9fbf2446a8d4d7b54054f2c73562f3d6a1bfd6ae41e21d5
6
+ metadata.gz: 94c1df2b9d56ab1c31aacd79a52473bf6ed3da9ba43d2e7403658fdee0e52bc31bded23181dbe7f60e82b4fd9b0f5e64c5d933de80cf99f546ca0982b883d878
7
+ data.tar.gz: ca8829f2003854bd1c24e2e77739b0b4c62bd173ee0bd31297171ba017f2c1a02dd31f6fb9bb7ef938fc1c67a93229c407f5fbae06b29c4aee305552f6eba242
data/.rdoc_options ADDED
@@ -0,0 +1,5 @@
1
+ # RDoc configuration for `rdoc`, `gem rdoc`, and rubydoc.info. All comments in
2
+ # lib/ and the docs/ guides are written in Markdown, not RDoc markup.
3
+ markup: markdown
4
+ main_page: README.md
5
+ title: nexo_ai
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.12.0] - 2026-09-12
4
+
5
+ The gem now says exactly which ruby_llm it supports, and its API docs are written in Markdown.
6
+
7
+ ### Changed
8
+
9
+ - **`ruby_llm` is now pinned to the 1.16 line (`~> 1.16`, was `>= 1.16`).** The supported
10
+ version is 1.16.0 — every composed API was verified against it. The upcoming ruby_llm 2.0
11
+ is not supported yet, and the pessimistic constraint keeps it from being resolved in.
12
+ - **API documentation is written in Markdown.** RDoc now runs with `markup: markdown`
13
+ (`.rdoc_options` + the `rake doc` task), and every doc comment under `lib/` was converted
14
+ from RDoc markup — backtick code spans, `##` headings, fenced code samples. Guides link the
15
+ `examples/` files by GitHub URL so the links work on rubydoc.info as well as on GitHub.
16
+
3
17
  ## [0.11.0] - 2026-08-20
4
18
 
5
19
  An agent's tool schema now tells the truth about what it may do.
data/README.md CHANGED
@@ -94,7 +94,8 @@ access until you explicitly opt in.
94
94
  ## Requirements
95
95
 
96
96
  - Ruby 3.3+
97
- - [ruby_llm](https://github.com/crmne/ruby_llm) >= 1.16
97
+ - [ruby_llm](https://github.com/crmne/ruby_llm) 1.16.x (`~> 1.16`) — the supported version is
98
+ **1.16.0**; the upcoming ruby_llm 2.0 is not supported yet
98
99
  - [ruby_llm-skills](https://github.com/kieranklaassen/ruby_llm-skills) — optional, only
99
100
  when you use the `skills` macro
100
101
  - [ruby_llm-mcp](https://github.com/patvice/ruby_llm-mcp) — optional, only when you attach
data/Rakefile CHANGED
@@ -31,7 +31,12 @@ RDoc::Task.new(:doc) do |rd|
31
31
  # are not part of the gem's Ruby API, so they belong in neither the generated
32
32
  # API site nor the coverage denominator.
33
33
  rd.rdoc_files.exclude("lib/generators/**/templates/*.rb")
34
+ # Local planning notes (gitignored) that happen to live under docs/.
35
+ rd.rdoc_files.exclude("docs/SITE_DOCS_PLAN.md")
34
36
  rd.rdoc_dir = "doc"
37
+ # Every comment in lib/ (and the docs/ guides) is Markdown, not RDoc markup.
38
+ # Mirrors `.rdoc_options`, which `gem rdoc`/rubydoc.info read instead of this task.
39
+ rd.markup = "markdown"
35
40
  end
36
41
 
37
42
  # NOTE: `default` intentionally stays test + standard — `rake doc`/`doc:coverage`
@@ -61,7 +61,7 @@ run:
61
61
  DocumentApproval.resume_later(run.id, { approved: true }, queue: :nexo)
62
62
  ```
63
63
 
64
- See [`examples/approval_workflow.rb`](../examples/approval_workflow.rb) for the full
64
+ See [`examples/approval_workflow.rb`](https://github.com/maquina-app/nexo/blob/main/examples/approval_workflow.rb) for the full
65
65
  offline flow (`ruby -Ilib examples/approval_workflow.rb`).
66
66
 
67
67
  ## Parallel checkpoints — `checkpoint_all`
@@ -187,7 +187,7 @@ resumed.status # => "done" (the gate allowed the
187
187
  `ruby_llm` swallows tool exceptions, tool-triggered approval would be constrained — a
188
188
  genuine upstream dependency, stated plainly.
189
189
 
190
- See [`examples/approval_agent.rb`](../examples/approval_agent.rb) for the live flow
190
+ See [`examples/approval_agent.rb`](https://github.com/maquina-app/nexo/blob/main/examples/approval_agent.rb) for the live flow
191
191
  (`NEXO_LIVE=1 NEXO_MODEL=… ruby -Ilib examples/approval_agent.rb`).
192
192
 
193
193
  The `state` column ships with fresh installs. Apps installed before this feature
@@ -97,7 +97,7 @@ Both are class macros with the same reader/writer convention as `model`. `provid
97
97
  is passed straight through to `RubyLLM.chat`; `assume_model_exists` defaults to
98
98
  `false` (registry validation on). Setting `assume_model_exists` **without** a
99
99
  `provider` raises `Nexo::ConfigurationError` — `ruby_llm` can't infer a provider once
100
- the lookup is skipped. See [`examples/code_reviewer.rb`](../examples/code_reviewer.rb)
100
+ the lookup is skipped. See [`examples/code_reviewer.rb`](https://github.com/maquina-app/nexo/blob/main/examples/code_reviewer.rb)
101
101
  for a runnable Ollama example.
102
102
 
103
103
  ← Back to the [README](../README.md)
data/docs/loops.md CHANGED
@@ -63,7 +63,8 @@ proven.
63
63
 
64
64
  ## Verified vs assumed
65
65
 
66
- Built against **`ruby_llm` 1.16** and **`ruby_llm-test` 0.2**. The tool body method is
66
+ Built against **`ruby_llm` 1.16.0** (the supported line — the gemspec pins `~> 1.16`, and the
67
+ upcoming `ruby_llm` 2.0 is not supported yet) and **`ruby_llm-test` 0.2**. The tool body method is
67
68
  `#execute`, tools attach with `chat.with_tools(*instances)`, and instructions set with
68
69
  `chat.with_instructions`. `Open3.capture3` has no `timeout:` keyword on the target Ruby, so
69
70
  `Local#shell` bounds the command with `Timeout.timeout`. These may differ on other
data/docs/rails.md CHANGED
@@ -156,7 +156,7 @@ run.artifact("digest.md") # => {"name" =>, "content" =>, "at" =>} or ni
156
156
  run.artifact_content("digest.md") # => "…the body…" or nil
157
157
  ```
158
158
 
159
- See [`examples/rails_usage.md`](../examples/rails_usage.md) for a controller +
159
+ See [`examples/rails_usage.md`](https://github.com/maquina-app/nexo/blob/main/examples/rails_usage.md) for a controller +
160
160
  Turbo-page walkthrough.
161
161
 
162
162
  ← Back to the [README](../README.md)
data/docs/workflows.md CHANGED
@@ -176,7 +176,7 @@ artifact("digest.md", from: "app/templates/digest.md.erb",
176
176
  > or uploaded template is remote code execution. If a body is untrusted, pass it
177
177
  > as `content:` (inert data), not as a `from:` template.
178
178
 
179
- See [`examples/artifact_from_template.rb`](../examples/artifact_from_template.rb) for
179
+ See [`examples/artifact_from_template.rb`](https://github.com/maquina-app/nexo/blob/main/examples/artifact_from_template.rb) for
180
180
  the full offline flow (`ruby -Ilib examples/artifact_from_template.rb`).
181
181
  ### Agent output — `produces`
182
182
 
@@ -307,7 +307,7 @@ end
307
307
  > *how* (skills/instructions). Driving an agent never widens its authority — its
308
308
  > safe default (`:read_only`) is untouched.
309
309
 
310
- See [`examples/inbox_digest_task.rb`](../examples/inbox_digest_task.rb) for a live
310
+ See [`examples/inbox_digest_task.rb`](https://github.com/maquina-app/nexo/blob/main/examples/inbox_digest_task.rb) for a live
311
311
  example that wraps the MCP-backed `InboxTriage` agent in a workflow and captures
312
312
  the digest as an artifact.
313
313
 
@@ -5,15 +5,17 @@ require "rails/generators/migration"
5
5
 
6
6
  module Nexo
7
7
  module Generators
8
- # Adds the +artifacts+ column to an already-installed +nexo_workflow_runs+
8
+ # Adds the `artifacts` column to an already-installed `nexo_workflow_runs`
9
9
  # table (Spec 7):
10
10
  #
11
- # rails g nexo:artifacts
11
+ # ```sh
12
+ # rails g nexo:artifacts
13
+ # ```
12
14
  #
13
- # copies a timestamped, additive migration adding a +json+ +artifacts+ column
14
- # (default +[]+), after which +rails db:migrate+ lets Nexo::Workflow runs
15
+ # copies a timestamped, additive migration adding a `json` `artifacts` column
16
+ # (default `[]`), after which `rails db:migrate` lets Nexo::Workflow runs
15
17
  # record named artifacts. Fresh installs get the column from
16
- # +nexo:workflows+ directly; this generator is for apps installed before
18
+ # `nexo:workflows` directly; this generator is for apps installed before
17
19
  # Spec 7. Modeled on WorkflowsGenerator.
18
20
  class ArtifactsGenerator < Rails::Generators::Base
19
21
  include Rails::Generators::Migration
@@ -27,8 +29,8 @@ module Nexo
27
29
  ::ActiveRecord::Migration.next_migration_number(next_migration_number)
28
30
  end
29
31
 
30
- # Generation step: copy the timestamped +artifacts+-column migration into
31
- # +db/migrate+.
32
+ # Generation step: copy the timestamped `artifacts`-column migration into
33
+ # `db/migrate`.
32
34
  def create_migration_file
33
35
  migration_template "add_artifacts_to_nexo_workflow_runs.rb", "db/migrate/add_artifacts_to_nexo_workflow_runs.rb"
34
36
  end
@@ -4,7 +4,7 @@
4
4
  # column from create_nexo_workflow_runs directly). Portable json column, default
5
5
  # [], matching the events column shape.
6
6
  class AddArtifactsToNexoWorkflowRuns < ActiveRecord::Migration[8.0]
7
- # Adds the +artifacts+ json column (default +[]+) to +nexo_workflow_runs+.
7
+ # Adds the `artifacts` json column (default `[]`) to `nexo_workflow_runs`.
8
8
  def change
9
9
  add_column :nexo_workflow_runs, :artifacts, :json, null: false, default: []
10
10
  end
@@ -3,13 +3,15 @@
3
3
  require "rails/generators"
4
4
 
5
5
  module Nexo
6
- # Namespace for Nexo's Rails generators (+rails g nexo:install+,
7
- # +nexo:workflows+, +nexo:artifacts+, +nexo:state+, +nexo:skill+). Each is
6
+ # Namespace for Nexo's Rails generators (`rails g nexo:install`,
7
+ # `nexo:workflows`, `nexo:artifacts`, `nexo:state`, `nexo:skill`). Each is
8
8
  # Rails-coupled and never autoloaded by the plain-Ruby core.
9
9
  module Generators
10
10
  # Sets up the conventional Nexo layout in a host Rails app:
11
11
  #
12
- # rails g nexo:install
12
+ # ```sh
13
+ # rails g nexo:install
14
+ # ```
13
15
  #
14
16
  # creates the app/agents, app/workflows and app/skills directories (each
15
17
  # with a committable .keep) and a provider-neutral config/initializers/nexo.rb.
@@ -17,7 +19,7 @@ module Nexo
17
19
  source_root File.expand_path("templates", __dir__)
18
20
 
19
21
  # Generation step: create the app/agents, app/workflows, and app/skills
20
- # directories, each with a committable +.keep+.
22
+ # directories, each with a committable `.keep`.
21
23
  def create_directories
22
24
  %w[app/agents app/workflows app/skills].each do |dir|
23
25
  empty_directory dir
@@ -26,7 +28,7 @@ module Nexo
26
28
  end
27
29
 
28
30
  # Generation step: copy the provider-neutral
29
- # +config/initializers/nexo.rb+ into the host app.
31
+ # `config/initializers/nexo.rb` into the host app.
30
32
  def copy_initializer
31
33
  copy_file "nexo.rb", "config/initializers/nexo.rb"
32
34
  end
@@ -7,21 +7,23 @@ module Nexo
7
7
  module Generators
8
8
  # Scaffolds an Agent Skills package in a host Rails app:
9
9
  #
10
- # rails g nexo:skill triage
10
+ # ```sh
11
+ # rails g nexo:skill triage
12
+ # ```
11
13
  #
12
- # creates +app/skills/triage/SKILL.md+ (valid frontmatter + placeholder
14
+ # creates `app/skills/triage/SKILL.md` (valid frontmatter + placeholder
13
15
  # process steps, per agentskills.io/specification) and a kept
14
- # +app/skills/triage/references/+ directory for supporting docs the skill can
15
- # cite. Reference it from an agent with the +skills :triage+ macro.
16
+ # `app/skills/triage/references/` directory for supporting docs the skill can
17
+ # cite. Reference it from an agent with the `skills :triage` macro.
16
18
  #
17
- # Rails-coupled, like Nexo's other generators: it requires +rails/generators+
19
+ # Rails-coupled, like Nexo's other generators: it requires `rails/generators`
18
20
  # at load time and is never autoloaded by the plain-Ruby core, so
19
- # +require "nexo"+ with no Rails present neither defines nor fails on it.
21
+ # `require "nexo"` with no Rails present neither defines nor fails on it.
20
22
  class SkillGenerator < Rails::Generators::NamedBase
21
23
  source_root File.expand_path("templates", __dir__)
22
24
 
23
- # Generation step: scaffold +app/skills/<name>/+ with a kept +references/+
24
- # directory and a valid +SKILL.md+ rendered from the template.
25
+ # Generation step: scaffold `app/skills/<name>/` with a kept `references/`
26
+ # directory and a valid `SKILL.md` rendered from the template.
25
27
  def create_skill_package
26
28
  empty_directory File.join(skill_root, "references")
27
29
  # An empty references/ would not survive git; .keep keeps it tracked.
@@ -5,15 +5,17 @@ require "rails/generators/migration"
5
5
 
6
6
  module Nexo
7
7
  module Generators
8
- # Adds the +state+ column to an already-installed +nexo_workflow_runs+ table
8
+ # Adds the `state` column to an already-installed `nexo_workflow_runs` table
9
9
  # (Spec 13):
10
10
  #
11
- # rails g nexo:state
11
+ # ```sh
12
+ # rails g nexo:state
13
+ # ```
12
14
  #
13
- # copies a timestamped, additive migration adding a +json+ +state+ column
14
- # (default +{}+), after which +rails db:migrate+ lets Nexo::Workflow runs
15
+ # copies a timestamped, additive migration adding a `json` `state` column
16
+ # (default `{}`), after which `rails db:migrate` lets Nexo::Workflow runs
15
17
  # store checkpoint results and suspend metadata (durable suspend/resume).
16
- # Fresh installs get the column from +nexo:workflows+ directly; this generator
18
+ # Fresh installs get the column from `nexo:workflows` directly; this generator
17
19
  # is for apps installed before Spec 13. Modeled on ArtifactsGenerator.
18
20
  class StateGenerator < Rails::Generators::Base
19
21
  include Rails::Generators::Migration
@@ -27,8 +29,8 @@ module Nexo
27
29
  ::ActiveRecord::Migration.next_migration_number(next_migration_number)
28
30
  end
29
31
 
30
- # Generation step: copy the timestamped +state+-column migration into
31
- # +db/migrate+.
32
+ # Generation step: copy the timestamped `state`-column migration into
33
+ # `db/migrate`.
32
34
  def create_migration_file
33
35
  migration_template "add_state_to_nexo_workflow_runs.rb", "db/migrate/add_state_to_nexo_workflow_runs.rb"
34
36
  end
@@ -6,7 +6,7 @@
6
6
  # "__suspend__" suspend metadata. The migration version is resolved from the host
7
7
  # app's ActiveRecord rather than hardcoded, so it tracks whatever Rails is installed.
8
8
  class AddStateToNexoWorkflowRuns < ActiveRecord::Migration[ActiveRecord::Migration.current_version]
9
- # Adds the +state+ json object column (default +{}+) to +nexo_workflow_runs+.
9
+ # Adds the `state` json object column (default `{}`) to `nexo_workflow_runs`.
10
10
  def change
11
11
  add_column :nexo_workflow_runs, :state, :json, null: false, default: {}
12
12
  end
@@ -4,9 +4,9 @@
4
4
  # SQLite and PostgreSQL without an adapter-aware path. The primary key is the
5
5
  # UUID string id assigned by Nexo::WorkflowRun before_create.
6
6
  class CreateNexoWorkflowRuns < ActiveRecord::Migration[8.0]
7
- # Creates the +nexo_workflow_runs+ table (UUID string primary key, portable
8
- # +json+ columns for payload/result/events/artifacts/state) plus indexes on
9
- # +workflow_class+ and +status+.
7
+ # Creates the `nexo_workflow_runs` table (UUID string primary key, portable
8
+ # `json` columns for payload/result/events/artifacts/state) plus indexes on
9
+ # `workflow_class` and `status`.
10
10
  def change
11
11
  create_table :nexo_workflow_runs, id: false do |t|
12
12
  t.string :id, null: false, primary_key: true
@@ -7,10 +7,12 @@ module Nexo
7
7
  module Generators
8
8
  # Installs the WorkflowRun persistence schema into a host Rails app:
9
9
  #
10
- # rails g nexo:workflows
10
+ # ```sh
11
+ # rails g nexo:workflows
12
+ # ```
11
13
  #
12
- # copies a timestamped migration that creates the +nexo_workflow_runs+
13
- # table, after which +rails db:migrate+ makes Nexo::Workflow runs persist
14
+ # copies a timestamped migration that creates the `nexo_workflow_runs`
15
+ # table, after which `rails db:migrate` makes Nexo::Workflow runs persist
14
16
  # to ActiveRecord instead of the in-memory store.
15
17
  class WorkflowsGenerator < Rails::Generators::Base
16
18
  include Rails::Generators::Migration
@@ -24,8 +26,8 @@ module Nexo
24
26
  ::ActiveRecord::Migration.next_migration_number(next_migration_number)
25
27
  end
26
28
 
27
- # Generation step: copy the timestamped +create_nexo_workflow_runs+
28
- # migration into +db/migrate+.
29
+ # Generation step: copy the timestamped `create_nexo_workflow_runs`
30
+ # migration into `db/migrate`.
29
31
  def create_migration_file
30
32
  migration_template "create_nexo_workflow_runs.rb", "db/migrate/create_nexo_workflow_runs.rb"
31
33
  end