# `PtcRunner.SubAgent.Definition`
[🔗](https://github.com/andreasronge/ptc_runner/blob/main/lib/ptc_runner/sub_agent/definition.ex#L1)

Struct and type definitions for SubAgent configuration.

Defines the `t()` struct used throughout the SubAgent pipeline — from
compilation through loop execution. Also defines shared types like
`language_spec()`, `system_prompt_opts()`, `llm_callback()`, etc.

You typically don't build this struct directly; use `PtcRunner.SubAgent.Compiler.compile/2`
or the DSL macros instead.

# `compression_opts`

```elixir
@type compression_opts() :: nil | false | true | module() | {module(), keyword()}
```

Compression strategy configuration.

Can be:
- `nil` or `false` - Compression disabled (default)
- `true` - Use default strategy (`SingleUserCoalesced`) with default options
- `Module` - Use custom strategy module with default options
- `{Module, opts}` - Use custom strategy module with custom options

See `PtcRunner.SubAgent.Compression` for details.

# `format_options`

```elixir
@type format_options() :: [
  feedback_limit: pos_integer(),
  feedback_max_chars: pos_integer(),
  history_max_bytes: pos_integer(),
  result_limit: pos_integer(),
  result_max_chars: pos_integer(),
  max_print_length: pos_integer()
]
```

Output format options for truncation and display.

Fields:
- `feedback_limit` - Max collection items in turn feedback (default: 10)
- `feedback_max_chars` - Max chars in turn feedback (default: 512)
- `history_max_bytes` - Truncation limit for `*1/*2/*3` history (default: 512)
- `result_limit` - Inspect `:limit` for final result (default: 50)
- `result_max_chars` - Final string truncation (default: 500)
- `max_print_length` - Max chars per `println` call (default: 2000)

# `language_spec`

```elixir
@type language_spec() ::
  String.t()
  | atom()
  | {:profile, atom()}
  | {:profile, atom(), keyword()}
  | (map() -&gt; String.t())
```

Language spec for system prompts.

Can be:
- String: used as-is
- Atom: resolved via `PtcRunner.Lisp.LanguageSpec.get!/1` (e.g., `:explicit_return`, `:single_shot`)
- Tuple: structured profile `{:profile, behavior, opts}` — see `PtcRunner.Lisp.LanguageSpec.resolve_profile/1`
- Function: callback receiving context map with `:turn`, `:model`, `:memory`, `:messages`

# `llm_callback`

```elixir
@type llm_callback() :: (map() -&gt; {:ok, llm_response()} | {:error, term()})
```

# `llm_ref`

```elixir
@type llm_ref() :: String.t() | atom() | llm_callback()
```

LLM reference for SubAgent execution.

Can be:
- `String.t()` — Model alias (e.g., `"haiku"`) or full ID (e.g., `"openrouter:anthropic/claude-haiku-4.5"`)
- `atom()` — Registry key (e.g., `:haiku`) that looks up in `llm_registry`
- `llm_callback()` — Direct callback function

# `llm_registry`

```elixir
@type llm_registry() :: %{required(atom()) =&gt; llm_callback()}
```

# `llm_response`

```elixir
@type llm_response() ::
  String.t()
  | %{
      :content =&gt; String.t(),
      optional(:tokens) =&gt; %{
        optional(:input) =&gt; pos_integer(),
        optional(:output) =&gt; pos_integer()
      }
    }
  | %{
      :tool_calls =&gt; [map()],
      optional(:content) =&gt; String.t() | nil,
      optional(:tokens) =&gt; map()
    }
```

LLM response format.

Can be either a plain string (backward compatible) or a map with content and optional tokens.
When tokens are provided, they are included in telemetry measurements and accumulated in Step.usage.

For `:tool_calling` mode, the LLM callback may also return tool calls:
`%{tool_calls: [%{id: "call_1", name: "search", args: %{"q" => "foo"}}], content: nil | "...", tokens: %{...}}`

# `output_mode`

```elixir
@type output_mode() :: :ptc_lisp | :text
```

Output mode for SubAgent execution.

- `:ptc_lisp` - Default. LLM generates PTC-Lisp code that is executed.
- `:text` - Auto-detects behavior from tools and signature return type:
  - No tools + `:string`/no signature → raw text response
  - No tools + complex return type → JSON response (validated)
  - Tools + `:string`/no signature → tool loop → text answer
  - Tools + complex return type → tool loop → JSON answer

# `plan_step`

```elixir
@type plan_step() :: {String.t(), String.t()}
```

Plan step definition.

Each step is a `{id, description}` tuple where:
- `id` is a string identifier (used as key in summaries)
- `description` is a human-readable description of the step

# `system_prompt_opts`

```elixir
@type system_prompt_opts() ::
  %{
    optional(:prefix) =&gt; String.t(),
    optional(:suffix) =&gt; String.t(),
    optional(:language_spec) =&gt; language_spec(),
    optional(:output_format) =&gt; String.t()
  }
  | (String.t() -&gt; String.t())
  | String.t()
```

# `t`

```elixir
@type t() :: %PtcRunner.SubAgent.Definition{
  builtin_tools: [atom()],
  completion_mode: :explicit,
  compression: compression_opts(),
  context_descriptions: map() | nil,
  description: String.t() | nil,
  field_descriptions: map() | nil,
  float_precision: non_neg_integer(),
  format_options: format_options(),
  journaling: boolean(),
  llm: llm_ref() | nil,
  llm_query: boolean(),
  llm_retry: map() | nil,
  max_depth: pos_integer(),
  max_heap: pos_integer() | nil,
  max_tool_calls: pos_integer() | nil,
  max_turns: pos_integer(),
  memory_limit: pos_integer() | nil,
  memory_strategy: :strict | :rollback,
  mission_timeout: pos_integer() | nil,
  name: String.t() | nil,
  output: output_mode(),
  parsed_signature: {:signature, list(), term()} | nil,
  plan: [plan_step()],
  pmap_max_concurrency: pos_integer(),
  pmap_timeout: term(),
  progress_fn: (map(), term() -&gt; {String.t(), term()}) | nil,
  prompt: String.t(),
  prompt_limit: map() | nil,
  retry_turns: non_neg_integer(),
  schema: term(),
  signature: String.t() | nil,
  system_prompt: system_prompt_opts() | nil,
  thinking: boolean(),
  timeout: pos_integer(),
  tools: map(),
  turn_budget: pos_integer()
}
```

---

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