Class: Rubino::Interaction::Lifecycle
- Inherits:
-
Object
- Object
- Rubino::Interaction::Lifecycle
- Defined in:
- lib/rubino/interaction/lifecycle.rb
Overview
Orchestrates the full lifecycle of a single user interaction. Coordinates all phases from input to final response and post-turn jobs.
Constant Summary collapse
- AUX_TITLE_MAX_CHARS =
The longest title the aux summary may produce; matches derive_title's default truncation so both the aux and deterministic paths yield comparably short titles (#45).
60- PRIORITY_EXTRACT_MEMORY =
Queue priority for the user-visible memory save (#79). Lower = drained first (the queue orders by
priority, run_at). Below the default 100 the other post-turn jobs use, so an ExtractMemoryJob jumps ahead of any default-priority backlog and the "remember X" → recall is prompt. 50
Instance Attribute Summary collapse
-
#last_stop_reason ⇒ Object
readonly
How the turn just run by #execute terminated (Agent::Loop#stop_reason), read back by the owning Runner so the subagent-completion path can report a force-summarized/truncated run as PARTIAL rather than "completed".
Instance Method Summary collapse
-
#active_session ⇒ Object
The session this lifecycle is currently bound to.
-
#execute(input, image_paths: [], input_queue: nil, paste_expansions: []) ⇒ Object
Executes the full interaction lifecycle for a user input.
-
#initialize(session:, event_bus:, ui:, config:, ignore_rules: false, agent_definition: nil, cancel_token: nil, model_override: nil, provider_override: nil, max_tool_iterations: nil, polishing: nil, interactive: false) ⇒ Lifecycle
constructor
A new instance of Lifecycle.
Constructor Details
#initialize(session:, event_bus:, ui:, config:, ignore_rules: false, agent_definition: nil, cancel_token: nil, model_override: nil, provider_override: nil, max_tool_iterations: nil, polishing: nil, interactive: false) ⇒ Lifecycle
Returns a new instance of Lifecycle.
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
# File 'lib/rubino/interaction/lifecycle.rb', line 34 def initialize(session:, event_bus:, ui:, config:, ignore_rules: false, agent_definition: nil, cancel_token: nil, model_override: nil, provider_override: nil, max_tool_iterations: nil, polishing: nil, interactive: false) @session = session @event_bus = event_bus @ui = ui @config = config @ignore_rules = ignore_rules @agent_definition = agent_definition @cancel_token = cancel_token @model_override = model_override @provider_override = provider_override # The Runner-owned detached post-turn polishing worker (#319). When # given, the post-turn jobs are handed to it to drain OFF the live # turn's critical path so the next prompt is never gated. Nil on the # API/server path and nested subagent runs, which keep the original # synchronous inline drain (no interactive prompt to free up). @polishing = polishing # True only on the interactive REPL (more in-process turns follow). Gates # automatic memory extraction OFF the live KV slot between turns (#608c). @interactive = interactive # Explicit per-run cap from `--max-turns` (Runner → here → IterationBudget). # nil ⇒ use the configured agent_max_tool_iterations (#141). @max_tool_iterations = max_tool_iterations @session_repo = Session::Repository.new @message_store = Session::Store.new end |
Instance Attribute Details
#last_stop_reason ⇒ Object (readonly)
How the turn just run by #execute terminated (Agent::Loop#stop_reason), read back by the owning Runner so the subagent-completion path can report a force-summarized/truncated run as PARTIAL rather than "completed". nil until a turn has run.
32 33 34 |
# File 'lib/rubino/interaction/lifecycle.rb', line 32 def last_stop_reason @last_stop_reason end |
Instance Method Details
#active_session ⇒ Object
The session this lifecycle is currently bound to. Starts as the session passed in, but an automatic budget-triggered compaction swaps it to the compaction child (see #check_and_compact). The owning Runner reads this back after #execute so the NEXT turn runs on the (small) child rather than re-compacting the dead parent every turn (P3 F1). Defined as a method (not attr_reader) because @session is REASSIGNED on compaction.
24 25 26 |
# File 'lib/rubino/interaction/lifecycle.rb', line 24 def active_session @session end |
#execute(input, image_paths: [], input_queue: nil, paste_expansions: []) ⇒ Object
Executes the full interaction lifecycle for a user input.
image_paths are vision-capable attachments routed natively to the
primary model (ruby_llm with: slot); only consumed on the first
iteration of the inner agent loop. Subsequent iterations carry tool
results, not user input, and don't re-attach the images.
input_queue is the optional steering hand-off (Interaction::InputQueue)
for mid-turn injection: when given, the inner agent loop drains any text
the user typed while it was working and folds it into the turn at a safe
iteration boundary. Nil for the API/server path and for nested SUBAGENT
runs, which stay isolated — no user injection, exactly as before.
73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 |
# File 'lib/rubino/interaction/lifecycle.rb', line 73 def execute(input, image_paths: [], input_queue: nil, paste_expansions: []) @event_bus.emit(Events::INTERACTION_STARTED, input: input) # 1. Persist user message (input, paste_expansions: paste_expansions) # 2. Load memory (if enabled) memory_context = load_memory(input) # 3. Build prompt/context = (input, memory_context) tools = load_tools # 4. Check token budget = check_and_compact() # 5. Run agent loop response = run_agent_loop(, tools, image_paths: image_paths, input_queue: input_queue) # 6. Persist session state update_session_state # 7. Enqueue post-turn jobs enqueue_post_turn_jobs # 8. Finish # Carry the final assistant text as the terminal event's authoritative # output, regardless of streaming mode. Streaming consumers also receive # it incrementally via MODEL_STREAM (message.delta), but the # non-streaming path emits no deltas — so without this, a completed run # would terminate with no final text for clients to display. This makes # run.completed the single source of truth for the answer. @event_bus.emit(Events::INTERACTION_FINISHED, output: response.to_s) response rescue StandardError => e @event_bus.emit(Events::INTERACTION_FAILED, error: e.) raise end |