class Riffer::Rig::Runtime
Never renders, prints or reads the filesystem: anything a host needs is a Runtime feature, so embedders get it too.
Constants
- BASE_PROMPT_TEMPLATE
- DEFAULT_MAX_STEPS
-
Unlimited: rifferโs own default (16) is too small for a general-purpose harness.
- DEFAULT_NAME
- DEFAULT_REASONING_LABEL
Attributes
Riffer::Agent
@dynamic agent, credentials, cwd, host, id
Hash[Symbol, Hash[Symbol, String]]
String
String
Public Class Methods
(String model, ?extensions: Array[Riffer::Rig::Extension], ?tools: Array[String]?, ?settings: Hash[Symbol, untyped], ?host: Riffer::Rig::Hosts::_Host, ?cwd: String?, ?name: String, ?instructions: String?, ?credentials: Hash[Symbol, Hash[Symbol, String]], ?env: Riffer::Rig::Env, ?auth_path: String, ?settings_path: String, ?reasoning: String?, ?riffer_config: Riffer::Config, ?mcp_registry: Riffer::Rig::Mcp::_Registry, ?max_steps: Integer?, ?model_options: Hash[Symbol, untyped], ?native_tools: Hash[Symbol, untyped], ?clock: ^() → Float, ?snapshot: (Riffer::Rig::Runtime::Snapshot | Hash[Symbol, untyped])?) → void
Source
# File lib/riffer/rig/runtime.rb, line 93 def initialize( model, extensions: [], tools: nil, settings: {}, host: Riffer::Rig::Hosts::Null.new, cwd: nil, name: DEFAULT_NAME, instructions: nil, credentials: {}, env: Riffer::Rig::Env.new, auth_path: Riffer::Rig::Credentials::PATH, settings_path: Riffer::Rig::Settings::File::PATH, reasoning: nil, riffer_config: Riffer.config, mcp_registry: Riffer::Mcp, max_steps: DEFAULT_MAX_STEPS, model_options: {}, native_tools: {}, clock: -> { Process.clock_gettime(Process::CLOCK_MONOTONIC) }, snapshot: nil ) saved = snapshot && Riffer::Rig::Runtime::Snapshot.of(snapshot) # Doubles as the snapshot id and ACP sessionId. @id = saved ? saved.id : ::SecureRandom.uuid_v7 @host = Riffer::Rig::Hosts::Mirror.new(host) @cwd = cwd || Dir.pwd @credentials = credentials @env = env @auth_path = auth_path @settings_path = settings_path @concurrency = Riffer::Rig::Runtime::Concurrency.new @core_commands = [] @reload_check = nil @clock = clock @turn = nil @cancel_flag = Riffer::Rig::Runtime::CancelFlag.new @abort = Riffer::Rig::Runtime::Abort.new @job_inbox = Riffer::Rig::Runtime::JobInbox.new(clock: clock) @jobs = Riffer::Rig::Runtime::Jobs.new(@job_inbox, clock: clock) @job_notices = Riffer::Rig::Runtime::JobNotices.new( @jobs, @job_inbox, emit: ->(event) { emit(event) }, deliver: ->(message) { deliver(message) } ) @session_start_pending = true @session_start_reason = saved ? :restore : :new @message_observers = [] @model_observers = [] @reasoning_observers = [] @reasoning = (saved && Riffer::Rig::Settings::ModelOptions.reasoning_level(saved.reasoning)) || reasoning @tool_allowlist = tools @native_tools = native_tools @model_options = model_options.merge(Riffer::Rig::Settings::ModelOptions.native_options(model, native_tools)) @system_prompt = Riffer::Rig::Runtime::SystemPrompt.new( instructions || format(BASE_PROMPT_TEMPLATE, name: name), @cwd ) @mcp = Riffer::Rig::Runtime::McpRegistrar.new( @id, registry: mcp_registry, host: @host, env: env, auth_path: auth_path ) assembly = Riffer::Rig::Runtime::Assembly.new(extensions, settings, tools) assembly.failures.each do |failure| @host.notify("Extension #{failure.extension.name} failed to load: #{failure.error.message}", level: :error) end mcp_servers = @mcp.register(assembly.mcp_declarations) hooks = Riffer::Rig::Runtime::Hooks.new(assembly.hooks, @host) config = agent_config(assembly, hooks, max_steps) agent = saved ? restore(saved, model, config) : build_agent(model, config) install(assembly, hooks, agent) @mcp.retain(mcp_servers) @agent.session.on_message do |message| Thread.handle_interrupt(Riffer::Rig::Runtime::Abort::SHIELD) { observe_message(message) } end end
@rbs model: String @rbs extensions: Array @rbs tools: Array? @rbs settings: Hash[Symbol, untyped] @rbs host: Riffer::Rig::Hosts::_Host @rbs cwd: String? @rbs name: String @rbs instructions: String? @rbs credentials: Hash[Symbol, Hash[Symbol, String]] @rbs env: Riffer::Rig::Env @rbs auth_path: String @rbs settings_path: String @rbs reasoning: String? @rbs riffer_config: Riffer::Config @rbs mcp_registry: Riffer::Rig::Mcp::_Registry @rbs max_steps: Integer? @rbs model_options: Hash[Symbol, untyped] @rbs native_tools: Hash[Symbol, untyped] @rbs clock: ^() -> Float @rbs snapshot: (Riffer::Rig::Runtime::Snapshot | Hash[Symbol, untyped])? @rbs return: void
Public Instance Methods
(String text) → Riffer::Agent::Response
Source
# File lib/riffer/rig/runtime.rb, line 182 def ask(text) @concurrency.claim begin turn { start_turn(text) }.each { |event| event } ensure @concurrency.release end end
@rbs text: String @rbs return: Riffer::Agent::Response
(?timeout: Numeric?) → bool
Source
# File lib/riffer/rig/runtime.rb, line 358 def await_job_exit(timeout: nil) @job_notices.await(timeout) end
@rbs timeout: Numeric? @rbs return: bool
() → bool
Source
# File lib/riffer/rig/runtime.rb, line 347 def busy? @concurrency.busy? end
@rbs return: bool
() → nil
Source
# File lib/riffer/rig/runtime.rb, line 368 def cancel @cancel_flag.set @abort.fire nil end
@rbs return: nil
() → void
Source
# File lib/riffer/rig/runtime.rb, line 380 def close return if @concurrency.closed? @concurrency.close @job_inbox.close @jobs.shutdown @mcp.retain({}) @hooks.observe(:session_end, Riffer::Rig::Events::SessionEnd.new(:close)) unless @session_start_pending end
@rbs return: void
() → Array[Riffer::Rig::Command]
Source
# File lib/riffer/rig/runtime.rb, line 192 def commands @commands.values end
@rbs return: Array
() → Hash[Symbol, Hash[Symbol, untyped]]
Source
# File lib/riffer/rig/runtime.rb, line 239 def declared_settings @assembly.declared_settings end
@rbs return: Hash[Symbol, Hash[Symbol, untyped]]
() → Array[Riffer::Rig::Extension::Failure]
Source
# File lib/riffer/rig/runtime.rb, line 229 def errors @assembly.failures.dup end
@rbs return: Array
() ?{ (::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event) → void } → (nil | Enumerator[::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event, Riffer::Agent::Response])
Source
# File lib/riffer/rig/runtime.rb, line 176 def follow_up(&) claimed(turn { start_follow_up }, &) end
@rbs &: ?(::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event) -> void @rbs return: (nil | Enumerator[::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event, Riffer::Agent::Response])
(Riffer::Rig::Command command) → nil
Source
# File lib/riffer/rig/runtime.rb, line 198 def install_command(command) # Kept apart from the extensions' commands, which a rebuild replaces. @core_commands << command @commands[command.name] = command nil end
@rbs command: Riffer::Rig::Command @rbs return: nil
(^(::Riffer::Rig::Runtime) → void check) → nil
Source
# File lib/riffer/rig/runtime.rb, line 207 def install_reload_check(check) @reload_check = check nil end
@rbs check: ^(::Riffer::Rig::Runtime) -> void @rbs return: nil
(Symbol provider, Hash[Symbol, String] values) → void
Source
# File lib/riffer/rig/runtime.rb, line 278 def merge_credentials(provider, values) @credentials = @credentials.merge(provider => values) end
@rbs provider: Symbol @rbs values: Hash[Symbol, String] @rbs return: void
() → String
Source
# File lib/riffer/rig/runtime.rb, line 244 def model "#{@agent.provider_name}/#{@agent.model_name}" end
@rbs return: String
(String model) → void
Source
# File lib/riffer/rig/runtime.rb, line 250 def model=(model) # Upstream candidate: riffer resolves the model Proc once, in Agent.new, so # a switch rebuilds the agent over the same session and config. agent = successor(model, @agent.config) rederive_options(model) @agent = agent @model_override = model @model_observers.each { |observer| observer.call(model) } end
@rbs model: String @rbs return: void
() { (Riffer::Rig::Runtime::JobCompletion) → void } → nil
Source
# File lib/riffer/rig/runtime.rb, line 336 def on_job_exit(&) @job_inbox.subscribe(&) nil end
@rbs &: (Riffer::Rig::Runtime::JobCompletion) -> void @rbs return: nil
() { (Riffer::Messages::Base) → void } → nil
Source
# File lib/riffer/rig/runtime.rb, line 315 def on_message(&block) @message_observers << block nil end
@rbs &block: (Riffer::Messages::Base) -> void @rbs return: nil
() { (String) → void } → nil
Source
# File lib/riffer/rig/runtime.rb, line 322 def on_model_change(&block) @model_observers << block nil end
@rbs &block: (String) -> void @rbs return: nil
() { (String) → void } → nil
Source
# File lib/riffer/rig/runtime.rb, line 329 def on_reasoning_change(&block) @reasoning_observers << block nil end
@rbs &block: (String) -> void @rbs return: nil
() → Integer
Source
# File lib/riffer/rig/runtime.rb, line 363 def pending_job_exit_count @job_notices.pending_exit_count end
@rbs return: Integer
() → bool
Source
# File lib/riffer/rig/runtime.rb, line 352 def pending_jobs? @job_notices.pending? end
@rbs return: bool
(String text) ?{ (::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event) → void } → (nil | Enumerator[::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event, Riffer::Agent::Response])
Source
# File lib/riffer/rig/runtime.rb, line 170 def prompt(text, &) claimed(turn { start_turn(text) }, &) end
@rbs text: String @rbs &: ?(::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event) -> void @rbs return: (nil | Enumerator[::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event, Riffer::Agent::Response])
() → String?
Source
# File lib/riffer/rig/runtime.rb, line 261 def reasoning reasoning_over(settings) end
@rbs return: String?
(String level) → void
Source
# File lib/riffer/rig/runtime.rb, line 267 def reasoning=(level) # Riffer's own validation raises before anything is assigned, so a level it # refuses leaves both the level and the config unchanged. @agent.config.reasoning = level @reasoning = level @reasoning_observers.each { |observer| observer.call(level) } end
@rbs level: String @rbs return: void
(extensions: Array[Riffer::Rig::Extension], settings: Hash[Symbol, untyped]) → nil
Source
# File lib/riffer/rig/runtime.rb, line 285 def rebuild(extensions:, settings:) @concurrency.claim_rebuild begin assembly = Riffer::Rig::Runtime::Assembly.new(extensions, settings, @tool_allowlist, strict: true) mcp_servers = @mcp.register(assembly.mcp_declarations) hooks = Riffer::Rig::Runtime::Hooks.new(assembly.hooks, @host) agent = successor(model, agent_config(assembly, hooks, @agent.config.max_steps)) lifecycle(:session_end, Riffer::Rig::Events::SessionEnd.new(:reload)) install(assembly, hooks, agent) @mcp.retain(mcp_servers) lifecycle(:session_start, Riffer::Rig::Events::SessionStart.new(@id, :reload)) ensure @concurrency.release end nil end
@rbs extensions: Array @rbs settings: Hash[Symbol, untyped] @rbs return: nil
(String name, ?String args) ?{ (::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event) → void } → nil
Source
# File lib/riffer/rig/runtime.rb, line 216 def run_command(name, args = '', &block) @concurrency.claim emit = block || ->(_event) {} #: ^(::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event) -> void begin execute(name, args, emit) ensure @concurrency.release end @host.drain.each(&emit) nil end
@rbs name: String @rbs args: String @rbs &block: ?(::Riffer::StreamEvents::Base | Riffer::Rig::Events::_Event) -> void @rbs return: nil
() → Integer
Source
# File lib/riffer/rig/runtime.rb, line 342 def running_job_count @job_notices.running_count end
@rbs return: Integer
() → Hash[Symbol, untyped]
Source
# File lib/riffer/rig/runtime.rb, line 234 def settings @assembly.settings end
@rbs return: Hash[Symbol, untyped]
() → Riffer::Providers::TokenUsage?
Source
# File lib/riffer/rig/runtime.rb, line 375 def tally @agent.context.token_usage end
@rbs return: Riffer::Providers::TokenUsage?
() → Hash[Symbol, untyped]
Source
# File lib/riffer/rig/runtime.rb, line 303 def to_h Riffer::Rig::Runtime::Snapshot.new( id: @id, messages: @agent.session.messages, model: @model_override, reasoning: @reasoning, skills: activated_skills ).to_h end
@rbs return: Hash[Symbol, untyped]