remoat 0.2.0
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/LICENSE +21 -0
- package/README.md +297 -0
- package/dist/bin/cli.d.ts +2 -0
- package/dist/bin/cli.js +80 -0
- package/dist/bin/cli.js.map +1 -0
- package/dist/bin/commands/doctor.d.ts +1 -0
- package/dist/bin/commands/doctor.js +211 -0
- package/dist/bin/commands/doctor.js.map +1 -0
- package/dist/bin/commands/open.d.ts +1 -0
- package/dist/bin/commands/open.js +187 -0
- package/dist/bin/commands/open.js.map +1 -0
- package/dist/bin/commands/setup.d.ts +1 -0
- package/dist/bin/commands/setup.js +267 -0
- package/dist/bin/commands/setup.js.map +1 -0
- package/dist/bin/commands/start.d.ts +2 -0
- package/dist/bin/commands/start.js +39 -0
- package/dist/bin/commands/start.js.map +1 -0
- package/dist/bot/index.d.ts +2 -0
- package/dist/bot/index.js +1393 -0
- package/dist/bot/index.js.map +1 -0
- package/dist/commands/chatCommandHandler.d.ts +20 -0
- package/dist/commands/chatCommandHandler.js +30 -0
- package/dist/commands/chatCommandHandler.js.map +1 -0
- package/dist/commands/cleanupCommandHandler.d.ts +21 -0
- package/dist/commands/cleanupCommandHandler.js +40 -0
- package/dist/commands/cleanupCommandHandler.js.map +1 -0
- package/dist/commands/joinCommandHandler.d.ts +19 -0
- package/dist/commands/joinCommandHandler.js +27 -0
- package/dist/commands/joinCommandHandler.js.map +1 -0
- package/dist/commands/messageParser.d.ts +7 -0
- package/dist/commands/messageParser.js +29 -0
- package/dist/commands/messageParser.js.map +1 -0
- package/dist/commands/slashCommandHandler.d.ts +21 -0
- package/dist/commands/slashCommandHandler.js +105 -0
- package/dist/commands/slashCommandHandler.js.map +1 -0
- package/dist/commands/workspaceCommandHandler.d.ts +16 -0
- package/dist/commands/workspaceCommandHandler.js +29 -0
- package/dist/commands/workspaceCommandHandler.js.map +1 -0
- package/dist/database/chatSessionRepository.d.ts +59 -0
- package/dist/database/chatSessionRepository.js +110 -0
- package/dist/database/chatSessionRepository.js.map +1 -0
- package/dist/database/scheduleRepository.d.ts +60 -0
- package/dist/database/scheduleRepository.js +106 -0
- package/dist/database/scheduleRepository.js.map +1 -0
- package/dist/database/templateRepository.d.ts +51 -0
- package/dist/database/templateRepository.js +90 -0
- package/dist/database/templateRepository.js.map +1 -0
- package/dist/database/workspaceBindingRepository.d.ts +48 -0
- package/dist/database/workspaceBindingRepository.js +92 -0
- package/dist/database/workspaceBindingRepository.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +11 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/auth.d.ts +5 -0
- package/dist/middleware/auth.js +14 -0
- package/dist/middleware/auth.js.map +1 -0
- package/dist/middleware/sanitize.d.ts +1 -0
- package/dist/middleware/sanitize.js +18 -0
- package/dist/middleware/sanitize.js.map +1 -0
- package/dist/services/antigravityLauncher.d.ts +7 -0
- package/dist/services/antigravityLauncher.js +94 -0
- package/dist/services/antigravityLauncher.js.map +1 -0
- package/dist/services/approvalDetector.d.ts +97 -0
- package/dist/services/approvalDetector.js +394 -0
- package/dist/services/approvalDetector.js.map +1 -0
- package/dist/services/assistantDomExtractor.d.ts +49 -0
- package/dist/services/assistantDomExtractor.js +340 -0
- package/dist/services/assistantDomExtractor.js.map +1 -0
- package/dist/services/autoAcceptService.d.ts +14 -0
- package/dist/services/autoAcceptService.js +81 -0
- package/dist/services/autoAcceptService.js.map +1 -0
- package/dist/services/cdpBridgeManager.d.ts +50 -0
- package/dist/services/cdpBridgeManager.js +355 -0
- package/dist/services/cdpBridgeManager.js.map +1 -0
- package/dist/services/cdpConnectionPool.d.ts +88 -0
- package/dist/services/cdpConnectionPool.js +235 -0
- package/dist/services/cdpConnectionPool.js.map +1 -0
- package/dist/services/cdpService.d.ts +214 -0
- package/dist/services/cdpService.js +1423 -0
- package/dist/services/cdpService.js.map +1 -0
- package/dist/services/chatSessionService.d.ts +89 -0
- package/dist/services/chatSessionService.js +738 -0
- package/dist/services/chatSessionService.js.map +1 -0
- package/dist/services/errorPopupDetector.d.ts +89 -0
- package/dist/services/errorPopupDetector.js +274 -0
- package/dist/services/errorPopupDetector.js.map +1 -0
- package/dist/services/modeService.d.ts +44 -0
- package/dist/services/modeService.js +74 -0
- package/dist/services/modeService.js.map +1 -0
- package/dist/services/modelService.d.ts +36 -0
- package/dist/services/modelService.js +64 -0
- package/dist/services/modelService.js.map +1 -0
- package/dist/services/planningDetector.d.ts +87 -0
- package/dist/services/planningDetector.js +321 -0
- package/dist/services/planningDetector.js.map +1 -0
- package/dist/services/processManager.d.ts +18 -0
- package/dist/services/processManager.js +62 -0
- package/dist/services/processManager.js.map +1 -0
- package/dist/services/progressSender.d.ts +20 -0
- package/dist/services/progressSender.js +65 -0
- package/dist/services/progressSender.js.map +1 -0
- package/dist/services/promptDispatcher.d.ts +38 -0
- package/dist/services/promptDispatcher.js +42 -0
- package/dist/services/promptDispatcher.js.map +1 -0
- package/dist/services/quotaService.d.ts +21 -0
- package/dist/services/quotaService.js +191 -0
- package/dist/services/quotaService.js.map +1 -0
- package/dist/services/responseMonitor.d.ts +129 -0
- package/dist/services/responseMonitor.js +996 -0
- package/dist/services/responseMonitor.js.map +1 -0
- package/dist/services/scheduleService.d.ts +58 -0
- package/dist/services/scheduleService.js +135 -0
- package/dist/services/scheduleService.js.map +1 -0
- package/dist/services/screenshotService.d.ts +55 -0
- package/dist/services/screenshotService.js +86 -0
- package/dist/services/screenshotService.js.map +1 -0
- package/dist/services/telegramTopicManager.d.ts +40 -0
- package/dist/services/telegramTopicManager.js +103 -0
- package/dist/services/telegramTopicManager.js.map +1 -0
- package/dist/services/titleGeneratorService.d.ts +32 -0
- package/dist/services/titleGeneratorService.js +114 -0
- package/dist/services/titleGeneratorService.js.map +1 -0
- package/dist/services/updateCheckService.d.ts +16 -0
- package/dist/services/updateCheckService.js +148 -0
- package/dist/services/updateCheckService.js.map +1 -0
- package/dist/services/userMessageDetector.d.ts +57 -0
- package/dist/services/userMessageDetector.js +222 -0
- package/dist/services/userMessageDetector.js.map +1 -0
- package/dist/services/workspaceService.d.ts +33 -0
- package/dist/services/workspaceService.js +65 -0
- package/dist/services/workspaceService.js.map +1 -0
- package/dist/ui/autoAcceptUi.d.ts +6 -0
- package/dist/ui/autoAcceptUi.js +22 -0
- package/dist/ui/autoAcceptUi.js.map +1 -0
- package/dist/ui/modeUi.d.ts +12 -0
- package/dist/ui/modeUi.js +40 -0
- package/dist/ui/modeUi.js.map +1 -0
- package/dist/ui/modelsUi.d.ts +12 -0
- package/dist/ui/modelsUi.js +101 -0
- package/dist/ui/modelsUi.js.map +1 -0
- package/dist/ui/projectListUi.d.ts +11 -0
- package/dist/ui/projectListUi.js +59 -0
- package/dist/ui/projectListUi.js.map +1 -0
- package/dist/ui/screenshotUi.d.ts +6 -0
- package/dist/ui/screenshotUi.js +28 -0
- package/dist/ui/screenshotUi.js.map +1 -0
- package/dist/ui/sessionPickerUi.d.ts +8 -0
- package/dist/ui/sessionPickerUi.js +32 -0
- package/dist/ui/sessionPickerUi.js.map +1 -0
- package/dist/ui/templateUi.d.ts +5 -0
- package/dist/ui/templateUi.js +44 -0
- package/dist/ui/templateUi.js.map +1 -0
- package/dist/utils/cdpPorts.d.ts +2 -0
- package/dist/utils/cdpPorts.js +6 -0
- package/dist/utils/cdpPorts.js.map +1 -0
- package/dist/utils/config.d.ts +14 -0
- package/dist/utils/config.js +12 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/configLoader.d.ts +23 -0
- package/dist/utils/configLoader.js +153 -0
- package/dist/utils/configLoader.js.map +1 -0
- package/dist/utils/htmlToTelegramMarkdown.d.ts +6 -0
- package/dist/utils/htmlToTelegramMarkdown.js +189 -0
- package/dist/utils/htmlToTelegramMarkdown.js.map +1 -0
- package/dist/utils/i18n.d.ts +3 -0
- package/dist/utils/i18n.js +78 -0
- package/dist/utils/i18n.js.map +1 -0
- package/dist/utils/imageHandler.d.ts +35 -0
- package/dist/utils/imageHandler.js +155 -0
- package/dist/utils/imageHandler.js.map +1 -0
- package/dist/utils/lockfile.d.ts +7 -0
- package/dist/utils/lockfile.js +117 -0
- package/dist/utils/lockfile.js.map +1 -0
- package/dist/utils/logger.d.ts +23 -0
- package/dist/utils/logger.js +85 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/logo.d.ts +1 -0
- package/dist/utils/logo.js +14 -0
- package/dist/utils/logo.js.map +1 -0
- package/dist/utils/metadataExtractor.d.ts +5 -0
- package/dist/utils/metadataExtractor.js +16 -0
- package/dist/utils/metadataExtractor.js.map +1 -0
- package/dist/utils/pathUtils.d.ts +23 -0
- package/dist/utils/pathUtils.js +58 -0
- package/dist/utils/pathUtils.js.map +1 -0
- package/dist/utils/processLogBuffer.d.ts +17 -0
- package/dist/utils/processLogBuffer.js +108 -0
- package/dist/utils/processLogBuffer.js.map +1 -0
- package/dist/utils/streamMessageFormatter.d.ts +18 -0
- package/dist/utils/streamMessageFormatter.js +91 -0
- package/dist/utils/streamMessageFormatter.js.map +1 -0
- package/dist/utils/telegramFormatter.d.ts +37 -0
- package/dist/utils/telegramFormatter.js +445 -0
- package/dist/utils/telegramFormatter.js.map +1 -0
- package/dist/utils/voiceHandler.d.ts +23 -0
- package/dist/utils/voiceHandler.js +169 -0
- package/dist/utils/voiceHandler.js.map +1 -0
- package/locales/en.json +85 -0
- package/locales/ja.json +109 -0
- package/package.json +84 -0
|
@@ -0,0 +1,996 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ResponseMonitor = exports.RESPONSE_SELECTORS = void 0;
|
|
4
|
+
const logger_1 = require("../utils/logger");
|
|
5
|
+
const assistantDomExtractor_1 = require("./assistantDomExtractor");
|
|
6
|
+
/** Lean DOM selectors for response extraction */
|
|
7
|
+
exports.RESPONSE_SELECTORS = {
|
|
8
|
+
/** Scored selector approach for extracting response text.
|
|
9
|
+
* Tie-breaking: newest wins (first found in reverse iteration).
|
|
10
|
+
* DOM is normal order: index 0 = oldest, N-1 = newest.
|
|
11
|
+
* Reverse iteration (N-1→0) visits newest first; strict > keeps it. */
|
|
12
|
+
RESPONSE_TEXT: `(() => {
|
|
13
|
+
const panel = document.querySelector('.antigravity-agent-side-panel');
|
|
14
|
+
const scopes = [panel, document].filter(Boolean);
|
|
15
|
+
|
|
16
|
+
const selectors = [
|
|
17
|
+
{ sel: '.rendered-markdown', score: 10 },
|
|
18
|
+
{ sel: '.leading-relaxed.select-text', score: 9 },
|
|
19
|
+
{ sel: '.flex.flex-col.gap-y-3', score: 8 },
|
|
20
|
+
{ sel: '[data-message-author-role="assistant"]', score: 7 },
|
|
21
|
+
{ sel: '[data-message-role="assistant"]', score: 6 },
|
|
22
|
+
{ sel: '[class*="assistant-message"]', score: 5 },
|
|
23
|
+
{ sel: '[class*="message-content"]', score: 4 },
|
|
24
|
+
{ sel: '[class*="markdown-body"]', score: 3 },
|
|
25
|
+
{ sel: '.prose', score: 2 },
|
|
26
|
+
];
|
|
27
|
+
|
|
28
|
+
const looksLikeActivityLog = (text) => {
|
|
29
|
+
const normalized = (text || '').trim().toLowerCase();
|
|
30
|
+
if (!normalized) return false;
|
|
31
|
+
const activityPattern = /^(?:analy[sz]ing|reading|writing|running|searching|planning|thinking|processing|loading|executing|testing|debugging|fetching|connecting|creating|updating|deleting|installing|building|compiling|deploying|checking|scanning|parsing|resolving|downloading|uploading|analyzed|read|wrote|ran|created|updated|deleted|fetched|built|compiled|installed|resolved|downloaded|connected)\\b/i;
|
|
32
|
+
if (activityPattern.test(normalized) && normalized.length <= 220) return true;
|
|
33
|
+
if (/^initiating\\s/i.test(normalized) && normalized.length <= 500) return true;
|
|
34
|
+
if (/^thought for\\s/i.test(normalized) && normalized.length <= 500) return true;
|
|
35
|
+
return false;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const looksLikeFeedbackFooter = (text) => {
|
|
39
|
+
const normalized = (text || '').trim().toLowerCase().replace(/\\s+/g, ' ');
|
|
40
|
+
if (!normalized) return false;
|
|
41
|
+
return normalized === 'good bad' || normalized === 'good' || normalized === 'bad';
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const isInsideExcludedContainer = (node) => {
|
|
45
|
+
if (node.closest('details')) return true;
|
|
46
|
+
if (node.closest('[class*="feedback"], footer')) return true;
|
|
47
|
+
if (node.closest('.notify-user-container')) return true;
|
|
48
|
+
if (node.closest('[role="dialog"]')) return true;
|
|
49
|
+
return false;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const looksLikeToolOutput = (text) => {
|
|
53
|
+
const first = (text || '').trim().split('\\n')[0] || '';
|
|
54
|
+
if (/^[a-z0-9._-]+\\s*\\/\\s*[a-z0-9._-]+$/i.test(first)) return true;
|
|
55
|
+
if (/^full output written to\\b/i.test(first)) return true;
|
|
56
|
+
if (/^output\\.[a-z0-9._-]+(?:#l\\d+(?:-\\d+)?)?$/i.test(first)) return true;
|
|
57
|
+
var lower = (text || '').trim().toLowerCase();
|
|
58
|
+
if (/^title:\\s/.test(lower) && /\\surl:\\s/.test(lower) && /\\ssnippet:\\s/.test(lower)) return true;
|
|
59
|
+
if (/^(json|javascript|typescript|python|bash|sh|html|css|xml|yaml|yml|toml|sql|graphql|markdown|text|plaintext|log|ruby|go|rust|java|c|cpp|csharp|php|swift|kotlin)$/i.test(first)) return true;
|
|
60
|
+
return false;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const looksLikeQuotaPopup = (text) => {
|
|
64
|
+
var lower = (text || '').trim().toLowerCase();
|
|
65
|
+
// Inline error: "Error You have exhausted your quota on this model."
|
|
66
|
+
if (lower.includes('exhausted your quota') || lower.includes('exhausted quota')) return true;
|
|
67
|
+
// Popup: quota keyword + dismiss/upgrade button text
|
|
68
|
+
if (!lower.includes('model quota reached') && !lower.includes('quota exceeded') && !lower.includes('rate limit')) return false;
|
|
69
|
+
return lower.includes('dismiss') || lower.includes('upgrade');
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const combinedSelector = selectors.map((s) => s.sel).join(', ');
|
|
73
|
+
const seen = new Set();
|
|
74
|
+
|
|
75
|
+
for (const scope of scopes) {
|
|
76
|
+
const nodes = scope.querySelectorAll(combinedSelector);
|
|
77
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
78
|
+
const node = nodes[i];
|
|
79
|
+
if (!node || seen.has(node)) continue;
|
|
80
|
+
seen.add(node);
|
|
81
|
+
if (isInsideExcludedContainer(node)) continue;
|
|
82
|
+
const text = (node.innerText || node.textContent || '').replace(/\\r/g, '').trim();
|
|
83
|
+
if (!text || text.length < 2) continue;
|
|
84
|
+
if (looksLikeActivityLog(text)) continue;
|
|
85
|
+
if (looksLikeFeedbackFooter(text)) continue;
|
|
86
|
+
if (looksLikeToolOutput(text)) continue;
|
|
87
|
+
if (looksLikeQuotaPopup(text)) continue;
|
|
88
|
+
// Prefer recency first: return the newest acceptable node.
|
|
89
|
+
return text;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
return null;
|
|
94
|
+
})()`,
|
|
95
|
+
/** Stop button detection via tooltip-id + text fallback */
|
|
96
|
+
STOP_BUTTON: `(() => {
|
|
97
|
+
const panel = document.querySelector('.antigravity-agent-side-panel');
|
|
98
|
+
const scopes = [panel, document].filter(Boolean);
|
|
99
|
+
|
|
100
|
+
for (const scope of scopes) {
|
|
101
|
+
const el = scope.querySelector('[data-tooltip-id="input-send-button-cancel-tooltip"]');
|
|
102
|
+
if (el) return { isGenerating: true };
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const normalize = (value) => (value || '').toLowerCase().replace(/\\s+/g, ' ').trim();
|
|
106
|
+
const STOP_PATTERNS = [
|
|
107
|
+
/^stop$/,
|
|
108
|
+
/^stop generating$/,
|
|
109
|
+
/^stop response$/,
|
|
110
|
+
/^停止$/,
|
|
111
|
+
/^生成を停止$/,
|
|
112
|
+
/^応答を停止$/,
|
|
113
|
+
];
|
|
114
|
+
const isStopLabel = (value) => {
|
|
115
|
+
const normalized = normalize(value);
|
|
116
|
+
if (!normalized) return false;
|
|
117
|
+
return STOP_PATTERNS.some((re) => re.test(normalized));
|
|
118
|
+
};
|
|
119
|
+
for (const scope of scopes) {
|
|
120
|
+
const buttons = scope.querySelectorAll('button, [role="button"]');
|
|
121
|
+
for (let i = 0; i < buttons.length; i++) {
|
|
122
|
+
const btn = buttons[i];
|
|
123
|
+
const labels = [
|
|
124
|
+
btn.textContent || '',
|
|
125
|
+
btn.getAttribute('aria-label') || '',
|
|
126
|
+
btn.getAttribute('title') || '',
|
|
127
|
+
];
|
|
128
|
+
if (labels.some(isStopLabel)) {
|
|
129
|
+
return { isGenerating: true };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
return { isGenerating: false };
|
|
135
|
+
})()`,
|
|
136
|
+
/** Check if planning dialog (Open/Proceed buttons) is active */
|
|
137
|
+
PLANNING_ACTIVE: `(() => {
|
|
138
|
+
var container = document.querySelector('.notify-user-container');
|
|
139
|
+
if (!container) return false;
|
|
140
|
+
var buttons = Array.from(container.querySelectorAll('button')).filter(function(btn) { return btn.offsetParent !== null; });
|
|
141
|
+
var hasOpen = buttons.some(function(btn) { return (btn.textContent || '').toLowerCase().trim() === 'open'; });
|
|
142
|
+
var hasProceed = buttons.some(function(btn) { return (btn.textContent || '').toLowerCase().trim() === 'proceed'; });
|
|
143
|
+
return hasOpen && hasProceed;
|
|
144
|
+
})()`,
|
|
145
|
+
/** Click stop button via tooltip-id + text fallback */
|
|
146
|
+
CLICK_STOP_BUTTON: `(() => {
|
|
147
|
+
const panel = document.querySelector('.antigravity-agent-side-panel');
|
|
148
|
+
const scopes = [panel, document].filter(Boolean);
|
|
149
|
+
|
|
150
|
+
for (const scope of scopes) {
|
|
151
|
+
const el = scope.querySelector('[data-tooltip-id="input-send-button-cancel-tooltip"]');
|
|
152
|
+
if (el && typeof el.click === 'function') {
|
|
153
|
+
el.click();
|
|
154
|
+
return { ok: true, method: 'tooltip-id' };
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const normalize = (value) => (value || '').toLowerCase().replace(/\\s+/g, ' ').trim();
|
|
159
|
+
const STOP_PATTERNS = [
|
|
160
|
+
/^stop$/,
|
|
161
|
+
/^stop generating$/,
|
|
162
|
+
/^stop response$/,
|
|
163
|
+
/^停止$/,
|
|
164
|
+
/^生成を停止$/,
|
|
165
|
+
/^応答を停止$/,
|
|
166
|
+
];
|
|
167
|
+
const isStopLabel = (value) => {
|
|
168
|
+
const normalized = normalize(value);
|
|
169
|
+
if (!normalized) return false;
|
|
170
|
+
return STOP_PATTERNS.some((re) => re.test(normalized));
|
|
171
|
+
};
|
|
172
|
+
for (const scope of scopes) {
|
|
173
|
+
const buttons = scope.querySelectorAll('button, [role="button"]');
|
|
174
|
+
for (let i = 0; i < buttons.length; i++) {
|
|
175
|
+
const btn = buttons[i];
|
|
176
|
+
const labels = [
|
|
177
|
+
btn.textContent || '',
|
|
178
|
+
btn.getAttribute('aria-label') || '',
|
|
179
|
+
btn.getAttribute('title') || '',
|
|
180
|
+
];
|
|
181
|
+
if (labels.some(isStopLabel) && typeof btn.click === 'function') {
|
|
182
|
+
btn.click();
|
|
183
|
+
return { ok: true, method: 'text-fallback' };
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return { ok: false, error: 'Stop button not found' };
|
|
189
|
+
})()`,
|
|
190
|
+
/** Diagnostic: dump ALL candidate text nodes with filter classification */
|
|
191
|
+
DUMP_ALL_TEXTS: `(() => {
|
|
192
|
+
const panel = document.querySelector('.antigravity-agent-side-panel');
|
|
193
|
+
const scopes = [panel, document].filter(Boolean);
|
|
194
|
+
|
|
195
|
+
const selectors = [
|
|
196
|
+
{ sel: '.rendered-markdown', score: 10 },
|
|
197
|
+
{ sel: '.leading-relaxed.select-text', score: 9 },
|
|
198
|
+
{ sel: '.flex.flex-col.gap-y-3', score: 8 },
|
|
199
|
+
{ sel: '[data-message-author-role="assistant"]', score: 7 },
|
|
200
|
+
{ sel: '[data-message-role="assistant"]', score: 6 },
|
|
201
|
+
{ sel: '[class*="assistant-message"]', score: 5 },
|
|
202
|
+
{ sel: '[class*="message-content"]', score: 4 },
|
|
203
|
+
{ sel: '[class*="markdown-body"]', score: 3 },
|
|
204
|
+
{ sel: '.prose', score: 2 },
|
|
205
|
+
];
|
|
206
|
+
|
|
207
|
+
const looksLikeActivityLog = (text) => {
|
|
208
|
+
const normalized = (text || '').trim().toLowerCase();
|
|
209
|
+
if (!normalized) return false;
|
|
210
|
+
const activityPattern = /^(?:analy[sz]ing|reading|writing|running|searching|planning|thinking|processing|loading|executing|testing|debugging|fetching|connecting|creating|updating|deleting|installing|building|compiling|deploying|checking|scanning|parsing|resolving|downloading|uploading|analyzed|read|wrote|ran|created|updated|deleted|fetched|built|compiled|installed|resolved|downloaded|connected)\\b/i;
|
|
211
|
+
if (activityPattern.test(normalized) && normalized.length <= 220) return true;
|
|
212
|
+
if (/^initiating\\s/i.test(normalized) && normalized.length <= 500) return true;
|
|
213
|
+
if (/^thought for\\s/i.test(normalized) && normalized.length <= 500) return true;
|
|
214
|
+
return false;
|
|
215
|
+
};
|
|
216
|
+
const looksLikeFeedbackFooter = (text) => {
|
|
217
|
+
const normalized = (text || '').trim().toLowerCase().replace(/\\s+/g, ' ');
|
|
218
|
+
if (!normalized) return false;
|
|
219
|
+
return normalized === 'good bad' || normalized === 'good' || normalized === 'bad';
|
|
220
|
+
};
|
|
221
|
+
const isInsideExcludedContainer = (node) => {
|
|
222
|
+
if (node.closest('details')) return true;
|
|
223
|
+
if (node.closest('[class*="feedback"], footer')) return true;
|
|
224
|
+
if (node.closest('.notify-user-container')) return true;
|
|
225
|
+
if (node.closest('[role="dialog"]')) return true;
|
|
226
|
+
return false;
|
|
227
|
+
};
|
|
228
|
+
const looksLikeToolOutput = (text) => {
|
|
229
|
+
const first = (text || '').trim().split('\\n')[0] || '';
|
|
230
|
+
if (/^[a-z0-9._-]+\\s*\\/\\s*[a-z0-9._-]+$/i.test(first)) return true;
|
|
231
|
+
if (/^full output written to\\b/i.test(first)) return true;
|
|
232
|
+
if (/^output\\.[a-z0-9._-]+(?:#l\\d+(?:-\\d+)?)?$/i.test(first)) return true;
|
|
233
|
+
var lower = (text || '').trim().toLowerCase();
|
|
234
|
+
if (/^title:\\s/.test(lower) && /\\surl:\\s/.test(lower) && /\\ssnippet:\\s/.test(lower)) return true;
|
|
235
|
+
if (/^(json|javascript|typescript|python|bash|sh|html|css|xml|yaml|yml|toml|sql|graphql|markdown|text|plaintext|log|ruby|go|rust|java|c|cpp|csharp|php|swift|kotlin)$/i.test(first)) return true;
|
|
236
|
+
return false;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
const results = [];
|
|
240
|
+
const seen = new Set();
|
|
241
|
+
|
|
242
|
+
for (const scope of scopes) {
|
|
243
|
+
for (const { sel, score } of selectors) {
|
|
244
|
+
const nodes = scope.querySelectorAll(sel);
|
|
245
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
246
|
+
const node = nodes[i];
|
|
247
|
+
if (!node || seen.has(node)) continue;
|
|
248
|
+
seen.add(node);
|
|
249
|
+
const text = (node.innerText || node.textContent || '').replace(/\\r/g, '').trim();
|
|
250
|
+
let skip = null;
|
|
251
|
+
if (!text || text.length < 2) skip = 'too-short';
|
|
252
|
+
else if (isInsideExcludedContainer(node)) skip = 'excluded-container';
|
|
253
|
+
else if (looksLikeActivityLog(text)) skip = 'activity-log';
|
|
254
|
+
else if (looksLikeFeedbackFooter(text)) skip = 'feedback-footer';
|
|
255
|
+
else if (looksLikeToolOutput(text)) skip = 'tool-output';
|
|
256
|
+
else {
|
|
257
|
+
var qlower = (text || '').trim().toLowerCase();
|
|
258
|
+
if (qlower.includes('exhausted your quota') || qlower.includes('exhausted quota')) skip = 'quota-popup';
|
|
259
|
+
else if ((qlower.includes('model quota reached') || qlower.includes('quota exceeded') || qlower.includes('rate limit'))
|
|
260
|
+
&& (qlower.includes('dismiss') || qlower.includes('upgrade'))) skip = 'quota-popup';
|
|
261
|
+
}
|
|
262
|
+
const classes = (node.className || '').toString().slice(0, 80);
|
|
263
|
+
results.push({
|
|
264
|
+
sel,
|
|
265
|
+
score,
|
|
266
|
+
skip,
|
|
267
|
+
len: text.length,
|
|
268
|
+
classes,
|
|
269
|
+
preview: text.slice(0, 120),
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
return results;
|
|
275
|
+
})()`,
|
|
276
|
+
/** Extract process log entries (activity messages + tool output) from DOM */
|
|
277
|
+
PROCESS_LOGS: `(() => {
|
|
278
|
+
const panel = document.querySelector('.antigravity-agent-side-panel');
|
|
279
|
+
const scopes = [panel, document].filter(Boolean);
|
|
280
|
+
|
|
281
|
+
const selectors = [
|
|
282
|
+
{ sel: '.rendered-markdown', score: 10 },
|
|
283
|
+
{ sel: '.leading-relaxed.select-text', score: 9 },
|
|
284
|
+
{ sel: '.flex.flex-col.gap-y-3', score: 8 },
|
|
285
|
+
{ sel: '[data-message-author-role="assistant"]', score: 7 },
|
|
286
|
+
{ sel: '[data-message-role="assistant"]', score: 6 },
|
|
287
|
+
{ sel: '[class*="assistant-message"]', score: 5 },
|
|
288
|
+
{ sel: '[class*="message-content"]', score: 4 },
|
|
289
|
+
{ sel: '[class*="markdown-body"]', score: 3 },
|
|
290
|
+
{ sel: '.prose', score: 2 },
|
|
291
|
+
];
|
|
292
|
+
|
|
293
|
+
const looksLikeActivityLog = (text) => {
|
|
294
|
+
const normalized = (text || '').trim().toLowerCase();
|
|
295
|
+
if (!normalized) return false;
|
|
296
|
+
const activityPattern = /^(?:analy[sz]ing|reading|writing|running|searching|planning|thinking|processing|loading|executing|testing|debugging|fetching|connecting|creating|updating|deleting|installing|building|compiling|deploying|checking|scanning|parsing|resolving|downloading|uploading|analyzed|read|wrote|ran|created|updated|deleted|fetched|built|compiled|installed|resolved|downloaded|connected)\\b/i;
|
|
297
|
+
if (activityPattern.test(normalized) && normalized.length <= 220) return true;
|
|
298
|
+
if (/^initiating\\s/i.test(normalized) && normalized.length <= 500) return true;
|
|
299
|
+
if (/^thought for\\s/i.test(normalized) && normalized.length <= 500) return true;
|
|
300
|
+
return false;
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
const looksLikeToolOutput = (text) => {
|
|
304
|
+
const first = (text || '').trim().split('\\n')[0] || '';
|
|
305
|
+
if (/^[a-z0-9._-]+\\s*\\/\\s*[a-z0-9._-]+$/i.test(first)) return true;
|
|
306
|
+
if (/^full output written to\\b/i.test(first)) return true;
|
|
307
|
+
if (/^output\\.[a-z0-9._-]+(?:#l\\d+(?:-\\d+)?)?$/i.test(first)) return true;
|
|
308
|
+
var lower = (text || '').trim().toLowerCase();
|
|
309
|
+
if (/^title:\\s/.test(lower) && /\\surl:\\s/.test(lower) && /\\ssnippet:\\s/.test(lower)) return true;
|
|
310
|
+
if (/^(json|javascript|typescript|python|bash|sh|html|css|xml|yaml|yml|toml|sql|graphql|markdown|text|plaintext|log|ruby|go|rust|java|c|cpp|csharp|php|swift|kotlin)$/i.test(first)) return true;
|
|
311
|
+
return false;
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
const isInsideExcludedContainer = (node) => {
|
|
315
|
+
if (node.closest('details')) return true;
|
|
316
|
+
if (node.closest('[class*="feedback"], footer')) return true;
|
|
317
|
+
if (node.closest('.notify-user-container')) return true;
|
|
318
|
+
if (node.closest('[role="dialog"]')) return true;
|
|
319
|
+
return false;
|
|
320
|
+
};
|
|
321
|
+
|
|
322
|
+
const results = [];
|
|
323
|
+
const seen = new Set();
|
|
324
|
+
|
|
325
|
+
for (const scope of scopes) {
|
|
326
|
+
for (const { sel } of selectors) {
|
|
327
|
+
const nodes = scope.querySelectorAll(sel);
|
|
328
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
329
|
+
const node = nodes[i];
|
|
330
|
+
if (!node || seen.has(node)) continue;
|
|
331
|
+
seen.add(node);
|
|
332
|
+
if (isInsideExcludedContainer(node)) continue;
|
|
333
|
+
const text = (node.innerText || node.textContent || '').replace(/\\r/g, '').trim();
|
|
334
|
+
if (!text || text.length < 4) continue;
|
|
335
|
+
if (looksLikeActivityLog(text) || looksLikeToolOutput(text)) {
|
|
336
|
+
results.push(text.slice(0, 300));
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
return results;
|
|
343
|
+
})()`,
|
|
344
|
+
/** Combined poll script — stop button + quota error + legacy text in one CDP call */
|
|
345
|
+
COMBINED_POLL: `(() => {
|
|
346
|
+
const panel = document.querySelector('.antigravity-agent-side-panel');
|
|
347
|
+
const scopes = [panel, document].filter(Boolean);
|
|
348
|
+
|
|
349
|
+
// --- Stop button ---
|
|
350
|
+
let isGenerating = false;
|
|
351
|
+
for (const scope of scopes) {
|
|
352
|
+
const el = scope.querySelector('[data-tooltip-id="input-send-button-cancel-tooltip"]');
|
|
353
|
+
if (el) { isGenerating = true; break; }
|
|
354
|
+
}
|
|
355
|
+
if (!isGenerating) {
|
|
356
|
+
const normalize = (value) => (value || '').toLowerCase().replace(/\\s+/g, ' ').trim();
|
|
357
|
+
const STOP_PATTERNS = [/^stop$/, /^stop generating$/, /^stop response$/, /^停止$/, /^生成を停止$/, /^応答を停止$/];
|
|
358
|
+
const isStopLabel = (value) => { const n = normalize(value); return n ? STOP_PATTERNS.some((re) => re.test(n)) : false; };
|
|
359
|
+
outer: for (const scope of scopes) {
|
|
360
|
+
const buttons = scope.querySelectorAll('button, [role="button"]');
|
|
361
|
+
for (let i = 0; i < buttons.length; i++) {
|
|
362
|
+
const btn = buttons[i];
|
|
363
|
+
if ([btn.textContent || '', btn.getAttribute('aria-label') || '', btn.getAttribute('title') || ''].some(isStopLabel)) {
|
|
364
|
+
isGenerating = true; break outer;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
// --- Quota error ---
|
|
371
|
+
let quotaError = false;
|
|
372
|
+
const scope = panel || document;
|
|
373
|
+
const QUOTA_KEYWORDS = ['model quota reached', 'rate limit', 'quota exceeded', 'exhausted your quota', 'exhausted quota'];
|
|
374
|
+
const isInsideResponse = (node) =>
|
|
375
|
+
node.closest('.rendered-markdown, .prose, pre, code, [data-message-author-role="assistant"], [data-message-role="assistant"], [class*="message-content"]');
|
|
376
|
+
const headings = scope.querySelectorAll('h3 span, h3');
|
|
377
|
+
for (const el of headings) {
|
|
378
|
+
if (isInsideResponse(el)) continue;
|
|
379
|
+
const text = (el.textContent || '').trim().toLowerCase();
|
|
380
|
+
if (QUOTA_KEYWORDS.some(kw => text.includes(kw))) { quotaError = true; break; }
|
|
381
|
+
}
|
|
382
|
+
if (!quotaError) {
|
|
383
|
+
const inlineSpans = scope.querySelectorAll('span');
|
|
384
|
+
for (const el of inlineSpans) {
|
|
385
|
+
if (isInsideResponse(el)) continue;
|
|
386
|
+
const text = (el.textContent || '').trim().toLowerCase();
|
|
387
|
+
if (text.includes('exhausted your quota') || text.includes('exhausted quota')) { quotaError = true; break; }
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (!quotaError) {
|
|
391
|
+
const errorSelectors = ['[role="alert"]','[class*="error"]','[class*="warning"]','[class*="toast"]','[class*="banner"]','[class*="notification"]','[class*="alert"]','[class*="quota"]','[class*="rate-limit"]'];
|
|
392
|
+
const errorElements = scope.querySelectorAll(errorSelectors.join(', '));
|
|
393
|
+
for (const el of errorElements) {
|
|
394
|
+
if (isInsideResponse(el)) continue;
|
|
395
|
+
const text = (el.textContent || '').trim().toLowerCase();
|
|
396
|
+
if (QUOTA_KEYWORDS.some(kw => text.includes(kw))) { quotaError = true; break; }
|
|
397
|
+
}
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
// --- Planning active ---
|
|
401
|
+
let planningActive = false;
|
|
402
|
+
const container = document.querySelector('.notify-user-container');
|
|
403
|
+
if (container) {
|
|
404
|
+
const buttons = Array.from(container.querySelectorAll('button')).filter(function(btn) { return btn.offsetParent !== null; });
|
|
405
|
+
const hasOpen = buttons.some(function(btn) { return (btn.textContent || '').toLowerCase().trim() === 'open'; });
|
|
406
|
+
const hasProceed = buttons.some(function(btn) { return (btn.textContent || '').toLowerCase().trim() === 'proceed'; });
|
|
407
|
+
planningActive = hasOpen && hasProceed;
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
// --- Legacy text extraction ---
|
|
411
|
+
const selectors = [
|
|
412
|
+
{ sel: '.rendered-markdown', score: 10 },
|
|
413
|
+
{ sel: '.leading-relaxed.select-text', score: 9 },
|
|
414
|
+
{ sel: '.flex.flex-col.gap-y-3', score: 8 },
|
|
415
|
+
{ sel: '[data-message-author-role="assistant"]', score: 7 },
|
|
416
|
+
{ sel: '[data-message-role="assistant"]', score: 6 },
|
|
417
|
+
{ sel: '[class*="assistant-message"]', score: 5 },
|
|
418
|
+
{ sel: '[class*="message-content"]', score: 4 },
|
|
419
|
+
{ sel: '[class*="markdown-body"]', score: 3 },
|
|
420
|
+
{ sel: '.prose', score: 2 },
|
|
421
|
+
];
|
|
422
|
+
const looksLikeActivityLog = (text) => {
|
|
423
|
+
const normalized = (text || '').trim().toLowerCase();
|
|
424
|
+
if (!normalized) return false;
|
|
425
|
+
const activityPattern = /^(?:analy[sz]ing|reading|writing|running|searching|planning|thinking|processing|loading|executing|testing|debugging|fetching|connecting|creating|updating|deleting|installing|building|compiling|deploying|checking|scanning|parsing|resolving|downloading|uploading|analyzed|read|wrote|ran|created|updated|deleted|fetched|built|compiled|installed|resolved|downloaded|connected)\\b/i;
|
|
426
|
+
if (activityPattern.test(normalized) && normalized.length <= 220) return true;
|
|
427
|
+
if (/^initiating\\s/i.test(normalized) && normalized.length <= 500) return true;
|
|
428
|
+
if (/^thought for\\s/i.test(normalized) && normalized.length <= 500) return true;
|
|
429
|
+
return false;
|
|
430
|
+
};
|
|
431
|
+
const looksLikeFeedbackFooter = (text) => {
|
|
432
|
+
const normalized = (text || '').trim().toLowerCase().replace(/\\s+/g, ' ');
|
|
433
|
+
if (!normalized) return false;
|
|
434
|
+
return normalized === 'good bad' || normalized === 'good' || normalized === 'bad';
|
|
435
|
+
};
|
|
436
|
+
const isInsideExcludedContainer = (node) => {
|
|
437
|
+
if (node.closest('details')) return true;
|
|
438
|
+
if (node.closest('[class*="feedback"], footer')) return true;
|
|
439
|
+
if (node.closest('.notify-user-container')) return true;
|
|
440
|
+
if (node.closest('[role="dialog"]')) return true;
|
|
441
|
+
return false;
|
|
442
|
+
};
|
|
443
|
+
const looksLikeToolOutput = (text) => {
|
|
444
|
+
const first = (text || '').trim().split('\\n')[0] || '';
|
|
445
|
+
if (/^[a-z0-9._-]+\\s*\\/\\s*[a-z0-9._-]+$/i.test(first)) return true;
|
|
446
|
+
if (/^full output written to\\b/i.test(first)) return true;
|
|
447
|
+
if (/^output\\.[a-z0-9._-]+(?:#l\\d+(?:-\\d+)?)?$/i.test(first)) return true;
|
|
448
|
+
var lower = (text || '').trim().toLowerCase();
|
|
449
|
+
if (/^title:\\s/.test(lower) && /\\surl:\\s/.test(lower) && /\\ssnippet:\\s/.test(lower)) return true;
|
|
450
|
+
if (/^(json|javascript|typescript|python|bash|sh|html|css|xml|yaml|yml|toml|sql|graphql|markdown|text|plaintext|log|ruby|go|rust|java|c|cpp|csharp|php|swift|kotlin)$/i.test(first)) return true;
|
|
451
|
+
return false;
|
|
452
|
+
};
|
|
453
|
+
const looksLikeQuotaPopup = (text) => {
|
|
454
|
+
var lower = (text || '').trim().toLowerCase();
|
|
455
|
+
if (lower.includes('exhausted your quota') || lower.includes('exhausted quota')) return true;
|
|
456
|
+
if (!lower.includes('model quota reached') && !lower.includes('quota exceeded') && !lower.includes('rate limit')) return false;
|
|
457
|
+
return lower.includes('dismiss') || lower.includes('upgrade');
|
|
458
|
+
};
|
|
459
|
+
const combinedSelector = selectors.map((s) => s.sel).join(', ');
|
|
460
|
+
const seen = new Set();
|
|
461
|
+
let responseText = null;
|
|
462
|
+
for (const s of scopes) {
|
|
463
|
+
const nodes = s.querySelectorAll(combinedSelector);
|
|
464
|
+
for (let i = nodes.length - 1; i >= 0; i--) {
|
|
465
|
+
const node = nodes[i];
|
|
466
|
+
if (!node || seen.has(node)) continue;
|
|
467
|
+
seen.add(node);
|
|
468
|
+
if (isInsideExcludedContainer(node)) continue;
|
|
469
|
+
const text = (node.innerText || node.textContent || '').replace(/\\r/g, '').trim();
|
|
470
|
+
if (!text || text.length < 2) continue;
|
|
471
|
+
if (looksLikeActivityLog(text)) continue;
|
|
472
|
+
if (looksLikeFeedbackFooter(text)) continue;
|
|
473
|
+
if (looksLikeToolOutput(text)) continue;
|
|
474
|
+
if (looksLikeQuotaPopup(text)) continue;
|
|
475
|
+
responseText = text;
|
|
476
|
+
break;
|
|
477
|
+
}
|
|
478
|
+
if (responseText !== null) break;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// --- Process logs ---
|
|
482
|
+
const logSeen = new Set();
|
|
483
|
+
const processLogs = [];
|
|
484
|
+
for (const s of scopes) {
|
|
485
|
+
for (const { sel } of selectors) {
|
|
486
|
+
const nodes = s.querySelectorAll(sel);
|
|
487
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
488
|
+
const node = nodes[i];
|
|
489
|
+
if (!node || logSeen.has(node)) continue;
|
|
490
|
+
logSeen.add(node);
|
|
491
|
+
if (isInsideExcludedContainer(node)) continue;
|
|
492
|
+
const text = (node.innerText || node.textContent || '').replace(/\\r/g, '').trim();
|
|
493
|
+
if (!text || text.length < 4) continue;
|
|
494
|
+
if (looksLikeActivityLog(text) || looksLikeToolOutput(text)) {
|
|
495
|
+
processLogs.push(text.slice(0, 300));
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return { isGenerating, quotaError, planningActive, responseText, processLogs };
|
|
502
|
+
})()`,
|
|
503
|
+
/** Quota error detection — text-based h3 span match first, class-based fallback second */
|
|
504
|
+
QUOTA_ERROR: `(() => {
|
|
505
|
+
const panel = document.querySelector('.antigravity-agent-side-panel');
|
|
506
|
+
const scope = panel || document;
|
|
507
|
+
const QUOTA_KEYWORDS = ['model quota reached', 'rate limit', 'quota exceeded', 'exhausted your quota', 'exhausted quota'];
|
|
508
|
+
const isInsideResponse = (node) =>
|
|
509
|
+
node.closest('.rendered-markdown, .prose, pre, code, [data-message-author-role="assistant"], [data-message-role="assistant"], [class*="message-content"]');
|
|
510
|
+
|
|
511
|
+
// Primary: text-based detection via h3 span (Tailwind-only popup)
|
|
512
|
+
const headings = scope.querySelectorAll('h3 span, h3');
|
|
513
|
+
for (const el of headings) {
|
|
514
|
+
if (isInsideResponse(el)) continue;
|
|
515
|
+
const text = (el.textContent || '').trim().toLowerCase();
|
|
516
|
+
if (QUOTA_KEYWORDS.some(kw => text.includes(kw))) return true;
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
// Inline error: "Error You have exhausted your quota on this model."
|
|
520
|
+
// Appears in process log area as a span inside flex containers
|
|
521
|
+
const inlineSpans = scope.querySelectorAll('span');
|
|
522
|
+
for (const el of inlineSpans) {
|
|
523
|
+
if (isInsideResponse(el)) continue;
|
|
524
|
+
const text = (el.textContent || '').trim().toLowerCase();
|
|
525
|
+
if (text.includes('exhausted your quota') || text.includes('exhausted quota')) return true;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
// Fallback: semantic class-based detection
|
|
529
|
+
const errorSelectors = [
|
|
530
|
+
'[role="alert"]',
|
|
531
|
+
'[class*="error"]',
|
|
532
|
+
'[class*="warning"]',
|
|
533
|
+
'[class*="toast"]',
|
|
534
|
+
'[class*="banner"]',
|
|
535
|
+
'[class*="notification"]',
|
|
536
|
+
'[class*="alert"]',
|
|
537
|
+
'[class*="quota"]',
|
|
538
|
+
'[class*="rate-limit"]',
|
|
539
|
+
];
|
|
540
|
+
const errorElements = scope.querySelectorAll(errorSelectors.join(', '));
|
|
541
|
+
for (const el of errorElements) {
|
|
542
|
+
if (isInsideResponse(el)) continue;
|
|
543
|
+
const text = (el.textContent || '').trim().toLowerCase();
|
|
544
|
+
if (QUOTA_KEYWORDS.some(kw => text.includes(kw))) return true;
|
|
545
|
+
}
|
|
546
|
+
return false;
|
|
547
|
+
})()`,
|
|
548
|
+
/** Structured DOM extraction — walks DOM to produce typed segment array */
|
|
549
|
+
RESPONSE_STRUCTURED: (0, assistantDomExtractor_1.extractAssistantSegmentsPayloadScript)(),
|
|
550
|
+
/** One-shot DOM diagnostic — dumps DOM structure around activity areas */
|
|
551
|
+
DOM_DIAGNOSTIC: `(() => {
|
|
552
|
+
var panel = document.querySelector('.antigravity-agent-side-panel');
|
|
553
|
+
var scope = panel || document;
|
|
554
|
+
var diag = { detailsCount: 0, detailsDump: [], activityNodes: [], allTextNodes: [] };
|
|
555
|
+
|
|
556
|
+
// 1. Dump all <details> elements
|
|
557
|
+
var details = scope.querySelectorAll('details');
|
|
558
|
+
diag.detailsCount = details.length;
|
|
559
|
+
for (var i = 0; i < Math.min(details.length, 5); i++) {
|
|
560
|
+
diag.detailsDump.push({
|
|
561
|
+
outerHTML: details[i].outerHTML.slice(0, 500),
|
|
562
|
+
summaryText: (details[i].querySelector('summary') || {}).textContent || '(no summary)',
|
|
563
|
+
childCount: details[i].children.length
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// 2. Find all text nodes that look like activity
|
|
568
|
+
var selectors = '.rendered-markdown, .leading-relaxed.select-text, .flex.flex-col.gap-y-3, [data-message-author-role="assistant"], [data-message-role="assistant"], [class*="assistant-message"], [class*="message-content"], [class*="markdown-body"], .prose';
|
|
569
|
+
var nodes = scope.querySelectorAll(selectors);
|
|
570
|
+
for (var j = 0; j < nodes.length; j++) {
|
|
571
|
+
var text = (nodes[j].innerText || nodes[j].textContent || '').trim();
|
|
572
|
+
if (!text || text.length < 2) continue;
|
|
573
|
+
diag.allTextNodes.push({
|
|
574
|
+
tag: nodes[j].tagName,
|
|
575
|
+
className: (nodes[j].className || '').toString().slice(0, 100),
|
|
576
|
+
text: text.slice(0, 200),
|
|
577
|
+
insideDetails: !!nodes[j].closest('details'),
|
|
578
|
+
length: text.length
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// 3. Broader scan: any element with activity-like text
|
|
583
|
+
var allEls = scope.querySelectorAll('*');
|
|
584
|
+
for (var k = 0; k < allEls.length; k++) {
|
|
585
|
+
var el = allEls[k];
|
|
586
|
+
if (el.children.length > 2) continue; // only leaf-ish nodes
|
|
587
|
+
var t = (el.textContent || '').trim();
|
|
588
|
+
if (!t || t.length < 5 || t.length > 300) continue;
|
|
589
|
+
var lower = t.toLowerCase();
|
|
590
|
+
if (/^(?:analy[sz]|read|writ|run|search|think|process|execut|debug|test)/i.test(lower) || /\\//.test(t)) {
|
|
591
|
+
diag.activityNodes.push({
|
|
592
|
+
tag: el.tagName,
|
|
593
|
+
className: (el.className || '').toString().slice(0, 100),
|
|
594
|
+
text: t.slice(0, 200),
|
|
595
|
+
parentTag: el.parentElement ? el.parentElement.tagName : null,
|
|
596
|
+
parentClass: el.parentElement ? (el.parentElement.className || '').toString().slice(0, 100) : null,
|
|
597
|
+
insideDetails: !!el.closest('details')
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
return diag;
|
|
602
|
+
})()`,
|
|
603
|
+
};
|
|
604
|
+
/**
|
|
605
|
+
* Lean AI response monitor.
|
|
606
|
+
*
|
|
607
|
+
* Each poll makes exactly 3 CDP calls: stop button, quota, text extraction.
|
|
608
|
+
* Completion: stop button gone N consecutive times -> complete.
|
|
609
|
+
* Simple baseline suppression via string comparison.
|
|
610
|
+
* NO network event subscription.
|
|
611
|
+
*/
|
|
612
|
+
class ResponseMonitor {
|
|
613
|
+
cdpService;
|
|
614
|
+
pollIntervalMs;
|
|
615
|
+
maxDurationMs;
|
|
616
|
+
stopGoneConfirmCount;
|
|
617
|
+
extractionMode;
|
|
618
|
+
onProgress;
|
|
619
|
+
onComplete;
|
|
620
|
+
onTimeout;
|
|
621
|
+
onPhaseChange;
|
|
622
|
+
onProcessLog;
|
|
623
|
+
pollTimer = null;
|
|
624
|
+
timeoutTimer = null;
|
|
625
|
+
isRunning = false;
|
|
626
|
+
lastText = null;
|
|
627
|
+
baselineText = null;
|
|
628
|
+
generationStarted = false;
|
|
629
|
+
currentPhase = 'waiting';
|
|
630
|
+
stopGoneCount = 0;
|
|
631
|
+
quotaDetected = false;
|
|
632
|
+
seenProcessLogKeys = new Set();
|
|
633
|
+
structuredDiagLogged = false;
|
|
634
|
+
lastExtractionSource = null;
|
|
635
|
+
constructor(options) {
|
|
636
|
+
this.cdpService = options.cdpService;
|
|
637
|
+
this.pollIntervalMs = options.pollIntervalMs ?? 2000;
|
|
638
|
+
this.maxDurationMs = options.maxDurationMs ?? 300000;
|
|
639
|
+
this.stopGoneConfirmCount = options.stopGoneConfirmCount ?? 3;
|
|
640
|
+
this.extractionMode = options.extractionMode
|
|
641
|
+
?? (process.env.EXTRACTION_MODE === 'legacy' ? 'legacy' : 'structured');
|
|
642
|
+
this.onProgress = options.onProgress;
|
|
643
|
+
this.onComplete = options.onComplete;
|
|
644
|
+
this.onTimeout = options.onTimeout;
|
|
645
|
+
this.onPhaseChange = options.onPhaseChange;
|
|
646
|
+
this.onProcessLog = options.onProcessLog;
|
|
647
|
+
}
|
|
648
|
+
/** Start monitoring */
|
|
649
|
+
async start() {
|
|
650
|
+
return this.initMonitoring(false);
|
|
651
|
+
}
|
|
652
|
+
/**
|
|
653
|
+
* Start monitoring in passive mode.
|
|
654
|
+
* Same as start() but with generationStarted=true, so text changes
|
|
655
|
+
* are detected immediately without waiting for the stop button to appear.
|
|
656
|
+
* Used when joining an existing session that may already be generating.
|
|
657
|
+
*/
|
|
658
|
+
async startPassive() {
|
|
659
|
+
return this.initMonitoring(true);
|
|
660
|
+
}
|
|
661
|
+
/** Internal initialization shared between start() and startPassive() */
|
|
662
|
+
async initMonitoring(passive) {
|
|
663
|
+
if (this.isRunning)
|
|
664
|
+
return;
|
|
665
|
+
this.isRunning = true;
|
|
666
|
+
this.lastText = null;
|
|
667
|
+
this.baselineText = null;
|
|
668
|
+
this.generationStarted = passive;
|
|
669
|
+
this.currentPhase = passive ? 'generating' : 'waiting';
|
|
670
|
+
this.stopGoneCount = 0;
|
|
671
|
+
this.quotaDetected = false;
|
|
672
|
+
this.seenProcessLogKeys = new Set();
|
|
673
|
+
this.onPhaseChange?.(this.currentPhase, null);
|
|
674
|
+
// Capture baselines in parallel (text + process logs + optional structured)
|
|
675
|
+
const baselinePromises = [
|
|
676
|
+
this.cdpService.call('Runtime.evaluate', this.buildEvaluateParams(exports.RESPONSE_SELECTORS.RESPONSE_TEXT)).catch(() => null),
|
|
677
|
+
this.cdpService.call('Runtime.evaluate', this.buildEvaluateParams(exports.RESPONSE_SELECTORS.PROCESS_LOGS)).catch(() => null),
|
|
678
|
+
];
|
|
679
|
+
if (this.extractionMode === 'structured') {
|
|
680
|
+
baselinePromises.push(this.cdpService.call('Runtime.evaluate', this.buildEvaluateParams(exports.RESPONSE_SELECTORS.RESPONSE_STRUCTURED)).catch(() => null));
|
|
681
|
+
}
|
|
682
|
+
const [baseResult, logResult, structuredBaseline] = await Promise.all(baselinePromises);
|
|
683
|
+
// Baseline text
|
|
684
|
+
const rawValue = baseResult?.result?.value;
|
|
685
|
+
this.baselineText = typeof rawValue === 'string' ? rawValue.trim() || null : null;
|
|
686
|
+
// Baseline process log keys
|
|
687
|
+
const logEntries = logResult?.result?.value;
|
|
688
|
+
if (Array.isArray(logEntries)) {
|
|
689
|
+
this.seenProcessLogKeys = new Set(logEntries
|
|
690
|
+
.map((s) => (s || '').replace(/\r/g, '').trim())
|
|
691
|
+
.filter((s) => s.length > 0)
|
|
692
|
+
.map((s) => s.slice(0, 200)));
|
|
693
|
+
}
|
|
694
|
+
// Structured baseline activity lines
|
|
695
|
+
if (structuredBaseline) {
|
|
696
|
+
try {
|
|
697
|
+
const baselineClassified = (0, assistantDomExtractor_1.classifyAssistantSegments)(structuredBaseline?.result?.value);
|
|
698
|
+
if (baselineClassified.diagnostics.source === 'dom-structured') {
|
|
699
|
+
for (const line of baselineClassified.activityLines) {
|
|
700
|
+
const key = (line || '').replace(/\r/g, '').trim().slice(0, 200);
|
|
701
|
+
if (key)
|
|
702
|
+
this.seenProcessLogKeys.add(key);
|
|
703
|
+
}
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
catch {
|
|
707
|
+
// structured baseline is best-effort
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
// Set timeout timer
|
|
711
|
+
if (this.maxDurationMs > 0) {
|
|
712
|
+
this.timeoutTimer = setTimeout(async () => {
|
|
713
|
+
// Guard: skip if already completed or quota-reached
|
|
714
|
+
if (this.currentPhase === 'complete' || this.currentPhase === 'quotaReached')
|
|
715
|
+
return;
|
|
716
|
+
const lastText = this.lastText ?? '';
|
|
717
|
+
this.setPhase('timeout', lastText);
|
|
718
|
+
await this.stop();
|
|
719
|
+
try {
|
|
720
|
+
await Promise.resolve(this.onTimeout?.(lastText));
|
|
721
|
+
}
|
|
722
|
+
catch (error) {
|
|
723
|
+
logger_1.logger.error('[ResponseMonitor] timeout callback failed:', error);
|
|
724
|
+
}
|
|
725
|
+
}, this.maxDurationMs);
|
|
726
|
+
}
|
|
727
|
+
const mode = passive ? 'Passive monitoring' : 'Monitoring';
|
|
728
|
+
logger_1.logger.debug(`── ${mode} started | poll=${this.pollIntervalMs}ms timeout=${this.maxDurationMs / 1000}s baseline=${this.baselineText?.length ?? 0}ch`);
|
|
729
|
+
this.schedulePoll();
|
|
730
|
+
}
|
|
731
|
+
/** Stop monitoring */
|
|
732
|
+
async stop() {
|
|
733
|
+
this.isRunning = false;
|
|
734
|
+
if (this.pollTimer) {
|
|
735
|
+
clearTimeout(this.pollTimer);
|
|
736
|
+
this.pollTimer = null;
|
|
737
|
+
}
|
|
738
|
+
if (this.timeoutTimer) {
|
|
739
|
+
clearTimeout(this.timeoutTimer);
|
|
740
|
+
this.timeoutTimer = null;
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
/** Get current phase */
|
|
744
|
+
getPhase() {
|
|
745
|
+
return this.currentPhase;
|
|
746
|
+
}
|
|
747
|
+
/** Whether quota error was detected */
|
|
748
|
+
getQuotaDetected() {
|
|
749
|
+
return this.quotaDetected;
|
|
750
|
+
}
|
|
751
|
+
/** Whether monitoring is active */
|
|
752
|
+
isActive() {
|
|
753
|
+
return this.isRunning;
|
|
754
|
+
}
|
|
755
|
+
/** Get last extracted text */
|
|
756
|
+
getLastText() {
|
|
757
|
+
return this.lastText;
|
|
758
|
+
}
|
|
759
|
+
/** Get last extraction source (structured = HTML, legacy = plain text) */
|
|
760
|
+
getLastExtractionSource() {
|
|
761
|
+
return this.lastExtractionSource;
|
|
762
|
+
}
|
|
763
|
+
/** Click the stop button to interrupt LLM generation */
|
|
764
|
+
async clickStopButton() {
|
|
765
|
+
try {
|
|
766
|
+
const result = await this.cdpService.call('Runtime.evaluate', this.buildEvaluateParams(exports.RESPONSE_SELECTORS.CLICK_STOP_BUTTON));
|
|
767
|
+
const value = result?.result?.value;
|
|
768
|
+
if (this.isRunning) {
|
|
769
|
+
await this.stop();
|
|
770
|
+
}
|
|
771
|
+
return value ?? { ok: false, error: 'CDP evaluation returned empty' };
|
|
772
|
+
}
|
|
773
|
+
catch (error) {
|
|
774
|
+
return { ok: false, error: error.message || 'Failed to click stop button' };
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
setPhase(phase, text) {
|
|
778
|
+
if (this.currentPhase !== phase) {
|
|
779
|
+
this.currentPhase = phase;
|
|
780
|
+
const len = text?.length ?? 0;
|
|
781
|
+
switch (phase) {
|
|
782
|
+
case 'thinking':
|
|
783
|
+
logger_1.logger.phase('Thinking');
|
|
784
|
+
break;
|
|
785
|
+
case 'generating':
|
|
786
|
+
logger_1.logger.phase(`Generating (${len} chars)`);
|
|
787
|
+
break;
|
|
788
|
+
case 'complete':
|
|
789
|
+
logger_1.logger.done(`Complete (${len} chars)`);
|
|
790
|
+
break;
|
|
791
|
+
case 'timeout':
|
|
792
|
+
logger_1.logger.warn(`Timeout (${len} chars captured)`);
|
|
793
|
+
break;
|
|
794
|
+
case 'quotaReached':
|
|
795
|
+
logger_1.logger.warn('Quota Reached');
|
|
796
|
+
break;
|
|
797
|
+
default:
|
|
798
|
+
logger_1.logger.phase(`${phase}`);
|
|
799
|
+
}
|
|
800
|
+
this.onPhaseChange?.(phase, text);
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
schedulePoll() {
|
|
804
|
+
if (!this.isRunning)
|
|
805
|
+
return;
|
|
806
|
+
this.pollTimer = setTimeout(async () => {
|
|
807
|
+
await this.poll();
|
|
808
|
+
if (this.isRunning) {
|
|
809
|
+
this.schedulePoll();
|
|
810
|
+
}
|
|
811
|
+
}, this.pollIntervalMs);
|
|
812
|
+
}
|
|
813
|
+
buildEvaluateParams(expression) {
|
|
814
|
+
const params = {
|
|
815
|
+
expression,
|
|
816
|
+
returnByValue: true,
|
|
817
|
+
awaitPromise: true,
|
|
818
|
+
};
|
|
819
|
+
const contextId = this.cdpService.getPrimaryContextId?.();
|
|
820
|
+
if (contextId !== null && contextId !== undefined) {
|
|
821
|
+
params.contextId = contextId;
|
|
822
|
+
}
|
|
823
|
+
return params;
|
|
824
|
+
}
|
|
825
|
+
/**
|
|
826
|
+
* Emit new process log entries, deduplicating against previously seen keys.
|
|
827
|
+
*/
|
|
828
|
+
emitNewProcessLogs(entries) {
|
|
829
|
+
const newEntries = [];
|
|
830
|
+
for (const line of entries) {
|
|
831
|
+
const normalized = (line || '').replace(/\r/g, '').trim();
|
|
832
|
+
if (!normalized)
|
|
833
|
+
continue;
|
|
834
|
+
const key = normalized.slice(0, 200);
|
|
835
|
+
if (this.seenProcessLogKeys.has(key))
|
|
836
|
+
continue;
|
|
837
|
+
this.seenProcessLogKeys.add(key);
|
|
838
|
+
newEntries.push(normalized.slice(0, 300));
|
|
839
|
+
}
|
|
840
|
+
if (newEntries.length > 0) {
|
|
841
|
+
try {
|
|
842
|
+
this.onProcessLog?.(newEntries.join('\n\n'));
|
|
843
|
+
}
|
|
844
|
+
catch {
|
|
845
|
+
// callback error
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
/**
|
|
850
|
+
* Single poll cycle.
|
|
851
|
+
* - Legacy mode: 4 CDP calls (stop, quota, text, process logs).
|
|
852
|
+
* - Structured mode: 3-4 CDP calls (stop, quota, structured; legacy text on fallback).
|
|
853
|
+
*/
|
|
854
|
+
async poll() {
|
|
855
|
+
try {
|
|
856
|
+
let isGenerating;
|
|
857
|
+
let quotaDetected;
|
|
858
|
+
let planningActive;
|
|
859
|
+
let currentText = null;
|
|
860
|
+
let structuredHandledLogs = false;
|
|
861
|
+
if (this.extractionMode === 'structured') {
|
|
862
|
+
// Structured mode: run combined (stop+quota+planning) in parallel with structured extraction
|
|
863
|
+
const [combinedResult, structuredResult] = await Promise.all([
|
|
864
|
+
this.cdpService.call('Runtime.evaluate', this.buildEvaluateParams(exports.RESPONSE_SELECTORS.COMBINED_POLL)),
|
|
865
|
+
this.cdpService.call('Runtime.evaluate', this.buildEvaluateParams(exports.RESPONSE_SELECTORS.RESPONSE_STRUCTURED)).catch(() => null),
|
|
866
|
+
]);
|
|
867
|
+
const combined = combinedResult?.result?.value ?? {};
|
|
868
|
+
isGenerating = !!combined.isGenerating;
|
|
869
|
+
quotaDetected = !!combined.quotaError;
|
|
870
|
+
planningActive = !!combined.planningActive;
|
|
871
|
+
// Try structured extraction first
|
|
872
|
+
if (structuredResult) {
|
|
873
|
+
try {
|
|
874
|
+
const payload = structuredResult?.result?.value;
|
|
875
|
+
const classified = (0, assistantDomExtractor_1.classifyAssistantSegments)(payload);
|
|
876
|
+
if (classified.diagnostics.source === 'dom-structured') {
|
|
877
|
+
currentText = classified.finalOutputText.trim() || null;
|
|
878
|
+
this.lastExtractionSource = 'structured';
|
|
879
|
+
structuredHandledLogs = true;
|
|
880
|
+
if (!this.structuredDiagLogged) {
|
|
881
|
+
this.structuredDiagLogged = true;
|
|
882
|
+
logger_1.logger.debug('[ResponseMonitor] Structured extraction OK — segments:', classified.diagnostics.segmentCounts);
|
|
883
|
+
}
|
|
884
|
+
if (classified.activityLines.length > 0) {
|
|
885
|
+
this.emitNewProcessLogs(classified.activityLines);
|
|
886
|
+
}
|
|
887
|
+
}
|
|
888
|
+
else if (!this.structuredDiagLogged) {
|
|
889
|
+
this.structuredDiagLogged = true;
|
|
890
|
+
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');
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
catch (error) {
|
|
894
|
+
logger_1.logger.warn('[ResponseMonitor:poll] RESPONSE_STRUCTURED classification failed:', error);
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
// Fallback to legacy text from combined result
|
|
898
|
+
if (currentText === null) {
|
|
899
|
+
currentText = typeof combined.responseText === 'string' ? combined.responseText.trim() || null : null;
|
|
900
|
+
this.lastExtractionSource = 'legacy';
|
|
901
|
+
}
|
|
902
|
+
// Process logs from combined result
|
|
903
|
+
if (!structuredHandledLogs && Array.isArray(combined.processLogs)) {
|
|
904
|
+
this.emitNewProcessLogs(combined.processLogs);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
else {
|
|
908
|
+
// Legacy mode: single combined CDP call gets everything
|
|
909
|
+
const combinedResult = await this.cdpService.call('Runtime.evaluate', this.buildEvaluateParams(exports.RESPONSE_SELECTORS.COMBINED_POLL));
|
|
910
|
+
const combined = combinedResult?.result?.value ?? {};
|
|
911
|
+
isGenerating = !!combined.isGenerating;
|
|
912
|
+
quotaDetected = !!combined.quotaError;
|
|
913
|
+
planningActive = !!combined.planningActive;
|
|
914
|
+
currentText = typeof combined.responseText === 'string' ? combined.responseText.trim() || null : null;
|
|
915
|
+
this.lastExtractionSource = 'legacy';
|
|
916
|
+
if (Array.isArray(combined.processLogs)) {
|
|
917
|
+
this.emitNewProcessLogs(combined.processLogs);
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
// Handle stop button appearing
|
|
921
|
+
if (isGenerating) {
|
|
922
|
+
if (!this.generationStarted) {
|
|
923
|
+
this.generationStarted = true;
|
|
924
|
+
this.setPhase('thinking', null);
|
|
925
|
+
}
|
|
926
|
+
this.stopGoneCount = 0;
|
|
927
|
+
}
|
|
928
|
+
// Handle quota detection
|
|
929
|
+
if (quotaDetected) {
|
|
930
|
+
const hasText = !!(this.lastText && this.lastText.trim().length > 0);
|
|
931
|
+
logger_1.logger.warn(`[ResponseMonitor] quota detected hasText=${hasText}`);
|
|
932
|
+
if (hasText) {
|
|
933
|
+
this.quotaDetected = true;
|
|
934
|
+
}
|
|
935
|
+
else {
|
|
936
|
+
this.setPhase('quotaReached', '');
|
|
937
|
+
await this.stop();
|
|
938
|
+
try {
|
|
939
|
+
await Promise.resolve(this.onComplete?.('', { source: 'legacy' }));
|
|
940
|
+
}
|
|
941
|
+
catch (error) {
|
|
942
|
+
logger_1.logger.error('[ResponseMonitor] complete callback failed:', error);
|
|
943
|
+
}
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
// Baseline suppression: do not emit progress for pre-existing text.
|
|
948
|
+
// IMPORTANT: do not early-return here; completion logic must still run.
|
|
949
|
+
const effectiveText = (currentText !== null &&
|
|
950
|
+
this.baselineText !== null &&
|
|
951
|
+
currentText === this.baselineText &&
|
|
952
|
+
this.lastText === null) ? null : currentText;
|
|
953
|
+
// Text change handling
|
|
954
|
+
const textChanged = effectiveText !== null && effectiveText !== this.lastText;
|
|
955
|
+
if (textChanged) {
|
|
956
|
+
this.lastText = effectiveText;
|
|
957
|
+
if (this.currentPhase === 'waiting' || this.currentPhase === 'thinking') {
|
|
958
|
+
this.setPhase('generating', effectiveText);
|
|
959
|
+
if (!this.generationStarted) {
|
|
960
|
+
this.generationStarted = true;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
this.onProgress?.(effectiveText);
|
|
964
|
+
}
|
|
965
|
+
// Completion: stop button gone N consecutive times
|
|
966
|
+
if (!isGenerating && this.generationStarted) {
|
|
967
|
+
// Planning check already done in combined poll script
|
|
968
|
+
if (planningActive) {
|
|
969
|
+
this.stopGoneCount = 0;
|
|
970
|
+
logger_1.logger.info('[ResponseMonitor] Planning dialog active — deferring completion');
|
|
971
|
+
}
|
|
972
|
+
else {
|
|
973
|
+
this.stopGoneCount++;
|
|
974
|
+
if (this.stopGoneCount >= this.stopGoneConfirmCount && this.isRunning) {
|
|
975
|
+
const finalText = this.lastText ?? '';
|
|
976
|
+
this.setPhase('complete', finalText);
|
|
977
|
+
await this.stop();
|
|
978
|
+
try {
|
|
979
|
+
const source = this.lastExtractionSource ?? 'legacy';
|
|
980
|
+
await Promise.resolve(this.onComplete?.(finalText, { source }));
|
|
981
|
+
}
|
|
982
|
+
catch (error) {
|
|
983
|
+
logger_1.logger.error('[ResponseMonitor] complete callback failed:', error);
|
|
984
|
+
}
|
|
985
|
+
return;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
catch (error) {
|
|
991
|
+
logger_1.logger.error('[ResponseMonitor] poll error:', error);
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
}
|
|
995
|
+
exports.ResponseMonitor = ResponseMonitor;
|
|
996
|
+
//# sourceMappingURL=responseMonitor.js.map
|