fix(classic): convert mid-conversation system messages to user messages

Some LLM providers (notably Anthropic) don't support system messages
in the middle of a conversation. Changed ChatMessage.system() to
ChatMessage.user() for all mid-conversation context messages across
components (action history, context, skills, system clock, todo,
error reporting, LATS, and multi-agent debate strategies).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Nicholas Tindle
2026-02-11 12:53:54 -06:00
parent 6210b3259d
commit f56abcef4f
8 changed files with 9 additions and 9 deletions

View File

@@ -93,7 +93,7 @@ class ActionHistoryComponent(
if step_summaries:
step_summaries_fmt = "\n\n".join(step_summaries)
yield ChatMessage.system(
yield ChatMessage.user(
f"## Progress on your Task so far\n"
"Here is a summary of the steps that you have executed so far, "
"use this as your consideration for determining the next action!\n"

View File

@@ -50,7 +50,7 @@ class ContextComponent(MessageProvider, CommandProvider):
def get_messages(self) -> Iterator[ChatMessage]:
if self.context:
yield ChatMessage.system(
yield ChatMessage.user(
"## Context\n"
f"{self.context.format_numbered(self.workspace)}\n\n"
"When a context item is no longer needed and you are not done yet, "

View File

@@ -99,7 +99,7 @@ class SkillComponent(
catalog_lines.append(
f"- **{name}**{loaded_marker}: {skill.metadata.description}"
)
yield ChatMessage.system("\n".join(catalog_lines))
yield ChatMessage.user("\n".join(catalog_lines))
# Provide loaded skill content
for name, skill in self._loaded_skills.items():
@@ -117,7 +117,7 @@ class SkillComponent(
loaded = " [loaded]" if f in skill.additional_files else ""
skill_content.append(f"- `{f}`{loaded}")
yield ChatMessage.system("\n".join(skill_content))
yield ChatMessage.user("\n".join(skill_content))
# -------------------------------------------------------------------------
# CommandProvider methods

View File

@@ -85,7 +85,7 @@ class SystemComponent(DirectiveProvider, MessageProvider, CommandProvider):
def get_messages(self) -> Iterator[ChatMessage]:
# Clock
yield ChatMessage.system(
yield ChatMessage.user(
f"## Clock\nThe current time and date is {time.strftime('%c')}"
)

View File

@@ -208,7 +208,7 @@ class TodoComponent(
if completed:
lines.append(f"\n**Completed:** {len(completed)} task(s)")
yield ChatMessage.system("\n".join(lines))
yield ChatMessage.user("\n".join(lines))
# -------------------------------------------------------------------------
# Helper Methods

View File

@@ -342,7 +342,7 @@ class Agent(BaseAgent[AnyActionProposal], Configurable[AgentSettings]):
self, prompt: ChatPrompt, exception: Optional[Exception] = None
) -> AnyActionProposal:
if exception:
prompt.messages.append(ChatMessage.system(f"Error: {exception}"))
prompt.messages.append(ChatMessage.user(f"Error: {exception}"))
# Build thinking/reasoning kwargs from app config
thinking_kwargs: dict[str, Any] = {}

View File

@@ -235,7 +235,7 @@ class LATSPromptStrategy(BaseMultiStepPromptStrategy):
ChatMessage.system(system_prompt),
ChatMessage.user(f'Task: """{task}"""'),
*messages,
ChatMessage.system(lats_context),
ChatMessage.user(lats_context),
ChatMessage.user(self._get_phase_instruction()),
],
prefill_response='{\n "thoughts":',

View File

@@ -219,7 +219,7 @@ class MultiAgentDebateStrategy(BaseMultiStepPromptStrategy):
ChatMessage.system(system_prompt),
ChatMessage.user(f'Task: """{task}"""'),
*messages,
ChatMessage.system(debate_context),
ChatMessage.user(debate_context),
ChatMessage.user(self._get_phase_instruction(task)),
],
prefill_response='{\n "thoughts":',