diff --git a/ovos_workshop/skills/converse.py b/ovos_workshop/skills/converse.py index 08110785..373a3dfb 100644 --- a/ovos_workshop/skills/converse.py +++ b/ovos_workshop/skills/converse.py @@ -14,7 +14,7 @@ from ovos_workshop.decorators.killable import AbortEvent, killable_event, AbortQuestion from ovos_workshop.resource_files import ResourceFile -from ovos_workshop.skills.ovos import OVOSSkill +from ovos_workshop.skills.ovos import _core_owns_utterance_handled, OVOSSkill class ConversationalSkill(OVOSSkill): @@ -200,10 +200,15 @@ def _handle_converse_request(self, message: Message): response_message.data["error"] = repr(e) self.bus.emit(response_message) - if is_latest: - self.bus.emit(message.forward(SpecMessage.UTTERANCE_HANDLED)) - else: - self.bus.emit(message.reply(SpecMessage.UTTERANCE_HANDLED)) + if not _core_owns_utterance_handled(): + # PIPELINE-1 §9.5: the orchestrator owns ovos.utterance.handled. With a + # core that emits it on every terminal path (>=2.3.0a1) we must not also + # emit it here, or consumers see the end-marker twice; only emit for an + # older/absent core during the migration window. + if is_latest: + self.bus.emit(message.forward(SpecMessage.UTTERANCE_HANDLED)) + else: + self.bus.emit(message.reply(SpecMessage.UTTERANCE_HANDLED)) def _handle_converse_intents(self, message): """ called before converse method diff --git a/ovos_workshop/skills/fallback.py b/ovos_workshop/skills/fallback.py index 3ebf9002..a192b376 100644 --- a/ovos_workshop/skills/fallback.py +++ b/ovos_workshop/skills/fallback.py @@ -23,7 +23,7 @@ from ovos_utils.skills import get_non_properties from ovos_workshop.decorators.killable import AbortEvent, killable_event -from ovos_workshop.skills.ovos import OVOSSkill +from ovos_workshop.skills.ovos import _core_owns_utterance_handled, OVOSSkill class FallbackSkill(OVOSSkill): @@ -158,7 +158,12 @@ def _handle_fallback_request(self, message: Message): self.bus.emit(message.forward( f"ovos.skills.fallback.{self.skill_id}.response", data={"result": status, "fallback_handler": handler_name})) - self.bus.emit(message.forward(SpecMessage.UTTERANCE_HANDLED)) + if not _core_owns_utterance_handled(): + # PIPELINE-1 §9.5: the orchestrator owns ovos.utterance.handled. With a + # core that emits it on every terminal path (>=2.3.0a1) we must not also + # emit it here, or consumers see the end-marker twice; only emit for an + # older/absent core during the migration window. + self.bus.emit(message.forward(SpecMessage.UTTERANCE_HANDLED)) def register_fallback(self, handler: Callable, priority: int) -> None: """