# `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.

# `compaction_opts`

```elixir
@type compaction_opts() :: nil | false | true | keyword()
```

Compaction configuration for multi-turn agents.

Pressure-triggered context compaction. Compaction reduces the LLM-input
message list when turn count or estimated token usage crosses a threshold.

Can be:
- `nil` or `false` - Compaction disabled (default)
- `true` - Use the `:trim` strategy with default options
- `keyword()` - Use the `:trim` strategy with custom options

See `PtcRunner.SubAgent.Compaction` for option details and validation rules.
Phase 1 supports `strategy: :trim` only; custom strategy modules and
`:summarize` are deferred.

# `completion_mode`

```elixir
@type completion_mode() :: :implicit | :explicit
```

Completion contract for PTC-Lisp SubAgents.

- `:implicit` preserves historic behavior: a final bare expression can
  complete single-shot agents.
- `:explicit` is reserved for hosts that require `(return ...)` or `(fail ...)`
  terminal forms. Phase 0 defines the contract; Worker A owns full
  explicit-mode enforcement.

# `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(),
  mission_log_in: :system_prompt | :user_message
]
```

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)
- `mission_log_in` - Where to inject the mission log: `:system_prompt` (default) or `:user_message`

# `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

# `ptc_reference`

```elixir
@type ptc_reference() :: :compact
```

PTC-Lisp reference card mode for combined-mode (`output: :text,
ptc_transport: :tool_call`) system prompts.

- `:compact` — bounded reference card (≤300 tokens), default in
  combined mode. Describes the core forms (`def`, `tool/`, `return`,
  `println`) and the `full_result_cached: true` cache-reuse pattern.

v1 supports only `:compact`; `:full` is deferred (the validator raises
`ArgumentError` on `:full`). See Addendum #1 of
`Plans/text-mode-ptc-compute-tool.md`.

# `ptc_transport`

```elixir
@type ptc_transport() :: :content | :tool_call
```

Transport mode for `output: :ptc_lisp` agents.

- `:content` - Default. LLM emits a markdown-fenced PTC-Lisp program in the
  assistant message content; the loop parses and executes it.
- `:tool_call` - LLM invokes the internal `lisp_eval` native tool with
  a `program` argument; final answers are returned as direct content and
  validated against `signature:`. Valid only with `output: :ptc_lisp`.

# `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()],
  compaction: compaction_opts(),
  completion_mode: completion_mode(),
  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,
  ptc_reference: ptc_reference(),
  ptc_transport: ptc_transport(),
  retry_turns: non_neg_integer(),
  runtime_prelude: PtcRunner.Lisp.Prelude.t() | nil,
  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*
