Problem
Several reporters append the user prompt to per-agent history before the provider call succeeds, but they do not roll that mutation back on failure.
Current behavior
This pattern appears in multiple places:
llm:chat
llm:chat-async
llm:choose
llm:chat-with-template (already problematic for separate reasons)
In each case, the user-side prompt is added to the mutable history buffer before the provider response is known. If the request fails because of timeout, invalid credentials, network error, or parsing error, the reporter throws an exception but the partial prompt remains in history.
Why this matters
This makes failures stateful in a way the caller cannot easily see:
- a retry runs with extra prompt context that came from the failed attempt
llm:choose can pollute history with the internal constrained-choice prompt text
- one transient provider error can permanently alter later conversation behavior for that agent
This is especially confusing because the call appears to have failed "cleanly" from NetLogo’s perspective, but the extension has already mutated state.
Affected code
src/main/LLMExtension.scala (ChatReporter.report)
src/main/LLMExtension.scala (ChatAsyncReporter.report)
src/main/LLMExtension.scala (ChooseReporter.report)
src/main/LLMExtension.scala (ChatWithTemplateReporter.report)
Suggested fix directions
- Treat provider calls as transactional with respect to history.
- Build request context from a snapshot and only commit user/assistant messages after success.
- Add failure-path tests proving that history is unchanged after provider exceptions or timeouts.
- Consider whether internal prompts used by
llm:choose should be hidden from persistent user-visible history entirely.
Problem
Several reporters append the user prompt to per-agent history before the provider call succeeds, but they do not roll that mutation back on failure.
Current behavior
This pattern appears in multiple places:
llm:chatllm:chat-asyncllm:choosellm:chat-with-template(already problematic for separate reasons)In each case, the user-side prompt is added to the mutable history buffer before the provider response is known. If the request fails because of timeout, invalid credentials, network error, or parsing error, the reporter throws an exception but the partial prompt remains in history.
Why this matters
This makes failures stateful in a way the caller cannot easily see:
llm:choosecan pollute history with the internal constrained-choice prompt textThis is especially confusing because the call appears to have failed "cleanly" from NetLogo’s perspective, but the extension has already mutated state.
Affected code
src/main/LLMExtension.scala(ChatReporter.report)src/main/LLMExtension.scala(ChatAsyncReporter.report)src/main/LLMExtension.scala(ChooseReporter.report)src/main/LLMExtension.scala(ChatWithTemplateReporter.report)Suggested fix directions
llm:chooseshould be hidden from persistent user-visible history entirely.