File size: 2,931 Bytes
2415446
a1bab2d
 
2415446
 
 
 
 
 
 
 
 
 
 
 
 
 
a1bab2d
2415446
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
"""Typed dependency surface for messaging slash commands."""
from __future__ import annotations


from dataclasses import dataclass
from typing import Protocol

from .managed_protocols import ManagedClaudeSessionManagerProtocol
from .models import MessageScope
from .platforms.ports import OutboundMessenger
from .transcript import RenderCtx


@dataclass(frozen=True, slots=True)
class ReplyClearResult:
    """Customer-facing result of clearing one literal reply subtree."""


    delete_message_ids: frozenset[str]
    tree_matched: bool


@dataclass(frozen=True, slots=True)
class StopOutcome:
    """Customer-facing stop result after terminal status ownership is assigned."""

    cancelled_count: int
    status_feedback_scopes: frozenset[MessageScope]
    fallback_required: bool

    def requires_confirmation(self, scope: MessageScope) -> bool:
        """Return whether this scope lacks complete terminal status feedback."""
        return (
            self.cancelled_count == 0
            or self.fallback_required
            or self.status_feedback_scopes != frozenset({scope})
        )


class MessagingCommandContext(Protocol):
    """Operations commands need from the messaging workflow."""

    outbound: OutboundMessenger
    cli_manager: ManagedClaudeSessionManagerProtocol

    def format_status(self, emoji: str, label: str, suffix: str | None = None) -> str:
        """Format a platform-specific status line."""
        ...

    def get_render_ctx(self) -> RenderCtx:
        """Return the render context for command output."""
        ...

    async def stop_all_tasks(self) -> StopOutcome:
        """Stop every pending or active messaging task."""
        ...

    async def stop_reply(
        self,
        scope: MessageScope,
        reply_id: str,
    ) -> StopOutcome:
        """Stop the exact voice/tree owner of a replied-to message."""
        ...

    def get_tree_count(self) -> int:
        """Return the number of conversation trees."""
        ...

    async def clear_reply(
        self,
        scope: MessageScope,
        reply_id: str,
    ) -> ReplyClearResult | None:
        """Clear the literal subtree rooted at a replied-to message."""
        ...

    async def clear_chat(self, platform: str, chat_id: str) -> frozenset[str]:
        """Clear one chat and return every tracked platform message ID."""
        ...

    def forget_tracked_message_ids(
        self,
        platform: str,
        chat_id: str,
        message_ids: set[str],
    ) -> None:
        """Forget platform message IDs removed from the managed conversation."""
        ...

    def record_outgoing_message(
        self,
        platform: str,
        chat_id: str,
        msg_id: str | None,
        kind: str,
    ) -> bool:
        """Record an outgoing platform message ID and report registry ownership."""
        ...


__all__ = ["MessagingCommandContext", "ReplyClearResult", "StopOutcome"]