teamshare-bridge 0.21.36 → 0.21.38
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/cli/commands/browse.js +88 -16
- package/dist/cli/commands/browse.js.map +1 -1
- package/dist/cli/commands/build.js +24 -5
- package/dist/cli/commands/build.js.map +1 -1
- package/dist/cli/commands/research.js +13 -0
- package/dist/cli/commands/research.js.map +1 -1
- package/dist/cli/commands/worker.js +25 -8
- package/dist/cli/commands/worker.js.map +1 -1
- package/dist/lib/api.d.ts +7 -0
- package/dist/lib/api.js +2 -0
- package/dist/lib/api.js.map +1 -1
- package/dist/lib/browser/cdp.d.ts +14 -2
- package/dist/lib/browser/cdp.js +66 -15
- package/dist/lib/browser/cdp.js.map +1 -1
- package/dist/lib/chat/chat-reply.js +19 -13
- package/dist/lib/chat/chat-reply.js.map +1 -1
- package/dist/lib/chat/doc-reply.js +8 -9
- package/dist/lib/chat/doc-reply.js.map +1 -1
- package/dist/lib/llm/llm.js +11 -1
- package/dist/lib/llm/llm.js.map +1 -1
- package/dist/lib/tools/docx-report.js +9 -0
- package/dist/lib/tools/docx-report.js.map +1 -1
- package/package.json +1 -1
|
@@ -622,9 +622,20 @@ async function cmdBrowse(flags) {
|
|
|
622
622
|
let doneRetries = 0;
|
|
623
623
|
/** How often to re-check which tab is active (ms). */
|
|
624
624
|
const TAB_CHECK_MS = 2000;
|
|
625
|
+
/**
|
|
626
|
+
* Background tab-watch interval (ms). The main loop only calls
|
|
627
|
+
* `refreshTabIfNeeded` once per iteration and an iteration can include a
|
|
628
|
+
* 30-120s vision call, so a separate watcher probes the active tab while the
|
|
629
|
+
* loop is busy and raises `tabSwitchPending`.
|
|
630
|
+
*/
|
|
631
|
+
const TAB_WATCH_MS = 1500;
|
|
625
632
|
let lastTabCheck = 0;
|
|
626
633
|
let consecutiveScreenshotFailures = 0;
|
|
627
634
|
let reattachFailures = 0;
|
|
635
|
+
/** Set by the background watcher when a different tab became active. */
|
|
636
|
+
let tabSwitchPending = false;
|
|
637
|
+
/** Guards against overlapping `pickActiveTab` probes from the watcher. */
|
|
638
|
+
let tabProbeInFlight = false;
|
|
628
639
|
// Latency observability: rolling average of vision-call latency plus
|
|
629
640
|
// abort/timeout/stall counts, so the operator can see how slow the model is.
|
|
630
641
|
const llmStats = { calls: 0, ok: 0, screenAborts: 0, timeouts: 0, stalls: 0, totalMs: 0 };
|
|
@@ -655,6 +666,9 @@ async function cmdBrowse(flags) {
|
|
|
655
666
|
codeConfirmedOptimal = false;
|
|
656
667
|
consecutiveFailures = 0;
|
|
657
668
|
consecutiveScreenshotFailures = 0;
|
|
669
|
+
// The switch we were following is now applied.
|
|
670
|
+
tabSwitchPending = false;
|
|
671
|
+
page._tabSwitchedAt = undefined;
|
|
658
672
|
console.log(`[browse] Re-attached to tab: ${page.title} (${page.targetId.slice(0, 8)})`);
|
|
659
673
|
}
|
|
660
674
|
/**
|
|
@@ -695,6 +709,27 @@ async function cmdBrowse(flags) {
|
|
|
695
709
|
return false;
|
|
696
710
|
}
|
|
697
711
|
}
|
|
712
|
+
/**
|
|
713
|
+
* Background watcher: the human (or the site/tool action) can change the
|
|
714
|
+
* active tab at any moment, and the main loop cannot notice while it is
|
|
715
|
+
* inside a long vision call. Probe the active tab every `TAB_WATCH_MS` and
|
|
716
|
+
* flag a switch; the vision heartbeat aborts the in-flight call so the loop
|
|
717
|
+
* re-binds to the newly active tab immediately. We never activate a tab.
|
|
718
|
+
*/
|
|
719
|
+
const tabWatcher = setInterval(() => {
|
|
720
|
+
if (stopped || page.disconnected || tabProbeInFlight)
|
|
721
|
+
return;
|
|
722
|
+
tabProbeInFlight = true;
|
|
723
|
+
void (0, cdp_1.pickActiveTab)(port, page.targetId)
|
|
724
|
+
.then((active) => {
|
|
725
|
+
if (active && active.id !== page.targetId) {
|
|
726
|
+
tabSwitchPending = true;
|
|
727
|
+
page._tabSwitchedAt = Date.now();
|
|
728
|
+
}
|
|
729
|
+
})
|
|
730
|
+
.catch(() => { })
|
|
731
|
+
.finally(() => { tabProbeInFlight = false; });
|
|
732
|
+
}, TAB_WATCH_MS);
|
|
698
733
|
process.on('SIGINT', () => {
|
|
699
734
|
console.log('\n[browse] Stopped by user');
|
|
700
735
|
stopped = true;
|
|
@@ -703,9 +738,17 @@ async function cmdBrowse(flags) {
|
|
|
703
738
|
console.log(`[browse] Agent started. ${continuous ? 'Running until Ctrl+C.' : `Max ${maxActions} actions.`} Press Ctrl+C to stop.\n`);
|
|
704
739
|
while (!stopped && actionCount < maxActions) {
|
|
705
740
|
try {
|
|
706
|
-
// Follow the
|
|
707
|
-
// the
|
|
708
|
-
|
|
741
|
+
// Follow the active tab: re-attach if the bound tab was closed, or if the
|
|
742
|
+
// watcher flagged a switch (force skips the throttle so we re-bind at once
|
|
743
|
+
// rather than working a stale tab for the rest of this iteration).
|
|
744
|
+
if (tabSwitchPending) {
|
|
745
|
+
tabSwitchPending = false;
|
|
746
|
+
page._tabSwitchedAt = undefined;
|
|
747
|
+
await refreshTabIfNeeded(true);
|
|
748
|
+
}
|
|
749
|
+
else {
|
|
750
|
+
await refreshTabIfNeeded();
|
|
751
|
+
}
|
|
709
752
|
// Take screenshot and extract elements in parallel
|
|
710
753
|
const [screenshot, elements, pageContext] = await Promise.all([
|
|
711
754
|
captureScreenshot(page, quality, SCREENSHOT_MAX_EDGE),
|
|
@@ -778,10 +821,14 @@ async function cmdBrowse(flags) {
|
|
|
778
821
|
fatalAuthError = true;
|
|
779
822
|
break;
|
|
780
823
|
}
|
|
781
|
-
// The screen changed
|
|
782
|
-
// Bail out of the retry loop and
|
|
783
|
-
// (speed matters: a 30s backoff
|
|
784
|
-
|
|
824
|
+
// The screen changed (or a new tab became active) while the LLM was
|
|
825
|
+
// thinking: its answer is stale. Bail out of the retry loop and
|
|
826
|
+
// re-analyze the active tab right away (speed matters: a 30s backoff
|
|
827
|
+
// here livelocked on dynamic pages).
|
|
828
|
+
if (result.error === 'screen_changed' || result.error === 'tab_switch') {
|
|
829
|
+
if (result.error === 'tab_switch') {
|
|
830
|
+
console.log('[browse] Active tab changed during analysis — re-analyzing the new tab');
|
|
831
|
+
}
|
|
785
832
|
screenChangedDuringLlm = true;
|
|
786
833
|
llmStats.screenAborts++;
|
|
787
834
|
logLlmStats();
|
|
@@ -1267,6 +1314,7 @@ Do NOT simplify variable names or change the function signature. Only optimize t
|
|
|
1267
1314
|
await sleep(intervalMs);
|
|
1268
1315
|
}
|
|
1269
1316
|
}
|
|
1317
|
+
clearInterval(tabWatcher);
|
|
1270
1318
|
if (actionCount >= maxActions && !continuous) {
|
|
1271
1319
|
console.log(`[browse] Reached max actions (${maxActions})`);
|
|
1272
1320
|
}
|
|
@@ -1285,12 +1333,14 @@ Do NOT simplify variable names or change the function signature. Only optimize t
|
|
|
1285
1333
|
/**
|
|
1286
1334
|
* Connect directly to a page's WebSocket from /json/list.
|
|
1287
1335
|
* This avoids the Target.attachToTarget complexity.
|
|
1336
|
+
*
|
|
1337
|
+
* Uses the ACTIVE-tab picker (`resolveActivePage`): the loop must bind to the
|
|
1338
|
+
* tab the human/site has focused right now, not the first entry of /json/list.
|
|
1339
|
+
* `resolveActivePage` falls back to the general attachable-page picker (which
|
|
1340
|
+
* may open a blank tab) only when no page can be probed.
|
|
1288
1341
|
*/
|
|
1289
1342
|
async function connectToPage(port, preferTargetId) {
|
|
1290
|
-
|
|
1291
|
-
// and opens a blank tab if none exists - so a Chrome launched on the NTP is
|
|
1292
|
-
// still attachable. (Was a local chrome:// filter that could never match.)
|
|
1293
|
-
const pageTarget = await (0, cdp_1.resolveAttachablePage)(port, preferTargetId);
|
|
1343
|
+
const pageTarget = await (0, cdp_1.resolveActivePage)(port, preferTargetId);
|
|
1294
1344
|
const wsUrl = pageTarget.webSocketDebuggerUrl;
|
|
1295
1345
|
return new Promise((resolve, reject) => {
|
|
1296
1346
|
const ws = new ws_1.default(wsUrl);
|
|
@@ -3396,6 +3446,14 @@ async function analyzeScreenshot(baseUrl, apiKey, model, screenshotBase64, eleme
|
|
|
3396
3446
|
let heartbeatTicks = 0;
|
|
3397
3447
|
const heartbeat = setInterval(async () => {
|
|
3398
3448
|
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
|
3449
|
+
// The human/page made a different tab active: this answer belongs to the
|
|
3450
|
+
// old tab, so abort and let the loop re-bind to the active one.
|
|
3451
|
+
if (page?._tabSwitchedAt && page._tabSwitchedAt > startTime) {
|
|
3452
|
+
console.log(`[browse] Active tab switched (${elapsed}s) — aborting LLM`);
|
|
3453
|
+
abortReason = 'tab_switch';
|
|
3454
|
+
controller.abort();
|
|
3455
|
+
return;
|
|
3456
|
+
}
|
|
3399
3457
|
// Check for screen change via CDP navigation event or MutationObserver flag
|
|
3400
3458
|
let screenChanged = false;
|
|
3401
3459
|
if (page?._cdpNavigated && page._cdpNavigated > startTime) {
|
|
@@ -3512,11 +3570,19 @@ async function analyzeScreenshot(baseUrl, apiKey, model, screenshotBase64, eleme
|
|
|
3512
3570
|
catch (err) {
|
|
3513
3571
|
if (err instanceof Error && err.name === 'AbortError') {
|
|
3514
3572
|
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
|
3515
|
-
if (abortReason === 'screen_change') {
|
|
3516
|
-
// The page changed while we were waiting:
|
|
3517
|
-
// Signal the caller to re-analyze
|
|
3518
|
-
|
|
3519
|
-
|
|
3573
|
+
if (abortReason === 'screen_change' || abortReason === 'tab_switch') {
|
|
3574
|
+
// The page changed (or a new tab became active) while we were waiting:
|
|
3575
|
+
// the answer is already stale. Signal the caller to re-analyze the
|
|
3576
|
+
// active tab right away (no backoff).
|
|
3577
|
+
console.log(abortReason === 'tab_switch'
|
|
3578
|
+
? `[browse] LLM aborted at ${elapsed}s — active tab changed, re-analyzing now`
|
|
3579
|
+
: `[browse] LLM aborted at ${elapsed}s — screen changed, re-analyzing now`);
|
|
3580
|
+
return {
|
|
3581
|
+
actions: [],
|
|
3582
|
+
attempt,
|
|
3583
|
+
durationMs: Date.now() - startTime,
|
|
3584
|
+
error: abortReason === 'tab_switch' ? 'tab_switch' : 'screen_changed',
|
|
3585
|
+
};
|
|
3520
3586
|
}
|
|
3521
3587
|
console.error(`[browse] LLM request timed out (${Math.round(timeoutMs / 1000)}s)`);
|
|
3522
3588
|
return { actions: [], attempt, durationMs: Date.now() - startTime, error: 'timeout' };
|
|
@@ -3564,6 +3630,12 @@ async function analyzeScreenshotWithPrompt(baseUrl, apiKey, model, screenshotBas
|
|
|
3564
3630
|
// wipes it) must also abort - check `_cdpNavigated` too, as in the main call.
|
|
3565
3631
|
const heartbeat = setInterval(async () => {
|
|
3566
3632
|
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
|
3633
|
+
if (page?._tabSwitchedAt && page._tabSwitchedAt > startTime) {
|
|
3634
|
+
console.log(`[browse] Active tab switched in opt pass (${elapsed}s) — aborting`);
|
|
3635
|
+
optAbortReason = 'screen_change';
|
|
3636
|
+
controller.abort();
|
|
3637
|
+
return;
|
|
3638
|
+
}
|
|
3567
3639
|
if (page?._cdpNavigated && page._cdpNavigated > startTime) {
|
|
3568
3640
|
console.log(`[browse] CDP navigation detected in opt pass (${elapsed}s) — aborting`);
|
|
3569
3641
|
optAbortReason = 'screen_change';
|