Matthias Nott
2026-03-08 4c266155785aad5050ebff7211e3d5f9e15c3238
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
export type MessageRole = "user" | "assistant" | "system";
export type MessageType = "text" | "voice" | "image";
export interface Message {
  id: string;
  role: MessageRole;
  type: MessageType;
  content: string;
  audioUri?: string;
  imageBase64?: string;
  timestamp: number;
  status?: "sending" | "sent" | "error";
  duration?: number;
}
export interface ServerConfig {
  host: string;
  port: number;
  localHost?: string;
  macAddress?: string;
}
export type ConnectionStatus = "disconnected" | "connecting" | "reconnecting" | "connected" | "compacting";
// --- WebSocket protocol ---
/** Outgoing from app to watcher */
export interface WsTextMessage {
  type: "text";
  content: string;
  sessionId?: string;
}
export interface WsVoiceMessage {
  type: "voice";
  audioBase64: string;
  content: string;
  messageId?: string;
  sessionId?: string;
}
export interface WsImageMessage {
  type: "image";
  imageBase64: string;
  caption: string;
  mimeType: string;
  sessionId?: string;
}
export interface WsCommandMessage {
  type: "command";
  command: string;
  args?: Record<string, unknown>;
  sessionId?: string;
}
export type WsOutgoing = WsTextMessage | WsVoiceMessage | WsImageMessage | WsCommandMessage;
/** Incoming from watcher to app */
export interface WsIncomingText {
  type: "text";
  content: string;
  sessionId?: string;
}
export interface WsIncomingVoice {
  type: "voice";
  content: string;
  audioBase64?: string;
  sessionId?: string;
}
export interface WsIncomingImage {
  type: "image";
  imageBase64: string;
  caption?: string;
  sessionId?: string;
}
export interface WsSession {
  index: number;
  name: string;
  type: "claude" | "terminal";
  kind?: "api" | "visual";
  isActive: boolean;
  id: string;
}
export interface WsIncomingSessions {
  type: "sessions";
  sessions: WsSession[];
}
export interface WsIncomingSessionSwitched {
  type: "session_switched";
  name: string;
  sessionId: string;
}
export interface WsIncomingSessionRenamed {
  type: "session_renamed";
  sessionId: string;
  name: string;
}
export interface WsIncomingTranscript {
  type: "transcript";
  messageId: string;
  content: string;
}
export interface WsIncomingTyping {
  type: "typing";
  typing: boolean;
  sessionId?: string;
}
export interface WsIncomingError {
  type: "error";
  message: string;
}
export interface WsIncomingStatus {
  type: "status";
  status: string;
}
export interface PaiProject {
  name: string;
  slug: string;
  path: string;
  sessions: number;
}
export interface WsIncomingProjects {
  type: "projects";
  projects: PaiProject[];
}
export type WsIncoming =
  | WsIncomingText
  | WsIncomingVoice
  | WsIncomingImage
  | WsIncomingSessions
  | WsIncomingSessionSwitched
  | WsIncomingSessionRenamed
  | WsIncomingTranscript
  | WsIncomingTyping
  | WsIncomingError
  | WsIncomingStatus
  | WsIncomingProjects;