ruby_llm-ag_ui 0.1.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 +7 -0
- data/CHANGELOG.md +48 -0
- data/CONTRIBUTING.md +56 -0
- data/LICENSE.txt +21 -0
- data/README.md +146 -0
- data/SECURITY.md +30 -0
- data/docs/_advanced/rails-integration.md +199 -0
- data/docs/_advanced/security-and-deployment.md +204 -0
- data/docs/_core_features/approval-and-resume.md +62 -0
- data/docs/_core_features/input-validation.md +64 -0
- data/docs/_core_features/reconnects.md +42 -0
- data/docs/_core_features/streaming-runs.md +49 -0
- data/docs/_getting_started/configuration.md +57 -0
- data/docs/_getting_started/getting-started.md +52 -0
- data/docs/_getting_started/overview.md +189 -0
- data/docs/_getting_started/whats-new-in-0-1.md +41 -0
- data/docs/_reference/api-reference.md +26 -0
- data/docs/_reference/compatibility.md +130 -0
- data/docs/_reference/protocol-coverage.md +175 -0
- data/docs/_reference/release-history.md +22 -0
- data/docs/_reference/releasing.md +54 -0
- data/docs/_reference/third-party-notices.md +82 -0
- data/docs/_reference/upgrading.md +42 -0
- data/docs/index.md +53 -0
- data/lib/generators/ruby_llm/ag_ui/install_generator.rb +37 -0
- data/lib/generators/ruby_llm/ag_ui/templates/create_ruby_llm_ag_ui_runs.rb.tt +24 -0
- data/lib/generators/ruby_llm/ag_ui/templates/initializer.rb.tt +24 -0
- data/lib/ruby_llm/ag_ui/canonical.rb +40 -0
- data/lib/ruby_llm/ag_ui/configuration.rb +113 -0
- data/lib/ruby_llm/ag_ui/coverage.rb +163 -0
- data/lib/ruby_llm/ag_ui/emitter.rb +119 -0
- data/lib/ruby_llm/ag_ui/error.rb +69 -0
- data/lib/ruby_llm/ag_ui/error_sanitizer.rb +69 -0
- data/lib/ruby_llm/ag_ui/input.rb +299 -0
- data/lib/ruby_llm/ag_ui/interrupt.rb +207 -0
- data/lib/ruby_llm/ag_ui/mapper.rb +333 -0
- data/lib/ruby_llm/ag_ui/message_parser.rb +127 -0
- data/lib/ruby_llm/ag_ui/protocol.rb +130 -0
- data/lib/ruby_llm/ag_ui/rails/active_record_store.rb +119 -0
- data/lib/ruby_llm/ag_ui/rails/railtie.rb +29 -0
- data/lib/ruby_llm/ag_ui/rails/record.rb +51 -0
- data/lib/ruby_llm/ag_ui/rails/streaming.rb +166 -0
- data/lib/ruby_llm/ag_ui/reconnect.rb +86 -0
- data/lib/ruby_llm/ag_ui/redaction.rb +58 -0
- data/lib/ruby_llm/ag_ui/redemption.rb +62 -0
- data/lib/ruby_llm/ag_ui/reservation.rb +56 -0
- data/lib/ruby_llm/ag_ui/resume.rb +108 -0
- data/lib/ruby_llm/ag_ui/resume_entry.rb +49 -0
- data/lib/ruby_llm/ag_ui/run.rb +177 -0
- data/lib/ruby_llm/ag_ui/run_record.rb +229 -0
- data/lib/ruby_llm/ag_ui/sse_writer.rb +119 -0
- data/lib/ruby_llm/ag_ui/store/base.rb +123 -0
- data/lib/ruby_llm/ag_ui/store/memory_store.rb +123 -0
- data/lib/ruby_llm/ag_ui/validation.rb +63 -0
- data/lib/ruby_llm/ag_ui/version.rb +8 -0
- data/lib/ruby_llm/ag_ui.rb +65 -0
- metadata +223 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: "How RubyLLM AG-UI Works"
|
|
4
|
+
nav_order: 2
|
|
5
|
+
description: One RubyLLM chat turn becomes one ordered AG-UI run with a single recorded outcome.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# {{ page.title }}
|
|
9
|
+
|
|
10
|
+
{{ page.description }}
|
|
11
|
+
{: .fs-6 .fw-300 }
|
|
12
|
+
|
|
13
|
+
After reading this guide, you will know:
|
|
14
|
+
|
|
15
|
+
* Which objects parse input, drive a chat, write events, and store run state.
|
|
16
|
+
* How the event stream and terminal outcome are ordered.
|
|
17
|
+
* Which responsibilities stay with the host application.
|
|
18
|
+
|
|
19
|
+
One RubyLLM chat turn is one AG-UI run. A run has one identity, one ordered
|
|
20
|
+
stream of events and exactly one outcome, and the parts that decide those things
|
|
21
|
+
are small enough to name:
|
|
22
|
+
|
|
23
|
+
```
|
|
24
|
+
POST RunAgentInput
|
|
25
|
+
│
|
|
26
|
+
▼
|
|
27
|
+
┌──────────────────┐ bounded, validated, typed ┌────────────────┐
|
|
28
|
+
│ Input.parse │──────────────────────────────▶│ Run │
|
|
29
|
+
└──────────────────┘ + canonical digest └────────────────┘
|
|
30
|
+
│ │ │ │
|
|
31
|
+
reserve (scope, thread, run) │ │ │ │
|
|
32
|
+
┌─────────────────┘ │ │ │
|
|
33
|
+
▼ │ │ │
|
|
34
|
+
┌───────────┐ │ │ │
|
|
35
|
+
│ RunStore │◀─────────────┘ │ │
|
|
36
|
+
└───────────┘ save/attach │ │
|
|
37
|
+
│ │
|
|
38
|
+
chat.complete { chunk } ──▶ ┌─────────┐ events │ │
|
|
39
|
+
chat.before_tool_call ──▶ │ Mapper │─────────┘ │
|
|
40
|
+
chat.after_message ──▶ └─────────┘ │
|
|
41
|
+
▼
|
|
42
|
+
┌──────────┐ one frame ┌──────────┐
|
|
43
|
+
│ Emitter │─────────────▶│ SSEWriter│──▶ IO
|
|
44
|
+
└──────────┘ per event └──────────┘
|
|
45
|
+
record: sequence, status, terminal
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
## The boot contract
|
|
49
|
+
|
|
50
|
+
```ruby
|
|
51
|
+
require 'ruby_llm/ag_ui'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
loads RubyLLM, `ag-ui-protocol`, Zeitwerk and the standard library, and nothing
|
|
55
|
+
else. It does not load Rails, open a network connection or read provider
|
|
56
|
+
credentials, and the test suite asserts that in a subprocess rather than by
|
|
57
|
+
inspection (`spec/integration/core_load_spec.rb`).
|
|
58
|
+
|
|
59
|
+
Two things are deliberately eager:
|
|
60
|
+
|
|
61
|
+
- **Errors** (`lib/ruby_llm/ag_ui/error.rb`) are ignored by Zeitwerk and
|
|
62
|
+
`require`d directly, so a host can `rescue RubyLLM::AgUI::InputError` before
|
|
63
|
+
anything has been autoloaded.
|
|
64
|
+
- **The namespace itself** (`RubyLLM::AgUI`, with `configure`, `config` and
|
|
65
|
+
`reset_configuration!`) lives in `lib/ruby_llm/ag_ui.rb`, because everything
|
|
66
|
+
else hangs off it.
|
|
67
|
+
|
|
68
|
+
The Rails adapter under `lib/ruby_llm/ag_ui/rails/` is ignored by the loader and
|
|
69
|
+
loaded only when `Rails::Railtie` is already defined. A pure-Ruby host never
|
|
70
|
+
loads ActiveRecord or ActionController.
|
|
71
|
+
|
|
72
|
+
`RubyLLM::AgUI` does not define or reopen the top-level `AgUi` namespace: that
|
|
73
|
+
belongs to the `ag-ui` gem, and this gem talks to the protocol SDK as
|
|
74
|
+
`::AgUiProtocol`. Inside the namespace the framework is `::Rails` for the same
|
|
75
|
+
reason.
|
|
76
|
+
|
|
77
|
+
## The pieces
|
|
78
|
+
|
|
79
|
+
| Component | Owns | Does not own |
|
|
80
|
+
|---|---|---|
|
|
81
|
+
| `Input` / `Parser` / `MessageParser` | Bounding the body, decoding JSON, stripping unknown members, validating shapes, computing the canonical `digest`. | Trust. Nothing a client sends grants a privilege. |
|
|
82
|
+
| `Run` | The turn: reserve identity, drive the chat, project callbacks, close and report the outcome. | Authentication, authorization, transport, chat construction. |
|
|
83
|
+
| `Mapper` | Which AG-UI events a RubyLLM signal becomes, and the ordering between them. Returns arrays; writes nothing. | Sequence numbers, persistence, the terminal event. |
|
|
84
|
+
| `Emitter` | Writing events, advancing the run record's sequence, persisting a terminal outcome before writing it, closing open content first. | Which events; the outcome. |
|
|
85
|
+
| `SSEWriter` | Framing events as `text/event-stream` on any object with `#write`. | The connection, buffering, keep-alive policy. |
|
|
86
|
+
| `Protocol` | The two 1.0 shapes `ag-ui-protocol` 0.2.0 cannot express, plus factories shared by the live and reconnect paths. | Schema evolution. |
|
|
87
|
+
| `Store::Base` / `MemoryStore` / `Rails::ActiveRecordStore` | Run identity, interrupts, redemption. | Chat history, message ids, tool output. |
|
|
88
|
+
| `RunRecord` / `Interrupt` | An immutable value per run and per approval pause, with legal transitions only. | Wire shapes. |
|
|
89
|
+
| `Resume` / `Reconnect` | Answering a previous run's interrupts; serving a run that already ended. | Permission. A resume is a request. |
|
|
90
|
+
| `Redaction` / `ErrorSanitizer` | What may appear on the wire: payload shapes without content, fixed error messages and codes. | Deciding what the host considers secret; that is the host's policy. |
|
|
91
|
+
| `Coverage` | The 31-type audit against the pinned schema. | What the SDK supports. |
|
|
92
|
+
|
|
93
|
+
## The run, step by step
|
|
94
|
+
|
|
95
|
+
1. **Parse.** `Input.parse` bounds the body (`config.max_input_bytes`), decodes
|
|
96
|
+
it, strips top-level members the pinned schema does not define (warnings are
|
|
97
|
+
logged; `strict: true` rejects instead), validates message/tool/context
|
|
98
|
+
counts and shapes, and freezes the result with a canonical digest.
|
|
99
|
+
2. **Reserve.** `Run` asks the store for `(scope, threadId, runId)`. The scope
|
|
100
|
+
must be a non-empty String the host built from authenticated identity. A
|
|
101
|
+
fresh key reserves; the same key with the same digest attaches; the same key
|
|
102
|
+
with a different digest is a conflict; a non-terminal record that is not
|
|
103
|
+
attachable is refused (`StreamError`) rather than streamed twice.
|
|
104
|
+
3. **Open.** `RUN_STARTED` carries `protocolVersion` and the accepted input, so
|
|
105
|
+
the client can see exactly what the host accepted.
|
|
106
|
+
4. **Drive.** `Run` registers one `before_tool_call`, one `after_message` and
|
|
107
|
+
one streaming block on the chat, then calls `Chat#complete`. RubyLLM owns the
|
|
108
|
+
agent loop: model calls, tool execution, approval pauses, cancellation
|
|
109
|
+
checkpoints.
|
|
110
|
+
5. **Project.** Each callback hands the mapper its RubyLLM value and gets back
|
|
111
|
+
the events to write. The mapper is a state machine: a text message opens on
|
|
112
|
+
its first non-empty chunk and closes before a tool call, a phase change or the
|
|
113
|
+
terminal event; a tool call opens when its id and name are both known, streams
|
|
114
|
+
its argument deltas as received, and closes before it runs; one step per phase
|
|
115
|
+
(`completion`, `tool`) so consecutive steps never overlap.
|
|
116
|
+
6. **Write.** The emitter writes the events as SSE, advancing the run record's
|
|
117
|
+
sequence by one per event.
|
|
118
|
+
7. **Close and report.** Open content is closed, then the outcome is persisted
|
|
119
|
+
and only then written: `RUN_FINISHED` with `success`, `interrupt` or
|
|
120
|
+
`cancelled`, or `RUN_ERROR` with a sanitized message and code. A chat paused
|
|
121
|
+
on anything the protocol cannot express is a failure, not a success.
|
|
122
|
+
|
|
123
|
+
## Invariants
|
|
124
|
+
|
|
125
|
+
- **One outcome per run.** `RunRecord` transitions from `reserved` to `started`
|
|
126
|
+
and then to exactly one of `succeeded`, `interrupted`, `cancelled`, `failed`.
|
|
127
|
+
A terminal record accepts no new start, and `finish` records the first failure
|
|
128
|
+
and keeps it.
|
|
129
|
+
- **Content before terminal.** No run ends with a text message, tool call or step
|
|
130
|
+
still open: a client that never sees `*_END` waits forever.
|
|
131
|
+
- **Terminal before transport.** The outcome is stored before it is written, so
|
|
132
|
+
a socket that dies after the decision cannot cause a second execution.
|
|
133
|
+
- **Sequence, not wall clock.** Every event advances the record's sequence; a
|
|
134
|
+
reconnect orders what it serves by that number.
|
|
135
|
+
- **Delta replay is forbidden.** A reconnect serves `RUN_STARTED`,
|
|
136
|
+
`STATE_SNAPSHOT` (when recorded) and the terminal event. Never text or tool
|
|
137
|
+
deltas: replaying them draws text twice, and re-running a tool repeats the
|
|
138
|
+
side effect.
|
|
139
|
+
- **Closed is final.** RubyLLM's callbacks are additive and cannot be removed, so
|
|
140
|
+
the callbacks a run registered stay attached to the chat. A mapper that has
|
|
141
|
+
closed its run therefore maps nothing further; the next turn is a different run
|
|
142
|
+
with its own ids, sequence and record.
|
|
143
|
+
|
|
144
|
+
## Concurrency
|
|
145
|
+
|
|
146
|
+
RubyLLM may run local tools on worker threads and appends tool results in the
|
|
147
|
+
caller's thread, so `Run` maps and writes under one mutex. That keeps the event
|
|
148
|
+
order consistent with the mapper's state; it does not make a run parallel.
|
|
149
|
+
|
|
150
|
+
Two streams for one live run are refused, not merged: relaying one run to two
|
|
151
|
+
clients is a transport the gem does not own. Two requests for one run identity
|
|
152
|
+
are resolved by the store, and the store's redemption step is the authoritative
|
|
153
|
+
race guard for a resume. The loser is told the answer was already recorded
|
|
154
|
+
instead of running the tool twice.
|
|
155
|
+
|
|
156
|
+
## Storage
|
|
157
|
+
|
|
158
|
+
A record is a value object; stores keep values. `RunRecord#to_h` is the reporting
|
|
159
|
+
shape (no interrupt argument digests, no creation times), and `#to_storage` is
|
|
160
|
+
the durable shape a store round-trips through JSON. `ruby_llm_ag_ui_runs` (the
|
|
161
|
+
Rails store) is one row per run: scope, thread, run, digest, status, sequence,
|
|
162
|
+
snapshot, failure, interrupts.
|
|
163
|
+
|
|
164
|
+
Why the scope must be a String: an arbitrary object hashes by identity, so the
|
|
165
|
+
same actor would produce a different key on the next request and a retry would
|
|
166
|
+
execute the run again. A String built from authenticated identity is stable
|
|
167
|
+
across requests and processes, which is the property the reservation needs.
|
|
168
|
+
|
|
169
|
+
## Extension points
|
|
170
|
+
|
|
171
|
+
| Need | Hook |
|
|
172
|
+
|---|---|
|
|
173
|
+
| Durable or shared run identity | `config.run_store`, or `store:` per run. Implement the `Store::Base` interface. |
|
|
174
|
+
| A different redaction policy | `config.redact`, or `redact:` per run. |
|
|
175
|
+
| Deterministic time in tests | `config.clock`, or `clock:` per run. |
|
|
176
|
+
| A shorter/longer approval window | `config.interrupt_ttl`, or `expires_in:` per run. |
|
|
177
|
+
| A different transport | Write to your own object: the gem needs `#write`. |
|
|
178
|
+
| A different server | Any Rack server; the Rails concern is one way to get a live response. |
|
|
179
|
+
|
|
180
|
+
## Non-goals
|
|
181
|
+
|
|
182
|
+
The gem is not an AG-UI SDK, a schema fork, a chat UI, a CopilotKit runtime, or a
|
|
183
|
+
second agent framework. It does not own conversation history, message ids, tool
|
|
184
|
+
authorization, or the decision to run a tool. It does not open connections, spawn
|
|
185
|
+
jobs or buffer frames.
|
|
186
|
+
|
|
187
|
+
See [Protocol Coverage]({% link _reference/protocol-coverage.md %}) for the wire events,
|
|
188
|
+
[Security and Deployment]({% link _advanced/security-and-deployment.md %}) for the threat model, and
|
|
189
|
+
[Rails Integration]({% link _advanced/rails-integration.md %}) for the adapter.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: "What's New in 0.1"
|
|
4
|
+
nav_order: 0
|
|
5
|
+
description: Use the first RubyLLM AG-UI release to stream, resume, cancel, and reconnect agent runs.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# {{ page.title }}
|
|
9
|
+
|
|
10
|
+
{{ page.description }}
|
|
11
|
+
{: .fs-6 .fw-300 }
|
|
12
|
+
|
|
13
|
+
After reading this guide, you will know:
|
|
14
|
+
|
|
15
|
+
* How to install version 0.1.
|
|
16
|
+
* Which run lifecycle features it provides.
|
|
17
|
+
* Which compatibility checks still need a real application.
|
|
18
|
+
|
|
19
|
+
## Install the First Release
|
|
20
|
+
|
|
21
|
+
```ruby
|
|
22
|
+
gem 'ruby_llm-ag_ui', '~> 0.1.0'
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Use Ruby 3.2 or later, RubyLLM 2.1 or later within version 2, and `ag-ui-protocol` 0.2. Follow [Getting Started]({% link _getting_started/getting-started.md %}) to create your first run.
|
|
26
|
+
|
|
27
|
+
## Stream One Chat Turn as One Run
|
|
28
|
+
|
|
29
|
+
The gem validates `RunAgentInput` and maps RubyLLM text, tool, and lifecycle callbacks to AG-UI events. An SSE writer sends each encoded event to the client. See [Streaming Runs]({% link _core_features/streaming-runs.md %}) and [Protocol Coverage]({% link _reference/protocol-coverage.md %}).
|
|
30
|
+
|
|
31
|
+
## Pause, Resume, Cancel, and Reconnect
|
|
32
|
+
|
|
33
|
+
Tool approval pauses use interrupts bound to their tool calls. Your host authorizes the actor and records the decision before resuming. Cancellation produces the cancelled outcome. A retry for a completed run replays its recorded state and terminal event. See [Approval and Resume]({% link _core_features/approval-and-resume.md %}) and [Reconnects]({% link _core_features/reconnects.md %}).
|
|
34
|
+
|
|
35
|
+
## Add the Optional Rails Adapter
|
|
36
|
+
|
|
37
|
+
The adapter supplies a streaming controller concern, an ActiveRecord run store, and an installation generator. The pure-Ruby entrypoint does not load Rails. Follow [Rails Integration]({% link _advanced/rails-integration.md %}).
|
|
38
|
+
|
|
39
|
+
## Check Your Application's Compatibility
|
|
40
|
+
|
|
41
|
+
Offline checks cover the mapping, schema, lifecycle, store, and adapter behavior. A release does not establish interoperability with every AG-UI client or live provider. Read [Compatibility]({% link _reference/compatibility.md %}) and record your application checks before making those claims.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: API Reference
|
|
4
|
+
nav_order: 1
|
|
5
|
+
guide: false
|
|
6
|
+
description: Browse RDoc pages generated from classes and methods in the gem source.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# {{ page.title }}
|
|
10
|
+
|
|
11
|
+
{{ page.description }}
|
|
12
|
+
{: .fs-6 .fw-300 }
|
|
13
|
+
|
|
14
|
+
The [API reference]({{ '/api/' | relative_url }}) opens on the `RubyLLM` module. The generated pages follow the RDoc comments in `lib/`.
|
|
15
|
+
|
|
16
|
+
| Build with | Reference |
|
|
17
|
+
| --- | --- |
|
|
18
|
+
| Configure defaults | [`RubyLLM::AgUI::Configuration`]({{ '/api/RubyLLM/AgUI/Configuration.html' | relative_url }}) |
|
|
19
|
+
| Parse a request | [`RubyLLM::AgUI::Input`]({{ '/api/RubyLLM/AgUI/Input.html' | relative_url }}) |
|
|
20
|
+
| Stream a chat turn | [`RubyLLM::AgUI::Run`]({{ '/api/RubyLLM/AgUI/Run.html' | relative_url }}) |
|
|
21
|
+
| Inspect event coverage | [`RubyLLM::AgUI::Coverage`]({{ '/api/RubyLLM/AgUI/Coverage.html' | relative_url }}) |
|
|
22
|
+
| Store run state | [`RubyLLM::AgUI::Store::MemoryStore`]({{ '/api/RubyLLM/AgUI/Store/MemoryStore.html' | relative_url }}) |
|
|
23
|
+
|
|
24
|
+
For Markdown, start with the [class index]({{ '/api/index.md' | relative_url }}). Every HTML class page has a matching `.md` page.
|
|
25
|
+
|
|
26
|
+
The reference is generated from source comments with RDoc. Build it locally with `docs/bin/build-api.sh tmp/api`.
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: "Compatibility"
|
|
4
|
+
nav_order: 2
|
|
5
|
+
description: Check the dependency versions and behaviors that this repository verifies.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# {{ page.title }}
|
|
9
|
+
|
|
10
|
+
{{ page.description }}
|
|
11
|
+
{: .fs-6 .fw-300 }
|
|
12
|
+
|
|
13
|
+
After reading this guide, you will know:
|
|
14
|
+
|
|
15
|
+
* Which runtime and protocol dependency ranges the gem supports.
|
|
16
|
+
* What the local and CI suites assert.
|
|
17
|
+
* Which client and provider claims remain unverified.
|
|
18
|
+
|
|
19
|
+
What this gem is checked against, what it is not, and what changes when a
|
|
20
|
+
dependency does.
|
|
21
|
+
|
|
22
|
+
## Supported versions
|
|
23
|
+
|
|
24
|
+
| Component | Range | Verified |
|
|
25
|
+
|---|---|---|
|
|
26
|
+
| Ruby | `>= 3.2` | 3.2, 3.3, 3.4 in CI; 3.3.5 locally |
|
|
27
|
+
| RubyLLM | `>= 2.1`, `< 3.0` | 2.1.1 (callback, approval and cancellation behavior read from its source) |
|
|
28
|
+
| `ag-ui-protocol` | `~> 0.2.0` | 0.2.0 |
|
|
29
|
+
| Rails (adapter only) | `railties`, `actionpack`, `activerecord` `>= 8.0` | 8.0.x and 8.1.x in CI |
|
|
30
|
+
| AG-UI schema | pinned commit `89cedd3fd549feeee41e50e1d7c6d373e549241a` | vendored, SHA-256 recorded in [AG-UI Protocol Coverage]({% link _reference/protocol-coverage.md %}) |
|
|
31
|
+
|
|
32
|
+
The pure-Ruby core depends only on `ruby_llm` and `ag-ui-protocol`. Rails,
|
|
33
|
+
`ToolViews`, `OpenUI` and `OddlyDock` are not runtime dependencies, and the Rails
|
|
34
|
+
adapter is only loaded when `Rails::Railtie` is already defined.
|
|
35
|
+
|
|
36
|
+
## What is asserted, and how
|
|
37
|
+
|
|
38
|
+
| Claim | How it is checked |
|
|
39
|
+
|---|---|
|
|
40
|
+
| `require 'ruby_llm/ag_ui'` loads no Rails, opens no connection, reads no credentials | `spec/integration/core_load_spec.rb` runs the require in a subprocess and inspects what it loaded |
|
|
41
|
+
| Every emitted event is a valid AG-UI 1.0 event | mapper and run specs validate each encoded event against the vendored schema (`SchemaHelper`) |
|
|
42
|
+
| The 31-type coverage map is the schema's own union | `spec/ruby_llm/ag_ui/coverage_spec.rb` compares the pinned list with `Coverage`, and the SDK enum at runtime |
|
|
43
|
+
| The SDK's five legacy `THINKING_*` constants are never counted as 1.0 | the same spec asserts `LEGACY_SDK_ONLY` is absent from the map |
|
|
44
|
+
| Text, tool and lifecycle sequences are ordered and closed | mapper specs, including partial arguments, a nil chunk, a tool that raises, and a thinking-only chunk |
|
|
45
|
+
| One outcome per run, and the outcome matches what happened | run specs: success, interrupt, cancelled, sanitized failure, unsupported pause |
|
|
46
|
+
| `requires_approval` → interrupt → resume executes the tool once | run specs drive a fake chat through the pause, approve, deny, replay and expiry cases |
|
|
47
|
+
| Identity is reserved, replayed and never re-executed | store specs (memory and ActiveRecord), run specs for conflict, reconnect and live-run refusal |
|
|
48
|
+
| The Rails adapter serves a run and refuses invalid requests before committing | adapter specs with a controller double, and an end-to-end `spec/integration/rails_adapter_spec.rb` |
|
|
49
|
+
| The installer resolves the documented command | `spec/ruby_llm/ag_ui/generators/install_generator_spec.rb` |
|
|
50
|
+
| The installed gem generates a working migration and initializer | `bin/verify-package --rails` generates files and migrates the table up and down in SQLite |
|
|
51
|
+
| `ruby_llm-ag_ui` installs outside the checkout | `bin/verify-package` resolves dependencies in a fresh gem home and loads the packaged core |
|
|
52
|
+
|
|
53
|
+
The RSpec suite runs offline with fakes and no provider credentials. Package verification uses the network only to install dependencies, then runs its core and Rails checks without a provider. Its advisory check includes the actual default-library gem versions activated by the isolated core install.
|
|
54
|
+
|
|
55
|
+
## What is not asserted
|
|
56
|
+
|
|
57
|
+
- **Real AG-UI clients** (CopilotKit, `@ag-ui/client`, any other consumer). No
|
|
58
|
+
client has been recorded against this producer. The events are schema-valid and
|
|
59
|
+
the ordering rules the protocol documents are enforced, but "a specific client
|
|
60
|
+
renders this correctly" is a separate claim, and it is not made here.
|
|
61
|
+
- **Live providers.** `spec/live/live_run_spec.rb` is the opt-in check
|
|
62
|
+
(`RUBY_LLM_LIVE=1`, plus a provider key); it is skipped in the default suite.
|
|
63
|
+
Provider behavior in 2.1.1 is derived from its source, not from a recorded
|
|
64
|
+
session.
|
|
65
|
+
- **Every RubyLLM patch release in the `>= 2.1, < 3.0` range.** The upper bound
|
|
66
|
+
is enforced by the gemspec; the verified version is 2.1.1.
|
|
67
|
+
- **Rails versions before 8.0.** The adapter uses `Rails.setup!` and an
|
|
68
|
+
initializer, and its specs run on 8.0/8.1. It may work on 7.2 and it is not
|
|
69
|
+
tested there.
|
|
70
|
+
- **Databases other than SQLite** for the ActiveRecord store. The store uses a
|
|
71
|
+
unique index and `with_lock`; PostgreSQL and MySQL should behave the same, and
|
|
72
|
+
that is untested.
|
|
73
|
+
|
|
74
|
+
## Deliberate behavior worth knowing
|
|
75
|
+
|
|
76
|
+
These are choices, not gaps, and each has a reason:
|
|
77
|
+
|
|
78
|
+
- **Tool results are `[redacted]` by default.** Redaction keeps the payload's
|
|
79
|
+
shape and withholds its content until a host opts out. A client will see
|
|
80
|
+
`[redacted]` until you configure a policy.
|
|
81
|
+
- **A resume run emits `TOOL_CALL_RESULT` without repeating
|
|
82
|
+
`TOOL_CALL_START`.** The call was already reported in the run that interrupted;
|
|
83
|
+
repeating it would duplicate the call in the client's conversation.
|
|
84
|
+
- **`MESSAGES_SNAPSHOT` is never produced.** History is the host's, and the gem
|
|
85
|
+
will not re-id it. `STATE_SNAPSHOT` is produced, on a reconnect.
|
|
86
|
+
- **No `ACTIVITY_*`, `REASONING_*`, `RAW` or `SUBAGENT_*` events.** RubyLLM 2.x
|
|
87
|
+
exposes no data for them on the supported path, and the gem does not invent
|
|
88
|
+
events. A thinking-only chunk produces nothing.
|
|
89
|
+
- **Message ids are the gem's** (`run-<id>:a<n>`), scoped to the run, because
|
|
90
|
+
RubyLLM streaming chunks carry no message id.
|
|
91
|
+
- **Steps are per phase** (`completion`, `tool`), named `step-<n>` per run.
|
|
92
|
+
- **Client-supplied tools are never registered.** `input.tools` is reported and
|
|
93
|
+
ignored.
|
|
94
|
+
- **Two live streams for one run are refused**, not merged; a run that already
|
|
95
|
+
ended is served by reconnect instead.
|
|
96
|
+
|
|
97
|
+
## When a dependency moves
|
|
98
|
+
|
|
99
|
+
**A new `ag-ui-protocol` 0.2.x** that adds a schema type will fail the coverage
|
|
100
|
+
spec's runtime comparison, which is the point: the map has to be updated
|
|
101
|
+
deliberately, with a status and a reason.
|
|
102
|
+
|
|
103
|
+
**`ag-ui-protocol` 0.3+** would need `Protocol` re-checked first: the gem
|
|
104
|
+
subclasses `Model` and `BaseEvent` for the cancelled outcome, the versioned
|
|
105
|
+
`RUN_STARTED` and the run-finished shape. The gaps listed in
|
|
106
|
+
the [protocol coverage guide]({% link _reference/protocol-coverage.md %}) may be closed upstream, in which case
|
|
107
|
+
the local subclasses should be removed rather than kept.
|
|
108
|
+
|
|
109
|
+
**A new RubyLLM 2.x minor** can change the callback contract (`before_tool_call`,
|
|
110
|
+
`after_message`, `complete`'s streaming block), approval state (`waiting?`,
|
|
111
|
+
`awaiting_approval?`, `pending_approvals`, `approve`, `deny`) or the shape of
|
|
112
|
+
chunks and messages. The mapper and run specs pin all of it, so an upgrade fails
|
|
113
|
+
loudly instead of silently dropping content. A 3.0 would need a new adapter
|
|
114
|
+
layer; the gemspec refuses it.
|
|
115
|
+
|
|
116
|
+
**A new AG-UI schema commit** is a deliberate change: vendor the file, update
|
|
117
|
+
`Protocol::SCHEMA_COMMIT` and the SHA-256 in
|
|
118
|
+
the [protocol coverage guide]({% link _reference/protocol-coverage.md %}), then let the coverage and schema
|
|
119
|
+
specs tell you what moved.
|
|
120
|
+
|
|
121
|
+
## Reporting an incompatibility
|
|
122
|
+
|
|
123
|
+
Open an issue with the versions from `RubyLLM::AgUI.config.to_h`, the event types
|
|
124
|
+
that went wrong, and whether a real client or a live provider was involved. A
|
|
125
|
+
redacted trace is enough; do not paste credentials or raw tool payloads.
|
|
126
|
+
|
|
127
|
+
## Next Steps
|
|
128
|
+
|
|
129
|
+
* [AG-UI Protocol Coverage]({% link _reference/protocol-coverage.md %}) - inspect event support.
|
|
130
|
+
* [Upgrading]({% link _reference/upgrading.md %}) - record breaking dependency changes.
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: "AG-UI Protocol Coverage"
|
|
4
|
+
nav_order: 1
|
|
5
|
+
description: See how this producer handles each event in the pinned AG-UI 1.0 schema.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# {{ page.title }}
|
|
9
|
+
|
|
10
|
+
{{ page.description }}
|
|
11
|
+
{: .fs-6 .fw-300 }
|
|
12
|
+
|
|
13
|
+
After reading this guide, you will know:
|
|
14
|
+
|
|
15
|
+
* Which event types the gem produces, leaves to the host, or does not emit.
|
|
16
|
+
* How success, interruption, cancellation, failure, and reconnect map to events.
|
|
17
|
+
* Which protocol gaps the pinned Ruby SDK does not express.
|
|
18
|
+
|
|
19
|
+
`ruby_llm-ag_ui` speaks AG-UI 1.0. This page is the audit of what it produces
|
|
20
|
+
against that schema: every one of the 31 event types the 1.0 `Event` union
|
|
21
|
+
defines, with the status this gem gives it and why.
|
|
22
|
+
|
|
23
|
+
A producer is not required to manufacture events it has no data for. What it must
|
|
24
|
+
never do is emit an event the schema does not define, or claim a run succeeded
|
|
25
|
+
when it paused, failed or was cancelled.
|
|
26
|
+
|
|
27
|
+
## The pinned schema
|
|
28
|
+
|
|
29
|
+
| | |
|
|
30
|
+
|---|---|
|
|
31
|
+
| Repository | <https://github.com/ag-ui-protocol/ag-ui> |
|
|
32
|
+
| Commit | `89cedd3fd549feeee41e50e1d7c6d373e549241a` |
|
|
33
|
+
| File | `docs/spec/1.0/schema.json` |
|
|
34
|
+
| Vendored copy | `spec/fixtures/schema/ag-ui-1.0.schema.json` |
|
|
35
|
+
| SHA-256 | `4b5c93226838a0e72d88e6c5df20633c686c49fcb75d9be2815c6cbf9e48e71a` |
|
|
36
|
+
|
|
37
|
+
The vendored schema is what the test suite validates against, so a mapping
|
|
38
|
+
change fails CI rather than a client. `RubyLLM::AgUI::Protocol::SCHEMA_COMMIT`
|
|
39
|
+
records the commit, and `schema_valid!` in the mapper and run specs checks every
|
|
40
|
+
emitted event against the vendored file.
|
|
41
|
+
|
|
42
|
+
## The 31 event types
|
|
43
|
+
|
|
44
|
+
Legend:
|
|
45
|
+
|
|
46
|
+
- **produced:** emitted from real RubyLLM data on the supported path.
|
|
47
|
+
- **optional:** emitted only when the host opts in and real data exists.
|
|
48
|
+
- **host-owned:** the host owns the payload; this gem does not produce it.
|
|
49
|
+
- **not produced:** the producer has no data for it and never fabricates it.
|
|
50
|
+
|
|
51
|
+
### Produced (13)
|
|
52
|
+
|
|
53
|
+
| Event type | Family | When |
|
|
54
|
+
|---|---|---|
|
|
55
|
+
| `RUN_STARTED` | run | Opens a reserved run; declares `protocolVersion` and echoes the accepted input. |
|
|
56
|
+
| `RUN_FINISHED` | run | Success, interrupt or cancelled outcome; never a raw partial. |
|
|
57
|
+
| `RUN_ERROR` | run | Sanitized message and code; never raw exception text. |
|
|
58
|
+
| `STEP_STARTED` | run | One step per RubyLLM phase (completion, tool round). |
|
|
59
|
+
| `STEP_FINISHED` | run | Closed before the terminal event. |
|
|
60
|
+
| `TEXT_MESSAGE_START` | text | Opened on the first non-empty assistant chunk. |
|
|
61
|
+
| `TEXT_MESSAGE_CONTENT` | text | One delta per non-empty `chunk.content`, in order. |
|
|
62
|
+
| `TEXT_MESSAGE_END` | text | Closed before a tool call, a phase change or the terminal event. |
|
|
63
|
+
| `TOOL_CALL_START` | tool | Emitted when a call is observed before it runs. |
|
|
64
|
+
| `TOOL_CALL_ARGS` | tool | Arguments as received; never re-synthesized. |
|
|
65
|
+
| `TOOL_CALL_END` | tool | Closed before the call runs or the run ends. |
|
|
66
|
+
| `TOOL_CALL_RESULT` | tool | The tool result, after the host redaction policy. |
|
|
67
|
+
| `STATE_SNAPSHOT` | state | On a reconnect: the state the host gave the run. |
|
|
68
|
+
|
|
69
|
+
### Optional (1)
|
|
70
|
+
|
|
71
|
+
| Event type | Family | When |
|
|
72
|
+
|---|---|---|
|
|
73
|
+
| `CUSTOM` | raw/custom | Only safe, documented host extensions. Nothing in the gem emits one today. |
|
|
74
|
+
|
|
75
|
+
### Host-owned, not produced (2)
|
|
76
|
+
|
|
77
|
+
| Event type | Family | Why the gem does not produce it |
|
|
78
|
+
|---|---|---|
|
|
79
|
+
| `MESSAGES_SNAPSHOT` | state | The conversation belongs to the host. Producing one would mean re-synthesizing message ids the gem did not mint, which a client would then render as new messages. |
|
|
80
|
+
| `STATE_DELTA` | state | Deltas describe a change the host made. The gem never invents a patch, and it has no API that publishes one. |
|
|
81
|
+
|
|
82
|
+
### Not produced (15)
|
|
83
|
+
|
|
84
|
+
| Event type | Family | Why |
|
|
85
|
+
|---|---|---|
|
|
86
|
+
| `TEXT_MESSAGE_CHUNK` | text | Convenience form of the explicit triad; the gem emits `START`/`CONTENT`/`END`. |
|
|
87
|
+
| `TOOL_CALL_CHUNK` | tool | Convenience form; the gem emits the explicit triad. |
|
|
88
|
+
| `ACTIVITY_SNAPSHOT` | activity | No `ACTIVITY_*` projection yet; RubyLLM's progress signals are not mapped. |
|
|
89
|
+
| `ACTIVITY_DELTA` | activity | Same: no progress projection, and no invented patches. |
|
|
90
|
+
| `RAW` | raw/custom | The gem never passes provider payloads through. |
|
|
91
|
+
| `REASONING_START` | reasoning | Private reasoning is not emitted until a host opts in, and there is no opt-in yet. |
|
|
92
|
+
| `REASONING_MESSAGE_START` | reasoning | A chunk that carries thinking is ignored. |
|
|
93
|
+
| `REASONING_MESSAGE_CONTENT` | reasoning | Same. |
|
|
94
|
+
| `REASONING_MESSAGE_END` | reasoning | Nothing opens a reasoning block to close. |
|
|
95
|
+
| `REASONING_MESSAGE_CHUNK` | reasoning | Convenience form; not emitted. |
|
|
96
|
+
| `REASONING_END` | reasoning | Nothing opens a reasoning block to close. |
|
|
97
|
+
| `REASONING_ENCRYPTED_VALUE` | reasoning | RubyLLM 2.x exposes no encrypted reasoning value. |
|
|
98
|
+
| `SUBAGENT_STARTED` | subagents | RubyLLM 2.x runs one chat; there is no subagent data. |
|
|
99
|
+
| `SUBAGENT_FINISHED` | subagents | Same. |
|
|
100
|
+
| `SUBAGENT_ERROR` | subagents | Same. |
|
|
101
|
+
|
|
102
|
+
Totals: 13 produced + 1 optional + 2 host-owned + 15 not produced = **31**.
|
|
103
|
+
|
|
104
|
+
## Terminal events
|
|
105
|
+
|
|
106
|
+
One run ends exactly once, and the outcome is part of the run record before it is
|
|
107
|
+
written:
|
|
108
|
+
|
|
109
|
+
| Situation | Terminal event |
|
|
110
|
+
|---|---|
|
|
111
|
+
| The chat completed | `RUN_FINISHED` with `{"type":"success"}` |
|
|
112
|
+
| The chat paused on `requires_approval` | `RUN_FINISHED` with `{"type":"interrupt","interrupts":[...]}` |
|
|
113
|
+
| The chat was cancelled (`Chat#cancel`) | `RUN_FINISHED` with `{"type":"cancelled"}` |
|
|
114
|
+
| The run failed | `RUN_ERROR` with a sanitized `message` and a `code` |
|
|
115
|
+
| The chat paused on something the protocol cannot express | `RUN_ERROR` (`unsupported_run_state`), never a success |
|
|
116
|
+
|
|
117
|
+
A paused run is never a success. A cancelled run is never a success. A transport
|
|
118
|
+
that dies mid-run reports nothing on the wire; the record stays non-terminal and
|
|
119
|
+
the client can reconnect, and it is never evidence that the work finished.
|
|
120
|
+
|
|
121
|
+
## Reconnect
|
|
122
|
+
|
|
123
|
+
A client that re-sends the same `(threadId, runId)` and the same input for a run
|
|
124
|
+
that already ended gets:
|
|
125
|
+
|
|
126
|
+
1. `RUN_STARTED` (with `protocolVersion` and the input echo),
|
|
127
|
+
2. `STATE_SNAPSHOT` when the run recorded one,
|
|
128
|
+
3. the recorded terminal event.
|
|
129
|
+
|
|
130
|
+
It does not get the run's text or tool deltas again: replaying them would draw
|
|
131
|
+
the text twice, and re-running a tool would duplicate the side effect. That is
|
|
132
|
+
also why `MESSAGES_SNAPSHOT` is not used because the client's own conversation is the
|
|
133
|
+
authority, and the gem will not hand it a second, re-id'd copy.
|
|
134
|
+
|
|
135
|
+
## Where this differs from `ag-ui-protocol` 0.2.0
|
|
136
|
+
|
|
137
|
+
The community Ruby SDK (`ag-ui-protocol` 0.2.0) is what this gem encodes with, so
|
|
138
|
+
its gaps are visible here. Issue drafts for these SDK gaps are kept under
|
|
139
|
+
`docs/.internal/upstream/` in the repository.
|
|
140
|
+
|
|
141
|
+
| ID | Gap | This gem's handling |
|
|
142
|
+
|---|---|---|
|
|
143
|
+
| SDK-GAP-01 | No `cancelled` run outcome. | `RubyLLM::AgUI::Protocol::CancelledOutcome`, a schema-valid `cancelled` outcome. |
|
|
144
|
+
| SDK-GAP-02 | `RunStartedEvent` has no `protocolVersion`. | `RubyLLM::AgUI::Protocol::RunStartedEvent` adds it, defaulting to `"1.0"`. |
|
|
145
|
+
| SDK-GAP-03 | No JSON → `RunAgentInput` parser. | `RubyLLM::AgUI::Input.parse`, with size and schema bounds. |
|
|
146
|
+
| SDK-GAP-04 | No `SUBAGENT_STARTED`/`_FINISHED`/`_ERROR` constants. | Not produced: RubyLLM 2.x has no subagent data to report. |
|
|
147
|
+
| SDK-GAP-05 | Five legacy `THINKING_*` constants that 1.0 does not define. | Never emitted. `Coverage` excludes them from the 31 and the suite asserts it. |
|
|
148
|
+
|
|
149
|
+
The SDK defines 33 event-type constants; the 1.0 schema defines 31. The two sets
|
|
150
|
+
differ by exactly the five `THINKING_*` constants (SDK-only) and the three
|
|
151
|
+
`SUBAGENT_*` types (schema-only), and `RubyLLM::AgUI::Coverage` computes that
|
|
152
|
+
difference at runtime rather than hardcoding it, so an SDK upgrade that closes a
|
|
153
|
+
gap shows up as a failing expectation instead of a stale document.
|
|
154
|
+
|
|
155
|
+
## Checking It Yourself
|
|
156
|
+
|
|
157
|
+
```ruby
|
|
158
|
+
require 'ruby_llm/ag_ui'
|
|
159
|
+
|
|
160
|
+
RubyLLM::AgUI::Coverage.entries.size # => 31
|
|
161
|
+
RubyLLM::AgUI::Coverage.sdk_only_event_types
|
|
162
|
+
# => ["THINKING_TEXT_MESSAGE_START", "THINKING_TEXT_MESSAGE_CONTENT", "THINKING_TEXT_MESSAGE_END", "THINKING_START", "THINKING_END"]
|
|
163
|
+
RubyLLM::AgUI::Coverage.schema_only_event_types
|
|
164
|
+
# => ["SUBAGENT_STARTED", "SUBAGENT_FINISHED", "SUBAGENT_ERROR"]
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Every event the mapper and the run emit is validated against the vendored schema
|
|
168
|
+
in the spec suite (`bundle exec rspec`), including the negative cases: partial
|
|
169
|
+
tool arguments, a nil chunk, a tool that raises, and a thinking-only chunk that
|
|
170
|
+
must produce no event at all.
|
|
171
|
+
|
|
172
|
+
## Next Steps
|
|
173
|
+
|
|
174
|
+
* [Streaming Runs]({% link _core_features/streaming-runs.md %}) - follow event order and outcomes.
|
|
175
|
+
* [Compatibility]({% link _reference/compatibility.md %}) - see what this repository verifies.
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Release History
|
|
4
|
+
nav_order: 9
|
|
5
|
+
guide: false
|
|
6
|
+
description: Review RubyLLM AG-UI releases, newest first, with highlights and upgrade guidance.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# {{ page.title }}
|
|
10
|
+
|
|
11
|
+
{{ page.description }}
|
|
12
|
+
{: .fs-6 .fw-300 }
|
|
13
|
+
|
|
14
|
+
{% if site.docs_unreleased %}This build follows `main`. The latest stable release is `{{ site.version }}`.
|
|
15
|
+
{% else %}This documentation describes version `{{ site.version }}`. Use its tag and release notes to identify the exact source.
|
|
16
|
+
{% endif %}
|
|
17
|
+
|
|
18
|
+
| Release | Date | Highlights |
|
|
19
|
+
| --- | --- | --- |
|
|
20
|
+
| [0.1.0](https://github.com/yorzi/ruby_llm-ag-ui/releases/tag/v0.1.0) | 2026-10-18 | [First release]({% link _getting_started/whats-new-in-0-1.md %}): AG-UI event mapping, approval and resume, cancellation, reconnect, and the optional Rails adapter |
|
|
21
|
+
|
|
22
|
+
To move an application from one release to the next, follow [Upgrading]({% link _reference/upgrading.md %}).
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Releasing
|
|
4
|
+
nav_order: 10
|
|
5
|
+
description: Verify an installed gem artifact and publish its matching version tag through RubyGems trusted publishing.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# {{ page.title }}
|
|
9
|
+
|
|
10
|
+
{{ page.description }}
|
|
11
|
+
{: .fs-6 .fw-300 }
|
|
12
|
+
|
|
13
|
+
After reading this guide, you will know:
|
|
14
|
+
|
|
15
|
+
* How to check a release artifact outside the checkout.
|
|
16
|
+
* Which settings bind RubyGems to the release workflow.
|
|
17
|
+
* How a version tag publishes the gem and its documentation.
|
|
18
|
+
|
|
19
|
+
## Verify the Package
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
bundle exec rake
|
|
23
|
+
bundle exec bundle-audit check --update
|
|
24
|
+
bundle exec rake build
|
|
25
|
+
bundle exec ruby bin/verify-package --rails --audit
|
|
26
|
+
docs/bin/build.sh
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The package check installs dependencies in a fresh temporary gem home and requires the installed artifact from outside the checkout. It verifies the core entrypoint, shipped templates and guides, and input parsing. With `--rails`, it invokes the documented generator and migrates its table up and down in SQLite. With `--audit`, it checks the actually activated core dependencies against the advisory database.
|
|
30
|
+
|
|
31
|
+
These checks make no live provider call. Keep provider and client interoperability evidence in [Compatibility]({% link _reference/compatibility.md %}).
|
|
32
|
+
|
|
33
|
+
## Configure Trusted Publishing
|
|
34
|
+
|
|
35
|
+
Before the first tag, register a pending trusted publisher on RubyGems with gem name `ruby_llm-ag_ui`, GitHub owner `yorzi`, repository `ruby_llm-ag-ui`, workflow `release.yml`, and environment `release`. The workflow uses a short-lived GitHub OIDC identity instead of a stored RubyGems API key.
|
|
36
|
+
|
|
37
|
+
## Prepare the Release Commit
|
|
38
|
+
|
|
39
|
+
Match the version in `lib/ruby_llm/ag_ui/version.rb` and `docs/_config.yml`. Set `released: true`, add a dated CHANGELOG section, update [Release History]({% link _reference/release-history.md %}), and record upgrade steps in [Upgrading]({% link _reference/upgrading.md %}). For a minor release, add its What's New guide.
|
|
40
|
+
|
|
41
|
+
Push the completed commit to `main` and wait for CI to pass. Tag that exact commit:
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
git tag -a v{{ site.version }} -m "Release {{ site.version }}"
|
|
45
|
+
git push origin v{{ site.version }}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
Replace the example tag with the version you are releasing. The `Release Gem` workflow rejects a tag that differs from the gem or docs version, or lacks a CHANGELOG section. It runs offline checks, the advisory audit, and isolated package verification before publishing.
|
|
49
|
+
|
|
50
|
+
## Confirm Publication
|
|
51
|
+
|
|
52
|
+
On success, the workflow creates a GitHub Release with the gem, checksum, and CHANGELOG notes. The Docs workflow runs after the release workflow succeeds. It publishes the tagged version at `/` and current `main` at `/next/`.
|
|
53
|
+
|
|
54
|
+
Check the version on RubyGems, the GitHub Release assets, and the documentation installation command. If a publish attempt fails, inspect its logs before retrying. A published RubyGems version cannot be overwritten.
|