%%%------------------------------------------------------------------- %%% @doc HK-AGENT :: maps a validated, authorized tool call onto the %%% concrete subsystem that performs it. %%% %%% Runs inside the short-lived worker `hk_agent_fsm' spawns for %%% each action (see `run_worker/6' there) -- never inside the FSM %%% itself and never inside `hk_browser_session' or a search job. %%% By the time a call reaches this module, `hk_agent_fsm' has %%% already checked capability grants, argument shape, and loop %%% limits; this module's only job is BROWSER_ACTION/OBSERVATION -- %%% perform the call, return the observation. %%% @end %%%------------------------------------------------------------------- -module(hk_agent_toolbox). -export([dispatch/4]). -spec dispatch(browser | search, atom(), map(), binary()) -> {ok, term()} | {error, term()}. dispatch(browser, create_session, Args, AgentId) -> SessionId = hk_id:new(<<"sess">>), Profile = maps:get(profile, Args, <<"agent">>), case hk_browser_session_sup:start_session(#{session_id => SessionId, owner_agent_id => AgentId, profile => Profile}) of {ok, _Pid} -> {ok, #{session_id => SessionId}}; {error, Reason} -> {error, Reason} end; dispatch(browser, close_session, #{session_id := Sid}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:close_session(Pid) end); dispatch(browser, open_tab, #{session_id := Sid, url := Url}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:open_tab(Pid, Url) end); dispatch(browser, close_tab, #{session_id := Sid, tab_id := TabId}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:close_tab(Pid, TabId) end); dispatch(browser, navigate, #{session_id := Sid, tab_id := TabId, url := Url}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:navigate(Pid, TabId, Url) end); dispatch(browser, back, #{session_id := Sid, tab_id := TabId}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:back(Pid, TabId) end); dispatch(browser, forward, #{session_id := Sid, tab_id := TabId}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:forward(Pid, TabId) end); dispatch(browser, reload, #{session_id := Sid, tab_id := TabId}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:reload(Pid, TabId) end); dispatch(browser, read_page, #{session_id := Sid, tab_id := TabId}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:read_page(Pid, TabId) end); dispatch(browser, query_element, #{session_id := Sid, tab_id := TabId, selector := Sel}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:query_element(Pid, TabId, Sel) end); dispatch(browser, click, #{session_id := Sid, tab_id := TabId, selector := Sel}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:click(Pid, TabId, Sel) end); dispatch(browser, type, #{session_id := Sid, tab_id := TabId, selector := Sel, text := Text}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:type(Pid, TabId, Sel, Text) end); dispatch(browser, scroll, #{session_id := Sid, tab_id := TabId, delta_y := Dy}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:scroll(Pid, TabId, Dy) end); dispatch(browser, screenshot, #{session_id := Sid, tab_id := TabId}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:screenshot(Pid, TabId) end); dispatch(browser, extract_links, #{session_id := Sid, tab_id := TabId}, _AgentId) -> with_session(Sid, fun(Pid) -> hk_browser_session:extract_links(Pid, TabId) end); dispatch(browser, _Op, _Args, _AgentId) -> {error, missing_session_or_tab_id}; dispatch(search, start, #{query := Query} = Args, AgentId) -> MaxIterations = maps:get(max_iterations, Args, 3), hk_search:run(AgentId, Query, #{max_iterations => MaxIterations}). %%% internal with_session(SessionId, Fun) -> case hk_browser_registry:lookup(SessionId) of {ok, Pid} -> Fun(Pid); {error, not_found} -> {error, {no_such_session, SessionId}} end.