nexo_ai 0.10.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 (55) hide show
  1. checksums.yaml +4 -4
  2. data/.rdoc_options +5 -0
  3. data/CHANGELOG.md +48 -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/permissions.md +17 -2
  10. data/docs/rails.md +1 -1
  11. data/docs/sandboxes.md +13 -4
  12. data/docs/tools.md +16 -6
  13. data/docs/workflows.md +2 -2
  14. data/lib/generators/nexo/artifacts/artifacts_generator.rb +9 -7
  15. data/lib/generators/nexo/artifacts/templates/add_artifacts_to_nexo_workflow_runs.rb +1 -1
  16. data/lib/generators/nexo/install/install_generator.rb +7 -5
  17. data/lib/generators/nexo/skill/skill_generator.rb +10 -8
  18. data/lib/generators/nexo/state/state_generator.rb +9 -7
  19. data/lib/generators/nexo/state/templates/add_state_to_nexo_workflow_runs.rb +1 -1
  20. data/lib/generators/nexo/workflows/templates/create_nexo_workflow_runs.rb +3 -3
  21. data/lib/generators/nexo/workflows/workflows_generator.rb +7 -5
  22. data/lib/nexo/agent.rb +163 -141
  23. data/lib/nexo/concurrent.rb +17 -15
  24. data/lib/nexo/configuration.rb +21 -21
  25. data/lib/nexo/engine.rb +1 -1
  26. data/lib/nexo/loop.rb +6 -6
  27. data/lib/nexo/loops/agent_sdk.rb +11 -11
  28. data/lib/nexo/loops/ruby_llm.rb +10 -10
  29. data/lib/nexo/mcp/gated_tool.rb +16 -16
  30. data/lib/nexo/mcp.rb +37 -37
  31. data/lib/nexo/output_truncator.rb +9 -9
  32. data/lib/nexo/permissions.rb +64 -41
  33. data/lib/nexo/read_tracker.rb +4 -4
  34. data/lib/nexo/run_store.rb +30 -28
  35. data/lib/nexo/sandbox.rb +29 -27
  36. data/lib/nexo/sandboxes/container.rb +75 -75
  37. data/lib/nexo/sandboxes/local.rb +15 -15
  38. data/lib/nexo/sandboxes/remote.rb +25 -23
  39. data/lib/nexo/sandboxes/virtual.rb +2 -2
  40. data/lib/nexo/sandboxes.rb +9 -9
  41. data/lib/nexo/session.rb +31 -29
  42. data/lib/nexo/skills.rb +30 -28
  43. data/lib/nexo/tools/fetch.rb +20 -20
  44. data/lib/nexo/tools/glob.rb +5 -5
  45. data/lib/nexo/tools/read_file.rb +7 -7
  46. data/lib/nexo/tools/shell.rb +6 -6
  47. data/lib/nexo/tools/web_search.rb +15 -15
  48. data/lib/nexo/tools/write_file.rb +9 -9
  49. data/lib/nexo/turbo_broadcaster.rb +1 -1
  50. data/lib/nexo/version.rb +2 -2
  51. data/lib/nexo/workflow.rb +197 -188
  52. data/lib/nexo/workflow_job.rb +6 -6
  53. data/lib/nexo/workflow_run.rb +10 -10
  54. data/lib/nexo.rb +26 -22
  55. metadata +4 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8815820e94035d1d5c68defa4e862111c95572852098b735e885eaf98b6a3407
4
- data.tar.gz: 57ca3d98c0d1acd6877276e9322715a7e003928b2da258866b1e7ccabcf33b29
3
+ metadata.gz: 6739e4be2c75c08ffb553133324b96b29d13e0212e21cc23c4cd6287316b3249
4
+ data.tar.gz: d051ce3a0916cd5fb9328df9b893e5aad15b6f60c8d5c23bb9be8b3126da7285
5
5
  SHA512:
6
- metadata.gz: 8ad1a55220bcddd2ea1e2d06ea2bc2c680ae4f2a52c1cd8f24e0942f66ad38e38c6b1dc992fcc766b720400083bb07b500feaae9285fbb671e203bfa3f46e0f4
7
- data.tar.gz: cea3ce8167dae3b4e44a5d87c84e42f04a333c7cd7e5aae8ad236133b821ba10fd3688e63851035d2c1b494b9c533807fa29bfbfd1c3439ed8c8ce6473fe1e52
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,53 @@
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
+
17
+ ## [0.11.0] - 2026-08-20
18
+
19
+ An agent's tool schema now tells the truth about what it may do.
20
+
21
+ ### Changed
22
+
23
+ - **An agent no longer advertises a tool its permission mode can never authorize.**
24
+ Tool attach was gated on the sandbox (`Sandbox#supports?`) but not on the gate, so a
25
+ `:read_only` agent — the default — put `WriteFile` and `Shell` in its schema on every
26
+ turn even though `#authorize!` was guaranteed to deny them. The same held for `Fetch`
27
+ and `WebSearch`, which were gated on `fetch_allow` / `search_backend` being declared
28
+ but not on the capability being permitted. Models do try these tools, and each attempt
29
+ costs a full round trip.
30
+
31
+ `Agent#chat`, `#apply_fetch` and `#apply_search` now also consult
32
+ `Permissions#never_allows?`. Only `:read_only` is decidable ahead of time; `:auto`,
33
+ `:ask` and `:approve` decide per call and still attach — `:approve` in particular must
34
+ reach the gate so it can raise `ApprovalRequired` and suspend the run.
35
+
36
+ This is a cost and description-accuracy measure, **not** a security change:
37
+ `#authorize!` remains the boundary and still denies at call time.
38
+
39
+ **Upgrading:** an agent that is supposed to write, shell out, fetch or search needs the
40
+ capability in `allow:` (or a non-`:read_only` mode). If it does not have it today the
41
+ tool was already failing on every call — the tool now disappears from the schema instead
42
+ of erroring. Note that `fetch_allow` scopes hosts and `search_backend` names a backend;
43
+ neither is a capability grant.
44
+
45
+ ### Added
46
+
47
+ - `Nexo::Permissions#never_allows?(capability)` — true when a capability can never be
48
+ authorized for this gate, for any call. Derived from the new `Permissions::PRIVILEGED`
49
+ constant, which `#authorize!` also reads, so the predicate cannot drift from the gate.
50
+
3
51
  ## [0.10.0] - 2026-08-20
4
52
 
5
53
  Durable workflows without a database.
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/permissions.md CHANGED
@@ -8,7 +8,7 @@ and the agent loop continues — it does not raise. A path that escapes the work
8
8
 
9
9
  | | `:read` | `:glob` | `:write` | `:shell` | `:fetch` | `:search` |
10
10
  | ------------------------ | ------- | ------- | ------------------- | --------------------------------- | ---------- | ---------- |
11
- | `:read_only` (default) | ✅ | ✅ | ❌ `{error}` | ❌ `{error}` | ❌ `{error}` | ❌ `{error}` |
11
+ | `:read_only` (default) | ✅ | ✅ | ❌ not attached ‡ | ❌ not attached ‡ | ❌ not attached ‡ | ❌ not attached ‡ |
12
12
  | `:auto` | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
13
13
  | `:ask` | ✅ | ✅ | per `on_ask` | per `on_ask` | per `on_ask` | per `on_ask` |
14
14
  | `:approve` | ✅ | ✅ | per `decision` | per `decision` | per `decision` | per `decision` |
@@ -18,7 +18,22 @@ and the agent loop continues — it does not raise. A path that escapes the work
18
18
 
19
19
  `:read`/`:glob` are auto-allowed under **every** mode (they sit in the default
20
20
  `allow` list), so `:ask`/`:approve` never prompt for them — only
21
- `:write`/`:shell`/`:fetch`/`:search` reach the gate. **†** `:fetch` and `:search`
21
+ `:write`/`:shell`/`:fetch`/`:search` reach the gate.
22
+
23
+ **‡** Under `:read_only` these four capabilities can *never* be authorized, so their tools are
24
+ not attached at all — the model never sees `WriteFile`, `Shell`, `Fetch` or `WebSearch` in its
25
+ schema. Anything named in `allow:` is exempt and attaches normally, and `:auto`/`:ask`/`:approve`
26
+ always attach because they decide per call. Leaving a guaranteed failure in the schema is not
27
+ free: a model that reads the schema tries the tool, and each attempt costs a full round trip.
28
+ `Permissions#never_allows?` is the predicate, and it is derived from the same `PRIVILEGED` list
29
+ `#authorize!` uses so the two cannot disagree. This is a cost and description-accuracy measure,
30
+ **not** a security boundary — `#authorize!` is still the gate and still denies at call time.
31
+
32
+ Note that declaring `fetch_allow` or a `search_backend` is *not* a capability grant: `fetch_allow`
33
+ scopes which hosts are reachable, and both still need `:fetch` / `:search` permitted before the
34
+ tool is attached.
35
+
36
+ **†** `:fetch` and `:search`
22
37
  run in the **host process** (stdlib `net/http` / a host-injected backend), so **no
23
38
  sandbox constrains them** — not even a `--network none` container. They are bounded
24
39
  only by the capability gate above plus `fetch_allow` / the injected backend.
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/sandboxes.md CHANGED
@@ -54,10 +54,19 @@ scope; none widens authority silently.
54
54
  instructions → sandbox instructions → skill instructions**. Provider-neutral, injected through
55
55
  the existing `with_instructions` path.
56
56
 
57
- - **Capability-gated tool attach (`Sandbox#supports?`).** A `:virtual` agent no longer advertises
58
- a `Shell` tool it can never run — `Agent#chat` attaches `Shell` only when
59
- `@sandbox.supports?(:shell)`. `Local`/`Container` support all four capabilities; `Virtual`
60
- supports everything but `:shell`. `ReadFile`/`WriteFile`/`Glob` are always attached.
57
+ - **Gated tool attach, on two axes.** An agent does not advertise a tool it could never
58
+ successfully call, because a guaranteed failure in the schema costs a round trip every time a
59
+ model tries it. `Agent#chat` attaches a tool only when **both** hold:
60
+
61
+ 1. *The sandbox supports the capability* (`Sandbox#supports?`). `Local`/`Container` support all
62
+ four; `Virtual` supports everything but `:shell`, so a `:virtual` agent has no `Shell`.
63
+ 2. *The permission gate does not deny it statically* (`Permissions#never_allows?`). Under
64
+ `:read_only`, `:write` and `:shell` can never be authorized unless listed in `allow:`, so
65
+ neither `WriteFile` nor `Shell` is attached. `:auto`, `:ask` and `:approve` decide per call
66
+ and always attach — `:approve` in particular *must* reach the gate so it can suspend the run.
67
+
68
+ `ReadFile` and `Glob` are always attached. This is a cost and description-accuracy measure, not
69
+ a security boundary: `Permissions#authorize!` remains the gate and still denies at call time.
61
70
 
62
71
  - **Shell output truncation (`Nexo::OutputTruncator`).** Unbounded command output (`npm install`,
63
72
  `git log`) is truncated before it reaches the model, so a single command can't blow a small
data/docs/tools.md CHANGED
@@ -1,12 +1,22 @@
1
1
  # Tools
2
2
  Nexo attaches four sandbox-backed tools — `ReadFile`, `WriteFile`, `Shell`, and `Glob` — each gated by the [sandbox](sandboxes.md) and [permission](permissions.md) seams.
3
3
 
4
- Which tools attach depends on what the sandbox supports:
5
-
6
- - **Capability-gated tool attach (`Sandbox#supports?`).** A `:virtual` agent no longer advertises
7
- a `Shell` tool it can never run — `Agent#chat` attaches `Shell` only when
8
- `@sandbox.supports?(:shell)`. `Local`/`Container` support all four capabilities; `Virtual`
9
- supports everything but `:shell`. `ReadFile`/`WriteFile`/`Glob` are always attached.
4
+ Which tools attach depends on what the sandbox supports *and* on what the permission mode could
5
+ ever allow:
6
+
7
+ - **Gated tool attach, on two axes.** An agent does not advertise a tool it could never
8
+ successfully call, because a guaranteed failure in the schema costs a round trip every time a
9
+ model tries it. `Agent#chat` attaches a tool only when **both** hold:
10
+
11
+ 1. *The sandbox supports the capability* (`Sandbox#supports?`). `Local`/`Container` support all
12
+ four; `Virtual` supports everything but `:shell`, so a `:virtual` agent has no `Shell`.
13
+ 2. *The permission gate does not deny it statically* (`Permissions#never_allows?`). Under
14
+ `:read_only`, `:write` and `:shell` can never be authorized unless listed in `allow:`, so
15
+ neither `WriteFile` nor `Shell` is attached. `:auto`, `:ask` and `:approve` decide per call
16
+ and always attach — `:approve` in particular *must* reach the gate so it can suspend the run.
17
+
18
+ `ReadFile` and `Glob` are always attached. This is a cost and description-accuracy measure, not
19
+ a security boundary: `Permissions#authorize!` remains the gate and still denies at call time.
10
20
 
11
21
  `Shell` truncates unbounded command output before it reaches the model:
12
22
 
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