Native tool-call transport handler for ptc_transport: :tool_call agents.
Owns the loop branch that consumes assistant turns shaped as native
tool_calls (the lisp_eval invocation) plus the direct
final-answer path. App tools are never exposed as provider-native
tools — they remain callable only from inside a PTC-Lisp program via
(tool/name ...). The system prompt continues to render the full
app-tool inventory.
See Plans/ptc-lisp-tool-call-transport.md for the full design.
Naming
In this module, "tool call" without qualifier refers to a native
tool call (the lisp_eval invocation on the provider wire).
PTC-Lisp (tool/...) invocations continue to be called "app tool
calls" and surface as lisp_step.tool_calls.
Public surface
tool_name/0,tool_description/0,tool_schema/0,request_tools/1— Phase 3 schema/request shape (unchanged).handle_response/3— Phase 4 entry point. Branches on the assistant response to either execute a singlelisp_evalcall, treat direct content as a final answer, or surface a paired protocol error.
Summary
Functions
Handle an assistant turn under ptc_transport: :tool_call.
Build the request tools list for an agent.
The canonical description string for the lisp_eval tool in
v1 PTC :tool_call mode.
The reserved native tool name ("lisp_eval").
Build the OpenAI-format tool schema for lisp_eval.
Functions
@spec handle_response( map(), PtcRunner.SubAgent.Definition.t(), PtcRunner.SubAgent.Loop.State.t() ) :: {:continue, PtcRunner.SubAgent.Loop.State.t(), PtcRunner.Turn.t()} | {:stop, {:ok | :error, PtcRunner.Step.t()}, PtcRunner.Turn.t() | nil, map() | nil}
Handle an assistant turn under ptc_transport: :tool_call.
Returns the same signal as content-mode response handling:
{:continue, new_state, turn}for non-terminating turns.{:stop, {:ok | :error, step}, turn, turn_tokens}to terminate.
Branches:
- No native tool calls: treat direct content as the final answer (signature handling matrix). Markdown-fenced clojure as content triggers targeted feedback (R16).
- Exactly one
lisp_evalcall: execute, append pairedrole: :toolmessage, continue or terminate on(return)/(fail). - One unknown tool call: paired
unknown_toolerror, continue. - More than one native tool call: paired
multiple_tool_callserror pertool_call_id, continue (R12, R13).
@spec request_tools(PtcRunner.SubAgent.Definition.t()) :: [map()] | nil
Build the request tools list for an agent.
In :tool_call mode, returns exactly one entry — the
lisp_eval schema — regardless of how many app tools the agent
declares. App tools stay in the system prompt's Tool Inventory and are
callable only from inside the sandboxed program.
In :content mode, returns nil so the request omits the tools
field (matching today's behavior where PTC-Lisp app tools are not
exposed as native provider tools).
@spec tool_description() :: String.t()
The canonical description string for the lisp_eval tool in
v1 PTC :tool_call mode.
Delegates to PtcRunner.PtcToolProtocol.tool_description/1 with the
:in_process_with_app_tools profile. Tests assert stable substrings
against this value; do not paraphrase the guidance elsewhere.
@spec tool_name() :: String.t()
The reserved native tool name ("lisp_eval").
@spec tool_schema() :: map()
Build the OpenAI-format tool schema for lisp_eval.
Returns a single map. The intended use in :tool_call mode is to put
exactly this one entry in the LLM request's tools field — app tools
are never included.
Examples
iex> schema = PtcRunner.SubAgent.Loop.PtcToolCall.tool_schema()
iex> schema["type"]
"function"
iex> schema["function"]["name"]
"lisp_eval"
iex> schema["function"]["parameters"]["required"]
["program"]