@heylemon/lemonade 0.6.4 → 0.6.6
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.
|
@@ -346,7 +346,20 @@ export function buildAgentSystemPrompt(params) {
|
|
|
346
346
|
"If the user asks you to do something (take a screenshot, send a file, etc.), just do it — pick the best approach and act.",
|
|
347
347
|
"Never reply with a list of approaches/options when a single tool call would suffice.",
|
|
348
348
|
"CRITICAL: NEVER stop after announcing what you're going to do. Do NOT say 'I'll check your LinkedIn' and then stop. Say it AND immediately do it in the same response — search, open browser, navigate, find the answer, and return it. The user should never have to ask twice. One request = one complete answer.",
|
|
349
|
-
|
|
349
|
+
"",
|
|
350
|
+
"## Screenshots",
|
|
351
|
+
'Endpoint: `curl -s "http://127.0.0.1:19848/screenshot?name=SHORT_NAME"`',
|
|
352
|
+
'Returns: `{"path": "/path/to/file.jpg", "mode": "window"}`',
|
|
353
|
+
"",
|
|
354
|
+
"Parameters:",
|
|
355
|
+
"- `name` (required): short slug describing the screenshot (e.g., `slack-dm-john`, `xcode-error`). Used as filename for later reference.",
|
|
356
|
+
"- `mode`: `window` (default, frontmost app), `screen` (full primary display), `display` (specific monitor)",
|
|
357
|
+
"- `display`: 0-based display index (use with `mode=display` for external monitors)",
|
|
358
|
+
"",
|
|
359
|
+
"Storage: `~/.lemonade/tmp/screenshots/<name>.jpg`",
|
|
360
|
+
"If user refers to a past screenshot and the path is no longer in conversation, run `ls ~/.lemonade/tmp/screenshots/` to find it by name.",
|
|
361
|
+
"",
|
|
362
|
+
"Usage: returned path works with `message` tool (send via WhatsApp/Slack) and `image` tool (analyze).",
|
|
350
363
|
"",
|
|
351
364
|
"## Third-Party App Requests (Trello, Jira, LinkedIn, Asana, HubSpot, Salesforce, Todoist, etc.)",
|
|
352
365
|
"",
|
package/dist/build-info.json
CHANGED
|
@@ -31,12 +31,15 @@ export async function handleTaskEventsHttpRequest(req, res, opts) {
|
|
|
31
31
|
const unsubscribe = onAgentEvent((evt) => {
|
|
32
32
|
if (!RELEVANT_STREAMS.has(evt.stream))
|
|
33
33
|
return;
|
|
34
|
-
// Notifications are always forwarded regardless of session key
|
|
35
|
-
|
|
34
|
+
// Notifications, task_info, and lifecycle are always forwarded regardless of session key.
|
|
35
|
+
// task_info creates tasks in the frontend; lifecycle (start/end/error) manages their state.
|
|
36
|
+
// Without this, runs whose sessionKey lacks a channel name (e.g. dmScope=main → "agent:main:main")
|
|
37
|
+
// would create a task but never complete it, leaving the UI hanging.
|
|
38
|
+
if (evt.stream !== "notification" && evt.stream !== "task_info" && evt.stream !== "lifecycle") {
|
|
36
39
|
const sk = evt.sessionKey ?? "";
|
|
37
40
|
const isMessagingChannel = sk.includes("whatsapp") || sk.includes("slack");
|
|
38
41
|
const isSubagent = sk.includes(":subagent:");
|
|
39
|
-
if (!isMessagingChannel && !isSubagent
|
|
42
|
+
if (!isMessagingChannel && !isSubagent)
|
|
40
43
|
return;
|
|
41
44
|
}
|
|
42
45
|
try {
|
|
@@ -102,6 +102,9 @@ export async function monitorWebChannel(verbose, listenerFactory = monitorWebInb
|
|
|
102
102
|
};
|
|
103
103
|
process.once("SIGINT", handleSigint);
|
|
104
104
|
let reconnectAttempts = 0;
|
|
105
|
+
// Track whether we've already attempted a one-time 515 socket restart.
|
|
106
|
+
// Reset after a successful connection so transient 515s can be retried once per cycle.
|
|
107
|
+
let has515Retried = false;
|
|
105
108
|
while (true) {
|
|
106
109
|
if (stopRequested())
|
|
107
110
|
break;
|
|
@@ -165,8 +168,7 @@ export async function monitorWebChannel(verbose, listenerFactory = monitorWebInb
|
|
|
165
168
|
}
|
|
166
169
|
catch (err) {
|
|
167
170
|
// Initial socket creation or WebSocket handshake failed (e.g., 408
|
|
168
|
-
// timeout, 428 Connection Terminated
|
|
169
|
-
// escapes the while-loop and the reconnect backoff is never reached.
|
|
171
|
+
// timeout, 428 Connection Terminated, 515 Stream Error).
|
|
170
172
|
const errStatusCode = getStatusCode(err);
|
|
171
173
|
const errorStr = formatError(err);
|
|
172
174
|
reconnectLogger.warn({ connectionId, error: errorStr, status: errStatusCode }, "web reconnect: initial connection failed");
|
|
@@ -183,6 +185,21 @@ export async function monitorWebChannel(verbose, listenerFactory = monitorWebInb
|
|
|
183
185
|
runtime.error(`WhatsApp session logged out. Run \`${formatCliCommand("lemonade channels login --channel web")}\` to relink.`);
|
|
184
186
|
break;
|
|
185
187
|
}
|
|
188
|
+
// 515 = "restart required" — WhatsApp demands a fresh socket with the same creds.
|
|
189
|
+
// Mirror the interactive login behavior: retry once with a short delay.
|
|
190
|
+
// If the retry also fails with 515, the session is stale and needs re-linking.
|
|
191
|
+
if (errStatusCode === 515 && !has515Retried) {
|
|
192
|
+
has515Retried = true;
|
|
193
|
+
reconnectLogger.info({ connectionId }, "web reconnect: 515 restart required — retrying with fresh socket");
|
|
194
|
+
runtime.log("WhatsApp asked for a restart (code 515); retrying connection once…");
|
|
195
|
+
try {
|
|
196
|
+
await sleep(2_000, abortSignal);
|
|
197
|
+
}
|
|
198
|
+
catch {
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
continue;
|
|
202
|
+
}
|
|
186
203
|
reconnectAttempts += 1;
|
|
187
204
|
status.reconnectAttempts = reconnectAttempts;
|
|
188
205
|
emitStatus();
|
|
@@ -210,6 +227,7 @@ export async function monitorWebChannel(verbose, listenerFactory = monitorWebInb
|
|
|
210
227
|
status.lastConnectedAt = Date.now();
|
|
211
228
|
status.lastEventAt = status.lastConnectedAt;
|
|
212
229
|
status.lastError = null;
|
|
230
|
+
has515Retried = false; // Reset on successful connection
|
|
213
231
|
emitStatus();
|
|
214
232
|
// Surface a concise connection event for the next main-session turn/heartbeat.
|
|
215
233
|
const { e164: selfE164 } = readWebSelfId(account.authDir);
|
|
@@ -362,6 +380,20 @@ export async function monitorWebChannel(verbose, listenerFactory = monitorWebInb
|
|
|
362
380
|
await closeListener();
|
|
363
381
|
break;
|
|
364
382
|
}
|
|
383
|
+
// 515 = "restart required" — retry once with a fresh socket (same credentials).
|
|
384
|
+
if (statusCode === 515 && !has515Retried) {
|
|
385
|
+
has515Retried = true;
|
|
386
|
+
reconnectLogger.info({ connectionId }, "web reconnect: 515 restart required after disconnect — retrying with fresh socket");
|
|
387
|
+
runtime.log("WhatsApp disconnected with 515 (restart required); retrying connection once…");
|
|
388
|
+
await closeListener();
|
|
389
|
+
try {
|
|
390
|
+
await sleep(2_000, abortSignal);
|
|
391
|
+
}
|
|
392
|
+
catch {
|
|
393
|
+
break;
|
|
394
|
+
}
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
365
397
|
reconnectAttempts += 1;
|
|
366
398
|
status.reconnectAttempts = reconnectAttempts;
|
|
367
399
|
emitStatus();
|