Class: Rubino::Tools::SteerTool
- Defined in:
- lib/rubino/tools/steer_tool.rb
Overview
steer — the MODEL-callable parent->child steering note (S2). The model counterpart of the human ‘/agents <id> steer “…”` affordance: a parent agent parks a note onto one of ITS OWN running children; the note is folded into that child’s context at its next turn boundary (Loop#inject_steered_input via the child’s steer_queue) and PERSISTS — it changes the child’s trajectory, unlike the ephemeral ‘probe`.
SCOPED AT CALL (the S1 correction): steer is registered for ALL agents and authorized by OWNERSHIP at call time. The caller is the thread-local Rubino.current_subagent_id (nil ⇒ the human / top-level agent). The target must be the caller’s OWN DIRECT child (BackgroundTasks.owned_by?), so a node with no children simply gets a “not your child” error. This tool does NOT touch the human CLI path (executor.rb’s steer_agent stays unscoped) and is NOT on any strip list.
Mechanism reuse: it wraps BackgroundTasks#steer verbatim (the SAME wire the human CLI uses) — no new transport, no new state.
Instance Attribute Summary
Attributes inherited from Base
#cancel_token, #read_tracker, #stream_chunk, #stream_kind
Instance Method Summary collapse
- #call(arguments) ⇒ Object
-
#config_key ⇒ Object
Gated by the same ‘tools.task` delegation key — steering a child is meaningless without the delegation substrate.
- #description ⇒ Object
- #input_schema ⇒ Object
- #name ⇒ Object
-
#risk_level ⇒ Object
Steering a child is a low-risk, non-destructive nudge (the child carries its own approval/risk gates for anything it does next).
Methods inherited from Base
#cancellation_requested?, #display_name, #emit_chunk, #mcp?, #risky?, #to_tool_definition, workspace_root, workspace_roots
Instance Method Details
#call(arguments) ⇒ Object
62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 |
# File 'lib/rubino/tools/steer_tool.rb', line 62 def call(arguments) task_id = (arguments["task_id"] || arguments[:task_id]).to_s.strip note = (arguments["note"] || arguments[:note]).to_s.strip return "Error: note is required" if note.empty? caller_id = Rubino.current_subagent_id registry = BackgroundTasks.instance entry = task_id.empty? ? nil : registry.find(task_id) # No such id at all → it is not a steerable running subagent. return "Cannot steer #{task_id} — no such running subagent." unless entry # Self-steer is meaningless and would loop a note into your own context. return "Error: cannot steer yourself." if task_id == caller_id # Ownership: only a DIRECT child of the caller may be steered. unless registry.owned_by?(caller_id, task_id) return "Error: #{task_id} is not one of your subagents — you can only steer children you started." end # A finished child has no live loop to fold the note into. return "Cannot steer #{task_id} — it already finished (#{entry.status})." unless live?(entry.status) # Wraps the SAME wire the human CLI uses. A false here means the child's # queue vanished between checks (a just-finished child) — treat as gone. return "Cannot steer #{task_id} — no such running subagent." unless registry.steer(task_id, note) "steer ▸ #{task_id} ← #{Rubino::Util::Output.elide(note, 80)} (parked · enters child context next turn)" end |
#config_key ⇒ Object
Gated by the same ‘tools.task` delegation key — steering a child is meaningless without the delegation substrate. Disabling delegation disables steer too.
30 31 32 |
# File 'lib/rubino/tools/steer_tool.rb', line 30 def config_key "task" end |
#description ⇒ Object
34 35 36 37 38 39 40 41 42 |
# File 'lib/rubino/tools/steer_tool.rb', line 34 def description "Steer one of YOUR OWN running subagents: park a short note that is " \ "folded into that child's context at its NEXT turn (it persists and " \ "changes what the child does). Use it to course-correct a child you " \ "started — add a constraint, narrow the scope, flag something it missed. " \ "You can ONLY steer subagents you started (your direct children); you " \ "cannot steer yourself, a sibling, or a finished child. The note is " \ "queued, not delivered instantly — the child sees it between turns." end |
#input_schema ⇒ Object
44 45 46 47 48 49 50 51 52 53 54 |
# File 'lib/rubino/tools/steer_tool.rb', line 44 def input_schema { type: "object", properties: { task_id: { type: "string", description: "The id (sa_…) of YOUR running subagent to steer." }, note: { type: "string", description: "The steering note to fold into the child's next turn. Keep it short and self-contained." } }, required: %w[task_id note] } end |
#name ⇒ Object
23 24 25 |
# File 'lib/rubino/tools/steer_tool.rb', line 23 def name "steer" end |
#risk_level ⇒ Object
Steering a child is a low-risk, non-destructive nudge (the child carries its own approval/risk gates for anything it does next).
58 59 60 |
# File 'lib/rubino/tools/steer_tool.rb', line 58 def risk_level :low end |