Class: Rubino::Tools::SubagentProbe

Inherits:
Object
  • Object
show all
Defined in:
lib/rubino/tools/subagent_probe.rb

Overview

probe — the parent's EPHEMERAL read-only peek into a running subagent (the second mechanism of the parent<->subagent comm design).

Unlike ‘steer` (a persisted note that changes the child's trajectory), `probe` is read-only and DISCARDED: it takes a SNAPSHOT of the child's current messages, runs ONE side-inference ([child messages] + question) on the child's own model, and returns the answer to the parent. Nothing is appended to the child's history, the child's loop is never touched, and the Q&A never enters the timeline — so a probe can never alter what the subagent does. The cost is one extra model round-trip that is billed but thrown away (keep probes short).

Reuse: the snapshot is just Session::Store#for_session on the child's own session id (the child Runner exposes #session); the inference is a one-shot AdapterFactory.build(…).chat — the SAME adapter seam Lifecycle and the auxiliary client use. No new transport, no shared state.

Constant Summary collapse

PREAMBLE =

The instruction prepended to the one-shot so the child's model answers AS the subagent, from its context-so-far, without trying to continue the task.

"You are the subagent above. Answer the following question from " + "your current context ONLY — do not take any action or continue " + "your task; this is a read-only check. Be brief."

Instance Method Summary collapse

Constructor Details

#initialize(adapter_factory: nil, message_store: nil) ⇒ SubagentProbe

Returns a new instance of SubagentProbe.

Parameters:

  • adapter_factory (#call) (defaults to: nil)

    test seam: a callable taking the resolved model id and returning an LLM adapter (anything responding to #chat). Defaults to the real AdapterFactory.build for the child's model.



31
32
33
34
# File 'lib/rubino/tools/subagent_probe.rb', line 31

def initialize(adapter_factory: nil, message_store: nil)
  @adapter_factory = adapter_factory
  @message_store   = message_store
end

Instance Method Details

#peek(entry:, question:) ⇒ Object

Runs the ephemeral peek and returns the answer string. Best-effort: any failure (no session yet, model error) returns a short diagnostic rather than raising — a probe must never break the parent REPL.



39
40
41
42
43
44
45
46
47
48
49
50
# File 'lib/rubino/tools/subagent_probe.rb', line 39

def peek(entry:, question:)
  snapshot = snapshot_messages(entry)
  messages = [{ role: "user", content: PREAMBLE }] + snapshot +
             [{ role: "user", content: question.to_s }]

  adapter  = build_adapter(entry)
  response = adapter.chat(messages: messages)
  text     = response.respond_to?(:content) ? response.content.to_s : response.to_s
  text.strip.empty? ? "(no answer)" : text.strip
rescue StandardError => e
  "(probe failed: #{e.message})"
end