Spectre.Runner (Spectre v0.3.0)

Copy Markdown View Source

Executes routed handlers without crossing action boundaries accidentally.

The runner translates a selected route into a result, but protected side effects remain staged until a policy approves them. This is the main safety boundary in Spectre: prompts may propose actions, and deterministic DSL routes may stage actions, but execution is separate.

Summary

Functions

Handles a deterministic action without calling the LLM.

Renders a prompt, calls the LLM, cleans visible replies, and lets the configured planner stage provider-neutral actions.

Renders a no-LLM reply handler.

Runs the handler selected by a route.

Calls an agent-local function declared through a run handler.

Functions

action(action_ref, input, ctx, opts \\ [])

@spec action(
  Spectre.Action.ref(),
  Spectre.Input.t(),
  Spectre.Context.t() | map(),
  keyword()
) ::
  {:ok, Spectre.Result.t()} | {:error, term()}

Handles a deterministic action without calling the LLM.

If the action is protected, Spectre stores it as a pending action effect and opens the configured policy awaitable. Pass reply: plus normal reply renderer options to use a no-LLM confirmation message; otherwise Spectre renders the policy request prompt through the normal ask path.

{:ok, result} = Spectre.Runner.action(:delete_account, input, ctx)

ask(prompt, input, ctx, opts \\ [])

@spec ask(
  atom() | String.t(),
  Spectre.Input.t(),
  Spectre.Context.t() | map(),
  keyword()
) ::
  {:ok, Spectre.Result.t()} | {:error, term()}

Renders a prompt, calls the LLM, cleans visible replies, and lets the configured planner stage provider-neutral actions.

{:ok, result} = Spectre.Runner.ask(:support_answer, input, ctx)

Policy prompts use the same rendering/LLM path but skip action planning so a confirmation question cannot accidentally stage another action.

reply(prompt, input, ctx, opts \\ [])

@spec reply(
  atom() | String.t(),
  Spectre.Input.t(),
  Spectre.Context.t() | map(),
  keyword()
) ::
  {:ok, Spectre.Result.t()} | {:error, term()}

Renders a no-LLM reply handler.

By default reply :some_prompt renders a prompt file under the agent prompt root. A host application may instead pass renderer: {Module, :function}. Renderer callbacks can use arity 3 (prompt, input, ctx), arity 2 (prompt, assigns), or arity 1 (assigns).

reply :fallback, renderer: {MyApp.Replies, :render}

run(route, ctx)

@spec run(Spectre.Route.t(), Spectre.Context.t()) ::
  {:ok, Spectre.Result.t()} | {:error, term()}

Runs the handler selected by a route.

{:ok, result} = Spectre.Runner.run(route, ctx)

run_function(function, input, ctx)

@spec run_function(atom(), Spectre.Input.t(), Spectre.Context.t() | map()) ::
  {:ok, Spectre.Result.t()} | {:error, term()}

Calls an agent-local function declared through a run handler.

{:ok, result} = Spectre.Runner.run_function(:prepare_case, input, ctx)