@rtrvr-ai/rover 1.1.0 → 1.1.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/README.md +40 -16
- package/dist/embed.js +16 -16
- package/dist/index.d.ts +25 -2
- package/dist/rover.js +414 -36
- package/dist/worker/rover-worker.js +61 -28
- package/package.json +1 -1
package/dist/rover.js
CHANGED
|
@@ -11107,7 +11107,7 @@ var Bridge = class {
|
|
|
11107
11107
|
this.runtimeId = opts.runtimeId;
|
|
11108
11108
|
this.domainScopeMode = normalizeDomainScopeMode(opts.domainScopeMode);
|
|
11109
11109
|
this.allowedDomains = normalizeAllowedDomains(opts.allowedDomains, window.location.hostname, this.domainScopeMode);
|
|
11110
|
-
this.externalNavigationPolicy = normalizeExternalNavigationPolicy(opts.externalNavigationPolicy
|
|
11110
|
+
this.externalNavigationPolicy = normalizeExternalNavigationPolicy(opts.externalNavigationPolicy);
|
|
11111
11111
|
this.registerOpenedTab = opts.registerOpenedTab;
|
|
11112
11112
|
this.listKnownTabs = opts.listKnownTabs;
|
|
11113
11113
|
this.switchToLogicalTab = opts.switchToLogicalTab;
|
|
@@ -11137,8 +11137,8 @@ var Bridge = class {
|
|
|
11137
11137
|
if (options.allowedDomains) {
|
|
11138
11138
|
this.allowedDomains = normalizeAllowedDomains(options.allowedDomains, window.location.hostname, this.domainScopeMode);
|
|
11139
11139
|
}
|
|
11140
|
-
if (options.externalNavigationPolicy
|
|
11141
|
-
this.externalNavigationPolicy = normalizeExternalNavigationPolicy(options.externalNavigationPolicy
|
|
11140
|
+
if (options.externalNavigationPolicy) {
|
|
11141
|
+
this.externalNavigationPolicy = normalizeExternalNavigationPolicy(options.externalNavigationPolicy);
|
|
11142
11142
|
}
|
|
11143
11143
|
}
|
|
11144
11144
|
async getSnapshot() {
|
|
@@ -11465,7 +11465,8 @@ var Bridge = class {
|
|
|
11465
11465
|
}
|
|
11466
11466
|
async openUrlInNewTab(targetUrl, options) {
|
|
11467
11467
|
const external = !isUrlAllowedByDomains(targetUrl, this.allowedDomains);
|
|
11468
|
-
const
|
|
11468
|
+
const knownTabIdsBeforeOpen = this.snapshotKnownTabIds();
|
|
11469
|
+
let popupAttempt = this.openVerifiedPopup(targetUrl);
|
|
11469
11470
|
let logicalTabId;
|
|
11470
11471
|
if (popupAttempt.opened && this.registerOpenedTab) {
|
|
11471
11472
|
try {
|
|
@@ -11479,6 +11480,16 @@ var Bridge = class {
|
|
|
11479
11480
|
} catch {
|
|
11480
11481
|
}
|
|
11481
11482
|
}
|
|
11483
|
+
if (popupAttempt.opened && !logicalTabId) {
|
|
11484
|
+
logicalTabId = await this.reconcileOpenedTab(targetUrl, knownTabIdsBeforeOpen);
|
|
11485
|
+
}
|
|
11486
|
+
if (!popupAttempt.opened) {
|
|
11487
|
+
const reconciledTabId = await this.reconcileOpenedTab(targetUrl, knownTabIdsBeforeOpen);
|
|
11488
|
+
if (reconciledTabId) {
|
|
11489
|
+
popupAttempt = { opened: true, verified: false };
|
|
11490
|
+
logicalTabId = logicalTabId || reconciledTabId;
|
|
11491
|
+
}
|
|
11492
|
+
}
|
|
11482
11493
|
const registrationFailed = popupAttempt.opened && !logicalTabId && !!this.registerOpenedTab;
|
|
11483
11494
|
if (!popupAttempt.opened) {
|
|
11484
11495
|
const reason = "Browser popup settings blocked opening a new tab.";
|
|
@@ -11588,6 +11599,51 @@ var Bridge = class {
|
|
|
11588
11599
|
return { opened: false, verified: false };
|
|
11589
11600
|
}
|
|
11590
11601
|
}
|
|
11602
|
+
snapshotKnownTabIds() {
|
|
11603
|
+
const ids = /* @__PURE__ */ new Set();
|
|
11604
|
+
if (!this.listKnownTabs)
|
|
11605
|
+
return ids;
|
|
11606
|
+
try {
|
|
11607
|
+
const tabs = this.listKnownTabs();
|
|
11608
|
+
for (const tab of tabs) {
|
|
11609
|
+
const logicalTabId = Number(tab?.logicalTabId);
|
|
11610
|
+
if (!Number.isFinite(logicalTabId) || logicalTabId <= 0)
|
|
11611
|
+
continue;
|
|
11612
|
+
ids.add(logicalTabId);
|
|
11613
|
+
}
|
|
11614
|
+
} catch {
|
|
11615
|
+
return ids;
|
|
11616
|
+
}
|
|
11617
|
+
return ids;
|
|
11618
|
+
}
|
|
11619
|
+
async reconcileOpenedTab(targetUrl, beforeIds) {
|
|
11620
|
+
if (!this.listKnownTabs)
|
|
11621
|
+
return void 0;
|
|
11622
|
+
const targetHost = extractHostname(targetUrl);
|
|
11623
|
+
const deadline = Date.now() + 2e3;
|
|
11624
|
+
while (Date.now() < deadline) {
|
|
11625
|
+
try {
|
|
11626
|
+
const knownTabs = this.listKnownTabs();
|
|
11627
|
+
for (const tab of knownTabs) {
|
|
11628
|
+
const logicalTabId = Number(tab?.logicalTabId);
|
|
11629
|
+
if (!Number.isFinite(logicalTabId) || logicalTabId <= 0)
|
|
11630
|
+
continue;
|
|
11631
|
+
if (beforeIds.has(logicalTabId))
|
|
11632
|
+
continue;
|
|
11633
|
+
const knownUrl = String(tab?.url || "").trim();
|
|
11634
|
+
if (!knownUrl)
|
|
11635
|
+
return logicalTabId;
|
|
11636
|
+
const knownHost = extractHostname(knownUrl);
|
|
11637
|
+
if (!targetHost || !knownHost || knownHost === targetHost) {
|
|
11638
|
+
return logicalTabId;
|
|
11639
|
+
}
|
|
11640
|
+
}
|
|
11641
|
+
} catch {
|
|
11642
|
+
}
|
|
11643
|
+
await new Promise((resolve) => setTimeout(resolve, 120));
|
|
11644
|
+
}
|
|
11645
|
+
return void 0;
|
|
11646
|
+
}
|
|
11591
11647
|
async openElementInNewTab(args) {
|
|
11592
11648
|
const elementId = args.element_id ?? args.source_element_id ?? args.target_element_id ?? args.center_element_id ?? null;
|
|
11593
11649
|
if (!elementId) {
|
|
@@ -11721,18 +11777,11 @@ function normalizeToolName(name) {
|
|
|
11721
11777
|
function normalizeDomainScopeMode(mode) {
|
|
11722
11778
|
return mode === "host_only" ? "host_only" : "registrable_domain";
|
|
11723
11779
|
}
|
|
11724
|
-
function
|
|
11725
|
-
if (policy === "allow")
|
|
11726
|
-
return "allow";
|
|
11727
|
-
if (policy === "block_new_tab")
|
|
11728
|
-
return "open_new_tab_notice";
|
|
11729
|
-
return void 0;
|
|
11730
|
-
}
|
|
11731
|
-
function normalizeExternalNavigationPolicy(policy, legacy) {
|
|
11780
|
+
function normalizeExternalNavigationPolicy(policy) {
|
|
11732
11781
|
if (policy === "allow" || policy === "block" || policy === "open_new_tab_notice") {
|
|
11733
11782
|
return policy;
|
|
11734
11783
|
}
|
|
11735
|
-
return
|
|
11784
|
+
return "open_new_tab_notice";
|
|
11736
11785
|
}
|
|
11737
11786
|
function normalizeAllowedDomains(input, currentHost, scopeMode) {
|
|
11738
11787
|
const candidates = Array.isArray(input) ? input : [];
|
|
@@ -14639,6 +14688,14 @@ var SessionCoordinator = class _SessionCoordinator {
|
|
|
14639
14688
|
const normalizedUrl = normalizeUrl2(payload.url);
|
|
14640
14689
|
let logicalTabId = 0;
|
|
14641
14690
|
this.mutate("local", (draft) => {
|
|
14691
|
+
const existing = draft.tabs.find((tab) => !tab.runtimeId && !!tab.external && tab.url === normalizedUrl);
|
|
14692
|
+
if (existing) {
|
|
14693
|
+
existing.title = payload.title || existing.title;
|
|
14694
|
+
existing.updatedAt = now();
|
|
14695
|
+
existing.openerRuntimeId = payload.openerRuntimeId || existing.openerRuntimeId;
|
|
14696
|
+
logicalTabId = existing.logicalTabId;
|
|
14697
|
+
return;
|
|
14698
|
+
}
|
|
14642
14699
|
logicalTabId = draft.nextLogicalTabId++;
|
|
14643
14700
|
draft.tabs.push({
|
|
14644
14701
|
logicalTabId,
|
|
@@ -15045,7 +15102,7 @@ var SessionCoordinator = class _SessionCoordinator {
|
|
|
15045
15102
|
};
|
|
15046
15103
|
|
|
15047
15104
|
// dist/cloudCheckpoint.js
|
|
15048
|
-
var
|
|
15105
|
+
var DEFAULT_EXTENSION_ROUTER_BASE = "https://extensionrouter.rtrvr.ai";
|
|
15049
15106
|
function toFiniteNumber(value, fallback) {
|
|
15050
15107
|
const n = Number(value);
|
|
15051
15108
|
return Number.isFinite(n) ? n : fallback;
|
|
@@ -15071,8 +15128,22 @@ function buildRevisionKey(payload) {
|
|
|
15071
15128
|
].join(":");
|
|
15072
15129
|
}
|
|
15073
15130
|
function normalizeRouterEndpoint(apiBase) {
|
|
15074
|
-
const
|
|
15075
|
-
|
|
15131
|
+
const fallback = DEFAULT_EXTENSION_ROUTER_BASE;
|
|
15132
|
+
const base = String(apiBase || fallback).trim().replace(/\/+$/, "");
|
|
15133
|
+
if (!base)
|
|
15134
|
+
return fallback;
|
|
15135
|
+
if (base.endsWith("/extensionRouter"))
|
|
15136
|
+
return base;
|
|
15137
|
+
try {
|
|
15138
|
+
const parsed = new URL(base);
|
|
15139
|
+
const pathname = parsed.pathname.replace(/\/+$/, "");
|
|
15140
|
+
if (pathname && pathname !== "/")
|
|
15141
|
+
return base;
|
|
15142
|
+
if (parsed.hostname.toLowerCase() === "extensionrouter.rtrvr.ai")
|
|
15143
|
+
return base;
|
|
15144
|
+
} catch {
|
|
15145
|
+
}
|
|
15146
|
+
return `${base}/extensionRouter`;
|
|
15076
15147
|
}
|
|
15077
15148
|
function toError(message, details) {
|
|
15078
15149
|
const error = new Error(message);
|
|
@@ -15091,6 +15162,7 @@ var RoverCloudCheckpointClient = class {
|
|
|
15091
15162
|
shouldWrite;
|
|
15092
15163
|
buildCheckpoint;
|
|
15093
15164
|
onCheckpoint;
|
|
15165
|
+
onStateChange;
|
|
15094
15166
|
onError;
|
|
15095
15167
|
started = false;
|
|
15096
15168
|
dirty = false;
|
|
@@ -15102,8 +15174,9 @@ var RoverCloudCheckpointClient = class {
|
|
|
15102
15174
|
lastAppliedRemoteUpdatedAt = 0;
|
|
15103
15175
|
pushInFlight = false;
|
|
15104
15176
|
pullInFlight = false;
|
|
15177
|
+
state = "active";
|
|
15105
15178
|
constructor(options) {
|
|
15106
|
-
const token = String(options.
|
|
15179
|
+
const token = String(options.authToken || options.apiKey || "").trim();
|
|
15107
15180
|
if (!token) {
|
|
15108
15181
|
throw toError("Rover cloud checkpoint requires apiKey or authToken.");
|
|
15109
15182
|
}
|
|
@@ -15118,12 +15191,14 @@ var RoverCloudCheckpointClient = class {
|
|
|
15118
15191
|
this.shouldWrite = options.shouldWrite || (() => true);
|
|
15119
15192
|
this.buildCheckpoint = options.buildCheckpoint;
|
|
15120
15193
|
this.onCheckpoint = options.onCheckpoint;
|
|
15194
|
+
this.onStateChange = options.onStateChange;
|
|
15121
15195
|
this.onError = options.onError;
|
|
15122
15196
|
}
|
|
15123
15197
|
start() {
|
|
15124
15198
|
if (this.started)
|
|
15125
15199
|
return;
|
|
15126
15200
|
this.started = true;
|
|
15201
|
+
this.setState("active");
|
|
15127
15202
|
this.flushTimer = window.setInterval(() => {
|
|
15128
15203
|
void this.flush(false);
|
|
15129
15204
|
}, this.flushIntervalMs);
|
|
@@ -15156,6 +15231,8 @@ var RoverCloudCheckpointClient = class {
|
|
|
15156
15231
|
async flush(force) {
|
|
15157
15232
|
if (this.pushInFlight)
|
|
15158
15233
|
return;
|
|
15234
|
+
if (this.state === "paused_auth")
|
|
15235
|
+
return;
|
|
15159
15236
|
if (!this.dirty && !force)
|
|
15160
15237
|
return;
|
|
15161
15238
|
if (!this.shouldWrite())
|
|
@@ -15189,8 +15266,9 @@ var RoverCloudCheckpointClient = class {
|
|
|
15189
15266
|
this.applyRemoteCheckpoint(response.checkpoint, "push_stale");
|
|
15190
15267
|
this.dirty = false;
|
|
15191
15268
|
}
|
|
15269
|
+
this.setState("active");
|
|
15192
15270
|
} catch (error) {
|
|
15193
|
-
this.
|
|
15271
|
+
this.handleCheckpointError(error, "roverSessionCheckpointUpsert");
|
|
15194
15272
|
} finally {
|
|
15195
15273
|
this.pushInFlight = false;
|
|
15196
15274
|
}
|
|
@@ -15198,6 +15276,8 @@ var RoverCloudCheckpointClient = class {
|
|
|
15198
15276
|
async pull(force) {
|
|
15199
15277
|
if (this.pullInFlight)
|
|
15200
15278
|
return;
|
|
15279
|
+
if (this.state === "paused_auth")
|
|
15280
|
+
return;
|
|
15201
15281
|
if (!force && Date.now() - this.lastPullAt < this.pullIntervalMs)
|
|
15202
15282
|
return;
|
|
15203
15283
|
this.pullInFlight = true;
|
|
@@ -15210,8 +15290,9 @@ var RoverCloudCheckpointClient = class {
|
|
|
15210
15290
|
if (!response?.found || !response?.checkpoint || typeof response.checkpoint !== "object")
|
|
15211
15291
|
return;
|
|
15212
15292
|
this.applyRemoteCheckpoint(response.checkpoint, "pull");
|
|
15293
|
+
this.setState("active");
|
|
15213
15294
|
} catch (error) {
|
|
15214
|
-
this.
|
|
15295
|
+
this.handleCheckpointError(error, "roverSessionCheckpointGet");
|
|
15215
15296
|
} finally {
|
|
15216
15297
|
this.pullInFlight = false;
|
|
15217
15298
|
}
|
|
@@ -15240,14 +15321,68 @@ var RoverCloudCheckpointClient = class {
|
|
|
15240
15321
|
const text = await response.text().catch(() => "");
|
|
15241
15322
|
payload2 = text ? { error: truncateText(text, 2e3) } : void 0;
|
|
15242
15323
|
}
|
|
15243
|
-
throw toError(`Checkpoint HTTP ${response.status}`,
|
|
15324
|
+
throw toError(`Checkpoint HTTP ${response.status}`, {
|
|
15325
|
+
action,
|
|
15326
|
+
status: response.status,
|
|
15327
|
+
payload: payload2
|
|
15328
|
+
});
|
|
15244
15329
|
}
|
|
15245
15330
|
const payload = await response.json();
|
|
15246
15331
|
if (payload?.success === false) {
|
|
15247
|
-
throw toError("Checkpoint extensionRouter returned success=false",
|
|
15332
|
+
throw toError("Checkpoint extensionRouter returned success=false", {
|
|
15333
|
+
action,
|
|
15334
|
+
status: response.status,
|
|
15335
|
+
payload
|
|
15336
|
+
});
|
|
15248
15337
|
}
|
|
15249
15338
|
return payload?.data;
|
|
15250
15339
|
}
|
|
15340
|
+
setState(next, context) {
|
|
15341
|
+
if (this.state === next)
|
|
15342
|
+
return;
|
|
15343
|
+
this.state = next;
|
|
15344
|
+
this.onStateChange?.(next, context || {});
|
|
15345
|
+
}
|
|
15346
|
+
handleCheckpointError(error, action) {
|
|
15347
|
+
const details = this.normalizeCheckpointError(error);
|
|
15348
|
+
const isAuthFailure = this.isAuthFailure(details);
|
|
15349
|
+
if (isAuthFailure) {
|
|
15350
|
+
this.setState("paused_auth", {
|
|
15351
|
+
reason: "auth_failed",
|
|
15352
|
+
action,
|
|
15353
|
+
code: details.code,
|
|
15354
|
+
message: details.message
|
|
15355
|
+
});
|
|
15356
|
+
}
|
|
15357
|
+
this.onError?.(error, {
|
|
15358
|
+
action,
|
|
15359
|
+
state: this.state,
|
|
15360
|
+
code: details.code,
|
|
15361
|
+
message: details.message,
|
|
15362
|
+
status: details.status,
|
|
15363
|
+
paused: isAuthFailure
|
|
15364
|
+
});
|
|
15365
|
+
}
|
|
15366
|
+
normalizeCheckpointError(error) {
|
|
15367
|
+
const anyError = error;
|
|
15368
|
+
const details = anyError?.details;
|
|
15369
|
+
const payload = details?.payload || details?.response || details;
|
|
15370
|
+
const candidateCode = payload?.errorCode || payload?.errorDetails?.code || payload?.error?.code || payload?.code;
|
|
15371
|
+
const candidateMessage = payload?.error || payload?.errorDetails?.message || payload?.error?.message || anyError?.message || "Checkpoint request failed";
|
|
15372
|
+
const status = Number(details?.status);
|
|
15373
|
+
return {
|
|
15374
|
+
code: typeof candidateCode === "string" ? candidateCode : void 0,
|
|
15375
|
+
message: truncateText(candidateMessage, 1e3),
|
|
15376
|
+
status: Number.isFinite(status) ? status : void 0
|
|
15377
|
+
};
|
|
15378
|
+
}
|
|
15379
|
+
isAuthFailure(details) {
|
|
15380
|
+
const code = String(details.code || "").toUpperCase();
|
|
15381
|
+
if (code === "INVALID_API_KEY" || code === "MISSING_API_KEY" || code === "UNAUTHENTICATED" || code === "PERMISSION_DENIED") {
|
|
15382
|
+
return true;
|
|
15383
|
+
}
|
|
15384
|
+
return details.status === 401 || details.status === 403;
|
|
15385
|
+
}
|
|
15251
15386
|
};
|
|
15252
15387
|
|
|
15253
15388
|
// dist/runtimeStorage.js
|
|
@@ -15387,6 +15522,9 @@ var VISITOR_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365;
|
|
|
15387
15522
|
var CHECKPOINT_PAYLOAD_VERSION = 1;
|
|
15388
15523
|
var ACTIVE_PENDING_RUN_GRACE_MS = 3e3;
|
|
15389
15524
|
var STALE_PENDING_RUN_MS = 9e4;
|
|
15525
|
+
var TELEMETRY_DEFAULT_FLUSH_INTERVAL_MS = 12e3;
|
|
15526
|
+
var TELEMETRY_DEFAULT_MAX_BATCH_SIZE = 30;
|
|
15527
|
+
var TELEMETRY_MAX_BUFFER_SIZE = 240;
|
|
15390
15528
|
var instance = null;
|
|
15391
15529
|
var bridge = null;
|
|
15392
15530
|
var worker = null;
|
|
@@ -15398,6 +15536,11 @@ var runtimeStateStore = null;
|
|
|
15398
15536
|
var runtimeId = "";
|
|
15399
15537
|
var sessionCoordinator = null;
|
|
15400
15538
|
var cloudCheckpointClient = null;
|
|
15539
|
+
var telemetryFlushTimer = null;
|
|
15540
|
+
var telemetryBuffer = [];
|
|
15541
|
+
var telemetryInFlight = false;
|
|
15542
|
+
var telemetryPausedAuth = false;
|
|
15543
|
+
var telemetrySeq = 0;
|
|
15401
15544
|
var resolvedVisitorId = void 0;
|
|
15402
15545
|
var suppressCheckpointSync = false;
|
|
15403
15546
|
var currentMode = "controller";
|
|
@@ -15423,6 +15566,7 @@ var RUN_SCOPED_WORKER_MESSAGE_TYPES = /* @__PURE__ */ new Set([
|
|
|
15423
15566
|
var pendingToolRegistrations = [];
|
|
15424
15567
|
var eventHandlers = /* @__PURE__ */ new Map();
|
|
15425
15568
|
function emit(event, payload) {
|
|
15569
|
+
recordTelemetryEvent(event, payload);
|
|
15426
15570
|
const handlers = eventHandlers.get(event);
|
|
15427
15571
|
if (!handlers)
|
|
15428
15572
|
return;
|
|
@@ -15628,20 +15772,207 @@ function safeSerialize(value) {
|
|
|
15628
15772
|
}
|
|
15629
15773
|
}
|
|
15630
15774
|
}
|
|
15631
|
-
function
|
|
15632
|
-
const raw =
|
|
15633
|
-
|
|
15634
|
-
|
|
15635
|
-
|
|
15775
|
+
function normalizeTelemetryConfig(cfg) {
|
|
15776
|
+
const raw = cfg?.telemetry;
|
|
15777
|
+
const sampleRateRaw = Number(raw?.sampleRate);
|
|
15778
|
+
const sampleRate = Number.isFinite(sampleRateRaw) ? Math.min(1, Math.max(0, sampleRateRaw)) : 1;
|
|
15779
|
+
const flushRaw = Number(raw?.flushIntervalMs);
|
|
15780
|
+
const flushIntervalMs = Number.isFinite(flushRaw) ? Math.min(6e4, Math.max(2e3, Math.floor(flushRaw))) : TELEMETRY_DEFAULT_FLUSH_INTERVAL_MS;
|
|
15781
|
+
const batchRaw = Number(raw?.maxBatchSize);
|
|
15782
|
+
const maxBatchSize = Number.isFinite(batchRaw) ? Math.min(80, Math.max(1, Math.floor(batchRaw))) : TELEMETRY_DEFAULT_MAX_BATCH_SIZE;
|
|
15783
|
+
return {
|
|
15784
|
+
enabled: raw?.enabled !== false,
|
|
15785
|
+
sampleRate,
|
|
15786
|
+
flushIntervalMs,
|
|
15787
|
+
maxBatchSize,
|
|
15788
|
+
includePayloads: raw?.includePayloads === true
|
|
15789
|
+
};
|
|
15636
15790
|
}
|
|
15637
|
-
function
|
|
15791
|
+
function canUseTelemetry(cfg) {
|
|
15792
|
+
if (!cfg)
|
|
15793
|
+
return false;
|
|
15794
|
+
const telemetry = normalizeTelemetryConfig(cfg);
|
|
15795
|
+
if (!telemetry.enabled)
|
|
15796
|
+
return false;
|
|
15797
|
+
if (!(cfg.authToken || cfg.apiKey))
|
|
15798
|
+
return false;
|
|
15799
|
+
return true;
|
|
15800
|
+
}
|
|
15801
|
+
function summarizeTelemetryPayload(payload) {
|
|
15802
|
+
if (payload == null)
|
|
15803
|
+
return void 0;
|
|
15804
|
+
if (typeof payload === "string")
|
|
15805
|
+
return truncateText2(payload, 260);
|
|
15806
|
+
if (typeof payload === "number" || typeof payload === "boolean")
|
|
15807
|
+
return payload;
|
|
15808
|
+
if (Array.isArray(payload)) {
|
|
15809
|
+
return { type: "array", length: payload.length };
|
|
15810
|
+
}
|
|
15811
|
+
if (typeof payload === "object") {
|
|
15812
|
+
const keys = Object.keys(payload).slice(0, 20);
|
|
15813
|
+
const summary = { type: "object", keys };
|
|
15814
|
+
const preferredKeys = ["code", "message", "stage", "status", "reason", "taskId", "runId", "policyAction"];
|
|
15815
|
+
for (const key of preferredKeys) {
|
|
15816
|
+
const value = payload?.[key];
|
|
15817
|
+
if (value == null)
|
|
15818
|
+
continue;
|
|
15819
|
+
if (typeof value === "string")
|
|
15820
|
+
summary[key] = truncateText2(value, 180);
|
|
15821
|
+
else if (typeof value === "number" || typeof value === "boolean")
|
|
15822
|
+
summary[key] = value;
|
|
15823
|
+
}
|
|
15824
|
+
return summary;
|
|
15825
|
+
}
|
|
15826
|
+
return void 0;
|
|
15827
|
+
}
|
|
15828
|
+
function buildTelemetryPayload(payload, includePayloads) {
|
|
15829
|
+
if (!includePayloads) {
|
|
15830
|
+
return summarizeTelemetryPayload(payload);
|
|
15831
|
+
}
|
|
15832
|
+
const cloned = cloneUnknown2(payload);
|
|
15833
|
+
if (cloned == null)
|
|
15834
|
+
return void 0;
|
|
15835
|
+
if (typeof cloned === "string")
|
|
15836
|
+
return truncateText2(cloned, 1e3);
|
|
15837
|
+
if (typeof cloned === "number" || typeof cloned === "boolean")
|
|
15838
|
+
return cloned;
|
|
15839
|
+
if (Array.isArray(cloned))
|
|
15840
|
+
return { type: "array", length: cloned.length };
|
|
15841
|
+
if (typeof cloned === "object")
|
|
15842
|
+
return cloned;
|
|
15843
|
+
return summarizeTelemetryPayload(payload);
|
|
15844
|
+
}
|
|
15845
|
+
function stopTelemetry() {
|
|
15846
|
+
if (telemetryFlushTimer) {
|
|
15847
|
+
clearInterval(telemetryFlushTimer);
|
|
15848
|
+
telemetryFlushTimer = null;
|
|
15849
|
+
}
|
|
15850
|
+
}
|
|
15851
|
+
var DEFAULT_EXTENSION_ROUTER_BASE2 = "https://extensionrouter.rtrvr.ai";
|
|
15852
|
+
function resolveExtensionRouterEndpoint(apiBase) {
|
|
15853
|
+
const fallback = DEFAULT_EXTENSION_ROUTER_BASE2;
|
|
15854
|
+
const base = String(apiBase || fallback).trim().replace(/\/+$/, "");
|
|
15855
|
+
if (!base)
|
|
15856
|
+
return fallback;
|
|
15857
|
+
if (base.endsWith("/extensionRouter"))
|
|
15858
|
+
return base;
|
|
15859
|
+
try {
|
|
15860
|
+
const parsed = new URL(base);
|
|
15861
|
+
const pathname = parsed.pathname.replace(/\/+$/, "");
|
|
15862
|
+
if (pathname && pathname !== "/")
|
|
15863
|
+
return base;
|
|
15864
|
+
if (parsed.hostname.toLowerCase() === "extensionrouter.rtrvr.ai")
|
|
15865
|
+
return base;
|
|
15866
|
+
} catch {
|
|
15867
|
+
}
|
|
15868
|
+
return `${base}/extensionRouter`;
|
|
15869
|
+
}
|
|
15870
|
+
function getTelemetryEndpoint(cfg) {
|
|
15871
|
+
return resolveExtensionRouterEndpoint(cfg.apiBase);
|
|
15872
|
+
}
|
|
15873
|
+
async function flushTelemetry(force = false) {
|
|
15874
|
+
if (telemetryInFlight)
|
|
15875
|
+
return;
|
|
15876
|
+
if (telemetryPausedAuth)
|
|
15877
|
+
return;
|
|
15878
|
+
if (!currentConfig || !canUseTelemetry(currentConfig)) {
|
|
15879
|
+
telemetryBuffer = [];
|
|
15880
|
+
return;
|
|
15881
|
+
}
|
|
15882
|
+
if (!telemetryBuffer.length)
|
|
15883
|
+
return;
|
|
15884
|
+
const telemetry = normalizeTelemetryConfig(currentConfig);
|
|
15885
|
+
const token = String(currentConfig.authToken || currentConfig.apiKey || "").trim();
|
|
15886
|
+
if (!token)
|
|
15887
|
+
return;
|
|
15888
|
+
const batch = telemetryBuffer.splice(0, telemetry.maxBatchSize);
|
|
15889
|
+
if (!batch.length)
|
|
15890
|
+
return;
|
|
15891
|
+
telemetryInFlight = true;
|
|
15892
|
+
try {
|
|
15893
|
+
const response = await fetch(getTelemetryEndpoint(currentConfig), {
|
|
15894
|
+
method: "POST",
|
|
15895
|
+
headers: {
|
|
15896
|
+
Authorization: `Bearer ${token}`,
|
|
15897
|
+
"Content-Type": "application/json"
|
|
15898
|
+
},
|
|
15899
|
+
body: JSON.stringify({
|
|
15900
|
+
action: "roverTelemetryIngest",
|
|
15901
|
+
data: {
|
|
15902
|
+
siteId: currentConfig.siteId,
|
|
15903
|
+
runtimeId,
|
|
15904
|
+
sessionId: runtimeState?.sessionId,
|
|
15905
|
+
visitorId: resolvedVisitorId,
|
|
15906
|
+
flushReason: force ? "manual" : "interval",
|
|
15907
|
+
sdkVersion: "rover_sdk_v1",
|
|
15908
|
+
pageUrl: window.location.href,
|
|
15909
|
+
userAgent: navigator.userAgent,
|
|
15910
|
+
sampleRate: telemetry.sampleRate,
|
|
15911
|
+
events: batch
|
|
15912
|
+
}
|
|
15913
|
+
})
|
|
15914
|
+
});
|
|
15915
|
+
if (!response.ok) {
|
|
15916
|
+
if (response.status === 401 || response.status === 403) {
|
|
15917
|
+
telemetryPausedAuth = true;
|
|
15918
|
+
} else {
|
|
15919
|
+
telemetryBuffer = [...batch, ...telemetryBuffer].slice(-TELEMETRY_MAX_BUFFER_SIZE);
|
|
15920
|
+
}
|
|
15921
|
+
return;
|
|
15922
|
+
}
|
|
15923
|
+
const payload = await response.json().catch(() => void 0);
|
|
15924
|
+
if (payload?.success === false) {
|
|
15925
|
+
const code = String(payload?.errorCode || payload?.errorDetails?.code || "").toUpperCase();
|
|
15926
|
+
if (code === "INVALID_API_KEY" || code === "MISSING_API_KEY" || code === "UNAUTHENTICATED" || code === "PERMISSION_DENIED") {
|
|
15927
|
+
telemetryPausedAuth = true;
|
|
15928
|
+
}
|
|
15929
|
+
return;
|
|
15930
|
+
}
|
|
15931
|
+
} catch {
|
|
15932
|
+
telemetryBuffer = [...batch, ...telemetryBuffer].slice(-TELEMETRY_MAX_BUFFER_SIZE);
|
|
15933
|
+
} finally {
|
|
15934
|
+
telemetryInFlight = false;
|
|
15935
|
+
}
|
|
15936
|
+
}
|
|
15937
|
+
function setupTelemetry(cfg) {
|
|
15938
|
+
stopTelemetry();
|
|
15939
|
+
telemetryPausedAuth = false;
|
|
15940
|
+
if (!canUseTelemetry(cfg)) {
|
|
15941
|
+
telemetryBuffer = [];
|
|
15942
|
+
return;
|
|
15943
|
+
}
|
|
15944
|
+
const telemetry = normalizeTelemetryConfig(cfg);
|
|
15945
|
+
telemetryFlushTimer = setInterval(() => {
|
|
15946
|
+
void flushTelemetry(false);
|
|
15947
|
+
}, telemetry.flushIntervalMs);
|
|
15948
|
+
}
|
|
15949
|
+
function recordTelemetryEvent(event, payload) {
|
|
15950
|
+
if (!canUseTelemetry(currentConfig) || telemetryPausedAuth)
|
|
15951
|
+
return;
|
|
15952
|
+
const telemetry = normalizeTelemetryConfig(currentConfig);
|
|
15953
|
+
if (telemetry.sampleRate < 1 && Math.random() > telemetry.sampleRate)
|
|
15954
|
+
return;
|
|
15955
|
+
const next = {
|
|
15956
|
+
name: event,
|
|
15957
|
+
ts: Date.now(),
|
|
15958
|
+
seq: ++telemetrySeq,
|
|
15959
|
+
payload: buildTelemetryPayload(payload, telemetry.includePayloads)
|
|
15960
|
+
};
|
|
15961
|
+
telemetryBuffer.push(next);
|
|
15962
|
+
if (telemetryBuffer.length > TELEMETRY_MAX_BUFFER_SIZE) {
|
|
15963
|
+
telemetryBuffer = telemetryBuffer.slice(-TELEMETRY_MAX_BUFFER_SIZE);
|
|
15964
|
+
}
|
|
15965
|
+
if (telemetryBuffer.length >= telemetry.maxBatchSize) {
|
|
15966
|
+
void flushTelemetry(false);
|
|
15967
|
+
}
|
|
15968
|
+
}
|
|
15969
|
+
function buildInaccessibleTabPageData(_cfg, tab, reason = "tab_not_accessible") {
|
|
15638
15970
|
const logicalTabId = Number(tab?.logicalTabId) || void 0;
|
|
15639
15971
|
const url = tab?.url || "";
|
|
15640
15972
|
const title = tab?.title || (tab?.external ? "External Tab (Inaccessible)" : "Inactive Tab");
|
|
15641
|
-
const embeddedDomain = resolveEmbeddedDomainLabel(cfg.allowedDomains);
|
|
15642
15973
|
const normalizedReason = String(reason || "").trim();
|
|
15643
15974
|
const reasonLine = normalizedReason ? ` Reason: ${normalizedReason}.` : "";
|
|
15644
|
-
const content = tab?.external ? `
|
|
15975
|
+
const content = tab?.external ? `This external tab is tracked in virtual mode only. Live DOM control and accessibility-tree access are unavailable here.${reasonLine}` : `This tab is currently not attached to an active Rover runtime. Switch to a live tab or reopen it.${reasonLine}`;
|
|
15645
15976
|
return {
|
|
15646
15977
|
url,
|
|
15647
15978
|
title,
|
|
@@ -15659,8 +15990,7 @@ function buildInaccessibleTabPageData(cfg, tab, reason = "tab_not_accessible") {
|
|
|
15659
15990
|
function buildTabAccessToolError(cfg, tab, reason = "tab_not_accessible") {
|
|
15660
15991
|
const logicalTabId = Number(tab?.logicalTabId) || 0;
|
|
15661
15992
|
const blockedUrl = tab?.url || "";
|
|
15662
|
-
const
|
|
15663
|
-
const message = tab?.external ? `Tab ${logicalTabId} is outside Rover's embedded domain scope (${embeddedDomain}) and cannot be controlled directly.` : `Tab ${logicalTabId} is not attached to an active Rover runtime.`;
|
|
15993
|
+
const message = tab?.external ? `Tab ${logicalTabId} is external to the active runtime and cannot be controlled directly.` : `Tab ${logicalTabId} is not attached to an active Rover runtime.`;
|
|
15664
15994
|
const code = tab?.external ? "DOMAIN_SCOPE_BLOCKED" : "TAB_NOT_ACCESSIBLE";
|
|
15665
15995
|
return {
|
|
15666
15996
|
success: false,
|
|
@@ -16010,6 +16340,8 @@ function ensureUnloadHandler() {
|
|
|
16010
16340
|
sessionCoordinator?.releaseWorkflowLock(runtimeState.pendingRun.id);
|
|
16011
16341
|
}
|
|
16012
16342
|
persistRuntimeState();
|
|
16343
|
+
void flushTelemetry(true);
|
|
16344
|
+
stopTelemetry();
|
|
16013
16345
|
cloudCheckpointClient?.markDirty();
|
|
16014
16346
|
cloudCheckpointClient?.syncNow();
|
|
16015
16347
|
cloudCheckpointClient?.stop();
|
|
@@ -16025,6 +16357,9 @@ function ensureUnloadHandler() {
|
|
|
16025
16357
|
sessionCoordinator.claimLease(false);
|
|
16026
16358
|
}
|
|
16027
16359
|
autoResumeAttempted = false;
|
|
16360
|
+
if (currentConfig) {
|
|
16361
|
+
setupTelemetry(currentConfig);
|
|
16362
|
+
}
|
|
16028
16363
|
};
|
|
16029
16364
|
window.addEventListener("pageshow", onPageShow);
|
|
16030
16365
|
}
|
|
@@ -16641,6 +16976,10 @@ function setupCloudCheckpointing(cfg) {
|
|
|
16641
16976
|
if (!resolvedVisitorId)
|
|
16642
16977
|
return;
|
|
16643
16978
|
try {
|
|
16979
|
+
const emitCheckpointState = (payload) => {
|
|
16980
|
+
emit("checkpoint_state", payload);
|
|
16981
|
+
cfg.checkpointing?.onStateChange?.(payload);
|
|
16982
|
+
};
|
|
16644
16983
|
cloudCheckpointClient = new RoverCloudCheckpointClient({
|
|
16645
16984
|
apiBase: cfg.apiBase,
|
|
16646
16985
|
apiKey: cfg.apiKey,
|
|
@@ -16656,7 +16995,36 @@ function setupCloudCheckpointing(cfg) {
|
|
|
16656
16995
|
onCheckpoint: (payload) => {
|
|
16657
16996
|
applyCloudCheckpointPayload(payload);
|
|
16658
16997
|
},
|
|
16659
|
-
|
|
16998
|
+
onStateChange: (state, context) => {
|
|
16999
|
+
emitCheckpointState({
|
|
17000
|
+
state,
|
|
17001
|
+
reason: context.reason,
|
|
17002
|
+
action: context.action,
|
|
17003
|
+
code: context.code,
|
|
17004
|
+
message: context.message
|
|
17005
|
+
});
|
|
17006
|
+
if (state === "paused_auth") {
|
|
17007
|
+
emit("checkpoint_error", {
|
|
17008
|
+
action: context.action,
|
|
17009
|
+
code: context.code || "INVALID_API_KEY",
|
|
17010
|
+
message: context.message || "Checkpoint sync paused due to auth failure.",
|
|
17011
|
+
disabled: true,
|
|
17012
|
+
reason: context.reason || "auth_failed"
|
|
17013
|
+
});
|
|
17014
|
+
}
|
|
17015
|
+
},
|
|
17016
|
+
onError: (_error, context) => {
|
|
17017
|
+
cfg.checkpointing?.onError?.(context);
|
|
17018
|
+
if (!context.paused) {
|
|
17019
|
+
emit("checkpoint_error", {
|
|
17020
|
+
action: context.action,
|
|
17021
|
+
code: context.code,
|
|
17022
|
+
message: context.message,
|
|
17023
|
+
status: context.status,
|
|
17024
|
+
disabled: false,
|
|
17025
|
+
reason: "transient_failure"
|
|
17026
|
+
});
|
|
17027
|
+
}
|
|
16660
17028
|
}
|
|
16661
17029
|
});
|
|
16662
17030
|
cloudCheckpointClient.start();
|
|
@@ -16965,6 +17333,10 @@ function handleWorkerMessage(msg) {
|
|
|
16965
17333
|
const needsUserInput = completionState.needsUserInput;
|
|
16966
17334
|
if (taskComplete) {
|
|
16967
17335
|
markTaskCompleted("worker_task_complete");
|
|
17336
|
+
sessionCoordinator?.pruneTabs({
|
|
17337
|
+
dropRuntimeDetached: true,
|
|
17338
|
+
dropAllDetachedExternal: true
|
|
17339
|
+
});
|
|
16968
17340
|
setUiStatus("Task completed");
|
|
16969
17341
|
finalizeSuccessfulRunTimeline(typeof msg.runId === "string" ? msg.runId : void 0);
|
|
16970
17342
|
} else {
|
|
@@ -17022,6 +17394,7 @@ function createRuntime(cfg) {
|
|
|
17022
17394
|
}
|
|
17023
17395
|
}
|
|
17024
17396
|
setupCloudCheckpointing(cfg);
|
|
17397
|
+
setupTelemetry(cfg);
|
|
17025
17398
|
const initialAllowActions = (cfg.allowActions ?? true) && (sessionCoordinator ? sessionCoordinator.isController() : true);
|
|
17026
17399
|
bridge = new Bridge({
|
|
17027
17400
|
allowActions: initialAllowActions,
|
|
@@ -17029,7 +17402,6 @@ function createRuntime(cfg) {
|
|
|
17029
17402
|
allowedDomains: cfg.allowedDomains,
|
|
17030
17403
|
domainScopeMode: cfg.domainScopeMode,
|
|
17031
17404
|
externalNavigationPolicy: cfg.externalNavigationPolicy,
|
|
17032
|
-
crossDomainPolicy: cfg.crossDomainPolicy,
|
|
17033
17405
|
registerOpenedTab: (payload) => sessionCoordinator?.registerOpenedTab(payload),
|
|
17034
17406
|
switchToLogicalTab: (logicalTabId) => sessionCoordinator?.switchToLogicalTab(logicalTabId) || { ok: false, reason: "No session coordinator" },
|
|
17035
17407
|
listKnownTabs: () => (sessionCoordinator?.listTabs() || []).map((tab) => ({
|
|
@@ -17406,16 +17778,16 @@ function update(cfg) {
|
|
|
17406
17778
|
setupSessionCoordinator(currentConfig);
|
|
17407
17779
|
}
|
|
17408
17780
|
setupCloudCheckpointing(currentConfig);
|
|
17781
|
+
setupTelemetry(currentConfig);
|
|
17409
17782
|
if (bridge) {
|
|
17410
17783
|
if (typeof cfg.allowActions === "boolean") {
|
|
17411
17784
|
bridge.setAllowActions(cfg.allowActions && currentMode === "controller");
|
|
17412
17785
|
}
|
|
17413
|
-
if (cfg.allowedDomains || cfg.
|
|
17786
|
+
if (cfg.allowedDomains || cfg.domainScopeMode || cfg.externalNavigationPolicy) {
|
|
17414
17787
|
bridge.setNavigationPolicy({
|
|
17415
17788
|
allowedDomains: cfg.allowedDomains,
|
|
17416
17789
|
domainScopeMode: cfg.domainScopeMode,
|
|
17417
|
-
externalNavigationPolicy: cfg.externalNavigationPolicy
|
|
17418
|
-
crossDomainPolicy: cfg.crossDomainPolicy
|
|
17790
|
+
externalNavigationPolicy: cfg.externalNavigationPolicy
|
|
17419
17791
|
});
|
|
17420
17792
|
}
|
|
17421
17793
|
}
|
|
@@ -17430,6 +17802,8 @@ function update(cfg) {
|
|
|
17430
17802
|
function shutdown() {
|
|
17431
17803
|
hideTaskSuggestion();
|
|
17432
17804
|
persistRuntimeState();
|
|
17805
|
+
void flushTelemetry(true);
|
|
17806
|
+
stopTelemetry();
|
|
17433
17807
|
cloudCheckpointClient?.markDirty();
|
|
17434
17808
|
cloudCheckpointClient?.syncNow();
|
|
17435
17809
|
cloudCheckpointClient?.stop();
|
|
@@ -17447,6 +17821,10 @@ function shutdown() {
|
|
|
17447
17821
|
runtimeId = "";
|
|
17448
17822
|
resolvedVisitorId = void 0;
|
|
17449
17823
|
suppressCheckpointSync = false;
|
|
17824
|
+
telemetryInFlight = false;
|
|
17825
|
+
telemetryPausedAuth = false;
|
|
17826
|
+
telemetryBuffer = [];
|
|
17827
|
+
telemetrySeq = 0;
|
|
17450
17828
|
currentMode = "controller";
|
|
17451
17829
|
pendingTaskSuggestion = null;
|
|
17452
17830
|
runtimeStateStore = null;
|