coffeeinabit 0.0.57 → 0.0.59

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -40,6 +40,7 @@ export class LinkedInAutomation {
40
40
  this.screenshotIntervalMs = 3000;
41
41
  this.actionPollingIntervalMs = 15000;
42
42
  this.currentAction = null;
43
+ this.currentActionStartedAt = null;
43
44
  this.userEmail = null;
44
45
  this.isActionPollingActive = false;
45
46
  this.linkedInSessionDir = getContextDirectory();
@@ -48,6 +49,9 @@ export class LinkedInAutomation {
48
49
  this._currentRefreshToken = null;
49
50
  this._currentAccessToken = null; // Track access token explicitly
50
51
 
52
+ // Action execution timeout (5 minutes)
53
+ this.actionTimeoutMs = 5 * 60 * 1000;
54
+
51
55
  // Authentication failure tracking
52
56
  this.consecutiveAuthFailures = 0;
53
57
  this.maxAuthFailures = 3; // Stop polling after 3 consecutive failures
@@ -701,12 +705,13 @@ export class LinkedInAutomation {
701
705
  try {
702
706
  const screenshot = await this.page.screenshot({
703
707
  fullPage: false,
704
- type: 'png',
708
+ type: 'jpeg',
709
+ quality: 50,
705
710
  timeout: 60000
706
711
  });
707
712
 
708
713
  const base64Screenshot = screenshot.toString('base64');
709
-
714
+
710
715
  this.io.emit('screenshot_update', {
711
716
  screenshot: base64Screenshot,
712
717
  url: this.currentUrl,
@@ -714,6 +719,9 @@ export class LinkedInAutomation {
714
719
  timestamp: new Date().toISOString()
715
720
  });
716
721
 
722
+ // Fire-and-forget upload to S3
723
+ this.uploadScreenshotToS3(base64Screenshot, this.currentAction?.action_id);
724
+
717
725
  } catch (error) {
718
726
  logger.error('[LinkedInAutomation] Failed to capture screenshot:', error.message);
719
727
  if (typeof error.message === 'string' && error.message.includes('Target page, context or browser has been closed')) {
@@ -722,6 +730,30 @@ export class LinkedInAutomation {
722
730
  }
723
731
  }
724
732
 
733
+ async uploadScreenshotToS3(base64Screenshot, actionId) {
734
+ try {
735
+ const idToken = await this.getAccessToken();
736
+ if (!idToken) return;
737
+
738
+ fetch('https://api.coffeeinabit.com/app/screenshots', {
739
+ method: 'POST',
740
+ headers: {
741
+ 'Content-Type': 'application/json',
742
+ 'Authorization': `Bearer ${idToken}`
743
+ },
744
+ body: JSON.stringify({
745
+ screenshot: base64Screenshot,
746
+ action_id: actionId || null,
747
+ timestamp: Date.now()
748
+ })
749
+ }).catch(err => {
750
+ logger.error('[LinkedInAutomation] Screenshot upload failed:', err.message);
751
+ });
752
+ } catch (error) {
753
+ logger.error('[LinkedInAutomation] Screenshot upload error:', error.message);
754
+ }
755
+ }
756
+
725
757
  startActionPolling() {
726
758
  if (this.isActionPollingActive) {
727
759
  logger.log('[LinkedInAutomation] Action polling is already active, skipping...');
@@ -751,10 +783,18 @@ export class LinkedInAutomation {
751
783
  }
752
784
 
753
785
  if (this.currentAction) {
754
- logger.log('[LinkedInAutomation] Action in progress, skipping polling...');
755
- logger.log('[LinkedInAutomation] Next poll in', Math.round(currentInterval / 1000), 'seconds');
756
- setTimeout(pollWithBackoff, currentInterval);
757
- return;
786
+ const elapsed = Date.now() - (this.currentActionStartedAt || Date.now());
787
+ if (elapsed > this.actionTimeoutMs) {
788
+ logger.error(`[LinkedInAutomation] Action ${this.currentAction.action} timed out after ${Math.round(elapsed / 1000)}s, force-clearing`);
789
+ this.currentAction = null;
790
+ this.currentActionStartedAt = null;
791
+ this.emitActionUpdate();
792
+ } else {
793
+ logger.log(`[LinkedInAutomation] Action in progress (${Math.round(elapsed / 1000)}s), skipping polling...`);
794
+ logger.log('[LinkedInAutomation] Next poll in', Math.round(currentInterval / 1000), 'seconds');
795
+ setTimeout(pollWithBackoff, currentInterval);
796
+ return;
797
+ }
758
798
  }
759
799
 
760
800
  // pollForActions now returns true if actions were found and processed
@@ -1002,13 +1042,14 @@ export class LinkedInAutomation {
1002
1042
  await closeAllConversationBubbles(this.page);
1003
1043
 
1004
1044
  try {
1005
- logger.log('[LinkedInAutomation] Starting execution of action:', action.action, 'ID:', action.action_id);
1045
+ logger.log('[LinkedInAutomation] Starting execution of action:', action.action, 'ID:', action.action_id, 'Local time:', new Date().toLocaleString());
1006
1046
  logger.log('[LinkedInAutomation] Action parameters:', JSON.stringify(action.parameters, null, 2));
1007
1047
  this.currentAction = action;
1048
+ this.currentActionStartedAt = Date.now();
1008
1049
  this.status = 'executing_action';
1009
1050
  this.emitActionUpdate();
1010
1051
  this.emitStatus();
1011
-
1052
+
1012
1053
  let result = null;
1013
1054
 
1014
1055
  // Normalize: if no username in parameters, use linkedin_profile_id from action
@@ -1111,6 +1152,7 @@ export class LinkedInAutomation {
1111
1152
  console.warn = originalConsoleWarn;
1112
1153
 
1113
1154
  this.currentAction = null;
1155
+ this.currentActionStartedAt = null;
1114
1156
  this.emitActionUpdate();
1115
1157
  }
1116
1158
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coffeeinabit",
3
- "version": "0.0.57",
3
+ "version": "0.0.59",
4
4
  "description": "coffeeinabit app",
5
5
  "main": "server.js",
6
6
  "type": "module",
@@ -25,7 +25,7 @@ export function createAutomationRoutes(cloudAuth, appState, linkedinAutomation,
25
25
  }
26
26
 
27
27
  try {
28
- const { headless = false, socketId = null } = req.body;
28
+ const { headless = true, socketId = null } = req.body;
29
29
 
30
30
  // If socketId provided, use process-specific automation
31
31
  // Otherwise, use legacy single instance (for backward compatibility)
@@ -1,676 +1,7 @@
1
1
  {
2
- "threads": [
3
- {
4
- "thread_id": "urn:li:messagingThread:2-NzcyNjc2MzQtYjg2MS00NzQxLWI2YzUtMGE5YzRkMzEwOTVkXzEwMA==",
5
- "participants": [
6
- {
7
- "name": "Melissa Torres",
8
- "uniq_id": "ACoAAABbk_8BlTyLieiBlKHVK5M2J7_nGDMNUso",
9
- "is_self": false
10
- },
11
- {
12
- "name": "Alena Borikova",
13
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
14
- "is_self": true
15
- }
16
- ],
17
- "last_activity_at": 1771021039024,
18
- "last_activity_at_iso": "2026-02-13T22:17:19.024Z",
19
- "messages": [
20
- {
21
- "author": "Melissa Torres",
22
- "author_id": "ACoAAABbk_8BlTyLieiBlKHVK5M2J7_nGDMNUso",
23
- "is_self": false,
24
- "text": "Hi Alena,\nLooking for job openings?Find open jobs near you that match your skills and experience.\nTo get started:\nSearch for open jobsFilter down to the jobs near you, based on experience level, salary, and more.Create a search alert to be the first to know about new jobs.Start your search today, and find people who are hiring on LinkedIn.\nWe’re in this together,Melissa from LinkedIn",
25
- "timestamp": 1771021039024,
26
- "timestamp_iso": "2026-02-13T22:17:19.024Z",
27
- "network_intercepted_at": "2026-02-18T20:12:27.020Z"
28
- }
29
- ],
30
- "message_count": 1
31
- },
32
- {
33
- "thread_id": "urn:li:messagingThread:2-MDVkNDEyNjAtZmM1ZC00MDQ3LWFmM2ItOGIwZjA5MTEzNTlkXzEwMA==",
34
- "participants": [
35
- {
36
- "name": "Alena Borikova",
37
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
38
- "is_self": true
39
- },
40
- {
41
- "name": "Daria Baiandina",
42
- "uniq_id": "ACoAACZnpFoBUlqVlDu1EwrsUoFrgaJdC6auxN0",
43
- "is_self": false
44
- }
45
- ],
46
- "last_activity_at": 1770831726029,
47
- "last_activity_at_iso": "2026-02-11T17:42:06.029Z",
48
- "messages": [
49
- {
50
- "author": "Alena Borikova",
51
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
52
- "is_self": true,
53
- "text": "Привет, Даша)\nвзаимно!\n\nСейчас я больше занимаюсь ux частью и рисерчем потребностей пользователей. А иллюстраторы / граф. дизайнеры есть в штате, так что помочь как-то к сожалению ничем не смогу (\nНо буду иметь ввиду, если что-то понадобится \n\nТы в Тбилиси живешь? 🥹\nЯ иногда бываю в Тбилиси, но в основном в Батуми",
54
- "timestamp": 1770831726029,
55
- "timestamp_iso": "2026-02-11T17:42:06.029Z",
56
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
57
- }
58
- ],
59
- "message_count": 1
60
- },
61
- {
62
- "thread_id": "urn:li:messagingThread:2-ZmQyYjQzZjAtYTI0Ny00MGQwLTkwOTItZDI1NmMxNjYyMGUxXzEwMA==",
63
- "participants": [
64
- {
65
- "name": "Assel Uvaliyeva",
66
- "uniq_id": "ACoAAARftn0BKiQPHroRgvmpVevd9aKx1gttAHU",
67
- "is_self": false
68
- },
69
- {
70
- "name": "Alena Borikova",
71
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
72
- "is_self": true
73
- }
74
- ],
75
- "last_activity_at": 1770640210236,
76
- "last_activity_at_iso": "2026-02-09T12:30:10.236Z",
77
- "messages": [
78
- {
79
- "author": "Assel Uvaliyeva",
80
- "author_id": "ACoAAARftn0BKiQPHroRgvmpVevd9aKx1gttAHU",
81
- "is_self": false,
82
- "text": "Dear Alena, I’m Dr Assel, Vice-President of Nazarbayev University (NU).\nThe joint NU-SOAS MA offers a full-time, one-year program — first in London, then in Astana — with modules in international relations, Eurasian geopolitics, and an internship in embassies. Ideal for aspiring diplomats, analysts, or NGO professionals. \nThe program is fee-paying, with a total tuition fee of £15,000.\nWhy Apply to NU?#1 University in Central Asia and the Caucasus and #4 in CIS98% employability rateEnglish-taught educationInternational environment: nearly 400 international students and 500 faculty members from 62 countries140+ career events annuallyCampus security is ensured through 24/7 surveillanceThe NU campus spans over 94 hectares, featuring modern academic buildings and recreational facilities. A network of enclosed Skywalks connects key buildings, ensuring comfort and accessibility throughout the year.\nFor additional information please contact us at info_admissions@nu.edu.kz\nGet a dual degree! Admission is open.",
83
- "timestamp": 1770640210236,
84
- "timestamp_iso": "2026-02-09T12:30:10.236Z",
85
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
86
- }
87
- ],
88
- "message_count": 1
89
- },
90
- {
91
- "thread_id": "urn:li:messagingThread:2-ZGU3NTUyMzMtMTJkNy00NDIxLTk4MDgtYmYxNGIwOTQ5YjFmXzEwMA==",
92
- "participants": [
93
- {
94
- "name": "Alena Borikova",
95
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
96
- "is_self": true
97
- },
98
- {
99
- "name": "Kate Yanchenko",
100
- "uniq_id": "ACoAABIyz2sB-O7ZtfsS5d8myngQBtR9JEKz_sY",
101
- "is_self": false
102
- }
103
- ],
104
- "last_activity_at": 1770139934759,
105
- "last_activity_at_iso": "2026-02-03T17:32:14.759Z",
106
- "messages": [
107
- {
108
- "author": "Kate Yanchenko",
109
- "author_id": "ACoAABIyz2sB-O7ZtfsS5d8myngQBtR9JEKz_sY",
110
- "is_self": false,
111
- "text": "Hello!",
112
- "timestamp": 1765865708197,
113
- "timestamp_iso": "2025-12-16T06:15:08.197Z",
114
- "network_intercepted_at": "2026-02-18T20:12:35.960Z"
115
- },
116
- {
117
- "author": "Alena Borikova",
118
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
119
- "is_self": true,
120
- "text": "Hi hi ",
121
- "timestamp": 1765867625215,
122
- "timestamp_iso": "2025-12-16T06:47:05.215Z",
123
- "network_intercepted_at": "2026-02-18T20:12:35.960Z"
124
- },
125
- {
126
- "author": "Kate Yanchenko",
127
- "author_id": "ACoAABIyz2sB-O7ZtfsS5d8myngQBtR9JEKz_sY",
128
- "is_self": false,
129
- "text": "Hello!",
130
- "timestamp": 1765872088845,
131
- "timestamp_iso": "2025-12-16T08:01:28.845Z",
132
- "network_intercepted_at": "2026-02-18T20:12:35.960Z"
133
- },
134
- {
135
- "author": "Kate Yanchenko",
136
- "author_id": "ACoAABIyz2sB-O7ZtfsS5d8myngQBtR9JEKz_sY",
137
- "is_self": false,
138
- "text": "just testing",
139
- "timestamp": 1765873430497,
140
- "timestamp_iso": "2025-12-16T08:23:50.497Z",
141
- "network_intercepted_at": "2026-02-18T20:12:35.960Z"
142
- },
143
- {
144
- "author": "Kate Yanchenko",
145
- "author_id": "ACoAABIyz2sB-O7ZtfsS5d8myngQBtR9JEKz_sY",
146
- "is_self": false,
147
- "text": "Hey, how are you?",
148
- "timestamp": 1770138675922,
149
- "timestamp_iso": "2026-02-03T17:11:15.922Z",
150
- "network_intercepted_at": "2026-02-18T20:12:35.960Z"
151
- },
152
- {
153
- "author": "Alena Borikova",
154
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
155
- "is_self": true,
156
- "text": "Fine, you? ❤️",
157
- "timestamp": 1770138705220,
158
- "timestamp_iso": "2026-02-03T17:11:45.220Z",
159
- "network_intercepted_at": "2026-02-18T20:12:35.960Z"
160
- },
161
- {
162
- "author": "Kate Yanchenko",
163
- "author_id": "ACoAABIyz2sB-O7ZtfsS5d8myngQBtR9JEKz_sY",
164
- "is_self": false,
165
- "text": "Hey, how are you?",
166
- "timestamp": 1770139276918,
167
- "timestamp_iso": "2026-02-03T17:21:16.918Z",
168
- "network_intercepted_at": "2026-02-18T20:12:35.960Z"
169
- },
170
- {
171
- "author": "Alena Borikova",
172
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
173
- "is_self": true,
174
- "text": "I'm good",
175
- "timestamp": 1770139300739,
176
- "timestamp_iso": "2026-02-03T17:21:40.739Z",
177
- "network_intercepted_at": "2026-02-18T20:12:35.960Z"
178
- },
179
- {
180
- "author": "Alena Borikova",
181
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
182
- "is_self": true,
183
- "text": "Thanks for asking 😁❤️",
184
- "timestamp": 1770139311452,
185
- "timestamp_iso": "2026-02-03T17:21:51.452Z",
186
- "network_intercepted_at": "2026-02-18T20:12:35.960Z"
187
- },
188
- {
189
- "author": "Kate Yanchenko",
190
- "author_id": "ACoAABIyz2sB-O7ZtfsS5d8myngQBtR9JEKz_sY",
191
- "is_self": false,
192
- "text": "Hey, how are you?",
193
- "timestamp": 1770139531176,
194
- "timestamp_iso": "2026-02-03T17:25:31.176Z",
195
- "network_intercepted_at": "2026-02-18T20:12:35.960Z"
196
- },
197
- {
198
- "author": "Alena Borikova",
199
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
200
- "is_self": true,
201
- "text": "🐥❤️",
202
- "timestamp": 1770139934759,
203
- "timestamp_iso": "2026-02-03T17:32:14.759Z",
204
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
205
- }
206
- ],
207
- "message_count": 11
208
- },
209
- {
210
- "thread_id": "urn:li:messagingThread:2-YTQ2MjM0YzUtOWE3Ny00NWFiLWJhOTAtOThjOTgwOGJlNDgxXzEwMA==",
211
- "participants": [
212
- {
213
- "name": "Lamri Gorguchadze",
214
- "uniq_id": "ACoAACahZMEBJyO-1ojzpnJdh5jgDY86PadkdQU",
215
- "is_self": false
216
- },
217
- {
218
- "name": "Alena Borikova",
219
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
220
- "is_self": true
221
- }
222
- ],
223
- "last_activity_at": 1767280366600,
224
- "last_activity_at_iso": "2026-01-01T15:12:46.600Z",
225
- "messages": [
226
- {
227
- "author": "Alena Borikova",
228
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
229
- "is_self": true,
230
- "text": "Thank you 🥹❤️\nI wish you all the best in NY 2026 too! 🫶",
231
- "timestamp": 1767280366600,
232
- "timestamp_iso": "2026-01-01T15:12:46.600Z",
233
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
234
- }
235
- ],
236
- "message_count": 1
237
- },
238
- {
239
- "thread_id": "urn:li:messagingThread:2-NzUyNTUzYTEtNmQwYS00ZDQ0LWI0ZDEtZThkYTcyOTIyMzFmXzEwMA==",
240
- "participants": [
241
- {
242
- "name": "Viktoryia Patarocha",
243
- "uniq_id": "ACoAADuVGhoBsbpjl2JPhPDOPwK8FORq6CkabbI",
244
- "is_self": false
245
- },
246
- {
247
- "name": "Alena Borikova",
248
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
249
- "is_self": true
250
- }
251
- ],
252
- "last_activity_at": 1767280357333,
253
- "last_activity_at_iso": "2026-01-01T15:12:37.333Z",
254
- "messages": [
255
- {
256
- "author": "Viktoryia Patarocha",
257
- "author_id": "ACoAADuVGhoBsbpjl2JPhPDOPwK8FORq6CkabbI",
258
- "is_self": false,
259
- "text": "Hi Alena! Are you happy at Empartech?\nWe’re looking for a Node.js Developer in Georgia to join our projects for European and US clients.\nLeverX has been on the market for over 20 years, building an expert team and a healthy work environment where people grow and enjoy their projects.\nYour profile looks like a possible fit. Would you be open to a short chat?",
260
- "timestamp": 1767280357333,
261
- "timestamp_iso": "2026-01-01T15:12:37.333Z",
262
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
263
- }
264
- ],
265
- "message_count": 1
266
- },
267
- {
268
- "thread_id": "urn:li:messagingThread:2-YjQ5N2RiMDMtMmIyMy00OGQyLWIzMDktZjRmNmUyMjhmYjQ1XzEwMA==",
269
- "participants": [
270
- {
271
- "name": "Jasper de Muynck",
272
- "uniq_id": "ACoAAB0pl_ABVSQdbBcQqUANIdz3xXBpsWW-1so",
273
- "is_self": false
274
- },
275
- {
276
- "name": "Alena Borikova",
277
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
278
- "is_self": true
279
- }
280
- ],
281
- "last_activity_at": 1765954826039,
282
- "last_activity_at_iso": "2025-12-17T07:00:26.039Z",
283
- "messages": [
284
- {
285
- "author": "Alena Borikova",
286
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
287
- "is_self": true,
288
- "text": "Hey James! Nice to meet you! \nTo be honest, it doesn't sound like anything exciting :)",
289
- "timestamp": 1765954826039,
290
- "timestamp_iso": "2025-12-17T07:00:26.039Z",
291
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
292
- }
293
- ],
294
- "message_count": 1
295
- },
296
- {
297
- "thread_id": "urn:li:messagingThread:2-NWVhNmRmNmItNTRjNi00NTUxLThiNmUtMjYxNzk3YzVjNTI5XzEwMA==",
298
- "participants": [
299
- {
300
- "name": "Lasha Shonia",
301
- "uniq_id": "ACoAABS6Ji8BvKEm9DvI5n5TQiY6UbMKN4-GZBQ",
302
- "is_self": false
303
- },
304
- {
305
- "name": "Alena Borikova",
306
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
307
- "is_self": true
308
- }
309
- ],
310
- "last_activity_at": 1765828489886,
311
- "last_activity_at_iso": "2025-12-15T19:54:49.886Z",
312
- "messages": [
313
- {
314
- "author": "Alena Borikova",
315
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
316
- "is_self": true,
317
- "text": "Yes! I’ll think about it 🙏",
318
- "timestamp": 1765828489886,
319
- "timestamp_iso": "2025-12-15T19:54:49.886Z",
320
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
321
- }
322
- ],
323
- "message_count": 1
324
- },
325
- {
326
- "thread_id": "urn:li:messagingThread:2-NThhNjNhYjgtZDQ3OC00OTRhLTlkNDQtMWE4Y2I1MzllOTM2XzEwMA==",
327
- "participants": [
328
- {
329
- "name": "Alena Borikova",
330
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
331
- "is_self": true
332
- },
333
- {
334
- "name": "Nataliia Bohomiakova",
335
- "uniq_id": "ACoAAChk4ooBhasNs29B6CQhKn9Fx1EQs5USnh4",
336
- "is_self": false
337
- }
338
- ],
339
- "last_activity_at": 1765816629196,
340
- "last_activity_at_iso": "2025-12-15T16:37:09.196Z",
341
- "messages": [
342
- {
343
- "author": "Alena Borikova",
344
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
345
- "is_self": true,
346
- "text": "Hi Nataliia. My name's Alena, I'm building an AI Sales tool for LinkedIn. \nI understand that the offering might not be directly relevant to you technically. But if it sounds somehow that it might help you with work, so you can refer us to the decision-maker, and I can do a quick demo.",
347
- "timestamp": 1765816629196,
348
- "timestamp_iso": "2025-12-15T16:37:09.196Z",
349
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
350
- }
351
- ],
352
- "message_count": 1
353
- },
354
- {
355
- "thread_id": "urn:li:messagingThread:2-ZDk0MDNiN2UtMjJhNi00ZGE4LWE2ODAtOTFiOTQ1MmY1MDc2XzEwMA==",
356
- "participants": [
357
- {
358
- "name": "Alena Borikova",
359
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
360
- "is_self": true
361
- },
362
- {
363
- "name": "Eralp Hatipoglu",
364
- "uniq_id": "ACoAAC6b4IEB3Iy-nOLNuoVyp3Q6SeTS9WMgiDs",
365
- "is_self": false
366
- }
367
- ],
368
- "last_activity_at": 1765816499877,
369
- "last_activity_at_iso": "2025-12-15T16:34:59.877Z",
370
- "messages": [
371
- {
372
- "author": "Alena Borikova",
373
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
374
- "is_self": true,
375
- "text": "Eralp, great connecting. Saw your Forbes chat-impressive vision for CityPay's expansion into Central Asia. 👏\n\nI'm building an AI Sales Tool for LinkedIn that acts as an automated sales team. It finds the right professionals in new markets and engages them personally to speed up acquisition.\n\nWe guarantee leads and closed deals, or it's free. Does it sound interesting? We can book a quick demo call and I can answer all questions.",
376
- "timestamp": 1765816499877,
377
- "timestamp_iso": "2025-12-15T16:34:59.877Z",
378
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
379
- }
380
- ],
381
- "message_count": 1
382
- },
383
- {
384
- "thread_id": "urn:li:messagingThread:2-OWEyMDBhMjItZDNhZS00ZjUyLWE5NjktNWQ5NzdjMDdmMjA2XzEwMA==",
385
- "participants": [
386
- {
387
- "name": "Alena Borikova",
388
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
389
- "is_self": true
390
- },
391
- {
392
- "name": "Lika Apakidze",
393
- "uniq_id": "ACoAACT0mF0BwImUvMxZ069pectEI0DyJFKnofA",
394
- "is_self": false
395
- }
396
- ],
397
- "last_activity_at": 1765217642382,
398
- "last_activity_at_iso": "2025-12-08T18:14:02.382Z",
399
- "messages": [
400
- {
401
- "author": "Alena Borikova",
402
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
403
- "is_self": true,
404
- "text": "Hi Lika. Great to connect. Saw your post on the psychotherapy event-it's a fascinating topic. It resonates with my work in UX, where understanding human psychology is everything.\n\nAs a fellow founder here in Tbilisi, I know the grind of finding new clients, especially while balancing other big commitments. I'm working on an AI tool that automates sales outreach. It might be a way to grow Printera without cloning yourself.\n\nWe offer a free trial and guarantee you'll get meetings. Worth a look?",
405
- "timestamp": 1765217642382,
406
- "timestamp_iso": "2025-12-08T18:14:02.382Z",
407
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
408
- }
409
- ],
410
- "message_count": 1
411
- },
412
- {
413
- "thread_id": "urn:li:messagingThread:2-ZmNmMTllN2MtNmQxNC00NTczLWI4NDItM2Q1MmUxZGRlNWQ2XzEwMA==",
414
- "participants": [
415
- {
416
- "name": "Saba Marshava",
417
- "uniq_id": "ACoAAEW9Z0gBSGrtG_eZP7eBoB05PvlSwY5dqS0",
418
- "is_self": false
419
- },
420
- {
421
- "name": "Alena Borikova",
422
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
423
- "is_self": true
424
- }
425
- ],
426
- "last_activity_at": 1765127593427,
427
- "last_activity_at_iso": "2025-12-07T17:13:13.427Z",
428
- "messages": [
429
- {
430
- "author": "Alena Borikova",
431
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
432
- "is_self": true,
433
- "text": "Saba, that Minecraft-style portfolio you shared was brilliant.\n\nIt's a perfect example of what I love-making deep tech feel intuitive. As a UX researcher with a full-stack background, that fusion is my entire focus.\n\nI'm building an AI SDR tool for founders. We're offering a few local startups a guaranteed-results trial: at least 3 qualified meetings with leads in a month, or it's free. Worth a quick chat?",
434
- "timestamp": 1765127593427,
435
- "timestamp_iso": "2025-12-07T17:13:13.427Z",
436
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
437
- }
438
- ],
439
- "message_count": 1
440
- },
441
- {
442
- "thread_id": "urn:li:messagingThread:2-MzFiMzIyNjItYmQ2NS00MzlhLWE4MWYtMTBlZTI4NzBkODk3XzEwMA==",
443
- "participants": [
444
- {
445
- "name": "Alena Borikova",
446
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
447
- "is_self": true
448
- },
449
- {
450
- "name": "Nicholas Ergemla",
451
- "uniq_id": "ACoAABuFt48BWRjCxQAcCi-5j16UOr7wbENffl0",
452
- "is_self": false
453
- }
454
- ],
455
- "last_activity_at": 1765127346294,
456
- "last_activity_at_iso": "2025-12-07T17:09:06.294Z",
457
- "messages": [
458
- {
459
- "author": "Alena Borikova",
460
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
461
- "is_self": true,
462
- "text": "Nicholas, your post about the useless banking feature was spot on. 'Best, Best but... Nobody use it!'\n\nI run an AI startup here in Tbilisi helping founders like us with the grind of finding new clients for their agencies. I'm guessing you'd rather spend your time on design for Steelmonk than on outreach.\n\nIt's a zero-risk trial, we guarantee you meetings. Worth a look?",
463
- "timestamp": 1765127346294,
464
- "timestamp_iso": "2025-12-07T17:09:06.294Z",
465
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
466
- }
467
- ],
468
- "message_count": 1
469
- },
470
- {
471
- "thread_id": "urn:li:messagingThread:2-OGI1NzZjNGMtODIzOC00NTFlLTlhMzMtN2FkYjExYTgxYjlhXzEwMA==",
472
- "participants": [
473
- {
474
- "name": "Irakli Museridze",
475
- "uniq_id": "ACoAACI3ULMBjZDgBp5XncFBHkYhiQTQyyl7w6k",
476
- "is_self": false
477
- },
478
- {
479
- "name": "Alena Borikova",
480
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
481
- "is_self": true
482
- }
483
- ],
484
- "last_activity_at": 1765127193692,
485
- "last_activity_at_iso": "2025-12-07T17:06:33.692Z",
486
- "messages": [
487
- {
488
- "author": "Alena Borikova",
489
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
490
- "is_self": true,
491
- "text": "Irakli, great to connect. As a fellow tech founder in Tbilisi with a dev background, I know the feeling. We'd rather spend our time building the product than trying to find the next client.\n\nI'm working on an AI SDR tool to automate that entire process for agencies. It comes with a guarantee of 3+ lead meetings in the first month, or it's free.\n\nWorth a look?",
492
- "timestamp": 1765127193692,
493
- "timestamp_iso": "2025-12-07T17:06:33.692Z",
494
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
495
- }
496
- ],
497
- "message_count": 1
498
- },
499
- {
500
- "thread_id": "urn:li:messagingThread:2-ZDk0MmE1MjMtZTE1Yi00YTIwLTk5MGQtMmJlNzcwNGY5MjdjXzEwMA==",
501
- "participants": [
502
- {
503
- "name": "Alena Borikova",
504
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
505
- "is_self": true
506
- },
507
- {
508
- "name": "Avtandil Kutchava",
509
- "uniq_id": "ACoAACETSqgBP8_EW1_lwMD7_Vkm1jIsbQdI0XI",
510
- "is_self": false
511
- }
512
- ],
513
- "last_activity_at": 1765127013084,
514
- "last_activity_at_iso": "2025-12-07T17:03:33.084Z",
515
- "messages": [
516
- {
517
- "author": "Alena Borikova",
518
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
519
- "is_self": true,
520
- "text": "Avtandil, your vision to 'pixelate the world' is ambitious. A big vision needs a constant stream of users.\n\nI'm tackling that exact problem.\n\nMy AI SDR tool automates lead-gen for founders. It helps you scale without the manual grind.\n\nIt's zero-risk to try. Two weeks free. And a guarantee of 3+ lead meetings in the first month.\n\nDoes it sound interesting?",
521
- "timestamp": 1765127013084,
522
- "timestamp_iso": "2025-12-07T17:03:33.084Z",
523
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
524
- }
525
- ],
526
- "message_count": 1
527
- },
528
- {
529
- "thread_id": "urn:li:messagingThread:2-NDMyOTkwY2YtMTQ4Yi00ZTMwLTgxMDMtZGU1ZTQ1NTEwZTViXzEwMA==",
530
- "participants": [
531
- {
532
- "name": "Natalia Shahmetova",
533
- "uniq_id": "ACoAABJp-4QBr2ddDaa7L8QEcMJfqfHpDIGSsCw",
534
- "is_self": false
535
- },
536
- {
537
- "name": "Alena Borikova",
538
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
539
- "is_self": true
540
- }
541
- ],
542
- "last_activity_at": 1765126372470,
543
- "last_activity_at_iso": "2025-12-07T16:52:52.470Z",
544
- "messages": [
545
- {
546
- "author": "Alena Borikova",
547
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
548
- "is_self": true,
549
- "text": "Привет, Наталья. Рада знакомству, я впечатлена ростом Woofz.\n\nВидела, что вы сейчас решаете комплексную UX-задачу по созданию 'holistic companion'. Это правильный фокус. Мой AI SDR-инструмент может взять на себя автоматизацию роста, чтобы ваша команда занималась продуктом.\n\nПопробовать можно без рисков: 2 недели триала и полный возврат, если не будет хотя бы 3 встреч с лидами за месяц.\n\nБыло бы вам интересно обсудить?",
550
- "timestamp": 1765126372470,
551
- "timestamp_iso": "2025-12-07T16:52:52.470Z",
552
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
553
- }
554
- ],
555
- "message_count": 1
556
- },
557
- {
558
- "thread_id": "urn:li:messagingThread:2-MmVmOWEwMWUtMmRkNy00YzliLTk4YzMtMjZkY2Y3N2VjMzVjXzAxMg==",
559
- "participants": [
560
- {
561
- "name": "Martin Schofield",
562
- "uniq_id": "ACoAABO9iYQB_-LPFFOtCTiII30Z5xW8GQEA_gg",
563
- "is_self": false
564
- },
565
- {
566
- "name": "Alena Borikova",
567
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
568
- "is_self": true
569
- }
570
- ],
571
- "last_activity_at": 1765035895512,
572
- "last_activity_at_iso": "2025-12-06T15:44:55.512Z",
573
- "messages": [
574
- {
575
- "author": "Alena Borikova",
576
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
577
- "is_self": true,
578
- "text": "Good evening, just wanted to mention our free offerings,\nWe give 2 weeks free trial and money back guarantee if there won't be at least 3 meetings with leads in the first month. Worth a quick call to see the system in action and I'll go through more details?",
579
- "timestamp": 1765035895512,
580
- "timestamp_iso": "2025-12-06T15:44:55.512Z",
581
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
582
- }
583
- ],
584
- "message_count": 1
585
- },
586
- {
587
- "thread_id": "urn:li:messagingThread:2-ZGQyMTNjOTctNzBkMy00OTJmLTljNDgtNDhmNmNlZWM1NTQ3XzEwMA==",
588
- "participants": [
589
- {
590
- "name": "🚀 Sergey Shchegrikovich",
591
- "uniq_id": "ACoAAAY9JvgBUOnccp6WuZ_3tuylIyy7YERe-hI",
592
- "is_self": false
593
- },
594
- {
595
- "name": "Alena Borikova",
596
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
597
- "is_self": true
598
- }
599
- ],
600
- "last_activity_at": 1765018887652,
601
- "last_activity_at_iso": "2025-12-06T11:01:27.652Z",
602
- "messages": [
603
- {
604
- "author": "Alena Borikova",
605
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
606
- "is_self": true,
607
- "text": "https://meet.google.com/auo-kvqr-fwh",
608
- "timestamp": 1765018887652,
609
- "timestamp_iso": "2025-12-06T11:01:27.652Z",
610
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
611
- }
612
- ],
613
- "message_count": 1
614
- },
615
- {
616
- "thread_id": "urn:li:messagingThread:2-OTc0MGE4ZjUtMzViYy00NDdlLTgzNDctMjA0NjVkY2JhNDYwXzEwMA==",
617
- "participants": [
618
- {
619
- "name": "Ilya Savianok",
620
- "uniq_id": "ACoAACirfNkBZpd-HtEqmdU8vKkQ5eD40doxthw",
621
- "is_self": false
622
- },
623
- {
624
- "name": "Alena Borikova",
625
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
626
- "is_self": true
627
- }
628
- ],
629
- "last_activity_at": 1764957281356,
630
- "last_activity_at_iso": "2025-12-05T17:54:41.356Z",
631
- "messages": [
632
- {
633
- "author": "Ilya Savianok",
634
- "author_id": "ACoAACirfNkBZpd-HtEqmdU8vKkQ5eD40doxthw",
635
- "is_self": false,
636
- "text": "С удаленной работой это несложно ",
637
- "timestamp": 1764957281356,
638
- "timestamp_iso": "2025-12-05T17:54:41.356Z",
639
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
640
- }
641
- ],
642
- "message_count": 1
643
- },
644
- {
645
- "thread_id": "urn:li:messagingThread:2-OTM5YmJjMjQtMmZiMy00N2ViLTk0YTgtMTk1ZDcwOTdlYjYzXzEwMA==",
646
- "participants": [
647
- {
648
- "name": "Temur Mindiashvili",
649
- "uniq_id": "ACoAAAhAjbcBorCqiw5jFAmNVsGL7We3X9RbXc0",
650
- "is_self": false
651
- },
652
- {
653
- "name": "Alena Borikova",
654
- "uniq_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
655
- "is_self": true
656
- }
657
- ],
658
- "last_activity_at": 1764957137344,
659
- "last_activity_at_iso": "2025-12-05T17:52:17.344Z",
660
- "messages": [
661
- {
662
- "author": "Alena Borikova",
663
- "author_id": "ACoAAB3lzOkB3_fTfG3OZRQMLMVLvIiQgowXmPk",
664
- "is_self": true,
665
- "text": "I'm working on an AI SDR tool to help founders automate client acquisition. We guarantee 3 qualified meetings in the first month. Given your role at Slick, thought you might find it interesting.",
666
- "timestamp": 1764957137344,
667
- "timestamp_iso": "2025-12-05T17:52:17.344Z",
668
- "network_intercepted_at": "2026-02-18T20:12:27.022Z"
669
- }
670
- ],
671
- "message_count": 1
672
- }
673
- ],
674
- "thread_count": 20,
675
- "total_messages": 30
2
+ "threads": [],
3
+ "thread_count": 0,
4
+ "total_messages": 0,
5
+ "status": "no_history",
6
+ "message": "No previous messages found with natalie-grigorchuk-48277811. Conversation overlay shows \"New message\"."
676
7
  }
@@ -348,10 +348,11 @@ export async function executeGetMessageThreads(page, action) {
348
348
  let cutoffTimestamp;
349
349
  const param = action.parameters?.last_updated_timestamp;
350
350
  if (param) {
351
- cutoffTimestamp = typeof param === 'string' ? new Date(param).getTime() : param;
351
+ cutoffTimestamp = parseInt(param, 10);
352
352
  } else {
353
353
  cutoffTimestamp = Date.now() - (14 * 24 * 60 * 60 * 1000);
354
354
  }
355
+ console.log(`[GetMessageThreads] Cutoff timestamp: ${cutoffTimestamp} (${new Date(cutoffTimestamp).toLocaleString()})`);
355
356
 
356
357
  const threadsMap = new Map();
357
358
  const processedMessageKeys = new Set();
@@ -234,16 +234,15 @@ export async function executeGetMessages(page, action) {
234
234
 
235
235
  console.log('[get_messages] Conversation opened, waiting for messages to load...');
236
236
 
237
- // Wait for messengerMessagesBySyncToken response to be captured
237
+ // Wait for initial network responses (conversation list + conversation messages)
238
238
  await new Promise(resolve => setTimeout(resolve, 3000));
239
239
 
240
240
  // Check network-captured data
241
- const networkMsgCount = countAllMessages(threadsMap);
241
+ let networkMsgCount = countAllMessages(threadsMap);
242
242
  console.log(`[get_messages] Network captured: ${threadsMap.size} threads, ${networkMsgCount} messages`);
243
243
 
244
- page.off('response', responseListener);
245
-
246
244
  if (networkMsgCount === 0) {
245
+ page.off('response', responseListener);
247
246
  console.log('[get_messages] No messages captured from network, checking DOM...');
248
247
  const messageData = await extractMessageData(page);
249
248
  if (!messageData.found || messageData.noHistory) {
@@ -262,8 +261,96 @@ export async function executeGetMessages(page, action) {
262
261
  return enriched;
263
262
  }
264
263
 
265
- // Build result from network data (has author_id on every message)
266
- const threads = buildResult(threadsMap);
264
+ // Check overlay header to see if this is a new compose or existing conversation
265
+ let overlayLeadName = '';
266
+ try {
267
+ for (const sel of [
268
+ '.msg-overlay-bubble-header__title a',
269
+ '.msg-overlay-bubble-header h2 a',
270
+ '.msg-overlay-bubble-header__title',
271
+ ]) {
272
+ const el = page.locator(sel).first();
273
+ if (await el.count() > 0) {
274
+ overlayLeadName = (await el.innerText().catch(() => '')).trim();
275
+ if (overlayLeadName) break;
276
+ }
277
+ }
278
+ } catch (e) {}
279
+ console.log(`[get_messages] Overlay header: "${overlayLeadName}"`);
280
+
281
+ // "New message" means no existing conversation with this lead
282
+ if (overlayLeadName.toLowerCase().includes('new message')) {
283
+ page.off('response', responseListener);
284
+ console.log('[get_messages] No existing conversation with this lead (New message overlay)');
285
+ await closeAllConversationBubbles(page);
286
+ return {
287
+ threads: [],
288
+ thread_count: 0,
289
+ total_messages: 0,
290
+ status: 'no_history',
291
+ message: `No previous messages found with ${username}. Conversation overlay shows "New message".`
292
+ };
293
+ }
294
+
295
+ // Record message counts per thread before scrolling (to identify target thread)
296
+ const countsBeforeScroll = new Map();
297
+ for (const [id, thread] of threadsMap) {
298
+ countsBeforeScroll.set(id, thread.messages.length);
299
+ }
300
+
301
+ // Scroll up in the conversation overlay to load full message history
302
+ const msgContainer = page.locator('.msg-s-message-list-container').first();
303
+ if (await msgContainer.count() > 0) {
304
+ for (let scrollAttempt = 0; scrollAttempt < 15; scrollAttempt++) {
305
+ const beforeCount = countAllMessages(threadsMap);
306
+ await msgContainer.evaluate(el => el.scrollTo({ top: 0, behavior: 'instant' }));
307
+ await new Promise(resolve => setTimeout(resolve, 2000));
308
+ const afterCount = countAllMessages(threadsMap);
309
+ const newMsgs = afterCount - beforeCount;
310
+ console.log(`[get_messages] Scroll ${scrollAttempt + 1}: +${newMsgs} messages (total: ${afterCount})`);
311
+ if (newMsgs === 0) break;
312
+ }
313
+ }
314
+
315
+ page.off('response', responseListener);
316
+ networkMsgCount = countAllMessages(threadsMap);
317
+
318
+ // Identify the target thread (the conversation with this specific lead)
319
+ let targetThreadId = null;
320
+
321
+ // Method 1: Thread that gained messages during scroll (most reliable)
322
+ for (const [id, thread] of threadsMap) {
323
+ const beforeCount = countsBeforeScroll.get(id) || 0;
324
+ if (thread.messages.length > beforeCount) {
325
+ targetThreadId = id;
326
+ console.log(`[get_messages] Target thread by scroll activity: ${id} (${beforeCount} -> ${thread.messages.length} msgs)`);
327
+ break;
328
+ }
329
+ }
330
+
331
+ // Method 2: Match by lead name from overlay header
332
+ if (!targetThreadId && overlayLeadName) {
333
+ for (const [id, thread] of threadsMap) {
334
+ const others = thread.participants.filter(p => !p.isSelf);
335
+ if (others.some(p => p.name === overlayLeadName)) {
336
+ targetThreadId = id;
337
+ console.log(`[get_messages] Target thread by name match: ${id}`);
338
+ break;
339
+ }
340
+ }
341
+ }
342
+
343
+ // Build result filtered to target thread only
344
+ const allThreads = buildResult(threadsMap);
345
+ let threads;
346
+ if (targetThreadId) {
347
+ console.log(`[get_messages] Filtering to target thread: ${targetThreadId}`);
348
+ threads = allThreads.filter(t => t.thread_id === targetThreadId);
349
+ } else {
350
+ // Could not identify target — do NOT return all threads (that's other people's conversations)
351
+ console.log(`[get_messages] Could not identify target thread for ${username}, returning empty`);
352
+ threads = [];
353
+ }
267
354
 
268
355
  let totalMessages = 0;
269
356
  for (const t of threads) totalMessages += t.message_count;