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.
- checksums.yaml +4 -4
- data/.rdoc_options +5 -0
- data/CHANGELOG.md +14 -0
- data/README.md +2 -1
- data/Rakefile +5 -0
- data/docs/durable-workflows.md +2 -2
- data/docs/getting-started.md +1 -1
- data/docs/loops.md +2 -1
- data/docs/rails.md +1 -1
- data/docs/workflows.md +2 -2
- data/lib/generators/nexo/artifacts/artifacts_generator.rb +9 -7
- data/lib/generators/nexo/artifacts/templates/add_artifacts_to_nexo_workflow_runs.rb +1 -1
- data/lib/generators/nexo/install/install_generator.rb +7 -5
- data/lib/generators/nexo/skill/skill_generator.rb +10 -8
- data/lib/generators/nexo/state/state_generator.rb +9 -7
- data/lib/generators/nexo/state/templates/add_state_to_nexo_workflow_runs.rb +1 -1
- data/lib/generators/nexo/workflows/templates/create_nexo_workflow_runs.rb +3 -3
- data/lib/generators/nexo/workflows/workflows_generator.rb +7 -5
- data/lib/nexo/agent.rb +145 -133
- data/lib/nexo/concurrent.rb +17 -15
- data/lib/nexo/configuration.rb +21 -21
- data/lib/nexo/engine.rb +1 -1
- data/lib/nexo/loop.rb +6 -6
- data/lib/nexo/loops/agent_sdk.rb +11 -11
- data/lib/nexo/loops/ruby_llm.rb +10 -10
- data/lib/nexo/mcp/gated_tool.rb +16 -16
- data/lib/nexo/mcp.rb +37 -37
- data/lib/nexo/output_truncator.rb +9 -9
- data/lib/nexo/permissions.rb +44 -44
- data/lib/nexo/read_tracker.rb +4 -4
- data/lib/nexo/run_store.rb +30 -28
- data/lib/nexo/sandbox.rb +29 -27
- data/lib/nexo/sandboxes/container.rb +75 -75
- data/lib/nexo/sandboxes/local.rb +15 -15
- data/lib/nexo/sandboxes/remote.rb +25 -23
- data/lib/nexo/sandboxes/virtual.rb +2 -2
- data/lib/nexo/sandboxes.rb +9 -9
- data/lib/nexo/session.rb +31 -29
- data/lib/nexo/skills.rb +30 -28
- data/lib/nexo/tools/fetch.rb +20 -20
- data/lib/nexo/tools/glob.rb +5 -5
- data/lib/nexo/tools/read_file.rb +7 -7
- data/lib/nexo/tools/shell.rb +6 -6
- data/lib/nexo/tools/web_search.rb +15 -15
- data/lib/nexo/tools/write_file.rb +9 -9
- data/lib/nexo/turbo_broadcaster.rb +1 -1
- data/lib/nexo/version.rb +2 -2
- data/lib/nexo/workflow.rb +197 -188
- data/lib/nexo/workflow_job.rb +6 -6
- data/lib/nexo/workflow_run.rb +10 -10
- data/lib/nexo.rb +26 -22
- metadata +4 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 6739e4be2c75c08ffb553133324b96b29d13e0212e21cc23c4cd6287316b3249
|
|
4
|
+
data.tar.gz: d051ce3a0916cd5fb9328df9b893e5aad15b6f60c8d5c23bb9be8b3126da7285
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 94c1df2b9d56ab1c31aacd79a52473bf6ed3da9ba43d2e7403658fdee0e52bc31bded23181dbe7f60e82b4fd9b0f5e64c5d933de80cf99f546ca0982b883d878
|
|
7
|
+
data.tar.gz: ca8829f2003854bd1c24e2e77739b0b4c62bd173ee0bd31297171ba017f2c1a02dd31f6fb9bb7ef938fc1c67a93229c407f5fbae06b29c4aee305552f6eba242
|
data/.rdoc_options
ADDED
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)
|
|
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`
|
data/docs/durable-workflows.md
CHANGED
|
@@ -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`](
|
|
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`](
|
|
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
|
data/docs/getting-started.md
CHANGED
|
@@ -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`](
|
|
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**
|
|
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`](
|
|
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`](
|
|
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`](
|
|
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
|
|
8
|
+
# Adds the `artifacts` column to an already-installed `nexo_workflow_runs`
|
|
9
9
|
# table (Spec 7):
|
|
10
10
|
#
|
|
11
|
-
#
|
|
11
|
+
# ```sh
|
|
12
|
+
# rails g nexo:artifacts
|
|
13
|
+
# ```
|
|
12
14
|
#
|
|
13
|
-
# copies a timestamped, additive migration adding a
|
|
14
|
-
# (default
|
|
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
|
-
#
|
|
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
|
|
31
|
-
#
|
|
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
|
|
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 (
|
|
7
|
-
#
|
|
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
|
-
#
|
|
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
|
|
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
|
-
#
|
|
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
|
-
#
|
|
10
|
+
# ```sh
|
|
11
|
+
# rails g nexo:skill triage
|
|
12
|
+
# ```
|
|
11
13
|
#
|
|
12
|
-
# creates
|
|
14
|
+
# creates `app/skills/triage/SKILL.md` (valid frontmatter + placeholder
|
|
13
15
|
# process steps, per agentskills.io/specification) and a kept
|
|
14
|
-
#
|
|
15
|
-
# cite. Reference it from an agent with the
|
|
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
|
|
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
|
-
#
|
|
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
|
|
24
|
-
# directory and a valid
|
|
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
|
|
8
|
+
# Adds the `state` column to an already-installed `nexo_workflow_runs` table
|
|
9
9
|
# (Spec 13):
|
|
10
10
|
#
|
|
11
|
-
#
|
|
11
|
+
# ```sh
|
|
12
|
+
# rails g nexo:state
|
|
13
|
+
# ```
|
|
12
14
|
#
|
|
13
|
-
# copies a timestamped, additive migration adding a
|
|
14
|
-
# (default
|
|
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
|
|
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
|
|
31
|
-
#
|
|
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
|
|
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
|
|
8
|
-
#
|
|
9
|
-
#
|
|
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
|
-
#
|
|
10
|
+
# ```sh
|
|
11
|
+
# rails g nexo:workflows
|
|
12
|
+
# ```
|
|
11
13
|
#
|
|
12
|
-
# copies a timestamped migration that creates the
|
|
13
|
-
# table, after which
|
|
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
|
|
28
|
-
# migration into
|
|
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
|