rubino-agent 0.3.0 → 0.5.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/.rubocop_todo.yml +11 -2
- data/AGENTS.md +1 -1
- data/CHANGELOG.md +172 -5
- data/CONTRIBUTING.md +10 -1
- data/README.md +14 -5
- data/Rakefile +31 -0
- data/docs/agents.md +42 -23
- data/docs/architecture.md +2 -2
- data/docs/commands.md +35 -3
- data/docs/configuration.md +20 -23
- data/docs/getting-started.md +5 -3
- data/docs/security.md +16 -5
- data/docs/skills.md +31 -0
- data/docs/troubleshooting.md +1 -1
- data/exe/rubino +16 -2
- data/install.sh +721 -59
- data/lib/rubino/active_agent.rb +73 -0
- data/lib/rubino/agent/action_claim_guard.rb +881 -0
- data/lib/rubino/agent/agent_registry.rb +5 -2
- data/lib/rubino/agent/definition.rb +1 -9
- data/lib/rubino/agent/fallback_chain.rb +0 -6
- data/lib/rubino/agent/iteration_budget.rb +109 -3
- data/lib/rubino/agent/loop.rb +476 -20
- data/lib/rubino/agent/model_call_runner.rb +81 -3
- data/lib/rubino/agent/prompts/build.txt +22 -5
- data/lib/rubino/agent/response_validator.rb +8 -0
- data/lib/rubino/agent/runner.rb +133 -8
- data/lib/rubino/agent/tool_executor.rb +166 -14
- data/lib/rubino/agent/truncation_continuation.rb +4 -1
- data/lib/rubino/api/server.rb +19 -0
- data/lib/rubino/attachments/classify.rb +35 -17
- data/lib/rubino/boot/config_guard.rb +71 -0
- data/lib/rubino/cli/chat/completion_builder.rb +42 -6
- data/lib/rubino/cli/chat/idle_card_host.rb +7 -1
- data/lib/rubino/cli/chat/session_resolver.rb +87 -21
- data/lib/rubino/cli/chat_command.rb +1189 -50
- data/lib/rubino/cli/commands.rb +282 -2
- data/lib/rubino/cli/config_command.rb +68 -8
- data/lib/rubino/cli/doctor_command.rb +204 -12
- data/lib/rubino/cli/jobs_command.rb +12 -0
- data/lib/rubino/cli/memory_command.rb +53 -20
- data/lib/rubino/cli/onboarding_wizard.rb +79 -6
- data/lib/rubino/cli/session_command.rb +172 -18
- data/lib/rubino/cli/setup_command.rb +131 -8
- data/lib/rubino/cli/skills_command.rb +183 -9
- data/lib/rubino/cli/trust_gate.rb +16 -7
- data/lib/rubino/commands/built_ins.rb +2 -0
- data/lib/rubino/commands/command.rb +12 -2
- data/lib/rubino/commands/executor.rb +149 -12
- data/lib/rubino/commands/handlers/agent_switch.rb +100 -0
- data/lib/rubino/commands/handlers/agents.rb +156 -41
- data/lib/rubino/commands/handlers/config.rb +4 -1
- data/lib/rubino/commands/handlers/help.rb +113 -14
- data/lib/rubino/commands/handlers/memory.rb +15 -5
- data/lib/rubino/commands/handlers/sessions.rb +26 -3
- data/lib/rubino/commands/handlers/status.rb +9 -4
- data/lib/rubino/commands/loader.rb +12 -0
- data/lib/rubino/config/configuration.rb +86 -24
- data/lib/rubino/config/defaults.rb +140 -33
- data/lib/rubino/config/loader.rb +62 -12
- data/lib/rubino/config/validator.rb +341 -0
- data/lib/rubino/config/writer.rb +123 -31
- data/lib/rubino/context/compressor.rb +184 -22
- data/lib/rubino/context/environment_inspector.rb +2 -2
- data/lib/rubino/context/file_discovery.rb +2 -2
- data/lib/rubino/context/message_boundary.rb +27 -1
- data/lib/rubino/context/project_languages.rb +90 -0
- data/lib/rubino/context/prompt_assembler.rb +105 -22
- data/lib/rubino/context/summary_builder.rb +45 -4
- data/lib/rubino/context/token_budget.rb +36 -11
- data/lib/rubino/context/token_estimate.rb +45 -0
- data/lib/rubino/context/tool_result_pruner.rb +81 -0
- data/lib/rubino/database/connection.rb +154 -3
- data/lib/rubino/database/migrations/001_create_initial_schema.rb +314 -40
- data/lib/rubino/database/migrator.rb +98 -5
- data/lib/rubino/documents/cap_exceeded.rb +13 -0
- data/lib/rubino/documents/converters/csv.rb +4 -3
- data/lib/rubino/documents/converters/docx.rb +29 -5
- data/lib/rubino/documents/converters/html.rb +5 -1
- data/lib/rubino/documents/converters/json.rb +2 -1
- data/lib/rubino/documents/converters/pdf.rb +11 -2
- data/lib/rubino/documents/converters/plain.rb +2 -1
- data/lib/rubino/documents/converters/pptx.rb +11 -2
- data/lib/rubino/documents/converters/xlsx.rb +35 -4
- data/lib/rubino/documents/converters/xml.rb +2 -1
- data/lib/rubino/documents/limits.rb +210 -0
- data/lib/rubino/documents.rb +10 -3
- data/lib/rubino/errors.rb +36 -5
- data/lib/rubino/interaction/cancel_token.rb +19 -3
- data/lib/rubino/interaction/events.rb +13 -0
- data/lib/rubino/interaction/lifecycle.rb +99 -13
- data/lib/rubino/interaction/polishing.rb +176 -0
- data/lib/rubino/jobs/cron_job_repository.rb +5 -8
- data/lib/rubino/jobs/handlers/cleanup_sessions_job.rb +11 -0
- data/lib/rubino/jobs/handlers/distill_skill_job.rb +65 -9
- data/lib/rubino/jobs/queue.rb +63 -8
- data/lib/rubino/jobs/runner.rb +24 -6
- data/lib/rubino/jobs/worker.rb +0 -4
- data/lib/rubino/llm/adapter_response.rb +47 -4
- data/lib/rubino/llm/credential_check.rb +15 -16
- data/lib/rubino/llm/error_classifier.rb +89 -1
- data/lib/rubino/llm/inline_think_filter.rb +69 -12
- data/lib/rubino/llm/request.rb +30 -3
- data/lib/rubino/llm/ruby_llm_adapter.rb +394 -46
- data/lib/rubino/llm/tool_bridge.rb +113 -9
- data/lib/rubino/mcp/manager.rb +18 -1
- data/lib/rubino/mcp/mcp_tool_wrapper.rb +14 -3
- data/lib/rubino/memory/aux_retry.rb +107 -0
- data/lib/rubino/memory/backends/sqlite.rb +73 -44
- data/lib/rubino/memory/backends.rb +23 -7
- data/lib/rubino/memory/salience_gate.rb +103 -0
- data/lib/rubino/memory/sqlite_extraction.rb +70 -0
- data/lib/rubino/memory/sqlite_extraction_prompt.rb +11 -0
- data/lib/rubino/memory/store.rb +33 -5
- data/lib/rubino/memory/threat_scanner.rb +52 -0
- data/lib/rubino/output/cost.rb +52 -0
- data/lib/rubino/output/headless_block_latch.rb +53 -0
- data/lib/rubino/output/result_serializer.rb +222 -0
- data/lib/rubino/output/turn_recorder.rb +77 -0
- data/lib/rubino/security/approval_policy.rb +227 -32
- data/lib/rubino/security/command_allowlist.rb +79 -4
- data/lib/rubino/security/doom_loop_detector.rb +21 -2
- data/lib/rubino/security/hardline_guard.rb +189 -16
- data/lib/rubino/security/pattern_matcher.rb +28 -5
- data/lib/rubino/security/prefix_deriver.rb +25 -6
- data/lib/rubino/security/readonly_commands.rb +145 -5
- data/lib/rubino/security/secret_path.rb +134 -0
- data/lib/rubino/security/url_safety.rb +255 -0
- data/lib/rubino/session/repository.rb +212 -11
- data/lib/rubino/session/store.rb +139 -14
- data/lib/rubino/skills/installer.rb +230 -0
- data/lib/rubino/skills/prompt_index.rb +2 -2
- data/lib/rubino/skills/registry.rb +52 -1
- data/lib/rubino/skills/skill.rb +64 -3
- data/lib/rubino/skills/skill_tool.rb +16 -5
- data/lib/rubino/tools/background_tasks.rb +157 -13
- data/lib/rubino/tools/base.rb +204 -3
- data/lib/rubino/tools/edit_tool.rb +73 -18
- data/lib/rubino/tools/glob_tool.rb +48 -9
- data/lib/rubino/tools/grep_tool.rb +103 -9
- data/lib/rubino/tools/multi_edit_tool.rb +64 -9
- data/lib/rubino/tools/patch_tool.rb +5 -0
- data/lib/rubino/tools/read_attachment_tool.rb +3 -1
- data/lib/rubino/tools/read_tool.rb +33 -15
- data/lib/rubino/tools/read_tracker.rb +153 -35
- data/lib/rubino/tools/registry.rb +113 -12
- data/lib/rubino/tools/result.rb +9 -1
- data/lib/rubino/tools/ruby_tool.rb +0 -0
- data/lib/rubino/tools/shell_registry.rb +70 -0
- data/lib/rubino/tools/shell_tool.rb +40 -1
- data/lib/rubino/tools/summarize_file_tool.rb +6 -0
- data/lib/rubino/tools/task_stop_tool.rb +10 -16
- data/lib/rubino/tools/task_tool.rb +36 -8
- data/lib/rubino/tools/vision_tool.rb +5 -0
- data/lib/rubino/tools/webfetch_tool.rb +39 -7
- data/lib/rubino/tools/websearch_tool.rb +92 -30
- data/lib/rubino/tools/write_tool.rb +23 -4
- data/lib/rubino/ui/api.rb +10 -1
- data/lib/rubino/ui/base.rb +11 -0
- data/lib/rubino/ui/bottom_composer.rb +382 -74
- data/lib/rubino/ui/cli.rb +515 -83
- data/lib/rubino/ui/completion_menu.rb +11 -7
- data/lib/rubino/ui/headless_trace.rb +63 -0
- data/lib/rubino/ui/live_region.rb +70 -7
- data/lib/rubino/ui/markdown_renderer.rb +142 -7
- data/lib/rubino/ui/notifier.rb +0 -2
- data/lib/rubino/ui/null.rb +52 -5
- data/lib/rubino/ui/paste_store.rb +16 -2
- data/lib/rubino/ui/queued_indicators.rb +6 -1
- data/lib/rubino/ui/status_bar.rb +61 -7
- data/lib/rubino/ui/streaming_markdown.rb +59 -6
- data/lib/rubino/ui/subagent_view.rb +29 -4
- data/lib/rubino/ui/tool_label.rb +52 -0
- data/lib/rubino/update_check.rb +39 -4
- data/lib/rubino/util/atomic_file.rb +117 -0
- data/lib/rubino/util/ignore_rules.rb +120 -0
- data/lib/rubino/util/output.rb +229 -12
- data/lib/rubino/util/secrets_mask.rb +70 -7
- data/lib/rubino/util/spill_store.rb +153 -0
- data/lib/rubino/version.rb +1 -1
- data/lib/rubino/workspace.rb +9 -1
- data/lib/rubino.rb +191 -7
- data/rubino-agent.gemspec +1 -0
- data/skills/ruby-expert/SKILL.md +1 -0
- metadata +42 -12
- data/lib/rubino/agent/router.rb +0 -65
- data/lib/rubino/database/migrations/002_create_runs.rb +0 -45
- data/lib/rubino/database/migrations/003_create_skill_states.rb +0 -15
- data/lib/rubino/database/migrations/004_create_cron_jobs.rb +0 -36
- data/lib/rubino/database/migrations/005_create_oauth_connections.rb +0 -27
- data/lib/rubino/database/migrations/006_create_webhook_deliveries.rb +0 -34
- data/lib/rubino/database/migrations/007_create_messages_fts.rb +0 -59
- data/lib/rubino/database/migrations/008_create_memory_facts.rb +0 -75
- data/lib/rubino/database/migrations/009_create_memory_graph.rb +0 -55
- data/lib/rubino/database/migrations/010_add_owner_pid_to_sessions.rb +0 -20
data/lib/rubino.rb
CHANGED
|
@@ -203,6 +203,53 @@ module Rubino
|
|
|
203
203
|
Thread.current[:rubino_current_subagent_id] = prev
|
|
204
204
|
end
|
|
205
205
|
|
|
206
|
+
# The CancelToken governing best-effort AUX work (post-turn polishing:
|
|
207
|
+
# memory-extract / skill-distill / summarize) running on THIS thread, if
|
|
208
|
+
# any. The detached polishing thread (Interaction::Polishing) binds its
|
|
209
|
+
# token here so the aux retry/backoff loop (Memory::AuxRetry) can poll it
|
|
210
|
+
# and abort the moment the user presses Esc — without threading a token
|
|
211
|
+
# through every aux call site. Nil on the foreground turn thread and on the
|
|
212
|
+
# API/server path (no detached polishing), where aux work is uncancellable
|
|
213
|
+
# as before.
|
|
214
|
+
def aux_cancel_token
|
|
215
|
+
Thread.current[:rubino_aux_cancel_token]
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# Binds +token+ as the aux cancel token for the duration of the block
|
|
219
|
+
# (set by Interaction::Polishing around its detached job drain, exactly
|
|
220
|
+
# like #with_ui binds the run's UI). Thread-local so the aux retry loop
|
|
221
|
+
# reaches it with zero signature churn.
|
|
222
|
+
def with_aux_cancel_token(token)
|
|
223
|
+
prev = Thread.current[:rubino_aux_cancel_token]
|
|
224
|
+
Thread.current[:rubino_aux_cancel_token] = token
|
|
225
|
+
yield
|
|
226
|
+
ensure
|
|
227
|
+
Thread.current[:rubino_aux_cancel_token] = prev
|
|
228
|
+
end
|
|
229
|
+
|
|
230
|
+
# True while a HEADLESS one-shot run (`rubino prompt`/-q) is executing on
|
|
231
|
+
# THIS thread. Bound by ChatCommand#run_oneshot via #with_headless so tools
|
|
232
|
+
# that behave differently with no live REPL can tell — today only TaskTool,
|
|
233
|
+
# which forces `task` subagents to run FOREGROUND in headless mode (#380): in
|
|
234
|
+
# one-shot there is no IdleCardHost to fold a background child's result back
|
|
235
|
+
# in and the process exits the instant the parent's answer is ready, so a
|
|
236
|
+
# background fan-out would be silently dropped. Nil/false on the interactive
|
|
237
|
+
# REPL and the API/server path, where background subagents are surfaced.
|
|
238
|
+
def headless?
|
|
239
|
+
Thread.current[:rubino_headless] || false
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Binds the headless one-shot flag for the duration of the block (set by
|
|
243
|
+
# ChatCommand#run_oneshot around the turn, exactly like #with_ui). Thread-
|
|
244
|
+
# local so a tool reaches it with zero signature churn through the loop.
|
|
245
|
+
def with_headless
|
|
246
|
+
prev = Thread.current[:rubino_headless]
|
|
247
|
+
Thread.current[:rubino_headless] = true
|
|
248
|
+
yield
|
|
249
|
+
ensure
|
|
250
|
+
Thread.current[:rubino_headless] = prev
|
|
251
|
+
end
|
|
252
|
+
|
|
206
253
|
# Returns the current structured logger.
|
|
207
254
|
def logger
|
|
208
255
|
@logger ||= Logger.new
|
|
@@ -216,6 +263,14 @@ module Rubino
|
|
|
216
263
|
@database ||= Database::Connection.new(configuration.database_path)
|
|
217
264
|
end
|
|
218
265
|
|
|
266
|
+
# Drops the memoized DB connection so the next #database call opens the file
|
|
267
|
+
# afresh. Used by `setup` after quarantining a corrupt DB so it reconnects
|
|
268
|
+
# to the newly-recreated file rather than the closed/renamed handle.
|
|
269
|
+
def reset_database!
|
|
270
|
+
@database&.close
|
|
271
|
+
@database = nil
|
|
272
|
+
end
|
|
273
|
+
|
|
219
274
|
# First-run guard for any DB-touching entry point. A brand-new RUBINO_HOME
|
|
220
275
|
# has no schema yet (setup/chat hasn't migrated it), so a read path like
|
|
221
276
|
# `rubino sessions list` would otherwise hit a raw
|
|
@@ -230,16 +285,123 @@ module Rubino
|
|
|
230
285
|
def ensure_database_ready!
|
|
231
286
|
connection = database
|
|
232
287
|
migrator = Database::Migrator.new(connection)
|
|
233
|
-
|
|
288
|
+
|
|
289
|
+
# FAST PATH (lock-free, race-safe): a side-effect-free read of
|
|
290
|
+
# `schema_info` that does NOT construct a Sequel migrator. The common case
|
|
291
|
+
# — an already-set-up home — returns here without touching the lock. Note
|
|
292
|
+
# we MUST NOT call `migrator.pending?` off the lock: merely constructing
|
|
293
|
+
# Sequel's IntegerMigrator inserts the version-0 row, and two concurrent
|
|
294
|
+
# boots both inserting it is exactly the duplicate-row corruption (#race).
|
|
295
|
+
if connection.healthy? && migrator.up_to_date?
|
|
296
|
+
# A fully-migrated home can still be MOUNTED read-only (F14): the schema
|
|
297
|
+
# reads fine, but the very next write (the session row) would crash with
|
|
298
|
+
# a raw `SQLite3::ReadOnlyException` past this guard. Catch it HERE — a
|
|
299
|
+
# cheap dir-writability probe, no DB write — and raise the accurate
|
|
300
|
+
# "not writable" diagnosis instead, matching the migrate-path branch
|
|
301
|
+
# below. A real (writable) home passes through untouched.
|
|
302
|
+
raise ConfigurationError, "rubino home / database is not writable: #{home_path}" \
|
|
303
|
+
unless home_writable?
|
|
304
|
+
|
|
305
|
+
return true
|
|
306
|
+
end
|
|
234
307
|
|
|
235
308
|
ensure_directories!
|
|
236
|
-
|
|
309
|
+
# Serialize the migration across concurrent boots (#race): N fresh
|
|
310
|
+
# `rubino` processes on a brand-new home would otherwise BOTH probe +
|
|
311
|
+
# migrate at once and corrupt the migrator bookkeeping. migrate! takes an
|
|
312
|
+
# exclusive flock and does the `pending?` probe + migrate entirely under
|
|
313
|
+
# it; waiters re-check and no-op. The lockfile lives in the home, which
|
|
314
|
+
# ensure_directories! just created.
|
|
315
|
+
migrator.migrate!(lock_path: migration_lock_path)
|
|
237
316
|
true
|
|
317
|
+
rescue Database::BusyError, ConfigurationError
|
|
318
|
+
# A sustained concurrent-migration lock that outlived the connection
|
|
319
|
+
# retry budget (#333/#359), or a careless RUBINO_HOME that points at a file
|
|
320
|
+
# / a read-only parent (F13), is NOT an "un-set-up" home — re-raise so the
|
|
321
|
+
# single CLI chokepoint surfaces the clean one-liner instead of this method
|
|
322
|
+
# masking it as `false` → a misleading "run setup" message.
|
|
323
|
+
raise
|
|
238
324
|
rescue StandardError => e
|
|
239
325
|
logger.debug(event: "ensure_database_ready_failed", error: "#{e.class}: #{e.message}")
|
|
326
|
+
# A read-only / not-writable home (F14) is NOT an un-set-up install: the
|
|
327
|
+
# files may be perfectly present, the directory is just mounted read-only
|
|
328
|
+
# or owned by another user, so migrate! can't open the lock/journal
|
|
329
|
+
# (Errno::EACCES/EROFS) or SQLite reports "attempt to write a readonly
|
|
330
|
+
# database". Masking that as `false` produced the misleading
|
|
331
|
+
# "isn't set up yet — run `rubino setup`" — doctor already diagnoses it
|
|
332
|
+
# correctly. Raise the ACCURATE diagnosis (matching the F13 home-error
|
|
333
|
+
# phrasing) so the single CLI chokepoint surfaces it instead of "not
|
|
334
|
+
# set up". Everything else still degrades to false.
|
|
335
|
+
if not_writable_error?(e)
|
|
336
|
+
raise ConfigurationError,
|
|
337
|
+
"rubino home / database is not writable: #{home_path} (#{clean_errno_message(e.message)})"
|
|
338
|
+
end
|
|
339
|
+
|
|
240
340
|
false
|
|
241
341
|
end
|
|
242
342
|
|
|
343
|
+
# Cheap, side-effect-free check that the home directory accepts writes — the
|
|
344
|
+
# F14 read-only-mount guard. Falls back to assuming writable on any probe
|
|
345
|
+
# hiccup (the migrate path will still catch a real failure with the same
|
|
346
|
+
# accurate message), so this never wrongly blocks a usable home.
|
|
347
|
+
def home_writable?
|
|
348
|
+
File.writable?(home_path)
|
|
349
|
+
rescue StandardError
|
|
350
|
+
true
|
|
351
|
+
end
|
|
352
|
+
|
|
353
|
+
# True when +error+ is a write-permission / read-only-filesystem failure
|
|
354
|
+
# (vs. a genuinely un-set-up or transiently-busy home): a directory mounted
|
|
355
|
+
# read-only, owned by another user, or a SQLite "readonly database" report.
|
|
356
|
+
# Used by ensure_database_ready! to give an ACCURATE message instead of the
|
|
357
|
+
# misleading "not set up" (F14).
|
|
358
|
+
def not_writable_error?(error)
|
|
359
|
+
return true if error.is_a?(Errno::EACCES) || error.is_a?(Errno::EROFS) || error.is_a?(Errno::EPERM)
|
|
360
|
+
|
|
361
|
+
error.message.to_s.downcase.include?("readonly") ||
|
|
362
|
+
error.message.to_s.downcase.include?("read-only") ||
|
|
363
|
+
error.message.to_s.downcase.include?("read only")
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
# A clean, user-facing form of an Errno message. Ruby appends an internal
|
|
367
|
+
# ` @ <syscall> - <path>` artifact to SystemCallError messages
|
|
368
|
+
# (e.g. "Operation not permitted @ apply2files - /home/x",
|
|
369
|
+
# "Permission denied @ dir_s_mkdir - /home/x") — the C function name and a
|
|
370
|
+
# path we already name elsewhere in the sentence. Strip that tail so the
|
|
371
|
+
# surfaced message is just the plain reason ("Operation not permitted").
|
|
372
|
+
def clean_errno_message(message)
|
|
373
|
+
message.to_s.sub(/ @ \S+ - .*\z/, "")
|
|
374
|
+
end
|
|
375
|
+
|
|
376
|
+
# Path to the inter-process migration lockfile in the rubino home. A single
|
|
377
|
+
# source of truth so setup and the boot path lock on the SAME file.
|
|
378
|
+
def migration_lock_path
|
|
379
|
+
File.join(home_path, ".migrate.lock")
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
# A clean, actionable message when the on-disk DB is PRESENT but UNUSABLE,
|
|
383
|
+
# else nil. Covers the un-setup-able state a user command must never crash
|
|
384
|
+
# on with a raw backtrace (#333/#359): a corrupt/malformed image →
|
|
385
|
+
# quarantine + recreate via setup. (The concurrent first-boot race that used
|
|
386
|
+
# to leave duplicate `schema_info` rows is now prevented at the source by the
|
|
387
|
+
# flock + side-effect-free `up_to_date?` fast path in the migrator, so there
|
|
388
|
+
# is no post-hoc duplicate-row state left to message about.)
|
|
389
|
+
# Read-only: never creates the file (matches doctor's #68 contract).
|
|
390
|
+
def database_repair_message
|
|
391
|
+
db = database
|
|
392
|
+
return nil if db.memory? || !File.exist?(db.db_path)
|
|
393
|
+
|
|
394
|
+
if db.corrupt?
|
|
395
|
+
"database is corrupt (malformed image): #{db.db_path}\n" \
|
|
396
|
+
"Run `rubino doctor` to diagnose, then `rubino setup` to quarantine it " \
|
|
397
|
+
"and recreate a fresh database."
|
|
398
|
+
end
|
|
399
|
+
rescue StandardError
|
|
400
|
+
# Detection itself must never crash a command; treat an unexpected probe
|
|
401
|
+
# failure as "no clean message available" and let normal flow continue.
|
|
402
|
+
nil
|
|
403
|
+
end
|
|
404
|
+
|
|
243
405
|
# Returns the event bus instance
|
|
244
406
|
def event_bus
|
|
245
407
|
@event_bus ||= Interaction::EventBus.new
|
|
@@ -295,11 +457,33 @@ module Rubino
|
|
|
295
457
|
# (#65): an auto-created home used to be left at the umask's 0755.
|
|
296
458
|
def ensure_directories!
|
|
297
459
|
home = home_path
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
460
|
+
# A careless RUBINO_HOME (the value points at an EXISTING FILE, or its
|
|
461
|
+
# parent is read-only) made FileUtils.mkdir_p raise a raw Errno::EEXIST /
|
|
462
|
+
# Errno::EACCES backtrace from deep in fileutils.rb — masking the actual,
|
|
463
|
+
# trivially-fixable mistake (F13). Normalize both into a clean, actionable
|
|
464
|
+
# domain error AT THE SOURCE so the single CLI chokepoint surfaces one line
|
|
465
|
+
# ("RUBINO_HOME is not a writable directory: <path>") + exit 1, in any
|
|
466
|
+
# output format, with no trace. A directory that already exists is fine.
|
|
467
|
+
if File.exist?(home) && !File.directory?(home)
|
|
468
|
+
raise ConfigurationError, "RUBINO_HOME is not a writable directory: #{home} " \
|
|
469
|
+
"(it points at an existing file — set RUBINO_HOME to a directory path)"
|
|
470
|
+
end
|
|
471
|
+
|
|
472
|
+
begin
|
|
473
|
+
FileUtils.mkdir_p(home)
|
|
474
|
+
# chmod/mkdir on an EXISTING read-only RUBINO_HOME (its parent let
|
|
475
|
+
# mkdir_p no-op, but the dir itself is not owner-writable) raises a raw
|
|
476
|
+
# Errno::EPERM/EACCES from deep in fileutils — the same unguarded
|
|
477
|
+
# backtrace F13 normalized for mkdir. Keep the perm ops inside the
|
|
478
|
+
# rescue so a non-writable home yields the SAME clean one-line domain
|
|
479
|
+
# error + exit 1, no trace.
|
|
480
|
+
File.chmod(0o700, home)
|
|
481
|
+
%w[memories sessions logs skills commands tools plugins].each do |subdir|
|
|
482
|
+
dir = File.join(home, subdir)
|
|
483
|
+
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
|
484
|
+
end
|
|
485
|
+
rescue SystemCallError => e
|
|
486
|
+
raise ConfigurationError, "RUBINO_HOME is not a writable directory: #{home} (#{clean_errno_message(e.message)})"
|
|
303
487
|
end
|
|
304
488
|
end
|
|
305
489
|
end
|
data/rubino-agent.gemspec
CHANGED
|
@@ -96,6 +96,7 @@ Gem::Specification.new do |spec|
|
|
|
96
96
|
spec.add_development_dependency "roo", "~> 2.10"
|
|
97
97
|
|
|
98
98
|
# Development dependencies
|
|
99
|
+
spec.add_development_dependency "parallel_tests", "~> 4.7"
|
|
99
100
|
spec.add_development_dependency "rack-test", "~> 2.1"
|
|
100
101
|
spec.add_development_dependency "rspec", "~> 3.12"
|
|
101
102
|
spec.add_development_dependency "rubocop", "~> 1.60"
|
data/skills/ruby-expert/SKILL.md
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: ruby-expert
|
|
3
3
|
description: Deep Ruby & Rails expertise — idioms, OO design, metaprogramming, errors/types, concurrency, Rails, testing, performance, security, tooling, gem authoring. Load when writing, reviewing, debugging, or designing Ruby or Rails code, or when a Ruby/Rails decision needs an authoritative answer.
|
|
4
|
+
languages: [ruby]
|
|
4
5
|
---
|
|
5
6
|
|
|
6
7
|
# Ruby expert
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: rubino-agent
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jhon Rojas
|
|
@@ -387,6 +387,20 @@ dependencies:
|
|
|
387
387
|
- - "~>"
|
|
388
388
|
- !ruby/object:Gem::Version
|
|
389
389
|
version: '2.10'
|
|
390
|
+
- !ruby/object:Gem::Dependency
|
|
391
|
+
name: parallel_tests
|
|
392
|
+
requirement: !ruby/object:Gem::Requirement
|
|
393
|
+
requirements:
|
|
394
|
+
- - "~>"
|
|
395
|
+
- !ruby/object:Gem::Version
|
|
396
|
+
version: '4.7'
|
|
397
|
+
type: :development
|
|
398
|
+
prerelease: false
|
|
399
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
400
|
+
requirements:
|
|
401
|
+
- - "~>"
|
|
402
|
+
- !ruby/object:Gem::Version
|
|
403
|
+
version: '4.7'
|
|
390
404
|
- !ruby/object:Gem::Dependency
|
|
391
405
|
name: rack-test
|
|
392
406
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -483,7 +497,9 @@ files:
|
|
|
483
497
|
- install.sh
|
|
484
498
|
- lib/rubino-agent.rb
|
|
485
499
|
- lib/rubino.rb
|
|
500
|
+
- lib/rubino/active_agent.rb
|
|
486
501
|
- lib/rubino/active_skill.rb
|
|
502
|
+
- lib/rubino/agent/action_claim_guard.rb
|
|
487
503
|
- lib/rubino/agent/agent_registry.rb
|
|
488
504
|
- lib/rubino/agent/backoff_policy.rb
|
|
489
505
|
- lib/rubino/agent/definition.rb
|
|
@@ -498,7 +514,6 @@ files:
|
|
|
498
514
|
- lib/rubino/agent/prompts/general.txt
|
|
499
515
|
- lib/rubino/agent/prompts/plan.txt
|
|
500
516
|
- lib/rubino/agent/response_validator.rb
|
|
501
|
-
- lib/rubino/agent/router.rb
|
|
502
517
|
- lib/rubino/agent/runner.rb
|
|
503
518
|
- lib/rubino/agent/tool_executor.rb
|
|
504
519
|
- lib/rubino/agent/truncation_continuation.rb
|
|
@@ -560,6 +575,7 @@ files:
|
|
|
560
575
|
- lib/rubino/attachments/defang.rb
|
|
561
576
|
- lib/rubino/attachments/policy.rb
|
|
562
577
|
- lib/rubino/attachments/preamble.rb
|
|
578
|
+
- lib/rubino/boot/config_guard.rb
|
|
563
579
|
- lib/rubino/boot/encryption_key.rb
|
|
564
580
|
- lib/rubino/cli/chat/bang_shell.rb
|
|
565
581
|
- lib/rubino/cli/chat/completion_builder.rb
|
|
@@ -582,6 +598,7 @@ files:
|
|
|
582
598
|
- lib/rubino/commands/built_ins.rb
|
|
583
599
|
- lib/rubino/commands/command.rb
|
|
584
600
|
- lib/rubino/commands/executor.rb
|
|
601
|
+
- lib/rubino/commands/handlers/agent_switch.rb
|
|
585
602
|
- lib/rubino/commands/handlers/agents.rb
|
|
586
603
|
- lib/rubino/commands/handlers/config.rb
|
|
587
604
|
- lib/rubino/commands/handlers/help.rb
|
|
@@ -596,28 +613,24 @@ files:
|
|
|
596
613
|
- lib/rubino/config/defaults.rb
|
|
597
614
|
- lib/rubino/config/loader.rb
|
|
598
615
|
- lib/rubino/config/reasoning_prefs.rb
|
|
616
|
+
- lib/rubino/config/validator.rb
|
|
599
617
|
- lib/rubino/config/writer.rb
|
|
600
618
|
- lib/rubino/context/compressor.rb
|
|
601
619
|
- lib/rubino/context/environment_inspector.rb
|
|
602
620
|
- lib/rubino/context/file_discovery.rb
|
|
603
621
|
- lib/rubino/context/message_boundary.rb
|
|
622
|
+
- lib/rubino/context/project_languages.rb
|
|
604
623
|
- lib/rubino/context/prompt_assembler.rb
|
|
605
624
|
- lib/rubino/context/summary_builder.rb
|
|
606
625
|
- lib/rubino/context/token_budget.rb
|
|
626
|
+
- lib/rubino/context/token_estimate.rb
|
|
607
627
|
- lib/rubino/context/tool_pair_sanitizer.rb
|
|
628
|
+
- lib/rubino/context/tool_result_pruner.rb
|
|
608
629
|
- lib/rubino/database/connection.rb
|
|
609
630
|
- lib/rubino/database/migrations/001_create_initial_schema.rb
|
|
610
|
-
- lib/rubino/database/migrations/002_create_runs.rb
|
|
611
|
-
- lib/rubino/database/migrations/003_create_skill_states.rb
|
|
612
|
-
- lib/rubino/database/migrations/004_create_cron_jobs.rb
|
|
613
|
-
- lib/rubino/database/migrations/005_create_oauth_connections.rb
|
|
614
|
-
- lib/rubino/database/migrations/006_create_webhook_deliveries.rb
|
|
615
|
-
- lib/rubino/database/migrations/007_create_messages_fts.rb
|
|
616
|
-
- lib/rubino/database/migrations/008_create_memory_facts.rb
|
|
617
|
-
- lib/rubino/database/migrations/009_create_memory_graph.rb
|
|
618
|
-
- lib/rubino/database/migrations/010_add_owner_pid_to_sessions.rb
|
|
619
631
|
- lib/rubino/database/migrator.rb
|
|
620
632
|
- lib/rubino/documents.rb
|
|
633
|
+
- lib/rubino/documents/cap_exceeded.rb
|
|
621
634
|
- lib/rubino/documents/converters/csv.rb
|
|
622
635
|
- lib/rubino/documents/converters/docx.rb
|
|
623
636
|
- lib/rubino/documents/converters/html.rb
|
|
@@ -628,6 +641,7 @@ files:
|
|
|
628
641
|
- lib/rubino/documents/converters/xlsx.rb
|
|
629
642
|
- lib/rubino/documents/converters/xml.rb
|
|
630
643
|
- lib/rubino/documents/html.rb
|
|
644
|
+
- lib/rubino/documents/limits.rb
|
|
631
645
|
- lib/rubino/documents/registry.rb
|
|
632
646
|
- lib/rubino/documents/table.rb
|
|
633
647
|
- lib/rubino/errors.rb
|
|
@@ -639,6 +653,7 @@ files:
|
|
|
639
653
|
- lib/rubino/interaction/image_input.rb
|
|
640
654
|
- lib/rubino/interaction/input_queue.rb
|
|
641
655
|
- lib/rubino/interaction/lifecycle.rb
|
|
656
|
+
- lib/rubino/interaction/polishing.rb
|
|
642
657
|
- lib/rubino/interaction/probe.rb
|
|
643
658
|
- lib/rubino/interaction/state.rb
|
|
644
659
|
- lib/rubino/jobs/cron_job_repository.rb
|
|
@@ -688,6 +703,7 @@ files:
|
|
|
688
703
|
- lib/rubino/mcp.rb
|
|
689
704
|
- lib/rubino/mcp/manager.rb
|
|
690
705
|
- lib/rubino/mcp/mcp_tool_wrapper.rb
|
|
706
|
+
- lib/rubino/memory/aux_retry.rb
|
|
691
707
|
- lib/rubino/memory/backend.rb
|
|
692
708
|
- lib/rubino/memory/backends.rb
|
|
693
709
|
- lib/rubino/memory/backends/default.rb
|
|
@@ -696,6 +712,8 @@ files:
|
|
|
696
712
|
- lib/rubino/memory/extractor.rb
|
|
697
713
|
- lib/rubino/memory/flusher.rb
|
|
698
714
|
- lib/rubino/memory/retriever.rb
|
|
715
|
+
- lib/rubino/memory/salience_gate.rb
|
|
716
|
+
- lib/rubino/memory/sqlite_extraction.rb
|
|
699
717
|
- lib/rubino/memory/sqlite_extraction_prompt.rb
|
|
700
718
|
- lib/rubino/memory/sqlite_graph.rb
|
|
701
719
|
- lib/rubino/memory/store.rb
|
|
@@ -708,6 +726,10 @@ files:
|
|
|
708
726
|
- lib/rubino/oauth/provider/google.rb
|
|
709
727
|
- lib/rubino/oauth/registry.rb
|
|
710
728
|
- lib/rubino/oauth/token_encryptor.rb
|
|
729
|
+
- lib/rubino/output/cost.rb
|
|
730
|
+
- lib/rubino/output/headless_block_latch.rb
|
|
731
|
+
- lib/rubino/output/result_serializer.rb
|
|
732
|
+
- lib/rubino/output/turn_recorder.rb
|
|
711
733
|
- lib/rubino/plugins.rb
|
|
712
734
|
- lib/rubino/plugins/registry.rb
|
|
713
735
|
- lib/rubino/run/approval_gate.rb
|
|
@@ -728,11 +750,14 @@ files:
|
|
|
728
750
|
- lib/rubino/security/pattern_matcher.rb
|
|
729
751
|
- lib/rubino/security/prefix_deriver.rb
|
|
730
752
|
- lib/rubino/security/readonly_commands.rb
|
|
753
|
+
- lib/rubino/security/secret_path.rb
|
|
754
|
+
- lib/rubino/security/url_safety.rb
|
|
731
755
|
- lib/rubino/session/exporter.rb
|
|
732
756
|
- lib/rubino/session/message.rb
|
|
733
757
|
- lib/rubino/session/repository.rb
|
|
734
758
|
- lib/rubino/session/store.rb
|
|
735
759
|
- lib/rubino/session/summary_store.rb
|
|
760
|
+
- lib/rubino/skills/installer.rb
|
|
736
761
|
- lib/rubino/skills/prompt_index.rb
|
|
737
762
|
- lib/rubino/skills/registry.rb
|
|
738
763
|
- lib/rubino/skills/skill.rb
|
|
@@ -790,6 +815,7 @@ files:
|
|
|
790
815
|
- lib/rubino/ui/completion_menu.rb
|
|
791
816
|
- lib/rubino/ui/completion_source.rb
|
|
792
817
|
- lib/rubino/ui/escape_reader.rb
|
|
818
|
+
- lib/rubino/ui/headless_trace.rb
|
|
793
819
|
- lib/rubino/ui/indented_io.rb
|
|
794
820
|
- lib/rubino/ui/input_history.rb
|
|
795
821
|
- lib/rubino/ui/live_region.rb
|
|
@@ -805,11 +831,15 @@ files:
|
|
|
805
831
|
- lib/rubino/ui/streaming_markdown.rb
|
|
806
832
|
- lib/rubino/ui/subagent_cards.rb
|
|
807
833
|
- lib/rubino/ui/subagent_view.rb
|
|
834
|
+
- lib/rubino/ui/tool_label.rb
|
|
808
835
|
- lib/rubino/update_check.rb
|
|
836
|
+
- lib/rubino/util/atomic_file.rb
|
|
809
837
|
- lib/rubino/util/duration.rb
|
|
810
838
|
- lib/rubino/util/hyperlink.rb
|
|
839
|
+
- lib/rubino/util/ignore_rules.rb
|
|
811
840
|
- lib/rubino/util/output.rb
|
|
812
841
|
- lib/rubino/util/secrets_mask.rb
|
|
842
|
+
- lib/rubino/util/spill_store.rb
|
|
813
843
|
- lib/rubino/version.rb
|
|
814
844
|
- lib/rubino/workspace.rb
|
|
815
845
|
- mise.toml
|
|
@@ -849,7 +879,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
849
879
|
- !ruby/object:Gem::Version
|
|
850
880
|
version: '0'
|
|
851
881
|
requirements: []
|
|
852
|
-
rubygems_version: 4.0.
|
|
882
|
+
rubygems_version: 4.0.14
|
|
853
883
|
specification_version: 4
|
|
854
884
|
summary: A lightweight Ruby coding and automation agent with persistent memory, sessions,
|
|
855
885
|
and context compaction
|
data/lib/rubino/agent/router.rb
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Rubino
|
|
4
|
-
module Agent
|
|
5
|
-
# Routes user input to the appropriate agent.
|
|
6
|
-
# Handles @mention syntax for subagent invocation and agent switching.
|
|
7
|
-
class Router
|
|
8
|
-
MENTION_REGEX = /\A@(\w+)\s+(.+)/m
|
|
9
|
-
|
|
10
|
-
def initialize(registry:, ui:)
|
|
11
|
-
@registry = registry
|
|
12
|
-
@ui = ui
|
|
13
|
-
@current_agent = registry.default
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
attr_reader :current_agent
|
|
17
|
-
|
|
18
|
-
# Switches to a different primary agent
|
|
19
|
-
def switch_to(agent_name)
|
|
20
|
-
agent = @registry.find(agent_name)
|
|
21
|
-
unless agent
|
|
22
|
-
@ui.error("unknown agent: #{agent_name}")
|
|
23
|
-
return false
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
unless agent.primary?
|
|
27
|
-
@ui.error("cannot switch to subagent '#{agent_name}'. Use @#{agent_name} to invoke it.")
|
|
28
|
-
return false
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
@current_agent = agent
|
|
32
|
-
@ui.info("Switched to agent: #{agent.name}")
|
|
33
|
-
true
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
# Routes input, returning [agent_definition, cleaned_input]
|
|
37
|
-
def route(input)
|
|
38
|
-
# Check for @mention
|
|
39
|
-
if input.match?(MENTION_REGEX)
|
|
40
|
-
match = input.match(MENTION_REGEX)
|
|
41
|
-
agent_name = match[1]
|
|
42
|
-
actual_input = match[2]
|
|
43
|
-
|
|
44
|
-
agent = @registry.find(agent_name)
|
|
45
|
-
return [agent, actual_input] if agent && (agent.subagent? || agent.primary?)
|
|
46
|
-
|
|
47
|
-
@ui.warning("Unknown agent '#{agent_name}', using current agent")
|
|
48
|
-
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
[@current_agent, input]
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
# Returns available agent names for autocomplete
|
|
55
|
-
def available_mentions
|
|
56
|
-
@registry.subagents.map { |a| "@#{a.name}" }
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
# Returns primary agent names for switching
|
|
60
|
-
def switchable_agents
|
|
61
|
-
@registry.primary_agents.map(&:name)
|
|
62
|
-
end
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
Sequel.migration do
|
|
4
|
-
up do
|
|
5
|
-
create_table(:runs) do
|
|
6
|
-
String :id, primary_key: true
|
|
7
|
-
String :session_id, null: false
|
|
8
|
-
String :status, null: false, default: "queued" # queued|running|completed|failed|stopped
|
|
9
|
-
Text :input_text
|
|
10
|
-
Text :attachments_json
|
|
11
|
-
Text :skills_json
|
|
12
|
-
String :model
|
|
13
|
-
String :provider
|
|
14
|
-
Integer :tokens_input, default: 0
|
|
15
|
-
Integer :tokens_output, default: 0
|
|
16
|
-
Text :error
|
|
17
|
-
Boolean :stop_requested, null: false, default: false
|
|
18
|
-
String :started_at
|
|
19
|
-
String :finished_at
|
|
20
|
-
String :created_at, null: false
|
|
21
|
-
String :updated_at, null: false
|
|
22
|
-
|
|
23
|
-
foreign_key [:session_id], :sessions, key: :id
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
add_index :runs, :session_id
|
|
27
|
-
add_index :runs, :status
|
|
28
|
-
|
|
29
|
-
alter_table(:events) do
|
|
30
|
-
add_column :run_id, String
|
|
31
|
-
add_column :seq, Integer # per-session monotonic seq for SSE Last-Event-ID
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
add_index :events, :run_id
|
|
35
|
-
add_index :events, %i[session_id seq]
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
down do
|
|
39
|
-
alter_table(:events) do
|
|
40
|
-
drop_column :seq
|
|
41
|
-
drop_column :run_id
|
|
42
|
-
end
|
|
43
|
-
drop_table(:runs)
|
|
44
|
-
end
|
|
45
|
-
end
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
Sequel.migration do
|
|
4
|
-
up do
|
|
5
|
-
create_table(:skill_states) do
|
|
6
|
-
String :name, primary_key: true
|
|
7
|
-
Boolean :enabled, null: false, default: true
|
|
8
|
-
String :updated_at, null: false
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
down do
|
|
13
|
-
drop_table(:skill_states)
|
|
14
|
-
end
|
|
15
|
-
end
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
Sequel.migration do
|
|
4
|
-
up do
|
|
5
|
-
create_table(:cron_jobs) do
|
|
6
|
-
String :id, primary_key: true
|
|
7
|
-
String :name, null: false
|
|
8
|
-
String :schedule, null: false # cron expression
|
|
9
|
-
Text :prompt, null: false
|
|
10
|
-
Text :skills_json
|
|
11
|
-
String :model
|
|
12
|
-
String :provider
|
|
13
|
-
String :deliver, null: false, default: "local" # local|webhook
|
|
14
|
-
Boolean :enabled, null: false, default: true
|
|
15
|
-
String :last_run_at
|
|
16
|
-
String :last_run_id
|
|
17
|
-
String :created_at, null: false
|
|
18
|
-
String :updated_at, null: false
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
add_index :cron_jobs, :enabled
|
|
22
|
-
add_index :cron_jobs, :name
|
|
23
|
-
|
|
24
|
-
alter_table(:runs) do
|
|
25
|
-
add_column :cron_job_id, String
|
|
26
|
-
end
|
|
27
|
-
add_index :runs, :cron_job_id
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
down do
|
|
31
|
-
alter_table(:runs) do
|
|
32
|
-
drop_column :cron_job_id
|
|
33
|
-
end
|
|
34
|
-
drop_table(:cron_jobs)
|
|
35
|
-
end
|
|
36
|
-
end
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
Sequel.migration do
|
|
4
|
-
up do
|
|
5
|
-
create_table(:oauth_connections) do
|
|
6
|
-
String :id, primary_key: true
|
|
7
|
-
String :provider, null: false
|
|
8
|
-
String :account_id, null: false
|
|
9
|
-
String :account_email
|
|
10
|
-
Text :access_token, null: false # encrypted
|
|
11
|
-
Text :refresh_token # encrypted
|
|
12
|
-
String :expires_at
|
|
13
|
-
Text :scopes_json, null: false
|
|
14
|
-
Text :metadata_json
|
|
15
|
-
String :created_at, null: false
|
|
16
|
-
String :updated_at, null: false
|
|
17
|
-
|
|
18
|
-
unique %i[provider account_id]
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
add_index :oauth_connections, :provider
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
down do
|
|
25
|
-
drop_table(:oauth_connections)
|
|
26
|
-
end
|
|
27
|
-
end
|
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
Sequel.migration do
|
|
4
|
-
up do
|
|
5
|
-
create_table(:webhook_deliveries) do
|
|
6
|
-
String :id, primary_key: true
|
|
7
|
-
String :job_id
|
|
8
|
-
String :run_id
|
|
9
|
-
String :target_url, null: false
|
|
10
|
-
# request_id (X-Rubino-Delivery-Id) is unique across delivery rows so
|
|
11
|
-
# a crash-then-restart cannot create two pending rows for the same logical
|
|
12
|
-
# attempt; the resume hook keys off this column.
|
|
13
|
-
String :request_id, null: false, unique: true
|
|
14
|
-
String :payload_sha256, null: false
|
|
15
|
-
Integer :attempt_count, null: false, default: 0
|
|
16
|
-
String :status, null: false, default: "pending" # pending|delivered|failed|dead
|
|
17
|
-
Text :last_error
|
|
18
|
-
Text :payload_json, null: false
|
|
19
|
-
String :scheduled_at, null: false
|
|
20
|
-
String :delivered_at
|
|
21
|
-
String :created_at, null: false
|
|
22
|
-
String :updated_at, null: false
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
add_index :webhook_deliveries, :status
|
|
26
|
-
add_index :webhook_deliveries, :scheduled_at
|
|
27
|
-
add_index :webhook_deliveries, :job_id
|
|
28
|
-
add_index :webhook_deliveries, :run_id
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
down do
|
|
32
|
-
drop_table(:webhook_deliveries)
|
|
33
|
-
end
|
|
34
|
-
end
|