PtcRunner.SubAgent.Loop.TurnFeedback (PtcRunner v0.11.0)

Copy Markdown View Source

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 turn
  • retry_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

append_turn_info(message, agent, state)

@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.

build_error_feedback(error_message, agent, state)

@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.

execution_feedback(agent, state, lisp_step)

@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=> ...), printed println output, and changed/new memory previews (;; items = [...]). Does not include append_turn_info or append_progress output.
  • :prints — the raw lisp_step.prints list (untruncated; truncation is reflected in feedback and the top-level truncated flag).
  • :result — preview string of lisp_step.return ("user=> ..."), or nil when lisp_step.return is nil or a Var. Rendered unconditionally for the structured field — the suppression rules that format/3 applies to its human-readable string (single-turn agents, or turns with non-empty prints) do not affect this field. Phase 4 of the PTC-Lisp tool-call plan relies on this so the lisp_eval tool-result JSON always carries the final value.
  • :memory.changed — map of name => preview for 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 of agent.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.truncatedtrue if any memory preview was truncated.
  • :visible_truncatedtrue if prints or result was truncated (excludes memory). Used by the MCP one-shot renderer where the memory field is omitted entirely (issue #879) — without this the top-level truncated flag could read true while no truncated field is actually visible to the caller.
  • :truncatedtrue if any preview (prints, result, or memory) was truncated.

format(agent, state, lisp_step)

@spec format(PtcRunner.SubAgent.Definition.t(), map(), map()) ::
  {String.t(), boolean(), term()}

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.

render_initial_progress(agent, progress_state \\ nil)

@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.