@rtrvr-ai/rover 1.0.3 → 1.1.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/README.md +9 -0
- package/dist/embed.js +16 -16
- package/dist/index.d.ts +11 -0
- package/dist/rover.js +371 -105
- package/dist/worker/rover-worker.js +836 -75
- package/package.json +1 -1
|
@@ -161,6 +161,27 @@ var DEFAULT_GEMINI_MODEL = GEMINI_MODEL.FLASH;
|
|
|
161
161
|
var PLANNER = FUNCTION_CALLS.PLANNER;
|
|
162
162
|
var maxFileSizeInBytes = 20 * 1024 * 1024;
|
|
163
163
|
|
|
164
|
+
// ../shared/dist/lib/types/agent-types.js
|
|
165
|
+
var SUB_AGENTS;
|
|
166
|
+
(function(SUB_AGENTS2) {
|
|
167
|
+
SUB_AGENTS2["enhance"] = "enhance";
|
|
168
|
+
SUB_AGENTS2["plan"] = "plan";
|
|
169
|
+
SUB_AGENTS2["actOnTabs"] = "actOnTabs";
|
|
170
|
+
SUB_AGENTS2["extract"] = "extract";
|
|
171
|
+
SUB_AGENTS2["infer"] = "infer";
|
|
172
|
+
SUB_AGENTS2["processTabWorkflows"] = "processTabWorkflows";
|
|
173
|
+
SUB_AGENTS2["processText"] = "processText";
|
|
174
|
+
SUB_AGENTS2["createSheetFromData"] = "createSheetFromData";
|
|
175
|
+
SUB_AGENTS2["queryRtrvrDocs"] = "queryRtrvrDocs";
|
|
176
|
+
SUB_AGENTS2["googleDocGenerator"] = "googleDocGenerator";
|
|
177
|
+
SUB_AGENTS2["googleSlidesGenerator"] = "googleSlidesGenerator";
|
|
178
|
+
SUB_AGENTS2["webpageGenerator"] = "webpageGenerator";
|
|
179
|
+
SUB_AGENTS2["pdfFiller"] = "pdfFiller";
|
|
180
|
+
SUB_AGENTS2["customToolGenerator"] = "customToolGenerator";
|
|
181
|
+
SUB_AGENTS2["getPageData"] = "getPageData";
|
|
182
|
+
SUB_AGENTS2["roverExternalPageData"] = "roverExternalPageData";
|
|
183
|
+
})(SUB_AGENTS || (SUB_AGENTS = {}));
|
|
184
|
+
|
|
164
185
|
// dist/tabular-memory/tabular-store.js
|
|
165
186
|
var makeId = () => {
|
|
166
187
|
if (typeof crypto !== "undefined" && "randomUUID" in crypto)
|
|
@@ -620,7 +641,8 @@ function defaultNextActionForCode(code) {
|
|
|
620
641
|
return void 0;
|
|
621
642
|
}
|
|
622
643
|
function toRoverErrorEnvelope(err, fallbackMessage = "Operation failed") {
|
|
623
|
-
const
|
|
644
|
+
const directCandidate = err && typeof err === "object" && typeof err.code === "string" && typeof err.message === "string" ? err : void 0;
|
|
645
|
+
const candidate = err?.roverError || err?.errorDetails || (typeof err?.error === "object" ? err.error : void 0) || directCandidate;
|
|
624
646
|
if (candidate && typeof candidate === "object" && candidate.code && candidate.message) {
|
|
625
647
|
return {
|
|
626
648
|
code: candidate.code,
|
|
@@ -664,6 +686,10 @@ var LEGACY_ADDITIONAL_TOOL_ALIASES = {
|
|
|
664
686
|
pdf_filling: "generate_docs",
|
|
665
687
|
generate_web_pages: "generate_websites"
|
|
666
688
|
};
|
|
689
|
+
var DEFAULT_EXTERNAL_WEB_CONFIG = {
|
|
690
|
+
enableExternalWebContext: false,
|
|
691
|
+
scrapeMode: "off"
|
|
692
|
+
};
|
|
667
693
|
function normalizeApiToolsConfig(input) {
|
|
668
694
|
if (!input)
|
|
669
695
|
return void 0;
|
|
@@ -680,6 +706,123 @@ function normalizeApiToolsConfig(input) {
|
|
|
680
706
|
enableAdditionalTools: normalizedAdditional
|
|
681
707
|
};
|
|
682
708
|
}
|
|
709
|
+
function normalizeDomainRules(input) {
|
|
710
|
+
if (!Array.isArray(input))
|
|
711
|
+
return [];
|
|
712
|
+
const seen = /* @__PURE__ */ new Set();
|
|
713
|
+
const out = [];
|
|
714
|
+
for (const raw of input) {
|
|
715
|
+
const next = String(raw || "").trim().toLowerCase().replace(/^\./, "");
|
|
716
|
+
if (!next || seen.has(next))
|
|
717
|
+
continue;
|
|
718
|
+
seen.add(next);
|
|
719
|
+
out.push(next);
|
|
720
|
+
}
|
|
721
|
+
return out;
|
|
722
|
+
}
|
|
723
|
+
function normalizeAgentName(input) {
|
|
724
|
+
const normalized = String(input || "").trim();
|
|
725
|
+
if (!normalized)
|
|
726
|
+
return void 0;
|
|
727
|
+
return normalized.slice(0, 64);
|
|
728
|
+
}
|
|
729
|
+
function hostFromUrl(url) {
|
|
730
|
+
try {
|
|
731
|
+
return new URL(String(url || "")).hostname.toLowerCase();
|
|
732
|
+
} catch {
|
|
733
|
+
return void 0;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
function matchesDomainRule(host, rule) {
|
|
737
|
+
const clean = String(rule || "").trim().toLowerCase();
|
|
738
|
+
if (!clean)
|
|
739
|
+
return false;
|
|
740
|
+
if (clean === "*")
|
|
741
|
+
return true;
|
|
742
|
+
if (clean.startsWith("*.")) {
|
|
743
|
+
const base = clean.slice(2);
|
|
744
|
+
return !!base && (host === base || host.endsWith(`.${base}`));
|
|
745
|
+
}
|
|
746
|
+
return host === clean || host.endsWith(`.${clean}`);
|
|
747
|
+
}
|
|
748
|
+
function normalizeExternalWebConfig(input) {
|
|
749
|
+
return {
|
|
750
|
+
enableExternalWebContext: input?.enableExternalWebContext ?? DEFAULT_EXTERNAL_WEB_CONFIG.enableExternalWebContext,
|
|
751
|
+
scrapeMode: input?.scrapeMode === "on_demand" ? "on_demand" : DEFAULT_EXTERNAL_WEB_CONFIG.scrapeMode,
|
|
752
|
+
allowDomains: normalizeDomainRules(input?.allowDomains),
|
|
753
|
+
denyDomains: normalizeDomainRules(input?.denyDomains)
|
|
754
|
+
};
|
|
755
|
+
}
|
|
756
|
+
function isAllowedByRules(url, rules) {
|
|
757
|
+
const host = hostFromUrl(url);
|
|
758
|
+
if (!host)
|
|
759
|
+
return { allowed: false, reason: "invalid_url" };
|
|
760
|
+
if (rules.denyDomains.some((rule) => matchesDomainRule(host, rule))) {
|
|
761
|
+
return { allowed: false, reason: `deny_rule:${host}` };
|
|
762
|
+
}
|
|
763
|
+
if (!rules.allowDomains.length)
|
|
764
|
+
return { allowed: true };
|
|
765
|
+
if (rules.allowDomains.some((rule) => matchesDomainRule(host, rule)))
|
|
766
|
+
return { allowed: true };
|
|
767
|
+
return { allowed: false, reason: `not_in_allowlist:${host}` };
|
|
768
|
+
}
|
|
769
|
+
function normalizeRuntimeExternalTabs(input) {
|
|
770
|
+
if (!Array.isArray(input))
|
|
771
|
+
return [];
|
|
772
|
+
const deduped = /* @__PURE__ */ new Map();
|
|
773
|
+
for (const tab of input) {
|
|
774
|
+
const tabId = Number(tab?.tabId);
|
|
775
|
+
if (!Number.isFinite(tabId))
|
|
776
|
+
continue;
|
|
777
|
+
const accessMode = tab?.accessMode === "external_scraped" ? "external_scraped" : "external_placeholder";
|
|
778
|
+
const host = String(tab?.host || "").trim() || void 0;
|
|
779
|
+
const title = String(tab?.title || "").trim() || void 0;
|
|
780
|
+
const reason = String(tab?.reason || "").trim() || void 0;
|
|
781
|
+
deduped.set(tabId, {
|
|
782
|
+
tabId,
|
|
783
|
+
accessMode,
|
|
784
|
+
host,
|
|
785
|
+
title,
|
|
786
|
+
reason
|
|
787
|
+
});
|
|
788
|
+
}
|
|
789
|
+
return Array.from(deduped.values());
|
|
790
|
+
}
|
|
791
|
+
function buildRoverRuntimeContext(config2) {
|
|
792
|
+
const runtimeContext = config2.runtimeContext;
|
|
793
|
+
if (!runtimeContext || runtimeContext.mode !== "rover_embed")
|
|
794
|
+
return void 0;
|
|
795
|
+
const compactExternalTabs = normalizeRuntimeExternalTabs(runtimeContext.externalTabs).slice(0, 8);
|
|
796
|
+
return {
|
|
797
|
+
mode: "rover_embed",
|
|
798
|
+
embeddedDomain: String(runtimeContext.embeddedDomain || "").trim() || void 0,
|
|
799
|
+
agentName: normalizeAgentName(runtimeContext.agentName) || "Rover",
|
|
800
|
+
externalNavigationPolicy: runtimeContext.externalNavigationPolicy || config2.externalNavigationPolicy,
|
|
801
|
+
tabIdContract: runtimeContext.tabIdContract || "tree_index_mapped_by_tab_order",
|
|
802
|
+
...compactExternalTabs.length ? { externalTabs: compactExternalTabs } : {}
|
|
803
|
+
};
|
|
804
|
+
}
|
|
805
|
+
function buildExternalPlaceholderPageData(params) {
|
|
806
|
+
const embedded = params.embeddedDomain || "the configured domain";
|
|
807
|
+
const agentName = normalizeAgentName(params.agentName) || "Rover";
|
|
808
|
+
const reason = params.reason || "external_domain_inaccessible";
|
|
809
|
+
const title = params.title || "External Tab (Inaccessible)";
|
|
810
|
+
const url = params.url || "";
|
|
811
|
+
const reasonLine = reason ? ` Reason: ${reason}.` : "";
|
|
812
|
+
return {
|
|
813
|
+
url,
|
|
814
|
+
title,
|
|
815
|
+
contentType: "text/html",
|
|
816
|
+
content: `${agentName} is embedded inside ${embedded}. This external tab is tracked, but live DOM control and accessibility-tree access are unavailable here.${reasonLine}`,
|
|
817
|
+
metadata: {
|
|
818
|
+
inaccessible: true,
|
|
819
|
+
external: true,
|
|
820
|
+
accessMode: "external_placeholder",
|
|
821
|
+
reason,
|
|
822
|
+
logicalTabId: params.tabId
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
}
|
|
683
826
|
function getUserFriendlyTimestamp() {
|
|
684
827
|
const now = /* @__PURE__ */ new Date();
|
|
685
828
|
const year = now.getFullYear();
|
|
@@ -712,8 +855,16 @@ function createAgentContext(config2, bridgeRpc2, tabularStore2) {
|
|
|
712
855
|
}
|
|
713
856
|
const base = (config2.apiBase || DEFAULT_CLOUD_FUNCTIONS_BASE).replace(/\/$/, "");
|
|
714
857
|
const endpoint = base.endsWith("/extensionRouter") ? base : `${base}/extensionRouter`;
|
|
858
|
+
const runtimeContext = buildRoverRuntimeContext(config2);
|
|
859
|
+
const externalWebConfig = normalizeExternalWebConfig(config2.tools?.web);
|
|
860
|
+
const externalPageDataCache = /* @__PURE__ */ new Map();
|
|
861
|
+
const externalPageDataErrorCache = /* @__PURE__ */ new Map();
|
|
862
|
+
let externalPageDataDisabledReason;
|
|
863
|
+
let cachedActiveTabId = 0;
|
|
864
|
+
let cachedActiveTabTs = 0;
|
|
715
865
|
const RETRY_DELAYS = [1e3, 3e3];
|
|
716
866
|
const MAX_RETRIES3 = RETRY_DELAYS.length;
|
|
867
|
+
const ACTIVE_TAB_CACHE_TTL_MS = 250;
|
|
717
868
|
const callExtensionRouter = async (action, data) => {
|
|
718
869
|
const token = config2.apiKey || config2.authToken;
|
|
719
870
|
if (!token) {
|
|
@@ -736,7 +887,13 @@ function createAgentContext(config2, bridgeRpc2, tabularStore2) {
|
|
|
736
887
|
Authorization: `Bearer ${token}`,
|
|
737
888
|
"Content-Type": "application/json"
|
|
738
889
|
},
|
|
739
|
-
body: JSON.stringify({
|
|
890
|
+
body: JSON.stringify({
|
|
891
|
+
action,
|
|
892
|
+
data: data && typeof data === "object" ? {
|
|
893
|
+
...data,
|
|
894
|
+
...!data.runtimeContext && runtimeContext ? { runtimeContext } : {}
|
|
895
|
+
} : data
|
|
896
|
+
}),
|
|
740
897
|
signal: config2.signal
|
|
741
898
|
});
|
|
742
899
|
if (!response.ok) {
|
|
@@ -789,11 +946,135 @@ function createAgentContext(config2, bridgeRpc2, tabularStore2) {
|
|
|
789
946
|
}
|
|
790
947
|
throw lastError || new Error("callExtensionRouter: unexpected retry exhaustion");
|
|
791
948
|
};
|
|
949
|
+
const getExternalPageData = async (url, options) => {
|
|
950
|
+
const normalizedUrl = String(url || "").trim();
|
|
951
|
+
if (!normalizedUrl)
|
|
952
|
+
throw new Error("external page data requires url");
|
|
953
|
+
const cacheKey = normalizedUrl;
|
|
954
|
+
if (externalPageDataCache.has(cacheKey)) {
|
|
955
|
+
return externalPageDataCache.get(cacheKey);
|
|
956
|
+
}
|
|
957
|
+
if (externalPageDataErrorCache.has(cacheKey)) {
|
|
958
|
+
throw new Error(externalPageDataErrorCache.get(cacheKey) || "external page data fetch failed");
|
|
959
|
+
}
|
|
960
|
+
if (externalPageDataDisabledReason) {
|
|
961
|
+
throw new Error(externalPageDataDisabledReason);
|
|
962
|
+
}
|
|
963
|
+
const source = options?.source || "direct_url";
|
|
964
|
+
const payload = {
|
|
965
|
+
url: normalizedUrl,
|
|
966
|
+
source,
|
|
967
|
+
siteId: config2.siteId,
|
|
968
|
+
roverPolicy: {
|
|
969
|
+
allowDomains: externalWebConfig.allowDomains,
|
|
970
|
+
denyDomains: externalWebConfig.denyDomains
|
|
971
|
+
}
|
|
972
|
+
};
|
|
973
|
+
try {
|
|
974
|
+
const response = await callExtensionRouter(SUB_AGENTS.roverExternalPageData, payload);
|
|
975
|
+
const pageData = response?.data || response;
|
|
976
|
+
externalPageDataCache.set(cacheKey, pageData);
|
|
977
|
+
return pageData;
|
|
978
|
+
} catch (error) {
|
|
979
|
+
const envelope = toRoverErrorEnvelope(error, "external page data fetch failed");
|
|
980
|
+
const normalizedMessage = envelope?.message || "external page data fetch failed";
|
|
981
|
+
externalPageDataErrorCache.set(cacheKey, normalizedMessage);
|
|
982
|
+
if (envelope.code === "PERMISSION_DENIED" || envelope.code === "MISSING_API_KEY" || envelope.code === "INVALID_API_KEY") {
|
|
983
|
+
externalPageDataDisabledReason = `${envelope.code}: ${normalizedMessage}`;
|
|
984
|
+
}
|
|
985
|
+
throw error;
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
const getActiveLogicalTabId = async () => {
|
|
989
|
+
const nowMs = Date.now();
|
|
990
|
+
if (cachedActiveTabId > 0 && nowMs - cachedActiveTabTs <= ACTIVE_TAB_CACHE_TTL_MS) {
|
|
991
|
+
return cachedActiveTabId;
|
|
992
|
+
}
|
|
993
|
+
try {
|
|
994
|
+
const context = await bridgeRpc2("getTabContext");
|
|
995
|
+
const active = Number(context?.activeLogicalTabId || context?.logicalTabId || context?.id);
|
|
996
|
+
if (Number.isFinite(active) && active > 0) {
|
|
997
|
+
cachedActiveTabId = active;
|
|
998
|
+
cachedActiveTabTs = nowMs;
|
|
999
|
+
return active;
|
|
1000
|
+
}
|
|
1001
|
+
} catch {
|
|
1002
|
+
}
|
|
1003
|
+
return void 0;
|
|
1004
|
+
};
|
|
792
1005
|
const getPageData = async (tabId, options) => {
|
|
793
|
-
|
|
794
|
-
|
|
1006
|
+
const numericTabId = Number(tabId);
|
|
1007
|
+
const rawOptions = options && typeof options === "object" ? options : void 0;
|
|
1008
|
+
const allowExternalFetch = rawOptions?.__roverAllowExternalFetch === true;
|
|
1009
|
+
const pageConfig = rawOptions && typeof rawOptions === "object" ? Object.fromEntries(Object.entries(rawOptions).filter(([key]) => key !== "__roverAllowExternalFetch")) : void 0;
|
|
1010
|
+
const hasPageConfig = !!pageConfig && Object.keys(pageConfig).length > 0;
|
|
1011
|
+
const localPageData = rawOptions ? await bridgeRpc2("getPageData", hasPageConfig ? { pageConfig, tabId: numericTabId } : { tabId: numericTabId }) : await bridgeRpc2("getPageData", { tabId: numericTabId });
|
|
1012
|
+
const metadata = localPageData?.metadata || {};
|
|
1013
|
+
const isExternal = !!metadata?.external || metadata?.accessMode === "external_placeholder" || metadata?.accessMode === "external_scraped";
|
|
1014
|
+
if (!isExternal) {
|
|
1015
|
+
return localPageData;
|
|
1016
|
+
}
|
|
1017
|
+
const pageUrl = typeof localPageData?.url === "string" ? localPageData.url.trim() : "";
|
|
1018
|
+
if (!externalWebConfig.enableExternalWebContext || externalWebConfig.scrapeMode === "off" || !pageUrl) {
|
|
1019
|
+
return localPageData;
|
|
1020
|
+
}
|
|
1021
|
+
if (externalPageDataDisabledReason) {
|
|
1022
|
+
return buildExternalPlaceholderPageData({
|
|
1023
|
+
tabId: numericTabId,
|
|
1024
|
+
url: pageUrl,
|
|
1025
|
+
title: localPageData?.title,
|
|
1026
|
+
embeddedDomain: runtimeContext?.embeddedDomain,
|
|
1027
|
+
agentName: runtimeContext?.agentName,
|
|
1028
|
+
reason: `cloud_fetch_disabled:${externalPageDataDisabledReason}`
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
const ruleCheck = isAllowedByRules(pageUrl, {
|
|
1032
|
+
allowDomains: externalWebConfig.allowDomains,
|
|
1033
|
+
denyDomains: externalWebConfig.denyDomains
|
|
1034
|
+
});
|
|
1035
|
+
if (!ruleCheck.allowed) {
|
|
1036
|
+
return buildExternalPlaceholderPageData({
|
|
1037
|
+
tabId: numericTabId,
|
|
1038
|
+
url: pageUrl,
|
|
1039
|
+
title: localPageData?.title,
|
|
1040
|
+
embeddedDomain: runtimeContext?.embeddedDomain,
|
|
1041
|
+
agentName: runtimeContext?.agentName,
|
|
1042
|
+
reason: `policy_blocked:${ruleCheck.reason || "blocked"}`
|
|
1043
|
+
});
|
|
1044
|
+
}
|
|
1045
|
+
const activeTabId = await getActiveLogicalTabId();
|
|
1046
|
+
const shouldFetchExternalContext = allowExternalFetch || (activeTabId ? activeTabId === numericTabId : false);
|
|
1047
|
+
if (!shouldFetchExternalContext) {
|
|
1048
|
+
return localPageData;
|
|
1049
|
+
}
|
|
1050
|
+
try {
|
|
1051
|
+
const cloudData = await getExternalPageData(pageUrl, {
|
|
1052
|
+
tabId: numericTabId,
|
|
1053
|
+
source: pageUrl.includes("google.com/search") ? "google_search" : "direct_url"
|
|
1054
|
+
});
|
|
1055
|
+
if (cloudData && typeof cloudData === "object") {
|
|
1056
|
+
return {
|
|
1057
|
+
...cloudData,
|
|
1058
|
+
metadata: {
|
|
1059
|
+
...cloudData.metadata || {},
|
|
1060
|
+
external: true,
|
|
1061
|
+
accessMode: "external_scraped",
|
|
1062
|
+
logicalTabId: numericTabId
|
|
1063
|
+
}
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
return localPageData;
|
|
1067
|
+
} catch (error) {
|
|
1068
|
+
const envelope = toRoverErrorEnvelope(error, "external page data fetch failed");
|
|
1069
|
+
return buildExternalPlaceholderPageData({
|
|
1070
|
+
tabId: numericTabId,
|
|
1071
|
+
url: pageUrl,
|
|
1072
|
+
title: localPageData?.title,
|
|
1073
|
+
embeddedDomain: runtimeContext?.embeddedDomain,
|
|
1074
|
+
agentName: runtimeContext?.agentName,
|
|
1075
|
+
reason: `cloud_fetch_failed:${envelope?.code || "unknown"}`
|
|
1076
|
+
});
|
|
795
1077
|
}
|
|
796
|
-
return bridgeRpc2("getPageData", { tabId });
|
|
797
1078
|
};
|
|
798
1079
|
const userProfile = config2.userProfile || (config2.userContext ? { userContext: config2.userContext } : void 0);
|
|
799
1080
|
const defaultApiToolsConfig = apiMode ? {
|
|
@@ -806,6 +1087,7 @@ function createAgentContext(config2, bridgeRpc2, tabularStore2) {
|
|
|
806
1087
|
llmIntegration,
|
|
807
1088
|
userProfile,
|
|
808
1089
|
getPageData,
|
|
1090
|
+
getExternalPageData,
|
|
809
1091
|
callExtensionRouter,
|
|
810
1092
|
apiMode,
|
|
811
1093
|
apiToolsConfig: normalizeApiToolsConfig(config2.apiToolsConfig ?? defaultApiToolsConfig),
|
|
@@ -1014,26 +1296,6 @@ var GeminiModel;
|
|
|
1014
1296
|
GeminiModel2["PRO"] = "Gemini Pro";
|
|
1015
1297
|
})(GeminiModel || (GeminiModel = {}));
|
|
1016
1298
|
|
|
1017
|
-
// ../shared/dist/lib/types/agent-types.js
|
|
1018
|
-
var SUB_AGENTS;
|
|
1019
|
-
(function(SUB_AGENTS2) {
|
|
1020
|
-
SUB_AGENTS2["enhance"] = "enhance";
|
|
1021
|
-
SUB_AGENTS2["plan"] = "plan";
|
|
1022
|
-
SUB_AGENTS2["actOnTabs"] = "actOnTabs";
|
|
1023
|
-
SUB_AGENTS2["extract"] = "extract";
|
|
1024
|
-
SUB_AGENTS2["infer"] = "infer";
|
|
1025
|
-
SUB_AGENTS2["processTabWorkflows"] = "processTabWorkflows";
|
|
1026
|
-
SUB_AGENTS2["processText"] = "processText";
|
|
1027
|
-
SUB_AGENTS2["createSheetFromData"] = "createSheetFromData";
|
|
1028
|
-
SUB_AGENTS2["queryRtrvrDocs"] = "queryRtrvrDocs";
|
|
1029
|
-
SUB_AGENTS2["googleDocGenerator"] = "googleDocGenerator";
|
|
1030
|
-
SUB_AGENTS2["googleSlidesGenerator"] = "googleSlidesGenerator";
|
|
1031
|
-
SUB_AGENTS2["webpageGenerator"] = "webpageGenerator";
|
|
1032
|
-
SUB_AGENTS2["pdfFiller"] = "pdfFiller";
|
|
1033
|
-
SUB_AGENTS2["customToolGenerator"] = "customToolGenerator";
|
|
1034
|
-
SUB_AGENTS2["getPageData"] = "getPageData";
|
|
1035
|
-
})(SUB_AGENTS || (SUB_AGENTS = {}));
|
|
1036
|
-
|
|
1037
1299
|
// ../shared/dist/lib/system-tools/tools.js
|
|
1038
1300
|
var SystemToolNames;
|
|
1039
1301
|
(function(SystemToolNames2) {
|
|
@@ -1315,6 +1577,127 @@ function formatFunctionResultsIntoPrevSteps(results, functionCalls) {
|
|
|
1315
1577
|
return steps;
|
|
1316
1578
|
}
|
|
1317
1579
|
|
|
1580
|
+
// dist/agent/runtimeTabs.js
|
|
1581
|
+
var DEFAULT_MAX_CONTEXT_TABS = 8;
|
|
1582
|
+
var DEFAULT_DETACHED_EXTERNAL_TAB_MAX_AGE_MS = 9e4;
|
|
1583
|
+
var DEFAULT_STALE_RUNTIME_TAB_MAX_AGE_MS = 45e3;
|
|
1584
|
+
function dedupePositiveTabIds(input) {
|
|
1585
|
+
const seen = /* @__PURE__ */ new Set();
|
|
1586
|
+
const out = [];
|
|
1587
|
+
for (const value of input) {
|
|
1588
|
+
const next = Number(value);
|
|
1589
|
+
if (!Number.isFinite(next) || next <= 0 || seen.has(next))
|
|
1590
|
+
continue;
|
|
1591
|
+
seen.add(next);
|
|
1592
|
+
out.push(next);
|
|
1593
|
+
}
|
|
1594
|
+
return out;
|
|
1595
|
+
}
|
|
1596
|
+
function normalizeAccessMode(value, external) {
|
|
1597
|
+
if (value === "external_scraped" || value === "external_placeholder" || value === "live_dom")
|
|
1598
|
+
return value;
|
|
1599
|
+
return external ? "external_placeholder" : "live_dom";
|
|
1600
|
+
}
|
|
1601
|
+
function normalizeFallbackTabs(input) {
|
|
1602
|
+
return input.map((tab) => ({
|
|
1603
|
+
id: Number(tab?.id),
|
|
1604
|
+
url: typeof tab?.url === "string" ? tab.url : void 0,
|
|
1605
|
+
title: typeof tab?.title === "string" ? tab.title : void 0,
|
|
1606
|
+
external: !!tab?.external,
|
|
1607
|
+
accessMode: normalizeAccessMode(tab?.accessMode, !!tab?.external),
|
|
1608
|
+
inaccessibleReason: typeof tab?.inaccessibleReason === "string" ? tab.inaccessibleReason : void 0
|
|
1609
|
+
})).filter((tab) => Number.isFinite(tab.id) && tab.id > 0);
|
|
1610
|
+
}
|
|
1611
|
+
function normalizeListedTabs(input) {
|
|
1612
|
+
if (!Array.isArray(input))
|
|
1613
|
+
return [];
|
|
1614
|
+
return input.map((tab) => {
|
|
1615
|
+
const id = Number(tab?.logicalTabId || tab?.id);
|
|
1616
|
+
if (!Number.isFinite(id) || id <= 0)
|
|
1617
|
+
return void 0;
|
|
1618
|
+
const external = !!tab?.external;
|
|
1619
|
+
return {
|
|
1620
|
+
id,
|
|
1621
|
+
runtimeId: typeof tab?.runtimeId === "string" ? tab.runtimeId : void 0,
|
|
1622
|
+
url: typeof tab?.url === "string" ? tab.url : void 0,
|
|
1623
|
+
title: typeof tab?.title === "string" ? tab.title : void 0,
|
|
1624
|
+
external,
|
|
1625
|
+
accessMode: normalizeAccessMode(tab?.accessMode, external),
|
|
1626
|
+
inaccessibleReason: typeof tab?.inaccessibleReason === "string" ? tab.inaccessibleReason : void 0,
|
|
1627
|
+
updatedAt: Number(tab?.updatedAt) || 0
|
|
1628
|
+
};
|
|
1629
|
+
}).filter((tab) => !!tab);
|
|
1630
|
+
}
|
|
1631
|
+
async function resolveRuntimeTabs(bridgeRpc2, fallbackTabs, options) {
|
|
1632
|
+
const maxContextTabs = Math.max(1, Number(options?.maxContextTabs) || DEFAULT_MAX_CONTEXT_TABS);
|
|
1633
|
+
const detachedExternalTabMaxAgeMs = Math.max(5e3, Number(options?.detachedExternalTabMaxAgeMs) || DEFAULT_DETACHED_EXTERNAL_TAB_MAX_AGE_MS);
|
|
1634
|
+
const staleRuntimeTabMaxAgeMs = Math.max(1e4, Number(options?.staleRuntimeTabMaxAgeMs) || DEFAULT_STALE_RUNTIME_TAB_MAX_AGE_MS);
|
|
1635
|
+
const fallbackSnapshots = normalizeFallbackTabs(fallbackTabs);
|
|
1636
|
+
const fallbackTabIds = dedupePositiveTabIds(fallbackSnapshots.map((tab) => tab.id));
|
|
1637
|
+
let tabIds = [...fallbackTabIds];
|
|
1638
|
+
let listedTabs = [];
|
|
1639
|
+
if (bridgeRpc2) {
|
|
1640
|
+
try {
|
|
1641
|
+
listedTabs = normalizeListedTabs(await bridgeRpc2("listSessionTabs"));
|
|
1642
|
+
const listedIds = dedupePositiveTabIds(listedTabs.map((tab) => tab.id));
|
|
1643
|
+
if (listedIds.length > 0) {
|
|
1644
|
+
tabIds = listedIds;
|
|
1645
|
+
}
|
|
1646
|
+
} catch {
|
|
1647
|
+
}
|
|
1648
|
+
}
|
|
1649
|
+
let activeTabId = tabIds[0] || fallbackTabIds[0] || 1;
|
|
1650
|
+
if (bridgeRpc2) {
|
|
1651
|
+
try {
|
|
1652
|
+
const context = await bridgeRpc2("getTabContext");
|
|
1653
|
+
const candidate = Number(context?.activeLogicalTabId || context?.logicalTabId || context?.id);
|
|
1654
|
+
if (Number.isFinite(candidate) && candidate > 0) {
|
|
1655
|
+
activeTabId = candidate;
|
|
1656
|
+
}
|
|
1657
|
+
} catch {
|
|
1658
|
+
}
|
|
1659
|
+
}
|
|
1660
|
+
if (!tabIds.length) {
|
|
1661
|
+
tabIds = [activeTabId];
|
|
1662
|
+
} else if (!tabIds.includes(activeTabId)) {
|
|
1663
|
+
tabIds = [activeTabId, ...tabIds];
|
|
1664
|
+
}
|
|
1665
|
+
const nowMs = Date.now();
|
|
1666
|
+
const listedById = /* @__PURE__ */ new Map();
|
|
1667
|
+
for (const tab of listedTabs)
|
|
1668
|
+
listedById.set(tab.id, tab);
|
|
1669
|
+
const prioritized = tabIds.filter((tabId) => {
|
|
1670
|
+
if (tabId === activeTabId)
|
|
1671
|
+
return true;
|
|
1672
|
+
const listed = listedById.get(tabId);
|
|
1673
|
+
if (!listed)
|
|
1674
|
+
return true;
|
|
1675
|
+
if (listed.runtimeId) {
|
|
1676
|
+
return nowMs - (listed.updatedAt || 0) <= staleRuntimeTabMaxAgeMs;
|
|
1677
|
+
}
|
|
1678
|
+
if (!listed.external)
|
|
1679
|
+
return true;
|
|
1680
|
+
return nowMs - (listed.updatedAt || 0) <= detachedExternalTabMaxAgeMs;
|
|
1681
|
+
});
|
|
1682
|
+
let tabOrder = (prioritized.length ? prioritized : tabIds).slice(0, maxContextTabs);
|
|
1683
|
+
if (!tabOrder.includes(activeTabId)) {
|
|
1684
|
+
tabOrder = [activeTabId, ...tabOrder].slice(0, maxContextTabs);
|
|
1685
|
+
}
|
|
1686
|
+
if (!tabOrder.length) {
|
|
1687
|
+
tabOrder = [activeTabId || 1];
|
|
1688
|
+
}
|
|
1689
|
+
const tabMetaById = {};
|
|
1690
|
+
for (const tab of fallbackSnapshots)
|
|
1691
|
+
tabMetaById[tab.id] = tab;
|
|
1692
|
+
for (const tab of listedTabs)
|
|
1693
|
+
tabMetaById[tab.id] = { ...tabMetaById[tab.id] || {}, ...tab };
|
|
1694
|
+
for (const tabId of tabOrder) {
|
|
1695
|
+
if (!tabMetaById[tabId])
|
|
1696
|
+
tabMetaById[tabId] = { id: tabId };
|
|
1697
|
+
}
|
|
1698
|
+
return { tabOrder, activeTabId, tabMetaById };
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1318
1701
|
// dist/agent/actAgent.js
|
|
1319
1702
|
var MAX_RETRIES = 3;
|
|
1320
1703
|
var MAX_ITERATIONS = 25;
|
|
@@ -1326,10 +1709,10 @@ async function executeAgenticSeek(options) {
|
|
|
1326
1709
|
let totalCreditsUsed = 0;
|
|
1327
1710
|
const allWarnings = [];
|
|
1328
1711
|
const accumulatedPrevSteps = Array.isArray(previousSteps) ? previousSteps : [];
|
|
1712
|
+
const fallbackTabs = tabOrder.map((id) => ({ id }));
|
|
1329
1713
|
let retry = 0;
|
|
1330
1714
|
let iterations = 0;
|
|
1331
1715
|
let pageDataOptions;
|
|
1332
|
-
const tabId = tabOrder[0];
|
|
1333
1716
|
while (retry < MAX_RETRIES) {
|
|
1334
1717
|
if (iterations++ >= MAX_ITERATIONS) {
|
|
1335
1718
|
return { error: "Max action iterations reached", prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed };
|
|
@@ -1340,13 +1723,44 @@ async function executeAgenticSeek(options) {
|
|
|
1340
1723
|
try {
|
|
1341
1724
|
await waitWhilePaused(void 0);
|
|
1342
1725
|
onStatusUpdate?.("Analyzing page content...", "Calling seek workflow", "analyze");
|
|
1343
|
-
const
|
|
1726
|
+
const { tabOrder: runtimeTabOrder, activeTabId } = await resolveRuntimeTabs(bridgeRpc2, fallbackTabs);
|
|
1727
|
+
const scopedTabOrder = runtimeTabOrder.length ? runtimeTabOrder : tabOrder;
|
|
1728
|
+
const webPageMap = {};
|
|
1729
|
+
try {
|
|
1730
|
+
webPageMap[activeTabId] = await ctx.getPageData(activeTabId, {
|
|
1731
|
+
...pageDataOptions || {},
|
|
1732
|
+
__roverAllowExternalFetch: true
|
|
1733
|
+
});
|
|
1734
|
+
} catch {
|
|
1735
|
+
retry++;
|
|
1736
|
+
continue;
|
|
1737
|
+
}
|
|
1738
|
+
const backgroundTabIds = scopedTabOrder.filter((currentTabId) => currentTabId !== activeTabId);
|
|
1739
|
+
const backgroundResults = await Promise.all(backgroundTabIds.map(async (currentTabId) => {
|
|
1740
|
+
try {
|
|
1741
|
+
const pageData = await ctx.getPageData(currentTabId);
|
|
1742
|
+
return { tabId: currentTabId, pageData };
|
|
1743
|
+
} catch {
|
|
1744
|
+
return { tabId: currentTabId, pageData: void 0 };
|
|
1745
|
+
}
|
|
1746
|
+
}));
|
|
1747
|
+
for (const result of backgroundResults) {
|
|
1748
|
+
if (result.pageData) {
|
|
1749
|
+
webPageMap[result.tabId] = result.pageData;
|
|
1750
|
+
} else {
|
|
1751
|
+
allWarnings.push(`Could not load page data for tab ${result.tabId}; continuing with remaining tabs.`);
|
|
1752
|
+
}
|
|
1753
|
+
}
|
|
1754
|
+
if (!webPageMap[activeTabId]) {
|
|
1755
|
+
retry++;
|
|
1756
|
+
continue;
|
|
1757
|
+
}
|
|
1344
1758
|
const request = {
|
|
1345
1759
|
siteId: ctx.siteId,
|
|
1346
1760
|
customTabWorkflow: "Seek",
|
|
1347
|
-
webPageMap
|
|
1348
|
-
tabOrder:
|
|
1349
|
-
activeTabId
|
|
1761
|
+
webPageMap,
|
|
1762
|
+
tabOrder: scopedTabOrder,
|
|
1763
|
+
activeTabId,
|
|
1350
1764
|
userInput,
|
|
1351
1765
|
dataJsonSchema: schema,
|
|
1352
1766
|
files,
|
|
@@ -1368,7 +1782,7 @@ async function executeAgenticSeek(options) {
|
|
|
1368
1782
|
}
|
|
1369
1783
|
const data = response.data;
|
|
1370
1784
|
totalCreditsUsed += data?.creditsUsed || 0;
|
|
1371
|
-
const tabResponse = data?.tabResponses?.[
|
|
1785
|
+
const tabResponse = data?.tabResponses?.[activeTabId];
|
|
1372
1786
|
if (!tabResponse) {
|
|
1373
1787
|
retry++;
|
|
1374
1788
|
continue;
|
|
@@ -1382,7 +1796,7 @@ async function executeAgenticSeek(options) {
|
|
|
1382
1796
|
const processResult = await processActionResponse({
|
|
1383
1797
|
request,
|
|
1384
1798
|
response: tabResponse,
|
|
1385
|
-
tabId,
|
|
1799
|
+
tabId: activeTabId,
|
|
1386
1800
|
prevSteps: accumulatedPrevSteps,
|
|
1387
1801
|
thought: tabResponse.thought,
|
|
1388
1802
|
bridgeRpc: bridgeRpc2,
|
|
@@ -1447,20 +1861,28 @@ async function executeExtract(options) {
|
|
|
1447
1861
|
}
|
|
1448
1862
|
let totalCreditsUsed = 0;
|
|
1449
1863
|
const warnings = [];
|
|
1450
|
-
const
|
|
1864
|
+
const fallbackTabs = tabOrder.map((id) => ({ id }));
|
|
1451
1865
|
const prevSteps = Array.isArray(previousSteps) ? previousSteps : [];
|
|
1452
1866
|
let pageDataOptions;
|
|
1453
1867
|
for (let retry = 0; retry < MAX_RETRIES2; retry++) {
|
|
1454
1868
|
await waitWhilePaused(void 0);
|
|
1455
|
-
const
|
|
1869
|
+
const { activeTabId } = await resolveRuntimeTabs(bridgeRpc2, fallbackTabs);
|
|
1870
|
+
const tabId = activeTabId;
|
|
1871
|
+
let pageData;
|
|
1872
|
+
try {
|
|
1873
|
+
pageData = await ctx.getPageData(tabId, pageDataOptions);
|
|
1874
|
+
} catch {
|
|
1875
|
+
warnings.push(`Could not load page data for tab ${tabId}; retrying.`);
|
|
1876
|
+
continue;
|
|
1877
|
+
}
|
|
1456
1878
|
const request = {
|
|
1457
1879
|
siteId: ctx.siteId,
|
|
1458
1880
|
userInput,
|
|
1459
1881
|
schema,
|
|
1460
1882
|
outputDestination,
|
|
1461
1883
|
schemaHeaderSheetInfo,
|
|
1462
|
-
webPageMap: { [
|
|
1463
|
-
tabOrder: [
|
|
1884
|
+
webPageMap: { [activeTabId]: pageData },
|
|
1885
|
+
tabOrder: [activeTabId],
|
|
1464
1886
|
plannerPrevSteps,
|
|
1465
1887
|
llmIntegration: ctx.llmIntegration,
|
|
1466
1888
|
apiMode: ctx.apiMode,
|
|
@@ -1491,12 +1913,12 @@ async function executeExtract(options) {
|
|
|
1491
1913
|
warnings
|
|
1492
1914
|
};
|
|
1493
1915
|
}
|
|
1494
|
-
const tabResponse = data?.tabResponses?.[
|
|
1916
|
+
const tabResponse = data?.tabResponses?.[activeTabId];
|
|
1495
1917
|
if (tabResponse) {
|
|
1496
1918
|
const processResult = await processActionResponse({
|
|
1497
1919
|
request,
|
|
1498
1920
|
response: tabResponse,
|
|
1499
|
-
tabId,
|
|
1921
|
+
tabId: activeTabId,
|
|
1500
1922
|
prevSteps,
|
|
1501
1923
|
thought: tabResponse.thought,
|
|
1502
1924
|
bridgeRpc: bridgeRpc2,
|
|
@@ -2151,6 +2573,8 @@ function resolvePath(obj, path) {
|
|
|
2151
2573
|
}
|
|
2152
2574
|
|
|
2153
2575
|
// dist/agent/toolExecutor.js
|
|
2576
|
+
var MAX_AGENT_CHATLOG_ENTRIES = 24;
|
|
2577
|
+
var MAX_AGENT_CHATLOG_MESSAGE_CHARS = 1e3;
|
|
2154
2578
|
function unsupportedToolResult(toolName, message) {
|
|
2155
2579
|
return {
|
|
2156
2580
|
error: message,
|
|
@@ -2180,10 +2604,18 @@ function buildStructuredErrorOutput(envelope) {
|
|
|
2180
2604
|
}
|
|
2181
2605
|
function normalizeAgentLog(agentLog) {
|
|
2182
2606
|
const prevSteps = Array.isArray(agentLog?.prevSteps) ? agentLog.prevSteps : [];
|
|
2607
|
+
const sanitizeMessage = (value) => {
|
|
2608
|
+
const normalized = String(value || "").replace(/\s+/g, " ").trim();
|
|
2609
|
+
if (!normalized)
|
|
2610
|
+
return "";
|
|
2611
|
+
if (normalized.length <= MAX_AGENT_CHATLOG_MESSAGE_CHARS)
|
|
2612
|
+
return normalized;
|
|
2613
|
+
return `${normalized.slice(0, MAX_AGENT_CHATLOG_MESSAGE_CHARS - 1)}\u2026`;
|
|
2614
|
+
};
|
|
2183
2615
|
const chatLog = Array.isArray(agentLog?.chatLog) ? agentLog.chatLog.map((entry) => ({
|
|
2184
2616
|
role: entry?.role === "user" ? "user" : "model",
|
|
2185
|
-
message: typeof entry?.message === "string" ? entry.message : ""
|
|
2186
|
-
})).filter((entry) => !!entry.message) : [];
|
|
2617
|
+
message: typeof entry?.message === "string" ? sanitizeMessage(entry.message) : ""
|
|
2618
|
+
})).filter((entry) => !!entry.message).slice(-MAX_AGENT_CHATLOG_ENTRIES) : [];
|
|
2187
2619
|
return { prevSteps, chatLog };
|
|
2188
2620
|
}
|
|
2189
2621
|
async function executeToolFromPlan(context) {
|
|
@@ -2192,7 +2624,9 @@ async function executeToolFromPlan(context) {
|
|
|
2192
2624
|
if (!effectiveCtx) {
|
|
2193
2625
|
return { error: "Agent context unavailable" };
|
|
2194
2626
|
}
|
|
2195
|
-
const
|
|
2627
|
+
const fallbackTabs = Array.isArray(tabs) && tabs.length ? tabs : [{ id: 1 }];
|
|
2628
|
+
const resolvedTabs = await resolveRuntimeTabs(bridgeRpc2, fallbackTabs);
|
|
2629
|
+
const tabOrder = resolvedTabs.tabOrder.length ? resolvedTabs.tabOrder : fallbackTabs.map((tab) => tab.id);
|
|
2196
2630
|
const effectiveAgentLog = normalizeAgentLog(agentLog);
|
|
2197
2631
|
try {
|
|
2198
2632
|
switch (toolName) {
|
|
@@ -2999,18 +3433,50 @@ async function executeCustomToolGenerator({ userInput, plannerPrevSteps, files,
|
|
|
2999
3433
|
|
|
3000
3434
|
// dist/agent/plannerAgent.js
|
|
3001
3435
|
var MAX_PLANNER_DEPTH = 15;
|
|
3436
|
+
var MAX_CHATLOG_ENTRIES = 24;
|
|
3437
|
+
var MAX_CHATLOG_MESSAGE_CHARS = 1e3;
|
|
3438
|
+
function normalizeChatLog(entries) {
|
|
3439
|
+
if (!Array.isArray(entries) || !entries.length)
|
|
3440
|
+
return [];
|
|
3441
|
+
return entries.map((entry) => ({
|
|
3442
|
+
role: entry?.role === "user" ? "user" : "model",
|
|
3443
|
+
message: String(entry?.message || "").replace(/\s+/g, " ").trim()
|
|
3444
|
+
})).filter((entry) => !!entry.message).map((entry) => ({
|
|
3445
|
+
role: entry.role,
|
|
3446
|
+
message: entry.message.length > MAX_CHATLOG_MESSAGE_CHARS ? `${entry.message.slice(0, MAX_CHATLOG_MESSAGE_CHARS - 1)}\u2026` : entry.message
|
|
3447
|
+
})).slice(-MAX_CHATLOG_ENTRIES);
|
|
3448
|
+
}
|
|
3002
3449
|
async function executePlanner(options) {
|
|
3003
|
-
const { userInput, tabs, previousMessages = [], trajectoryId: trajectoryId2, previousSteps = [], files, continuePlanning = false, recordingContext, driveAuthToken, agentLog, lastToolPreviousSteps, ctx, functionDeclarations } = options;
|
|
3004
|
-
const
|
|
3450
|
+
const { userInput, tabs, previousMessages = [], trajectoryId: trajectoryId2, previousSteps = [], files, continuePlanning = false, recordingContext, driveAuthToken, agentLog, lastToolPreviousSteps, ctx, bridgeRpc: bridgeRpc2, functionDeclarations } = options;
|
|
3451
|
+
const fallbackTabs = Array.isArray(tabs) && tabs.length ? tabs : [{ id: 1 }];
|
|
3452
|
+
const resolvedTabs = await resolveRuntimeTabs(bridgeRpc2, fallbackTabs);
|
|
3453
|
+
const tabOrder = resolvedTabs.tabOrder.length ? resolvedTabs.tabOrder : fallbackTabs.map((tab) => tab.id);
|
|
3454
|
+
const activeTabId = resolvedTabs.activeTabId;
|
|
3455
|
+
const tabMetaById = resolvedTabs.tabMetaById;
|
|
3005
3456
|
const webPageMap = {};
|
|
3006
|
-
|
|
3457
|
+
const loadPageData = async (tabId, options2) => {
|
|
3007
3458
|
try {
|
|
3008
|
-
|
|
3459
|
+
return await ctx.getPageData(tabId, {
|
|
3460
|
+
onlyTextContent: false,
|
|
3461
|
+
...options2?.allowExternalFetch ? { __roverAllowExternalFetch: true } : {}
|
|
3462
|
+
});
|
|
3009
3463
|
} catch {
|
|
3010
|
-
|
|
3464
|
+
const tab = tabMetaById[tabId];
|
|
3465
|
+
return {
|
|
3466
|
+
url: tab?.url || "",
|
|
3467
|
+
title: tab?.title || (tab?.external ? "External Tab (Inaccessible)" : ""),
|
|
3468
|
+
content: "",
|
|
3469
|
+
contentType: "text/html"
|
|
3470
|
+
};
|
|
3011
3471
|
}
|
|
3472
|
+
};
|
|
3473
|
+
webPageMap[activeTabId] = await loadPageData(activeTabId, { allowExternalFetch: true });
|
|
3474
|
+
const backgroundTabIds = tabOrder.filter((tabId) => tabId !== activeTabId);
|
|
3475
|
+
const backgroundResults = await Promise.all(backgroundTabIds.map(async (tabId) => ({ tabId, pageData: await loadPageData(tabId) })));
|
|
3476
|
+
for (const { tabId, pageData } of backgroundResults) {
|
|
3477
|
+
webPageMap[tabId] = pageData;
|
|
3012
3478
|
}
|
|
3013
|
-
const chatLog = agentLog?.chatLog?.length ? agentLog.chatLog : previousMessages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => ({ role: m.role === "user" ? "user" : "model", message: m.content }));
|
|
3479
|
+
const chatLog = agentLog?.chatLog?.length ? normalizeChatLog(agentLog.chatLog) : normalizeChatLog(previousMessages.filter((m) => m.role === "user" || m.role === "assistant").map((m) => ({ role: m.role === "user" ? "user" : "model", message: m.content })));
|
|
3014
3480
|
const request = {
|
|
3015
3481
|
siteId: ctx.siteId,
|
|
3016
3482
|
userInput,
|
|
@@ -3687,7 +4153,98 @@ var activeAbortController = null;
|
|
|
3687
4153
|
var lastStatusKey = "";
|
|
3688
4154
|
var seenStatusKeys = /* @__PURE__ */ new Set();
|
|
3689
4155
|
var completedRunIds = /* @__PURE__ */ new Set();
|
|
4156
|
+
var completedRunOutcomes = /* @__PURE__ */ new Map();
|
|
3690
4157
|
var RPC_TIMEOUT_MS = 3e4;
|
|
4158
|
+
var DETACHED_EXTERNAL_TAB_MAX_AGE_MS = 9e4;
|
|
4159
|
+
var MAX_CHATLOG_ENTRIES2 = 24;
|
|
4160
|
+
var MAX_CHATLOG_MESSAGE_CHARS2 = 1e3;
|
|
4161
|
+
function resolveEmbeddedDomain(config2) {
|
|
4162
|
+
const domains = Array.isArray(config2?.allowedDomains) ? config2.allowedDomains : [];
|
|
4163
|
+
const first = String(domains[0] || "").trim();
|
|
4164
|
+
if (!first)
|
|
4165
|
+
return void 0;
|
|
4166
|
+
return first.replace(/^\*?\./, "").replace(/^=/, "");
|
|
4167
|
+
}
|
|
4168
|
+
function resolveAgentName(config2) {
|
|
4169
|
+
const raw = String(config2?.ui?.agent?.name || "").trim();
|
|
4170
|
+
if (!raw)
|
|
4171
|
+
return "Rover";
|
|
4172
|
+
return raw.slice(0, 64);
|
|
4173
|
+
}
|
|
4174
|
+
function hostFromUrl2(url) {
|
|
4175
|
+
const raw = String(url || "").trim();
|
|
4176
|
+
if (!raw)
|
|
4177
|
+
return void 0;
|
|
4178
|
+
try {
|
|
4179
|
+
return new URL(raw).hostname.toLowerCase();
|
|
4180
|
+
} catch {
|
|
4181
|
+
return void 0;
|
|
4182
|
+
}
|
|
4183
|
+
}
|
|
4184
|
+
function extractWebToolsConfig(config2) {
|
|
4185
|
+
if (!config2?.tools || Array.isArray(config2.tools))
|
|
4186
|
+
return void 0;
|
|
4187
|
+
return config2.tools.web;
|
|
4188
|
+
}
|
|
4189
|
+
function buildRoverRuntimeContext2(params) {
|
|
4190
|
+
const externalTabs = params.tabs.map((tab, index) => {
|
|
4191
|
+
if (!tab.external && tab.accessMode !== "external_placeholder" && tab.accessMode !== "external_scraped") {
|
|
4192
|
+
return void 0;
|
|
4193
|
+
}
|
|
4194
|
+
const accessMode = tab.accessMode === "external_scraped" ? "external_scraped" : "external_placeholder";
|
|
4195
|
+
return {
|
|
4196
|
+
tabId: index,
|
|
4197
|
+
host: hostFromUrl2(tab.url),
|
|
4198
|
+
title: String(tab.title || "").trim() || void 0,
|
|
4199
|
+
accessMode,
|
|
4200
|
+
reason: String(tab.inaccessibleReason || "").trim() || void 0
|
|
4201
|
+
};
|
|
4202
|
+
}).filter((tab) => !!tab).slice(0, 8);
|
|
4203
|
+
return {
|
|
4204
|
+
mode: "rover_embed",
|
|
4205
|
+
embeddedDomain: params.embeddedDomain,
|
|
4206
|
+
agentName: params.agentName,
|
|
4207
|
+
externalNavigationPolicy: params.externalNavigationPolicy,
|
|
4208
|
+
tabIdContract: "tree_index_mapped_by_tab_order",
|
|
4209
|
+
...externalTabs.length ? { externalTabs } : {}
|
|
4210
|
+
};
|
|
4211
|
+
}
|
|
4212
|
+
function mergeWorkerTools(current, incoming) {
|
|
4213
|
+
if (incoming === void 0)
|
|
4214
|
+
return current;
|
|
4215
|
+
if (Array.isArray(incoming))
|
|
4216
|
+
return incoming;
|
|
4217
|
+
if (Array.isArray(current)) {
|
|
4218
|
+
return {
|
|
4219
|
+
...incoming,
|
|
4220
|
+
client: incoming.client ?? current,
|
|
4221
|
+
web: {
|
|
4222
|
+
...incoming.web || {}
|
|
4223
|
+
}
|
|
4224
|
+
};
|
|
4225
|
+
}
|
|
4226
|
+
return {
|
|
4227
|
+
...current,
|
|
4228
|
+
...incoming,
|
|
4229
|
+
client: incoming.client ?? current?.client,
|
|
4230
|
+
web: {
|
|
4231
|
+
...current?.web || {},
|
|
4232
|
+
...incoming.web || {}
|
|
4233
|
+
}
|
|
4234
|
+
};
|
|
4235
|
+
}
|
|
4236
|
+
function mergeWorkerUi(current, incoming) {
|
|
4237
|
+
if (!incoming)
|
|
4238
|
+
return current;
|
|
4239
|
+
return {
|
|
4240
|
+
...current,
|
|
4241
|
+
...incoming,
|
|
4242
|
+
agent: {
|
|
4243
|
+
...current?.agent || {},
|
|
4244
|
+
...incoming.agent || {}
|
|
4245
|
+
}
|
|
4246
|
+
};
|
|
4247
|
+
}
|
|
3691
4248
|
function createRpcClient(port) {
|
|
3692
4249
|
const pending = /* @__PURE__ */ new Map();
|
|
3693
4250
|
port.onmessage = (ev) => {
|
|
@@ -3725,16 +4282,18 @@ async function getCurrentTab() {
|
|
|
3725
4282
|
return {
|
|
3726
4283
|
id: Number(tabContext.logicalTabId || tabContext.id || 1),
|
|
3727
4284
|
url: tabContext.url,
|
|
3728
|
-
title: tabContext.title
|
|
4285
|
+
title: tabContext.title,
|
|
4286
|
+
external: false,
|
|
4287
|
+
accessMode: "live_dom"
|
|
3729
4288
|
};
|
|
3730
4289
|
}
|
|
3731
4290
|
} catch {
|
|
3732
4291
|
}
|
|
3733
4292
|
try {
|
|
3734
4293
|
const pageData = await bridgeRpc("getPageData");
|
|
3735
|
-
return { id: 1, url: pageData?.url, title: pageData?.title };
|
|
4294
|
+
return { id: 1, url: pageData?.url, title: pageData?.title, external: false, accessMode: "live_dom" };
|
|
3736
4295
|
} catch {
|
|
3737
|
-
return { id: 1 };
|
|
4296
|
+
return { id: 1, external: false, accessMode: "live_dom" };
|
|
3738
4297
|
}
|
|
3739
4298
|
}
|
|
3740
4299
|
async function getKnownTabs() {
|
|
@@ -3743,11 +4302,32 @@ async function getKnownTabs() {
|
|
|
3743
4302
|
try {
|
|
3744
4303
|
const listed = await bridgeRpc("listSessionTabs");
|
|
3745
4304
|
if (Array.isArray(listed) && listed.length > 0) {
|
|
3746
|
-
const
|
|
3747
|
-
|
|
3748
|
-
|
|
3749
|
-
|
|
3750
|
-
|
|
4305
|
+
const nowMs = Date.now();
|
|
4306
|
+
const mapped = listed.map((tab) => {
|
|
4307
|
+
const id = Number(tab?.logicalTabId || tab?.id || 0);
|
|
4308
|
+
const external = !!tab?.external;
|
|
4309
|
+
return {
|
|
4310
|
+
id,
|
|
4311
|
+
runtimeId: typeof tab?.runtimeId === "string" ? tab.runtimeId : void 0,
|
|
4312
|
+
updatedAt: Number(tab?.updatedAt) || 0,
|
|
4313
|
+
url: typeof tab?.url === "string" ? tab.url : void 0,
|
|
4314
|
+
title: typeof tab?.title === "string" ? tab.title : void 0,
|
|
4315
|
+
external,
|
|
4316
|
+
accessMode: tab?.accessMode === "external_scraped" || tab?.accessMode === "external_placeholder" ? tab.accessMode : external ? "external_placeholder" : "live_dom",
|
|
4317
|
+
inaccessibleReason: typeof tab?.inaccessibleReason === "string" ? tab.inaccessibleReason : void 0
|
|
4318
|
+
};
|
|
4319
|
+
}).filter((tab) => Number.isFinite(tab.id) && tab.id > 0).filter((tab) => {
|
|
4320
|
+
if (!tab.external || tab.runtimeId)
|
|
4321
|
+
return true;
|
|
4322
|
+
return nowMs - (tab.updatedAt || 0) <= DETACHED_EXTERNAL_TAB_MAX_AGE_MS;
|
|
4323
|
+
}).map((tab) => ({
|
|
4324
|
+
id: tab.id,
|
|
4325
|
+
url: tab.url,
|
|
4326
|
+
title: tab.title,
|
|
4327
|
+
external: tab.external,
|
|
4328
|
+
accessMode: tab.accessMode,
|
|
4329
|
+
inaccessibleReason: tab.inaccessibleReason
|
|
4330
|
+
}));
|
|
3751
4331
|
if (mapped.length)
|
|
3752
4332
|
return mapped;
|
|
3753
4333
|
}
|
|
@@ -4112,19 +4692,46 @@ function maybePostNavigationGuardrailFromToolResult(toolResult) {
|
|
|
4112
4692
|
});
|
|
4113
4693
|
}
|
|
4114
4694
|
function buildChatLogFromHistory(input, currentUserInput) {
|
|
4695
|
+
const sanitizeChatText = (raw, role) => {
|
|
4696
|
+
let text = String(raw || "").replace(/\s+/g, " ").trim();
|
|
4697
|
+
if (!text)
|
|
4698
|
+
return "";
|
|
4699
|
+
if (role === "assistant") {
|
|
4700
|
+
if (/^\w[\w_]*:\s*\{/.test(text) || /^\[error\]/i.test(text) || /"success":\s*false/.test(text)) {
|
|
4701
|
+
const firstSentence = text.split(/(?<=\.)\s+/)[0] || text;
|
|
4702
|
+
text = firstSentence.trim();
|
|
4703
|
+
}
|
|
4704
|
+
}
|
|
4705
|
+
if (text.length > MAX_CHATLOG_MESSAGE_CHARS2) {
|
|
4706
|
+
text = `${text.slice(0, MAX_CHATLOG_MESSAGE_CHARS2 - 1)}\u2026`;
|
|
4707
|
+
}
|
|
4708
|
+
return text;
|
|
4709
|
+
};
|
|
4710
|
+
const normalizedCurrentUserInput = currentUserInput ? sanitizeChatText(currentUserInput, "user") : "";
|
|
4115
4711
|
const entries = input.filter((message) => message.role === "user" || message.role === "assistant").map((message) => ({
|
|
4116
4712
|
role: message.role,
|
|
4117
|
-
content: String(message.content || "")
|
|
4118
|
-
}));
|
|
4119
|
-
if (
|
|
4713
|
+
content: sanitizeChatText(String(message.content || ""), message.role)
|
|
4714
|
+
})).filter((message) => !!message.content);
|
|
4715
|
+
if (normalizedCurrentUserInput) {
|
|
4120
4716
|
for (let i = entries.length - 1; i >= 0; i -= 1) {
|
|
4121
|
-
if (entries[i].role === "user" && entries[i].content ===
|
|
4717
|
+
if (entries[i].role === "user" && entries[i].content === normalizedCurrentUserInput) {
|
|
4122
4718
|
entries.splice(i, 1);
|
|
4123
4719
|
break;
|
|
4124
4720
|
}
|
|
4125
4721
|
}
|
|
4126
4722
|
}
|
|
4127
|
-
|
|
4723
|
+
const deduped = [];
|
|
4724
|
+
for (const entry of entries) {
|
|
4725
|
+
const previous = deduped[deduped.length - 1];
|
|
4726
|
+
if (previous && previous.role === entry.role && previous.content === entry.content)
|
|
4727
|
+
continue;
|
|
4728
|
+
deduped.push(entry);
|
|
4729
|
+
}
|
|
4730
|
+
const tail = deduped.slice(-MAX_CHATLOG_ENTRIES2);
|
|
4731
|
+
while (tail.length > 1 && tail[0]?.role !== "user") {
|
|
4732
|
+
tail.shift();
|
|
4733
|
+
}
|
|
4734
|
+
return tail.map((message) => ({
|
|
4128
4735
|
role: message.role === "user" ? "user" : "model",
|
|
4129
4736
|
message: message.content
|
|
4130
4737
|
}));
|
|
@@ -4222,6 +4829,9 @@ async function waitForNewTabReady(logicalTabId, timeoutMs = 1e4) {
|
|
|
4222
4829
|
const tabs = await bridgeRpc("listSessionTabs");
|
|
4223
4830
|
if (Array.isArray(tabs)) {
|
|
4224
4831
|
const target = tabs.find((t) => Number(t?.logicalTabId) === logicalTabId);
|
|
4832
|
+
if (target?.external) {
|
|
4833
|
+
return true;
|
|
4834
|
+
}
|
|
4225
4835
|
if (target?.runtimeId) {
|
|
4226
4836
|
await new Promise((resolve) => setTimeout(resolve, 1e3));
|
|
4227
4837
|
return true;
|
|
@@ -4242,6 +4852,65 @@ function detectOpenedTabFromToolResult(result) {
|
|
|
4242
4852
|
}
|
|
4243
4853
|
return void 0;
|
|
4244
4854
|
}
|
|
4855
|
+
function deriveDirectToolRunOutcome(result) {
|
|
4856
|
+
if (!result || typeof result !== "object") {
|
|
4857
|
+
return { taskComplete: false };
|
|
4858
|
+
}
|
|
4859
|
+
if (result.error) {
|
|
4860
|
+
return { taskComplete: false };
|
|
4861
|
+
}
|
|
4862
|
+
const output = result.output;
|
|
4863
|
+
if (output && typeof output === "object") {
|
|
4864
|
+
if (Array.isArray(output)) {
|
|
4865
|
+
return { taskComplete: true };
|
|
4866
|
+
}
|
|
4867
|
+
if (output.needsUserInput === true || output.waitingForUserInput === true) {
|
|
4868
|
+
return { taskComplete: false, needsUserInput: true };
|
|
4869
|
+
}
|
|
4870
|
+
if (Array.isArray(output.questions) && output.questions.length > 0) {
|
|
4871
|
+
return { taskComplete: false, needsUserInput: true };
|
|
4872
|
+
}
|
|
4873
|
+
if (typeof output.taskComplete === "boolean") {
|
|
4874
|
+
return { taskComplete: !!output.taskComplete };
|
|
4875
|
+
}
|
|
4876
|
+
const taskStatus = String(output.taskStatus || output.status || "").toLowerCase();
|
|
4877
|
+
if (taskStatus) {
|
|
4878
|
+
if (taskStatus === "waiting_input" || taskStatus === "needs_input" || taskStatus === "pending_user_input") {
|
|
4879
|
+
return { taskComplete: false, needsUserInput: true };
|
|
4880
|
+
}
|
|
4881
|
+
if (taskStatus === "running" || taskStatus === "in_progress" || taskStatus === "pending") {
|
|
4882
|
+
return { taskComplete: false };
|
|
4883
|
+
}
|
|
4884
|
+
if (taskStatus === "completed" || taskStatus === "complete" || taskStatus === "done" || taskStatus === "success") {
|
|
4885
|
+
return { taskComplete: true };
|
|
4886
|
+
}
|
|
4887
|
+
}
|
|
4888
|
+
if (output.success === false) {
|
|
4889
|
+
return { taskComplete: false };
|
|
4890
|
+
}
|
|
4891
|
+
}
|
|
4892
|
+
if (output != null) {
|
|
4893
|
+
return { taskComplete: true };
|
|
4894
|
+
}
|
|
4895
|
+
return { taskComplete: false };
|
|
4896
|
+
}
|
|
4897
|
+
function normalizeRunOutcome(outcome) {
|
|
4898
|
+
if (!outcome || typeof outcome !== "object") {
|
|
4899
|
+
return { taskComplete: false, needsUserInput: false };
|
|
4900
|
+
}
|
|
4901
|
+
const needsUserInput = outcome.needsUserInput === true;
|
|
4902
|
+
const taskComplete = outcome.taskComplete === true && !needsUserInput;
|
|
4903
|
+
return {
|
|
4904
|
+
route: outcome.route,
|
|
4905
|
+
taskComplete,
|
|
4906
|
+
needsUserInput
|
|
4907
|
+
};
|
|
4908
|
+
}
|
|
4909
|
+
function clearTaskScopedContextAfterCompletion() {
|
|
4910
|
+
history.length = 0;
|
|
4911
|
+
plannerHistory = [];
|
|
4912
|
+
agentPrevSteps = [];
|
|
4913
|
+
}
|
|
4245
4914
|
async function maybeWaitForNewTab(result) {
|
|
4246
4915
|
const logicalTabId = detectOpenedTabFromToolResult(result);
|
|
4247
4916
|
if (logicalTabId) {
|
|
@@ -4261,10 +4930,52 @@ async function handleUserMessage(text, options) {
|
|
|
4261
4930
|
postStateSnapshot();
|
|
4262
4931
|
}
|
|
4263
4932
|
const tabs = await getKnownTabs();
|
|
4933
|
+
const fallbackTabs = tabs.length > 0 ? tabs : [
|
|
4934
|
+
{
|
|
4935
|
+
id: 1,
|
|
4936
|
+
external: false,
|
|
4937
|
+
accessMode: "live_dom"
|
|
4938
|
+
}
|
|
4939
|
+
];
|
|
4940
|
+
const resolvedTabs = await resolveRuntimeTabs(bridgeRpc, fallbackTabs);
|
|
4941
|
+
const tabsById = new Map(tabs.map((tab) => [tab.id, tab]));
|
|
4942
|
+
const orderedTabs = resolvedTabs.tabOrder.map((tabId) => {
|
|
4943
|
+
const knownTab = tabsById.get(tabId);
|
|
4944
|
+
if (knownTab)
|
|
4945
|
+
return knownTab;
|
|
4946
|
+
const tabMeta = resolvedTabs.tabMetaById[tabId];
|
|
4947
|
+
if (!tabMeta)
|
|
4948
|
+
return { id: tabId };
|
|
4949
|
+
const external = !!tabMeta.external;
|
|
4950
|
+
return {
|
|
4951
|
+
id: tabId,
|
|
4952
|
+
url: tabMeta.url,
|
|
4953
|
+
title: tabMeta.title,
|
|
4954
|
+
external,
|
|
4955
|
+
accessMode: tabMeta.accessMode || (external ? "external_placeholder" : "live_dom"),
|
|
4956
|
+
inaccessibleReason: tabMeta.inaccessibleReason
|
|
4957
|
+
};
|
|
4958
|
+
}).filter((tab) => Number.isFinite(tab.id) && tab.id > 0);
|
|
4959
|
+
const tabsForRun = orderedTabs.length > 0 ? orderedTabs : fallbackTabs;
|
|
4264
4960
|
if (!tabularStore) {
|
|
4265
4961
|
tabularStore = new TabularStore(`rover-${trajectoryId}`);
|
|
4266
4962
|
}
|
|
4267
|
-
const
|
|
4963
|
+
const embeddedDomain = resolveEmbeddedDomain(config);
|
|
4964
|
+
const agentName = resolveAgentName(config);
|
|
4965
|
+
const runtimeContext = buildRoverRuntimeContext2({
|
|
4966
|
+
tabs: tabsForRun,
|
|
4967
|
+
embeddedDomain,
|
|
4968
|
+
agentName,
|
|
4969
|
+
externalNavigationPolicy: config.externalNavigationPolicy
|
|
4970
|
+
});
|
|
4971
|
+
const ctx = createAgentContext({
|
|
4972
|
+
...config,
|
|
4973
|
+
signal: activeAbortController?.signal,
|
|
4974
|
+
runtimeContext,
|
|
4975
|
+
tools: {
|
|
4976
|
+
web: extractWebToolsConfig(config)
|
|
4977
|
+
}
|
|
4978
|
+
}, bridgeRpc, tabularStore);
|
|
4268
4979
|
const currentRunId = activeRun?.runId;
|
|
4269
4980
|
ctx.isCancelled = () => cancelledRunId === currentRunId;
|
|
4270
4981
|
const functionDeclarations = dedupeFunctionDeclarations(removePlannerNameCollisions(toolRegistry.getFunctionDeclarations()));
|
|
@@ -4278,7 +4989,7 @@ async function handleUserMessage(text, options) {
|
|
|
4278
4989
|
postStateSnapshot();
|
|
4279
4990
|
};
|
|
4280
4991
|
const result = await handleSendMessageWithFunctions(text, {
|
|
4281
|
-
tabs,
|
|
4992
|
+
tabs: tabsForRun,
|
|
4282
4993
|
previousMessages: history,
|
|
4283
4994
|
trajectoryId,
|
|
4284
4995
|
files: [],
|
|
@@ -4308,7 +5019,7 @@ async function handleUserMessage(text, options) {
|
|
|
4308
5019
|
history.push({ role: "assistant", content: errorMsg });
|
|
4309
5020
|
postStatus("Execution failed", errorPayload.error.message, "complete");
|
|
4310
5021
|
postStateSnapshot();
|
|
4311
|
-
return result.route;
|
|
5022
|
+
return { route: result.route, taskComplete: false };
|
|
4312
5023
|
}
|
|
4313
5024
|
if (result.executedFunctions?.length) {
|
|
4314
5025
|
for (const fn of result.executedFunctions) {
|
|
@@ -4323,7 +5034,7 @@ async function handleUserMessage(text, options) {
|
|
|
4323
5034
|
history.push({ role: "assistant", content: msg });
|
|
4324
5035
|
postStatus("Execution completed", "Function calls finished", "complete");
|
|
4325
5036
|
postStateSnapshot();
|
|
4326
|
-
return result.route;
|
|
5037
|
+
return { route: result.route, taskComplete: true };
|
|
4327
5038
|
}
|
|
4328
5039
|
if (result.directToolResult) {
|
|
4329
5040
|
await maybeWaitForNewTab(result.directToolResult);
|
|
@@ -4341,7 +5052,12 @@ async function handleUserMessage(text, options) {
|
|
|
4341
5052
|
history.push({ role: "assistant", content: msg });
|
|
4342
5053
|
postStatus("Execution completed", structuredError?.error.message, "complete");
|
|
4343
5054
|
postStateSnapshot();
|
|
4344
|
-
|
|
5055
|
+
const outcome = deriveDirectToolRunOutcome(result.directToolResult);
|
|
5056
|
+
return {
|
|
5057
|
+
route: result.route,
|
|
5058
|
+
taskComplete: outcome.taskComplete,
|
|
5059
|
+
needsUserInput: outcome.needsUserInput
|
|
5060
|
+
};
|
|
4345
5061
|
}
|
|
4346
5062
|
if (result.plannerResponse) {
|
|
4347
5063
|
postStatus("Verifying planner output", void 0, "verify");
|
|
@@ -4360,7 +5076,7 @@ ${qText}`;
|
|
|
4360
5076
|
history.push({ role: "assistant", content: msg2 });
|
|
4361
5077
|
postStatus("Planner needs user input", void 0, "verify");
|
|
4362
5078
|
postStateSnapshot();
|
|
4363
|
-
return result.route;
|
|
5079
|
+
return { route: result.route, taskComplete: false, needsUserInput: true };
|
|
4364
5080
|
}
|
|
4365
5081
|
const toolResults = result.plannerResponse.toolResults || [];
|
|
4366
5082
|
for (const toolResult of toolResults) {
|
|
@@ -4377,18 +5093,30 @@ ${qText}`;
|
|
|
4377
5093
|
history.push({ role: "assistant", content: msg });
|
|
4378
5094
|
postStatus("Planner execution completed", response.overallThought, "complete");
|
|
4379
5095
|
postStateSnapshot();
|
|
4380
|
-
return
|
|
5096
|
+
return {
|
|
5097
|
+
route: result.route,
|
|
5098
|
+
taskComplete: !!response.taskComplete && !responseError,
|
|
5099
|
+
needsUserInput: false
|
|
5100
|
+
};
|
|
4381
5101
|
}
|
|
4382
5102
|
postAssistantMessage("Done.");
|
|
4383
5103
|
history.push({ role: "assistant", content: "Done." });
|
|
4384
5104
|
postStatus("Completed", void 0, "complete");
|
|
4385
5105
|
postStateSnapshot();
|
|
4386
|
-
return result.route;
|
|
5106
|
+
return { route: result.route, taskComplete: true };
|
|
4387
5107
|
}
|
|
4388
5108
|
async function runUserMessage(text, meta) {
|
|
4389
5109
|
const runId = meta?.runId || crypto.randomUUID();
|
|
4390
5110
|
if (completedRunIds.has(runId)) {
|
|
4391
|
-
|
|
5111
|
+
const cachedOutcome = normalizeRunOutcome(completedRunOutcomes.get(runId));
|
|
5112
|
+
self.postMessage({
|
|
5113
|
+
type: "run_completed",
|
|
5114
|
+
runId,
|
|
5115
|
+
ok: true,
|
|
5116
|
+
route: cachedOutcome.route,
|
|
5117
|
+
taskComplete: cachedOutcome.taskComplete,
|
|
5118
|
+
needsUserInput: cachedOutcome.needsUserInput
|
|
5119
|
+
});
|
|
4392
5120
|
return;
|
|
4393
5121
|
}
|
|
4394
5122
|
if (activeRun && activeRun.runId === runId) {
|
|
@@ -4402,13 +5130,39 @@ async function runUserMessage(text, meta) {
|
|
|
4402
5130
|
self.postMessage({ type: "run_started", runId, text, resume });
|
|
4403
5131
|
postStateSnapshot();
|
|
4404
5132
|
try {
|
|
4405
|
-
const
|
|
4406
|
-
|
|
5133
|
+
const outcome = normalizeRunOutcome(await handleUserMessage(text, { resume }));
|
|
5134
|
+
completedRunOutcomes.set(runId, outcome);
|
|
5135
|
+
self.postMessage({
|
|
5136
|
+
type: "run_completed",
|
|
5137
|
+
runId,
|
|
5138
|
+
ok: true,
|
|
5139
|
+
route: outcome.route,
|
|
5140
|
+
taskComplete: outcome.taskComplete,
|
|
5141
|
+
needsUserInput: outcome.needsUserInput
|
|
5142
|
+
});
|
|
5143
|
+
if (outcome.taskComplete && !outcome.needsUserInput) {
|
|
5144
|
+
clearTaskScopedContextAfterCompletion();
|
|
5145
|
+
postStateSnapshot();
|
|
5146
|
+
}
|
|
4407
5147
|
} catch (error) {
|
|
4408
5148
|
if (error?.name === "AbortError") {
|
|
4409
|
-
self.postMessage({
|
|
5149
|
+
self.postMessage({
|
|
5150
|
+
type: "run_completed",
|
|
5151
|
+
runId,
|
|
5152
|
+
ok: false,
|
|
5153
|
+
error: "Run cancelled",
|
|
5154
|
+
taskComplete: false,
|
|
5155
|
+
needsUserInput: false
|
|
5156
|
+
});
|
|
4410
5157
|
} else {
|
|
4411
|
-
self.postMessage({
|
|
5158
|
+
self.postMessage({
|
|
5159
|
+
type: "run_completed",
|
|
5160
|
+
runId,
|
|
5161
|
+
ok: false,
|
|
5162
|
+
error: error?.message || String(error),
|
|
5163
|
+
taskComplete: false,
|
|
5164
|
+
needsUserInput: false
|
|
5165
|
+
});
|
|
4412
5166
|
throw error;
|
|
4413
5167
|
}
|
|
4414
5168
|
} finally {
|
|
@@ -4417,8 +5171,10 @@ async function runUserMessage(text, meta) {
|
|
|
4417
5171
|
completedRunIds.add(runId);
|
|
4418
5172
|
if (completedRunIds.size > 50) {
|
|
4419
5173
|
const oldest = completedRunIds.values().next().value;
|
|
4420
|
-
if (oldest)
|
|
5174
|
+
if (oldest) {
|
|
4421
5175
|
completedRunIds.delete(oldest);
|
|
5176
|
+
completedRunOutcomes.delete(oldest);
|
|
5177
|
+
}
|
|
4422
5178
|
}
|
|
4423
5179
|
postStateSnapshot();
|
|
4424
5180
|
}
|
|
@@ -4457,7 +5213,12 @@ self.onmessage = async (ev) => {
|
|
|
4457
5213
|
if (!config)
|
|
4458
5214
|
throw new Error("Worker not initialized");
|
|
4459
5215
|
const partial = data.config || {};
|
|
4460
|
-
config = {
|
|
5216
|
+
config = {
|
|
5217
|
+
...config,
|
|
5218
|
+
...partial,
|
|
5219
|
+
ui: mergeWorkerUi(config.ui, partial.ui),
|
|
5220
|
+
tools: mergeWorkerTools(config.tools, partial.tools)
|
|
5221
|
+
};
|
|
4461
5222
|
if (typeof partial.sessionId === "string" && partial.sessionId.trim() && partial.sessionId.trim() !== trajectoryId) {
|
|
4462
5223
|
trajectoryId = partial.sessionId.trim();
|
|
4463
5224
|
plannerHistory = [];
|