lazy-gravity 0.6.1 → 0.6.2
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.
- package/dist/bot/index.js +62 -17
- package/dist/bot/telegramMessageHandler.js +3 -0
- package/dist/services/responseMonitor.js +235 -48
- package/package.json +1 -1
package/dist/bot/index.js
CHANGED
|
@@ -184,13 +184,19 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
184
184
|
const enqueueGeneral = createSerialTaskQueueForTest('general', monitorTraceId);
|
|
185
185
|
const enqueueResponse = createSerialTaskQueueForTest('response', monitorTraceId);
|
|
186
186
|
const enqueueActivity = createSerialTaskQueueForTest('activity', monitorTraceId);
|
|
187
|
+
const logDeliveryError = (scope, error) => {
|
|
188
|
+
const messageText = error instanceof Error ? error.message : String(error);
|
|
189
|
+
logger_1.logger.warn(`[DiscordDelivery:${monitorTraceId}] ${scope} failed: ${messageText}`);
|
|
190
|
+
};
|
|
187
191
|
const sendEmbed = (title, description, color, fields, footerText) => enqueueGeneral(async () => {
|
|
188
192
|
if (!channel)
|
|
189
193
|
return;
|
|
190
194
|
if (outputFormat === 'plain') {
|
|
191
195
|
const chunks = (0, plainTextFormatter_1.formatAsPlainText)({ title, description, fields, footerText });
|
|
192
196
|
for (const chunk of chunks) {
|
|
193
|
-
await channel.send({ content: chunk }).catch(() => {
|
|
197
|
+
await channel.send({ content: chunk }).catch((error) => {
|
|
198
|
+
logDeliveryError('sendEmbed/plain/send', error);
|
|
199
|
+
});
|
|
194
200
|
}
|
|
195
201
|
return;
|
|
196
202
|
}
|
|
@@ -205,7 +211,9 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
205
211
|
if (footerText) {
|
|
206
212
|
embed.setFooter({ text: footerText });
|
|
207
213
|
}
|
|
208
|
-
await channel.send({ embeds: [embed] }).catch(() => {
|
|
214
|
+
await channel.send({ embeds: [embed] }).catch((error) => {
|
|
215
|
+
logDeliveryError('sendEmbed/embed/send', error);
|
|
216
|
+
});
|
|
209
217
|
}, 'send-embed');
|
|
210
218
|
const shouldTryGeneratedImages = (inputPrompt, responseText) => {
|
|
211
219
|
const prompt = (inputPrompt || '').toLowerCase();
|
|
@@ -238,7 +246,9 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
238
246
|
await channel.send({
|
|
239
247
|
content: (0, i18n_1.t)(`🖼️ Detected generated images (${files.length})`),
|
|
240
248
|
files,
|
|
241
|
-
}).catch(() => {
|
|
249
|
+
}).catch((error) => {
|
|
250
|
+
logDeliveryError('sendGeneratedImages/send', error);
|
|
251
|
+
});
|
|
242
252
|
}, 'send-generated-images');
|
|
243
253
|
};
|
|
244
254
|
const tryEmergencyExtractText = async () => {
|
|
@@ -322,7 +332,9 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
322
332
|
// Apply default model preference on CDP connect
|
|
323
333
|
const defaultModelResult = await (0, defaultModelApplicator_1.applyDefaultModel)(cdp, modelService);
|
|
324
334
|
if (defaultModelResult.stale && defaultModelResult.staleMessage && channel) {
|
|
325
|
-
await channel.send(defaultModelResult.staleMessage).catch(() => {
|
|
335
|
+
await channel.send(defaultModelResult.staleMessage).catch((error) => {
|
|
336
|
+
logDeliveryError('defaultModelResult/send', error);
|
|
337
|
+
});
|
|
326
338
|
}
|
|
327
339
|
const localMode = modeService.getCurrentMode();
|
|
328
340
|
const modeName = modeService_1.MODE_UI_NAMES[localMode] || localMode;
|
|
@@ -383,11 +395,18 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
383
395
|
lastLiveResponseKey = renderKey;
|
|
384
396
|
for (let i = 0; i < plainChunks.length; i++) {
|
|
385
397
|
if (!liveResponseMessages[i]) {
|
|
386
|
-
liveResponseMessages[i] = await channel.send({ content: plainChunks[i] }).catch(() =>
|
|
398
|
+
liveResponseMessages[i] = await channel.send({ content: plainChunks[i] }).catch((error) => {
|
|
399
|
+
logDeliveryError('liveResponse/plain/send', error);
|
|
400
|
+
return null;
|
|
401
|
+
});
|
|
387
402
|
continue;
|
|
388
403
|
}
|
|
389
|
-
await liveResponseMessages[i].edit({ content: plainChunks[i] }).catch(async () => {
|
|
390
|
-
|
|
404
|
+
await liveResponseMessages[i].edit({ content: plainChunks[i] }).catch(async (error) => {
|
|
405
|
+
logDeliveryError('liveResponse/plain/edit', error);
|
|
406
|
+
liveResponseMessages[i] = await channel.send({ content: plainChunks[i] }).catch((sendError) => {
|
|
407
|
+
logDeliveryError('liveResponse/plain/resend', sendError);
|
|
408
|
+
return null;
|
|
409
|
+
});
|
|
391
410
|
});
|
|
392
411
|
}
|
|
393
412
|
while (liveResponseMessages.length > plainChunks.length) {
|
|
@@ -412,11 +431,18 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
412
431
|
.setFooter({ text: footerText })
|
|
413
432
|
.setTimestamp();
|
|
414
433
|
if (!liveResponseMessages[i]) {
|
|
415
|
-
liveResponseMessages[i] = await channel.send({ embeds: [embed] }).catch(() =>
|
|
434
|
+
liveResponseMessages[i] = await channel.send({ embeds: [embed] }).catch((error) => {
|
|
435
|
+
logDeliveryError('liveResponse/embed/send', error);
|
|
436
|
+
return null;
|
|
437
|
+
});
|
|
416
438
|
continue;
|
|
417
439
|
}
|
|
418
|
-
await liveResponseMessages[i].edit({ embeds: [embed] }).catch(async () => {
|
|
419
|
-
|
|
440
|
+
await liveResponseMessages[i].edit({ embeds: [embed] }).catch(async (error) => {
|
|
441
|
+
logDeliveryError('liveResponse/embed/edit', error);
|
|
442
|
+
liveResponseMessages[i] = await channel.send({ embeds: [embed] }).catch((sendError) => {
|
|
443
|
+
logDeliveryError('liveResponse/embed/resend', sendError);
|
|
444
|
+
return null;
|
|
445
|
+
});
|
|
420
446
|
});
|
|
421
447
|
}
|
|
422
448
|
// Delete excess messages if page count decreased
|
|
@@ -444,11 +470,18 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
444
470
|
lastLiveActivityKey = renderKey;
|
|
445
471
|
for (let i = 0; i < plainChunks.length; i++) {
|
|
446
472
|
if (!liveActivityMessages[i]) {
|
|
447
|
-
liveActivityMessages[i] = await channel.send({ content: plainChunks[i] }).catch(() =>
|
|
473
|
+
liveActivityMessages[i] = await channel.send({ content: plainChunks[i] }).catch((error) => {
|
|
474
|
+
logDeliveryError('liveActivity/plain/send', error);
|
|
475
|
+
return null;
|
|
476
|
+
});
|
|
448
477
|
continue;
|
|
449
478
|
}
|
|
450
|
-
await liveActivityMessages[i].edit({ content: plainChunks[i] }).catch(async () => {
|
|
451
|
-
|
|
479
|
+
await liveActivityMessages[i].edit({ content: plainChunks[i] }).catch(async (error) => {
|
|
480
|
+
logDeliveryError('liveActivity/plain/edit', error);
|
|
481
|
+
liveActivityMessages[i] = await channel.send({ content: plainChunks[i] }).catch((sendError) => {
|
|
482
|
+
logDeliveryError('liveActivity/plain/resend', sendError);
|
|
483
|
+
return null;
|
|
484
|
+
});
|
|
452
485
|
});
|
|
453
486
|
}
|
|
454
487
|
while (liveActivityMessages.length > plainChunks.length) {
|
|
@@ -473,11 +506,18 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
473
506
|
.setFooter({ text: footerText })
|
|
474
507
|
.setTimestamp();
|
|
475
508
|
if (!liveActivityMessages[i]) {
|
|
476
|
-
liveActivityMessages[i] = await channel.send({ embeds: [embed] }).catch(() =>
|
|
509
|
+
liveActivityMessages[i] = await channel.send({ embeds: [embed] }).catch((error) => {
|
|
510
|
+
logDeliveryError('liveActivity/embed/send', error);
|
|
511
|
+
return null;
|
|
512
|
+
});
|
|
477
513
|
continue;
|
|
478
514
|
}
|
|
479
|
-
await liveActivityMessages[i].edit({ embeds: [embed] }).catch(async () => {
|
|
480
|
-
|
|
515
|
+
await liveActivityMessages[i].edit({ embeds: [embed] }).catch(async (error) => {
|
|
516
|
+
logDeliveryError('liveActivity/embed/edit', error);
|
|
517
|
+
liveActivityMessages[i] = await channel.send({ embeds: [embed] }).catch((sendError) => {
|
|
518
|
+
logDeliveryError('liveActivity/embed/resend', sendError);
|
|
519
|
+
return null;
|
|
520
|
+
});
|
|
481
521
|
});
|
|
482
522
|
}
|
|
483
523
|
while (liveActivityMessages.length > descriptions.length) {
|
|
@@ -488,6 +528,7 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
488
528
|
}
|
|
489
529
|
}, `upsert-activity:${opts?.source ?? 'unknown'}`);
|
|
490
530
|
try {
|
|
531
|
+
const baseline = await (0, responseMonitor_1.captureResponseMonitorBaseline)(cdp);
|
|
491
532
|
logger_1.logger.prompt(prompt);
|
|
492
533
|
let injectResult;
|
|
493
534
|
if (inboundImages.length > 0) {
|
|
@@ -516,6 +557,8 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
516
557
|
maxDurationMs: options?.responseTimeoutMs,
|
|
517
558
|
stopGoneConfirmCount: 3,
|
|
518
559
|
extractionMode: options?.extractionMode,
|
|
560
|
+
initialBaselineText: baseline.text,
|
|
561
|
+
initialSeenProcessLogKeys: baseline.processLogKeys,
|
|
519
562
|
onPhaseChange: (_phase, _text) => {
|
|
520
563
|
// Phase transitions are already logged inside ResponseMonitor.setPhase()
|
|
521
564
|
},
|
|
@@ -578,7 +621,9 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
|
|
|
578
621
|
try {
|
|
579
622
|
const modelsPayload = await (0, modelsUi_1.buildModelsUI)(cdp, () => bridge.quota.fetchQuota());
|
|
580
623
|
if (modelsPayload && channel) {
|
|
581
|
-
await channel.send({ ...modelsPayload })
|
|
624
|
+
await channel.send({ ...modelsPayload }).catch((error) => {
|
|
625
|
+
logDeliveryError('quota/modelsPayload/send', error);
|
|
626
|
+
});
|
|
582
627
|
}
|
|
583
628
|
}
|
|
584
629
|
catch (e) {
|
|
@@ -155,6 +155,7 @@ function createTelegramMessageHandler(deps) {
|
|
|
155
155
|
}
|
|
156
156
|
// Determine the prompt text — use default for image-only messages
|
|
157
157
|
const effectivePrompt = promptText || 'Please review the attached images and respond accordingly.';
|
|
158
|
+
const baseline = await (0, responseMonitor_1.captureResponseMonitorBaseline)(cdp);
|
|
158
159
|
// Inject prompt (with or without images) into Antigravity
|
|
159
160
|
logger_1.logger.prompt(effectivePrompt);
|
|
160
161
|
let injectResult;
|
|
@@ -208,6 +209,8 @@ function createTelegramMessageHandler(deps) {
|
|
|
208
209
|
maxDurationMs: TIMEOUT_MS,
|
|
209
210
|
stopGoneConfirmCount: 3,
|
|
210
211
|
extractionMode: deps.extractionMode,
|
|
212
|
+
initialBaselineText: baseline.text,
|
|
213
|
+
initialSeenProcessLogKeys: baseline.processLogKeys,
|
|
211
214
|
onProcessLog: (logText) => {
|
|
212
215
|
if (logText && logText.trim().length > 0) {
|
|
213
216
|
lastActivityLogText = processLogBuffer.append(logText);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ResponseMonitor = exports.RESPONSE_SELECTORS = void 0;
|
|
4
|
+
exports.captureResponseMonitorBaseline = captureResponseMonitorBaseline;
|
|
4
5
|
const logger_1 = require("../utils/logger");
|
|
5
6
|
const assistantDomExtractor_1 = require("./assistantDomExtractor");
|
|
6
7
|
/** Lean DOM selectors for response extraction */
|
|
@@ -433,6 +434,125 @@ exports.RESPONSE_SELECTORS = {
|
|
|
433
434
|
return diag;
|
|
434
435
|
})()`,
|
|
435
436
|
};
|
|
437
|
+
/**
|
|
438
|
+
* Prefer the active runtime context first, then fall back to any discovered contexts.
|
|
439
|
+
*/
|
|
440
|
+
function getOrderedContextTargets(cdpService) {
|
|
441
|
+
const primaryId = cdpService.getPrimaryContextId?.() ?? null;
|
|
442
|
+
const rawContexts = cdpService.getContexts?.() ?? [];
|
|
443
|
+
const contexts = rawContexts
|
|
444
|
+
.filter((ctx) => ctx && typeof ctx.id === 'number')
|
|
445
|
+
.map((ctx) => ({
|
|
446
|
+
id: ctx.id,
|
|
447
|
+
name: typeof ctx.name === 'string' ? ctx.name : undefined,
|
|
448
|
+
url: typeof ctx.url === 'string' ? ctx.url : undefined,
|
|
449
|
+
}));
|
|
450
|
+
if (contexts.length === 0) {
|
|
451
|
+
return primaryId !== null ? [{ id: primaryId }] : [];
|
|
452
|
+
}
|
|
453
|
+
if (primaryId === null) {
|
|
454
|
+
return contexts;
|
|
455
|
+
}
|
|
456
|
+
const primary = contexts.find((ctx) => ctx.id === primaryId);
|
|
457
|
+
const ordered = primary ? [primary] : [{ id: primaryId }];
|
|
458
|
+
for (const ctx of contexts) {
|
|
459
|
+
if (ctx.id !== primaryId)
|
|
460
|
+
ordered.push(ctx);
|
|
461
|
+
}
|
|
462
|
+
return ordered;
|
|
463
|
+
}
|
|
464
|
+
/**
|
|
465
|
+
* Evaluate an expression across known runtime contexts until one returns an acceptable value.
|
|
466
|
+
*/
|
|
467
|
+
async function evaluateAcrossContexts(cdpService, expression, accept, options) {
|
|
468
|
+
const awaitPromise = options?.awaitPromise ?? true;
|
|
469
|
+
const targets = getOrderedContextTargets(cdpService);
|
|
470
|
+
let firstValue = null;
|
|
471
|
+
let lastError = null;
|
|
472
|
+
if (targets.length === 0) {
|
|
473
|
+
const result = await cdpService.call('Runtime.evaluate', {
|
|
474
|
+
expression,
|
|
475
|
+
returnByValue: true,
|
|
476
|
+
awaitPromise,
|
|
477
|
+
});
|
|
478
|
+
return {
|
|
479
|
+
value: (result?.result?.value ?? null),
|
|
480
|
+
contextId: null,
|
|
481
|
+
contextName: null,
|
|
482
|
+
contextUrl: null,
|
|
483
|
+
};
|
|
484
|
+
}
|
|
485
|
+
for (const target of targets) {
|
|
486
|
+
try {
|
|
487
|
+
const result = await cdpService.call('Runtime.evaluate', {
|
|
488
|
+
expression,
|
|
489
|
+
returnByValue: true,
|
|
490
|
+
awaitPromise,
|
|
491
|
+
contextId: target.id,
|
|
492
|
+
});
|
|
493
|
+
const value = (result?.result?.value ?? null);
|
|
494
|
+
const probed = {
|
|
495
|
+
value,
|
|
496
|
+
contextId: target.id,
|
|
497
|
+
contextName: target.name ?? null,
|
|
498
|
+
contextUrl: target.url ?? null,
|
|
499
|
+
};
|
|
500
|
+
if (!firstValue)
|
|
501
|
+
firstValue = probed;
|
|
502
|
+
if (accept(value)) {
|
|
503
|
+
return probed;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
catch (error) {
|
|
507
|
+
lastError = error;
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
if (firstValue) {
|
|
511
|
+
return firstValue;
|
|
512
|
+
}
|
|
513
|
+
if (lastError) {
|
|
514
|
+
throw lastError;
|
|
515
|
+
}
|
|
516
|
+
return {
|
|
517
|
+
value: null,
|
|
518
|
+
contextId: null,
|
|
519
|
+
contextName: null,
|
|
520
|
+
contextUrl: null,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* Capture the current assistant/output DOM state before sending a new prompt.
|
|
525
|
+
* This avoids races where a fast reply is mistaken for baseline text.
|
|
526
|
+
*/
|
|
527
|
+
async function captureResponseMonitorBaseline(cdpService) {
|
|
528
|
+
let text = null;
|
|
529
|
+
try {
|
|
530
|
+
const textResult = await evaluateAcrossContexts(cdpService, exports.RESPONSE_SELECTORS.RESPONSE_TEXT, (value) => typeof value === 'string' && value.trim().length > 0);
|
|
531
|
+
text = typeof textResult.value === 'string' ? textResult.value.trim() || null : null;
|
|
532
|
+
}
|
|
533
|
+
catch {
|
|
534
|
+
text = null;
|
|
535
|
+
}
|
|
536
|
+
const processLogKeys = new Set();
|
|
537
|
+
try {
|
|
538
|
+
const logResult = await evaluateAcrossContexts(cdpService, exports.RESPONSE_SELECTORS.PROCESS_LOGS, (value) => Array.isArray(value) && value.length > 0);
|
|
539
|
+
const logEntries = logResult.value;
|
|
540
|
+
if (Array.isArray(logEntries)) {
|
|
541
|
+
for (const entry of logEntries) {
|
|
542
|
+
const key = String(entry || '').replace(/\r/g, '').trim().slice(0, 200);
|
|
543
|
+
if (key)
|
|
544
|
+
processLogKeys.add(key);
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
catch {
|
|
549
|
+
// best-effort baseline capture
|
|
550
|
+
}
|
|
551
|
+
return {
|
|
552
|
+
text,
|
|
553
|
+
processLogKeys: Array.from(processLogKeys),
|
|
554
|
+
};
|
|
555
|
+
}
|
|
436
556
|
/**
|
|
437
557
|
* Lean AI response monitor.
|
|
438
558
|
*
|
|
@@ -452,6 +572,8 @@ class ResponseMonitor {
|
|
|
452
572
|
onTimeout;
|
|
453
573
|
onPhaseChange;
|
|
454
574
|
onProcessLog;
|
|
575
|
+
initialBaselineText;
|
|
576
|
+
initialSeenProcessLogKeys;
|
|
455
577
|
pollTimer = null;
|
|
456
578
|
isRunning = false;
|
|
457
579
|
lastText = null;
|
|
@@ -462,6 +584,7 @@ class ResponseMonitor {
|
|
|
462
584
|
quotaDetected = false;
|
|
463
585
|
seenProcessLogKeys = new Set();
|
|
464
586
|
structuredDiagLogged = false;
|
|
587
|
+
lastContentContextId = null;
|
|
465
588
|
// CDP disconnect handling (#48)
|
|
466
589
|
isPaused = false;
|
|
467
590
|
onCdpDisconnected = null;
|
|
@@ -480,6 +603,8 @@ class ResponseMonitor {
|
|
|
480
603
|
this.onTimeout = options.onTimeout;
|
|
481
604
|
this.onPhaseChange = options.onPhaseChange;
|
|
482
605
|
this.onProcessLog = options.onProcessLog;
|
|
606
|
+
this.initialBaselineText = options.initialBaselineText;
|
|
607
|
+
this.initialSeenProcessLogKeys = options.initialSeenProcessLogKeys;
|
|
483
608
|
}
|
|
484
609
|
/** Start monitoring */
|
|
485
610
|
async start() {
|
|
@@ -508,28 +633,38 @@ class ResponseMonitor {
|
|
|
508
633
|
this.quotaDetected = false;
|
|
509
634
|
this.seenProcessLogKeys = new Set();
|
|
510
635
|
this.onPhaseChange?.(this.currentPhase, null);
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
const baseResult = await this.cdpService.call('Runtime.evaluate', this.buildEvaluateParams(exports.RESPONSE_SELECTORS.RESPONSE_TEXT));
|
|
514
|
-
const rawValue = baseResult?.result?.value;
|
|
515
|
-
this.baselineText = typeof rawValue === 'string' ? rawValue.trim() || null : null;
|
|
516
|
-
}
|
|
517
|
-
catch {
|
|
518
|
-
this.baselineText = null;
|
|
636
|
+
if (this.initialBaselineText !== undefined) {
|
|
637
|
+
this.baselineText = this.initialBaselineText;
|
|
519
638
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
.filter((s) => s.length > 0)
|
|
528
|
-
.map((s) => s.slice(0, 200)));
|
|
639
|
+
else {
|
|
640
|
+
try {
|
|
641
|
+
const baseResult = await this.evaluateAcrossContexts(exports.RESPONSE_SELECTORS.RESPONSE_TEXT, (value) => typeof value === 'string' && value.trim().length > 0);
|
|
642
|
+
this.baselineText = typeof baseResult.value === 'string' ? baseResult.value.trim() || null : null;
|
|
643
|
+
}
|
|
644
|
+
catch {
|
|
645
|
+
this.baselineText = null;
|
|
529
646
|
}
|
|
530
647
|
}
|
|
531
|
-
|
|
532
|
-
|
|
648
|
+
if (this.initialSeenProcessLogKeys !== undefined) {
|
|
649
|
+
this.seenProcessLogKeys = new Set(this.initialSeenProcessLogKeys
|
|
650
|
+
.map((s) => (s || '').replace(/\r/g, '').trim())
|
|
651
|
+
.filter((s) => s.length > 0)
|
|
652
|
+
.map((s) => s.slice(0, 200)));
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
try {
|
|
656
|
+
const logResult = await this.evaluateAcrossContexts(exports.RESPONSE_SELECTORS.PROCESS_LOGS, (value) => Array.isArray(value) && value.length > 0);
|
|
657
|
+
const logEntries = logResult.value;
|
|
658
|
+
if (Array.isArray(logEntries)) {
|
|
659
|
+
this.seenProcessLogKeys = new Set(logEntries
|
|
660
|
+
.map((s) => (s || '').replace(/\r/g, '').trim())
|
|
661
|
+
.filter((s) => s.length > 0)
|
|
662
|
+
.map((s) => s.slice(0, 200)));
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
catch {
|
|
666
|
+
// baseline capture only
|
|
667
|
+
}
|
|
533
668
|
}
|
|
534
669
|
// In structured mode, also capture activity lines from the structured
|
|
535
670
|
// extraction to align the baseline with polling logic. The PROCESS_LOGS
|
|
@@ -538,8 +673,8 @@ class ResponseMonitor {
|
|
|
538
673
|
// entries from previous turns leak into the process log as "new" entries.
|
|
539
674
|
if (this.extractionMode === 'structured') {
|
|
540
675
|
try {
|
|
541
|
-
const structuredBaseline = await this.
|
|
542
|
-
const baselineClassified = (0, assistantDomExtractor_1.classifyAssistantSegments)(structuredBaseline
|
|
676
|
+
const structuredBaseline = await this.evaluateAcrossContexts(exports.RESPONSE_SELECTORS.RESPONSE_STRUCTURED, (value) => (0, assistantDomExtractor_1.classifyAssistantSegments)(value).diagnostics.source === 'dom-structured');
|
|
677
|
+
const baselineClassified = (0, assistantDomExtractor_1.classifyAssistantSegments)(structuredBaseline.value);
|
|
543
678
|
if (baselineClassified.diagnostics.source === 'dom-structured') {
|
|
544
679
|
for (const line of baselineClassified.activityLines) {
|
|
545
680
|
const key = (line || '').replace(/\r/g, '').trim().slice(0, 200);
|
|
@@ -589,12 +724,15 @@ class ResponseMonitor {
|
|
|
589
724
|
/** Click the stop button to interrupt LLM generation */
|
|
590
725
|
async clickStopButton() {
|
|
591
726
|
try {
|
|
592
|
-
const result = await this.
|
|
593
|
-
const value = result
|
|
727
|
+
const result = await this.evaluateAcrossContexts(exports.RESPONSE_SELECTORS.CLICK_STOP_BUTTON, (value) => !!(value && typeof value === 'object' && value.ok));
|
|
728
|
+
const value = result.value;
|
|
594
729
|
if (this.isRunning) {
|
|
595
730
|
await this.stop();
|
|
596
731
|
}
|
|
597
|
-
|
|
732
|
+
if (value && typeof value.ok === 'boolean') {
|
|
733
|
+
return value;
|
|
734
|
+
}
|
|
735
|
+
return { ok: false, error: 'CDP evaluation returned empty' };
|
|
598
736
|
}
|
|
599
737
|
catch (error) {
|
|
600
738
|
return { ok: false, error: error.message || 'Failed to click stop button' };
|
|
@@ -693,17 +831,70 @@ class ResponseMonitor {
|
|
|
693
831
|
}
|
|
694
832
|
}, this.pollIntervalMs);
|
|
695
833
|
}
|
|
696
|
-
|
|
697
|
-
const
|
|
698
|
-
expression,
|
|
699
|
-
returnByValue: true,
|
|
834
|
+
async evaluateAcrossContexts(expression, accept) {
|
|
835
|
+
const result = await evaluateAcrossContexts(this.cdpService, expression, accept, {
|
|
700
836
|
awaitPromise: true,
|
|
701
|
-
};
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
837
|
+
});
|
|
838
|
+
if (result.contextId !== null
|
|
839
|
+
&& accept(result.value)
|
|
840
|
+
&& this.lastContentContextId !== result.contextId) {
|
|
841
|
+
this.lastContentContextId = result.contextId;
|
|
842
|
+
logger_1.logger.debug(`[ResponseMonitor] Using context ${result.contextId} (${result.contextName ?? 'unknown'} | ${result.contextUrl ?? 'no-url'})`);
|
|
843
|
+
}
|
|
844
|
+
return result;
|
|
845
|
+
}
|
|
846
|
+
async logStructuredExtractionDiagnostics(payload) {
|
|
847
|
+
try {
|
|
848
|
+
const dumpResult = await this.evaluateAcrossContexts(exports.RESPONSE_SELECTORS.DUMP_ALL_TEXTS, (value) => Array.isArray(value) && value.length > 0);
|
|
849
|
+
const dumpValue = Array.isArray(dumpResult.value) ? dumpResult.value : [];
|
|
850
|
+
const accepted = dumpValue.filter((entry) => !entry?.skip).slice(0, 5);
|
|
851
|
+
const skipped = dumpValue.filter((entry) => entry?.skip).slice(0, 5);
|
|
852
|
+
logger_1.logger.warn(`[ResponseMonitor:diag] Structured payload invalid — ${dumpValue.length} candidate(s), ` +
|
|
853
|
+
`${accepted.length} accepted, ${skipped.length} skipped ` +
|
|
854
|
+
`(context=${dumpResult.contextId ?? 'none'})`);
|
|
855
|
+
logger_1.logger.debug('[ResponseMonitor:diag] Candidate details:', JSON.stringify({
|
|
856
|
+
payloadType: payload === null ? 'null' : typeof payload,
|
|
857
|
+
contextId: dumpResult.contextId,
|
|
858
|
+
contextUrl: dumpResult.contextUrl,
|
|
859
|
+
totalCandidates: dumpValue.length,
|
|
860
|
+
accepted: accepted.map((entry) => ({
|
|
861
|
+
sel: entry.sel,
|
|
862
|
+
len: entry.len,
|
|
863
|
+
preview: entry.preview,
|
|
864
|
+
})),
|
|
865
|
+
skipped: skipped.map((entry) => ({
|
|
866
|
+
sel: entry.sel,
|
|
867
|
+
skip: entry.skip,
|
|
868
|
+
len: entry.len,
|
|
869
|
+
preview: entry.preview,
|
|
870
|
+
})),
|
|
871
|
+
}));
|
|
872
|
+
}
|
|
873
|
+
catch (error) {
|
|
874
|
+
logger_1.logger.warn('[ResponseMonitor:diag] DUMP_ALL_TEXTS failed:', error);
|
|
875
|
+
}
|
|
876
|
+
try {
|
|
877
|
+
const domResult = await this.evaluateAcrossContexts(exports.RESPONSE_SELECTORS.DOM_DIAGNOSTIC, (value) => !!value && typeof value === 'object' && (Array.isArray(value.allTextNodes)
|
|
878
|
+
|| Array.isArray(value.activityNodes)
|
|
879
|
+
|| Array.isArray(value.detailsDump)));
|
|
880
|
+
const domValue = domResult.value;
|
|
881
|
+
logger_1.logger.warn(`[ResponseMonitor:diag] DOM_DIAGNOSTIC — ` +
|
|
882
|
+
`details=${domValue?.detailsCount ?? 0}, ` +
|
|
883
|
+
`activity=${Array.isArray(domValue?.activityNodes) ? domValue.activityNodes.length : 0}, ` +
|
|
884
|
+
`textNodes=${Array.isArray(domValue?.allTextNodes) ? domValue.allTextNodes.length : 0} ` +
|
|
885
|
+
`(context=${domResult.contextId ?? 'none'})`);
|
|
886
|
+
logger_1.logger.debug('[ResponseMonitor:diag] DOM_DIAGNOSTIC details:', JSON.stringify({
|
|
887
|
+
contextId: domResult.contextId,
|
|
888
|
+
contextUrl: domResult.contextUrl,
|
|
889
|
+
detailsCount: domValue?.detailsCount ?? null,
|
|
890
|
+
detailsDump: Array.isArray(domValue?.detailsDump) ? domValue.detailsDump.slice(0, 3) : [],
|
|
891
|
+
activityNodes: Array.isArray(domValue?.activityNodes) ? domValue.activityNodes.slice(0, 5) : [],
|
|
892
|
+
allTextNodes: Array.isArray(domValue?.allTextNodes) ? domValue.allTextNodes.slice(0, 5) : [],
|
|
893
|
+
}));
|
|
894
|
+
}
|
|
895
|
+
catch (error) {
|
|
896
|
+
logger_1.logger.warn('[ResponseMonitor:diag] DOM_DIAGNOSTIC failed:', error);
|
|
705
897
|
}
|
|
706
|
-
return params;
|
|
707
898
|
}
|
|
708
899
|
/**
|
|
709
900
|
* Emit new process log entries, deduplicating against previously seen keys.
|
|
@@ -738,20 +929,20 @@ class ResponseMonitor {
|
|
|
738
929
|
async poll() {
|
|
739
930
|
try {
|
|
740
931
|
// 1. Stop button check
|
|
741
|
-
const stopResult = await this.
|
|
742
|
-
const stopValue = stopResult
|
|
932
|
+
const stopResult = await this.evaluateAcrossContexts(exports.RESPONSE_SELECTORS.STOP_BUTTON, (value) => !!(value && typeof value === 'object' && value.isGenerating));
|
|
933
|
+
const stopValue = stopResult.value;
|
|
743
934
|
const isGenerating = !!(stopValue && typeof stopValue === 'object' && stopValue.isGenerating);
|
|
744
935
|
// 2. Quota error check
|
|
745
|
-
const quotaResult = await this.
|
|
746
|
-
const quotaDetected = quotaResult
|
|
936
|
+
const quotaResult = await this.evaluateAcrossContexts(exports.RESPONSE_SELECTORS.QUOTA_ERROR, (value) => value === true);
|
|
937
|
+
const quotaDetected = quotaResult.value === true;
|
|
747
938
|
// 3. Text extraction (structured or legacy)
|
|
748
939
|
let currentText = null;
|
|
749
940
|
let structuredHandledLogs = false;
|
|
750
941
|
if (this.extractionMode === 'structured') {
|
|
751
942
|
// Structured: use DOM segment extraction with HTML-to-Markdown
|
|
752
943
|
try {
|
|
753
|
-
const structuredResult = await this.
|
|
754
|
-
const payload = structuredResult
|
|
944
|
+
const structuredResult = await this.evaluateAcrossContexts(exports.RESPONSE_SELECTORS.RESPONSE_STRUCTURED, (value) => (0, assistantDomExtractor_1.classifyAssistantSegments)(value).diagnostics.source === 'dom-structured');
|
|
945
|
+
const payload = structuredResult.value;
|
|
755
946
|
const classified = (0, assistantDomExtractor_1.classifyAssistantSegments)(payload);
|
|
756
947
|
if (classified.diagnostics.source === 'dom-structured') {
|
|
757
948
|
currentText = classified.finalOutputText.trim() || null;
|
|
@@ -768,6 +959,7 @@ class ResponseMonitor {
|
|
|
768
959
|
else if (!this.structuredDiagLogged) {
|
|
769
960
|
this.structuredDiagLogged = true;
|
|
770
961
|
logger_1.logger.warn('[ResponseMonitor:poll] Structured extraction failed — reason:', classified.diagnostics.fallbackReason ?? 'unknown', '| payload type:', typeof payload, '| payload:', payload === null ? 'null' : payload === undefined ? 'undefined' : 'object');
|
|
962
|
+
await this.logStructuredExtractionDiagnostics(payload);
|
|
771
963
|
}
|
|
772
964
|
}
|
|
773
965
|
catch (error) {
|
|
@@ -776,19 +968,14 @@ class ResponseMonitor {
|
|
|
776
968
|
}
|
|
777
969
|
// Legacy path (or fallback from structured)
|
|
778
970
|
if (currentText === null) {
|
|
779
|
-
const textResult = await this.
|
|
780
|
-
|
|
781
|
-
const exceptionDetail = textResult?.result?.exceptionDetails ?? textResult?.exceptionDetails;
|
|
782
|
-
if (exceptionDetail) {
|
|
783
|
-
logger_1.logger.warn('[ResponseMonitor:poll] RESPONSE_TEXT threw:', exceptionDetail.text ?? JSON.stringify(exceptionDetail).slice(0, 200));
|
|
784
|
-
}
|
|
785
|
-
currentText = typeof rawText === 'string' ? rawText.trim() || null : null;
|
|
971
|
+
const textResult = await this.evaluateAcrossContexts(exports.RESPONSE_SELECTORS.RESPONSE_TEXT, (value) => typeof value === 'string' && value.trim().length > 0);
|
|
972
|
+
currentText = typeof textResult.value === 'string' ? textResult.value.trim() || null : null;
|
|
786
973
|
}
|
|
787
974
|
// 4. Process log extraction — always when structured didn't handle it
|
|
788
975
|
if (!structuredHandledLogs) {
|
|
789
976
|
try {
|
|
790
|
-
const logResult = await this.
|
|
791
|
-
const logEntries = logResult
|
|
977
|
+
const logResult = await this.evaluateAcrossContexts(exports.RESPONSE_SELECTORS.PROCESS_LOGS, (value) => Array.isArray(value) && value.length > 0);
|
|
978
|
+
const logEntries = logResult.value;
|
|
792
979
|
if (Array.isArray(logEntries)) {
|
|
793
980
|
this.emitNewProcessLogs(logEntries);
|
|
794
981
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lazy-gravity",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Control Antigravity from anywhere — a local, secure bot (Discord + Telegram) that lets you remotely operate Antigravity on your home PC from your smartphone.",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"bin": {
|