Class: Rubino::Config::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/rubino/config/configuration.rb

Overview

Central configuration object providing typed accessors for all config sections. Wraps the raw hash loaded by Config::Loader with convenient method access.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(raw: nil, home_path: nil) ⇒ Configuration

Returns a new instance of Configuration.



10
11
12
13
# File 'lib/rubino/config/configuration.rb', line 10

def initialize(raw: nil, home_path: nil)
  @home_path = home_path
  @raw = raw || load_from_file
end

Instance Attribute Details

#rawObject (readonly)

Returns the value of attribute raw.



8
9
10
# File 'lib/rubino/config/configuration.rb', line 8

def raw
  @raw
end

Instance Method Details

#agent_api_max_retriesObject



149
150
151
# File 'lib/rubino/config/configuration.rb', line 149

def agent_api_max_retries
  dig("agent", "api_max_retries")
end

#agent_disabled_toolsetsObject



153
154
155
# File 'lib/rubino/config/configuration.rb', line 153

def agent_disabled_toolsets
  dig("agent", "disabled_toolsets") || []
end

#agent_max_tool_iterationsObject

Iteration/time caps fall back to the built-in defaults when the config value is nil/missing (e.g. ‘config set agent.max_tool_iterations nil`, whose writer coerces “nil” -> nil). A bare nil here would crash every turn in IterationBudget’s numeric comparisons (#139).



141
142
143
# File 'lib/rubino/config/configuration.rb', line 141

def agent_max_tool_iterations
  dig("agent", "max_tool_iterations") || Defaults.dig("agent", "max_tool_iterations")
end

#agent_max_turn_secondsObject



145
146
147
# File 'lib/rubino/config/configuration.rb', line 145

def agent_max_turn_seconds
  dig("agent", "max_turn_seconds") || Defaults.dig("agent", "max_turn_seconds")
end

#agent_max_turnsObject

– Agent section –



133
134
135
# File 'lib/rubino/config/configuration.rb', line 133

def agent_max_turns
  dig("agent", "max_turns")
end

#approvals_modeObject

– Security section –



332
333
334
# File 'lib/rubino/config/configuration.rb', line 332

def approvals_mode
  dig("approvals", "mode")
end

#approvals_readonly_commandsObject

Extra command names / leading-token prefixes merged into the built-in read-only set (Security::ReadonlyCommands::SAFE_COMMANDS).



357
358
359
# File 'lib/rubino/config/configuration.rb', line 357

def approvals_readonly_commands
  dig("approvals", "readonly_commands") || []
end

#approvals_wait_timeoutObject

Seconds a run blocks on a human approval/clarification before the gate gives up and AUTO-DENIES (freeing the worker thread). nil = wait indefinitely (interruptible only by an explicit stop). Used by ApprovalGate as its default await deadline so an abandoned approval never parks a server worker for the whole window (W1).



341
342
343
344
345
346
# File 'lib/rubino/config/configuration.rb', line 341

def approvals_wait_timeout
  raw = dig("approvals", "wait_timeout_seconds")
  return nil if raw.nil?

  raw.to_f
end

#auto_allow_readonly?Boolean

Auto-allow provably read-only shell commands (ls, cat, grep, git log, …) without an approval prompt. Default ON (key absent = on); the hardline floor and permissions:deny still precede it.

Returns:

  • (Boolean)


351
352
353
# File 'lib/rubino/config/configuration.rb', line 351

def auto_allow_readonly?
  dig("approvals", "auto_allow_readonly") != false
end

#auxiliary_compression_configObject

– Auxiliary section –



396
397
398
# File 'lib/rubino/config/configuration.rb', line 396

def auxiliary_compression_config
  dig("auxiliary", "compression") || {}
end

#auxiliary_config(task) ⇒ Object

Generic accessor for auxiliary task config blocks. Returns {} when the task isn’t defined, so callers can chain .dig safely.



406
407
408
# File 'lib/rubino/config/configuration.rb', line 406

def auxiliary_config(task)
  dig("auxiliary", task.to_s) || {}
end

#auxiliary_vision_configObject



400
401
402
# File 'lib/rubino/config/configuration.rb', line 400

def auxiliary_vision_config
  dig("auxiliary", "vision") || {}
end

#compression_enabled?Boolean

– Compression section –

Returns:

  • (Boolean)


239
240
241
# File 'lib/rubino/config/configuration.rb', line 239

def compression_enabled?
  dig("compression", "enabled") == true
end

#compression_gateway_thresholdObject



247
248
249
# File 'lib/rubino/config/configuration.rb', line 247

def compression_gateway_threshold
  dig("compression", "gateway_threshold")
end

#compression_max_summary_tokensObject



263
264
265
# File 'lib/rubino/config/configuration.rb', line 263

def compression_max_summary_tokens
  dig("compression", "max_summary_tokens")
end

#compression_preserve_tool_pairs?Boolean

Returns:

  • (Boolean)


267
268
269
# File 'lib/rubino/config/configuration.rb', line 267

def compression_preserve_tool_pairs?
  dig("compression", "preserve_tool_pairs") == true
end

#compression_protect_first_nObject



255
256
257
# File 'lib/rubino/config/configuration.rb', line 255

def compression_protect_first_n
  dig("compression", "protect_first_n")
end

#compression_protect_last_nObject



259
260
261
# File 'lib/rubino/config/configuration.rb', line 259

def compression_protect_last_n
  dig("compression", "protect_last_n")
end

#compression_target_ratioObject



251
252
253
# File 'lib/rubino/config/configuration.rb', line 251

def compression_target_ratio
  dig("compression", "target_ratio")
end

#compression_thresholdObject



243
244
245
# File 'lib/rubino/config/configuration.rb', line 243

def compression_threshold
  dig("compression", "threshold")
end

#confirm_policyObject

Effective shell prompt policy: :confirm_all (every not-otherwise-allowed shell command prompts — today’s default) or :dangerous_only (safe shell commands run unprompted; only DangerousPatterns matches prompt).

Resolution / coercion (documented in defaults.rb):

- if security.confirm_policy is set explicitly, it WINS (over the
  legacy alias);
- otherwise it is DERIVED from require_confirmation_for_shell
  (true -> :confirm_all, false -> :dangerous_only),

so any deployment that only ever set the old alias keeps its behavior. An unrecognized value falls back to the derived alias result.



379
380
381
382
383
384
# File 'lib/rubino/config/configuration.rb', line 379

def confirm_policy
  raw = dig("security", "confirm_policy")
  return raw.to_sym if %w[confirm_all dangerous_only].include?(raw.to_s)

  require_confirmation_for_shell? ? :confirm_all : :dangerous_only
end

#database_pathObject

– Database section – Resolves the sqlite path. The DEFAULT (sentinel) follows the resolved home so RUBINO_HOME relocates the DB alongside config/.env/skills, avoiding the split brain where config went to the isolated home but the DB to the real ~/.rubino (issue #96). An EXPLICIT database.path in config.yml wins and is expanded verbatim.



38
39
40
41
42
43
44
45
# File 'lib/rubino/config/configuration.rb', line 38

def database_path
  path = dig("database", "path")
  if path == Defaults::DEFAULT_DATABASE_PATH
    File.join(resolved_home, "rubino.sqlite3")
  else
    File.expand_path(path)
  end
end

#dig(*keys) ⇒ Object

– Generic access –



422
423
424
# File 'lib/rubino/config/configuration.rb', line 422

def dig(*keys)
  @raw.dig(*keys)
end

#display_input_max_rowsObject

Cap on the chat input’s visual rows (display.input_max_rows). Falls back to the composer default for nil/zero/garbage so a bad value can never collapse or unbound the input block.



84
85
86
87
# File 'lib/rubino/config/configuration.rb', line 84

def display_input_max_rows
  value = dig("display", "input_max_rows").to_i
  value.positive? ? value : UI::BottomComposer::MAX_INPUT_ROWS
end

#display_statusbar?Boolean

The status bar under the chat input (display.statusbar, default true). Only an explicit false disables it.

Returns:

  • (Boolean)


68
69
70
# File 'lib/rubino/config/configuration.rb', line 68

def display_statusbar?
  dig("display", "statusbar") != false
end

#display_streaming?Boolean

– Display section –

Returns:

  • (Boolean)


62
63
64
# File 'lib/rubino/config/configuration.rb', line 62

def display_streaming?
  dig("display", "streaming") == true
end

#display_tool_output_preview_linesObject

Transcript preview budget for tool output (display.tool_output_preview_lines): head lines shown before the “… +N lines (full output → context)” marker. 0 = no collapse (full dump). Display-only — the model-facing output is untouched.



76
77
78
79
# File 'lib/rubino/config/configuration.rb', line 76

def display_tool_output_preview_lines
  value = dig("display", "tool_output_preview_lines")
  value.nil? ? 3 : value.to_i
end

#jobs_max_attemptsObject



314
315
316
# File 'lib/rubino/config/configuration.rb', line 314

def jobs_max_attempts
  dig("jobs", "max_attempts")
end

#jobs_modeObject

– Jobs section –



306
307
308
# File 'lib/rubino/config/configuration.rb', line 306

def jobs_mode
  dig("jobs", "mode")
end

#jobs_poll_intervalObject



310
311
312
# File 'lib/rubino/config/configuration.rb', line 310

def jobs_poll_interval
  dig("jobs", "poll_interval")
end

#memory_auto_extract?Boolean

Returns:

  • (Boolean)


276
277
278
# File 'lib/rubino/config/configuration.rb', line 276

def memory_auto_extract?
  dig("memory", "auto_extract") == true
end

#memory_char_limitObject



280
281
282
# File 'lib/rubino/config/configuration.rb', line 280

def memory_char_limit
  dig("memory", "memory_char_limit")
end

#memory_enabled?Boolean

– Memory section –

Returns:

  • (Boolean)


272
273
274
# File 'lib/rubino/config/configuration.rb', line 272

def memory_enabled?
  dig("memory", "enabled") == true
end

#memory_ingest_char_limitObject

Ingest/store budget for the live memory set, decoupled from the injection budget (‘memory_char_limit`). `nil` => unbounded ingest.



301
302
303
# File 'lib/rubino/config/configuration.rb', line 301

def memory_ingest_char_limit
  dig("memory", "ingest_char_limit")
end

#memory_user_char_limitObject



295
296
297
# File 'lib/rubino/config/configuration.rb', line 295

def memory_user_char_limit
  dig("memory", "user_char_limit")
end

#model_context_lengthObject



24
25
26
# File 'lib/rubino/config/configuration.rb', line 24

def model_context_length
  dig("model", "context_length")
end

#model_defaultObject

– Model section –



16
17
18
# File 'lib/rubino/config/configuration.rb', line 16

def model_default
  dig("model", "default")
end

#model_providerObject



20
21
22
# File 'lib/rubino/config/configuration.rb', line 20

def model_provider
  dig("model", "provider")
end

#model_supports_vision?Boolean

Returns true when the primary model can ingest images directly. Honours an explicit ‘model.supports_vision` override; otherwise falls back to ContentBuilder’s name-pattern heuristic. Used by VisionTool to decide whether to expose itself (no point delegating if the primary can see).

Returns:

  • (Boolean)


414
415
416
417
418
419
# File 'lib/rubino/config/configuration.rb', line 414

def model_supports_vision?
  raw = dig("model", "supports_vision")
  return raw == true unless raw.nil?

  LLM::ContentBuilder.supports_vision?(model_default.to_s)
end

#model_temperatureObject



28
29
30
# File 'lib/rubino/config/configuration.rb', line 28

def model_temperature
  dig("model", "temperature")
end

#notifications_bell?Boolean

Returns:

  • (Boolean)


113
114
115
# File 'lib/rubino/config/configuration.rb', line 113

def notifications_bell?
  dig("notifications", "bell") != false
end

#notifications_commandObject



117
118
119
120
# File 'lib/rubino/config/configuration.rb', line 117

def notifications_command
  value = dig("notifications", "command").to_s
  value.empty? ? nil : value
end

#notifications_enabled?Boolean

– Notifications section (UI::Notifier: attention bell + hook) – enabled/bell are on unless explicitly false; command is nil unless a non-empty string is set; min_turn_seconds falls back to the default.

Returns:

  • (Boolean)


109
110
111
# File 'lib/rubino/config/configuration.rb', line 109

def notifications_enabled?
  dig("notifications", "enabled") != false
end

#notifications_min_turn_secondsObject



122
123
124
125
# File 'lib/rubino/config/configuration.rb', line 122

def notifications_min_turn_seconds
  value = dig("notifications", "min_turn_seconds")
  (value.nil? ? Defaults.dig("notifications", "min_turn_seconds") : value).to_f
end

#paste_collapse_linesObject

– Paste section (UI::PasteStore: the file-backed paste pipeline) – A paste with MORE than this many lines collapses to a “[Pasted text #N +M lines]” placeholder in the composer (expanded to the full body at send). Falls back for nil/zero/garbage.



93
94
95
96
# File 'lib/rubino/config/configuration.rb', line 93

def paste_collapse_lines
  value = dig("paste", "collapse_lines").to_i
  value.positive? ? value : UI::PasteStore::DEFAULT_COLLAPSE_LINES
end

#paste_file_threshold_tokensObject

A paste estimated above this many tokens (chars/4, the same rule compaction uses) overflows to <home>/sessions/<id>/paste_N.txt and the message carries a read-tool pointer instead of the content.



101
102
103
104
# File 'lib/rubino/config/configuration.rb', line 101

def paste_file_threshold_tokens
  value = dig("paste", "file_threshold_tokens").to_i
  value.positive? ? value : UI::PasteStore::DEFAULT_THRESHOLD_TOKENS
end

#paths_homeObject

– Paths section –



48
49
50
# File 'lib/rubino/config/configuration.rb', line 48

def paths_home
  dig("paths", "home")
end

#prompts_environment_enabled?Boolean

Returns:

  • (Boolean)


206
207
208
209
210
211
# File 'lib/rubino/config/configuration.rb', line 206

def prompts_environment_enabled?
  # Default to on when the key is absent — env injection is the cheap
  # win we don't want a forgetful config.yml to disable accidentally.
  value = dig("prompts", "environment", "enabled")
  value.nil? || value == true
end

#prompts_environment_extra_utilitiesObject



213
214
215
# File 'lib/rubino/config/configuration.rb', line 213

def prompts_environment_extra_utilities
  Array(dig("prompts", "environment", "extra_utilities")).map(&:to_s)
end

#prompts_override_for(role) ⇒ Object

Returns the override string for a given role name, or nil if the built-in default prompt should be used.



219
220
221
222
223
224
225
# File 'lib/rubino/config/configuration.rb', line 219

def prompts_override_for(role)
  value = dig("prompts", "overrides", role.to_s)
  return nil if value.nil?

  text = value.to_s.strip
  text.empty? ? nil : text
end

#prompts_preambleObject

– Prompts section – The customer-facing preamble prepended to every assembled system prompt. nil/empty disables the layer.



198
199
200
201
202
203
204
# File 'lib/rubino/config/configuration.rb', line 198

def prompts_preamble
  value = dig("prompts", "preamble")
  return nil if value.nil?

  text = value.to_s.strip
  text.empty? ? nil : text
end

#provider_config(name) ⇒ Object

– Providers section –



391
392
393
# File 'lib/rubino/config/configuration.rb', line 391

def provider_config(name)
  dig("providers", name.to_s) || {}
end

#reload!Object



435
436
437
# File 'lib/rubino/config/configuration.rb', line 435

def reload!
  @raw = load_from_file
end

#require_confirmation_for_shell?Boolean

When true, a ‘shell` tool call must always be confirmed in manual mode even if the tool’s own risk level wouldn’t otherwise require it. Default true (key absent = on) so shell-by-default stays gated behind a human.

Returns:

  • (Boolean)


364
365
366
# File 'lib/rubino/config/configuration.rb', line 364

def require_confirmation_for_shell?
  dig("security", "require_confirmation_for_shell") != false
end

#run_idle_event_timeoutObject

– Run lifecycle section – Returns Float seconds (or nil to disable). EventsOperation uses this to bound how long a “running” row can go without producing a new event before the watchdog promotes it to failed.



231
232
233
234
235
236
# File 'lib/rubino/config/configuration.rb', line 231

def run_idle_event_timeout
  raw = dig("run", "idle_event_timeout")
  return nil if raw.nil?

  raw.to_f
end

#security_command_allowlistObject



386
387
388
# File 'lib/rubino/config/configuration.rb', line 386

def security_command_allowlist
  dig("security", "command_allowlist") || []
end

#set(*keys, value) ⇒ Object



426
427
428
429
430
431
432
433
# File 'lib/rubino/config/configuration.rb', line 426

def set(*keys, value)
  hash = @raw
  keys[0..-2].each do |key|
    hash[key] ||= {}
    hash = hash[key]
  end
  hash[keys.last] = value
end

#skills_auto_distill?Boolean

Post-turn skill distillation. Defaults to true (skills feature on + distill key absent ⇒ distill on), mirroring memory_auto_extract? as the gate for an aux-spending background job. Turning skills off disables it too, since there is no point distilling skills that won’t be loaded.

Returns:

  • (Boolean)


288
289
290
291
292
293
# File 'lib/rubino/config/configuration.rb', line 288

def skills_auto_distill?
  return false unless dig("skills", "enabled") != false

  value = dig("skills", "auto_distill")
  value.nil? || value == true
end

#streaming_enabled?Boolean

– Streaming section –

Returns:

  • (Boolean)


128
129
130
# File 'lib/rubino/config/configuration.rb', line 128

def streaming_enabled?
  dig("streaming", "enabled") == true
end

#tasks_ask_parent_timeoutObject

Bound (seconds) a BLOCKING ask_parent waits for an answer before the child self-heals and proceeds with its best judgement (S5a). Reuses the approval-gate timeout convention — a sane upper bound, never “forever” —so an abandoned ask never parks the child’s thread indefinitely. Default 900.



191
192
193
# File 'lib/rubino/config/configuration.rb', line 191

def tasks_ask_parent_timeout
  dig("tasks", "ask_parent_timeout") || Defaults.dig("tasks", "ask_parent_timeout")
end

#tasks_max_children_per_nodeObject

Maximum number of LIVE direct children a single node (the human/top-level or one subagent) may have at once. Default 3.



169
170
171
# File 'lib/rubino/config/configuration.rb', line 169

def tasks_max_children_per_node
  dig("tasks", "max_children_per_node") || Defaults.dig("tasks", "max_children_per_node")
end

#tasks_max_concurrent_totalObject

Hard global ceiling on the total number of LIVE subagents across the whole tree, so depth × fan-out cannot blow past the process’s thread/cost budget. Default 8.



176
177
178
# File 'lib/rubino/config/configuration.rb', line 176

def tasks_max_concurrent_total
  dig("tasks", "max_concurrent_total") || Defaults.dig("tasks", "max_concurrent_total")
end

#tasks_max_depthObject

– Tasks / nested-subagent caps – Maximum nesting depth for the ‘task` delegation tree. depth 0 is a human/top-level-spawned child; the cap bounds how deep a chain of subagents-spawning-subagents may go. Default 2 ⇒ human→child→grandchild. Falls back to the built-in default when missing/nil so the numeric caps in BackgroundTask#reserve never crash on a bare nil.



163
164
165
# File 'lib/rubino/config/configuration.rb', line 163

def tasks_max_depth
  dig("tasks", "max_depth") || Defaults.dig("tasks", "max_depth")
end

#tasks_max_live_probes_per_childObject

Per-child budget for BILLED live probes (‘probe(live:true)`). Over budget, the model is steered to the FREE live:false snapshot. Free snapshots are unlimited. Default 5.



183
184
185
# File 'lib/rubino/config/configuration.rb', line 183

def tasks_max_live_probes_per_child
  dig("tasks", "max_live_probes_per_child") || Defaults.dig("tasks", "max_live_probes_per_child")
end

#tool_enabled?(name) ⇒ Boolean

– Tools section –

Returns:

  • (Boolean)


319
320
321
# File 'lib/rubino/config/configuration.rb', line 319

def tool_enabled?(name)
  dig("tools", name.to_s) == true
end

#tool_output_max_bytesObject



323
324
325
# File 'lib/rubino/config/configuration.rb', line 323

def tool_output_max_bytes
  dig("tool_output", "max_bytes")
end

#tool_output_max_linesObject



327
328
329
# File 'lib/rubino/config/configuration.rb', line 327

def tool_output_max_lines
  dig("tool_output", "max_lines")
end

#ui_adapterObject

– UI section –



53
54
55
# File 'lib/rubino/config/configuration.rb', line 53

def ui_adapter
  dig("ui", "adapter")
end

#ui_verbose?Boolean

Returns:

  • (Boolean)


57
58
59
# File 'lib/rubino/config/configuration.rb', line 57

def ui_verbose?
  dig("ui", "verbose") == true
end