never executed always true always false
1 module PureClaw.Handles.Channel
2 ( -- * Message types
3 IncomingMessage (..)
4 , OutgoingMessage (..)
5 -- * Streaming
6 , StreamChunk (..)
7 -- * Handle type
8 , ChannelHandle (..)
9 -- * Implementations
10 , mkNoOpChannelHandle
11 ) where
12
13 import Data.Text (Text)
14
15 import PureClaw.Core.Errors
16 import PureClaw.Core.Types
17
18 -- | A message received from a channel user.
19 data IncomingMessage = IncomingMessage
20 { _im_userId :: UserId
21 , _im_content :: Text
22 }
23 deriving stock (Show, Eq)
24
25 -- | A message to send to a channel user.
26 newtype OutgoingMessage = OutgoingMessage
27 { _om_content :: Text
28 }
29 deriving stock (Show, Eq)
30
31 -- | A chunk of streamed text from the provider.
32 data StreamChunk
33 = ChunkText Text -- ^ Partial text content
34 | ChunkDone -- ^ Stream finished
35 deriving stock (Show, Eq)
36
37 -- | Channel communication capability interface. Concrete implementations
38 -- (CLI, Telegram, Signal) live in @PureClaw.Channels.*@ modules.
39 --
40 -- 'sendError' only accepts 'PublicError' — internal errors with stack
41 -- traces or model names cannot be sent to channel users. This is enforced
42 -- at the type level.
43 data ChannelHandle = ChannelHandle
44 { _ch_receive :: IO IncomingMessage
45 , _ch_send :: OutgoingMessage -> IO ()
46 , _ch_sendError :: PublicError -> IO ()
47 , _ch_sendChunk :: StreamChunk -> IO ()
48 , _ch_streaming :: Bool
49 -- ^ Whether this channel supports streaming output. When 'True',
50 -- the agent loop sends text via '_ch_sendChunk' during generation
51 -- and skips the full '_ch_send'. When 'False', only '_ch_send'
52 -- is used for the final complete response.
53 , _ch_readSecret :: IO Text -- ^ Read a line without echo (CLI only)
54 , _ch_prompt :: Text -> IO Text
55 -- ^ Display a prompt and read input on the same line (no trailing
56 -- newline after the prompt text). For non-interactive channels this
57 -- falls back to send-then-receive.
58 , _ch_promptSecret :: Text -> IO Text
59 -- ^ Like '_ch_prompt' but with echo disabled (for passwords / API keys).
60 -- For non-interactive channels this falls back to '_ch_readSecret'.
61 }
62
63 -- | No-op channel handle. Receive returns an empty message, send and
64 -- sendError are silent. readSecret returns empty text.
65 mkNoOpChannelHandle :: ChannelHandle
66 mkNoOpChannelHandle = ChannelHandle
67 { _ch_receive = pure (IncomingMessage (UserId "") "")
68 , _ch_send = \_ -> pure ()
69 , _ch_sendError = \_ -> pure ()
70 , _ch_sendChunk = \_ -> pure ()
71 , _ch_streaming = False
72 , _ch_readSecret = pure ""
73 , _ch_prompt = \_ -> pure ""
74 , _ch_promptSecret = \_ -> pure ""
75 }