Turn feedback formatting for SubAgent execution.
Formats execution results and turn state information for LLM feedback. Supports the unified budget model with work turns and retry turns.
Uses Mustache templates from PtcRunner.Prompts:
must_return_warning/0- Warning for final work turnretry_feedback/0- Turn info during retry phase
Summary
Functions
Append turn progress info to a feedback message.
Build error feedback with appropriate turn info based on unified budget model.
Render the execution-feedback portion of a PTC-Lisp turn into a structured map.
Format execution result feedback for the next LLM turn.
Render initial progress for the first user message.
Functions
@spec append_turn_info(String.t(), PtcRunner.SubAgent.Definition.t(), map()) :: String.t()
Append turn progress info to a feedback message.
For multi-turn agents with retry_turns, shows unified budget info. For multi-turn agents without retry_turns, shows legacy turn count.
@spec build_error_feedback(String.t(), PtcRunner.SubAgent.Definition.t(), map()) :: String.t()
Build error feedback with appropriate turn info based on unified budget model.
This is used by the loop to format error messages with context about work/retry budgets.
@spec execution_feedback(PtcRunner.SubAgent.Definition.t() | map(), map(), map()) :: %{ feedback: String.t(), prints: [String.t()], result: String.t() | nil, memory: %{ changed: %{required(String.t()) => String.t()}, stored_keys: [String.t()], truncated: boolean() }, visible_truncated: boolean(), truncated: boolean() }
Render the execution-feedback portion of a PTC-Lisp turn into a structured map.
This is the canonical execution-feedback renderer. format/3 calls it and
then layers on append_turn_info/3 and append_progress/4 for content-mode
multi-turn agents. The upcoming :tool_call transport (Phase 4 of the
PTC-Lisp tool-call plan) reuses this function directly to populate the
feedback field of the lisp_eval tool-result JSON, ensuring loop
control scaffolding (turn budgets, progress_fn output) does not leak into
native tool results.
Fields
:feedback— the LLM-facing feedback string. Contains only the execution portion: result preview (user=> ...), printedprintlnoutput, and changed/new memory previews (;; items = [...]). Does not includeappend_turn_infoorappend_progressoutput.:prints— the rawlisp_step.printslist (untruncated; truncation is reflected infeedbackand the top-leveltruncatedflag).:result— preview string oflisp_step.return("user=> ..."), ornilwhenlisp_step.returnisnilor aVar. Rendered unconditionally for the structured field — the suppression rules thatformat/3applies to its human-readable string (single-turn agents, or turns with non-emptyprints) do not affect this field. Phase 4 of the PTC-Lisp tool-call plan relies on this so thelisp_evaltool-result JSON always carries the final value.:memory.changed— map ofname => previewfor memory bindings that are new or whose value changed since the previous turn. String-keyed for direct use in tool-result JSON. Populated unconditionally regardless ofagent.max_turns, for the same Phase 4 reason.:memory.stored_keys— sorted list of all currently-stored memory binding names (string-keyed). Fallback orientation hint when nothing changed or previews were truncated.:memory.truncated—trueif any memory preview was truncated.:visible_truncated—trueifprintsorresultwas truncated (excludes memory). Used by the MCP one-shot renderer where the memory field is omitted entirely (issue #879) — without this the top-leveltruncatedflag could readtruewhile no truncated field is actually visible to the caller.:truncated—trueif any preview (prints, result, or memory) was truncated.
Format execution result feedback for the next LLM turn.
Returns {feedback_string, truncated?, new_progress_state}.
Only shows explicit println output - the LLM must be intentional about what it inspects.
This is a thin wrapper around execution_feedback/3 that additionally appends
append_turn_info/3 and append_progress/4 output. Tool-call transport
(Phase 4 of the PTC-Lisp tool-call plan) reuses execution_feedback/3
directly so loop-control scaffolding (turn budgets, custom progress_fn
output) does not leak into the lisp_eval tool-result JSON.
@spec render_initial_progress(PtcRunner.SubAgent.Definition.t(), term()) :: {String.t(), term()}
Render initial progress for the first user message.
Returns {text, progress_state}. Uses progress_fn if set, otherwise
renders the default checklist from plan. Returns {"", nil} if no plan
and no custom progress_fn.