Orchestrate one recall call: search the graph, interpret the hits against
the buffered conversation, wrap the result in a <gralkor-memory> block.
Pure orchestration — all dependencies (search, interpret LLM call, turns
source) are passed as functions in opts. Production wiring lives in
Gralkor.Client.Native.
See ex-recall in gralkor/TEST_TREES.md.
Summary
Types
@type group_id() :: String.t()
@type opts() :: [ search_fn: search_fn(), interpret_fn: interpret_fn(), turns_fn: turns_fn(), max_results: pos_integer(), deadline_ms: pos_integer() ]
@type search_fn() :: (group_id(), query :: String.t(), max :: pos_integer() -> {:ok, [String.t()]} | {:error, term()})
@type session_id() :: String.t() | nil
@type turns_fn() :: (String.t() -> [[Gralkor.Message.t()]])