fix: exclude slash-command/setup messages from LLM context (#2634) (#2640)

Slash-command replies and the echoed /setup command are persisted to session
history so they render in the transcript, but they are UI chatter the user
never meant as conversation. They were sent to the model on the next turn,
which then commented on '/setup ...' and exposed transient values (e.g. the
Copilot device user_code) to the LLM.

- get_context_messages() (the LLM-API view) now skips messages tagged
  metadata.source == 'slash'. Display/history-load paths use raw history and
  are unaffected.
- slashCommands.js tags the echoed user command with source:'slash' too (the
  assistant replies already carried it); the user line was the one untagged
  path that still reached context.

Fixes #2634.
This commit is contained in:
Kenny Van de Maele
2026-06-04 21:42:23 +02:00
committed by GitHub
parent baf9179d94
commit 67782e684e
3 changed files with 61 additions and 3 deletions

View File

@@ -76,8 +76,20 @@ class Session:
_session_manager._persist_message(self.id, message)
def get_context_messages(self) -> List[Dict[str, Any]]:
"""Get messages in format for LLM API."""
return [msg.to_dict() for msg in self.history]
"""Get messages in format for LLM API.
Slash-command / setup replies are persisted to history so they render
in the transcript, but they are UI chatter (e.g. ``/setup ...`` and its
status lines) the user never meant as conversation. They carry
``metadata.source == "slash"``; exclude them here so they never reach
the model. Display/history-load paths use the raw ``history`` and are
unaffected.
"""
return [
msg.to_dict()
for msg in self.history
if (msg.metadata or {}).get("source") != "slash"
]
def get(self, key: str, default=None):
"""Dict-like access for compatibility."""