riffer-rig 0.9.0 → 0.10.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/CHANGELOG.md +8 -0
- data/README.md +2 -0
- data/lib/riffer/rig/cli/flags.rb +13 -1
- data/lib/riffer/rig/cli.rb +38 -7
- data/lib/riffer/rig/loader.rb +149 -25
- data/lib/riffer/rig/runtime.rb +14 -0
- data/lib/riffer/rig/settings/document.rb +3 -1
- data/lib/riffer/rig/stores/jsonl.rb +14 -1
- data/lib/riffer/rig/stores/recorder.rb +8 -3
- data/lib/riffer/rig/version.rb +1 -1
- data/sig/generated/riffer/rig/cli/flags.rbs +8 -2
- data/sig/generated/riffer/rig/cli.rbs +15 -0
- data/sig/generated/riffer/rig/loader.rbs +68 -10
- data/sig/generated/riffer/rig/runtime.rbs +11 -2
- data/sig/generated/riffer/rig/settings/document.rbs +3 -1
- data/sig/generated/riffer/rig/stores/jsonl.rbs +7 -2
- data/sig/generated/riffer/rig/stores/recorder.rbs +7 -2
- data/sig/manual/riffer/rig/stores.rbs +1 -0
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0bc260756e56f8ed0d89556ef071a3ecb1922b637eb5b10cc0268416e0ceb26d
|
|
4
|
+
data.tar.gz: b7e6d6314a893ff23f289336b5e3df25f11e032f060272d7656695f1e96e584d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: edda5ec3d1675966361db52a009328703f0f62340d8d18ea3f5e689f3e9194762fec33804072989f1cb92d357038e03ca9481e7ab9f34eb123964f3f025b8749
|
|
7
|
+
data.tar.gz: c1953f30afa3a7871291852d3aaeba52a256006732582c82f71ad3245234f1e2a974524b174a801df81096af6effc37186e3a10e11ace5f5be644edbe4ed1c49
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.10.0](https://github.com/bottrall/riffer-rig/compare/v0.9.0...v0.10.0) (2026-10-05)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* continue and resume sessions with -c and -r ([#220](https://github.com/bottrall/riffer-rig/issues/220)) ([e43d3ac](https://github.com/bottrall/riffer-rig/commit/e43d3ac44678e56016791545ea241779569687c9))
|
|
9
|
+
* reload automatically before each request ([#222](https://github.com/bottrall/riffer-rig/issues/222)) ([fe96aa4](https://github.com/bottrall/riffer-rig/commit/fe96aa4476b0765e58824a8eb59301f0986e397c))
|
|
10
|
+
|
|
3
11
|
## [0.9.0](https://github.com/bottrall/riffer-rig/compare/v0.8.0...v0.9.0) (2026-10-04)
|
|
4
12
|
|
|
5
13
|
|
data/README.md
CHANGED
|
@@ -47,6 +47,8 @@ The model is the first of these that is set: `--model`, `RIFFER_MODEL`, `model`
|
|
|
47
47
|
| `--tools read,bash` | only these tools, by identifier ([Tools](docs/TOOLS.md)) |
|
|
48
48
|
| `--max-steps N` | stops a turn after `N` model calls |
|
|
49
49
|
| `--no-save` | does not save this session to the [store](docs/SESSIONS.md) |
|
|
50
|
+
| `-c`, `--continue` | continues the most recent session in this directory ([Sessions](docs/SESSIONS.md#resuming)) |
|
|
51
|
+
| `-r ID`, `--resume ID` | resumes the session with id `ID` ([Sessions](docs/SESSIONS.md#resuming)) |
|
|
50
52
|
| `-h`, `--help` | prints the usage |
|
|
51
53
|
|
|
52
54
|
`riffer -p` (one prompt, no terminal) and `riffer acp` (an editor's agent over stdio) are reserved: for now each prints the usage and exits with status 2.
|
data/lib/riffer/rig/cli/flags.rb
CHANGED
|
@@ -9,12 +9,14 @@ class Riffer::Rig::CLI::Flags
|
|
|
9
9
|
riffer acp (not available yet)
|
|
10
10
|
TEXT
|
|
11
11
|
|
|
12
|
-
# @dynamic model, extensions, skills, agents_md, tools, max_steps, help, save
|
|
12
|
+
# @dynamic model, extensions, skills, agents_md, tools, max_steps, help, save, continue, resume
|
|
13
13
|
attr_reader :model #: String?
|
|
14
14
|
attr_reader :extensions, :skills, :agents_md, :help #: bool
|
|
15
15
|
attr_reader :tools #: Array[String]?
|
|
16
16
|
attr_reader :max_steps #: Integer?
|
|
17
17
|
attr_reader :save #: bool
|
|
18
|
+
attr_reader :continue #: bool
|
|
19
|
+
attr_reader :resume #: String?
|
|
18
20
|
|
|
19
21
|
# @rbs argv: Array[String]
|
|
20
22
|
# @rbs return: Riffer::Rig::CLI::Flags | String
|
|
@@ -31,6 +33,8 @@ class Riffer::Rig::CLI::Flags
|
|
|
31
33
|
tools: values[:tools],
|
|
32
34
|
max_steps: values[:'max-steps'],
|
|
33
35
|
save: !values.key?(:'no-save'),
|
|
36
|
+
continue: values.key?(:continue),
|
|
37
|
+
resume: values[:resume],
|
|
34
38
|
help: values.key?(:help)
|
|
35
39
|
)
|
|
36
40
|
rescue OptionParser::ParseError => e
|
|
@@ -54,6 +58,8 @@ class Riffer::Rig::CLI::Flags
|
|
|
54
58
|
parser.on('--tools NAME,NAME', Array, 'Only these tools, by identifier')
|
|
55
59
|
parser.on('--max-steps N', Integer, 'Stop a turn after N model calls')
|
|
56
60
|
parser.on('--no-save', 'Do not save this session')
|
|
61
|
+
parser.on('-c', '--continue', 'Continue the most recent session in this directory')
|
|
62
|
+
parser.on('-r ID', '--resume ID', String, 'Resume the session with this id')
|
|
57
63
|
parser.on('-h', '--help', 'Show this help')
|
|
58
64
|
end
|
|
59
65
|
end
|
|
@@ -66,6 +72,8 @@ class Riffer::Rig::CLI::Flags
|
|
|
66
72
|
# @rbs tools: Array[String]?
|
|
67
73
|
# @rbs max_steps: Integer?
|
|
68
74
|
# @rbs save: bool
|
|
75
|
+
# @rbs continue: bool
|
|
76
|
+
# @rbs resume: String?
|
|
69
77
|
# @rbs help: bool
|
|
70
78
|
# @rbs return: void
|
|
71
79
|
def initialize(
|
|
@@ -76,6 +84,8 @@ class Riffer::Rig::CLI::Flags
|
|
|
76
84
|
tools: nil,
|
|
77
85
|
max_steps: nil,
|
|
78
86
|
save: true,
|
|
87
|
+
continue: false,
|
|
88
|
+
resume: nil,
|
|
79
89
|
help: false
|
|
80
90
|
)
|
|
81
91
|
@model = model
|
|
@@ -85,6 +95,8 @@ class Riffer::Rig::CLI::Flags
|
|
|
85
95
|
@tools = tools&.freeze
|
|
86
96
|
@max_steps = max_steps
|
|
87
97
|
@save = save
|
|
98
|
+
@continue = continue
|
|
99
|
+
@resume = resume
|
|
88
100
|
@help = help
|
|
89
101
|
freeze
|
|
90
102
|
end
|
data/lib/riffer/rig/cli.rb
CHANGED
|
@@ -52,22 +52,53 @@ module Riffer::Rig::CLI
|
|
|
52
52
|
version: Riffer::Rig::VERSION,
|
|
53
53
|
no_color: env.is_a?(Riffer::Rig::Env) && env.no_color
|
|
54
54
|
).run do |host|
|
|
55
|
-
Riffer::Rig::Loader.
|
|
55
|
+
loader = Riffer::Rig::Loader.new(
|
|
56
56
|
cwd: cwd,
|
|
57
57
|
host: host,
|
|
58
58
|
env: env,
|
|
59
59
|
home: home,
|
|
60
|
-
model: flags.model,
|
|
61
|
-
extensions: flags.extensions,
|
|
62
|
-
skills: flags.skills,
|
|
63
|
-
agents_md: flags.agents_md,
|
|
64
|
-
tools: flags.tools,
|
|
65
|
-
max_steps: flags.max_steps,
|
|
66
60
|
store: flags.save ? Riffer::Rig::Stores::JSONL.new : nil
|
|
67
61
|
)
|
|
62
|
+
resumed(flags, host, loader) || build(loader, flags)
|
|
68
63
|
end
|
|
69
64
|
end
|
|
70
65
|
|
|
66
|
+
# @rbs flags: Riffer::Rig::CLI::Flags
|
|
67
|
+
# @rbs host: Riffer::Rig::Hosts::_Host
|
|
68
|
+
# @rbs loader: Riffer::Rig::Loader
|
|
69
|
+
# @rbs return: Riffer::Rig::Runtime?
|
|
70
|
+
def resumed(flags, host, loader)
|
|
71
|
+
if flags.resume
|
|
72
|
+
started = loader.resume(flags.resume, **keywords(flags))
|
|
73
|
+
host.notify("No saved session #{flags.resume}.") unless started
|
|
74
|
+
started
|
|
75
|
+
elsif flags.continue
|
|
76
|
+
started = loader.continue(**keywords(flags))
|
|
77
|
+
host.notify('No saved session in this directory.') unless started
|
|
78
|
+
started
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @rbs loader: Riffer::Rig::Loader
|
|
83
|
+
# @rbs flags: Riffer::Rig::CLI::Flags
|
|
84
|
+
# @rbs return: Riffer::Rig::Runtime
|
|
85
|
+
def build(loader, flags)
|
|
86
|
+
loader.runtime(**keywords(flags))
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# @rbs flags: Riffer::Rig::CLI::Flags
|
|
90
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
91
|
+
def keywords(flags)
|
|
92
|
+
{
|
|
93
|
+
model: flags.model,
|
|
94
|
+
extensions: flags.extensions,
|
|
95
|
+
skills: flags.skills,
|
|
96
|
+
agents_md: flags.agents_md,
|
|
97
|
+
tools: flags.tools,
|
|
98
|
+
max_steps: flags.max_steps
|
|
99
|
+
}
|
|
100
|
+
end
|
|
101
|
+
|
|
71
102
|
# @rbs name: String
|
|
72
103
|
# @rbs error: IO
|
|
73
104
|
# @rbs return: Integer
|
data/lib/riffer/rig/loader.rb
CHANGED
|
@@ -21,6 +21,7 @@ class Riffer::Rig::Loader
|
|
|
21
21
|
# @rbs @extensions_enabled: bool
|
|
22
22
|
# @rbs @gem_extensions: Array[Riffer::Rig::Extension]
|
|
23
23
|
# @rbs @file_state: Hash[String, Time?]?
|
|
24
|
+
# @rbs @reload_mode: (:auto | :manual | nil)
|
|
24
25
|
|
|
25
26
|
# @dynamic tracked_files
|
|
26
27
|
attr_reader :tracked_files #: Array[String]
|
|
@@ -37,6 +38,8 @@ class Riffer::Rig::Loader
|
|
|
37
38
|
# @rbs tools: Array[String]?
|
|
38
39
|
# @rbs max_steps: Integer?
|
|
39
40
|
# @rbs store: Riffer::Rig::Stores::_Store | nil
|
|
41
|
+
# @rbs reload: (:auto | :manual | nil)
|
|
42
|
+
# @rbs snapshot: Hash[Symbol, untyped]?
|
|
40
43
|
# @rbs return: Riffer::Rig::Runtime
|
|
41
44
|
def self.runtime(
|
|
42
45
|
cwd:,
|
|
@@ -50,10 +53,20 @@ class Riffer::Rig::Loader
|
|
|
50
53
|
agents_md: true,
|
|
51
54
|
tools: nil,
|
|
52
55
|
max_steps: Riffer::Rig::Runtime::DEFAULT_MAX_STEPS,
|
|
53
|
-
store: Riffer::Rig::Stores::JSONL.new
|
|
56
|
+
store: Riffer::Rig::Stores::JSONL.new,
|
|
57
|
+
reload: nil,
|
|
58
|
+
snapshot: nil
|
|
54
59
|
)
|
|
55
|
-
new(cwd:, host:, env:, home:, riffer_config:, store:)
|
|
56
|
-
|
|
60
|
+
new(cwd:, host:, env:, home:, riffer_config:, store:).runtime(
|
|
61
|
+
model:,
|
|
62
|
+
extensions:,
|
|
63
|
+
skills:,
|
|
64
|
+
agents_md:,
|
|
65
|
+
tools:,
|
|
66
|
+
max_steps:,
|
|
67
|
+
reload:,
|
|
68
|
+
snapshot:
|
|
69
|
+
)
|
|
57
70
|
end
|
|
58
71
|
|
|
59
72
|
# @rbs cwd: String
|
|
@@ -84,6 +97,7 @@ class Riffer::Rig::Loader
|
|
|
84
97
|
@extensions_enabled = true
|
|
85
98
|
@gem_extensions = []
|
|
86
99
|
@file_state = nil
|
|
100
|
+
@reload_mode = nil
|
|
87
101
|
end
|
|
88
102
|
|
|
89
103
|
# @rbs model: String?
|
|
@@ -92,6 +106,8 @@ class Riffer::Rig::Loader
|
|
|
92
106
|
# @rbs agents_md: bool
|
|
93
107
|
# @rbs tools: Array[String]?
|
|
94
108
|
# @rbs max_steps: Integer?
|
|
109
|
+
# @rbs reload: (:auto | :manual | nil)
|
|
110
|
+
# @rbs snapshot: Hash[Symbol, untyped]?
|
|
95
111
|
# @rbs return: Riffer::Rig::Runtime
|
|
96
112
|
def runtime(
|
|
97
113
|
model: nil,
|
|
@@ -99,7 +115,9 @@ class Riffer::Rig::Loader
|
|
|
99
115
|
skills: true,
|
|
100
116
|
agents_md: true,
|
|
101
117
|
tools: nil,
|
|
102
|
-
max_steps: Riffer::Rig::Runtime::DEFAULT_MAX_STEPS
|
|
118
|
+
max_steps: Riffer::Rig::Runtime::DEFAULT_MAX_STEPS,
|
|
119
|
+
reload: nil,
|
|
120
|
+
snapshot: nil
|
|
103
121
|
)
|
|
104
122
|
settings = merged_settings
|
|
105
123
|
document = Riffer::Rig::Settings::Document.new(settings)
|
|
@@ -108,13 +126,14 @@ class Riffer::Rig::Loader
|
|
|
108
126
|
ensure_sdk(provider)
|
|
109
127
|
credentials = { provider.to_sym => credentials_for(provider) }
|
|
110
128
|
@extensions_enabled = extensions
|
|
129
|
+
@reload_mode = reload
|
|
111
130
|
@stripped = { skills: skills, agents_md: agents_md }.reject { |_name, kept| kept }.keys
|
|
112
131
|
loaded = Riffer::Rig::Bundled::BY_NAME.except(*document.disabled.map(&:to_sym), *@stripped).values
|
|
113
132
|
@tracked_files = []
|
|
114
133
|
@gem_extensions = []
|
|
115
134
|
loaded += load_rig_files(document.autoload) if extensions
|
|
116
135
|
|
|
117
|
-
recorder = recorder_for(document)
|
|
136
|
+
recorder = recorder_for(document, resumed: !snapshot.nil?)
|
|
118
137
|
loaded << Riffer::Rig::Stores::Recorder.extension(recorder) if recorder
|
|
119
138
|
|
|
120
139
|
built = Riffer::Rig::Runtime.new(
|
|
@@ -129,25 +148,99 @@ class Riffer::Rig::Loader
|
|
|
129
148
|
pricing: document.models,
|
|
130
149
|
riffer_config: @riffer_config,
|
|
131
150
|
model_options: Riffer::Rig::Settings.model_options(selected, document.reasoning),
|
|
132
|
-
native_tools: document.native_tools
|
|
151
|
+
native_tools: document.native_tools,
|
|
152
|
+
snapshot: snapshot
|
|
133
153
|
)
|
|
134
154
|
built.install_command(Riffer::Rig::Commands::Reload.command(self))
|
|
155
|
+
built.install_reload_check(->(runtime) { automatic_reload(runtime) }) unless reload_mode(document) == :manual
|
|
135
156
|
@file_state = file_state
|
|
136
157
|
recorder&.attach(built)
|
|
137
158
|
built
|
|
138
159
|
end
|
|
139
160
|
|
|
161
|
+
# @rbs model: String?
|
|
162
|
+
# @rbs extensions: bool
|
|
163
|
+
# @rbs skills: bool
|
|
164
|
+
# @rbs agents_md: bool
|
|
165
|
+
# @rbs tools: Array[String]?
|
|
166
|
+
# @rbs max_steps: Integer?
|
|
167
|
+
# @rbs return: Riffer::Rig::Runtime?
|
|
168
|
+
def continue(
|
|
169
|
+
model: nil,
|
|
170
|
+
extensions: true,
|
|
171
|
+
skills: true,
|
|
172
|
+
agents_md: true,
|
|
173
|
+
tools: nil,
|
|
174
|
+
max_steps: Riffer::Rig::Runtime::DEFAULT_MAX_STEPS
|
|
175
|
+
)
|
|
176
|
+
store = @store
|
|
177
|
+
return nil unless store
|
|
178
|
+
|
|
179
|
+
latest = store.list(cwd: @cwd).max_by { |header| store.updated(header.fetch(:id)) || Time.at(0) }
|
|
180
|
+
return nil unless latest
|
|
181
|
+
|
|
182
|
+
id = latest.fetch(:id) #: String
|
|
183
|
+
resume(id, model:, extensions:, skills:, agents_md:, tools:, max_steps:)
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# A resume carries the history over and nothing else: the settings,
|
|
187
|
+
# credentials, extensions and tools are today's.
|
|
188
|
+
# @rbs id: String
|
|
189
|
+
# @rbs model: String?
|
|
190
|
+
# @rbs extensions: bool
|
|
191
|
+
# @rbs skills: bool
|
|
192
|
+
# @rbs agents_md: bool
|
|
193
|
+
# @rbs tools: Array[String]?
|
|
194
|
+
# @rbs max_steps: Integer?
|
|
195
|
+
# @rbs return: Riffer::Rig::Runtime?
|
|
196
|
+
def resume(
|
|
197
|
+
id,
|
|
198
|
+
model: nil,
|
|
199
|
+
extensions: true,
|
|
200
|
+
skills: true,
|
|
201
|
+
agents_md: true,
|
|
202
|
+
tools: nil,
|
|
203
|
+
max_steps: Riffer::Rig::Runtime::DEFAULT_MAX_STEPS
|
|
204
|
+
)
|
|
205
|
+
store = @store
|
|
206
|
+
return nil unless store
|
|
207
|
+
|
|
208
|
+
entries = store.read(id)
|
|
209
|
+
return nil if entries.empty?
|
|
210
|
+
|
|
211
|
+
runtime(model:, extensions:, skills:, agents_md:, tools:, max_steps:, snapshot: snapshot_of(entries, id))
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
# @rbs all: bool
|
|
215
|
+
# @rbs return: Array[Hash[Symbol, untyped]]
|
|
216
|
+
def list(all: false)
|
|
217
|
+
store = @store
|
|
218
|
+
return [] unless store
|
|
219
|
+
|
|
220
|
+
all ? store.list : store.list(cwd: @cwd)
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
# @rbs id: String
|
|
224
|
+
# @rbs return: void
|
|
225
|
+
def delete(id)
|
|
226
|
+
store = @store
|
|
227
|
+
return unless store
|
|
228
|
+
|
|
229
|
+
store.delete(id)
|
|
230
|
+
end
|
|
231
|
+
|
|
140
232
|
# Without force, a no-op while the tracked file set and the two rig.rb paths
|
|
141
|
-
# look as the last discovery left them
|
|
142
|
-
# trigger (#133) calls. The snapshot advances
|
|
143
|
-
# included, so a failure does not retry every
|
|
144
|
-
# once and reported as the message, the way
|
|
145
|
-
# rejection; the Runtime is the success value.
|
|
233
|
+
# look as the last discovery left them and the reload mode is not manual —
|
|
234
|
+
# the entry point the automatic trigger (#133) calls. The snapshot advances
|
|
235
|
+
# on every attempt, failed ones included, so a failure does not retry every
|
|
236
|
+
# request. Failures are notified once and reported as the message, the way
|
|
237
|
+
# Settings.rejection reports a rejection; the Runtime is the success value.
|
|
146
238
|
# @rbs runtime: Riffer::Rig::Runtime
|
|
147
239
|
# @rbs force: bool
|
|
148
240
|
# @rbs return: (Riffer::Rig::Runtime | String)
|
|
149
241
|
def reload(runtime, force: false)
|
|
150
242
|
return runtime if !force && file_state == @file_state
|
|
243
|
+
return runtime if !force && manual_reload?
|
|
151
244
|
|
|
152
245
|
outcome = perform_reload(runtime)
|
|
153
246
|
@file_state = file_state
|
|
@@ -156,6 +249,27 @@ class Riffer::Rig::Loader
|
|
|
156
249
|
|
|
157
250
|
private
|
|
158
251
|
|
|
252
|
+
# The reload mode a build runs under: the keyword wins over the setting,
|
|
253
|
+
# the setting over the default.
|
|
254
|
+
# @rbs document: Riffer::Rig::Settings::Document
|
|
255
|
+
# @rbs return: (:auto | :manual)
|
|
256
|
+
def reload_mode(document)
|
|
257
|
+
@reload_mode || document.reload
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# @rbs return: bool
|
|
261
|
+
def manual_reload?
|
|
262
|
+
reload_mode(Riffer::Rig::Settings::Document.new(merged_settings)) == :manual
|
|
263
|
+
end
|
|
264
|
+
|
|
265
|
+
# The automatic trigger: runs at every before_request boundary, so the
|
|
266
|
+
# change check decides and a no-op costs the stat alone.
|
|
267
|
+
# @rbs runtime: Riffer::Rig::Runtime
|
|
268
|
+
# @rbs return: void
|
|
269
|
+
def automatic_reload(runtime)
|
|
270
|
+
_ = reload(runtime)
|
|
271
|
+
end
|
|
272
|
+
|
|
159
273
|
# @rbs return: Hash[Symbol, untyped]
|
|
160
274
|
def merged_settings
|
|
161
275
|
Riffer::Rig::Settings.merge(
|
|
@@ -164,28 +278,24 @@ class Riffer::Rig::Loader
|
|
|
164
278
|
)
|
|
165
279
|
end
|
|
166
280
|
|
|
167
|
-
# Notifies every failure once and reports its message
|
|
168
|
-
# the
|
|
281
|
+
# Notifies every failure once and reports its message, unexpected raises
|
|
282
|
+
# included — the automatic check runs mid-turn with no other isolation, and a
|
|
283
|
+
# raised attempt must still advance the snapshot in reload. A rebuilt Runtime
|
|
284
|
+
# is the success value.
|
|
169
285
|
# @rbs runtime: Riffer::Rig::Runtime
|
|
170
286
|
# @rbs return: (Riffer::Rig::Runtime | String)
|
|
171
287
|
def perform_reload(runtime)
|
|
172
288
|
settings = merged_settings
|
|
173
289
|
document = Riffer::Rig::Settings::Document.new(settings)
|
|
174
|
-
|
|
175
|
-
extensions = reload_extensions(document, settings)
|
|
176
|
-
rescue AbandonedError => e
|
|
177
|
-
return notify_failure(e.message)
|
|
178
|
-
end
|
|
290
|
+
extensions = reload_extensions(document, settings)
|
|
179
291
|
|
|
180
292
|
failure = reload_credentials(runtime)
|
|
181
293
|
return notify_failure(failure) if failure
|
|
182
294
|
|
|
183
|
-
|
|
184
|
-
runtime.rebuild(extensions: validated(extensions, settings), settings: settings)
|
|
185
|
-
rescue StandardError => e
|
|
186
|
-
return notify_failure(e.message)
|
|
187
|
-
end
|
|
295
|
+
runtime.rebuild(extensions: validated(extensions, settings), settings: settings)
|
|
188
296
|
runtime
|
|
297
|
+
rescue StandardError => e
|
|
298
|
+
notify_failure(e.message)
|
|
189
299
|
end
|
|
190
300
|
|
|
191
301
|
# @rbs message: String
|
|
@@ -453,15 +563,29 @@ class Riffer::Rig::Loader
|
|
|
453
563
|
File.join(@home, '.riffer', 'settings.json')
|
|
454
564
|
end
|
|
455
565
|
|
|
566
|
+
# @rbs entries: Array[Hash[Symbol, untyped]]
|
|
567
|
+
# @rbs id: String
|
|
568
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
569
|
+
def snapshot_of(entries, id)
|
|
570
|
+
models = entries.select { |entry| entry[:type] == 'model' }
|
|
571
|
+
{
|
|
572
|
+
id: id,
|
|
573
|
+
messages: entries.select { |entry| entry[:type] == 'message' }.map { |entry| entry.fetch(:message) },
|
|
574
|
+
model: models.last&.fetch(:model),
|
|
575
|
+
skills: entries.select { |entry| entry[:type] == 'skill' }.map { |entry| entry.fetch(:skill) }
|
|
576
|
+
}
|
|
577
|
+
end
|
|
578
|
+
|
|
456
579
|
# The store records unless it was declined with store: nil or globally with
|
|
457
580
|
# "sessions": {"save": false}.
|
|
458
581
|
# @rbs document: Riffer::Rig::Settings::Document
|
|
582
|
+
# @rbs resumed: bool
|
|
459
583
|
# @rbs return: Riffer::Rig::Stores::Recorder?
|
|
460
|
-
def recorder_for(document)
|
|
584
|
+
def recorder_for(document, resumed:)
|
|
461
585
|
store = @store
|
|
462
586
|
return nil unless document.save
|
|
463
587
|
return nil unless store
|
|
464
588
|
|
|
465
|
-
Riffer::Rig::Stores::Recorder.new(store: store)
|
|
589
|
+
Riffer::Rig::Stores::Recorder.new(store: store, resumed: resumed)
|
|
466
590
|
end
|
|
467
591
|
end
|
data/lib/riffer/rig/runtime.rb
CHANGED
|
@@ -66,6 +66,7 @@ class Riffer::Rig::Runtime
|
|
|
66
66
|
# @rbs @mcp_servers: Hash[String, Riffer::Rig::Mcp::Server]
|
|
67
67
|
# @rbs @model_options: Hash[Symbol, untyped]
|
|
68
68
|
# @rbs @native_tools: Hash[Symbol, untyped]
|
|
69
|
+
# @rbs @reload_check: (^(::Riffer::Rig::Runtime) -> void)?
|
|
69
70
|
|
|
70
71
|
# @dynamic agent, credentials, cwd, host, id, settings, declared_settings
|
|
71
72
|
attr_reader :agent #: Riffer::Agent
|
|
@@ -122,6 +123,7 @@ class Riffer::Rig::Runtime
|
|
|
122
123
|
@claim_depth = 0
|
|
123
124
|
@claim_thread = nil
|
|
124
125
|
@core_commands = []
|
|
126
|
+
@reload_check = nil
|
|
125
127
|
@cancel_flag = Riffer::Rig::Runtime::CancelFlag.new
|
|
126
128
|
@session_start_pending = true
|
|
127
129
|
@session_start_reason = snapshot ? :restore : :new
|
|
@@ -187,6 +189,16 @@ class Riffer::Rig::Runtime
|
|
|
187
189
|
nil
|
|
188
190
|
end
|
|
189
191
|
|
|
192
|
+
# Installs a host-of-the-runtime check (the Loader's automatic reload) so it
|
|
193
|
+
# survives every rebuild. The Runtime calls it at both before_request
|
|
194
|
+
# boundaries, before any handler runs, and ignores the result.
|
|
195
|
+
# @rbs check: ^(::Riffer::Rig::Runtime) -> void
|
|
196
|
+
# @rbs return: nil
|
|
197
|
+
def install_reload_check(check)
|
|
198
|
+
@reload_check = check
|
|
199
|
+
nil
|
|
200
|
+
end
|
|
201
|
+
|
|
190
202
|
# @rbs name: String
|
|
191
203
|
# @rbs args: String
|
|
192
204
|
# @rbs &block: ?(::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event) -> void
|
|
@@ -448,6 +460,7 @@ class Riffer::Rig::Runtime
|
|
|
448
460
|
# @rbs return: Enumerator[Riffer::StreamEvents::Base, Riffer::Agent::Response]
|
|
449
461
|
def start_turn(text)
|
|
450
462
|
@cancel_flag.clear
|
|
463
|
+
@reload_check&.call(self)
|
|
451
464
|
refresh_system_message
|
|
452
465
|
prompt = @hooks.before_prompt(text)
|
|
453
466
|
return blocked_turn(prompt.reason) if prompt.is_a?(Riffer::Rig::Runtime::Blocked)
|
|
@@ -554,6 +567,7 @@ class Riffer::Rig::Runtime
|
|
|
554
567
|
|
|
555
568
|
# @rbs return: void
|
|
556
569
|
def before_next_request
|
|
570
|
+
@reload_check&.call(self)
|
|
557
571
|
messages = @agent.session.messages
|
|
558
572
|
# Upstream candidate: riffer has no hook between tool results and the next
|
|
559
573
|
# request, so the last tool result's on_message stands in for one.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
class Riffer::Rig::Settings::Document
|
|
4
|
-
# @dynamic model, reasoning, models, providers, disabled, autoload, native_tools, save
|
|
4
|
+
# @dynamic model, reasoning, models, providers, disabled, autoload, native_tools, save, reload
|
|
5
5
|
attr_reader :model #: String?
|
|
6
6
|
attr_reader :reasoning #: String?
|
|
7
7
|
attr_reader :models #: Hash[String, Riffer::Rig::Settings::Pricing]
|
|
@@ -10,6 +10,7 @@ class Riffer::Rig::Settings::Document
|
|
|
10
10
|
attr_reader :autoload #: bool
|
|
11
11
|
attr_reader :native_tools #: Hash[Symbol, bool | Hash[Symbol, untyped]]
|
|
12
12
|
attr_reader :save #: bool
|
|
13
|
+
attr_reader :reload #: (:auto | :manual)
|
|
13
14
|
|
|
14
15
|
# @rbs source: Hash[Symbol, untyped]
|
|
15
16
|
# @rbs return: void
|
|
@@ -31,6 +32,7 @@ class Riffer::Rig::Settings::Document
|
|
|
31
32
|
@disabled = names.freeze
|
|
32
33
|
@autoload = hash_or_empty(source[:extensions])[:autoload] == true
|
|
33
34
|
@save = hash_or_empty(source[:sessions])[:save] != false
|
|
35
|
+
@reload = source[:reload] == 'manual' ? :manual : :auto
|
|
34
36
|
@native_tools = hash_or_empty(hash_or_empty(source[:tools])[:native])
|
|
35
37
|
.select { |_tool, switch| switch == true || switch.is_a?(Hash) }
|
|
36
38
|
.transform_keys(&:to_sym).freeze
|
|
@@ -35,10 +35,23 @@ class Riffer::Rig::Stores::JSONL
|
|
|
35
35
|
# @rbs cwd: String?
|
|
36
36
|
# @rbs return: Array[Hash[Symbol, untyped]]
|
|
37
37
|
def list(cwd: nil)
|
|
38
|
-
headers = Dir.glob(File.join(root, '*', '*.jsonl')).filter_map
|
|
38
|
+
headers = Dir.glob(File.join(root, '*', '*.jsonl')).filter_map do |path|
|
|
39
|
+
# Seeding the path cache here keeps picking the most recently updated
|
|
40
|
+
# session one mtime per header instead of one glob.
|
|
41
|
+
@paths[File.basename(path, '.jsonl')] = path
|
|
42
|
+
header_of(path)
|
|
43
|
+
end
|
|
39
44
|
cwd ? headers.select { |header| header[:cwd] == cwd } : headers
|
|
40
45
|
end
|
|
41
46
|
|
|
47
|
+
# A session's last update is its file's mtime: appends are the only writes.
|
|
48
|
+
# @rbs id: String
|
|
49
|
+
# @rbs return: Time?
|
|
50
|
+
def updated(id)
|
|
51
|
+
path = find(id)
|
|
52
|
+
path ? File.mtime(path) : nil
|
|
53
|
+
end
|
|
54
|
+
|
|
42
55
|
# @rbs id: String
|
|
43
56
|
# @rbs return: void
|
|
44
57
|
def delete(id)
|
|
@@ -6,6 +6,7 @@ class Riffer::Rig::Stores::Recorder
|
|
|
6
6
|
EXTENSION_NAME = 'session-store' #: String
|
|
7
7
|
|
|
8
8
|
# @rbs @store: Riffer::Rig::Stores::_Store
|
|
9
|
+
# @rbs @resumed: bool
|
|
9
10
|
# @rbs @runtime: Riffer::Rig::Runtime?
|
|
10
11
|
# @rbs @header_written: bool
|
|
11
12
|
# @rbs @pending: Array[Hash[Symbol, untyped]]
|
|
@@ -14,11 +15,15 @@ class Riffer::Rig::Stores::Recorder
|
|
|
14
15
|
# Records one Loader-built session into the store: the header on the first
|
|
15
16
|
# message (its title needs the first prompt), one entry per message, one per
|
|
16
17
|
# model switch and one per skill activation. Entries that land before the
|
|
17
|
-
# first message (a /model before the first prompt) wait for the header.
|
|
18
|
+
# first message (a /model before the first prompt) wait for the header. A
|
|
19
|
+
# resumed session appends to the file its first run wrote, so it rewrites
|
|
20
|
+
# no header.
|
|
18
21
|
# @rbs store: Riffer::Rig::Stores::_Store
|
|
22
|
+
# @rbs resumed: bool
|
|
19
23
|
# @rbs return: void
|
|
20
|
-
def initialize(store:)
|
|
24
|
+
def initialize(store:, resumed: false)
|
|
21
25
|
@store = store
|
|
26
|
+
@resumed = resumed
|
|
22
27
|
@runtime = nil
|
|
23
28
|
@header_written = false
|
|
24
29
|
@pending = []
|
|
@@ -84,7 +89,7 @@ class Riffer::Rig::Stores::Recorder
|
|
|
84
89
|
# @rbs return: void
|
|
85
90
|
def write_header
|
|
86
91
|
@header_written = true
|
|
87
|
-
@store.append(runtime.id, header_entry)
|
|
92
|
+
@store.append(runtime.id, header_entry) unless @resumed
|
|
88
93
|
@pending.each { |entry| @store.append(runtime.id, entry) }
|
|
89
94
|
@pending.clear
|
|
90
95
|
end
|
data/lib/riffer/rig/version.rb
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
class Riffer::Rig::CLI::Flags
|
|
4
4
|
BANNER: String
|
|
5
5
|
|
|
6
|
-
# @dynamic model, extensions, skills, agents_md, tools, max_steps, help, save
|
|
6
|
+
# @dynamic model, extensions, skills, agents_md, tools, max_steps, help, save, continue, resume
|
|
7
7
|
attr_reader model: String?
|
|
8
8
|
|
|
9
9
|
attr_reader extensions: bool
|
|
@@ -20,6 +20,10 @@ class Riffer::Rig::CLI::Flags
|
|
|
20
20
|
|
|
21
21
|
attr_reader save: bool
|
|
22
22
|
|
|
23
|
+
attr_reader continue: bool
|
|
24
|
+
|
|
25
|
+
attr_reader resume: String?
|
|
26
|
+
|
|
23
27
|
# @rbs argv: Array[String]
|
|
24
28
|
# @rbs return: Riffer::Rig::CLI::Flags | String
|
|
25
29
|
def self.parse: (Array[String] argv) -> (Riffer::Rig::CLI::Flags | String)
|
|
@@ -37,7 +41,9 @@ class Riffer::Rig::CLI::Flags
|
|
|
37
41
|
# @rbs tools: Array[String]?
|
|
38
42
|
# @rbs max_steps: Integer?
|
|
39
43
|
# @rbs save: bool
|
|
44
|
+
# @rbs continue: bool
|
|
45
|
+
# @rbs resume: String?
|
|
40
46
|
# @rbs help: bool
|
|
41
47
|
# @rbs return: void
|
|
42
|
-
def initialize: (?model: String?, ?extensions: bool, ?skills: bool, ?agents_md: bool, ?tools: Array[String]?, ?max_steps: Integer?, ?save: bool, ?help: bool) -> void
|
|
48
|
+
def initialize: (?model: String?, ?extensions: bool, ?skills: bool, ?agents_md: bool, ?tools: Array[String]?, ?max_steps: Integer?, ?save: bool, ?continue: bool, ?resume: String?, ?help: bool) -> void
|
|
43
49
|
end
|
|
@@ -28,6 +28,21 @@ module Riffer::Rig::CLI
|
|
|
28
28
|
# @rbs return: Integer
|
|
29
29
|
def terminal: (Riffer::Rig::CLI::Flags flags, input: IO, output: IO, env: Riffer::Rig::Env | Riffer::Rig::Env::Invalid, cwd: String, home: String) -> Integer
|
|
30
30
|
|
|
31
|
+
# @rbs flags: Riffer::Rig::CLI::Flags
|
|
32
|
+
# @rbs host: Riffer::Rig::Hosts::_Host
|
|
33
|
+
# @rbs loader: Riffer::Rig::Loader
|
|
34
|
+
# @rbs return: Riffer::Rig::Runtime?
|
|
35
|
+
def resumed: (Riffer::Rig::CLI::Flags flags, Riffer::Rig::Hosts::_Host host, Riffer::Rig::Loader loader) -> Riffer::Rig::Runtime?
|
|
36
|
+
|
|
37
|
+
# @rbs loader: Riffer::Rig::Loader
|
|
38
|
+
# @rbs flags: Riffer::Rig::CLI::Flags
|
|
39
|
+
# @rbs return: Riffer::Rig::Runtime
|
|
40
|
+
def build: (Riffer::Rig::Loader loader, Riffer::Rig::CLI::Flags flags) -> Riffer::Rig::Runtime
|
|
41
|
+
|
|
42
|
+
# @rbs flags: Riffer::Rig::CLI::Flags
|
|
43
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
44
|
+
def keywords: (Riffer::Rig::CLI::Flags flags) -> Hash[Symbol, untyped]
|
|
45
|
+
|
|
31
46
|
# @rbs name: String
|
|
32
47
|
# @rbs error: IO
|
|
33
48
|
# @rbs return: Integer
|
|
@@ -10,6 +10,8 @@ class Riffer::Rig::Loader
|
|
|
10
10
|
|
|
11
11
|
EXTENSION_FEATURE: String
|
|
12
12
|
|
|
13
|
+
@reload_mode: :auto | :manual | nil
|
|
14
|
+
|
|
13
15
|
@file_state: Hash[String, Time?]?
|
|
14
16
|
|
|
15
17
|
@gem_extensions: Array[Riffer::Rig::Extension]
|
|
@@ -47,8 +49,10 @@ class Riffer::Rig::Loader
|
|
|
47
49
|
# @rbs tools: Array[String]?
|
|
48
50
|
# @rbs max_steps: Integer?
|
|
49
51
|
# @rbs store: Riffer::Rig::Stores::_Store | nil
|
|
52
|
+
# @rbs reload: (:auto | :manual | nil)
|
|
53
|
+
# @rbs snapshot: Hash[Symbol, untyped]?
|
|
50
54
|
# @rbs return: Riffer::Rig::Runtime
|
|
51
|
-
def self.runtime: (cwd: String, host: Riffer::Rig::Hosts::_Host, ?env: Riffer::Rig::Env | Riffer::Rig::Env::Invalid, ?home: String, ?riffer_config: Riffer::Config, ?model: String?, ?extensions: bool, ?skills: bool, ?agents_md: bool, ?tools: Array[String]?, ?max_steps: Integer?, ?store: Riffer::Rig::Stores::_Store | nil) -> Riffer::Rig::Runtime
|
|
55
|
+
def self.runtime: (cwd: String, host: Riffer::Rig::Hosts::_Host, ?env: Riffer::Rig::Env | Riffer::Rig::Env::Invalid, ?home: String, ?riffer_config: Riffer::Config, ?model: String?, ?extensions: bool, ?skills: bool, ?agents_md: bool, ?tools: Array[String]?, ?max_steps: Integer?, ?store: Riffer::Rig::Stores::_Store | nil, ?reload: :auto | :manual | nil, ?snapshot: Hash[Symbol, untyped]?) -> Riffer::Rig::Runtime
|
|
52
56
|
|
|
53
57
|
# @rbs cwd: String
|
|
54
58
|
# @rbs host: Riffer::Rig::Hosts::_Host
|
|
@@ -65,15 +69,46 @@ class Riffer::Rig::Loader
|
|
|
65
69
|
# @rbs agents_md: bool
|
|
66
70
|
# @rbs tools: Array[String]?
|
|
67
71
|
# @rbs max_steps: Integer?
|
|
72
|
+
# @rbs reload: (:auto | :manual | nil)
|
|
73
|
+
# @rbs snapshot: Hash[Symbol, untyped]?
|
|
68
74
|
# @rbs return: Riffer::Rig::Runtime
|
|
69
|
-
def runtime: (?model: String?, ?extensions: bool, ?skills: bool, ?agents_md: bool, ?tools: Array[String]?, ?max_steps: Integer?) -> Riffer::Rig::Runtime
|
|
75
|
+
def runtime: (?model: String?, ?extensions: bool, ?skills: bool, ?agents_md: bool, ?tools: Array[String]?, ?max_steps: Integer?, ?reload: :auto | :manual | nil, ?snapshot: Hash[Symbol, untyped]?) -> Riffer::Rig::Runtime
|
|
76
|
+
|
|
77
|
+
# @rbs model: String?
|
|
78
|
+
# @rbs extensions: bool
|
|
79
|
+
# @rbs skills: bool
|
|
80
|
+
# @rbs agents_md: bool
|
|
81
|
+
# @rbs tools: Array[String]?
|
|
82
|
+
# @rbs max_steps: Integer?
|
|
83
|
+
# @rbs return: Riffer::Rig::Runtime?
|
|
84
|
+
def continue: (?model: String?, ?extensions: bool, ?skills: bool, ?agents_md: bool, ?tools: Array[String]?, ?max_steps: Integer?) -> Riffer::Rig::Runtime?
|
|
85
|
+
|
|
86
|
+
# A resume carries the history over and nothing else: the settings,
|
|
87
|
+
# credentials, extensions and tools are today's.
|
|
88
|
+
# @rbs id: String
|
|
89
|
+
# @rbs model: String?
|
|
90
|
+
# @rbs extensions: bool
|
|
91
|
+
# @rbs skills: bool
|
|
92
|
+
# @rbs agents_md: bool
|
|
93
|
+
# @rbs tools: Array[String]?
|
|
94
|
+
# @rbs max_steps: Integer?
|
|
95
|
+
# @rbs return: Riffer::Rig::Runtime?
|
|
96
|
+
def resume: (String id, ?model: String?, ?extensions: bool, ?skills: bool, ?agents_md: bool, ?tools: Array[String]?, ?max_steps: Integer?) -> Riffer::Rig::Runtime?
|
|
97
|
+
|
|
98
|
+
# @rbs all: bool
|
|
99
|
+
# @rbs return: Array[Hash[Symbol, untyped]]
|
|
100
|
+
def list: (?all: bool) -> Array[Hash[Symbol, untyped]]
|
|
101
|
+
|
|
102
|
+
# @rbs id: String
|
|
103
|
+
# @rbs return: void
|
|
104
|
+
def delete: (String id) -> void
|
|
70
105
|
|
|
71
106
|
# Without force, a no-op while the tracked file set and the two rig.rb paths
|
|
72
|
-
# look as the last discovery left them
|
|
73
|
-
# trigger (#133) calls. The snapshot advances
|
|
74
|
-
# included, so a failure does not retry every
|
|
75
|
-
# once and reported as the message, the way
|
|
76
|
-
# rejection; the Runtime is the success value.
|
|
107
|
+
# look as the last discovery left them and the reload mode is not manual —
|
|
108
|
+
# the entry point the automatic trigger (#133) calls. The snapshot advances
|
|
109
|
+
# on every attempt, failed ones included, so a failure does not retry every
|
|
110
|
+
# request. Failures are notified once and reported as the message, the way
|
|
111
|
+
# Settings.rejection reports a rejection; the Runtime is the success value.
|
|
77
112
|
# @rbs runtime: Riffer::Rig::Runtime
|
|
78
113
|
# @rbs force: bool
|
|
79
114
|
# @rbs return: (Riffer::Rig::Runtime | String)
|
|
@@ -81,11 +116,28 @@ class Riffer::Rig::Loader
|
|
|
81
116
|
|
|
82
117
|
private
|
|
83
118
|
|
|
119
|
+
# The reload mode a build runs under: the keyword wins over the setting,
|
|
120
|
+
# the setting over the default.
|
|
121
|
+
# @rbs document: Riffer::Rig::Settings::Document
|
|
122
|
+
# @rbs return: (:auto | :manual)
|
|
123
|
+
def reload_mode: (Riffer::Rig::Settings::Document document) -> (:auto | :manual)
|
|
124
|
+
|
|
125
|
+
# @rbs return: bool
|
|
126
|
+
def manual_reload?: () -> bool
|
|
127
|
+
|
|
128
|
+
# The automatic trigger: runs at every before_request boundary, so the
|
|
129
|
+
# change check decides and a no-op costs the stat alone.
|
|
130
|
+
# @rbs runtime: Riffer::Rig::Runtime
|
|
131
|
+
# @rbs return: void
|
|
132
|
+
def automatic_reload: (Riffer::Rig::Runtime runtime) -> void
|
|
133
|
+
|
|
84
134
|
# @rbs return: Hash[Symbol, untyped]
|
|
85
135
|
def merged_settings: () -> Hash[Symbol, untyped]
|
|
86
136
|
|
|
87
|
-
# Notifies every failure once and reports its message
|
|
88
|
-
# the
|
|
137
|
+
# Notifies every failure once and reports its message, unexpected raises
|
|
138
|
+
# included — the automatic check runs mid-turn with no other isolation, and a
|
|
139
|
+
# raised attempt must still advance the snapshot in reload. A rebuilt Runtime
|
|
140
|
+
# is the success value.
|
|
89
141
|
# @rbs runtime: Riffer::Rig::Runtime
|
|
90
142
|
# @rbs return: (Riffer::Rig::Runtime | String)
|
|
91
143
|
def perform_reload: (Riffer::Rig::Runtime runtime) -> (Riffer::Rig::Runtime | String)
|
|
@@ -206,9 +258,15 @@ class Riffer::Rig::Loader
|
|
|
206
258
|
# @rbs return: String
|
|
207
259
|
def home_settings_path: () -> String
|
|
208
260
|
|
|
261
|
+
# @rbs entries: Array[Hash[Symbol, untyped]]
|
|
262
|
+
# @rbs id: String
|
|
263
|
+
# @rbs return: Hash[Symbol, untyped]
|
|
264
|
+
def snapshot_of: (Array[Hash[Symbol, untyped]] entries, String id) -> Hash[Symbol, untyped]
|
|
265
|
+
|
|
209
266
|
# The store records unless it was declined with store: nil or globally with
|
|
210
267
|
# "sessions": {"save": false}.
|
|
211
268
|
# @rbs document: Riffer::Rig::Settings::Document
|
|
269
|
+
# @rbs resumed: bool
|
|
212
270
|
# @rbs return: Riffer::Rig::Stores::Recorder?
|
|
213
|
-
def recorder_for: (Riffer::Rig::Settings::Document document) -> Riffer::Rig::Stores::Recorder?
|
|
271
|
+
def recorder_for: (Riffer::Rig::Settings::Document document, resumed: bool) -> Riffer::Rig::Stores::Recorder?
|
|
214
272
|
end
|
|
@@ -26,6 +26,8 @@ class Riffer::Rig::Runtime
|
|
|
26
26
|
# tests and embedders can exercise the switches.
|
|
27
27
|
NATIVE_TOOLS_BY_PROVIDER: Hash[Symbol, Hash[Symbol, bool]]
|
|
28
28
|
|
|
29
|
+
@model_observers: Array[^(String) -> void]
|
|
30
|
+
|
|
29
31
|
@message_observers: Array[^(Riffer::Messages::Base) -> void]
|
|
30
32
|
|
|
31
33
|
@model_override: String?
|
|
@@ -54,8 +56,6 @@ class Riffer::Rig::Runtime
|
|
|
54
56
|
|
|
55
57
|
@agent: Riffer::Agent
|
|
56
58
|
|
|
57
|
-
@model_observers: Array[^(String) -> void]
|
|
58
|
-
|
|
59
59
|
@prompts: Hash[Symbol, ^(Riffer::Rig::Runtime) -> String?]
|
|
60
60
|
|
|
61
61
|
@commands: Hash[String, Riffer::Rig::Command]
|
|
@@ -82,6 +82,8 @@ class Riffer::Rig::Runtime
|
|
|
82
82
|
|
|
83
83
|
@native_tools: Hash[Symbol, untyped]
|
|
84
84
|
|
|
85
|
+
@reload_check: (^(::Riffer::Rig::Runtime) -> void)?
|
|
86
|
+
|
|
85
87
|
# @dynamic agent, credentials, cwd, host, id, settings, declared_settings
|
|
86
88
|
attr_reader agent: Riffer::Agent
|
|
87
89
|
|
|
@@ -134,6 +136,13 @@ class Riffer::Rig::Runtime
|
|
|
134
136
|
# @rbs return: nil
|
|
135
137
|
def install_command: (Riffer::Rig::Command command) -> nil
|
|
136
138
|
|
|
139
|
+
# Installs a host-of-the-runtime check (the Loader's automatic reload) so it
|
|
140
|
+
# survives every rebuild. The Runtime calls it at both before_request
|
|
141
|
+
# boundaries, before any handler runs, and ignores the result.
|
|
142
|
+
# @rbs check: ^(::Riffer::Rig::Runtime) -> void
|
|
143
|
+
# @rbs return: nil
|
|
144
|
+
def install_reload_check: (^(::Riffer::Rig::Runtime) -> void check) -> nil
|
|
145
|
+
|
|
137
146
|
# @rbs name: String
|
|
138
147
|
# @rbs args: String
|
|
139
148
|
# @rbs &block: ?(::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event) -> void
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# Generated from lib/riffer/rig/settings/document.rb with RBS::Inline
|
|
2
2
|
|
|
3
3
|
class Riffer::Rig::Settings::Document
|
|
4
|
-
# @dynamic model, reasoning, models, providers, disabled, autoload, native_tools, save
|
|
4
|
+
# @dynamic model, reasoning, models, providers, disabled, autoload, native_tools, save, reload
|
|
5
5
|
attr_reader model: String?
|
|
6
6
|
|
|
7
7
|
attr_reader reasoning: String?
|
|
@@ -18,6 +18,8 @@ class Riffer::Rig::Settings::Document
|
|
|
18
18
|
|
|
19
19
|
attr_reader save: bool
|
|
20
20
|
|
|
21
|
+
attr_reader reload: :auto | :manual
|
|
22
|
+
|
|
21
23
|
# @rbs source: Hash[Symbol, untyped]
|
|
22
24
|
# @rbs return: void
|
|
23
25
|
def initialize: (Hash[Symbol, untyped] source) -> void
|
|
@@ -3,10 +3,10 @@
|
|
|
3
3
|
class Riffer::Rig::Stores::JSONL
|
|
4
4
|
HEADER_VERSION: Integer
|
|
5
5
|
|
|
6
|
-
@home: String
|
|
7
|
-
|
|
8
6
|
@paths: Hash[String, String]
|
|
9
7
|
|
|
8
|
+
@home: String
|
|
9
|
+
|
|
10
10
|
# @rbs home: String
|
|
11
11
|
# @rbs return: void
|
|
12
12
|
def initialize: (?home: String) -> void
|
|
@@ -24,6 +24,11 @@ class Riffer::Rig::Stores::JSONL
|
|
|
24
24
|
# @rbs return: Array[Hash[Symbol, untyped]]
|
|
25
25
|
def list: (?cwd: String?) -> Array[Hash[Symbol, untyped]]
|
|
26
26
|
|
|
27
|
+
# A session's last update is its file's mtime: appends are the only writes.
|
|
28
|
+
# @rbs id: String
|
|
29
|
+
# @rbs return: Time?
|
|
30
|
+
def updated: (String id) -> Time?
|
|
31
|
+
|
|
27
32
|
# @rbs id: String
|
|
28
33
|
# @rbs return: void
|
|
29
34
|
def delete: (String id) -> void
|
|
@@ -11,15 +11,20 @@ class Riffer::Rig::Stores::Recorder
|
|
|
11
11
|
|
|
12
12
|
@runtime: Riffer::Rig::Runtime?
|
|
13
13
|
|
|
14
|
+
@resumed: bool
|
|
15
|
+
|
|
14
16
|
@store: Riffer::Rig::Stores::_Store
|
|
15
17
|
|
|
16
18
|
# Records one Loader-built session into the store: the header on the first
|
|
17
19
|
# message (its title needs the first prompt), one entry per message, one per
|
|
18
20
|
# model switch and one per skill activation. Entries that land before the
|
|
19
|
-
# first message (a /model before the first prompt) wait for the header.
|
|
21
|
+
# first message (a /model before the first prompt) wait for the header. A
|
|
22
|
+
# resumed session appends to the file its first run wrote, so it rewrites
|
|
23
|
+
# no header.
|
|
20
24
|
# @rbs store: Riffer::Rig::Stores::_Store
|
|
25
|
+
# @rbs resumed: bool
|
|
21
26
|
# @rbs return: void
|
|
22
|
-
def initialize: (store: Riffer::Rig::Stores::_Store) -> void
|
|
27
|
+
def initialize: (store: Riffer::Rig::Stores::_Store, ?resumed: bool) -> void
|
|
23
28
|
|
|
24
29
|
# @rbs runtime: Riffer::Rig::Runtime
|
|
25
30
|
# @rbs return: void
|
|
@@ -6,6 +6,7 @@ module Riffer::Rig::Stores
|
|
|
6
6
|
def append: (String id, Hash[Symbol, untyped] entry) -> void
|
|
7
7
|
def read: (String id) -> Array[Hash[Symbol, untyped]]
|
|
8
8
|
def list: (?cwd: String?) -> Array[Hash[Symbol, untyped]]
|
|
9
|
+
def updated: (String id) -> Time?
|
|
9
10
|
def delete: (String id) -> void
|
|
10
11
|
end
|
|
11
12
|
end
|