# `PtcRunner.Tool`
[🔗](https://github.com/andreasronge/ptc_runner/blob/main/lib/ptc_runner/tool.ex#L1)

Normalized tool definition for PTC-Lisp and SubAgent.

Tools can be defined in multiple formats and are normalized to this struct.
Supports function references, explicit signatures, and introspection.

## Tool Type

Tools can be one of three types:
- `:native` - Elixir function
- `:llm` - LLM-powered tool (SubAgent only)
- `:subagent` - SubAgent wrapped as tool (SubAgent only)

## Tool Formats

All tool formats are accepted and normalized internally. Common patterns:

### 1. Function reference (extracts @spec and @doc)
```elixir
"get_user" => &MyApp.get_user/1
```

### 2. Function with explicit signature
```elixir
"search" => {&MyApp.search/2, "(query :string, limit :int) -> [{id :int}]"}
```

### 3. Function with signature and description
```elixir
"analyze" => {&MyApp.analyze/1,
  signature: "(data :map) -> {score :float}",
  description: "Analyze data and return anomaly score"
}
```

### 4. Anonymous function
```elixir
"get_time" => fn _args -> DateTime.utc_now() end
```

### 5. Skip validation explicitly
```elixir
"dynamic" => {&MyApp.dynamic/1, :skip}
```

## Type Definition

```elixir
%PtcRunner.Tool{
  name: "get_user",
  function: &MyApp.get_user/1,
  signature: "(id :int) -> {id :int, name :string}",
  description: "Get user by ID",
  type: :native
}
```

## Field Reference

- `name` - Tool name as string (required)
- `function` - Callable (required for native tools)
- `signature` - Optional signature for validation: `"(inputs) -> outputs"`
- `description` - Optional description for LLM visibility
- `type` - Tool type: `:native`, `:llm`, `:subagent`
- `cache` - Enable result caching by `{tool_name, args}` (default: `false`)
- `expose` - Exposure layer for combined text/PTC mode: `:native | :ptc_lisp | :both`
  (default: `nil`, resolved per-mode by `PtcRunner.SubAgent.Exposure`)
 - `native_result` - Native preview metadata (keyword list or `nil`).
   Only valid when `expose: :both` AND `cache: true`. See
   `PtcRunner.SubAgent.Validator` for the validation contract and
   `PtcRunner.SubAgent.Exposure` for resolution semantics.
 - `visibility` - Tool visibility, `:public` or `:private` (default:
   `:public`). Private tools are hidden from LLM-facing discovery and may
   only be called by an active prelude export that declared the tool in its
   inferred `tool_refs`.

## Result Caching

Set `cache: true` on tools with **stable, pure outputs** — identical inputs always
produce the same result. Cached results persist across turns within a single
`SubAgent.run/2` call.

    "get-config" => {&MyApp.get_config/1,
      signature: "(key :string) -> :any",
      cache: true
    }

**Do not use** `cache: true` on tools that read mutable state modifiable by other
tools in the session, as the cache has no automatic invalidation.

In `pmap`, two parallel branches may both miss the cache and execute — last-write-wins
on merge. Only successful results are cached; errors are never stored.

## Exposure And Native Preview (combined text + PTC mode)

When an agent runs in combined mode (`output: :text, ptc_transport: :tool_call`),
each tool may declare how it surfaces to the LLM:

    "search_logs" =>
      {&MyApp.search_logs/1,
       signature: "(query :string) -> [:any]",
       description: "Search log events.",
       expose: :both,
       cache: true,
       native_result: [preview: :metadata]}

Accepted `expose:` values: `:native`, `:ptc_lisp`, `:both`. Missing/`nil` is
accepted and resolved to a per-mode default by
`PtcRunner.SubAgent.Exposure.effective_expose/2`.

`native_result:` is a keyword list with optional fields:

- `preview:` — `:metadata` (default), `:rows`, or a 1-arity function
  that receives the tool's full result and returns a JSON-encodable map.
- `limit:` — positive integer (default `20`); only consulted for
  `preview: :rows`.

`native_result:` is rejected at agent construction unless the tool also
has `expose: :both` AND `cache: true`. Setting `expose: :both, cache: false`
(without `native_result:`) is legal — native calls return the actual result
and PTC-Lisp calls re-execute the function (no shared cache).

# `expose_layer`

```elixir
@type expose_layer() :: :native | :ptc_lisp | :both
```

# `t`

```elixir
@type t() :: %PtcRunner.Tool{
  cache: boolean(),
  description: String.t() | nil,
  expose: expose_layer() | nil,
  function: (map() -&gt; term()) | nil,
  name: String.t(),
  native_result: keyword() | nil,
  signature: String.t() | nil,
  type: :native | :llm | :subagent,
  visibility: :public | :private
}
```

# `tool_format`

```elixir
@type tool_format() ::
  (map() -&gt; term())
  | {(map() -&gt; term()), String.t()}
  | {(map() -&gt; term()), keyword()}
  | {(map() -&gt; term()), :skip}
  | :builtin_grep
  | :builtin_grep_n
  | :builtin_llm_query
  | PtcRunner.SubAgent.LLMTool.t()
  | PtcRunner.SubAgent.SubAgentTool.t()
  | t()
```

# `new`

```elixir
@spec new(String.t(), tool_format()) :: {:ok, t()} | {:error, term()}
```

Creates a normalized Tool struct from a name and format.

Handles multiple input formats and normalizes to a consistent structure.
Attempts to extract @spec and @doc from bare function references.

## Parameters

- `name` - Tool name as string
- `format` - One of: function, {function, signature}, {function, options}, :skip

## Returns

`{:ok, tool}` on success, `{:error, reason}` on failure.

## Examples

Simple function reference (auto-extracts @doc and @spec if available):
    iex> {:ok, tool} = PtcRunner.Tool.new("get_time", fn _args -> DateTime.utc_now() end)
    iex> tool.name
    "get_time"
    iex> tool.type
    :native

Function with explicit signature:
    iex> {:ok, tool} = PtcRunner.Tool.new("search", {fn _args -> [] end, "(query :string, limit :int) -> [{id :int}]"})
    iex> tool.signature
    "(query :string, limit :int) -> [{id :int}]"

Function with signature and description:
    iex> {:ok, tool} = PtcRunner.Tool.new("analyze", {fn _args -> %{} end,
    ...>   signature: "(data :map) -> {score :float}",
    ...>   description: "Analyze data and return anomaly score"
    ...> })
    iex> tool.description
    "Analyze data and return anomaly score"

Skip validation:
    iex> {:ok, tool} = PtcRunner.Tool.new("dynamic", {fn _args -> nil end, :skip})
    iex> tool.signature
    nil

# `private?`

```elixir
@spec private?(t()) :: boolean()
```

Whether a normalized tool is private to host/prelude authority.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
