/ internal / daemon / types.go
types.go
  1  package daemon
  2  
  3  import "encoding/json"
  4  
  5  // Server -> Daemon message types
  6  const (
  7  	MsgTypeConnected = "connected"
  8  	MsgTypeMessage   = "message"
  9  	MsgTypeClaimAck  = "claim_ack"
 10  	MsgTypeSystem    = "system"
 11  )
 12  
 13  // Daemon -> Server message types
 14  const (
 15  	MsgTypeClaim      = "claim"
 16  	MsgTypeReply      = "reply"
 17  	MsgTypeProgress   = "progress"
 18  	MsgTypeDisconnect = "disconnect"
 19  	MsgTypeEvent      = "event"
 20  	MsgTypeProactive  = "proactive"
 21  )
 22  
 23  // Approval protocol (bidirectional relay via Cloud)
 24  const (
 25  	MsgTypeApprovalRequest  = "approval_request"
 26  	MsgTypeApprovalResponse = "approval_response"
 27  	MsgTypeApprovalResolved = "approval_resolved"
 28  )
 29  
 30  // ApprovalRequest is sent by daemon when a tool needs user approval.
 31  type ApprovalRequest struct {
 32  	Channel   string `json:"channel"`
 33  	ThreadID  string `json:"thread_id"`
 34  	RequestID string `json:"request_id"`
 35  	Tool      string `json:"tool"`
 36  	Args      string `json:"args"`
 37  	Agent     string `json:"agent"`
 38  }
 39  
 40  // ApprovalResponse is received from the client (via Cloud relay).
 41  type ApprovalResponse struct {
 42  	RequestID  string           `json:"request_id"`
 43  	Decision   ApprovalDecision `json:"decision"`    // "allow", "deny", "always_allow"
 44  	ResolvedBy string           `json:"resolved_by,omitempty"` // populated by Cloud
 45  }
 46  
 47  // ApprovalResolvedPayload is sent daemon→Cloud when Ptfrog resolves first.
 48  type ApprovalResolvedPayload struct {
 49  	RequestID  string           `json:"request_id"`
 50  	Decision   ApprovalDecision `json:"decision"`
 51  	ResolvedBy string           `json:"resolved_by"` // "ptfrog", "slack", "line"
 52  }
 53  
 54  // Channel types
 55  const (
 56  	ChannelSlack    = "slack"
 57  	ChannelLINE     = "line"
 58  	ChannelTeams    = "teams"
 59  	ChannelWeChat   = "wechat"
 60  	ChannelWeb      = "web"
 61  	ChannelFeishu   = "feishu"
 62  	ChannelLark     = "lark"
 63  	ChannelDiscord  = "discord"
 64  	ChannelSchedule = "schedule"
 65  	ChannelSystem   = "system"
 66  )
 67  
 68  // Reply format types
 69  const (
 70  	FormatText     = "text"
 71  	FormatMarkdown = "markdown"
 72  )
 73  
 74  // ServerMessage is the envelope for all server-to-daemon messages.
 75  type ServerMessage struct {
 76  	Type      string          `json:"type"`
 77  	MessageID string          `json:"message_id,omitempty"`
 78  	Payload   json.RawMessage `json:"payload,omitempty"`
 79  }
 80  
 81  // DaemonMessage is the envelope for all daemon-to-server messages.
 82  type DaemonMessage struct {
 83  	Type      string          `json:"type"`
 84  	MessageID string          `json:"message_id,omitempty"`
 85  	Payload   json.RawMessage `json:"payload,omitempty"`
 86  }
 87  
 88  // MessagePayload is what the daemon's agent loop processes.
 89  type MessagePayload struct {
 90  	Channel   string               `json:"channel"`
 91  	ThreadID  string               `json:"thread_id"`
 92  	Sender    string               `json:"sender"`
 93  	Text      string               `json:"text"`
 94  	Content   []RequestContentBlock `json:"content,omitempty"` // multimodal content blocks (reserved for Cloud)
 95  	AgentName string               `json:"agent_name,omitempty"`
 96  	MessageID string               `json:"-"` // set locally from envelope, not from JSON
 97  	Timestamp string               `json:"timestamp"`
 98  	Source    string               `json:"source,omitempty"` // populated by Cloud; "slack", "line", "webhook"
 99  	CWD       string               `json:"cwd,omitempty"`    // project path override from Cloud/Desktop
100  	Files     []RemoteFile         `json:"files,omitempty"` // file attachments from messaging platforms
101  }
102  
103  // RemoteFile describes a file attachment forwarded by Cloud from a messaging platform.
104  type RemoteFile struct {
105  	Name       string `json:"name"`
106  	MimeType   string `json:"mimetype,omitempty"`
107  	Size       int64  `json:"size,omitempty"`
108  	URL        string `json:"url"`
109  	AuthHeader string `json:"auth_header,omitempty"`
110  }
111  
112  // ReplyPayload is sent back after agent completes.
113  type ReplyPayload struct {
114  	Channel  string `json:"channel"`
115  	ThreadID string `json:"thread_id"`
116  	Text     string `json:"text"`
117  	Format   string `json:"format,omitempty"`
118  }
119  
120  // ProactivePayload is sent by the daemon to push an unsolicited message
121  // to all channels mapped to the named agent.
122  type ProactivePayload struct {
123  	AgentName string `json:"agent_name"`
124  	Text      string `json:"text"`
125  	Format    string `json:"format,omitempty"` // "text" (default) or "markdown"
126  	SessionID string `json:"session_id,omitempty"`
127  }
128  
129  // DaemonEventPayload carries a single agent loop event to Cloud.
130  type DaemonEventPayload struct {
131  	EventType string                 `json:"event_type"`
132  	Message   string                 `json:"message"`
133  	Data      map[string]interface{} `json:"data,omitempty"`
134  	Seq       int64                  `json:"seq"`
135  	Timestamp string                 `json:"ts"`
136  }
137  
138  // ClaimAckPayload is sent to confirm or deny a claim.
139  type ClaimAckPayload struct {
140  	Granted bool `json:"granted"`
141  }
142  
143  // IsSystemChannel returns true for channels that don't expect agent processing.
144  func IsSystemChannel(channel string) bool {
145  	return channel == ChannelSystem
146  }