@rtrvr-ai/rover 1.3.1 → 1.3.3

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.
@@ -113,6 +113,8 @@ var PLANNER_FUNCTION_CALLS;
113
113
  PLANNER_FUNCTION_CALLS2["GOOGLE_SLIDES_GENERATOR"] = "google_slides_generator";
114
114
  PLANNER_FUNCTION_CALLS2["PROCESS_TEXT"] = "process_text";
115
115
  PLANNER_FUNCTION_CALLS2["CREATE_SHEET_FROM_DATA"] = "create_sheet_from_data";
116
+ PLANNER_FUNCTION_CALLS2["ROVER_EXTERNAL_READ_CONTEXT"] = "rover_external_read_context";
117
+ PLANNER_FUNCTION_CALLS2["ROVER_EXTERNAL_ACT_CONTEXT"] = "rover_external_act_context";
116
118
  PLANNER_FUNCTION_CALLS2["CONFIGURE_API_KEY"] = "configure_api_key";
117
119
  PLANNER_FUNCTION_CALLS2["TASK_COMPLETE"] = "task_complete";
118
120
  PLANNER_FUNCTION_CALLS2["ASK_USER"] = "ask_user";
@@ -135,6 +137,8 @@ var FUNCTION_CALLS;
135
137
  FUNCTION_CALLS2["GOOGLE_SLIDES_GENERATOR"] = "Google Slides Generator";
136
138
  FUNCTION_CALLS2["PROCESS_TEXT"] = "Process Text";
137
139
  FUNCTION_CALLS2["CREATE_SHEET_FROM_DATA"] = "Create Sheet From Data";
140
+ FUNCTION_CALLS2["ROVER_EXTERNAL_READ_CONTEXT"] = "Rover External Read Context";
141
+ FUNCTION_CALLS2["ROVER_EXTERNAL_ACT_CONTEXT"] = "Rover External Act Context";
138
142
  FUNCTION_CALLS2["EXECUTE_MULTIPLE_TOOLS"] = "Execute Multiple User Tools";
139
143
  FUNCTION_CALLS2["CONFIGURE_API_KEY"] = "Configure Gemini API Key";
140
144
  FUNCTION_CALLS2["QUERY_RTRVR_AI_DOCUMENTATION"] = "query_rtrvr_ai_documentation";
@@ -152,6 +156,8 @@ var plannerFunctionCallValueToFunctionCallValueMap = {
152
156
  [PLANNER_FUNCTION_CALLS.GOOGLE_SLIDES_GENERATOR]: FUNCTION_CALLS.GOOGLE_SLIDES_GENERATOR,
153
157
  [PLANNER_FUNCTION_CALLS.PROCESS_TEXT]: FUNCTION_CALLS.PROCESS_TEXT,
154
158
  [PLANNER_FUNCTION_CALLS.CREATE_SHEET_FROM_DATA]: FUNCTION_CALLS.CREATE_SHEET_FROM_DATA,
159
+ [PLANNER_FUNCTION_CALLS.ROVER_EXTERNAL_READ_CONTEXT]: FUNCTION_CALLS.ROVER_EXTERNAL_READ_CONTEXT,
160
+ [PLANNER_FUNCTION_CALLS.ROVER_EXTERNAL_ACT_CONTEXT]: FUNCTION_CALLS.ROVER_EXTERNAL_ACT_CONTEXT,
155
161
  [PLANNER_FUNCTION_CALLS.EXECUTE_MULTIPLE_TOOLS]: FUNCTION_CALLS.EXECUTE_MULTIPLE_TOOLS,
156
162
  [PLANNER_FUNCTION_CALLS.CONFIGURE_API_KEY]: FUNCTION_CALLS.CONFIGURE_API_KEY,
157
163
  [PLANNER_FUNCTION_CALLS.QUERY_RTRVR_AI_DOCUMENTATION]: FUNCTION_CALLS.QUERY_RTRVR_AI_DOCUMENTATION
@@ -161,27 +167,6 @@ var DEFAULT_GEMINI_MODEL = GEMINI_MODEL.FLASH;
161
167
  var PLANNER = FUNCTION_CALLS.PLANNER;
162
168
  var maxFileSizeInBytes = 20 * 1024 * 1024;
163
169
 
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
-
185
170
  // dist/tabular-memory/tabular-store.js
186
171
  var makeId = () => {
187
172
  if (typeof crypto !== "undefined" && "randomUUID" in crypto)
@@ -607,6 +592,18 @@ var isMemorySheetId = (sheetId) => {
607
592
  // dist/agent/errors.js
608
593
  function inferErrorCode(message) {
609
594
  const text = String(message || "").toLowerCase();
595
+ if (text.includes("stale_seq") || text.includes("stale seq"))
596
+ return "STALE_SEQ";
597
+ if (text.includes("stale_epoch") || text.includes("stale epoch"))
598
+ return "STALE_EPOCH";
599
+ if (text.includes("session token") && text.includes("expired"))
600
+ return "SESSION_TOKEN_EXPIRED";
601
+ if (text.includes("session token") && text.includes("invalid"))
602
+ return "SESSION_TOKEN_INVALID";
603
+ if (text.includes("bootstrap") && text.includes("required"))
604
+ return "BOOTSTRAP_REQUIRED";
605
+ if (text.includes("navigation handoff"))
606
+ return "NAVIGATION_HANDOFF_PENDING";
610
607
  if ((text.includes("auth token") || text.includes("authentication token")) && (text.includes("missing") || text.includes("required"))) {
611
608
  return "MISSING_AUTH_TOKEN";
612
609
  }
@@ -630,31 +627,78 @@ function inferErrorCode(message) {
630
627
  }
631
628
  function defaultNextActionForCode(code) {
632
629
  if (code === "MISSING_AUTH" || code === "MISSING_AUTH_TOKEN") {
633
- return "Provide the required auth token in tool input or rover.boot(...).";
630
+ return "Provide a valid rvrsess_* sessionToken from /v1/rover/session/start in rover.boot(...).";
634
631
  }
635
632
  if (code === "MISSING_API_KEY") {
636
- return "Provide apiKey in rover.boot(...) or an Authorization Bearer token.";
633
+ return "Use a Rover session token (rvrsess_*) from /v1/rover/session/start.";
637
634
  }
638
635
  if (code === "INVALID_API_KEY") {
639
- return "Use a valid active rtrvr_ API key.";
636
+ return "Use a valid active Rover session token or rotate your site public key.";
637
+ }
638
+ if (code === "STALE_SEQ") {
639
+ return "Sync session projection and retry with latest seq.";
640
+ }
641
+ if (code === "STALE_EPOCH") {
642
+ return "Refresh session token/projection and retry with latest epoch.";
643
+ }
644
+ if (code === "SESSION_TOKEN_EXPIRED") {
645
+ return "Refresh session via /v1/rover/session/start using bootstrap public key (pk_site_*), then retry.";
646
+ }
647
+ if (code === "SESSION_TOKEN_INVALID") {
648
+ return "Initialize a fresh session token via /v1/rover/session/start and retry.";
649
+ }
650
+ if (code === "BOOTSTRAP_REQUIRED") {
651
+ return "Provide a valid pk_site_* key in bootstrapToken/publicKey and retry session start.";
652
+ }
653
+ if (code === "NAVIGATION_HANDOFF_PENDING") {
654
+ return "Wait for post-navigation hydration/projection sync and retry.";
640
655
  }
641
656
  return void 0;
642
657
  }
643
658
  function toRoverErrorEnvelope(err, fallbackMessage = "Operation failed") {
659
+ const rawErrorCode = String(err?.error || "").trim().toLowerCase();
660
+ if (rawErrorCode === "stale_seq" || rawErrorCode === "stale_epoch" || rawErrorCode === "session_token_expired" || rawErrorCode === "session_token_invalid" || rawErrorCode === "bootstrap_required") {
661
+ const code2 = rawErrorCode === "stale_epoch" ? "STALE_EPOCH" : rawErrorCode === "session_token_expired" ? "SESSION_TOKEN_EXPIRED" : rawErrorCode === "session_token_invalid" ? "SESSION_TOKEN_INVALID" : rawErrorCode === "bootstrap_required" ? "BOOTSTRAP_REQUIRED" : "STALE_SEQ";
662
+ const data = err?.data && typeof err.data === "object" ? err.data : {};
663
+ const message2 = typeof data?.reason === "string" ? data.reason : rawErrorCode === "stale_epoch" ? "Session epoch is stale." : rawErrorCode === "session_token_expired" ? "Session token expired." : rawErrorCode === "session_token_invalid" ? "Session token invalid." : rawErrorCode === "bootstrap_required" ? "Bootstrap token required." : "Run sequence is stale.";
664
+ return {
665
+ code: code2,
666
+ message: message2,
667
+ next_action: defaultNextActionForCode(code2),
668
+ retryable: code2 === "STALE_SEQ" || code2 === "STALE_EPOCH" || code2 === "SESSION_TOKEN_EXPIRED",
669
+ details: err
670
+ };
671
+ }
644
672
  const directCandidate = err && typeof err === "object" && typeof err.code === "string" && typeof err.message === "string" ? err : void 0;
645
673
  const candidate = err?.roverError || err?.errorDetails || (typeof err?.error === "object" ? err.error : void 0) || directCandidate;
646
674
  if (candidate && typeof candidate === "object" && candidate.code && candidate.message) {
675
+ const candidateCodeRaw = String(candidate.code || "").trim();
676
+ const normalizedCandidateCode = candidateCodeRaw === "stale_seq" ? "STALE_SEQ" : candidateCodeRaw === "stale_epoch" ? "STALE_EPOCH" : candidateCodeRaw === "session_token_expired" ? "SESSION_TOKEN_EXPIRED" : candidateCodeRaw === "session_token_invalid" ? "SESSION_TOKEN_INVALID" : candidateCodeRaw === "bootstrap_required" ? "BOOTSTRAP_REQUIRED" : candidateCodeRaw === "navigation_handoff_pending" ? "NAVIGATION_HANDOFF_PENDING" : candidateCodeRaw;
677
+ const normalizedCode = normalizedCandidateCode;
678
+ const retryable = typeof candidate.retryable === "boolean" ? candidate.retryable : normalizedCode === "STALE_SEQ" || normalizedCode === "STALE_EPOCH" || normalizedCode === "SESSION_TOKEN_EXPIRED" || normalizedCode === "NAVIGATION_HANDOFF_PENDING";
647
679
  return {
648
- code: candidate.code,
680
+ code: normalizedCode,
649
681
  message: candidate.message,
650
682
  requires_api_key: !!candidate.requires_api_key,
651
683
  missing: Array.isArray(candidate.missing) ? candidate.missing : void 0,
652
- next_action: candidate.next_action,
653
- retryable: candidate.retryable,
684
+ next_action: candidate.next_action || defaultNextActionForCode(normalizedCode),
685
+ retryable,
654
686
  degraded: candidate.degraded,
655
687
  details: candidate.details
656
688
  };
657
689
  }
690
+ if (typeof err === "string") {
691
+ const message2 = err || fallbackMessage;
692
+ const code2 = inferErrorCode(message2);
693
+ return {
694
+ code: code2,
695
+ message: message2,
696
+ requires_api_key: code2 === "MISSING_API_KEY" || code2 === "INVALID_API_KEY",
697
+ missing: code2 === "MISSING_AUTH" || code2 === "MISSING_AUTH_TOKEN" ? ["authToken"] : void 0,
698
+ next_action: defaultNextActionForCode(code2),
699
+ retryable: code2 === "NETWORK_ERROR" || code2 === "STALE_SEQ" || code2 === "STALE_EPOCH" || code2 === "SESSION_TOKEN_EXPIRED" || code2 === "NAVIGATION_HANDOFF_PENDING"
700
+ };
701
+ }
658
702
  const message = String(err?.message || err?.error || fallbackMessage);
659
703
  const code = inferErrorCode(message);
660
704
  const requiresApiKey = code === "MISSING_API_KEY" || code === "INVALID_API_KEY";
@@ -662,9 +706,9 @@ function toRoverErrorEnvelope(err, fallbackMessage = "Operation failed") {
662
706
  code,
663
707
  message,
664
708
  requires_api_key: requiresApiKey,
665
- missing: requiresApiKey ? ["apiKey"] : code === "MISSING_AUTH" || code === "MISSING_AUTH_TOKEN" ? ["authToken"] : void 0,
709
+ missing: requiresApiKey ? ["publicKey"] : code === "MISSING_AUTH" || code === "MISSING_AUTH_TOKEN" ? ["authToken"] : void 0,
666
710
  next_action: defaultNextActionForCode(code),
667
- retryable: code === "NETWORK_ERROR",
711
+ retryable: code === "NETWORK_ERROR" || code === "STALE_SEQ" || code === "STALE_EPOCH" || code === "SESSION_TOKEN_EXPIRED" || code === "NAVIGATION_HANDOFF_PENDING",
668
712
  details: err
669
713
  };
670
714
  }
@@ -788,6 +832,30 @@ function normalizeRuntimeExternalTabs(input) {
788
832
  }
789
833
  return Array.from(deduped.values());
790
834
  }
835
+ function selectExternalIntent(requestedIntent, message) {
836
+ if (requestedIntent === "act")
837
+ return "act";
838
+ if (requestedIntent === "read_context")
839
+ return "read_context";
840
+ if (requestedIntent === "open_only")
841
+ return "open_only";
842
+ const normalized = String(message || "").toLowerCase();
843
+ if (!normalized.trim())
844
+ return "read_context";
845
+ const mutationSignals = /\b(fill|submit|book|buy|purchase|apply|sign up|signup|log in|login|register|delete|update|create|post|send|checkout|pay)\b/i;
846
+ const navigationSignals = /\b(open|navigate|go to|visit|take me to|bring me to)\b/i;
847
+ const readSignals = /\b(read|summarize|extract|inspect|analyze|check|find|lookup|list|show|compare|review)\b/i;
848
+ if (mutationSignals.test(normalized) && !/\b(do not|don't)\b/i.test(normalized)) {
849
+ return "act";
850
+ }
851
+ if (navigationSignals.test(normalized) && !readSignals.test(normalized)) {
852
+ return "read_context";
853
+ }
854
+ if (readSignals.test(normalized)) {
855
+ return "read_context";
856
+ }
857
+ return "read_context";
858
+ }
791
859
  function resolveExtensionRouterEndpoint(apiBase) {
792
860
  const fallback = DEFAULT_EXTENSION_ROUTER_BASE;
793
861
  const base = String(apiBase || fallback).trim().replace(/\/+$/, "");
@@ -806,6 +874,24 @@ function resolveExtensionRouterEndpoint(apiBase) {
806
874
  }
807
875
  return `${base}/extensionRouter`;
808
876
  }
877
+ function resolveRoverV1Endpoint(apiBase) {
878
+ const fallback = DEFAULT_EXTENSION_ROUTER_BASE;
879
+ const base = String(apiBase || fallback).trim().replace(/\/+$/, "");
880
+ if (!base)
881
+ return `${fallback}/v1/rover`;
882
+ if (base.endsWith("/extensionRouter")) {
883
+ return `${base.slice(0, -"/extensionRouter".length)}/v1/rover`;
884
+ }
885
+ if (base.endsWith("/v1/rover"))
886
+ return base;
887
+ return `${base}/v1/rover`;
888
+ }
889
+ function createRequestNonce() {
890
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") {
891
+ return crypto.randomUUID();
892
+ }
893
+ return `${Date.now()}-${Math.random().toString(16).slice(2)}`;
894
+ }
809
895
  function buildRoverRuntimeContext(config2) {
810
896
  const runtimeContext = config2.runtimeContext;
811
897
  if (!runtimeContext || runtimeContext.mode !== "rover_embed")
@@ -854,7 +940,7 @@ function getUserFriendlyTimestamp() {
854
940
  }
855
941
  function createAgentContext(config2, bridgeRpc2, tabularStore2) {
856
942
  const userTimestamp = getUserFriendlyTimestamp();
857
- const apiMode = typeof config2.apiMode === "boolean" ? config2.apiMode : !!config2.apiKey;
943
+ const apiMode = typeof config2.apiMode === "boolean" ? config2.apiMode : !!String(config2.sessionToken || config2.authToken || "").trim();
858
944
  const llmIntegration = {
859
945
  model: config2.llmIntegration?.model || config2.model || DEFAULT_GEMINI_MODEL
860
946
  };
@@ -870,6 +956,7 @@ function createAgentContext(config2, bridgeRpc2, tabularStore2) {
870
956
  llmIntegration.enableGoogleAiStudioApiKey = true;
871
957
  }
872
958
  const endpoint = resolveExtensionRouterEndpoint(config2.apiBase);
959
+ const roverV1Endpoint = resolveRoverV1Endpoint(config2.apiBase);
873
960
  const runtimeContext = buildRoverRuntimeContext(config2);
874
961
  const externalWebConfig = normalizeExternalWebConfig(config2.tools?.web);
875
962
  const externalPageDataCache = /* @__PURE__ */ new Map();
@@ -882,13 +969,21 @@ function createAgentContext(config2, bridgeRpc2, tabularStore2) {
882
969
  const ACTIVE_TAB_CACHE_TTL_MS = 250;
883
970
  const EXTERNAL_PAGE_CACHE_TTL_MS = 45e3;
884
971
  const callExtensionRouter = async (action, data) => {
885
- const token = config2.authToken || config2.apiKey;
886
- if (!token) {
972
+ let sessionToken = String(config2.sessionToken || config2.authToken || "").trim();
973
+ if (!sessionToken || !sessionToken.startsWith("rvrsess_")) {
974
+ for (let wait = 0; wait < 3; wait++) {
975
+ await new Promise((resolve) => setTimeout(resolve, 800));
976
+ sessionToken = String(config2.sessionToken || config2.authToken || "").trim();
977
+ if (sessionToken && sessionToken.startsWith("rvrsess_"))
978
+ break;
979
+ }
980
+ }
981
+ if (!sessionToken || !sessionToken.startsWith("rvrsess_")) {
887
982
  throw createRoverError({
888
- code: "MISSING_API_KEY",
889
- message: "Rover API key is required to call extensionRouter.",
890
- requires_api_key: true,
891
- next_action: "Provide apiKey in rover.boot(...) or an Authorization Bearer token.",
983
+ code: "MISSING_AUTH_TOKEN",
984
+ message: "Rover session token is required to call backend action routes.",
985
+ requires_api_key: false,
986
+ next_action: "Initialize Rover session via /v1/rover/session/start and pass sessionToken to rover.boot(...).",
892
987
  retryable: false
893
988
  });
894
989
  }
@@ -900,15 +995,24 @@ function createAgentContext(config2, bridgeRpc2, tabularStore2) {
900
995
  const response = await fetch(endpoint, {
901
996
  method: "POST",
902
997
  headers: {
903
- Authorization: `Bearer ${token}`,
904
998
  "Content-Type": "application/json"
905
999
  },
906
1000
  body: JSON.stringify({
907
1001
  action,
908
1002
  data: data && typeof data === "object" ? {
909
1003
  ...data,
1004
+ sessionToken,
1005
+ sessionId: String(config2.sessionId || "").trim() || void 0,
1006
+ runId: String(config2.activeRunId || "").trim() || void 0,
1007
+ requestNonce: createRequestNonce(),
910
1008
  ...!data.runtimeContext && runtimeContext ? { runtimeContext } : {}
911
- } : data
1009
+ } : {
1010
+ payload: data,
1011
+ sessionToken,
1012
+ sessionId: String(config2.sessionId || "").trim() || void 0,
1013
+ runId: String(config2.activeRunId || "").trim() || void 0,
1014
+ requestNonce: createRequestNonce()
1015
+ }
912
1016
  }),
913
1017
  signal: config2.signal
914
1018
  });
@@ -967,7 +1071,9 @@ function createAgentContext(config2, bridgeRpc2, tabularStore2) {
967
1071
  if (!normalizedUrl)
968
1072
  throw new Error("external page data requires url");
969
1073
  const cacheTabId = Number(options?.tabId) || 0;
970
- const cacheKey = `${cacheTabId}:${normalizedUrl}`;
1074
+ const source = options?.source || "direct_url";
1075
+ const intent = options?.intent === "act" ? "act" : options?.intent === "open_only" ? "open_only" : "read_context";
1076
+ const cacheKey = `${cacheTabId}:${intent}:${source}:${normalizedUrl}`;
971
1077
  const nowMs = Date.now();
972
1078
  const cachedData = externalPageDataCache.get(cacheKey);
973
1079
  if (cachedData && nowMs - cachedData.ts <= EXTERNAL_PAGE_CACHE_TTL_MS) {
@@ -982,19 +1088,49 @@ function createAgentContext(config2, bridgeRpc2, tabularStore2) {
982
1088
  if (externalPageDataDisabledReason) {
983
1089
  throw new Error(externalPageDataDisabledReason);
984
1090
  }
985
- const source = options?.source || "direct_url";
986
- const payload = {
987
- url: normalizedUrl,
988
- source,
989
- siteId: config2.siteId,
990
- roverPolicy: {
991
- allowDomains: externalWebConfig.allowDomains,
992
- denyDomains: externalWebConfig.denyDomains
993
- }
994
- };
1091
+ const sessionToken = String(config2.sessionToken || "").trim();
1092
+ if (!sessionToken.startsWith("rvrsess_")) {
1093
+ throw createRoverError({
1094
+ code: "MISSING_AUTH_TOKEN",
1095
+ message: "External context requires a Rover session token.",
1096
+ next_action: "Initialize Rover v1 session/start before requesting external context.",
1097
+ retryable: false
1098
+ });
1099
+ }
995
1100
  try {
996
- const response = await callExtensionRouter(SUB_AGENTS.roverExternalPageData, payload);
997
- const pageData = response?.data || response;
1101
+ const v1Resp = await fetch(`${roverV1Endpoint}/context/external`, {
1102
+ method: "POST",
1103
+ headers: {
1104
+ "Content-Type": "application/json"
1105
+ },
1106
+ body: JSON.stringify({
1107
+ requestNonce: createRequestNonce(),
1108
+ sessionToken,
1109
+ sessionId: String(config2.sessionId || "").trim() || void 0,
1110
+ runId: String(options?.runId || config2.activeRunId || "").trim() || void 0,
1111
+ expectedEpoch: Number.isFinite(Number(config2.sessionEpoch)) ? Number(config2.sessionEpoch) : void 0,
1112
+ expectedSeq: Number.isFinite(Number(config2.sessionSeq)) ? Number(config2.sessionSeq) : void 0,
1113
+ logicalTabId: Number(options?.tabId) > 0 ? String(Math.trunc(Number(options?.tabId))) : void 0,
1114
+ intent,
1115
+ url: normalizedUrl,
1116
+ source,
1117
+ message: options?.message
1118
+ }),
1119
+ signal: config2.signal
1120
+ });
1121
+ const payload = await v1Resp.json().catch(() => void 0);
1122
+ if (!v1Resp.ok || payload?.success === false) {
1123
+ const envelope = toRoverErrorEnvelope(payload, payload?.error || `external context request failed (${v1Resp.status})`);
1124
+ throw createRoverError({
1125
+ ...envelope,
1126
+ details: {
1127
+ endpoint: `${roverV1Endpoint}/context/external`,
1128
+ status: v1Resp.status,
1129
+ response: payload
1130
+ }
1131
+ });
1132
+ }
1133
+ const pageData = payload?.data?.pageData || payload?.data;
998
1134
  externalPageDataCache.set(cacheKey, { data: pageData, ts: nowMs });
999
1135
  return pageData;
1000
1136
  } catch (error) {
@@ -1028,7 +1164,10 @@ function createAgentContext(config2, bridgeRpc2, tabularStore2) {
1028
1164
  const numericTabId = Number(tabId);
1029
1165
  const rawOptions = options && typeof options === "object" ? options : void 0;
1030
1166
  const allowExternalFetch = rawOptions?.__roverAllowExternalFetch === true;
1031
- const pageConfig = rawOptions && typeof rawOptions === "object" ? Object.fromEntries(Object.entries(rawOptions).filter(([key]) => key !== "__roverAllowExternalFetch")) : void 0;
1167
+ const requestedExternalIntent = rawOptions?.__roverExternalIntent === "act" || rawOptions?.__roverExternalIntent === "open_only" || rawOptions?.__roverExternalIntent === "read_context" || rawOptions?.__roverExternalIntent === "auto" ? rawOptions.__roverExternalIntent : "auto";
1168
+ const externalMessage = typeof rawOptions?.__roverExternalMessage === "string" ? String(rawOptions.__roverExternalMessage) : void 0;
1169
+ const externalIntent = selectExternalIntent(requestedExternalIntent, externalMessage);
1170
+ const pageConfig = rawOptions && typeof rawOptions === "object" ? Object.fromEntries(Object.entries(rawOptions).filter(([key]) => key !== "__roverAllowExternalFetch" && key !== "__roverExternalIntent" && key !== "__roverExternalMessage")) : void 0;
1032
1171
  const hasPageConfig = !!pageConfig && Object.keys(pageConfig).length > 0;
1033
1172
  const localPageData = rawOptions ? await bridgeRpc2("getPageData", hasPageConfig ? { pageConfig, tabId: numericTabId } : { tabId: numericTabId }) : await bridgeRpc2("getPageData", { tabId: numericTabId });
1034
1173
  const metadata = localPageData?.metadata || {};
@@ -1070,8 +1209,13 @@ function createAgentContext(config2, bridgeRpc2, tabularStore2) {
1070
1209
  try {
1071
1210
  const cloudData = await getExternalPageData(pageUrl, {
1072
1211
  tabId: numericTabId,
1073
- source: pageUrl.includes("google.com/search") ? "google_search" : "direct_url"
1212
+ source: pageUrl.includes("google.com/search") ? "google_search" : "direct_url",
1213
+ intent: externalIntent,
1214
+ message: externalMessage
1074
1215
  });
1216
+ if (externalIntent === "open_only") {
1217
+ return localPageData;
1218
+ }
1075
1219
  if (cloudData && typeof cloudData === "object") {
1076
1220
  return {
1077
1221
  ...cloudData,
@@ -1315,6 +1459,27 @@ var GeminiModel;
1315
1459
  GeminiModel2["PRO"] = "Gemini Pro";
1316
1460
  })(GeminiModel || (GeminiModel = {}));
1317
1461
 
1462
+ // ../shared/dist/lib/types/agent-types.js
1463
+ var SUB_AGENTS;
1464
+ (function(SUB_AGENTS2) {
1465
+ SUB_AGENTS2["enhance"] = "enhance";
1466
+ SUB_AGENTS2["plan"] = "plan";
1467
+ SUB_AGENTS2["actOnTabs"] = "actOnTabs";
1468
+ SUB_AGENTS2["extract"] = "extract";
1469
+ SUB_AGENTS2["infer"] = "infer";
1470
+ SUB_AGENTS2["processTabWorkflows"] = "processTabWorkflows";
1471
+ SUB_AGENTS2["processText"] = "processText";
1472
+ SUB_AGENTS2["createSheetFromData"] = "createSheetFromData";
1473
+ SUB_AGENTS2["queryRtrvrDocs"] = "queryRtrvrDocs";
1474
+ SUB_AGENTS2["googleDocGenerator"] = "googleDocGenerator";
1475
+ SUB_AGENTS2["googleSlidesGenerator"] = "googleSlidesGenerator";
1476
+ SUB_AGENTS2["webpageGenerator"] = "webpageGenerator";
1477
+ SUB_AGENTS2["pdfFiller"] = "pdfFiller";
1478
+ SUB_AGENTS2["customToolGenerator"] = "customToolGenerator";
1479
+ SUB_AGENTS2["getPageData"] = "getPageData";
1480
+ SUB_AGENTS2["roverExternalPageData"] = "roverExternalPageData";
1481
+ })(SUB_AGENTS || (SUB_AGENTS = {}));
1482
+
1318
1483
  // ../shared/dist/lib/system-tools/tools.js
1319
1484
  var SystemToolNames;
1320
1485
  (function(SystemToolNames2) {
@@ -1350,6 +1515,8 @@ var SystemToolNames;
1350
1515
  SystemToolNames2["describe_images"] = "describe_images";
1351
1516
  SystemToolNames2["google_search"] = "google_search";
1352
1517
  SystemToolNames2["network_run_recipe"] = "network_run_recipe";
1518
+ SystemToolNames2["rover_external_read_context"] = "rover_external_read_context";
1519
+ SystemToolNames2["rover_external_act_context"] = "rover_external_act_context";
1353
1520
  SystemToolNames2["copy_text"] = "copy_text";
1354
1521
  SystemToolNames2["paste_text"] = "paste_text";
1355
1522
  SystemToolNames2["wait_action"] = "wait_action";
@@ -1367,6 +1534,7 @@ var NAVIGATION_TOOLS = /* @__PURE__ */ new Set([
1367
1534
  SystemToolNames.go_back,
1368
1535
  SystemToolNames.go_forward,
1369
1536
  SystemToolNames.refresh_page,
1537
+ SystemToolNames.open_new_tab,
1370
1538
  SystemToolNames.switch_tab,
1371
1539
  SystemToolNames.close_tab
1372
1540
  ]);
@@ -1381,12 +1549,26 @@ var VIEWPORT_SENSITIVE_TOOLS = /* @__PURE__ */ new Set([
1381
1549
  SystemToolNames.focus_element
1382
1550
  ]);
1383
1551
  var ACTION_DELAY_MS = 600;
1384
- async function executeSystemToolCallsSequentially({ calls, bridgeRpc: bridgeRpc2 }) {
1552
+ var NAVIGATION_OUTCOMES = /* @__PURE__ */ new Set([
1553
+ "same_tab_scheduled",
1554
+ "new_tab_opened",
1555
+ "blocked",
1556
+ "switch_tab"
1557
+ ]);
1558
+ function throwIfCancelled(isCancelled) {
1559
+ if (!isCancelled?.())
1560
+ return;
1561
+ throw new DOMException("Run cancelled", "AbortError");
1562
+ }
1563
+ async function executeSystemToolCallsSequentially({ calls, bridgeRpc: bridgeRpc2, isCancelled }) {
1385
1564
  const results = [];
1386
1565
  let sawViewportSensitiveToolSuccess = false;
1387
1566
  let navigationOccurred = false;
1388
1567
  let navigationTool;
1568
+ let navigationOutcome;
1569
+ let logicalTabId;
1389
1570
  for (const call of calls) {
1571
+ throwIfCancelled(isCancelled);
1390
1572
  const name = call.name;
1391
1573
  const args = call.args || {};
1392
1574
  if (navigationOccurred) {
@@ -1405,8 +1587,10 @@ async function executeSystemToolCallsSequentially({ calls, bridgeRpc: bridgeRpc2
1405
1587
  }
1406
1588
  let response;
1407
1589
  try {
1590
+ throwIfCancelled(isCancelled);
1408
1591
  response = await bridgeRpc2("executeTool", { call });
1409
1592
  } catch (err) {
1593
+ throwIfCancelled(isCancelled);
1410
1594
  response = { success: false, error: err?.message || String(err), allowFallback: true };
1411
1595
  }
1412
1596
  const llmResponse = {
@@ -1418,20 +1602,51 @@ async function executeSystemToolCallsSequentially({ calls, bridgeRpc: bridgeRpc2
1418
1602
  const resolvedResult = { name: name || "unknown", args, response: llmResponse };
1419
1603
  results.push(resolvedResult);
1420
1604
  if (response?.success) {
1421
- if (NAVIGATION_TOOLS.has(name)) {
1605
+ const output = response?.output && typeof response.output === "object" ? response.output : void 0;
1606
+ const outputNavigationOutcomeRaw = String(output?.navigationOutcome || "").trim().toLowerCase();
1607
+ const outputNavigationOutcome = NAVIGATION_OUTCOMES.has(outputNavigationOutcomeRaw) ? outputNavigationOutcomeRaw : void 0;
1608
+ const outputNavigationPending = output?.navigationPending === true;
1609
+ const outputOpenedInNewTab = output?.openedInNewTab === true;
1610
+ const inferredNavigation = !!outputNavigationOutcome || outputNavigationPending || outputOpenedInNewTab;
1611
+ if (NAVIGATION_TOOLS.has(name) || inferredNavigation) {
1422
1612
  navigationOccurred = true;
1423
1613
  navigationTool = name;
1424
1614
  sawViewportSensitiveToolSuccess = false;
1615
+ if (outputNavigationOutcome) {
1616
+ navigationOutcome = outputNavigationOutcome;
1617
+ } else if (outputOpenedInNewTab) {
1618
+ navigationOutcome = "new_tab_opened";
1619
+ } else if (name === SystemToolNames.switch_tab) {
1620
+ navigationOutcome = "switch_tab";
1621
+ } else if (name === SystemToolNames.open_new_tab) {
1622
+ navigationOutcome = "new_tab_opened";
1623
+ } else if (outputNavigationPending) {
1624
+ navigationOutcome = "same_tab_scheduled";
1625
+ } else {
1626
+ navigationOutcome = "same_tab_scheduled";
1627
+ }
1628
+ const outputLogicalTabId = Number(output?.logicalTabId ?? output?.logical_tab_id ?? output?.tabId ?? output?.tab_id ?? args?.logical_tab_id ?? args?.tab_id);
1629
+ if (Number.isFinite(outputLogicalTabId) && outputLogicalTabId > 0) {
1630
+ logicalTabId = outputLogicalTabId;
1631
+ }
1425
1632
  } else if (VIEWPORT_SENSITIVE_TOOLS.has(name)) {
1426
1633
  sawViewportSensitiveToolSuccess = true;
1427
1634
  }
1428
1635
  }
1429
1636
  if (ACTION_DELAY_MS > 0) {
1430
1637
  await new Promise((resolve) => setTimeout(resolve, ACTION_DELAY_MS));
1638
+ throwIfCancelled(isCancelled);
1431
1639
  }
1432
1640
  }
1433
1641
  const disableAutoScroll = sawViewportSensitiveToolSuccess && !navigationOccurred;
1434
- return { results, disableAutoScroll, navigationOccurred, navigationTool };
1642
+ return {
1643
+ results,
1644
+ disableAutoScroll,
1645
+ navigationOccurred,
1646
+ navigationTool,
1647
+ navigationOutcome,
1648
+ logicalTabId
1649
+ };
1435
1650
  }
1436
1651
 
1437
1652
  // dist/agent/utils.js
@@ -1445,7 +1660,22 @@ async function waitWhilePaused(executionRef) {
1445
1660
  await new Promise((resolve) => setTimeout(resolve, 100));
1446
1661
  }
1447
1662
  }
1448
- async function processActionResponse({ request, response, tabId, prevSteps, thought, bridgeRpc: bridgeRpc2, userFunctionDeclarations, onStatusUpdate, onPrevStepsUpdate }) {
1663
+ function buildWorkerStopSignal(params) {
1664
+ if (params.isCancelled) {
1665
+ return {
1666
+ state: "cancel_requested",
1667
+ reason: params.reason || "worker_cancelled"
1668
+ };
1669
+ }
1670
+ return { state: "continue" };
1671
+ }
1672
+ async function processActionResponse({ request, response, tabId, prevSteps, thought, bridgeRpc: bridgeRpc2, isCancelled, userFunctionDeclarations, onStatusUpdate, onPrevStepsUpdate }) {
1673
+ const throwIfCancelled2 = () => {
1674
+ if (!isCancelled?.())
1675
+ return;
1676
+ throw new DOMException("Run cancelled", "AbortError");
1677
+ };
1678
+ throwIfCancelled2();
1449
1679
  const { functionCalls, modelParts, data, accTreeId } = response || {};
1450
1680
  let disableAutoScroll = false;
1451
1681
  if (data && Array.isArray(data) && data.length > 0) {
@@ -1548,6 +1778,7 @@ async function processActionResponse({ request, response, tabId, prevSteps, thou
1548
1778
  }
1549
1779
  }
1550
1780
  if (externalCalls.length > 0) {
1781
+ throwIfCancelled2();
1551
1782
  if (userFunctionDeclarations?.length) {
1552
1783
  for (const funcCall of externalCalls) {
1553
1784
  const found = userFunctionDeclarations.find((decl) => decl.name === funcCall.name);
@@ -1575,6 +1806,7 @@ async function processActionResponse({ request, response, tabId, prevSteps, thou
1575
1806
  return { needsRetry: false, functionCalls: externalCalls };
1576
1807
  }
1577
1808
  if (systemCalls.length > 0) {
1809
+ throwIfCancelled2();
1578
1810
  onStatusUpdate?.(`Executing browser actions: ${systemCalls.map((c) => c.name).join(", ")}`, thought, "execute");
1579
1811
  prevSteps.push({
1580
1812
  accTreeId,
@@ -1589,11 +1821,13 @@ async function processActionResponse({ request, response, tabId, prevSteps, thou
1589
1821
  limitPrevSteps(prevSteps);
1590
1822
  onPrevStepsUpdate?.(prevSteps);
1591
1823
  const stepIndex = prevSteps.length - 1;
1592
- const { results, disableAutoScroll: isScroll } = await executeSystemToolCallsSequentially({
1824
+ const { results, disableAutoScroll: isScroll, navigationOccurred, navigationTool, navigationOutcome, logicalTabId } = await executeSystemToolCallsSequentially({
1593
1825
  calls: systemCalls,
1594
- bridgeRpc: bridgeRpc2
1826
+ bridgeRpc: bridgeRpc2,
1827
+ isCancelled
1595
1828
  });
1596
1829
  disableAutoScroll = isScroll;
1830
+ throwIfCancelled2();
1597
1831
  if (stepIndex >= 0 && stepIndex < prevSteps.length) {
1598
1832
  prevSteps[stepIndex].functions = results;
1599
1833
  const failedCount = results.filter((r) => r.response.status === "Failure").length;
@@ -1603,7 +1837,14 @@ async function processActionResponse({ request, response, tabId, prevSteps, thou
1603
1837
  }
1604
1838
  limitPrevSteps(prevSteps);
1605
1839
  onPrevStepsUpdate?.(prevSteps);
1606
- return { needsRetry: false, disableAutoScroll };
1840
+ return {
1841
+ needsRetry: false,
1842
+ disableAutoScroll,
1843
+ navigationOccurred,
1844
+ navigationTool,
1845
+ navigationOutcome,
1846
+ logicalTabId
1847
+ };
1607
1848
  }
1608
1849
  }
1609
1850
  prevSteps.push({ accTreeId, thought, modelParts, fail: "Empty or unusable response" });
@@ -1757,81 +1998,192 @@ function normalizeListedTabs(input) {
1757
1998
  };
1758
1999
  }).filter((tab) => !!tab);
1759
2000
  }
2001
+ function preferScopedOrder(scopedOrder, activeTabId, maxContextTabs) {
2002
+ const deduped = dedupePositiveTabIds(scopedOrder);
2003
+ if (!deduped.length)
2004
+ return [];
2005
+ const activeInScope = deduped.includes(activeTabId);
2006
+ const resolvedActive = activeInScope ? activeTabId : deduped[0];
2007
+ const ordered = [
2008
+ resolvedActive,
2009
+ ...deduped.filter((tabId) => tabId !== resolvedActive)
2010
+ ];
2011
+ return ordered.slice(0, maxContextTabs);
2012
+ }
2013
+ function filterTabIdsToScope(tabIds, scopedTabIds2) {
2014
+ if (!scopedTabIds2.length)
2015
+ return dedupePositiveTabIds(tabIds);
2016
+ const scoped = new Set(scopedTabIds2);
2017
+ return dedupePositiveTabIds(tabIds).filter((tabId) => scoped.has(tabId));
2018
+ }
1760
2019
  async function resolveRuntimeTabs(bridgeRpc2, fallbackTabs, options) {
1761
2020
  const maxContextTabs = Math.max(1, Number(options?.maxContextTabs) || DEFAULT_MAX_CONTEXT_TABS);
1762
2021
  const detachedExternalTabMaxAgeMs = Math.max(5e3, Number(options?.detachedExternalTabMaxAgeMs) || DEFAULT_DETACHED_EXTERNAL_TAB_MAX_AGE_MS);
1763
2022
  const staleRuntimeTabMaxAgeMs = Math.max(1e4, Number(options?.staleRuntimeTabMaxAgeMs) || DEFAULT_STALE_RUNTIME_TAB_MAX_AGE_MS);
2023
+ const scopedTabIds2 = dedupePositiveTabIds(options?.scopedTabIds || []);
2024
+ const hasExplicitScope = scopedTabIds2.length > 0;
2025
+ const scopedOrder = [...scopedTabIds2];
2026
+ const scopedTabSet = new Set(scopedOrder);
1764
2027
  const fallbackSnapshots = normalizeFallbackTabs(fallbackTabs);
1765
- const fallbackTabIds = dedupePositiveTabIds(fallbackSnapshots.map((tab) => tab.id));
2028
+ const fallbackTabIds = dedupePositiveTabIds((scopedTabIds2.length > 0 ? fallbackSnapshots.filter((tab) => scopedTabIds2.includes(tab.id)) : fallbackSnapshots).map((tab) => tab.id));
1766
2029
  let tabIds = [...fallbackTabIds];
1767
2030
  let listedTabs = [];
2031
+ let listedTabIds = [];
2032
+ const isTabInScope = (tabId) => {
2033
+ if (!hasExplicitScope)
2034
+ return true;
2035
+ return scopedTabSet.has(tabId);
2036
+ };
1768
2037
  if (bridgeRpc2) {
1769
2038
  try {
1770
- listedTabs = normalizeListedTabs(await bridgeRpc2("listSessionTabs"));
1771
- const listedIds = dedupePositiveTabIds(listedTabs.map((tab) => tab.id));
1772
- if (listedIds.length > 0) {
1773
- tabIds = listedIds;
2039
+ const listed = await bridgeRpc2("listSessionTabs");
2040
+ listedTabs = normalizeListedTabs(Array.isArray(listed) ? listed : []);
2041
+ listedTabIds = dedupePositiveTabIds(listedTabs.map((tab) => tab.id));
2042
+ if (listedTabIds.length > 0) {
2043
+ tabIds = hasExplicitScope ? filterTabIdsToScope(listedTabIds, scopedOrder) : listedTabIds;
2044
+ } else if (hasExplicitScope) {
2045
+ tabIds = [...scopedOrder];
1774
2046
  }
1775
2047
  } catch {
1776
2048
  }
1777
2049
  }
1778
- let activeTabId = tabIds[0] || fallbackTabIds[0] || 1;
2050
+ if (hasExplicitScope) {
2051
+ tabIds = dedupePositiveTabIds(tabIds).filter((tabId) => isTabInScope(tabId));
2052
+ if (!tabIds.length && scopedOrder.length) {
2053
+ tabIds = [...scopedOrder];
2054
+ }
2055
+ }
2056
+ let activeTabId = Number(options?.seedTabId) > 0 ? Number(options?.seedTabId) : tabIds[0] || fallbackTabIds[0] || 1;
1779
2057
  if (bridgeRpc2) {
1780
2058
  try {
1781
2059
  const context = await bridgeRpc2("getTabContext");
1782
2060
  const candidate = Number(context?.activeLogicalTabId || context?.logicalTabId || context?.id);
1783
- if (Number.isFinite(candidate) && candidate > 0) {
2061
+ if (Number.isFinite(candidate) && candidate > 0 && isTabInScope(candidate)) {
1784
2062
  activeTabId = candidate;
1785
2063
  }
1786
2064
  } catch {
1787
2065
  }
1788
2066
  }
1789
- if (!tabIds.length) {
1790
- tabIds = [activeTabId];
1791
- } else if (!tabIds.includes(activeTabId)) {
1792
- tabIds = [activeTabId, ...tabIds];
2067
+ if (hasExplicitScope && !isTabInScope(activeTabId) && scopedOrder.length > 0) {
2068
+ activeTabId = scopedOrder[0];
1793
2069
  }
1794
2070
  const nowMs = Date.now();
1795
2071
  const listedById = /* @__PURE__ */ new Map();
1796
2072
  for (const tab of listedTabs)
1797
2073
  listedById.set(tab.id, tab);
1798
- const prioritized = tabIds.filter((tabId) => {
2074
+ const freshRuntimeListedIds = listedTabs.filter((tab) => !!tab.runtimeId && nowMs - (tab.updatedAt || 0) <= staleRuntimeTabMaxAgeMs).map((tab) => tab.id);
2075
+ if (freshRuntimeListedIds.length > 0 && !freshRuntimeListedIds.includes(activeTabId)) {
2076
+ const freshestRuntimeTabId = [...freshRuntimeListedIds].sort((a, b) => Number(listedById.get(b)?.updatedAt || 0) - Number(listedById.get(a)?.updatedAt || 0))[0];
2077
+ if (Number.isFinite(freshestRuntimeTabId) && freshestRuntimeTabId > 0 && isTabInScope(freshestRuntimeTabId)) {
2078
+ activeTabId = freshestRuntimeTabId;
2079
+ }
2080
+ }
2081
+ if (!tabIds.length) {
2082
+ tabIds = hasExplicitScope ? scopedOrder.length > 0 ? [...scopedOrder] : [activeTabId] : [activeTabId];
2083
+ } else if (!tabIds.includes(activeTabId)) {
2084
+ if (hasExplicitScope) {
2085
+ tabIds = scopedOrder.length > 0 ? [...scopedOrder] : tabIds;
2086
+ } else if (listedTabs.length > 0) {
2087
+ activeTabId = tabIds[0];
2088
+ } else {
2089
+ tabIds = [activeTabId, ...tabIds];
2090
+ }
2091
+ }
2092
+ const baseOrder = hasExplicitScope ? [...scopedOrder] : listedTabs.length > 0 ? dedupePositiveTabIds(listedTabs.map((tab) => tab.id)) : [...tabIds];
2093
+ const prioritized = baseOrder.filter((tabId) => {
1799
2094
  if (tabId === activeTabId)
1800
2095
  return true;
1801
2096
  const listed = listedById.get(tabId);
1802
- if (!listed)
1803
- return true;
2097
+ if (!listed) {
2098
+ return hasExplicitScope;
2099
+ }
1804
2100
  if (listed.runtimeId) {
1805
2101
  return nowMs - (listed.updatedAt || 0) <= staleRuntimeTabMaxAgeMs;
1806
2102
  }
1807
- if (!listed.external)
1808
- return true;
1809
- return nowMs - (listed.updatedAt || 0) <= detachedExternalTabMaxAgeMs;
2103
+ if (listed.external) {
2104
+ return nowMs - (listed.updatedAt || 0) <= detachedExternalTabMaxAgeMs;
2105
+ }
2106
+ return nowMs - (listed.updatedAt || 0) <= staleRuntimeTabMaxAgeMs;
1810
2107
  });
1811
- let tabOrder = (prioritized.length ? prioritized : tabIds).slice(0, maxContextTabs);
1812
- if (!tabOrder.includes(activeTabId)) {
1813
- tabOrder = [activeTabId, ...tabOrder].slice(0, maxContextTabs);
2108
+ let tabOrder = hasExplicitScope ? preferScopedOrder(scopedOrder, activeTabId, maxContextTabs) : [...new Set(prioritized.length ? prioritized : baseOrder.length ? baseOrder : tabIds)];
2109
+ if (!hasExplicitScope) {
2110
+ if (tabOrder.includes(activeTabId)) {
2111
+ tabOrder = [activeTabId, ...tabOrder.filter((tabId) => tabId !== activeTabId)];
2112
+ } else {
2113
+ tabOrder = [activeTabId, ...tabOrder];
2114
+ }
2115
+ tabOrder = tabOrder.slice(0, maxContextTabs);
1814
2116
  }
1815
2117
  if (!tabOrder.length) {
1816
- tabOrder = [activeTabId || 1];
2118
+ tabOrder = hasExplicitScope ? scopedOrder.length > 0 ? preferScopedOrder(scopedOrder, activeTabId || scopedOrder[0], maxContextTabs) : [activeTabId || 1] : [activeTabId || 1];
1817
2119
  }
1818
2120
  const tabMetaById = {};
1819
2121
  for (const tab of fallbackSnapshots)
1820
2122
  tabMetaById[tab.id] = tab;
1821
2123
  for (const tab of listedTabs)
1822
2124
  tabMetaById[tab.id] = { ...tabMetaById[tab.id] || {}, ...tab };
2125
+ for (const tabId of scopedOrder) {
2126
+ if (!tabMetaById[tabId]) {
2127
+ tabMetaById[tabId] = {
2128
+ id: tabId,
2129
+ accessMode: "live_dom",
2130
+ inaccessibleReason: "detached_runtime_placeholder"
2131
+ };
2132
+ }
2133
+ }
1823
2134
  for (const tabId of tabOrder) {
1824
- if (!tabMetaById[tabId])
1825
- tabMetaById[tabId] = { id: tabId };
2135
+ if (!tabMetaById[tabId]) {
2136
+ tabMetaById[tabId] = {
2137
+ id: tabId,
2138
+ accessMode: "live_dom",
2139
+ inaccessibleReason: "detached_runtime_placeholder"
2140
+ };
2141
+ }
2142
+ }
2143
+ const diagnostics = {
2144
+ hasExplicitScope,
2145
+ scopedTabIdsInput: scopedTabIds2,
2146
+ listedTabIds,
2147
+ keptScopedTabIds: scopedOrder,
2148
+ resolvedTabOrder: tabOrder
2149
+ };
2150
+ if (hasExplicitScope) {
2151
+ if (scopedOrder.length && !scopedTabSet.has(activeTabId)) {
2152
+ activeTabId = scopedOrder[0];
2153
+ }
2154
+ tabOrder = preferScopedOrder(scopedOrder, activeTabId, maxContextTabs);
2155
+ const scopedMeta = {};
2156
+ for (const tabId of scopedOrder) {
2157
+ scopedMeta[tabId] = tabMetaById[tabId] || { id: tabId };
2158
+ }
2159
+ options?.onDiagnostics?.({
2160
+ ...diagnostics,
2161
+ resolvedTabOrder: tabOrder
2162
+ });
2163
+ return { tabOrder, activeTabId, tabMetaById: scopedMeta };
1826
2164
  }
2165
+ options?.onDiagnostics?.(diagnostics);
1827
2166
  return { tabOrder, activeTabId, tabMetaById };
1828
2167
  }
1829
2168
 
1830
2169
  // dist/agent/actAgent.js
1831
2170
  var MAX_RETRIES = 3;
1832
- var MAX_ITERATIONS = 25;
2171
+ function dedupePositiveTabIds2(input) {
2172
+ if (!Array.isArray(input))
2173
+ return [];
2174
+ const seen = /* @__PURE__ */ new Set();
2175
+ const out = [];
2176
+ for (const value of input) {
2177
+ const tabId = Number(value);
2178
+ if (!Number.isFinite(tabId) || tabId <= 0 || seen.has(tabId))
2179
+ continue;
2180
+ seen.add(tabId);
2181
+ out.push(tabId);
2182
+ }
2183
+ return out;
2184
+ }
1833
2185
  async function executeAgenticSeek(options) {
1834
- const { tabOrder, userInput, schema, previousSteps = [], plannerPrevSteps = [], files = [], chatLog = [], recordingContext, trajectoryId: trajectoryId2, onStatusUpdate, functionDeclarations, bridgeRpc: bridgeRpc2, ctx, onPrevStepsUpdate } = options;
2186
+ const { tabOrder, scopedTabIds: scopedTabIds2, seedTabId, onScopedTabIdsTouched, userInput, schema, previousSteps = [], plannerPrevSteps = [], files = [], chatLog = [], recordingContext, trajectoryId, onStatusUpdate, functionDeclarations, bridgeRpc: bridgeRpc2, ctx, onPrevStepsUpdate } = options;
1835
2187
  if (!tabOrder?.length) {
1836
2188
  return { error: "No tabs available for processing", warnings: ["No active tab found"] };
1837
2189
  }
@@ -1839,26 +2191,48 @@ async function executeAgenticSeek(options) {
1839
2191
  const allWarnings = [];
1840
2192
  const accumulatedPrevSteps = Array.isArray(previousSteps) ? previousSteps : [];
1841
2193
  const fallbackTabs = tabOrder.map((id) => ({ id }));
2194
+ let runtimeScopedTabIds = dedupePositiveTabIds2(scopedTabIds2 || []);
2195
+ if (Number(seedTabId) > 0 && !runtimeScopedTabIds.includes(Number(seedTabId))) {
2196
+ runtimeScopedTabIds.unshift(Number(seedTabId));
2197
+ }
2198
+ const touchScopedTabIds = (tabIds) => {
2199
+ const touched = dedupePositiveTabIds2(tabIds);
2200
+ if (!touched.length)
2201
+ return;
2202
+ const nextScoped = dedupePositiveTabIds2([...runtimeScopedTabIds, ...touched]);
2203
+ if (nextScoped.length === runtimeScopedTabIds.length && nextScoped.every((id, index) => id === runtimeScopedTabIds[index])) {
2204
+ return;
2205
+ }
2206
+ runtimeScopedTabIds = nextScoped;
2207
+ onScopedTabIdsTouched?.(nextScoped);
2208
+ };
1842
2209
  let retry = 0;
1843
- let iterations = 0;
1844
2210
  let pageDataOptions;
1845
2211
  while (retry < MAX_RETRIES) {
1846
- if (iterations++ >= MAX_ITERATIONS) {
1847
- return { error: "Max action iterations reached", prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed };
1848
- }
1849
2212
  if (ctx.isCancelled?.()) {
1850
2213
  return { error: "Run cancelled", prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed };
1851
2214
  }
1852
2215
  try {
1853
2216
  await waitWhilePaused(void 0);
2217
+ if (ctx.isCancelled?.()) {
2218
+ return { error: "Run cancelled", prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed };
2219
+ }
1854
2220
  onStatusUpdate?.("Analyzing page content...", "Calling seek workflow", "analyze");
1855
- const { tabOrder: runtimeTabOrder, activeTabId } = await resolveRuntimeTabs(bridgeRpc2, fallbackTabs);
2221
+ const { tabOrder: runtimeTabOrder, activeTabId } = await resolveRuntimeTabs(bridgeRpc2, fallbackTabs, {
2222
+ scopedTabIds: runtimeScopedTabIds,
2223
+ seedTabId
2224
+ });
2225
+ if (ctx.isCancelled?.()) {
2226
+ return { error: "Run cancelled", prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed };
2227
+ }
1856
2228
  const scopedTabOrder = runtimeTabOrder.length ? runtimeTabOrder : tabOrder;
1857
2229
  const webPageMap = {};
1858
2230
  try {
1859
2231
  webPageMap[activeTabId] = await ctx.getPageData(activeTabId, {
1860
2232
  ...pageDataOptions || {},
1861
- __roverAllowExternalFetch: true
2233
+ __roverAllowExternalFetch: true,
2234
+ __roverExternalIntent: "auto",
2235
+ __roverExternalMessage: userInput
1862
2236
  });
1863
2237
  } catch {
1864
2238
  retry++;
@@ -1873,6 +2247,9 @@ async function executeAgenticSeek(options) {
1873
2247
  return { tabId: currentTabId, pageData: void 0 };
1874
2248
  }
1875
2249
  }));
2250
+ if (ctx.isCancelled?.()) {
2251
+ return { error: "Run cancelled", prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed };
2252
+ }
1876
2253
  for (const result of backgroundResults) {
1877
2254
  if (result.pageData) {
1878
2255
  webPageMap[result.tabId] = result.pageData;
@@ -1902,12 +2279,22 @@ async function executeAgenticSeek(options) {
1902
2279
  functionDeclarations,
1903
2280
  authToken: void 0,
1904
2281
  timestamp: ctx.userTimestamp,
1905
- trajectoryId: trajectoryId2,
1906
- userProfile: ctx.userProfile
2282
+ trajectoryId,
2283
+ userProfile: ctx.userProfile,
2284
+ stop: buildWorkerStopSignal({
2285
+ isCancelled: !!ctx.isCancelled?.()
2286
+ })
1907
2287
  };
1908
2288
  const response = await ctx.callExtensionRouter(SUB_AGENTS.processTabWorkflows, request);
2289
+ if (ctx.isCancelled?.()) {
2290
+ return { error: "Run cancelled", prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed };
2291
+ }
1909
2292
  if (!response?.success) {
1910
- return { error: response?.error || "Failed to process tab workflows", creditsUsed: totalCreditsUsed };
2293
+ return {
2294
+ error: response?.error || "Failed to process tab workflows",
2295
+ errorDetails: response?.errorDetails || void 0,
2296
+ creditsUsed: totalCreditsUsed
2297
+ };
1911
2298
  }
1912
2299
  const data = response.data;
1913
2300
  totalCreditsUsed += data?.creditsUsed || 0;
@@ -1918,6 +2305,10 @@ async function executeAgenticSeek(options) {
1918
2305
  }
1919
2306
  if (tabResponse?.warnings?.length)
1920
2307
  allWarnings.push(...tabResponse.warnings);
2308
+ if (tabResponse?.stopState && tabResponse.stopState !== "continue") {
2309
+ const stopReason = String(tabResponse.stopReason || tabResponse.error || "").trim() || `Execution stopped (${tabResponse.stopState})`;
2310
+ return { error: stopReason, prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed, warnings: allWarnings };
2311
+ }
1921
2312
  if (tabResponse?.error) {
1922
2313
  retry++;
1923
2314
  continue;
@@ -1929,15 +2320,58 @@ async function executeAgenticSeek(options) {
1929
2320
  prevSteps: accumulatedPrevSteps,
1930
2321
  thought: tabResponse.thought,
1931
2322
  bridgeRpc: bridgeRpc2,
2323
+ isCancelled: ctx.isCancelled,
1932
2324
  userFunctionDeclarations: functionDeclarations,
1933
2325
  onStatusUpdate,
1934
2326
  onPrevStepsUpdate
1935
2327
  });
2328
+ if (ctx.isCancelled?.()) {
2329
+ return { error: "Run cancelled", prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed };
2330
+ }
1936
2331
  if (processResult.needsRetry) {
1937
2332
  pageDataOptions = processResult.disableAutoScroll ? { disableAutoScroll: true } : void 0;
1938
2333
  retry++;
1939
2334
  continue;
1940
2335
  }
2336
+ if (processResult.navigationOccurred) {
2337
+ const navigationOutcome = processResult.navigationOutcome;
2338
+ const logicalTabId = Number(processResult.logicalTabId);
2339
+ if (Number.isFinite(logicalTabId) && logicalTabId > 0) {
2340
+ touchScopedTabIds([logicalTabId]);
2341
+ }
2342
+ if (navigationOutcome === "new_tab_opened" || navigationOutcome === "switch_tab") {
2343
+ if (Number.isFinite(logicalTabId) && logicalTabId > 0) {
2344
+ try {
2345
+ await bridgeRpc2("executeTool", {
2346
+ call: {
2347
+ name: "switch_tab",
2348
+ args: {
2349
+ logical_tab_id: logicalTabId,
2350
+ tab_id: logicalTabId
2351
+ }
2352
+ },
2353
+ payload: {
2354
+ reason: "act_loop_navigation_continue"
2355
+ }
2356
+ });
2357
+ } catch {
2358
+ }
2359
+ }
2360
+ pageDataOptions = processResult.disableAutoScroll ? { disableAutoScroll: true } : void 0;
2361
+ continue;
2362
+ }
2363
+ managePrevStepsSize(accumulatedPrevSteps);
2364
+ onPrevStepsUpdate?.(accumulatedPrevSteps);
2365
+ return {
2366
+ prevSteps: accumulatedPrevSteps,
2367
+ creditsUsed: totalCreditsUsed,
2368
+ warnings: allWarnings,
2369
+ navigationPending: true,
2370
+ navigationTool: processResult.navigationTool,
2371
+ navigationOutcome: processResult.navigationOutcome,
2372
+ logicalTabId: processResult.logicalTabId
2373
+ };
2374
+ }
1941
2375
  if (processResult.data) {
1942
2376
  managePrevStepsSize(accumulatedPrevSteps);
1943
2377
  onPrevStepsUpdate?.(accumulatedPrevSteps);
@@ -1966,10 +2400,19 @@ async function executeAgenticSeek(options) {
1966
2400
  }));
1967
2401
  const functionResults = {};
1968
2402
  for (const fc of functionCallsWithIds) {
2403
+ if (ctx.isCancelled?.()) {
2404
+ return { error: "Run cancelled", prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed };
2405
+ }
1969
2406
  try {
1970
2407
  const result = await bridgeRpc2("executeClientTool", { name: fc.name, args: fc.args });
2408
+ if (ctx.isCancelled?.()) {
2409
+ return { error: "Run cancelled", prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed };
2410
+ }
1971
2411
  functionResults[fc.callId] = { success: true, result };
1972
2412
  } catch (error) {
2413
+ if (ctx.isCancelled?.()) {
2414
+ return { error: "Run cancelled", prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed };
2415
+ }
1973
2416
  functionResults[fc.callId] = { success: false, error: { message: error?.message || String(error) } };
1974
2417
  }
1975
2418
  }
@@ -1984,7 +2427,12 @@ async function executeAgenticSeek(options) {
1984
2427
  }
1985
2428
  retry++;
1986
2429
  if (retry >= MAX_RETRIES) {
1987
- return { error: error?.message || "Agentic seek failed", prevSteps: accumulatedPrevSteps, creditsUsed: totalCreditsUsed };
2430
+ return {
2431
+ error: error?.message || "Agentic seek failed",
2432
+ errorDetails: error?.roverError || void 0,
2433
+ prevSteps: accumulatedPrevSteps,
2434
+ creditsUsed: totalCreditsUsed
2435
+ };
1988
2436
  }
1989
2437
  await new Promise((resolve) => setTimeout(resolve, 500 * retry));
1990
2438
  }
@@ -1994,19 +2442,57 @@ async function executeAgenticSeek(options) {
1994
2442
 
1995
2443
  // dist/agent/extractAgent.js
1996
2444
  var MAX_RETRIES2 = 3;
2445
+ function dedupePositiveTabIds3(input) {
2446
+ if (!Array.isArray(input))
2447
+ return [];
2448
+ const seen = /* @__PURE__ */ new Set();
2449
+ const out = [];
2450
+ for (const value of input) {
2451
+ const tabId = Number(value);
2452
+ if (!Number.isFinite(tabId) || tabId <= 0 || seen.has(tabId))
2453
+ continue;
2454
+ seen.add(tabId);
2455
+ out.push(tabId);
2456
+ }
2457
+ return out;
2458
+ }
1997
2459
  async function executeExtract(options) {
1998
- const { tabOrder, userInput, schema, outputDestination, trajectoryId: trajectoryId2, recordingContext, plannerPrevSteps = [], files = [], onStatusUpdate, schemaHeaderSheetInfo, returnDataOnly, previousSteps = [], bridgeRpc: bridgeRpc2, ctx, onPrevStepsUpdate } = options;
2460
+ const { tabOrder, scopedTabIds: scopedTabIds2, seedTabId, onScopedTabIdsTouched, userInput, schema, outputDestination, trajectoryId, recordingContext, plannerPrevSteps = [], files = [], onStatusUpdate, schemaHeaderSheetInfo, returnDataOnly, previousSteps = [], bridgeRpc: bridgeRpc2, ctx, onPrevStepsUpdate } = options;
1999
2461
  if (!tabOrder?.length) {
2000
2462
  return { error: "No tabs available for extraction", warnings: ["No active tab found"] };
2001
2463
  }
2002
2464
  let totalCreditsUsed = 0;
2003
2465
  const warnings = [];
2004
2466
  const fallbackTabs = tabOrder.map((id) => ({ id }));
2467
+ let runtimeScopedTabIds = dedupePositiveTabIds3(scopedTabIds2 || []);
2468
+ if (Number(seedTabId) > 0 && !runtimeScopedTabIds.includes(Number(seedTabId))) {
2469
+ runtimeScopedTabIds.unshift(Number(seedTabId));
2470
+ }
2471
+ const touchScopedTabIds = (tabIds) => {
2472
+ const touched = dedupePositiveTabIds3(tabIds);
2473
+ if (!touched.length)
2474
+ return;
2475
+ const nextScoped = dedupePositiveTabIds3([...runtimeScopedTabIds, ...touched]);
2476
+ if (nextScoped.length === runtimeScopedTabIds.length && nextScoped.every((id, index) => id === runtimeScopedTabIds[index])) {
2477
+ return;
2478
+ }
2479
+ runtimeScopedTabIds = nextScoped;
2480
+ onScopedTabIdsTouched?.(nextScoped);
2481
+ };
2005
2482
  const prevSteps = Array.isArray(previousSteps) ? previousSteps : [];
2006
2483
  let pageDataOptions;
2007
2484
  for (let retry = 0; retry < MAX_RETRIES2; retry++) {
2008
2485
  await waitWhilePaused(void 0);
2009
- const { activeTabId } = await resolveRuntimeTabs(bridgeRpc2, fallbackTabs);
2486
+ if (ctx.isCancelled?.()) {
2487
+ return { error: "Run cancelled", prevSteps, creditsUsed: totalCreditsUsed, warnings };
2488
+ }
2489
+ const { activeTabId } = await resolveRuntimeTabs(bridgeRpc2, fallbackTabs, {
2490
+ scopedTabIds: runtimeScopedTabIds,
2491
+ seedTabId
2492
+ });
2493
+ if (ctx.isCancelled?.()) {
2494
+ return { error: "Run cancelled", prevSteps, creditsUsed: totalCreditsUsed, warnings };
2495
+ }
2010
2496
  const tabId = activeTabId;
2011
2497
  let pageData;
2012
2498
  try {
@@ -2029,14 +2515,20 @@ async function executeExtract(options) {
2029
2515
  apiToolsConfig: ctx.apiToolsConfig,
2030
2516
  authToken: void 0,
2031
2517
  timestamp: ctx.userTimestamp,
2032
- trajectoryId: trajectoryId2,
2518
+ trajectoryId,
2033
2519
  userProfile: ctx.userProfile,
2034
2520
  recordingContext,
2035
2521
  files,
2036
- returnDataOnly
2522
+ returnDataOnly,
2523
+ stop: buildWorkerStopSignal({
2524
+ isCancelled: !!ctx.isCancelled?.()
2525
+ })
2037
2526
  };
2038
2527
  onStatusUpdate?.("Extracting data...", "Calling extract sub-agent", "execute");
2039
2528
  const response = await ctx.callExtensionRouter(SUB_AGENTS.extract, request);
2529
+ if (ctx.isCancelled?.()) {
2530
+ return { error: "Run cancelled", prevSteps, creditsUsed: totalCreditsUsed, warnings };
2531
+ }
2040
2532
  if (!response?.success) {
2041
2533
  return { error: response?.error || "Extract request failed", creditsUsed: totalCreditsUsed };
2042
2534
  }
@@ -2055,6 +2547,10 @@ async function executeExtract(options) {
2055
2547
  }
2056
2548
  const tabResponse = data?.tabResponses?.[activeTabId];
2057
2549
  if (tabResponse) {
2550
+ if (tabResponse?.stopState && tabResponse.stopState !== "continue") {
2551
+ const stopReason = String(tabResponse.stopReason || tabResponse.error || "").trim() || `Execution stopped (${tabResponse.stopState})`;
2552
+ return { error: stopReason, prevSteps, creditsUsed: totalCreditsUsed, warnings };
2553
+ }
2058
2554
  const processResult = await processActionResponse({
2059
2555
  request,
2060
2556
  response: tabResponse,
@@ -2062,12 +2558,22 @@ async function executeExtract(options) {
2062
2558
  prevSteps,
2063
2559
  thought: tabResponse.thought,
2064
2560
  bridgeRpc: bridgeRpc2,
2561
+ isCancelled: ctx.isCancelled,
2065
2562
  onPrevStepsUpdate
2066
2563
  });
2564
+ if (ctx.isCancelled?.()) {
2565
+ return { error: "Run cancelled", prevSteps, creditsUsed: totalCreditsUsed, warnings };
2566
+ }
2067
2567
  if (processResult.needsRetry) {
2068
2568
  pageDataOptions = processResult.disableAutoScroll ? { disableAutoScroll: true } : void 0;
2069
2569
  continue;
2070
2570
  }
2571
+ if (processResult.navigationOccurred) {
2572
+ const logicalTabId = Number(processResult.logicalTabId);
2573
+ if (Number.isFinite(logicalTabId) && logicalTabId > 0) {
2574
+ touchScopedTabIds([logicalTabId]);
2575
+ }
2576
+ }
2071
2577
  pageDataOptions = processResult.disableAutoScroll ? { disableAutoScroll: true } : void 0;
2072
2578
  if (processResult.data) {
2073
2579
  return {
@@ -2080,9 +2586,18 @@ async function executeExtract(options) {
2080
2586
  }
2081
2587
  if (processResult.functionCalls?.length) {
2082
2588
  for (const fc of processResult.functionCalls) {
2589
+ if (ctx.isCancelled?.()) {
2590
+ return { error: "Run cancelled", prevSteps, creditsUsed: totalCreditsUsed, warnings };
2591
+ }
2083
2592
  try {
2084
2593
  await bridgeRpc2("executeClientTool", { name: fc.name, args: fc.args });
2594
+ if (ctx.isCancelled?.()) {
2595
+ return { error: "Run cancelled", prevSteps, creditsUsed: totalCreditsUsed, warnings };
2596
+ }
2085
2597
  } catch {
2598
+ if (ctx.isCancelled?.()) {
2599
+ return { error: "Run cancelled", prevSteps, creditsUsed: totalCreditsUsed, warnings };
2600
+ }
2086
2601
  }
2087
2602
  }
2088
2603
  }
@@ -2258,8 +2773,14 @@ function attachSheetData(store, sheetInfo) {
2258
2773
 
2259
2774
  // dist/agent/sheetsWorkflowAgent.js
2260
2775
  async function executeSheetsWorkflow(options) {
2261
- const { workflow, userInput, trajectoryId: trajectoryId2, plannerPrevSteps, agentLog, files, onStatusUpdate, ctx, bridgeRpc: bridgeRpc2, driveAuthToken } = options;
2776
+ const { workflow, userInput, trajectoryId, plannerPrevSteps, agentLog, files, onStatusUpdate, ctx, bridgeRpc: bridgeRpc2, driveAuthToken } = options;
2777
+ const throwIfCancelled2 = () => {
2778
+ if (!ctx.isCancelled?.())
2779
+ return;
2780
+ throw new DOMException("Run cancelled", "AbortError");
2781
+ };
2262
2782
  try {
2783
+ throwIfCancelled2();
2263
2784
  const sourceSheetInfo = workflow.sourceSheetFromHistory ? resolveHistorySheetInfo(workflow.sourceSheetFromHistory, plannerPrevSteps) : void 0;
2264
2785
  let sheetId = sourceSheetInfo?.sheetId || workflow.sheetId;
2265
2786
  let sheetTabTitle = sourceSheetInfo?.sheetTab || workflow.sheetTabTitle;
@@ -2293,6 +2814,7 @@ async function executeSheetsWorkflow(options) {
2293
2814
  if (!sheetTabTitle) {
2294
2815
  return { error: "Could not resolve sheet tab title for workflow." };
2295
2816
  }
2817
+ throwIfCancelled2();
2296
2818
  onStatusUpdate?.("Loading sheet rows...", sheetTabTitle, "analyze");
2297
2819
  grid = await fetchWorkflowSheetTabDataSmart(sheetId, sheetTabTitle, getAuthToken) || [];
2298
2820
  }
@@ -2311,6 +2833,7 @@ async function executeSheetsWorkflow(options) {
2311
2833
  const outputRows = [];
2312
2834
  const newTabOutputs = {};
2313
2835
  for (let rowIdx = startRow; rowIdx < endRow; rowIdx++) {
2836
+ throwIfCancelled2();
2314
2837
  const row = dataRows[rowIdx] || [];
2315
2838
  const rowData = buildRowData(header, row);
2316
2839
  const context = buildContextData(header, row, contextIndices);
@@ -2323,11 +2846,13 @@ async function executeSheetsWorkflow(options) {
2323
2846
  const stepOutputs = {};
2324
2847
  const rowOutputValues = [];
2325
2848
  for (const step of workflow.workflowSteps) {
2849
+ throwIfCancelled2();
2326
2850
  const stepName = step.stepName || step.tool;
2327
2851
  const resolvedUserInput = resolveTemplate(step.userInputTemplate || userInput, rowContext, stepOutputs);
2328
2852
  if (step.tabManagement?.urlTemplate) {
2329
2853
  const targetUrl = resolveTemplate(step.tabManagement.urlTemplate, rowContext, stepOutputs);
2330
2854
  if (targetUrl) {
2855
+ throwIfCancelled2();
2331
2856
  await bridgeRpc2("executeTool", { call: { name: "goto_url", args: { tab_id: 0, url: targetUrl } } });
2332
2857
  }
2333
2858
  }
@@ -2335,7 +2860,7 @@ async function executeSheetsWorkflow(options) {
2335
2860
  const stepResult = await executeWorkflowStep({
2336
2861
  step,
2337
2862
  userInput: resolvedUserInput,
2338
- trajectoryId: trajectoryId2,
2863
+ trajectoryId,
2339
2864
  plannerPrevSteps,
2340
2865
  agentLog,
2341
2866
  files,
@@ -2346,6 +2871,7 @@ async function executeSheetsWorkflow(options) {
2346
2871
  toolArgs: resolvedToolArgs
2347
2872
  });
2348
2873
  stepOutputs[stepName] = stepResult;
2874
+ throwIfCancelled2();
2349
2875
  if (step.outputMapping === SheetOutputFormat.CONTEXT) {
2350
2876
  continue;
2351
2877
  }
@@ -2364,6 +2890,7 @@ async function executeSheetsWorkflow(options) {
2364
2890
  outputRows.push(rowOutputValues);
2365
2891
  }
2366
2892
  if (useMemory) {
2893
+ throwIfCancelled2();
2367
2894
  const store = ctx.tabularStore;
2368
2895
  const baseTab = sheetTabId !== void 0 ? store.getTab(sheetId, sheetTabId) : store.getTabByTitle(sheetId, sheetTabTitle) || store.getTab(sheetId, 0);
2369
2896
  if (outputColumnHeaders.length) {
@@ -2402,6 +2929,7 @@ async function executeSheetsWorkflow(options) {
2402
2929
  };
2403
2930
  }
2404
2931
  if (outputColumnHeaders.length) {
2932
+ throwIfCancelled2();
2405
2933
  await appendColumnsToSheet({
2406
2934
  sheetId,
2407
2935
  sheetTabTitle,
@@ -2413,6 +2941,7 @@ async function executeSheetsWorkflow(options) {
2413
2941
  });
2414
2942
  }
2415
2943
  for (const [stepName, rows] of Object.entries(newTabOutputs)) {
2944
+ throwIfCancelled2();
2416
2945
  const title = truncateTabTitle(stepName);
2417
2946
  const newTitle = await createSheetTab(sheetId, title, getAuthToken);
2418
2947
  const headerRowValues = buildNewTabHeader(rows);
@@ -2443,7 +2972,10 @@ async function executeSheetsWorkflow(options) {
2443
2972
  return { error: error?.message || String(error) };
2444
2973
  }
2445
2974
  }
2446
- async function executeWorkflowStep({ step, userInput, trajectoryId: trajectoryId2, plannerPrevSteps, agentLog, files, onStatusUpdate, ctx, bridgeRpc: bridgeRpc2, driveAuthToken, toolArgs }) {
2975
+ async function executeWorkflowStep({ step, userInput, trajectoryId, plannerPrevSteps, agentLog, files, onStatusUpdate, ctx, bridgeRpc: bridgeRpc2, driveAuthToken, toolArgs }) {
2976
+ if (ctx.isCancelled?.()) {
2977
+ throw new DOMException("Run cancelled", "AbortError");
2978
+ }
2447
2979
  const toolName = step.tool;
2448
2980
  if (toolName === PLANNER_FUNCTION_CALLS.PROCESS_TEXT) {
2449
2981
  const result = await executeToolFromPlan({
@@ -2455,7 +2987,7 @@ async function executeWorkflowStep({ step, userInput, trajectoryId: trajectoryId
2455
2987
  },
2456
2988
  userInput,
2457
2989
  tabs: [{ id: 1 }],
2458
- trajectoryId: trajectoryId2,
2990
+ trajectoryId,
2459
2991
  plannerPrevSteps,
2460
2992
  agentLog,
2461
2993
  files,
@@ -2480,7 +3012,7 @@ async function executeWorkflowStep({ step, userInput, trajectoryId: trajectoryId
2480
3012
  },
2481
3013
  userInput,
2482
3014
  tabs: [{ id: 1 }],
2483
- trajectoryId: trajectoryId2,
3015
+ trajectoryId,
2484
3016
  plannerPrevSteps,
2485
3017
  agentLog,
2486
3018
  files,
@@ -2495,7 +3027,13 @@ async function executeWorkflowStep({ step, userInput, trajectoryId: trajectoryId
2495
3027
  return result.output ?? result;
2496
3028
  }
2497
3029
  if (toolArgs || step.toolArgs || typeof toolName === "string") {
3030
+ if (ctx.isCancelled?.()) {
3031
+ throw new DOMException("Run cancelled", "AbortError");
3032
+ }
2498
3033
  const result = await bridgeRpc2("executeClientTool", { name: toolName, args: toolArgs || step.toolArgs || {} });
3034
+ if (ctx.isCancelled?.()) {
3035
+ throw new DOMException("Run cancelled", "AbortError");
3036
+ }
2499
3037
  return result;
2500
3038
  }
2501
3039
  return { error: `Unsupported step tool: ${toolName}` };
@@ -2757,28 +3295,98 @@ function normalizeAgentLog(agentLog) {
2757
3295
  continue;
2758
3296
  dedupedChatLog.push(entry);
2759
3297
  }
2760
- const firstUser = dedupedChatLog.find((entry) => entry.role === "user");
2761
- const tailBudget = firstUser ? Math.max(1, MAX_AGENT_CHATLOG_ENTRIES - 1) : MAX_AGENT_CHATLOG_ENTRIES;
2762
- let chatLog = dedupedChatLog.slice(-tailBudget);
2763
- if (firstUser) {
2764
- const hasAnchor = chatLog.some((entry) => entry.role === "user" && entry.message === firstUser.message);
2765
- if (!hasAnchor) {
2766
- chatLog = [firstUser, ...chatLog];
2767
- }
3298
+ let chatLog = dedupedChatLog.slice(-MAX_AGENT_CHATLOG_ENTRIES);
3299
+ const seen = /* @__PURE__ */ new Set();
3300
+ const compactedReverse = [];
3301
+ for (let i = chatLog.length - 1; i >= 0; i -= 1) {
3302
+ const entry = chatLog[i];
3303
+ const key = `${entry.role}::${entry.message.toLowerCase()}`;
3304
+ if (seen.has(key))
3305
+ continue;
3306
+ seen.add(key);
3307
+ compactedReverse.push(entry);
2768
3308
  }
3309
+ chatLog = compactedReverse.reverse();
2769
3310
  if (chatLog.length > MAX_AGENT_CHATLOG_ENTRIES) {
2770
3311
  chatLog = chatLog.slice(-MAX_AGENT_CHATLOG_ENTRIES);
2771
3312
  }
2772
3313
  return { prevSteps, chatLog };
2773
3314
  }
3315
+ function isExecutionCancelled(ctx) {
3316
+ if (!ctx?.isCancelled)
3317
+ return false;
3318
+ try {
3319
+ return !!ctx.isCancelled();
3320
+ } catch {
3321
+ return false;
3322
+ }
3323
+ }
3324
+ function throwIfExecutionCancelled(ctx) {
3325
+ if (!isExecutionCancelled(ctx))
3326
+ return;
3327
+ throw new DOMException("Run cancelled", "AbortError");
3328
+ }
3329
+ function cancelledToolResult() {
3330
+ return { error: "Run cancelled" };
3331
+ }
3332
+ async function executeRoverExternalContextPlannerTool(params) {
3333
+ const bridge = params.bridgeRpc;
3334
+ if (!bridge) {
3335
+ return { error: `Bridge RPC unavailable for ${params.toolName}` };
3336
+ }
3337
+ const message = String(params.toolArgs?.message || params.toolArgs?.user_input || params.toolArgs?.task_instruction || params.userInput || "").trim();
3338
+ params.onStatusUpdate?.(params.toolName === PLANNER_FUNCTION_CALLS.ROVER_EXTERNAL_ACT_CONTEXT ? "Fetching external action context..." : "Fetching external context...", `Calling ${params.toolName}`, "execute");
3339
+ const routedResponse = await bridge("executeTool", {
3340
+ call: {
3341
+ name: params.toolName,
3342
+ args: {
3343
+ ...params.toolArgs,
3344
+ ...message ? { message } : {}
3345
+ }
3346
+ }
3347
+ });
3348
+ const success = routedResponse?.success === true;
3349
+ if (!success) {
3350
+ const errorMessage = String(routedResponse?.output?.error?.message || routedResponse?.error || `${params.toolName} failed`);
3351
+ return {
3352
+ error: errorMessage,
3353
+ output: routedResponse?.output,
3354
+ warnings: errorMessage ? [errorMessage] : void 0
3355
+ };
3356
+ }
3357
+ return {
3358
+ output: routedResponse?.output,
3359
+ warnings: Array.isArray(routedResponse?.output?.warnings) ? routedResponse.output.warnings : void 0
3360
+ };
3361
+ }
3362
+ async function callExtensionRouterWithCancel(ctx, action, request) {
3363
+ throwIfExecutionCancelled(ctx);
3364
+ const response = await ctx.callExtensionRouter(action, request);
3365
+ throwIfExecutionCancelled(ctx);
3366
+ return response;
3367
+ }
2774
3368
  async function executeToolFromPlan(context) {
2775
- const { toolName, toolArgs, userInput, tabs, trajectoryId: trajectoryId2, plannerPrevSteps, files, onStatusUpdate, recordingContext, toolFunctions, bridgeRpc: bridgeRpc2, ctx, functionDeclarations, driveAuthToken, agentLog, onPrevStepsUpdate } = context;
3369
+ const { toolName, toolArgs, userInput, tabs, scopedTabIds: scopedTabIds2, seedTabId, getScopedTabRuntimeContext, onScopedTabIdsTouched, trajectoryId, plannerPrevSteps, files, onStatusUpdate, recordingContext, toolFunctions, bridgeRpc: bridgeRpc2, ctx, functionDeclarations, driveAuthToken, agentLog, onPrevStepsUpdate } = context;
2776
3370
  const effectiveCtx = ctx;
2777
3371
  if (!effectiveCtx) {
2778
3372
  return { error: "Agent context unavailable" };
2779
3373
  }
3374
+ if (isExecutionCancelled(effectiveCtx)) {
3375
+ return cancelledToolResult();
3376
+ }
2780
3377
  const fallbackTabs = Array.isArray(tabs) && tabs.length ? tabs : [{ id: 1 }];
2781
- const resolvedTabs = await resolveRuntimeTabs(bridgeRpc2, fallbackTabs);
3378
+ const runtimeScope = getScopedTabRuntimeContext?.() || {};
3379
+ const scopedTabIdsInput = runtimeScope.scopedTabIds ?? scopedTabIds2;
3380
+ const seedTabIdInput = runtimeScope.seedTabId ?? seedTabId;
3381
+ const resolvedScopedTabIds = Array.isArray(scopedTabIdsInput) && scopedTabIdsInput.length ? Array.from(new Set(scopedTabIdsInput.map((tabId) => Number(tabId)).filter((tabId) => Number.isFinite(tabId) && tabId > 0))) : Array.from(new Set(fallbackTabs.map((tab) => Number(tab?.id)).filter((tabId) => Number.isFinite(tabId) && tabId > 0)));
3382
+ const resolvedSeedTabId = Number(seedTabIdInput) > 0 ? Number(seedTabIdInput) : resolvedScopedTabIds[0];
3383
+ const resolvedTabs = await resolveRuntimeTabs(bridgeRpc2, fallbackTabs, {
3384
+ scopedTabIds: resolvedScopedTabIds,
3385
+ seedTabId: resolvedSeedTabId
3386
+ });
3387
+ if (isExecutionCancelled(effectiveCtx)) {
3388
+ return cancelledToolResult();
3389
+ }
2782
3390
  const tabOrder = resolvedTabs.tabOrder.length ? resolvedTabs.tabOrder : fallbackTabs.map((tab) => tab.id);
2783
3391
  const effectiveAgentLog = normalizeAgentLog(agentLog);
2784
3392
  try {
@@ -2787,6 +3395,9 @@ async function executeToolFromPlan(context) {
2787
3395
  const prompt = toolArgs?.user_input || toolArgs?.prompt || toolArgs?.task_instruction || userInput;
2788
3396
  const actResult = await executeAgenticSeek({
2789
3397
  tabOrder,
3398
+ scopedTabIds: resolvedScopedTabIds,
3399
+ seedTabId: resolvedSeedTabId,
3400
+ onScopedTabIdsTouched,
2790
3401
  userInput: prompt,
2791
3402
  schema: toolArgs?.schema,
2792
3403
  previousSteps: effectiveAgentLog.prevSteps,
@@ -2794,7 +3405,7 @@ async function executeToolFromPlan(context) {
2794
3405
  files,
2795
3406
  chatLog: effectiveAgentLog.chatLog,
2796
3407
  recordingContext,
2797
- trajectoryId: trajectoryId2,
3408
+ trajectoryId,
2798
3409
  onStatusUpdate,
2799
3410
  functionDeclarations,
2800
3411
  bridgeRpc: bridgeRpc2,
@@ -2805,6 +3416,13 @@ async function executeToolFromPlan(context) {
2805
3416
  status: "waiting_input",
2806
3417
  needsUserInput: true,
2807
3418
  questions: Array.isArray(actResult.questions) ? actResult.questions : []
3419
+ } : actResult.navigationPending ? {
3420
+ status: "in_progress",
3421
+ taskStatus: "in_progress",
3422
+ navigationPending: true,
3423
+ navigationTool: actResult.navigationTool,
3424
+ navigationOutcome: actResult.navigationOutcome,
3425
+ logicalTabId: actResult.logicalTabId
2808
3426
  } : void 0);
2809
3427
  return { ...actResult, output: actOutput };
2810
3428
  }
@@ -2816,6 +3434,9 @@ async function executeToolFromPlan(context) {
2816
3434
  const returnDataOnly = toolArgs?.return_data_only ?? toolArgs?.returnDataOnly ?? shouldMemory;
2817
3435
  const extractResult = await executeExtract({
2818
3436
  tabOrder,
3437
+ scopedTabIds: resolvedScopedTabIds,
3438
+ seedTabId: resolvedSeedTabId,
3439
+ onScopedTabIdsTouched,
2819
3440
  userInput: prompt,
2820
3441
  schema: toolArgs?.schema,
2821
3442
  outputDestination,
@@ -2824,7 +3445,7 @@ async function executeToolFromPlan(context) {
2824
3445
  files,
2825
3446
  recordingContext,
2826
3447
  previousSteps: effectiveAgentLog.prevSteps,
2827
- trajectoryId: trajectoryId2,
3448
+ trajectoryId,
2828
3449
  returnDataOnly,
2829
3450
  onStatusUpdate,
2830
3451
  bridgeRpc: bridgeRpc2,
@@ -2871,7 +3492,7 @@ async function executeToolFromPlan(context) {
2871
3492
  files,
2872
3493
  recordingContext,
2873
3494
  previousSteps: effectiveAgentLog.prevSteps,
2874
- trajectoryId: trajectoryId2,
3495
+ trajectoryId,
2875
3496
  onStatusUpdate,
2876
3497
  returnDataOnly,
2877
3498
  bridgeRpc: bridgeRpc2,
@@ -2913,13 +3534,23 @@ async function executeToolFromPlan(context) {
2913
3534
  schema: toolArgs?.schema,
2914
3535
  plannerPrevSteps,
2915
3536
  files,
2916
- trajectoryId: trajectoryId2,
3537
+ trajectoryId,
2917
3538
  onStatusUpdate,
2918
3539
  agentLog: effectiveAgentLog,
2919
3540
  ctx: effectiveCtx,
2920
3541
  driveAuthToken: toolArgs?.authToken || toolArgs?.driveAuthToken || driveAuthToken
2921
3542
  });
2922
3543
  }
3544
+ case PLANNER_FUNCTION_CALLS.ROVER_EXTERNAL_READ_CONTEXT:
3545
+ case PLANNER_FUNCTION_CALLS.ROVER_EXTERNAL_ACT_CONTEXT: {
3546
+ return executeRoverExternalContextPlannerTool({
3547
+ toolName,
3548
+ toolArgs: toolArgs || {},
3549
+ userInput,
3550
+ bridgeRpc: bridgeRpc2,
3551
+ onStatusUpdate
3552
+ });
3553
+ }
2923
3554
  case PLANNER_FUNCTION_CALLS.CREATE_SHEET_FROM_DATA: {
2924
3555
  const dataInputs = toolArgs?.data_inputs || toolArgs?.dataInputs || [];
2925
3556
  const taskInstruction = toolArgs?.task_instruction || toolArgs?.taskInstruction || userInput;
@@ -2930,7 +3561,7 @@ async function executeToolFromPlan(context) {
2930
3561
  outputSheetParameters: toolArgs?.output_sheet_parameters || toolArgs?.outputSheetParameters,
2931
3562
  plannerPrevSteps,
2932
3563
  files,
2933
- trajectoryId: trajectoryId2,
3564
+ trajectoryId,
2934
3565
  onStatusUpdate,
2935
3566
  agentLog: effectiveAgentLog,
2936
3567
  ctx: effectiveCtx,
@@ -2942,7 +3573,7 @@ async function executeToolFromPlan(context) {
2942
3573
  toolArgs,
2943
3574
  plannerPrevSteps,
2944
3575
  files,
2945
- trajectoryId: trajectoryId2,
3576
+ trajectoryId,
2946
3577
  onStatusUpdate,
2947
3578
  agentLog: effectiveAgentLog,
2948
3579
  ctx: effectiveCtx,
@@ -2951,7 +3582,7 @@ async function executeToolFromPlan(context) {
2951
3582
  }
2952
3583
  case PLANNER_FUNCTION_CALLS.QUERY_RTRVR_AI_DOCUMENTATION: {
2953
3584
  const userQuestion = toolArgs?.user_question || toolArgs?.userQuestion || userInput;
2954
- return executeQueryDocs({ userQuestion, trajectoryId: trajectoryId2, plannerPrevSteps, onStatusUpdate, agentLog: effectiveAgentLog, ctx: effectiveCtx });
3585
+ return executeQueryDocs({ userQuestion, trajectoryId, plannerPrevSteps, onStatusUpdate, agentLog: effectiveAgentLog, ctx: effectiveCtx });
2955
3586
  }
2956
3587
  case PLANNER_FUNCTION_CALLS.GOOGLE_DOC_GENERATOR: {
2957
3588
  const prompt = toolArgs?.user_input || toolArgs?.prompt || toolArgs?.task_instruction || userInput;
@@ -2961,7 +3592,7 @@ async function executeToolFromPlan(context) {
2961
3592
  tabOrder,
2962
3593
  plannerPrevSteps,
2963
3594
  files,
2964
- trajectoryId: trajectoryId2,
3595
+ trajectoryId,
2965
3596
  onStatusUpdate,
2966
3597
  agentLog: effectiveAgentLog,
2967
3598
  ctx: effectiveCtx,
@@ -2976,7 +3607,7 @@ async function executeToolFromPlan(context) {
2976
3607
  tabOrder,
2977
3608
  plannerPrevSteps,
2978
3609
  files,
2979
- trajectoryId: trajectoryId2,
3610
+ trajectoryId,
2980
3611
  onStatusUpdate,
2981
3612
  agentLog: effectiveAgentLog,
2982
3613
  ctx: effectiveCtx,
@@ -2991,7 +3622,7 @@ async function executeToolFromPlan(context) {
2991
3622
  tabOrder,
2992
3623
  plannerPrevSteps,
2993
3624
  files,
2994
- trajectoryId: trajectoryId2,
3625
+ trajectoryId,
2995
3626
  onStatusUpdate,
2996
3627
  agentLog: effectiveAgentLog,
2997
3628
  ctx: effectiveCtx
@@ -3005,7 +3636,7 @@ async function executeToolFromPlan(context) {
3005
3636
  tabOrder,
3006
3637
  plannerPrevSteps,
3007
3638
  files,
3008
- trajectoryId: trajectoryId2,
3639
+ trajectoryId,
3009
3640
  onStatusUpdate,
3010
3641
  agentLog: effectiveAgentLog,
3011
3642
  ctx: effectiveCtx,
@@ -3018,7 +3649,7 @@ async function executeToolFromPlan(context) {
3018
3649
  userInput: prompt,
3019
3650
  plannerPrevSteps,
3020
3651
  files,
3021
- trajectoryId: trajectoryId2,
3652
+ trajectoryId,
3022
3653
  onStatusUpdate,
3023
3654
  agentLog: effectiveAgentLog,
3024
3655
  ctx: effectiveCtx
@@ -3029,7 +3660,7 @@ async function executeToolFromPlan(context) {
3029
3660
  return executeSheetsWorkflow({
3030
3661
  workflow,
3031
3662
  userInput,
3032
- trajectoryId: trajectoryId2,
3663
+ trajectoryId,
3033
3664
  plannerPrevSteps,
3034
3665
  files,
3035
3666
  onStatusUpdate,
@@ -3047,6 +3678,9 @@ async function executeToolFromPlan(context) {
3047
3678
  const results = [];
3048
3679
  if (Array.isArray(toolCalls)) {
3049
3680
  for (const call of toolCalls) {
3681
+ if (isExecutionCancelled(effectiveCtx)) {
3682
+ return { error: "Run cancelled", output: results };
3683
+ }
3050
3684
  const name = call.tool_name || call.name;
3051
3685
  const args = call.tool_args || call.args || {};
3052
3686
  try {
@@ -3056,8 +3690,14 @@ async function executeToolFromPlan(context) {
3056
3690
  } else {
3057
3691
  res = await bridgeRpc2?.("executeClientTool", { name, args });
3058
3692
  }
3693
+ if (isExecutionCancelled(effectiveCtx)) {
3694
+ return { error: "Run cancelled", output: results };
3695
+ }
3059
3696
  results.push({ name, result: res, success: true });
3060
3697
  } catch (error) {
3698
+ if (isExecutionCancelled(effectiveCtx)) {
3699
+ return { error: "Run cancelled", output: results };
3700
+ }
3061
3701
  results.push({ name, error: error?.message || String(error), success: false });
3062
3702
  }
3063
3703
  }
@@ -3065,12 +3705,18 @@ async function executeToolFromPlan(context) {
3065
3705
  return { output: results };
3066
3706
  }
3067
3707
  case PLANNER_FUNCTION_CALLS.CONFIGURE_API_KEY: {
3068
- return unsupportedToolResult(PLANNER_FUNCTION_CALLS.CONFIGURE_API_KEY, "configure_api_key is not supported in rover embed mode. Provide apiKey via rover.boot(...) or rover.update(...).");
3708
+ return unsupportedToolResult(PLANNER_FUNCTION_CALLS.CONFIGURE_API_KEY, "configure_api_key is not supported in rover embed mode. Provide publicKey (pk_site_*) via rover.boot(...) or an rvrsess_* sessionToken.");
3069
3709
  }
3070
3710
  default: {
3071
3711
  if (toolFunctions && toolFunctions[toolName]) {
3072
3712
  try {
3713
+ if (isExecutionCancelled(effectiveCtx)) {
3714
+ return cancelledToolResult();
3715
+ }
3073
3716
  const result = await bridgeRpc2?.("executeClientTool", { name: toolName, args: toolArgs });
3717
+ if (isExecutionCancelled(effectiveCtx)) {
3718
+ return cancelledToolResult();
3719
+ }
3074
3720
  return { output: result };
3075
3721
  } catch (error) {
3076
3722
  return { error: error?.message || String(error) };
@@ -3091,7 +3737,7 @@ async function executeToolFromPlan(context) {
3091
3737
  };
3092
3738
  }
3093
3739
  }
3094
- async function executeProcessText({ textInputs, taskInstruction, schema, plannerPrevSteps, files, trajectoryId: trajectoryId2, onStatusUpdate, agentLog, ctx, driveAuthToken }) {
3740
+ async function executeProcessText({ textInputs, taskInstruction, schema, plannerPrevSteps, files, trajectoryId, onStatusUpdate, agentLog, ctx, driveAuthToken }) {
3095
3741
  onStatusUpdate?.("Processing text...", "Calling process_text", "execute");
3096
3742
  const request = {
3097
3743
  siteId: ctx.siteId,
@@ -3108,11 +3754,11 @@ async function executeProcessText({ textInputs, taskInstruction, schema, planner
3108
3754
  apiToolsConfig: ctx.apiToolsConfig,
3109
3755
  authToken: driveAuthToken || "",
3110
3756
  timestamp: ctx.userTimestamp,
3111
- trajectoryId: trajectoryId2,
3757
+ trajectoryId,
3112
3758
  userProfile: ctx.userProfile,
3113
3759
  files
3114
3760
  };
3115
- const response = await ctx.callExtensionRouter(SUB_AGENTS.processText, request);
3761
+ const response = await callExtensionRouterWithCancel(ctx, SUB_AGENTS.processText, request);
3116
3762
  if (!response?.success)
3117
3763
  return { error: response?.error || "process_text failed" };
3118
3764
  return {
@@ -3123,7 +3769,7 @@ async function executeProcessText({ textInputs, taskInstruction, schema, planner
3123
3769
  warnings: response.data?.warnings
3124
3770
  };
3125
3771
  }
3126
- async function executeCreateSheetFromData({ dataInputs, taskInstruction, schema, outputSheetParameters, plannerPrevSteps, files, trajectoryId: trajectoryId2, onStatusUpdate, agentLog, ctx, driveAuthToken }) {
3772
+ async function executeCreateSheetFromData({ dataInputs, taskInstruction, schema, outputSheetParameters, plannerPrevSteps, files, trajectoryId, onStatusUpdate, agentLog, ctx, driveAuthToken }) {
3127
3773
  onStatusUpdate?.("Creating sheet...", "Calling create_sheet_from_data", "execute");
3128
3774
  const memoryTarget = resolveMemoryTarget(outputSheetParameters, plannerPrevSteps);
3129
3775
  const useMemory = ctx.apiMode || !driveAuthToken || memoryTarget.sheetId && isMemorySheetId(memoryTarget.sheetId);
@@ -3143,12 +3789,12 @@ async function executeCreateSheetFromData({ dataInputs, taskInstruction, schema,
3143
3789
  apiToolsConfig: ctx.apiToolsConfig,
3144
3790
  authToken: driveAuthToken || "",
3145
3791
  timestamp: ctx.userTimestamp,
3146
- trajectoryId: trajectoryId2,
3792
+ trajectoryId,
3147
3793
  userProfile: ctx.userProfile,
3148
3794
  files,
3149
3795
  returnDataOnly: useMemory
3150
3796
  };
3151
- const response = await ctx.callExtensionRouter(SUB_AGENTS.createSheetFromData, request);
3797
+ const response = await callExtensionRouterWithCancel(ctx, SUB_AGENTS.createSheetFromData, request);
3152
3798
  if (!response?.success)
3153
3799
  return { error: response?.error || "create_sheet_from_data failed" };
3154
3800
  if (useMemory) {
@@ -3189,7 +3835,7 @@ async function executeCreateSheetFromData({ dataInputs, taskInstruction, schema,
3189
3835
  schemaHeaderSheetInfo: response.data?.schemaHeaderSheetInfo
3190
3836
  };
3191
3837
  }
3192
- async function executeInferSheetData({ toolArgs, plannerPrevSteps, files, trajectoryId: trajectoryId2, onStatusUpdate, agentLog, ctx, driveAuthToken }) {
3838
+ async function executeInferSheetData({ toolArgs, plannerPrevSteps, files, trajectoryId, onStatusUpdate, agentLog, ctx, driveAuthToken }) {
3193
3839
  onStatusUpdate?.("Inferring sheet data...", "Calling infer_sheet_data", "execute");
3194
3840
  const sourceSheetRef = toolArgs?.source_sheet_from_history || toolArgs?.sourceSheetFromHistory;
3195
3841
  const resolvedSheetInfo = sourceSheetRef ? resolveHistorySheetInfo(sourceSheetRef, plannerPrevSteps) : void 0;
@@ -3224,11 +3870,11 @@ async function executeInferSheetData({ toolArgs, plannerPrevSteps, files, trajec
3224
3870
  apiToolsConfig: ctx.apiToolsConfig,
3225
3871
  authToken: driveAuthToken || "",
3226
3872
  timestamp: ctx.userTimestamp,
3227
- trajectoryId: trajectoryId2,
3873
+ trajectoryId,
3228
3874
  userProfile: ctx.userProfile,
3229
3875
  files
3230
3876
  };
3231
- const response = await ctx.callExtensionRouter(SUB_AGENTS.infer, request);
3877
+ const response = await callExtensionRouterWithCancel(ctx, SUB_AGENTS.infer, request);
3232
3878
  if (!response?.success)
3233
3879
  return { error: response?.error || "infer_sheet_data failed" };
3234
3880
  const inferredItems = Array.isArray(response.data?.data) ? response.data.data : [];
@@ -3307,7 +3953,7 @@ async function executeInferSheetData({ toolArgs, plannerPrevSteps, files, trajec
3307
3953
  warnings: response.data?.warnings
3308
3954
  };
3309
3955
  }
3310
- async function executeQueryDocs({ userQuestion, trajectoryId: trajectoryId2, plannerPrevSteps, onStatusUpdate, agentLog, ctx }) {
3956
+ async function executeQueryDocs({ userQuestion, trajectoryId, plannerPrevSteps, onStatusUpdate, agentLog, ctx }) {
3311
3957
  onStatusUpdate?.("Querying docs...", "Calling query_rtrvr_docs", "execute");
3312
3958
  const request = {
3313
3959
  siteId: ctx.siteId,
@@ -3322,10 +3968,10 @@ async function executeQueryDocs({ userQuestion, trajectoryId: trajectoryId2, pla
3322
3968
  apiToolsConfig: ctx.apiToolsConfig,
3323
3969
  authToken: "",
3324
3970
  timestamp: ctx.userTimestamp,
3325
- trajectoryId: trajectoryId2,
3971
+ trajectoryId,
3326
3972
  userProfile: ctx.userProfile
3327
3973
  };
3328
- const response = await ctx.callExtensionRouter(SUB_AGENTS.queryRtrvrDocs, request);
3974
+ const response = await callExtensionRouterWithCancel(ctx, SUB_AGENTS.queryRtrvrDocs, request);
3329
3975
  if (!response?.success)
3330
3976
  return { error: response?.error || "query_rtrvr_docs failed" };
3331
3977
  return {
@@ -3382,7 +4028,7 @@ Slide 3: Summary`;
3382
4028
  warnings: [warning]
3383
4029
  };
3384
4030
  }
3385
- async function executeDocGenerator({ userInput, toolArgs, tabOrder, plannerPrevSteps, files, trajectoryId: trajectoryId2, onStatusUpdate, agentLog, ctx, driveAuthToken }) {
4031
+ async function executeDocGenerator({ userInput, toolArgs, tabOrder, plannerPrevSteps, files, trajectoryId, onStatusUpdate, agentLog, ctx, driveAuthToken }) {
3386
4032
  onStatusUpdate?.("Generating doc...", "Calling google_doc_generator", "execute");
3387
4033
  if (!driveAuthToken) {
3388
4034
  return buildMissingAuthToolResult("google_doc_generator", ["authToken"]);
@@ -3404,13 +4050,13 @@ async function executeDocGenerator({ userInput, toolArgs, tabOrder, plannerPrevS
3404
4050
  apiToolsConfig: ctx.apiToolsConfig,
3405
4051
  authToken: driveAuthToken || "",
3406
4052
  timestamp: ctx.userTimestamp,
3407
- trajectoryId: trajectoryId2,
4053
+ trajectoryId,
3408
4054
  userProfile: ctx.userProfile,
3409
4055
  files
3410
4056
  };
3411
4057
  let response;
3412
4058
  try {
3413
- response = await ctx.callExtensionRouter(SUB_AGENTS.googleDocGenerator, request);
4059
+ response = await callExtensionRouterWithCancel(ctx, SUB_AGENTS.googleDocGenerator, request);
3414
4060
  } catch (err) {
3415
4061
  if (shouldFallbackToMemoryArtifact(err)) {
3416
4062
  const envelope2 = toRoverErrorEnvelope(err);
@@ -3433,7 +4079,7 @@ async function executeDocGenerator({ userInput, toolArgs, tabOrder, plannerPrevS
3433
4079
  warnings: response.data?.warnings
3434
4080
  };
3435
4081
  }
3436
- async function executeSlidesGenerator({ userInput, toolArgs, tabOrder, plannerPrevSteps, files, trajectoryId: trajectoryId2, onStatusUpdate, agentLog, ctx, driveAuthToken }) {
4082
+ async function executeSlidesGenerator({ userInput, toolArgs, tabOrder, plannerPrevSteps, files, trajectoryId, onStatusUpdate, agentLog, ctx, driveAuthToken }) {
3437
4083
  onStatusUpdate?.("Generating slides...", "Calling google_slides_generator", "execute");
3438
4084
  if (!driveAuthToken) {
3439
4085
  return buildMissingAuthToolResult("google_slides_generator", ["authToken"]);
@@ -3455,13 +4101,13 @@ async function executeSlidesGenerator({ userInput, toolArgs, tabOrder, plannerPr
3455
4101
  apiToolsConfig: ctx.apiToolsConfig,
3456
4102
  authToken: driveAuthToken || "",
3457
4103
  timestamp: ctx.userTimestamp,
3458
- trajectoryId: trajectoryId2,
4104
+ trajectoryId,
3459
4105
  userProfile: ctx.userProfile,
3460
4106
  files
3461
4107
  };
3462
4108
  let response;
3463
4109
  try {
3464
- response = await ctx.callExtensionRouter(SUB_AGENTS.googleSlidesGenerator, request);
4110
+ response = await callExtensionRouterWithCancel(ctx, SUB_AGENTS.googleSlidesGenerator, request);
3465
4111
  } catch (err) {
3466
4112
  if (shouldFallbackToMemoryArtifact(err)) {
3467
4113
  const envelope2 = toRoverErrorEnvelope(err);
@@ -3484,7 +4130,7 @@ async function executeSlidesGenerator({ userInput, toolArgs, tabOrder, plannerPr
3484
4130
  warnings: response.data?.warnings
3485
4131
  };
3486
4132
  }
3487
- async function executeWebpageGenerator({ userInput, toolArgs, tabOrder, plannerPrevSteps, files, trajectoryId: trajectoryId2, onStatusUpdate, agentLog, ctx }) {
4133
+ async function executeWebpageGenerator({ userInput, toolArgs, tabOrder, plannerPrevSteps, files, trajectoryId, onStatusUpdate, agentLog, ctx }) {
3488
4134
  onStatusUpdate?.("Generating webpage...", "Calling webpage_generator", "execute");
3489
4135
  const pageData = await ctx.getPageData(tabOrder[0]);
3490
4136
  const request = {
@@ -3502,12 +4148,12 @@ async function executeWebpageGenerator({ userInput, toolArgs, tabOrder, plannerP
3502
4148
  apiToolsConfig: ctx.apiToolsConfig,
3503
4149
  authToken: "",
3504
4150
  timestamp: ctx.userTimestamp,
3505
- trajectoryId: trajectoryId2,
4151
+ trajectoryId,
3506
4152
  userProfile: ctx.userProfile,
3507
4153
  files,
3508
4154
  outputDestination: toolArgs?.output_destination || toolArgs?.outputDestination
3509
4155
  };
3510
- const response = await ctx.callExtensionRouter(SUB_AGENTS.webpageGenerator, request);
4156
+ const response = await callExtensionRouterWithCancel(ctx, SUB_AGENTS.webpageGenerator, request);
3511
4157
  if (!response?.success)
3512
4158
  return { error: response?.error || "webpage_generator failed" };
3513
4159
  return {
@@ -3519,7 +4165,7 @@ async function executeWebpageGenerator({ userInput, toolArgs, tabOrder, plannerP
3519
4165
  warnings: response.data?.warnings
3520
4166
  };
3521
4167
  }
3522
- async function executePdfFiller({ userInput, toolArgs, tabOrder, plannerPrevSteps, files, trajectoryId: trajectoryId2, onStatusUpdate, agentLog, ctx, driveAuthToken }) {
4168
+ async function executePdfFiller({ userInput, toolArgs, tabOrder, plannerPrevSteps, files, trajectoryId, onStatusUpdate, agentLog, ctx, driveAuthToken }) {
3523
4169
  onStatusUpdate?.("Filling PDF...", "Calling pdf_filler", "execute");
3524
4170
  if (!driveAuthToken) {
3525
4171
  return buildMissingAuthToolResult("pdf_filler", ["authToken"]);
@@ -3540,12 +4186,12 @@ async function executePdfFiller({ userInput, toolArgs, tabOrder, plannerPrevStep
3540
4186
  apiToolsConfig: ctx.apiToolsConfig,
3541
4187
  authToken: driveAuthToken || "",
3542
4188
  timestamp: ctx.userTimestamp,
3543
- trajectoryId: trajectoryId2,
4189
+ trajectoryId,
3544
4190
  userProfile: ctx.userProfile,
3545
4191
  files,
3546
4192
  outputDestination: toolArgs?.output_destination || toolArgs?.outputDestination
3547
4193
  };
3548
- const response = await ctx.callExtensionRouter(SUB_AGENTS.pdfFiller, request);
4194
+ const response = await callExtensionRouterWithCancel(ctx, SUB_AGENTS.pdfFiller, request);
3549
4195
  if (!response?.success)
3550
4196
  return { error: response?.error || "pdf_filler failed" };
3551
4197
  return {
@@ -3557,7 +4203,7 @@ async function executePdfFiller({ userInput, toolArgs, tabOrder, plannerPrevStep
3557
4203
  warnings: response.data?.warnings
3558
4204
  };
3559
4205
  }
3560
- async function executeCustomToolGenerator({ userInput, plannerPrevSteps, files, trajectoryId: trajectoryId2, onStatusUpdate, agentLog, ctx }) {
4206
+ async function executeCustomToolGenerator({ userInput, plannerPrevSteps, files, trajectoryId, onStatusUpdate, agentLog, ctx }) {
3561
4207
  onStatusUpdate?.("Generating custom tool...", "Calling custom_tool_generator", "execute");
3562
4208
  const request = {
3563
4209
  siteId: ctx.siteId,
@@ -3572,11 +4218,11 @@ async function executeCustomToolGenerator({ userInput, plannerPrevSteps, files,
3572
4218
  apiToolsConfig: ctx.apiToolsConfig,
3573
4219
  authToken: "",
3574
4220
  timestamp: ctx.userTimestamp,
3575
- trajectoryId: trajectoryId2,
4221
+ trajectoryId,
3576
4222
  userProfile: ctx.userProfile,
3577
4223
  files
3578
4224
  };
3579
- const response = await ctx.callExtensionRouter(SUB_AGENTS.customToolGenerator, request);
4225
+ const response = await callExtensionRouterWithCancel(ctx, SUB_AGENTS.customToolGenerator, request);
3580
4226
  if (!response?.success)
3581
4227
  return { error: response?.error || "custom_tool_generator failed" };
3582
4228
  return {
@@ -3606,24 +4252,33 @@ function normalizeChatLog(entries) {
3606
4252
  continue;
3607
4253
  deduped.push(entry);
3608
4254
  }
3609
- const firstUser = deduped.find((entry) => entry.role === "user");
3610
- const tailBudget = firstUser ? Math.max(1, MAX_CHATLOG_ENTRIES - 1) : MAX_CHATLOG_ENTRIES;
3611
- let selected = deduped.slice(-tailBudget);
3612
- if (firstUser) {
3613
- const hasAnchor = selected.some((entry) => entry.role === "user" && entry.message === firstUser.message);
3614
- if (!hasAnchor) {
3615
- selected = [firstUser, ...selected];
3616
- }
4255
+ let selected = deduped.slice(-MAX_CHATLOG_ENTRIES);
4256
+ const seen = /* @__PURE__ */ new Set();
4257
+ const compactedReverse = [];
4258
+ for (let i = selected.length - 1; i >= 0; i -= 1) {
4259
+ const entry = selected[i];
4260
+ const key = `${entry.role}::${entry.message.toLowerCase()}`;
4261
+ if (seen.has(key))
4262
+ continue;
4263
+ seen.add(key);
4264
+ compactedReverse.push(entry);
3617
4265
  }
4266
+ selected = compactedReverse.reverse();
3618
4267
  if (selected.length > MAX_CHATLOG_ENTRIES) {
3619
4268
  selected = selected.slice(-MAX_CHATLOG_ENTRIES);
3620
4269
  }
3621
4270
  return selected;
3622
4271
  }
3623
4272
  async function executePlanner(options) {
3624
- const { userInput, tabs, previousMessages = [], trajectoryId: trajectoryId2, previousSteps = [], files, continuePlanning = false, recordingContext, driveAuthToken, agentLog, lastToolPreviousSteps, ctx, bridgeRpc: bridgeRpc2, functionDeclarations } = options;
4273
+ const { userInput, tabs, scopedTabIds: scopedTabIds2, seedTabId, getScopedTabRuntimeContext, previousMessages = [], trajectoryId, previousSteps = [], files, continuePlanning = false, recordingContext, driveAuthToken, agentLog, lastToolPreviousSteps, ctx, bridgeRpc: bridgeRpc2, functionDeclarations } = options;
4274
+ const runtimeScope = getScopedTabRuntimeContext?.() || {};
4275
+ const scopedTabIdsInput = runtimeScope.scopedTabIds ?? scopedTabIds2;
4276
+ const seedTabIdInput = runtimeScope.seedTabId ?? seedTabId;
3625
4277
  const fallbackTabs = Array.isArray(tabs) && tabs.length ? tabs : [{ id: 1 }];
3626
- const resolvedTabs = await resolveRuntimeTabs(bridgeRpc2, fallbackTabs);
4278
+ const resolvedTabs = await resolveRuntimeTabs(bridgeRpc2, fallbackTabs, {
4279
+ scopedTabIds: scopedTabIdsInput,
4280
+ seedTabId: seedTabIdInput
4281
+ });
3627
4282
  const tabOrder = resolvedTabs.tabOrder.length ? resolvedTabs.tabOrder : fallbackTabs.map((tab) => tab.id);
3628
4283
  const activeTabId = resolvedTabs.activeTabId;
3629
4284
  const tabMetaById = resolvedTabs.tabMetaById;
@@ -3632,7 +4287,9 @@ async function executePlanner(options) {
3632
4287
  try {
3633
4288
  return await ctx.getPageData(tabId, {
3634
4289
  onlyTextContent: false,
3635
- ...options2?.allowExternalFetch ? { __roverAllowExternalFetch: true } : {}
4290
+ ...options2?.allowExternalFetch ? { __roverAllowExternalFetch: true } : {},
4291
+ ...options2?.allowExternalFetch ? { __roverExternalIntent: "auto" } : {},
4292
+ ...options2?.allowExternalFetch ? { __roverExternalMessage: userInput } : {}
3636
4293
  });
3637
4294
  } catch {
3638
4295
  const tab = tabMetaById[tabId];
@@ -3668,7 +4325,7 @@ async function executePlanner(options) {
3668
4325
  authToken: driveAuthToken || "",
3669
4326
  timestamp: ctx.userTimestamp,
3670
4327
  continuePlanning,
3671
- trajectoryId: trajectoryId2,
4328
+ trajectoryId,
3672
4329
  userProfile: ctx.userProfile
3673
4330
  };
3674
4331
  const response = await ctx.callExtensionRouter(SUB_AGENTS.plan, request);
@@ -4087,55 +4744,130 @@ function convertParametersToTypes(parameters, functionDef) {
4087
4744
 
4088
4745
  // dist/agent/messageOrchestrator.js
4089
4746
  var COMPLEX_TASK_HINTS = [
4090
- "plan",
4091
- "research",
4092
- "compare",
4093
- "summarize",
4094
- "table",
4095
- "spreadsheet",
4096
- "sheet",
4097
4747
  "extract",
4098
- "crawl",
4099
- "workflow",
4748
+ "compare",
4749
+ "list",
4100
4750
  "across",
4101
- "multi",
4102
4751
  "multiple",
4103
- "then",
4104
- "after",
4105
- "finally",
4106
- "slides",
4752
+ "many",
4753
+ "report",
4107
4754
  "document",
4108
- "website",
4109
- "webpage",
4110
- "pdf"
4755
+ "spreadsheet",
4756
+ "sheet",
4757
+ "table",
4758
+ "csv",
4759
+ "json",
4760
+ "pdf",
4761
+ "slides"
4111
4762
  ];
4763
+ var MULTI_LABEL_TLDS = /* @__PURE__ */ new Set([
4764
+ "co.uk",
4765
+ "org.uk",
4766
+ "gov.uk",
4767
+ "ac.uk",
4768
+ "com.au",
4769
+ "net.au",
4770
+ "org.au",
4771
+ "co.jp",
4772
+ "com.br",
4773
+ "com.mx",
4774
+ "com.sg",
4775
+ "co.in"
4776
+ ]);
4777
+ function extractHostFromUrl(input) {
4778
+ const raw = String(input || "").trim();
4779
+ if (!raw)
4780
+ return "";
4781
+ try {
4782
+ return new URL(raw).hostname.toLowerCase();
4783
+ } catch {
4784
+ return "";
4785
+ }
4786
+ }
4787
+ function registrableDomain(host) {
4788
+ const clean = String(host || "").trim().toLowerCase().replace(/^\.+/, "");
4789
+ if (!clean)
4790
+ return "";
4791
+ if (clean === "localhost" || /^\d+\.\d+\.\d+\.\d+$/.test(clean))
4792
+ return clean;
4793
+ const parts = clean.split(".").filter(Boolean);
4794
+ if (parts.length < 2)
4795
+ return clean;
4796
+ const tail2 = `${parts[parts.length - 2]}.${parts[parts.length - 1]}`;
4797
+ if (parts.length >= 3 && MULTI_LABEL_TLDS.has(tail2)) {
4798
+ return `${parts[parts.length - 3]}.${tail2}`;
4799
+ }
4800
+ return tail2;
4801
+ }
4802
+ function hasCrossDomainPlanDependency(userInput, tabs) {
4803
+ const currentUrl = String(tabs?.find((tab) => typeof tab?.url === "string" && String(tab.url || "").trim())?.url || "");
4804
+ const currentDomain = registrableDomain(extractHostFromUrl(currentUrl));
4805
+ if (!currentDomain)
4806
+ return false;
4807
+ const urls = String(userInput || "").match(/https?:\/\/[^\s)]+/g) || [];
4808
+ if (!urls.length)
4809
+ return false;
4810
+ return urls.some((url) => {
4811
+ const targetDomain = registrableDomain(extractHostFromUrl(url));
4812
+ return !!targetDomain && targetDomain !== currentDomain;
4813
+ });
4814
+ }
4815
+ function countCrossDomainNavigationDependencies(userInput, tabs) {
4816
+ const currentUrl = String(tabs?.find((tab) => typeof tab?.url === "string" && String(tab.url || "").trim())?.url || "");
4817
+ const currentDomain = registrableDomain(extractHostFromUrl(currentUrl));
4818
+ const urls = String(userInput || "").match(/https?:\/\/[^\s)]+/g) || [];
4819
+ if (!urls.length || !currentDomain)
4820
+ return 0;
4821
+ const distinct = /* @__PURE__ */ new Set();
4822
+ for (const url of urls) {
4823
+ const targetDomain = registrableDomain(extractHostFromUrl(url));
4824
+ if (!targetDomain || targetDomain === currentDomain)
4825
+ continue;
4826
+ distinct.add(targetDomain);
4827
+ }
4828
+ return distinct.size;
4829
+ }
4830
+ function hasMultiStepDependencyMarkers(text) {
4831
+ const input = String(text || "").toLowerCase();
4832
+ if (!input)
4833
+ return false;
4834
+ const sequenceMarkers = (input.match(/\b(first|second|third|then|after that|next|finally)\b/g) || []).length;
4835
+ if (sequenceMarkers >= 2)
4836
+ return true;
4837
+ if ((input.match(/\bstep\s*[1-9]\b/g) || []).length >= 2)
4838
+ return true;
4839
+ if ((input.match(/\b\d+\.\s+/g) || []).length >= 2)
4840
+ return true;
4841
+ return false;
4842
+ }
4112
4843
  function computeComplexityScore(text) {
4113
4844
  const input = String(text || "").toLowerCase().trim();
4114
4845
  if (!input)
4115
4846
  return 0;
4116
4847
  let score = 0;
4117
- const words = input.split(/\s+/).filter(Boolean);
4118
- if (words.length >= 14)
4119
- score += 1;
4120
- if (words.length >= 26)
4848
+ const actionVerbs = new Set((input.match(/\b(click|open|go|navigate|fill|type|submit|extract|find|search|compare|summarize|collect|download|upload|create|generate|report)\b/g) || []).map((v) => v.toLowerCase()));
4849
+ if (input.length > 120)
4121
4850
  score += 1;
4122
- if ((input.match(/\b(and then|then|after that|next|finally)\b/g) || []).length >= 1)
4123
- score += 1;
4124
- if ((input.match(/[;,]/g) || []).length >= 2)
4851
+ if (actionVerbs.size >= 2)
4125
4852
  score += 1;
4853
+ if ((input.match(/\b(and then|then|after|next|finally|first|second|third)\b/g) || []).length >= 1)
4854
+ score += 2;
4855
+ if ((input.match(/\b(extract|compare|list|all|each|every|across|multiple|many)\b/g) || []).length >= 2)
4856
+ score += 2;
4126
4857
  if (/https?:\/\/|www\./.test(input))
4127
- score += 1;
4128
- if (/\b(json|csv|table|schema|columns?)\b/.test(input))
4129
- score += 1;
4858
+ score += 2;
4859
+ if ((input.match(/\b(report|document|doc|spreadsheet|sheet|table|csv|json|pdf|slides|summary)\b/g) || []).length >= 1)
4860
+ score += 2;
4130
4861
  for (const hint of COMPLEX_TASK_HINTS) {
4131
4862
  if (input.includes(hint)) {
4132
- score += 1;
4863
+ score += 0.25;
4133
4864
  }
4134
4865
  }
4866
+ const words = input.split(/\s+/).filter(Boolean);
4135
4867
  if (/^(click|type|fill|open|go to|scroll|press|select)\b/.test(input) && words.length <= 10) {
4136
4868
  score = Math.max(0, score - 2);
4137
4869
  }
4138
- return score;
4870
+ return Math.max(0, Math.min(10, Math.round(score)));
4139
4871
  }
4140
4872
  function decideRouting(message, options) {
4141
4873
  const mode = options.taskRouting?.mode || "act";
@@ -4145,21 +4877,97 @@ function decideRouting(message, options) {
4145
4877
  if (mode === "act") {
4146
4878
  return { mode: "act", reason: "Configured act mode" };
4147
4879
  }
4148
- const threshold = Math.max(1, Number(options.taskRouting?.actHeuristicThreshold) || 5);
4880
+ const hasAwaitingUserChain = Array.isArray(options.previousSteps) && options.previousSteps.some((step) => Array.isArray(step?.questionsAsked) && step.questionsAsked.length > 0 && !(step?.userAnswers && Object.keys(step.userAnswers).length > 0));
4881
+ if (hasAwaitingUserChain) {
4882
+ const score2 = computeComplexityScore(message);
4883
+ return {
4884
+ mode: "planner",
4885
+ score: score2,
4886
+ reason: "Forced planner due to awaiting_user continuation chain."
4887
+ };
4888
+ }
4889
+ if (hasMultiStepDependencyMarkers(message)) {
4890
+ const score2 = computeComplexityScore(message);
4891
+ return {
4892
+ mode: "planner",
4893
+ score: score2,
4894
+ reason: "Forced planner due to multi-step dependency markers in prompt."
4895
+ };
4896
+ }
4897
+ const crossDomainDependencies = countCrossDomainNavigationDependencies(message, options.tabs || []);
4898
+ if (crossDomainDependencies > 1) {
4899
+ const score2 = computeComplexityScore(message);
4900
+ return {
4901
+ mode: "planner",
4902
+ score: score2,
4903
+ reason: `Forced planner due to ${crossDomainDependencies} cross-domain dependencies.`
4904
+ };
4905
+ }
4149
4906
  const score = computeComplexityScore(message);
4150
- if (score >= threshold) {
4907
+ const plannerThreshold = Math.max(3, Math.min(10, Number(options.taskRouting?.actHeuristicThreshold) || 7));
4908
+ if (score >= plannerThreshold) {
4151
4909
  return {
4152
4910
  mode: "planner",
4153
4911
  score,
4154
- reason: `Complexity score ${score} >= threshold ${threshold}`
4912
+ reason: `Complexity score ${score} >= ${plannerThreshold}`
4155
4913
  };
4156
4914
  }
4157
4915
  return {
4158
4916
  mode: "act",
4159
4917
  score,
4160
- reason: `Complexity score ${score} < threshold ${threshold}`
4918
+ reason: `Complexity score ${score} < ${plannerThreshold}`
4161
4919
  };
4162
4920
  }
4921
+ function hasUsableActOutcome(actResult) {
4922
+ if (!actResult || typeof actResult !== "object")
4923
+ return false;
4924
+ if (actResult.error)
4925
+ return false;
4926
+ const hasNonEmptyValue = (value) => {
4927
+ if (Array.isArray(value))
4928
+ return value.length > 0;
4929
+ if (value && typeof value === "object")
4930
+ return Object.keys(value).length > 0;
4931
+ if (typeof value === "string")
4932
+ return value.trim().length > 0;
4933
+ return value !== void 0 && value !== null;
4934
+ };
4935
+ if (hasNonEmptyValue(actResult.data))
4936
+ return true;
4937
+ if (actResult.navigationPending === true)
4938
+ return true;
4939
+ if (actResult.needsUserInput === true)
4940
+ return true;
4941
+ const output = actResult.output;
4942
+ if (!output)
4943
+ return false;
4944
+ if (typeof output === "string")
4945
+ return output.trim().length > 0;
4946
+ if (typeof output !== "object")
4947
+ return hasNonEmptyValue(output);
4948
+ if (output.success === false || output.error)
4949
+ return false;
4950
+ const navigationOutcome = String(output.navigationOutcome || "").trim().toLowerCase();
4951
+ if (navigationOutcome === "same_tab_scheduled" || navigationOutcome === "new_tab_opened")
4952
+ return true;
4953
+ if (output.navigationPending === true)
4954
+ return true;
4955
+ if (output.needsUserInput === true || output.waitingForUserInput === true)
4956
+ return true;
4957
+ if (Array.isArray(output.questions) && output.questions.length > 0)
4958
+ return true;
4959
+ if (String(output.taskStatus || "").trim().toLowerCase() === "in_progress")
4960
+ return true;
4961
+ if (output.taskComplete === true)
4962
+ return true;
4963
+ if (hasNonEmptyValue(output.data))
4964
+ return true;
4965
+ if (typeof output.response === "string" && String(output.response || "").trim())
4966
+ return true;
4967
+ if (hasNonEmptyValue(output))
4968
+ return true;
4969
+ return false;
4970
+ }
4163
4971
  async function processMessageWithFunctions(options) {
4164
4972
  const { message, toolFunctions } = options;
4165
4973
  const parsed = parseMessage(message);
@@ -4255,6 +5063,7 @@ async function handleSendMessageWithFunctions(userInput, context) {
4255
5063
  let routedAgentPrevSteps = context.agentLog?.prevSteps;
4256
5064
  if (routing.mode === "act") {
4257
5065
  context.onStatusUpdate?.("Executing action plan", "Using ACT tool loop", "execute");
5066
+ const actStartedAt = Date.now();
4258
5067
  const actResult = await executeToolFromPlan({
4259
5068
  ...context,
4260
5069
  toolName: PLANNER_FUNCTION_CALLS.ACT,
@@ -4273,7 +5082,17 @@ async function handleSendMessageWithFunctions(userInput, context) {
4273
5082
  routedAgentPrevSteps = actResult.prevSteps;
4274
5083
  context.onPrevStepsUpdate?.(routedAgentPrevSteps);
4275
5084
  }
4276
- const shouldEscalateToPlanner = !!actResult.error && (context.taskRouting?.plannerOnActError ?? true) && context.taskRouting?.mode !== "act";
5085
+ const actElapsedMs = Date.now() - actStartedAt;
5086
+ const actFunctionCount = Array.isArray(actResult.prevSteps?.[actResult.prevSteps.length - 1]?.functions) ? actResult.prevSteps[actResult.prevSteps.length - 1].functions.length : 0;
5087
+ const recoverableFailures = Array.isArray(actResult.prevSteps) ? actResult.prevSteps.reduce((count, step) => {
5088
+ const failures = Array.isArray(step.functions) ? step.functions.filter((fn) => fn?.response?.status === "Failure").length : 0;
5089
+ return count + failures;
5090
+ }, 0) : 0;
5091
+ const crossDomainPlanDependency = hasCrossDomainPlanDependency(userInput, context.tabs || []);
5092
+ const crossDomainDependencyCount = countCrossDomainNavigationDependencies(userInput, context.tabs || []);
5093
+ const multiStepDependency = hasMultiStepDependencyMarkers(userInput);
5094
+ const actProducedUsableOutcome = hasUsableActOutcome(actResult);
5095
+ const shouldEscalateToPlanner = !actProducedUsableOutcome && (!!actResult.error && (context.taskRouting?.plannerOnActError ?? true) && context.taskRouting?.mode !== "act" || actElapsedMs > 8e3 || actFunctionCount > 3 || recoverableFailures >= 2 || crossDomainDependencyCount > 1 || crossDomainPlanDependency || multiStepDependency);
4277
5096
  if (!shouldEscalateToPlanner) {
4278
5097
  return {
4279
5098
  success: !actResult.error,
@@ -4311,6 +5130,19 @@ async function handleSendMessageWithFunctions(userInput, context) {
4311
5130
  return { success: true, processedMessage: result.processedMessage };
4312
5131
  }
4313
5132
 
5133
+ // dist/runHistoryGuards.js
5134
+ function shouldClearHistoryForRun(params) {
5135
+ return !params.resume && !params.preserveHistory;
5136
+ }
5137
+ function shouldBuildResumeCueChatLog(params) {
5138
+ return !!(params.resume || params.preserveHistory) && params.resumeFollowupMode === "deterministic_cues";
5139
+ }
5140
+ function shouldUseFollowupChatLog(params) {
5141
+ if (params.resume)
5142
+ return false;
5143
+ return Number(params.followupChatLogLength) > 0;
5144
+ }
5145
+
4314
5146
  // dist/worker.js
4315
5147
  var history = [];
4316
5148
  var config = null;
@@ -4319,7 +5151,12 @@ var toolRegistry = new ToolRegistry();
4319
5151
  var plannerHistory = [];
4320
5152
  var agentPrevSteps = [];
4321
5153
  var pendingAskUser;
4322
- var trajectoryId = crypto.randomUUID();
5154
+ var rootUserInput = "";
5155
+ var workerSessionId = "";
5156
+ var taskTrajectoryId = crypto.randomUUID();
5157
+ var taskBoundaryId = crypto.randomUUID();
5158
+ var scopedTabIds = [];
5159
+ var scopedSeedTabId;
4323
5160
  var tabularStore = null;
4324
5161
  var PLANNER_TOOL_NAME_SET = new Set(Object.values(PLANNER_FUNCTION_CALLS));
4325
5162
  var activeRun = null;
@@ -4327,10 +5164,12 @@ var cancelledRunIds = /* @__PURE__ */ new Set();
4327
5164
  var activeAbortController = null;
4328
5165
  var lastStatusKey = "";
4329
5166
  var seenStatusKeys = /* @__PURE__ */ new Set();
5167
+ var lastRuntimeTabsDiagnosticsKey = "";
4330
5168
  var terminalRuns = /* @__PURE__ */ new Map();
4331
5169
  var RPC_TIMEOUT_MS = 3e4;
4332
5170
  var DETACHED_EXTERNAL_TAB_MAX_AGE_MS = 9e4;
4333
- var MAX_CHATLOG_ENTRIES2 = 12;
5171
+ var PENDING_ATTACH_TAB_MAX_AGE_MS = 2e4;
5172
+ var MAX_RESUME_CUE_TURNS = 2;
4334
5173
  function resolveAgentName(config2) {
4335
5174
  const raw = String(config2?.ui?.agent?.name || "").trim();
4336
5175
  if (!raw)
@@ -4379,6 +5218,7 @@ function buildRoverRuntimeContext2(params) {
4379
5218
  agentName: params.agentName,
4380
5219
  externalNavigationPolicy: params.externalNavigationPolicy,
4381
5220
  tabIdContract: "tree_index_mapped_by_tab_order",
5221
+ taskBoundaryId: params.taskBoundaryId,
4382
5222
  ...externalTabs.length ? { externalTabs } : {}
4383
5223
  };
4384
5224
  }
@@ -4483,6 +5323,8 @@ async function getKnownTabs() {
4483
5323
  id,
4484
5324
  runtimeId: typeof tab?.runtimeId === "string" ? tab.runtimeId : void 0,
4485
5325
  updatedAt: Number(tab?.updatedAt) || 0,
5326
+ detachedAt: Number(tab?.detachedAt) || 0,
5327
+ detachedReason: typeof tab?.detachedReason === "string" ? tab.detachedReason : void 0,
4486
5328
  url: typeof tab?.url === "string" ? tab.url : void 0,
4487
5329
  title: typeof tab?.title === "string" ? tab.title : void 0,
4488
5330
  external,
@@ -4490,9 +5332,15 @@ async function getKnownTabs() {
4490
5332
  inaccessibleReason: typeof tab?.inaccessibleReason === "string" ? tab.inaccessibleReason : void 0
4491
5333
  };
4492
5334
  }).filter((tab) => Number.isFinite(tab.id) && tab.id > 0).filter((tab) => {
4493
- if (!tab.external || tab.runtimeId)
5335
+ const freshness = Math.max(Number(tab.updatedAt) || 0, Number(tab.detachedAt) || 0);
5336
+ if (tab.runtimeId)
4494
5337
  return true;
4495
- return nowMs - (tab.updatedAt || 0) <= DETACHED_EXTERNAL_TAB_MAX_AGE_MS;
5338
+ if (tab.external)
5339
+ return nowMs - freshness <= DETACHED_EXTERNAL_TAB_MAX_AGE_MS;
5340
+ if (tab.detachedReason === "opened_pending_attach") {
5341
+ return nowMs - freshness <= PENDING_ATTACH_TAB_MAX_AGE_MS;
5342
+ }
5343
+ return false;
4496
5344
  }).map((tab) => ({
4497
5345
  id: tab.id,
4498
5346
  url: tab.url,
@@ -4534,6 +5382,8 @@ function postStatus(message, thought, stage) {
4534
5382
  const resolvedStage = inferStatusStage(message, thought, stage);
4535
5383
  const compact = compactThought(message, thought);
4536
5384
  const runId = activeRun?.runId || "no-run";
5385
+ if (runId !== "no-run" && cancelledRunIds.has(runId))
5386
+ return;
4537
5387
  const key = `${runId}|${resolvedStage}|${String(message || "").trim().toLowerCase()}|${compact.toLowerCase()}`;
4538
5388
  if (key === lastStatusKey || seenStatusKeys.has(key))
4539
5389
  return;
@@ -4546,6 +5396,27 @@ function postStatus(message, thought, stage) {
4546
5396
  self.postMessage({ type: "status", message, thought, stage: resolvedStage, compactThought: compact, runId: activeRun?.runId });
4547
5397
  postStateSnapshot();
4548
5398
  }
5399
+ function postRuntimeTabsDiagnostics(payload) {
5400
+ const runId = activeRun?.runId || "no-run";
5401
+ if (runId !== "no-run" && cancelledRunIds.has(runId))
5402
+ return;
5403
+ const signature = [
5404
+ runId,
5405
+ payload.hasExplicitScope ? "1" : "0",
5406
+ payload.scopedTabIdsInput.join(","),
5407
+ payload.listedTabIds.join(","),
5408
+ payload.keptScopedTabIds.join(","),
5409
+ payload.resolvedTabOrder.join(",")
5410
+ ].join("|");
5411
+ if (signature === lastRuntimeTabsDiagnosticsKey)
5412
+ return;
5413
+ lastRuntimeTabsDiagnosticsKey = signature;
5414
+ self.postMessage({
5415
+ type: "runtime_tabs_diagnostics",
5416
+ runId: activeRun?.runId,
5417
+ diagnostics: payload
5418
+ });
5419
+ }
4549
5420
  function cloneUnknown(value) {
4550
5421
  if (value == null)
4551
5422
  return value;
@@ -4562,8 +5433,8 @@ function cloneUnknown(value) {
4562
5433
  }
4563
5434
  }
4564
5435
  function sanitizeHistoryForPersist(input) {
4565
- return input.slice(-80).map((message) => ({
4566
- ...message,
5436
+ return input.filter((message) => message?.role === "user").slice(-20).map((message) => ({
5437
+ role: "user",
4567
5438
  content: String(message?.content ?? "")
4568
5439
  }));
4569
5440
  }
@@ -4631,6 +5502,7 @@ function questionToDisplayText(question) {
4631
5502
  function normalizeAskUserAnswerMeta(raw, questions, fallbackText) {
4632
5503
  const validKeys = new Set(questions.map((question) => question.key));
4633
5504
  const answersByKey = {};
5505
+ const submittedKeys = Array.isArray(raw?.keys) ? raw.keys.map((key) => String(key || "").trim()).filter((key) => !!key && validKeys.has(key)) : [];
4634
5506
  if (raw?.answersByKey && typeof raw.answersByKey === "object") {
4635
5507
  for (const [key, value] of Object.entries(raw.answersByKey)) {
4636
5508
  if (!validKeys.has(key))
@@ -4660,8 +5532,14 @@ function normalizeAskUserAnswerMeta(raw, questions, fallbackText) {
4660
5532
  }
4661
5533
  }
4662
5534
  const rawText = String(raw?.rawText || fallback || "").trim();
4663
- if (!rawText && Object.keys(answersByKey).length === 0)
4664
- return void 0;
5535
+ if (!rawText && Object.keys(answersByKey).length === 0) {
5536
+ if (!submittedKeys.length)
5537
+ return void 0;
5538
+ return {
5539
+ answersByKey,
5540
+ rawText: submittedKeys.map((key) => `${key}: (no answer provided)`).join("\n")
5541
+ };
5542
+ }
4665
5543
  return { answersByKey, rawText };
4666
5544
  }
4667
5545
  function buildAskUserAnswerContext(questions, answers) {
@@ -4676,10 +5554,256 @@ function buildAskUserAnswerContext(questions, answers) {
4676
5554
  }
4677
5555
  return lines.join("\n");
4678
5556
  }
5557
+ function normalizeRootUserInput(input) {
5558
+ const normalized = String(input || "").trim();
5559
+ return normalized || void 0;
5560
+ }
5561
+ function normalizeFollowupChatLog(input) {
5562
+ if (!Array.isArray(input))
5563
+ return [];
5564
+ const normalized = input.map((entry) => ({
5565
+ role: entry?.role === "user" ? "user" : "model",
5566
+ message: String(entry?.message || "").replace(/\s+/g, " ").trim()
5567
+ })).filter((entry) => !!entry.message);
5568
+ if (!normalized.length)
5569
+ return [];
5570
+ const deduped = [];
5571
+ for (const entry of normalized) {
5572
+ const previous = deduped[deduped.length - 1];
5573
+ if (previous && previous.role === entry.role && previous.message === entry.message)
5574
+ continue;
5575
+ deduped.push(entry);
5576
+ }
5577
+ return deduped.slice(-12);
5578
+ }
5579
+ function dedupePositiveTabIds4(input) {
5580
+ if (!Array.isArray(input))
5581
+ return [];
5582
+ const seen = /* @__PURE__ */ new Set();
5583
+ const out = [];
5584
+ for (const value of input) {
5585
+ const tabId = Number(value);
5586
+ if (!Number.isFinite(tabId) || tabId <= 0 || seen.has(tabId))
5587
+ continue;
5588
+ seen.add(tabId);
5589
+ out.push(tabId);
5590
+ }
5591
+ return out;
5592
+ }
5593
+ function resolveScopedTabConfig(partial) {
5594
+ const directScoped = dedupePositiveTabIds4(partial?.scopedTabIds);
5595
+ const scope = partial?.taskTabScope;
5596
+ const scopedFromScope = dedupePositiveTabIds4(scope?.touchedTabIds);
5597
+ const seedCandidate = Number(scope?.seedTabId);
5598
+ const seedTabId = Number.isFinite(seedCandidate) && seedCandidate > 0 ? seedCandidate : void 0;
5599
+ const scoped = dedupePositiveTabIds4(directScoped.length > 0 ? directScoped : scopedFromScope.length > 0 ? scopedFromScope : seedTabId ? [seedTabId] : []);
5600
+ if (seedTabId && !scoped.includes(seedTabId)) {
5601
+ scoped.unshift(seedTabId);
5602
+ }
5603
+ return { scoped, seedTabId };
5604
+ }
5605
+ function applyScopedTabConfig(partial) {
5606
+ if (!partial || typeof partial !== "object")
5607
+ return;
5608
+ const hasScopedTabIds = Object.prototype.hasOwnProperty.call(partial, "scopedTabIds");
5609
+ const hasTaskTabScope = Object.prototype.hasOwnProperty.call(partial, "taskTabScope");
5610
+ if (!hasScopedTabIds && !hasTaskTabScope)
5611
+ return;
5612
+ const next = resolveScopedTabConfig(partial);
5613
+ scopedTabIds = next.scoped;
5614
+ scopedSeedTabId = next.seedTabId;
5615
+ }
5616
+ function touchRunScopedTabIds(tabIds) {
5617
+ const touched = dedupePositiveTabIds4(Array.isArray(tabIds) ? tabIds : [tabIds]);
5618
+ if (!touched.length)
5619
+ return scopedTabIds;
5620
+ const next = dedupePositiveTabIds4([
5621
+ ...Number(scopedSeedTabId) > 0 ? [Number(scopedSeedTabId)] : [],
5622
+ ...scopedTabIds,
5623
+ ...touched
5624
+ ]);
5625
+ if (next.length === scopedTabIds.length && next.every((tabId, index) => tabId === scopedTabIds[index])) {
5626
+ return scopedTabIds;
5627
+ }
5628
+ scopedTabIds = next;
5629
+ return scopedTabIds;
5630
+ }
5631
+ function getRootUserInputFallbackFromHistory() {
5632
+ for (const message of history) {
5633
+ if (message.role !== "user")
5634
+ continue;
5635
+ const content = normalizeRootUserInput(String(message.content || ""));
5636
+ if (content)
5637
+ return content;
5638
+ }
5639
+ return void 0;
5640
+ }
5641
+ function buildContinuePlanningInput(focus, lastToolName, lastToolOutput) {
5642
+ const safeToolName = String(lastToolName || "").trim();
5643
+ const safeFocus = String(focus || "").trim();
5644
+ const lines = [
5645
+ safeToolName ? `The tool \`${safeToolName}\` just finished executing.` : "No tool was issued previously.",
5646
+ `Please analyze the progress based on the complete history and determine the single next best step required to fulfill the overall request: ${safeFocus || "(none)"}.`,
5647
+ `If the request is complete, invoke tool \`${PLANNER_FUNCTION_CALLS.TASK_COMPLETE}\` via \`tool_code\`. If not, invoke the appropriate tool.`
5648
+ ];
5649
+ const output = String(lastToolOutput || "").trim();
5650
+ if (output) {
5651
+ lines.push("[LAST_TOOL_OUTPUT]");
5652
+ lines.push(output);
5653
+ }
5654
+ return lines.join("\n");
5655
+ }
5656
+ function findLatestAskUserFunctionStep(steps) {
5657
+ if (!Array.isArray(steps) || steps.length === 0)
5658
+ return void 0;
5659
+ for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) {
5660
+ const step = steps[stepIndex];
5661
+ const functions = Array.isArray(step?.functions) ? step.functions : [];
5662
+ for (let functionIndex = functions.length - 1; functionIndex >= 0; functionIndex -= 1) {
5663
+ const fn = functions[functionIndex];
5664
+ if (String(fn?.name || "").trim().toLowerCase() === "ask_user") {
5665
+ return { stepIndex, functionIndex };
5666
+ }
5667
+ }
5668
+ }
5669
+ return void 0;
5670
+ }
5671
+ function resolveAskUserStepRef(steps, prompt) {
5672
+ const explicit = prompt?.stepRef;
5673
+ if (explicit && prompt?.boundaryId && prompt.boundaryId === taskBoundaryId && Number.isFinite(explicit.stepIndex) && explicit.stepIndex >= 0 && Number.isFinite(explicit.functionIndex) && explicit.functionIndex >= 0 && explicit.stepIndex < steps.length) {
5674
+ const step = steps[explicit.stepIndex];
5675
+ const functions = Array.isArray(step?.functions) ? step.functions : [];
5676
+ const fn = functions[explicit.functionIndex];
5677
+ if (String(fn?.name || "").trim().toLowerCase() === "ask_user") {
5678
+ return {
5679
+ stepIndex: explicit.stepIndex,
5680
+ functionIndex: explicit.functionIndex
5681
+ };
5682
+ }
5683
+ }
5684
+ const refs = [];
5685
+ for (let stepIndex = 0; stepIndex < steps.length; stepIndex += 1) {
5686
+ const step = steps[stepIndex];
5687
+ const functions = Array.isArray(step?.functions) ? step.functions : [];
5688
+ for (let functionIndex = 0; functionIndex < functions.length; functionIndex += 1) {
5689
+ const fn = functions[functionIndex];
5690
+ if (String(fn?.name || "").trim().toLowerCase() === "ask_user") {
5691
+ refs.push({ stepIndex, functionIndex });
5692
+ }
5693
+ }
5694
+ }
5695
+ if (refs.length === 1)
5696
+ return refs[0];
5697
+ if (refs.length > 1)
5698
+ return refs[refs.length - 1];
5699
+ return void 0;
5700
+ }
5701
+ function captureLatestAskUserStepRef(steps) {
5702
+ const ref = findLatestAskUserFunctionStep(steps);
5703
+ if (!ref)
5704
+ return void 0;
5705
+ const step = steps[ref.stepIndex];
5706
+ return {
5707
+ stepIndex: ref.stepIndex,
5708
+ functionIndex: ref.functionIndex,
5709
+ ...typeof step?.accTreeId === "string" && String(step.accTreeId).trim() ? { accTreeId: String(step.accTreeId).trim() } : {}
5710
+ };
5711
+ }
5712
+ function buildPendingAskUserPrompt(source, questions) {
5713
+ const stepRef = captureLatestAskUserStepRef(agentPrevSteps);
5714
+ return {
5715
+ questions,
5716
+ source,
5717
+ askedAt: Date.now(),
5718
+ boundaryId: taskBoundaryId,
5719
+ ...stepRef ? { stepRef } : {}
5720
+ };
5721
+ }
5722
+ function mergeAskUserAnswerIntoPrevSteps(inputSteps, prompt, answers) {
5723
+ const next = sanitizeAgentPrevStepsForPersist(Array.isArray(inputSteps) ? inputSteps : []);
5724
+ const questions = normalizePlannerQuestions(prompt?.questions);
5725
+ if (!questions.length)
5726
+ return next;
5727
+ const askUserRef = resolveAskUserStepRef(next, prompt);
5728
+ const questionPayload = normalizePlannerQuestions(questions).map((question) => ({
5729
+ key: question.key,
5730
+ query: question.query,
5731
+ ...question.required === false ? { required: false } : {}
5732
+ }));
5733
+ const buildOutputPayload = (previousOutput2) => {
5734
+ const prev = previousOutput2 && typeof previousOutput2 === "object" && !Array.isArray(previousOutput2) ? previousOutput2 : {};
5735
+ const prevQuestions = normalizePlannerQuestions(prev.questions);
5736
+ const resolvedQuestions = prevQuestions.length ? prevQuestions : normalizePlannerQuestions(questions);
5737
+ return {
5738
+ ...prev,
5739
+ status: "answered",
5740
+ needsUserInput: false,
5741
+ ...resolvedQuestions.length ? { questions: resolvedQuestions } : {},
5742
+ ask_user_answers: { ...answers.answersByKey },
5743
+ ...answers.rawText ? { raw_user_reply: answers.rawText } : {},
5744
+ answeredAt: Date.now()
5745
+ };
5746
+ };
5747
+ if (!askUserRef) {
5748
+ return next;
5749
+ }
5750
+ const step = next[askUserRef.stepIndex];
5751
+ if (!Array.isArray(step.functions))
5752
+ step.functions = [];
5753
+ const existingFn = step.functions[askUserRef.functionIndex];
5754
+ const previousOutput = existingFn?.response?.output;
5755
+ step.functions[askUserRef.functionIndex] = {
5756
+ name: "ask_user",
5757
+ args: {
5758
+ questions_to_ask: questionPayload
5759
+ },
5760
+ response: {
5761
+ status: "Success",
5762
+ output: buildOutputPayload(previousOutput)
5763
+ }
5764
+ };
5765
+ return next;
5766
+ }
5767
+ function mergeAskUserAnswersIntoPlannerHistory(input, questions, answersByKey) {
5768
+ const next = sanitizePlannerHistoryForPersist(Array.isArray(input) ? input : []);
5769
+ for (let i = next.length - 1; i >= 0; i -= 1) {
5770
+ const step = next[i];
5771
+ if (!step || typeof step !== "object")
5772
+ continue;
5773
+ const asked = normalizePlannerQuestions(step.questionsAsked);
5774
+ if (!asked.length)
5775
+ continue;
5776
+ const askedKeys = new Set(asked.map((question) => question.key.toLowerCase()));
5777
+ const overlaps = questions.some((question) => askedKeys.has(String(question.key || "").toLowerCase()));
5778
+ if (!overlaps)
5779
+ continue;
5780
+ const existingAnswers = step.userAnswers && typeof step.userAnswers === "object" && !Array.isArray(step.userAnswers) ? step.userAnswers : {};
5781
+ step.userAnswers = {
5782
+ ...existingAnswers,
5783
+ ...answersByKey
5784
+ };
5785
+ return sanitizePlannerHistoryForPersist(next);
5786
+ }
5787
+ next.push({
5788
+ thought: "User provided clarification answers.",
5789
+ questionsAsked: questions,
5790
+ userAnswers: answersByKey
5791
+ });
5792
+ return sanitizePlannerHistoryForPersist(next);
5793
+ }
4679
5794
  function buildPersistedState() {
4680
5795
  const safePrevSteps = sanitizeAgentPrevStepsForPersist(Array.isArray(agentPrevSteps) ? agentPrevSteps : []);
5796
+ const normalizedRootUserInput = normalizeRootUserInput(rootUserInput);
5797
+ const pendingStepRef = pendingAskUser?.stepRef;
5798
+ const safePendingStepRef = pendingStepRef && Number.isFinite(pendingStepRef.stepIndex) && pendingStepRef.stepIndex >= 0 && Number.isFinite(pendingStepRef.functionIndex) && pendingStepRef.functionIndex >= 0 ? {
5799
+ stepIndex: pendingStepRef.stepIndex,
5800
+ functionIndex: pendingStepRef.functionIndex,
5801
+ ...typeof pendingStepRef.accTreeId === "string" && pendingStepRef.accTreeId.trim() ? { accTreeId: pendingStepRef.accTreeId.trim() } : {}
5802
+ } : void 0;
4681
5803
  return {
4682
- trajectoryId,
5804
+ trajectoryId: taskTrajectoryId,
5805
+ taskBoundaryId,
5806
+ ...normalizedRootUserInput ? { rootUserInput: normalizedRootUserInput } : {},
4683
5807
  history: sanitizeHistoryForPersist(history),
4684
5808
  plannerHistory: sanitizePlannerHistoryForPersist(Array.isArray(plannerHistory) ? plannerHistory : []),
4685
5809
  agentPrevSteps: safePrevSteps,
@@ -4687,7 +5811,9 @@ function buildPersistedState() {
4687
5811
  pendingAskUser: pendingAskUser ? {
4688
5812
  questions: normalizePlannerQuestions(pendingAskUser.questions),
4689
5813
  source: pendingAskUser.source,
4690
- askedAt: Number(pendingAskUser.askedAt) || Date.now()
5814
+ askedAt: Number(pendingAskUser.askedAt) || Date.now(),
5815
+ ...typeof pendingAskUser.boundaryId === "string" && pendingAskUser.boundaryId.trim() ? { boundaryId: pendingAskUser.boundaryId.trim() } : {},
5816
+ ...safePendingStepRef ? { stepRef: safePendingStepRef } : {}
4691
5817
  } : void 0
4692
5818
  };
4693
5819
  }
@@ -4722,20 +5848,33 @@ function hydrateState(raw) {
4722
5848
  agentPrevSteps = sanitizeAgentPrevStepsForPersist(snapshot.lastToolPreviousSteps);
4723
5849
  }
4724
5850
  const hydratedQuestions = normalizePlannerQuestions(snapshot.pendingAskUser?.questions);
5851
+ const hydratedStepRefRaw = snapshot.pendingAskUser?.stepRef;
5852
+ const hydratedStepRef = hydratedStepRefRaw && Number.isFinite(Number(hydratedStepRefRaw.stepIndex)) && Number(hydratedStepRefRaw.stepIndex) >= 0 && Number.isFinite(Number(hydratedStepRefRaw.functionIndex)) && Number(hydratedStepRefRaw.functionIndex) >= 0 ? {
5853
+ stepIndex: Number(hydratedStepRefRaw.stepIndex),
5854
+ functionIndex: Number(hydratedStepRefRaw.functionIndex),
5855
+ ...typeof hydratedStepRefRaw.accTreeId === "string" && hydratedStepRefRaw.accTreeId.trim() ? { accTreeId: hydratedStepRefRaw.accTreeId.trim() } : {}
5856
+ } : void 0;
4725
5857
  if (hydratedQuestions.length > 0) {
4726
5858
  pendingAskUser = {
4727
5859
  questions: hydratedQuestions,
4728
5860
  source: snapshot.pendingAskUser?.source === "planner" ? "planner" : "act",
4729
- askedAt: Number(snapshot.pendingAskUser?.askedAt) || Date.now()
5861
+ askedAt: Number(snapshot.pendingAskUser?.askedAt) || Date.now(),
5862
+ boundaryId: typeof snapshot.pendingAskUser?.boundaryId === "string" ? String(snapshot.pendingAskUser.boundaryId).trim() || void 0 : void 0,
5863
+ ...hydratedStepRef ? { stepRef: hydratedStepRef } : {}
4730
5864
  };
4731
5865
  } else {
4732
5866
  pendingAskUser = void 0;
4733
5867
  }
4734
5868
  if (typeof snapshot.trajectoryId === "string" && snapshot.trajectoryId.trim()) {
4735
- trajectoryId = snapshot.trajectoryId.trim();
5869
+ taskTrajectoryId = snapshot.trajectoryId.trim();
4736
5870
  }
5871
+ if (typeof snapshot.taskBoundaryId === "string" && String(snapshot.taskBoundaryId).trim()) {
5872
+ taskBoundaryId = String(snapshot.taskBoundaryId).trim();
5873
+ }
5874
+ const hydratedRootUserInput = normalizeRootUserInput(snapshot.rootUserInput);
5875
+ rootUserInput = hydratedRootUserInput || normalizeRootUserInput(getRootUserInputFallbackFromHistory()) || "";
4737
5876
  if (!tabularStore) {
4738
- tabularStore = new TabularStore(`rover-${trajectoryId}`);
5877
+ tabularStore = new TabularStore(`rover-${taskTrajectoryId}`);
4739
5878
  }
4740
5879
  postStateSnapshot();
4741
5880
  }
@@ -4757,7 +5896,7 @@ function sanitizeAssistantBlocks(input) {
4757
5896
  if (type === "tool_output" || type === "json") {
4758
5897
  out.push({
4759
5898
  type,
4760
- data: cloneUnknown(raw.data),
5899
+ data: normalizeRuntimeToolOutput(raw.data),
4761
5900
  label: typeof raw.label === "string" ? raw.label : void 0,
4762
5901
  toolName: typeof raw.toolName === "string" ? raw.toolName : void 0
4763
5902
  });
@@ -4765,6 +5904,21 @@ function sanitizeAssistantBlocks(input) {
4765
5904
  }
4766
5905
  return out.length ? out : void 0;
4767
5906
  }
5907
+ function normalizeRuntimeToolOutput(value) {
5908
+ const cloned = cloneUnknown(value);
5909
+ if (cloned == null)
5910
+ return null;
5911
+ if (typeof cloned === "string" || typeof cloned === "number" || typeof cloned === "boolean") {
5912
+ return cloned;
5913
+ }
5914
+ if (Array.isArray(cloned)) {
5915
+ return cloned;
5916
+ }
5917
+ if (typeof cloned === "object") {
5918
+ return cloned;
5919
+ }
5920
+ return String(cloned);
5921
+ }
4768
5922
  function summarizeOutputText(output) {
4769
5923
  if (output == null)
4770
5924
  return void 0;
@@ -4786,7 +5940,7 @@ function summarizeOutputText(output) {
4786
5940
  }
4787
5941
  if (lines.length)
4788
5942
  return lines.join("\n");
4789
- return `Received ${output.length} item(s).`;
5943
+ return void 0;
4790
5944
  }
4791
5945
  if (typeof output === "object") {
4792
5946
  const preferredKeys = ["response", "message", "summary", "text", "content", "result", "description"];
@@ -4796,10 +5950,7 @@ function summarizeOutputText(output) {
4796
5950
  return value.trim();
4797
5951
  }
4798
5952
  }
4799
- const keys = Object.keys(output);
4800
- if (!keys.length)
4801
- return void 0;
4802
- return `Received ${keys.length} field(s).`;
5953
+ return void 0;
4803
5954
  }
4804
5955
  return void 0;
4805
5956
  }
@@ -4844,14 +5995,14 @@ function shouldAttachStructuredBlock(output, summaryText) {
4844
5995
  }
4845
5996
  function buildAssistantPayloadFromToolOutput(output, options) {
4846
5997
  const summaryText = summarizeOutputText(output);
4847
- const text = summaryText || options?.fallbackText || "Done.";
5998
+ const text = summaryText || options?.fallbackText || "";
4848
5999
  const blocks = [];
4849
6000
  if (shouldAttachStructuredBlock(output, summaryText)) {
4850
6001
  blocks.push({
4851
6002
  type: "tool_output",
4852
6003
  label: options?.label,
4853
6004
  toolName: options?.toolName,
4854
- data: cloneUnknown(output)
6005
+ data: normalizeRuntimeToolOutput(output)
4855
6006
  });
4856
6007
  }
4857
6008
  return { text, blocks: blocks.length ? blocks : void 0 };
@@ -4861,8 +6012,15 @@ function postAssistantMessage(payload) {
4861
6012
  const blocks = typeof payload === "string" ? void 0 : sanitizeAssistantBlocks(payload.blocks);
4862
6013
  const firstTextBlock = blocks?.find((block) => block.type === "text");
4863
6014
  const firstStructuredBlock = blocks?.find((block) => block.type === "tool_output" || block.type === "json");
4864
- const resolvedText = text || firstTextBlock?.text || summarizeOutputText(firstStructuredBlock?.data) || "Done.";
4865
- self.postMessage({ type: "assistant", text: resolvedText, blocks, runId: activeRun?.runId });
6015
+ const resolvedText = text || firstTextBlock?.text || summarizeOutputText(firstStructuredBlock?.data) || "";
6016
+ if (!resolvedText && (!blocks || blocks.length === 0)) {
6017
+ return "";
6018
+ }
6019
+ const runId = activeRun?.runId;
6020
+ if (runId && cancelledRunIds.has(runId)) {
6021
+ return resolvedText;
6022
+ }
6023
+ self.postMessage({ type: "assistant", text: resolvedText, blocks, runId });
4866
6024
  return resolvedText;
4867
6025
  }
4868
6026
  function dedupeFunctionDeclarations(declarations) {
@@ -4880,9 +6038,20 @@ function removePlannerNameCollisions(declarations) {
4880
6038
  return declarations.filter((decl) => !!decl?.name && !PLANNER_TOOL_NAME_SET.has(decl.name));
4881
6039
  }
4882
6040
  function postAuthRequired(err) {
6041
+ const runId = activeRun?.runId;
6042
+ if (runId && cancelledRunIds.has(runId))
6043
+ return;
4883
6044
  const envelope = err && typeof err === "object" && err.code && err.message ? toRoverErrorEnvelope({ errorDetails: err }, "Rover API key is required.") : toRoverErrorEnvelope(err, "Rover API key is required.");
4884
- self.postMessage({ type: "auth_required", error: envelope, runId: activeRun?.runId });
4885
- }
6045
+ self.postMessage({ type: "auth_required", error: envelope, runId });
6046
+ }
6047
+ var NAVIGATION_TOOL_NAME_SET = /* @__PURE__ */ new Set([
6048
+ "goto_url",
6049
+ "google_search",
6050
+ "go_back",
6051
+ "go_forward",
6052
+ "refresh_page",
6053
+ "open_new_tab"
6054
+ ]);
4886
6055
  function toStructuredErrorPayload(err, fallbackMessage = "Operation failed") {
4887
6056
  const envelope = toRoverErrorEnvelope(err, fallbackMessage);
4888
6057
  const payload = {
@@ -4923,6 +6092,58 @@ function extractStructuredErrorFromToolResult(result) {
4923
6092
  }
4924
6093
  return void 0;
4925
6094
  }
6095
+ function sawRecentSuccessfulNavigationStep(steps) {
6096
+ if (!Array.isArray(steps) || steps.length === 0)
6097
+ return false;
6098
+ let inspectedStepCount = 0;
6099
+ for (let stepIndex = steps.length - 1; stepIndex >= 0; stepIndex -= 1) {
6100
+ const step = steps[stepIndex];
6101
+ const functions = Array.isArray(step?.functions) ? step.functions : [];
6102
+ if (!functions.length)
6103
+ continue;
6104
+ inspectedStepCount += 1;
6105
+ for (let fnIndex = functions.length - 1; fnIndex >= 0; fnIndex -= 1) {
6106
+ const fn = functions[fnIndex];
6107
+ const toolName = String(fn?.name || fn?.toolName || fn?.functionName || "").trim();
6108
+ if (!toolName || !NAVIGATION_TOOL_NAME_SET.has(toolName))
6109
+ continue;
6110
+ const status = String(fn?.response?.status || fn?.status || "").trim().toLowerCase();
6111
+ if (status === "success") {
6112
+ return true;
6113
+ }
6114
+ }
6115
+ if (inspectedStepCount >= 2)
6116
+ break;
6117
+ }
6118
+ return false;
6119
+ }
6120
+ var NAVIGATION_TRANSIENT_CODES = /* @__PURE__ */ new Set([
6121
+ "UNKNOWN_ERROR",
6122
+ "MISSING_AUTH_TOKEN",
6123
+ "MISSING_AUTH",
6124
+ "SESSION_TOKEN_EXPIRED",
6125
+ "SESSION_TOKEN_INVALID",
6126
+ "BOOTSTRAP_REQUIRED"
6127
+ ]);
6128
+ function normalizeLifecycleHandoffError(payload, steps) {
6129
+ const code = String(payload?.error?.code || "").trim().toUpperCase();
6130
+ if (code && !NAVIGATION_TRANSIENT_CODES.has(code))
6131
+ return payload;
6132
+ if (!sawRecentSuccessfulNavigationStep(steps))
6133
+ return payload;
6134
+ return {
6135
+ ...payload,
6136
+ error: {
6137
+ ...payload.error,
6138
+ code: "NAVIGATION_HANDOFF_PENDING",
6139
+ message: payload.error?.message || "Navigation handoff is in progress.",
6140
+ retryable: true,
6141
+ next_action: "Wait for post-navigation resume and continue the task."
6142
+ },
6143
+ retryable: true,
6144
+ next_action: "Wait for post-navigation resume and continue the task."
6145
+ };
6146
+ }
4926
6147
  function maybePostNavigationGuardrailFromToolResult(toolResult) {
4927
6148
  if (!toolResult || typeof toolResult !== "object")
4928
6149
  return;
@@ -4942,52 +6163,71 @@ function maybePostNavigationGuardrailFromToolResult(toolResult) {
4942
6163
  allowedDomains: output?.allowed_domains || details?.details?.allowedDomains
4943
6164
  });
4944
6165
  }
4945
- function buildChatLogFromHistory(input, currentUserInput) {
4946
- const sanitizeChatText = (raw) => {
4947
- return String(raw || "").replace(/\s+/g, " ").trim();
4948
- };
4949
- const normalizedCurrentUserInput = currentUserInput ? sanitizeChatText(currentUserInput) : "";
6166
+ function assessAdversarialInput(rawText) {
6167
+ const text = String(rawText || "").toLowerCase();
6168
+ if (!text.trim())
6169
+ return { blocked: false, score: 0, reasons: [] };
6170
+ const signals = [
6171
+ { reason: "prompt_injection", pattern: /\b(ignore|bypass|override)\b.{0,40}\b(instruction|policy|guardrail|system|developer)\b/i, weight: 2 },
6172
+ { reason: "secret_exfiltration", pattern: /\b(token|cookie|password|secret|api key|session key)\b.{0,40}\b(extract|dump|reveal|steal|exfiltrat)/i, weight: 3 },
6173
+ { reason: "credential_harvest", pattern: /\b(phish|credential|login prompt|social engineering)\b/i, weight: 3 },
6174
+ { reason: "policy_evasion", pattern: /\b(do not tell|without asking|silently|hidden|covert)\b/i, weight: 1 }
6175
+ ];
6176
+ const reasons = [];
6177
+ let score = 0;
6178
+ for (const signal of signals) {
6179
+ if (!signal.pattern.test(text))
6180
+ continue;
6181
+ reasons.push(signal.reason);
6182
+ score += signal.weight;
6183
+ }
6184
+ const blocked = score >= 3;
6185
+ return { blocked, score, reasons };
6186
+ }
6187
+ function buildChatLogFromHistory(input, currentUserInput, maxTurns = MAX_RESUME_CUE_TURNS) {
6188
+ const normalize = (value) => String(value || "").replace(/\s+/g, " ").trim();
6189
+ const normalizedCurrent = normalize(String(currentUserInput || ""));
6190
+ if (!normalizedCurrent)
6191
+ return [];
4950
6192
  const entries = input.filter((message) => message.role === "user" || message.role === "assistant").map((message) => ({
4951
6193
  role: message.role,
4952
- content: sanitizeChatText(String(message.content || ""))
4953
- })).filter((message) => !!message.content);
4954
- if (normalizedCurrentUserInput) {
4955
- for (let i = entries.length - 1; i >= 0; i -= 1) {
4956
- if (entries[i].role === "user" && entries[i].content === normalizedCurrentUserInput) {
4957
- entries.splice(i, 1);
4958
- break;
4959
- }
4960
- }
4961
- }
4962
- const deduped = [];
6194
+ content: normalize(String(message.content || ""))
6195
+ })).filter((message) => !!message.content).filter((message) => {
6196
+ const lowered = message.content.toLowerCase();
6197
+ if (lowered.includes("[ask_user_answers]"))
6198
+ return false;
6199
+ if (lowered.includes("[raw_user_reply]"))
6200
+ return false;
6201
+ if (lowered.includes("[continue_planning]"))
6202
+ return false;
6203
+ return true;
6204
+ });
6205
+ const turns = [];
4963
6206
  for (const entry of entries) {
4964
- const previous = deduped[deduped.length - 1];
4965
- if (previous && previous.role === entry.role && previous.content === entry.content)
6207
+ if (entry.role === "user") {
6208
+ turns.push({ user: entry.content });
4966
6209
  continue;
4967
- deduped.push(entry);
4968
- }
4969
- const firstUser = deduped.find((message) => message.role === "user");
4970
- const tailBudget = firstUser ? Math.max(1, MAX_CHATLOG_ENTRIES2 - 1) : MAX_CHATLOG_ENTRIES2;
4971
- let selected = deduped.slice(-tailBudget);
4972
- if (firstUser) {
4973
- const hasAnchor = selected.some((message) => message.role === "user" && message.content === firstUser.content);
4974
- if (!hasAnchor) {
4975
- selected = [firstUser, ...selected];
6210
+ }
6211
+ if (!turns.length)
6212
+ continue;
6213
+ const latest = turns[turns.length - 1];
6214
+ if (!latest.assistant) {
6215
+ latest.assistant = entry.content;
4976
6216
  }
4977
6217
  }
4978
- if (selected.length > MAX_CHATLOG_ENTRIES2) {
4979
- selected = selected.slice(-MAX_CHATLOG_ENTRIES2);
4980
- }
4981
- if (selected.length > 1 && selected[0]?.role !== "user") {
4982
- const firstUserIndex = selected.findIndex((message) => message.role === "user");
4983
- if (firstUserIndex > 0) {
4984
- selected = selected.slice(firstUserIndex);
6218
+ if (!turns.length)
6219
+ return [];
6220
+ const cappedTurns = turns.slice(-Math.max(1, Math.min(4, Math.floor(maxTurns))));
6221
+ const messages = [];
6222
+ for (const turn of cappedTurns) {
6223
+ if (turn.user && turn.user !== normalizedCurrent) {
6224
+ messages.push({ role: "user", message: turn.user });
6225
+ }
6226
+ if (turn.assistant) {
6227
+ messages.push({ role: "model", message: turn.assistant });
4985
6228
  }
4986
6229
  }
4987
- return selected.map((message) => ({
4988
- role: message.role === "user" ? "user" : "model",
4989
- message: message.content
4990
- }));
6230
+ return messages;
4991
6231
  }
4992
6232
  function applyAgentPrevSteps(next, options) {
4993
6233
  if (!Array.isArray(next) || !next.length)
@@ -5049,14 +6289,14 @@ function buildPlannerToolResultBlocks(toolResults) {
5049
6289
  type: "tool_output",
5050
6290
  label: result.toolName ? `${stepLabel}: ${result.toolName}` : stepLabel,
5051
6291
  toolName: typeof result.toolName === "string" ? result.toolName : void 0,
5052
- data: cloneUnknown(output)
6292
+ data: normalizeRuntimeToolOutput(output)
5053
6293
  });
5054
6294
  }
5055
6295
  if (result.error || result.errorDetails) {
5056
6296
  blocks.push({
5057
6297
  type: "json",
5058
6298
  label: `${stepLabel} error`,
5059
- data: cloneUnknown({
6299
+ data: normalizeRuntimeToolOutput({
5060
6300
  error: result.error,
5061
6301
  errorDetails: result.errorDetails
5062
6302
  })
@@ -5142,6 +6382,24 @@ function detectOpenedTabFromToolResult(result) {
5142
6382
  }
5143
6383
  return void 0;
5144
6384
  }
6385
+ function extractTouchedTabIdsFromToolResult(result) {
6386
+ if (!result || typeof result !== "object")
6387
+ return [];
6388
+ const output = result.output && typeof result.output === "object" ? result.output : void 0;
6389
+ const candidates = [
6390
+ Number(result.logicalTabId ?? result.logical_tab_id ?? result.tabId ?? result.tab_id),
6391
+ Number(output?.logicalTabId ?? output?.logical_tab_id ?? output?.tabId ?? output?.tab_id)
6392
+ ];
6393
+ const unique = /* @__PURE__ */ new Set();
6394
+ const out = [];
6395
+ for (const value of candidates) {
6396
+ if (!Number.isFinite(value) || value <= 0 || unique.has(value))
6397
+ continue;
6398
+ unique.add(value);
6399
+ out.push(value);
6400
+ }
6401
+ return out;
6402
+ }
5145
6403
  function extractQuestionsFromResult(result) {
5146
6404
  if (!result || typeof result !== "object")
5147
6405
  return void 0;
@@ -5149,7 +6407,7 @@ function extractQuestionsFromResult(result) {
5149
6407
  if (topLevel.length)
5150
6408
  return topLevel;
5151
6409
  const output = result.output;
5152
- const outputQuestions = normalizePlannerQuestions(output?.questions);
6410
+ const outputQuestions = normalizePlannerQuestions(output && typeof output === "object" ? output.questions : void 0);
5153
6411
  if (outputQuestions.length)
5154
6412
  return outputQuestions;
5155
6413
  return void 0;
@@ -5173,77 +6431,170 @@ function extractPlannerQuestionsFromToolResults(toolResults) {
5173
6431
  }
5174
6432
  return combined.slice(0, 6);
5175
6433
  }
5176
- function deriveDirectToolRunOutcome(result) {
5177
- if (!result || typeof result !== "object") {
5178
- return { taskComplete: false };
6434
+ function inferPlannerContinuation(toolResults) {
6435
+ if (!Array.isArray(toolResults) || toolResults.length === 0) {
6436
+ return {
6437
+ failed: false,
6438
+ needsUserInput: false,
6439
+ navigationPending: false,
6440
+ sameTabNavigationPending: false
6441
+ };
5179
6442
  }
5180
- const questions = extractQuestionsFromResult(result);
5181
- const topLevelStatus = String(result.status || "").trim().toLowerCase();
5182
- if (topLevelStatus === "failure" || topLevelStatus === "failed" || topLevelStatus === "error") {
5183
- return { taskComplete: false };
6443
+ let failed = false;
6444
+ let needsUserInput = false;
6445
+ let navigationPending = false;
6446
+ let sameTabNavigationPending = false;
6447
+ for (const result of toolResults) {
6448
+ if (!result || typeof result !== "object")
6449
+ continue;
6450
+ if (result.error || result.errorDetails) {
6451
+ failed = true;
6452
+ continue;
6453
+ }
6454
+ const output = result.output && typeof result.output === "object" ? result.output : void 0;
6455
+ if (!output)
6456
+ continue;
6457
+ const navigationOutcome = String(output.navigationOutcome || "").trim().toLowerCase();
6458
+ if (output.navigationPending === true || navigationOutcome === "same_tab_scheduled" || navigationOutcome === "new_tab_opened") {
6459
+ navigationPending = true;
6460
+ if (navigationOutcome === "same_tab_scheduled") {
6461
+ sameTabNavigationPending = true;
6462
+ }
6463
+ }
6464
+ const status = String(output.taskStatus || output.status || "").trim().toLowerCase();
6465
+ if (status === "failure" || status === "failed" || status === "error") {
6466
+ failed = true;
6467
+ }
6468
+ if (status === "waiting_input" || status === "needs_input" || status === "pending_user_input") {
6469
+ needsUserInput = true;
6470
+ }
6471
+ if (output.needsUserInput === true || output.waitingForUserInput === true) {
6472
+ needsUserInput = true;
6473
+ }
6474
+ const questions = normalizePlannerQuestions(output.questions);
6475
+ if (questions.length > 0) {
6476
+ needsUserInput = true;
6477
+ }
5184
6478
  }
5185
- if (topLevelStatus === "waiting_input" || topLevelStatus === "needs_input" || topLevelStatus === "pending_user_input") {
5186
- return { taskComplete: false, needsUserInput: true, questions };
6479
+ return { failed, needsUserInput, navigationPending, sameTabNavigationPending };
6480
+ }
6481
+ function deriveDirectToolRunOutcome(result) {
6482
+ if (!result || typeof result !== "object") {
6483
+ return {
6484
+ taskComplete: true,
6485
+ terminalState: "completed",
6486
+ contextResetRecommended: true
6487
+ };
5187
6488
  }
5188
- if (result.error) {
5189
- return { taskComplete: false };
6489
+ const topLevelNavigationOutcome = String(result.navigationOutcome || "").trim().toLowerCase();
6490
+ if (result.navigationPending === true) {
6491
+ const isSameTabHandoff = topLevelNavigationOutcome === "same_tab_scheduled";
6492
+ return {
6493
+ taskComplete: false,
6494
+ terminalState: "in_progress",
6495
+ navigationPending: isSameTabHandoff,
6496
+ continuationReason: isSameTabHandoff ? "same_tab_navigation_handoff" : "loop_continue"
6497
+ };
5190
6498
  }
6499
+ const questions = extractQuestionsFromResult(result);
6500
+ if (result.error)
6501
+ return { taskComplete: false, terminalState: "failed" };
5191
6502
  const output = result.output;
5192
6503
  if (output && typeof output === "object") {
5193
- if (Array.isArray(output)) {
5194
- return { taskComplete: true };
6504
+ const outputRecord = output;
6505
+ const topLevelStatus = String(result.status || "").trim().toLowerCase();
6506
+ if (topLevelStatus === "failure" || topLevelStatus === "failed" || topLevelStatus === "error") {
6507
+ return { taskComplete: false, terminalState: "failed" };
5195
6508
  }
5196
- if (output.needsUserInput === true || output.waitingForUserInput === true) {
5197
- return { taskComplete: false, needsUserInput: true, questions };
6509
+ if (topLevelStatus === "waiting_input" || topLevelStatus === "needs_input" || topLevelStatus === "pending_user_input") {
6510
+ return {
6511
+ taskComplete: false,
6512
+ needsUserInput: true,
6513
+ questions,
6514
+ terminalState: "waiting_input",
6515
+ continuationReason: "awaiting_user"
6516
+ };
5198
6517
  }
5199
- if (Array.isArray(output.questions) && output.questions.length > 0) {
5200
- return { taskComplete: false, needsUserInput: true, questions };
6518
+ const navigationOutcome = String(outputRecord.navigationOutcome || "").trim().toLowerCase();
6519
+ const navigationMode = String(outputRecord.navigation || "").trim().toLowerCase();
6520
+ if (outputRecord.navigationPending === true || navigationOutcome === "same_tab_scheduled" || navigationOutcome === "new_tab_opened" || navigationMode === "same_tab") {
6521
+ const sameTabHandoff = navigationOutcome === "same_tab_scheduled" || navigationMode === "same_tab";
6522
+ return {
6523
+ taskComplete: false,
6524
+ terminalState: "in_progress",
6525
+ navigationPending: sameTabHandoff,
6526
+ continuationReason: sameTabHandoff ? "same_tab_navigation_handoff" : "loop_continue",
6527
+ contextResetRecommended: false
6528
+ };
5201
6529
  }
5202
- if (output.error) {
5203
- return { taskComplete: false };
6530
+ if (outputRecord.needsUserInput === true || outputRecord.waitingForUserInput === true) {
6531
+ return {
6532
+ taskComplete: false,
6533
+ needsUserInput: true,
6534
+ questions,
6535
+ terminalState: "waiting_input",
6536
+ continuationReason: "awaiting_user"
6537
+ };
5204
6538
  }
5205
- if (typeof output.taskComplete === "boolean") {
5206
- return { taskComplete: !!output.taskComplete };
6539
+ if (Array.isArray(outputRecord.questions) && outputRecord.questions.length > 0) {
6540
+ return {
6541
+ taskComplete: false,
6542
+ needsUserInput: true,
6543
+ questions,
6544
+ terminalState: "waiting_input",
6545
+ continuationReason: "awaiting_user"
6546
+ };
5207
6547
  }
5208
- const taskStatus = String(output.taskStatus || output.status || "").toLowerCase();
6548
+ if (outputRecord.error || outputRecord.success === false)
6549
+ return { taskComplete: false, terminalState: "failed" };
6550
+ const taskStatus = String(outputRecord.taskStatus || outputRecord.status || "").toLowerCase();
5209
6551
  if (taskStatus) {
5210
6552
  if (taskStatus === "waiting_input" || taskStatus === "needs_input" || taskStatus === "pending_user_input") {
5211
- return { taskComplete: false, needsUserInput: true };
5212
- }
5213
- if (taskStatus === "running" || taskStatus === "in_progress" || taskStatus === "pending") {
5214
- return { taskComplete: false };
5215
- }
5216
- if (taskStatus === "completed" || taskStatus === "complete" || taskStatus === "done" || taskStatus === "success") {
5217
- return { taskComplete: true };
6553
+ return {
6554
+ taskComplete: false,
6555
+ needsUserInput: true,
6556
+ terminalState: "waiting_input",
6557
+ continuationReason: "awaiting_user"
6558
+ };
5218
6559
  }
5219
6560
  if (taskStatus === "failure" || taskStatus === "failed" || taskStatus === "error") {
5220
- return { taskComplete: false };
6561
+ return { taskComplete: false, terminalState: "failed" };
5221
6562
  }
5222
6563
  }
5223
- if (output.success === false) {
5224
- return { taskComplete: false };
5225
- }
5226
- if (String(output.status || "").trim().toLowerCase() === "failure") {
5227
- return { taskComplete: false };
5228
- }
5229
- }
5230
- if (output != null) {
5231
- return { taskComplete: true };
5232
6564
  }
5233
- return { taskComplete: true };
6565
+ return {
6566
+ taskComplete: true,
6567
+ terminalState: "completed",
6568
+ contextResetRecommended: true
6569
+ };
5234
6570
  }
5235
6571
  function normalizeRunOutcome(outcome) {
5236
6572
  if (!outcome || typeof outcome !== "object") {
5237
- return { taskComplete: false, needsUserInput: false };
6573
+ return {
6574
+ taskComplete: true,
6575
+ needsUserInput: false,
6576
+ terminalState: "completed",
6577
+ continuationReason: void 0,
6578
+ contextResetRecommended: true
6579
+ };
5238
6580
  }
5239
- const needsUserInput = outcome.needsUserInput === true;
5240
- const taskComplete = outcome.taskComplete === true && !needsUserInput;
6581
+ const inferredNeedsUserInput = outcome.needsUserInput === true;
6582
+ const incomingTerminalState = String(outcome.terminalState || "").trim().toLowerCase();
6583
+ const terminalState = incomingTerminalState === "waiting_input" || incomingTerminalState === "in_progress" || incomingTerminalState === "completed" || incomingTerminalState === "failed" ? incomingTerminalState : inferredNeedsUserInput ? "waiting_input" : outcome.taskComplete === true ? "completed" : outcome.taskComplete === false ? "in_progress" : "completed";
6584
+ const needsUserInput = terminalState === "waiting_input" || inferredNeedsUserInput;
6585
+ const taskComplete = terminalState === "completed" || outcome.taskComplete === true && !needsUserInput;
5241
6586
  const questions = normalizePlannerQuestions(outcome.questions);
6587
+ const continuationCandidate = String(outcome.continuationReason || "").trim().toLowerCase();
6588
+ const continuationReason = continuationCandidate === "loop_continue" || continuationCandidate === "same_tab_navigation_handoff" || continuationCandidate === "awaiting_user" ? continuationCandidate : needsUserInput ? "awaiting_user" : outcome.navigationPending === true ? "same_tab_navigation_handoff" : terminalState === "in_progress" && !taskComplete ? "loop_continue" : void 0;
6589
+ const contextResetRecommended = outcome.contextResetRecommended === true || terminalState === "completed" || terminalState === "failed";
5242
6590
  return {
5243
6591
  route: outcome.route,
5244
6592
  taskComplete,
5245
6593
  needsUserInput,
5246
- questions: questions.length ? questions : void 0
6594
+ questions: questions.length ? questions : void 0,
6595
+ terminalState,
6596
+ continuationReason,
6597
+ contextResetRecommended
5247
6598
  };
5248
6599
  }
5249
6600
  function rememberTerminalRun(runId, result) {
@@ -5264,11 +6615,19 @@ function rememberCancelledRun(runId) {
5264
6615
  cancelledRunIds.delete(oldest);
5265
6616
  }
5266
6617
  }
5267
- function clearTaskScopedContextAfterCompletion() {
6618
+ function throwIfCancelledRun(runId) {
6619
+ if (!runId)
6620
+ return;
6621
+ if (cancelledRunIds.has(runId)) {
6622
+ throw new DOMException("Run cancelled", "AbortError");
6623
+ }
6624
+ }
6625
+ function clearTaskScopedContextAfterBoundary(_reason) {
5268
6626
  history.length = 0;
5269
6627
  plannerHistory = [];
5270
6628
  agentPrevSteps = [];
5271
6629
  pendingAskUser = void 0;
6630
+ rootUserInput = "";
5272
6631
  }
5273
6632
  async function maybeWaitForNewTab(result) {
5274
6633
  const openedTab = detectOpenedTabFromToolResult(result);
@@ -5284,52 +6643,66 @@ async function handleUserMessage(text, options) {
5284
6643
  throw new Error("Worker not initialized");
5285
6644
  if (!bridgeRpc)
5286
6645
  throw new Error("Bridge RPC not initialized");
6646
+ const activeRunId = activeRun?.runId;
6647
+ throwIfCancelledRun(activeRunId);
5287
6648
  postStatus("Analyzing request", text, "analyze");
5288
6649
  const shouldSkipUserPush = options?.resume && history.length > 0 && history[history.length - 1]?.role === "user" && history[history.length - 1]?.content === text;
5289
- if (!shouldSkipUserPush) {
5290
- history.push({ role: "user", content: text });
5291
- postStateSnapshot();
6650
+ const normalizedIncomingText = String(text || "").trim();
6651
+ if (config.external?.adversarialGate === "pre_tool_block") {
6652
+ const adversarial = assessAdversarialInput(normalizedIncomingText);
6653
+ if (adversarial.blocked) {
6654
+ const blockedMessage = "I can\u2019t run that request because it appears adversarial. Please rephrase with a safe, task-focused instruction.";
6655
+ postAssistantMessage(blockedMessage);
6656
+ postStatus("Blocked unsafe request", adversarial.reasons.join(", ") || "adversarial_input", "complete");
6657
+ postStateSnapshot();
6658
+ return {
6659
+ taskComplete: false,
6660
+ needsUserInput: false,
6661
+ terminalState: "failed",
6662
+ contextResetRecommended: true
6663
+ };
6664
+ }
6665
+ }
6666
+ const pendingAskUserSnapshot = pendingAskUser && pendingAskUser.boundaryId && pendingAskUser.boundaryId !== taskBoundaryId ? void 0 : pendingAskUser;
6667
+ if (!pendingAskUserSnapshot && pendingAskUser) {
6668
+ pendingAskUser = void 0;
6669
+ }
6670
+ const fallbackRootInput = normalizeRootUserInput(getRootUserInputFallbackFromHistory());
6671
+ const existingRootInput = normalizeRootUserInput(rootUserInput) || fallbackRootInput;
6672
+ if (existingRootInput) {
6673
+ rootUserInput = existingRootInput;
6674
+ }
6675
+ if (!pendingAskUserSnapshot?.questions?.length && normalizedIncomingText && !options?.resume) {
6676
+ rootUserInput = normalizedIncomingText;
5292
6677
  }
5293
6678
  let effectiveUserInput = text;
5294
- if (pendingAskUser?.questions?.length) {
5295
- const normalizedAnswers = normalizeAskUserAnswerMeta(options?.askUserAnswers, pendingAskUser.questions, text);
6679
+ let chatLogCurrentInputForDedupe = text;
6680
+ let consumedAsAskUserAnswer = false;
6681
+ if (pendingAskUserSnapshot?.questions?.length) {
6682
+ const normalizedAnswers = normalizeAskUserAnswerMeta(options?.askUserAnswers, pendingAskUserSnapshot.questions, text);
5296
6683
  if (normalizedAnswers) {
5297
- const answerContext = buildAskUserAnswerContext(pendingAskUser.questions, normalizedAnswers);
5298
- effectiveUserInput = answerContext;
5299
- plannerHistory = sanitizePlannerHistoryForPersist([
5300
- ...plannerHistory,
5301
- {
5302
- thought: "User provided clarification answers.",
5303
- questionsAsked: pendingAskUser.questions,
5304
- userAnswers: normalizedAnswers.answersByKey
5305
- }
5306
- ]);
5307
- applyAgentPrevSteps([
5308
- ...agentPrevSteps,
5309
- {
5310
- functions: [{
5311
- name: "ask_user",
5312
- args: {
5313
- questions_to_ask: pendingAskUser.questions.map((q) => ({
5314
- key: q.key,
5315
- query: q.query
5316
- }))
5317
- },
5318
- response: {
5319
- status: "Success",
5320
- output: {
5321
- ask_user_answers: normalizedAnswers.answersByKey,
5322
- raw_user_reply: normalizedAnswers.rawText
5323
- }
5324
- }
5325
- }]
5326
- }
5327
- ], { snapshot: false });
6684
+ consumedAsAskUserAnswer = true;
6685
+ const answerContext = buildAskUserAnswerContext(pendingAskUserSnapshot.questions, normalizedAnswers);
6686
+ const focusInput = normalizeRootUserInput(rootUserInput) || normalizedIncomingText || fallbackRootInput || "";
6687
+ if (focusInput)
6688
+ rootUserInput = focusInput;
6689
+ effectiveUserInput = buildContinuePlanningInput(focusInput, "ask_user", answerContext);
6690
+ chatLogCurrentInputForDedupe = focusInput || text;
6691
+ if (pendingAskUserSnapshot.source === "planner") {
6692
+ plannerHistory = mergeAskUserAnswersIntoPlannerHistory(plannerHistory, pendingAskUserSnapshot.questions, normalizedAnswers.answersByKey);
6693
+ }
6694
+ applyAgentPrevSteps(mergeAskUserAnswerIntoPrevSteps(agentPrevSteps, pendingAskUserSnapshot, normalizedAnswers), { snapshot: false });
5328
6695
  pendingAskUser = void 0;
5329
6696
  postStateSnapshot();
5330
6697
  }
5331
6698
  }
6699
+ if (!shouldSkipUserPush && !consumedAsAskUserAnswer) {
6700
+ history.push({ role: "user", content: text });
6701
+ postStateSnapshot();
6702
+ }
6703
+ throwIfCancelledRun(activeRunId);
5332
6704
  const tabs = await getKnownTabs();
6705
+ throwIfCancelledRun(activeRunId);
5333
6706
  const fallbackTabs = tabs.length > 0 ? tabs : [
5334
6707
  {
5335
6708
  id: 1,
@@ -5337,7 +6710,14 @@ async function handleUserMessage(text, options) {
5337
6710
  accessMode: "live_dom"
5338
6711
  }
5339
6712
  ];
5340
- const resolvedTabs = await resolveRuntimeTabs(bridgeRpc, fallbackTabs);
6713
+ const resolvedTabs = await resolveRuntimeTabs(bridgeRpc, fallbackTabs, {
6714
+ scopedTabIds,
6715
+ seedTabId: scopedSeedTabId,
6716
+ onDiagnostics: (payload) => {
6717
+ postRuntimeTabsDiagnostics(payload);
6718
+ }
6719
+ });
6720
+ throwIfCancelledRun(activeRunId);
5341
6721
  const tabsById = new Map(tabs.map((tab) => [tab.id, tab]));
5342
6722
  const orderedTabs = resolvedTabs.tabOrder.map((tabId) => {
5343
6723
  const knownTab = tabsById.get(tabId);
@@ -5357,28 +6737,42 @@ async function handleUserMessage(text, options) {
5357
6737
  };
5358
6738
  }).filter((tab) => Number.isFinite(tab.id) && tab.id > 0);
5359
6739
  const tabsForRun = orderedTabs.length > 0 ? orderedTabs : fallbackTabs;
6740
+ if (orderedTabs.length === 0) {
6741
+ postStatus("Using fallback tab mapping", `active=${resolvedTabs.activeTabId}; order=${resolvedTabs.tabOrder.join(",") || "none"}`, "analyze");
6742
+ }
5360
6743
  if (!tabularStore) {
5361
- tabularStore = new TabularStore(`rover-${trajectoryId}`);
6744
+ tabularStore = new TabularStore(`rover-${taskTrajectoryId}`);
5362
6745
  }
5363
6746
  const agentName = resolveAgentName(config);
5364
6747
  const runtimeContext = buildRoverRuntimeContext2({
5365
6748
  tabs: tabsForRun,
5366
6749
  agentName,
5367
- externalNavigationPolicy: resolveRuntimeExternalNavigationPolicy(config)
6750
+ externalNavigationPolicy: resolveRuntimeExternalNavigationPolicy(config),
6751
+ taskBoundaryId
5368
6752
  });
5369
6753
  const ctx = createAgentContext({
5370
6754
  ...config,
5371
6755
  signal: activeAbortController?.signal,
6756
+ sessionId: workerSessionId || config.sessionId || taskTrajectoryId,
6757
+ activeRunId,
5372
6758
  runtimeContext,
5373
6759
  tools: {
5374
6760
  web: extractWebToolsConfig(config)
5375
6761
  }
5376
6762
  }, bridgeRpc, tabularStore);
5377
- const currentRunId = activeRun?.runId;
5378
- ctx.isCancelled = () => !!(currentRunId && cancelledRunIds.has(currentRunId));
6763
+ ctx.isCancelled = () => !!(activeRunId && cancelledRunIds.has(activeRunId));
5379
6764
  const functionDeclarations = dedupeFunctionDeclarations(removePlannerNameCollisions(toolRegistry.getFunctionDeclarations()));
5380
6765
  const toolFunctions = toolRegistry.getToolFunctions();
5381
- const chatLog = buildChatLogFromHistory(history, text);
6766
+ const resumeFollowupTurns = Math.max(1, Math.min(4, Math.floor(Number(config.chat?.resumeFollowup?.maxTurns) || MAX_RESUME_CUE_TURNS)));
6767
+ const normalizedFollowupChatLog = normalizeFollowupChatLog(options?.followupChatLog);
6768
+ const chatLog = shouldBuildResumeCueChatLog({
6769
+ resume: options?.resume,
6770
+ preserveHistory: options?.preserveHistory,
6771
+ resumeFollowupMode: config.chat?.resumeFollowup?.mode
6772
+ }) ? buildChatLogFromHistory(history, chatLogCurrentInputForDedupe, resumeFollowupTurns) : shouldUseFollowupChatLog({
6773
+ resume: options?.resume,
6774
+ followupChatLogLength: normalizedFollowupChatLog.length
6775
+ }) ? normalizedFollowupChatLog : [];
5382
6776
  const onPrevStepsUpdate = (steps) => {
5383
6777
  applyAgentPrevSteps(steps, { snapshot: true });
5384
6778
  };
@@ -5386,10 +6780,22 @@ async function handleUserMessage(text, options) {
5386
6780
  plannerHistory = sanitizePlannerHistoryForPersist(Array.isArray(steps) ? steps : []);
5387
6781
  postStateSnapshot();
5388
6782
  };
6783
+ const getScopedTabRuntimeContext = () => ({
6784
+ scopedTabIds: [...scopedTabIds],
6785
+ seedTabId: scopedSeedTabId
6786
+ });
6787
+ const onScopedTabIdsTouched = (tabIds) => {
6788
+ touchRunScopedTabIds(tabIds);
6789
+ };
6790
+ throwIfCancelledRun(activeRunId);
5389
6791
  const result = await handleSendMessageWithFunctions(effectiveUserInput, {
5390
6792
  tabs: tabsForRun,
6793
+ scopedTabIds,
6794
+ seedTabId: scopedSeedTabId,
6795
+ getScopedTabRuntimeContext,
6796
+ onScopedTabIdsTouched,
5391
6797
  previousMessages: history,
5392
- trajectoryId,
6798
+ trajectoryId: taskTrajectoryId,
5393
6799
  files: [],
5394
6800
  recordingContext: config.recordingContext,
5395
6801
  previousSteps: plannerHistory,
@@ -5407,25 +6813,49 @@ async function handleUserMessage(text, options) {
5407
6813
  onPrevStepsUpdate,
5408
6814
  onPlannerHistoryUpdate
5409
6815
  });
6816
+ throwIfCancelledRun(activeRunId);
6817
+ if (result.directToolResult) {
6818
+ touchRunScopedTabIds(extractTouchedTabIdsFromToolResult(result.directToolResult));
6819
+ }
6820
+ if (Array.isArray(result.executedFunctions) && result.executedFunctions.length > 0) {
6821
+ for (const executed of result.executedFunctions) {
6822
+ touchRunScopedTabIds(extractTouchedTabIdsFromToolResult(executed?.result));
6823
+ }
6824
+ }
5410
6825
  if (!result.success) {
5411
- const errorPayload = toStructuredErrorPayload(result.error, "Something went wrong.");
6826
+ const rawErrorPayload = toStructuredErrorPayload(result.error, "Something went wrong.");
6827
+ const errorPayload = normalizeLifecycleHandoffError(rawErrorPayload, agentPrevSteps);
6828
+ const errorCode = String(errorPayload.error.code || "").trim().toUpperCase();
6829
+ const isRetryableLifecycleError = !!errorPayload.error.retryable || errorCode === "STALE_SEQ" || errorCode === "STALE_EPOCH" || errorCode === "SESSION_TOKEN_EXPIRED" || errorCode === "NAVIGATION_HANDOFF_PENDING";
6830
+ if (isRetryableLifecycleError) {
6831
+ const retryMessage = `${errorPayload.error.code}: ${errorPayload.error.message}`;
6832
+ postAssistantMessage(retryMessage);
6833
+ postStatus("Waiting for navigation/session sync", errorPayload.error.message, "verify");
6834
+ postStateSnapshot();
6835
+ return {
6836
+ route: result.route,
6837
+ taskComplete: false,
6838
+ terminalState: "in_progress",
6839
+ continuationReason: "loop_continue",
6840
+ contextResetRecommended: false
6841
+ };
6842
+ }
5412
6843
  if (errorPayload.error.requires_api_key) {
5413
6844
  postAuthRequired(errorPayload.error);
5414
6845
  }
5415
- const errorMsg = postAssistantMessage({
6846
+ postAssistantMessage({
5416
6847
  text: `${errorPayload.error.code}: ${errorPayload.error.message}`,
5417
6848
  blocks: [
5418
6849
  {
5419
6850
  type: "json",
5420
6851
  label: "Error details",
5421
- data: cloneUnknown(errorPayload)
6852
+ data: normalizeRuntimeToolOutput(errorPayload)
5422
6853
  }
5423
6854
  ]
5424
6855
  });
5425
- history.push({ role: "assistant", content: errorMsg });
5426
6856
  postStatus("Execution failed", errorPayload.error.message, "complete");
5427
6857
  postStateSnapshot();
5428
- return { route: result.route, taskComplete: false };
6858
+ return { route: result.route, taskComplete: false, terminalState: "failed" };
5429
6859
  }
5430
6860
  if (result.executedFunctions?.length) {
5431
6861
  for (const fn of result.executedFunctions) {
@@ -5433,6 +6863,12 @@ async function handleUserMessage(text, options) {
5433
6863
  }
5434
6864
  const blocks = [];
5435
6865
  const lines = [];
6866
+ let inferredFailed = false;
6867
+ let inferredCompleted = false;
6868
+ let inferredNavigationPending = false;
6869
+ let inferredSameTabPending = false;
6870
+ let inferredNeedsUserInput = false;
6871
+ let inferredQuestions = [];
5436
6872
  for (const fn of result.executedFunctions) {
5437
6873
  const summary = summarizeOutputText(fn.result);
5438
6874
  if (fn.result !== void 0 && shouldAttachStructuredBlock(fn.result, summary)) {
@@ -5440,28 +6876,100 @@ async function handleUserMessage(text, options) {
5440
6876
  type: "tool_output",
5441
6877
  toolName: fn.name,
5442
6878
  label: `${fn.name} output`,
5443
- data: cloneUnknown(fn.result)
6879
+ data: normalizeRuntimeToolOutput(fn.result)
5444
6880
  });
5445
6881
  }
5446
6882
  if (fn.error) {
5447
6883
  blocks.push({
5448
6884
  type: "json",
5449
6885
  label: `${fn.name} error`,
5450
- data: cloneUnknown({ error: fn.error })
6886
+ data: normalizeRuntimeToolOutput({ error: fn.error })
5451
6887
  });
5452
6888
  }
5453
6889
  lines.push(summary ? `@${fn.name}: ${summary}` : `@${fn.name}: ${fn.error || "ok"}`);
6890
+ if (fn.error) {
6891
+ inferredFailed = true;
6892
+ continue;
6893
+ }
6894
+ const rawResult = fn.result;
6895
+ if (!rawResult || typeof rawResult !== "object")
6896
+ continue;
6897
+ const normalized = Array.isArray(rawResult) ? rawResult.find((item) => item && typeof item === "object") : rawResult;
6898
+ if (!normalized)
6899
+ continue;
6900
+ const taskComplete = normalized.taskComplete;
6901
+ if (typeof taskComplete === "boolean" && taskComplete) {
6902
+ inferredCompleted = true;
6903
+ }
6904
+ const navigationPending = normalized.navigationPending === true;
6905
+ const navigationOutcome = String(normalized.navigationOutcome || "").trim().toLowerCase();
6906
+ if (navigationPending || navigationOutcome === "same_tab_scheduled" || navigationOutcome === "new_tab_opened") {
6907
+ inferredNavigationPending = true;
6908
+ if (navigationOutcome === "same_tab_scheduled") {
6909
+ inferredSameTabPending = true;
6910
+ }
6911
+ }
6912
+ const statusRaw = String(normalized.taskStatus || normalized.status || "").trim().toLowerCase();
6913
+ if (statusRaw === "failed" || statusRaw === "error" || statusRaw === "failure") {
6914
+ inferredFailed = true;
6915
+ }
6916
+ if (statusRaw === "waiting_input" || statusRaw === "needs_input" || statusRaw === "pending_user_input") {
6917
+ inferredNeedsUserInput = true;
6918
+ }
6919
+ if (statusRaw === "completed" || statusRaw === "complete" || statusRaw === "done" || statusRaw === "success") {
6920
+ inferredCompleted = true;
6921
+ }
6922
+ if (normalized.needsUserInput === true || normalized.waitingForUserInput === true) {
6923
+ inferredNeedsUserInput = true;
6924
+ }
6925
+ const q = extractQuestionsFromResult(normalized);
6926
+ if (q?.length) {
6927
+ inferredNeedsUserInput = true;
6928
+ inferredQuestions = normalizePlannerQuestions([...inferredQuestions, ...q]);
6929
+ }
5454
6930
  }
5455
- const msg = postAssistantMessage({
5456
- text: lines.join("\n") || "Done.",
6931
+ postAssistantMessage({
6932
+ text: lines.join("\n"),
5457
6933
  blocks
5458
6934
  });
5459
- history.push({ role: "assistant", content: msg });
5460
6935
  postStatus("Execution completed", "Function calls finished", "complete");
5461
6936
  postStateSnapshot();
5462
- return { route: result.route, taskComplete: true };
6937
+ if (inferredFailed) {
6938
+ return {
6939
+ route: result.route,
6940
+ taskComplete: false,
6941
+ terminalState: "failed"
6942
+ };
6943
+ }
6944
+ if (inferredNavigationPending) {
6945
+ return {
6946
+ route: result.route,
6947
+ taskComplete: false,
6948
+ terminalState: "in_progress",
6949
+ continuationReason: inferredSameTabPending ? "same_tab_navigation_handoff" : "loop_continue",
6950
+ contextResetRecommended: false
6951
+ };
6952
+ }
6953
+ if (inferredNeedsUserInput) {
6954
+ return {
6955
+ route: result.route,
6956
+ taskComplete: false,
6957
+ needsUserInput: true,
6958
+ questions: inferredQuestions.length ? inferredQuestions : void 0,
6959
+ terminalState: "waiting_input",
6960
+ continuationReason: "awaiting_user"
6961
+ };
6962
+ }
6963
+ return {
6964
+ route: result.route,
6965
+ taskComplete: true,
6966
+ terminalState: "completed",
6967
+ continuationReason: void 0,
6968
+ contextResetRecommended: true
6969
+ };
5463
6970
  }
5464
6971
  if (result.directToolResult) {
6972
+ throwIfCancelledRun(activeRunId);
5465
6973
  const newTabWait = await maybeWaitForNewTab(result.directToolResult);
5466
6974
  if (newTabWait.openedTab && newTabWait.readyState && !newTabWait.readyState.ready && !newTabWait.readyState.external) {
5467
6975
  const fallbackUrl = String(newTabWait.openedTab.url || "").trim();
@@ -5507,9 +7015,7 @@ async function handleUserMessage(text, options) {
5507
7015
  const questions = normalizePlannerQuestions(outcome.questions);
5508
7016
  if (outcome.needsUserInput && questions.length > 0) {
5509
7017
  pendingAskUser = {
5510
- questions,
5511
- source: "act",
5512
- askedAt: Date.now()
7018
+ ...buildPendingAskUserPrompt("act", questions)
5513
7019
  };
5514
7020
  plannerHistory = sanitizePlannerHistoryForPersist([
5515
7021
  ...plannerHistory,
@@ -5519,16 +7025,17 @@ async function handleUserMessage(text, options) {
5519
7025
  }
5520
7026
  ]);
5521
7027
  const qText = questions.map((question) => `- ${question.key}: ${questionToDisplayText(question)}`).join("\n");
5522
- const msg2 = postAssistantMessage(`I need a bit more info before continuing:
7028
+ postAssistantMessage(`I need a bit more info before continuing:
5523
7029
  ${qText}`);
5524
- history.push({ role: "assistant", content: msg2 });
5525
7030
  postStatus("Need more input to continue", void 0, "verify");
5526
7031
  postStateSnapshot();
5527
7032
  return {
5528
7033
  route: result.route,
5529
7034
  taskComplete: false,
5530
7035
  needsUserInput: true,
5531
- questions
7036
+ questions,
7037
+ terminalState: "waiting_input",
7038
+ continuationReason: "awaiting_user"
5532
7039
  };
5533
7040
  }
5534
7041
  pendingAskUser = void 0;
@@ -5537,27 +7044,28 @@ ${qText}`);
5537
7044
  if (structuredError?.error.requires_api_key) {
5538
7045
  postAuthRequired(structuredError.error);
5539
7046
  }
5540
- const msg = structuredError ? postAssistantMessage({
7047
+ structuredError ? postAssistantMessage({
5541
7048
  text: `${structuredError.error.code}: ${structuredError.error.message}`,
5542
7049
  blocks: [
5543
7050
  {
5544
7051
  type: "json",
5545
7052
  label: "Error details",
5546
- data: cloneUnknown(structuredError)
7053
+ data: normalizeRuntimeToolOutput(structuredError)
5547
7054
  }
5548
7055
  ]
5549
7056
  }) : postAssistantMessage(buildAssistantPayloadFromToolOutput(output, {
5550
- label: "Tool output",
5551
- fallbackText: "Done."
7057
+ label: "Tool output"
5552
7058
  }));
5553
- history.push({ role: "assistant", content: msg });
5554
7059
  postStatus("Execution completed", structuredError?.error.message, "complete");
5555
7060
  postStateSnapshot();
5556
7061
  return {
5557
7062
  route: result.route,
5558
7063
  taskComplete: outcome.taskComplete,
5559
7064
  needsUserInput: outcome.needsUserInput,
5560
- questions: normalizePlannerQuestions(outcome.questions)
7065
+ questions: normalizePlannerQuestions(outcome.questions),
7066
+ terminalState: outcome.terminalState,
7067
+ continuationReason: outcome.continuationReason,
7068
+ contextResetRecommended: outcome.contextResetRecommended
5561
7069
  };
5562
7070
  }
5563
7071
  if (result.plannerResponse) {
@@ -5575,23 +7083,19 @@ ${qText}`);
5575
7083
  const plannerQuestions = responseQuestions.length ? responseQuestions : fallbackQuestions;
5576
7084
  if (plannerQuestions.length) {
5577
7085
  const questions = plannerQuestions;
5578
- pendingAskUser = questions.length ? {
5579
- questions,
5580
- source: "planner",
5581
- askedAt: Date.now()
5582
- } : void 0;
7086
+ pendingAskUser = questions.length ? buildPendingAskUserPrompt("planner", questions) : void 0;
5583
7087
  const qText = questions.map((question) => `- ${question.key}: ${questionToDisplayText(question)}`).join("\n");
5584
- const msg2 = `I need a bit more info:
5585
- ${qText}`;
5586
- postAssistantMessage(msg2);
5587
- history.push({ role: "assistant", content: msg2 });
7088
+ postAssistantMessage(`I need a bit more info:
7089
+ ${qText}`);
5588
7090
  postStatus("Planner needs user input", void 0, "verify");
5589
7091
  postStateSnapshot();
5590
7092
  return {
5591
7093
  route: result.route,
5592
7094
  taskComplete: false,
5593
7095
  needsUserInput: true,
5594
- questions
7096
+ questions,
7097
+ terminalState: "waiting_input",
7098
+ continuationReason: "awaiting_user"
5595
7099
  };
5596
7100
  }
5597
7101
  pendingAskUser = void 0;
@@ -5601,37 +7105,44 @@ ${qText}`;
5601
7105
  }
5602
7106
  const toolBlocks = buildPlannerToolResultBlocks(toolResults);
5603
7107
  const responseError = response.error || response.errorDetails ? toStructuredErrorPayload(response.errorDetails || { message: response.error }, "Planner failed") : void 0;
7108
+ const plannerContinuation = inferPlannerContinuation(toolResults);
5604
7109
  if (responseError?.error.requires_api_key) {
5605
7110
  postAuthRequired(responseError.error);
5606
7111
  }
5607
- const msg = responseError ? postAssistantMessage({
7112
+ responseError ? postAssistantMessage({
5608
7113
  text: `${responseError.error.code}: ${responseError.error.message}`,
5609
7114
  blocks: [
5610
7115
  {
5611
7116
  type: "json",
5612
7117
  label: "Planner error",
5613
- data: cloneUnknown(responseError)
7118
+ data: normalizeRuntimeToolOutput(responseError)
5614
7119
  },
5615
7120
  ...toolBlocks || []
5616
7121
  ]
5617
7122
  }) : postAssistantMessage({
5618
- text: String(response.overallThought || summarizePlannerToolResults(toolResults) || "Done."),
7123
+ text: String(response.overallThought || summarizePlannerToolResults(toolResults) || ""),
5619
7124
  blocks: toolBlocks
5620
7125
  });
5621
- history.push({ role: "assistant", content: msg });
5622
7126
  postStatus("Planner execution completed", response.overallThought, "complete");
5623
7127
  postStateSnapshot();
7128
+ const plannerComplete = !!response.taskComplete && !responseError && !plannerContinuation.needsUserInput && !plannerContinuation.navigationPending;
5624
7129
  return {
5625
7130
  route: result.route,
5626
- taskComplete: !!response.taskComplete && !responseError,
5627
- needsUserInput: false
7131
+ taskComplete: plannerComplete,
7132
+ needsUserInput: plannerContinuation.needsUserInput,
7133
+ terminalState: responseError ? "failed" : plannerContinuation.needsUserInput ? "waiting_input" : plannerContinuation.navigationPending ? "in_progress" : "completed",
7134
+ continuationReason: responseError ? void 0 : plannerContinuation.needsUserInput ? "awaiting_user" : plannerContinuation.navigationPending ? plannerContinuation.sameTabNavigationPending ? "same_tab_navigation_handoff" : "loop_continue" : void 0,
7135
+ contextResetRecommended: !responseError && plannerComplete
5628
7136
  };
5629
7137
  }
5630
- const doneMsg = postAssistantMessage("Done.");
5631
- history.push({ role: "assistant", content: doneMsg });
5632
7138
  postStatus("Completed", void 0, "complete");
5633
7139
  postStateSnapshot();
5634
- return { route: result.route, taskComplete: true };
7140
+ return {
7141
+ route: result.route,
7142
+ taskComplete: true,
7143
+ terminalState: "completed",
7144
+ contextResetRecommended: true
7145
+ };
5635
7146
  }
5636
7147
  async function runUserMessage(text, meta) {
5637
7148
  const runId = meta?.runId || crypto.randomUUID();
@@ -5642,20 +7153,27 @@ async function runUserMessage(text, meta) {
5642
7153
  self.postMessage({
5643
7154
  type: "run_completed",
5644
7155
  runId,
7156
+ taskBoundaryId: terminal.taskBoundaryId,
5645
7157
  ok: true,
5646
7158
  route: cachedOutcome.route,
5647
7159
  taskComplete: cachedOutcome.taskComplete,
5648
7160
  needsUserInput: cachedOutcome.needsUserInput,
5649
- questions: cachedOutcome.questions
7161
+ questions: cachedOutcome.questions,
7162
+ terminalState: cachedOutcome.terminalState,
7163
+ continuationReason: cachedOutcome.continuationReason,
7164
+ contextResetRecommended: cachedOutcome.contextResetRecommended
5650
7165
  });
5651
7166
  } else {
5652
7167
  self.postMessage({
5653
7168
  type: "run_completed",
5654
7169
  runId,
7170
+ taskBoundaryId: terminal.taskBoundaryId,
5655
7171
  ok: false,
5656
7172
  error: terminal.error,
5657
7173
  taskComplete: false,
5658
- needsUserInput: false
7174
+ needsUserInput: false,
7175
+ terminalState: "failed",
7176
+ contextResetRecommended: terminal.contextResetRecommended
5659
7177
  });
5660
7178
  }
5661
7179
  return;
@@ -5664,32 +7182,58 @@ async function runUserMessage(text, meta) {
5664
7182
  return;
5665
7183
  }
5666
7184
  const resume = !!meta?.resume;
7185
+ const preserveHistory = !!meta?.preserveHistory;
7186
+ const runTaskBoundaryId = taskBoundaryId;
5667
7187
  lastStatusKey = "";
5668
7188
  seenStatusKeys = /* @__PURE__ */ new Set();
7189
+ lastRuntimeTabsDiagnosticsKey = "";
5669
7190
  cancelledRunIds.delete(runId);
5670
7191
  activeAbortController = new AbortController();
5671
- activeRun = { runId, text, startedAt: Date.now(), resume };
5672
- self.postMessage({ type: "run_started", runId, text, resume });
7192
+ activeRun = { runId, text, startedAt: Date.now(), resume, preserveHistory };
7193
+ self.postMessage({ type: "run_started", runId, text, resume, taskBoundaryId: runTaskBoundaryId });
7194
+ if (shouldClearHistoryForRun({ resume, preserveHistory })) {
7195
+ history.length = 0;
7196
+ }
5673
7197
  postStateSnapshot();
5674
7198
  try {
5675
7199
  const outcome = normalizeRunOutcome(await handleUserMessage(text, {
5676
7200
  resume,
7201
+ preserveHistory,
7202
+ followupChatLog: normalizeFollowupChatLog(meta?.followupChatLog),
5677
7203
  routing: meta?.routing,
5678
7204
  askUserAnswers: meta?.askUserAnswers
5679
7205
  }));
5680
- rememberTerminalRun(runId, { ok: true, outcome });
5681
- self.postMessage({
5682
- type: "run_completed",
5683
- runId,
5684
- ok: true,
5685
- route: outcome.route,
5686
- taskComplete: outcome.taskComplete,
5687
- needsUserInput: outcome.needsUserInput,
5688
- questions: outcome.questions
5689
- });
5690
- if (outcome.taskComplete && !outcome.needsUserInput) {
5691
- clearTaskScopedContextAfterCompletion();
5692
- postStateSnapshot();
7206
+ const isTerminalOutcome = outcome.terminalState === "completed" || outcome.terminalState === "failed";
7207
+ if (isTerminalOutcome) {
7208
+ rememberTerminalRun(runId, { ok: true, outcome, taskBoundaryId: runTaskBoundaryId });
7209
+ self.postMessage({
7210
+ type: "run_completed",
7211
+ runId,
7212
+ taskBoundaryId: runTaskBoundaryId,
7213
+ ok: true,
7214
+ route: outcome.route,
7215
+ taskComplete: outcome.taskComplete,
7216
+ needsUserInput: outcome.needsUserInput,
7217
+ questions: outcome.questions,
7218
+ terminalState: outcome.terminalState,
7219
+ continuationReason: outcome.continuationReason,
7220
+ contextResetRecommended: outcome.contextResetRecommended
7221
+ });
7222
+ } else {
7223
+ terminalRuns.delete(runId);
7224
+ self.postMessage({
7225
+ type: "run_state_transition",
7226
+ runId,
7227
+ taskBoundaryId: runTaskBoundaryId,
7228
+ ok: true,
7229
+ route: outcome.route,
7230
+ taskComplete: outcome.taskComplete,
7231
+ needsUserInput: outcome.needsUserInput,
7232
+ questions: outcome.questions,
7233
+ terminalState: outcome.terminalState,
7234
+ continuationReason: outcome.continuationReason,
7235
+ contextResetRecommended: outcome.contextResetRecommended
7236
+ });
5693
7237
  }
5694
7238
  } catch (error) {
5695
7239
  if (error?.name === "AbortError") {
@@ -5697,30 +7241,42 @@ async function runUserMessage(text, meta) {
5697
7241
  ok: false,
5698
7242
  error: "Run cancelled",
5699
7243
  taskComplete: false,
5700
- needsUserInput: false
7244
+ needsUserInput: false,
7245
+ terminalState: "failed",
7246
+ contextResetRecommended: false,
7247
+ taskBoundaryId: runTaskBoundaryId
5701
7248
  });
5702
7249
  self.postMessage({
5703
7250
  type: "run_completed",
5704
7251
  runId,
7252
+ taskBoundaryId: runTaskBoundaryId,
5705
7253
  ok: false,
5706
7254
  error: "Run cancelled",
5707
7255
  taskComplete: false,
5708
- needsUserInput: false
7256
+ needsUserInput: false,
7257
+ terminalState: "failed",
7258
+ contextResetRecommended: false
5709
7259
  });
5710
7260
  } else {
5711
7261
  rememberTerminalRun(runId, {
5712
7262
  ok: false,
5713
7263
  error: error?.message || String(error),
5714
7264
  taskComplete: false,
5715
- needsUserInput: false
7265
+ needsUserInput: false,
7266
+ terminalState: "failed",
7267
+ contextResetRecommended: true,
7268
+ taskBoundaryId: runTaskBoundaryId
5716
7269
  });
5717
7270
  self.postMessage({
5718
7271
  type: "run_completed",
5719
7272
  runId,
7273
+ taskBoundaryId: runTaskBoundaryId,
5720
7274
  ok: false,
5721
7275
  error: error?.message || String(error),
5722
7276
  taskComplete: false,
5723
- needsUserInput: false
7277
+ needsUserInput: false,
7278
+ terminalState: "failed",
7279
+ contextResetRecommended: true
5724
7280
  });
5725
7281
  throw error;
5726
7282
  }
@@ -5735,9 +7291,14 @@ self.onmessage = async (ev) => {
5735
7291
  try {
5736
7292
  if (data.type === "init") {
5737
7293
  config = data.config;
5738
- trajectoryId = config.sessionId || crypto.randomUUID();
7294
+ workerSessionId = typeof config.sessionId === "string" && config.sessionId.trim() ? config.sessionId.trim() : workerSessionId || crypto.randomUUID();
7295
+ taskBoundaryId = typeof config.taskBoundaryId === "string" && config.taskBoundaryId.trim() ? config.taskBoundaryId.trim() : taskBoundaryId;
7296
+ applyScopedTabConfig(config);
7297
+ if (config?.timing?.actionTimeoutMs) {
7298
+ RPC_TIMEOUT_MS = Math.max(5e3, Math.min(12e4, Number(config.timing.actionTimeoutMs)));
7299
+ }
5739
7300
  if (!tabularStore) {
5740
- tabularStore = new TabularStore(`rover-${trajectoryId}`);
7301
+ tabularStore = new TabularStore(`rover-${taskTrajectoryId}`);
5741
7302
  }
5742
7303
  if (data.port) {
5743
7304
  bridgeRpc = createRpcClient(data.port);
@@ -5770,15 +7331,16 @@ self.onmessage = async (ev) => {
5770
7331
  ui: mergeWorkerUi(config.ui, partial.ui),
5771
7332
  tools: mergeWorkerTools(config.tools, partial.tools)
5772
7333
  };
5773
- if (typeof partial.sessionId === "string" && partial.sessionId.trim() && partial.sessionId.trim() !== trajectoryId) {
5774
- trajectoryId = partial.sessionId.trim();
5775
- plannerHistory = [];
5776
- agentPrevSteps = [];
5777
- pendingAskUser = void 0;
5778
- terminalRuns.clear();
5779
- cancelledRunIds.clear();
5780
- tabularStore = new TabularStore(`rover-${trajectoryId}`);
7334
+ if (typeof partial.sessionId === "string" && partial.sessionId.trim()) {
7335
+ workerSessionId = partial.sessionId.trim();
7336
+ }
7337
+ if (partial?.timing?.actionTimeoutMs) {
7338
+ RPC_TIMEOUT_MS = Math.max(5e3, Math.min(12e4, Number(partial.timing.actionTimeoutMs)));
5781
7339
  }
7340
+ if (typeof partial.taskBoundaryId === "string" && partial.taskBoundaryId.trim()) {
7341
+ taskBoundaryId = partial.taskBoundaryId.trim();
7342
+ }
7343
+ applyScopedTabConfig(partial);
5782
7344
  const tools = partial.tools;
5783
7345
  if (Array.isArray(tools)) {
5784
7346
  for (const def of tools)
@@ -5800,15 +7362,15 @@ self.onmessage = async (ev) => {
5800
7362
  if (!config)
5801
7363
  throw new Error("Worker not initialized");
5802
7364
  const nextTaskId = typeof data.taskId === "string" && data.taskId.trim() ? data.taskId.trim() : crypto.randomUUID();
7365
+ const nextTaskBoundaryId = typeof data.taskBoundaryId === "string" && data.taskBoundaryId.trim() ? data.taskBoundaryId.trim() : crypto.randomUUID();
5803
7366
  activeAbortController?.abort();
5804
- history.length = 0;
5805
- plannerHistory = [];
5806
- agentPrevSteps = [];
5807
- pendingAskUser = void 0;
7367
+ clearTaskScopedContextAfterBoundary("new_task");
5808
7368
  terminalRuns.clear();
5809
7369
  cancelledRunIds.clear();
5810
- trajectoryId = nextTaskId;
5811
- tabularStore = new TabularStore(`rover-${trajectoryId}`);
7370
+ taskTrajectoryId = nextTaskId;
7371
+ taskBoundaryId = nextTaskBoundaryId;
7372
+ applyScopedTabConfig(data);
7373
+ tabularStore = new TabularStore(`rover-${taskTrajectoryId}`);
5812
7374
  activeRun = null;
5813
7375
  activeAbortController = null;
5814
7376
  postStateSnapshot();
@@ -5820,14 +7382,17 @@ self.onmessage = async (ev) => {
5820
7382
  rememberCancelledRun(data.runId);
5821
7383
  activeAbortController?.abort();
5822
7384
  }
5823
- pendingAskUser = void 0;
7385
+ clearTaskScopedContextAfterBoundary("cancel");
5824
7386
  postStateSnapshot();
5825
7387
  return;
5826
7388
  }
5827
7389
  if (data.type === "run") {
7390
+ applyScopedTabConfig(data);
5828
7391
  await runUserMessage(String(data.text || ""), {
5829
7392
  runId: data.runId,
5830
7393
  resume: !!data.resume,
7394
+ preserveHistory: !!data.preserveHistory,
7395
+ followupChatLog: normalizeFollowupChatLog(data.followupChatLog),
5831
7396
  routing: data.routing,
5832
7397
  askUserAnswers: data.askUserAnswers
5833
7398
  });
@@ -5837,6 +7402,8 @@ self.onmessage = async (ev) => {
5837
7402
  await runUserMessage(String(data.text || ""), {
5838
7403
  runId: data.runId,
5839
7404
  resume: !!data.resume,
7405
+ preserveHistory: !!data.preserveHistory,
7406
+ followupChatLog: normalizeFollowupChatLog(data.followupChatLog),
5840
7407
  askUserAnswers: data.askUserAnswers
5841
7408
  });
5842
7409
  return;