yiyan-browser-agent 1.0.6 → 1.0.8
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/package.json +1 -1
- package/src/browser.js +46 -90
package/package.json
CHANGED
package/src/browser.js
CHANGED
|
@@ -228,51 +228,21 @@ class YiyanBrowser {
|
|
|
228
228
|
});
|
|
229
229
|
}
|
|
230
230
|
|
|
231
|
-
// ── Sending Messages (
|
|
231
|
+
// ── Sending Messages (stable: keyboard.type) ────────────────────────────────
|
|
232
232
|
|
|
233
233
|
async sendMessage(text) {
|
|
234
234
|
const { el } = await this._findInput();
|
|
235
235
|
|
|
236
|
-
// Focus
|
|
237
|
-
await el.
|
|
236
|
+
// Focus and select all existing content
|
|
237
|
+
await el.click({ clickCount: 3, force: true });
|
|
238
|
+
await this.page.waitForTimeout(200);
|
|
238
239
|
|
|
239
|
-
// Clear
|
|
240
|
-
await
|
|
241
|
-
|
|
242
|
-
if (e.innerText) e.innerText = '';
|
|
243
|
-
});
|
|
244
|
-
|
|
245
|
-
// Use clipboard paste for instant input (much faster than typing)
|
|
246
|
-
await this.page.evaluate(async (text) => {
|
|
247
|
-
// Try modern clipboard API first
|
|
248
|
-
try {
|
|
249
|
-
await navigator.clipboard.writeText(text);
|
|
250
|
-
document.execCommand('paste');
|
|
251
|
-
return;
|
|
252
|
-
} catch {}
|
|
253
|
-
|
|
254
|
-
// Fallback: direct DOM manipulation with proper event dispatch
|
|
255
|
-
const activeEl = document.activeElement;
|
|
256
|
-
if (activeEl && activeEl.isContentEditable) {
|
|
257
|
-
activeEl.textContent = text;
|
|
258
|
-
|
|
259
|
-
// Dispatch input event so Yiyan recognizes the content
|
|
260
|
-
const inputEvent = new InputEvent('input', {
|
|
261
|
-
bubbles: true,
|
|
262
|
-
cancelable: true,
|
|
263
|
-
inputType: 'insertText',
|
|
264
|
-
data: text,
|
|
265
|
-
});
|
|
266
|
-
activeEl.dispatchEvent(inputEvent);
|
|
267
|
-
|
|
268
|
-
// Also dispatch change event for good measure
|
|
269
|
-
const changeEvent = new Event('change', { bubbles: true });
|
|
270
|
-
activeEl.dispatchEvent(changeEvent);
|
|
271
|
-
}
|
|
272
|
-
}, text);
|
|
240
|
+
// Clear by pressing Delete
|
|
241
|
+
await this.page.keyboard.press('Delete');
|
|
242
|
+
await this.page.waitForTimeout(100);
|
|
273
243
|
|
|
274
|
-
//
|
|
275
|
-
await this.page.
|
|
244
|
+
// Type text character by character (stable, works reliably)
|
|
245
|
+
await this.page.keyboard.type(text, { delay: 30 });
|
|
276
246
|
|
|
277
247
|
// Press Enter to send
|
|
278
248
|
await this.page.keyboard.press('Enter');
|
|
@@ -297,73 +267,59 @@ class YiyanBrowser {
|
|
|
297
267
|
);
|
|
298
268
|
}
|
|
299
269
|
|
|
300
|
-
// ── Waiting for Response (
|
|
301
|
-
|
|
270
|
+
// ── Waiting for Response (stable polling) ────────────────────────────────────
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Wait until Yiyan finishes generating and return the response text.
|
|
274
|
+
*
|
|
275
|
+
* Algorithm:
|
|
276
|
+
* 1. Record how many assistant messages are on the page right now.
|
|
277
|
+
* 2. Wait until a new message appears (count goes up).
|
|
278
|
+
* 3. Poll the last message text every 300 ms.
|
|
279
|
+
* 4. When the text has not changed for STABLE_DELAY ms AND
|
|
280
|
+
* no stop/loading indicator is visible → done.
|
|
281
|
+
*/
|
|
302
282
|
async waitForResponse() {
|
|
303
283
|
const timeout = config.RESPONSE_TIMEOUT;
|
|
304
284
|
const stableDelay = config.STABLE_DELAY;
|
|
305
285
|
const start = Date.now();
|
|
306
286
|
|
|
307
|
-
//
|
|
308
|
-
const
|
|
309
|
-
|
|
310
|
-
let stableStart = null;
|
|
311
|
-
let done = false;
|
|
312
|
-
|
|
313
|
-
// Find response container
|
|
314
|
-
const container = document.querySelector('[class*="answer"], [class*="response"], [class*="markdown"], .ds-markdown, [class*="content"]');
|
|
315
|
-
if (!container) return null;
|
|
316
|
-
|
|
317
|
-
const observer = new MutationObserver(() => {
|
|
318
|
-
const currentText = container.textContent || '';
|
|
319
|
-
|
|
320
|
-
if (currentText !== lastText) {
|
|
321
|
-
lastText = currentText;
|
|
322
|
-
stableStart = Date.now();
|
|
323
|
-
} else if (stableStart && Date.now() - stableStart >= 2500) {
|
|
324
|
-
// Text stable for 2.5s = done
|
|
325
|
-
done = true;
|
|
326
|
-
}
|
|
327
|
-
});
|
|
287
|
+
// Phase 1: wait for a new message to appear
|
|
288
|
+
const initialCount = await this._getMessageCount();
|
|
289
|
+
let appeared = false;
|
|
328
290
|
|
|
329
|
-
|
|
291
|
+
for (let i = 0; i < 30; i++) {
|
|
292
|
+
const count = await this._getMessageCount();
|
|
293
|
+
if (count > initialCount) { appeared = true; break; }
|
|
294
|
+
await this.page.waitForTimeout(300);
|
|
295
|
+
}
|
|
330
296
|
|
|
331
|
-
|
|
332
|
-
window.__yiyanObserver = observer;
|
|
333
|
-
window.__yiyanDone = () => done;
|
|
334
|
-
window.__yiyanText = () => lastText;
|
|
297
|
+
if (!appeared) logger.warn('Response may have been delayed — continuing to wait...');
|
|
335
298
|
|
|
336
|
-
|
|
337
|
-
|
|
299
|
+
// Phase 2: wait for text to stabilise
|
|
300
|
+
let lastText = '';
|
|
301
|
+
let stableStart = null;
|
|
302
|
+
let dotCount = 0;
|
|
338
303
|
|
|
339
|
-
// Wait for completion signal
|
|
340
|
-
let dotCount = 0;
|
|
341
304
|
while (Date.now() - start < timeout) {
|
|
342
|
-
const
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
305
|
+
const text = await this._extractLastMessage();
|
|
306
|
+
|
|
307
|
+
if (text !== lastText) {
|
|
308
|
+
lastText = text;
|
|
309
|
+
stableStart = null;
|
|
310
|
+
} else if (text.length > 0) {
|
|
311
|
+
if (!stableStart) stableStart = Date.now();
|
|
312
|
+
else if (Date.now() - stableStart >= stableDelay) {
|
|
313
|
+
if (!await this._isGenerating()) break; // confirmed done
|
|
314
|
+
stableStart = null; // still generating, reset
|
|
315
|
+
}
|
|
350
316
|
}
|
|
351
317
|
|
|
352
318
|
// Progress indicator
|
|
353
319
|
dotCount = (dotCount + 1) % 4;
|
|
354
|
-
logger.thinking(`Receiving response${'.'.repeat(dotCount)} (${
|
|
320
|
+
logger.thinking(`Receiving response${'.'.repeat(dotCount)} (${text.length} chars)`);
|
|
355
321
|
}
|
|
356
322
|
|
|
357
|
-
// Cleanup observer
|
|
358
|
-
await this.page.evaluate(() => {
|
|
359
|
-
if (window.__yiyanObserver) {
|
|
360
|
-
window.__yiyanObserver.disconnect();
|
|
361
|
-
delete window.__yiyanObserver;
|
|
362
|
-
delete window.__yiyanDone;
|
|
363
|
-
delete window.__yiyanText;
|
|
364
|
-
}
|
|
365
|
-
});
|
|
366
|
-
|
|
367
323
|
logger.clearLine();
|
|
368
324
|
|
|
369
325
|
const final = await this._extractLastMessage();
|