bunnyquery 1.8.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bunnyquery.js +455 -43
- package/dist/engine.cjs +489 -33
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +232 -3
- package/dist/engine.d.ts +232 -3
- package/dist/engine.mjs +462 -34
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/ai_agent.ts +80 -0
- package/src/engine/budget.ts +113 -7
- package/src/engine/download_encoding.ts +281 -0
- package/src/engine/errors.ts +34 -0
- package/src/engine/history.ts +48 -18
- package/src/engine/index.ts +11 -0
- package/src/engine/requests.ts +15 -3
- package/src/engine/session.ts +227 -6
package/bunnyquery.js
CHANGED
|
@@ -433,6 +433,19 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
433
433
|
}
|
|
434
434
|
|
|
435
435
|
// src/engine/errors.ts
|
|
436
|
+
var STATUS_MESSAGE = {
|
|
437
|
+
"408": "The AI provider timed out before it started.",
|
|
438
|
+
"409": "The AI provider rejected the request as conflicting.",
|
|
439
|
+
"413": "The request was too large for the AI provider.",
|
|
440
|
+
"429": "The AI provider is rate limiting requests right now.",
|
|
441
|
+
"500": "The AI provider hit an internal error.",
|
|
442
|
+
"502": "The AI provider is temporarily unreachable.",
|
|
443
|
+
"503": "The AI provider is temporarily unavailable.",
|
|
444
|
+
"504": "The AI provider timed out."
|
|
445
|
+
};
|
|
446
|
+
function isTransientStatus(status) {
|
|
447
|
+
return status === 408 || status === 425 || status === 429 || status >= 500;
|
|
448
|
+
}
|
|
436
449
|
function getErrorMessage(input) {
|
|
437
450
|
if (!input) return "Something went wrong.";
|
|
438
451
|
if (typeof input === "string") return input;
|
|
@@ -440,6 +453,11 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
440
453
|
if (input.body && input.body.error && input.body.error.message) return input.body.error.message;
|
|
441
454
|
if (input.body && typeof input.body.message === "string") return input.body.message;
|
|
442
455
|
if (input.message) return input.message;
|
|
456
|
+
var status = typeof input.status_code === "number" ? input.status_code : typeof input.status === "number" ? input.status : 0;
|
|
457
|
+
if (status) {
|
|
458
|
+
var text = STATUS_MESSAGE[String(status)] || (status >= 500 ? "The AI provider returned a server error." : "The AI provider rejected the request.");
|
|
459
|
+
return text + " (error " + status + ")" + (isTransientStatus(status) ? " This is usually temporary, please try again." : "");
|
|
460
|
+
}
|
|
443
461
|
return "Something went wrong.";
|
|
444
462
|
}
|
|
445
463
|
function isErrorResponseBody(response) {
|
|
@@ -751,24 +769,63 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
751
769
|
// src/engine/budget.ts
|
|
752
770
|
var CONTEXT_WINDOW_DEFAULT = { claude: 2e5, openai: 128e3 };
|
|
753
771
|
var CONTEXT_WINDOW_BY_MODEL = {
|
|
754
|
-
|
|
772
|
+
// exact ids
|
|
773
|
+
"claude-opus-5": 1e6,
|
|
774
|
+
"claude-opus-4-8": 1e6,
|
|
775
|
+
"claude-opus-4-7": 1e6,
|
|
776
|
+
"claude-sonnet-5": 1e6,
|
|
777
|
+
"claude-sonnet-4-6": 1e6,
|
|
755
778
|
"claude-sonnet-4": 2e5,
|
|
756
|
-
"
|
|
779
|
+
"claude-haiku-4-5": 2e5,
|
|
780
|
+
"gpt-5.4": 128e3,
|
|
781
|
+
"gpt-5.6-luna": 128e3,
|
|
782
|
+
// family keys
|
|
783
|
+
"claude-opus": 1e6,
|
|
784
|
+
"claude-sonnet": 1e6,
|
|
785
|
+
"claude-haiku": 2e5,
|
|
786
|
+
"gpt-5.6": 128e3,
|
|
787
|
+
"gpt-5": 128e3
|
|
757
788
|
};
|
|
789
|
+
var apiReportedContextWindows = {};
|
|
790
|
+
var projectContextWindows = {};
|
|
791
|
+
function setProjectContextWindow(serviceId, tokens) {
|
|
792
|
+
var key = (serviceId || "").trim();
|
|
793
|
+
if (!key) return;
|
|
794
|
+
var n = Number(tokens);
|
|
795
|
+
if (Number.isFinite(n) && n > 0) projectContextWindows[key] = Math.floor(n);
|
|
796
|
+
else delete projectContextWindows[key];
|
|
797
|
+
}
|
|
798
|
+
function getProjectContextWindow(serviceId) {
|
|
799
|
+
var key = (serviceId || "").trim();
|
|
800
|
+
return key && projectContextWindows[key] ? projectContextWindows[key] : null;
|
|
801
|
+
}
|
|
758
802
|
var OUTPUT_TOKEN_RESERVE = 22e3;
|
|
759
803
|
var TOOL_AND_RESPONSE_BUFFER = 4e3;
|
|
760
804
|
var MIN_INPUT_TOKEN_BUDGET = 8e3;
|
|
761
805
|
var CLAUDE_PER_REQUEST_INPUT_CAP = 28e3;
|
|
806
|
+
var MAX_HISTORY_MESSAGES = 20;
|
|
762
807
|
var HISTORY_TOKEN_BUDGET = 8e3;
|
|
808
|
+
var CLAUDE_INPUT_CAP_RATIO = 0.16;
|
|
809
|
+
var HISTORY_BUDGET_RATIO = 0.08;
|
|
763
810
|
function estimateTextTokens(text) {
|
|
764
811
|
return Math.ceil((text || "").length / 3);
|
|
765
812
|
}
|
|
766
813
|
function estimateMessageTokens(msg) {
|
|
767
814
|
return estimateTextTokens(msg.content) + estimateTextTokens(msg.role) + 6;
|
|
768
815
|
}
|
|
769
|
-
function getContextWindow(platform, model) {
|
|
816
|
+
function getContextWindow(platform, model, serviceId) {
|
|
817
|
+
var override = serviceId ? getProjectContextWindow(serviceId) : null;
|
|
818
|
+
if (override) return override;
|
|
770
819
|
var normalized = (model || "").trim().toLowerCase();
|
|
771
|
-
if (normalized
|
|
820
|
+
if (normalized) {
|
|
821
|
+
if (apiReportedContextWindows[normalized]) return apiReportedContextWindows[normalized];
|
|
822
|
+
if (CONTEXT_WINDOW_BY_MODEL[normalized]) return CONTEXT_WINDOW_BY_MODEL[normalized];
|
|
823
|
+
var parts = normalized.split("-");
|
|
824
|
+
for (var end = parts.length - 1; end > 0; end--) {
|
|
825
|
+
var family = parts.slice(0, end).join("-");
|
|
826
|
+
if (CONTEXT_WINDOW_BY_MODEL[family]) return CONTEXT_WINDOW_BY_MODEL[family];
|
|
827
|
+
}
|
|
828
|
+
}
|
|
772
829
|
return CONTEXT_WINDOW_DEFAULT[platform];
|
|
773
830
|
}
|
|
774
831
|
function stripFileBlocksFromHistory(content) {
|
|
@@ -776,15 +833,19 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
776
833
|
return content.replace(/```([^\n`]+?\.[^\s.`]+)\n[\s\S]*?```/g, "[file previously attached: $1]");
|
|
777
834
|
}
|
|
778
835
|
function buildBoundedChatMessages(options) {
|
|
779
|
-
var contextWindow = getContextWindow(options.platform, options.model);
|
|
836
|
+
var contextWindow = getContextWindow(options.platform, options.model, options.serviceId);
|
|
780
837
|
var contextBasedBudget = Math.max(
|
|
781
838
|
MIN_INPUT_TOKEN_BUDGET,
|
|
782
839
|
contextWindow - OUTPUT_TOKEN_RESERVE - TOOL_AND_RESPONSE_BUFFER
|
|
783
840
|
);
|
|
784
|
-
var
|
|
841
|
+
var scaled = !!(options.serviceId && getProjectContextWindow(options.serviceId));
|
|
842
|
+
var claudeInputCap = scaled ? Math.max(CLAUDE_PER_REQUEST_INPUT_CAP, Math.round(contextBasedBudget * CLAUDE_INPUT_CAP_RATIO)) : CLAUDE_PER_REQUEST_INPUT_CAP;
|
|
843
|
+
var availableInputBudget = options.platform === "claude" ? Math.min(contextBasedBudget, claudeInputCap) : contextBasedBudget;
|
|
785
844
|
var systemCost = estimateTextTokens(options.systemPrompt) + 12;
|
|
786
|
-
var
|
|
787
|
-
var
|
|
845
|
+
var historyAllowance = scaled ? Math.max(HISTORY_TOKEN_BUDGET, Math.round(contextBasedBudget * HISTORY_BUDGET_RATIO)) : HISTORY_TOKEN_BUDGET;
|
|
846
|
+
var budgetForHistory = Math.max(1e3, Math.min(historyAllowance, availableInputBudget - systemCost));
|
|
847
|
+
var maxHistoryMessages = scaled ? Math.max(MAX_HISTORY_MESSAGES, Math.round(MAX_HISTORY_MESSAGES * (budgetForHistory / HISTORY_TOKEN_BUDGET))) : MAX_HISTORY_MESSAGES;
|
|
848
|
+
var windowed = options.history.slice(-maxHistoryMessages);
|
|
788
849
|
var latestIndex = windowed.length - 1;
|
|
789
850
|
var trimmed = windowed.map(function(m, i2) {
|
|
790
851
|
if (i2 === latestIndex) return m;
|
|
@@ -809,6 +870,163 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
809
870
|
};
|
|
810
871
|
}
|
|
811
872
|
|
|
873
|
+
// src/engine/download_encoding.ts
|
|
874
|
+
var BOM = "\uFEFF";
|
|
875
|
+
var BOM_EXTS = /* @__PURE__ */ new Set(["csv", "tsv", "tab", "txt", "text", "log"]);
|
|
876
|
+
var HTML_EXTS = /* @__PURE__ */ new Set(["html", "htm", "xhtml"]);
|
|
877
|
+
var XML_EXTS = /* @__PURE__ */ new Set(["xml", "svg", "rss", "atom", "xsl", "xslt", "plist", "kml"]);
|
|
878
|
+
var RTF_EXTS = /* @__PURE__ */ new Set(["rtf"]);
|
|
879
|
+
var EXT_CONTENT_TYPES = {
|
|
880
|
+
csv: "text/csv; charset=utf-8",
|
|
881
|
+
tsv: "text/tab-separated-values; charset=utf-8",
|
|
882
|
+
tab: "text/tab-separated-values; charset=utf-8",
|
|
883
|
+
txt: "text/plain; charset=utf-8",
|
|
884
|
+
text: "text/plain; charset=utf-8",
|
|
885
|
+
log: "text/plain; charset=utf-8",
|
|
886
|
+
md: "text/markdown; charset=utf-8",
|
|
887
|
+
markdown: "text/markdown; charset=utf-8",
|
|
888
|
+
json: "application/json; charset=utf-8",
|
|
889
|
+
jsonl: "application/x-ndjson; charset=utf-8",
|
|
890
|
+
ndjson: "application/x-ndjson; charset=utf-8",
|
|
891
|
+
geojson: "application/geo+json; charset=utf-8",
|
|
892
|
+
yaml: "text/yaml; charset=utf-8",
|
|
893
|
+
yml: "text/yaml; charset=utf-8",
|
|
894
|
+
toml: "text/plain; charset=utf-8",
|
|
895
|
+
ini: "text/plain; charset=utf-8",
|
|
896
|
+
sql: "text/plain; charset=utf-8",
|
|
897
|
+
html: "text/html; charset=utf-8",
|
|
898
|
+
htm: "text/html; charset=utf-8",
|
|
899
|
+
xhtml: "application/xhtml+xml; charset=utf-8",
|
|
900
|
+
xml: "application/xml; charset=utf-8",
|
|
901
|
+
svg: "image/svg+xml; charset=utf-8",
|
|
902
|
+
css: "text/css; charset=utf-8",
|
|
903
|
+
js: "text/javascript; charset=utf-8",
|
|
904
|
+
ts: "text/plain; charset=utf-8",
|
|
905
|
+
py: "text/x-python; charset=utf-8",
|
|
906
|
+
sh: "text/x-shellscript; charset=utf-8",
|
|
907
|
+
srt: "application/x-subrip; charset=utf-8",
|
|
908
|
+
vtt: "text/vtt; charset=utf-8",
|
|
909
|
+
ics: "text/calendar; charset=utf-8",
|
|
910
|
+
vcf: "text/vcard; charset=utf-8",
|
|
911
|
+
// RTF is 7-bit ASCII by specification, so it takes no charset parameter.
|
|
912
|
+
rtf: "application/rtf",
|
|
913
|
+
// Binary types the model can only ever REFERENCE, never author in a fence, but
|
|
914
|
+
// which keep the type sensible if one ever shows up.
|
|
915
|
+
pdf: "application/pdf",
|
|
916
|
+
png: "image/png",
|
|
917
|
+
jpg: "image/jpeg",
|
|
918
|
+
jpeg: "image/jpeg",
|
|
919
|
+
gif: "image/gif",
|
|
920
|
+
webp: "image/webp"
|
|
921
|
+
};
|
|
922
|
+
function normalizeExt(ext) {
|
|
923
|
+
return String(ext || "").trim().replace(/^\./, "").toLowerCase();
|
|
924
|
+
}
|
|
925
|
+
function extOf(filename) {
|
|
926
|
+
const name = String(filename || "");
|
|
927
|
+
const dot = name.lastIndexOf(".");
|
|
928
|
+
return dot > 0 ? normalizeExt(name.slice(dot + 1)) : "";
|
|
929
|
+
}
|
|
930
|
+
function encodingClassForExt(ext) {
|
|
931
|
+
const e = normalizeExt(ext);
|
|
932
|
+
if (BOM_EXTS.has(e)) return "bom";
|
|
933
|
+
if (HTML_EXTS.has(e)) return "html";
|
|
934
|
+
if (XML_EXTS.has(e)) return "xml";
|
|
935
|
+
if (RTF_EXTS.has(e)) return "rtf";
|
|
936
|
+
return "none";
|
|
937
|
+
}
|
|
938
|
+
function contentTypeForExt(ext, fallback = "text/plain; charset=utf-8") {
|
|
939
|
+
return EXT_CONTENT_TYPES[normalizeExt(ext)] || fallback;
|
|
940
|
+
}
|
|
941
|
+
function hasBom(text) {
|
|
942
|
+
return typeof text === "string" && text.charCodeAt(0) === 65279;
|
|
943
|
+
}
|
|
944
|
+
var HTML_HEAD_WINDOW = 4096;
|
|
945
|
+
var META_CHARSET_RE = /<meta[^>]+charset\s*=\s*["']?\s*([a-z0-9_-]+)/i;
|
|
946
|
+
var META_HTTP_EQUIV_RE = /<meta[^>]+http-equiv\s*=\s*["']?content-type["']?[^>]*>/i;
|
|
947
|
+
function ensureHtmlCharset(text) {
|
|
948
|
+
const src = String(text == null ? "" : text);
|
|
949
|
+
const head = src.slice(0, HTML_HEAD_WINDOW);
|
|
950
|
+
const declared = META_CHARSET_RE.exec(head);
|
|
951
|
+
if (declared) {
|
|
952
|
+
if (declared[1].toLowerCase().replace(/[^a-z0-9]/g, "") === "utf8") return src;
|
|
953
|
+
const start = declared.index + declared[0].length - declared[1].length;
|
|
954
|
+
return src.slice(0, start) + "utf-8" + src.slice(start + declared[1].length);
|
|
955
|
+
}
|
|
956
|
+
const httpEquiv = META_HTTP_EQUIV_RE.exec(head);
|
|
957
|
+
if (httpEquiv) {
|
|
958
|
+
return src.slice(0, httpEquiv.index) + '<meta charset="utf-8">' + src.slice(httpEquiv.index + httpEquiv[0].length);
|
|
959
|
+
}
|
|
960
|
+
const tag = '<meta charset="utf-8">';
|
|
961
|
+
const headOpen = /<head[^>]*>/i.exec(head);
|
|
962
|
+
if (headOpen) {
|
|
963
|
+
const at = headOpen.index + headOpen[0].length;
|
|
964
|
+
return src.slice(0, at) + "\n" + tag + src.slice(at);
|
|
965
|
+
}
|
|
966
|
+
const htmlOpen = /<html[^>]*>/i.exec(head);
|
|
967
|
+
if (htmlOpen) {
|
|
968
|
+
const at = htmlOpen.index + htmlOpen[0].length;
|
|
969
|
+
return src.slice(0, at) + "\n<head>" + tag + "</head>" + src.slice(at);
|
|
970
|
+
}
|
|
971
|
+
const doctype = /<!doctype[^>]*>/i.exec(head);
|
|
972
|
+
if (doctype) {
|
|
973
|
+
const at = doctype.index + doctype[0].length;
|
|
974
|
+
return src.slice(0, at) + "\n" + tag + src.slice(at);
|
|
975
|
+
}
|
|
976
|
+
return tag + "\n" + src;
|
|
977
|
+
}
|
|
978
|
+
var XML_DECL_RE = /^\s*<\?xml\s[^?]*\?>/i;
|
|
979
|
+
function ensureXmlEncoding(text) {
|
|
980
|
+
const src = String(text == null ? "" : text);
|
|
981
|
+
const decl = XML_DECL_RE.exec(src);
|
|
982
|
+
if (!decl) return src;
|
|
983
|
+
const found = /encoding\s*=\s*["']([^"']*)["']/i.exec(decl[0]);
|
|
984
|
+
if (!found) return src;
|
|
985
|
+
if (found[1].toLowerCase().replace(/[^a-z0-9]/g, "") === "utf8") return src;
|
|
986
|
+
const fixedDecl = decl[0].slice(0, found.index) + found[0].replace(found[1], "UTF-8") + decl[0].slice(found.index + found[0].length);
|
|
987
|
+
return fixedDecl + src.slice(decl[0].length);
|
|
988
|
+
}
|
|
989
|
+
var RTF_SIGNATURE_RE = /^[\s]*\{\\rtf/i;
|
|
990
|
+
function looksLikeRtf(text) {
|
|
991
|
+
return RTF_SIGNATURE_RE.test(String(text == null ? "" : text));
|
|
992
|
+
}
|
|
993
|
+
function escapeRtfNonAscii(text) {
|
|
994
|
+
const src = String(text == null ? "" : text);
|
|
995
|
+
let out = "";
|
|
996
|
+
let plainFrom = 0;
|
|
997
|
+
for (let i = 0; i < src.length; i++) {
|
|
998
|
+
const code = src.charCodeAt(i);
|
|
999
|
+
if (code < 128) continue;
|
|
1000
|
+
out += src.slice(plainFrom, i);
|
|
1001
|
+
out += `\\u${code > 32767 ? code - 65536 : code}?`;
|
|
1002
|
+
plainFrom = i + 1;
|
|
1003
|
+
}
|
|
1004
|
+
return plainFrom === 0 ? src : out + src.slice(plainFrom);
|
|
1005
|
+
}
|
|
1006
|
+
function applyEncodingDeclaration(text, ext) {
|
|
1007
|
+
const src = String(text == null ? "" : text);
|
|
1008
|
+
switch (encodingClassForExt(ext)) {
|
|
1009
|
+
case "bom":
|
|
1010
|
+
return hasBom(src) ? src : BOM + src;
|
|
1011
|
+
case "html":
|
|
1012
|
+
return ensureHtmlCharset(src);
|
|
1013
|
+
case "xml":
|
|
1014
|
+
return ensureXmlEncoding(src);
|
|
1015
|
+
case "rtf":
|
|
1016
|
+
return looksLikeRtf(src) ? escapeRtfNonAscii(src) : hasBom(src) ? src : BOM + src;
|
|
1017
|
+
default:
|
|
1018
|
+
return src;
|
|
1019
|
+
}
|
|
1020
|
+
}
|
|
1021
|
+
function prepareDownloadText(filename, body) {
|
|
1022
|
+
const ext = extOf(filename);
|
|
1023
|
+
return {
|
|
1024
|
+
ext,
|
|
1025
|
+
text: applyEncodingDeclaration(body, ext),
|
|
1026
|
+
contentType: contentTypeForExt(ext)
|
|
1027
|
+
};
|
|
1028
|
+
}
|
|
1029
|
+
|
|
812
1030
|
// src/engine/time.ts
|
|
813
1031
|
function wallClockNow() {
|
|
814
1032
|
return Date.now();
|
|
@@ -829,6 +1047,31 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
829
1047
|
}
|
|
830
1048
|
}
|
|
831
1049
|
|
|
1050
|
+
// src/engine/ai_agent.ts
|
|
1051
|
+
function normalizePlatform(raw) {
|
|
1052
|
+
var p = (raw || "").trim().toLowerCase();
|
|
1053
|
+
return p === "claude" || p === "openai" ? p : null;
|
|
1054
|
+
}
|
|
1055
|
+
function parseAiAgentValue(value) {
|
|
1056
|
+
var raw = (value || "").trim();
|
|
1057
|
+
if (!raw || raw.toLowerCase() === "none") {
|
|
1058
|
+
return { platform: null, model: "", contextWindow: null, hasPlatform: false };
|
|
1059
|
+
}
|
|
1060
|
+
var firstHash = raw.indexOf("#");
|
|
1061
|
+
if (firstHash === -1) {
|
|
1062
|
+
var only = normalizePlatform(raw);
|
|
1063
|
+
return { platform: only, model: "", contextWindow: null, hasPlatform: !!only };
|
|
1064
|
+
}
|
|
1065
|
+
var platform = normalizePlatform(raw.slice(0, firstHash));
|
|
1066
|
+
var rest = raw.slice(firstHash + 1);
|
|
1067
|
+
var secondHash = rest.indexOf("#");
|
|
1068
|
+
var model = (secondHash === -1 ? rest : rest.slice(0, secondHash)).trim();
|
|
1069
|
+
var windowRaw = secondHash === -1 ? "" : rest.slice(secondHash + 1).trim();
|
|
1070
|
+
var parsedWindow = windowRaw ? Number(windowRaw) : NaN;
|
|
1071
|
+
var contextWindow = Number.isFinite(parsedWindow) && parsedWindow > 0 ? Math.floor(parsedWindow) : null;
|
|
1072
|
+
return { platform, model, contextWindow, hasPlatform: !!platform };
|
|
1073
|
+
}
|
|
1074
|
+
|
|
832
1075
|
// src/engine/requests.ts
|
|
833
1076
|
var ANTHROPIC_MESSAGES_API_URL = "https://api.anthropic.com/v1/messages";
|
|
834
1077
|
var ANTHROPIC_VERSION = "2023-06-01";
|
|
@@ -945,7 +1188,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
945
1188
|
return { ...m, content: blocks };
|
|
946
1189
|
});
|
|
947
1190
|
}
|
|
948
|
-
var POLL_INTERVAL =
|
|
1191
|
+
var POLL_INTERVAL = 3e3;
|
|
949
1192
|
async function callClaudeWithMcp({
|
|
950
1193
|
prompt,
|
|
951
1194
|
messages,
|
|
@@ -1310,7 +1553,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1310
1553
|
method: "POST"
|
|
1311
1554
|
},
|
|
1312
1555
|
{ service: params.service, owner: params.owner },
|
|
1313
|
-
params.queue ? { queue: params.queue } : {}
|
|
1556
|
+
params.queue ? { queue: params.queue } : {},
|
|
1557
|
+
params.status ? { status: params.status } : {}
|
|
1314
1558
|
);
|
|
1315
1559
|
return chatEngineConfig().clientSecretRequestHistory(
|
|
1316
1560
|
p,
|
|
@@ -1347,6 +1591,25 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1347
1591
|
}
|
|
1348
1592
|
return "";
|
|
1349
1593
|
}
|
|
1594
|
+
function isIndexingRequestText(userText) {
|
|
1595
|
+
if (typeof userText !== "string") return false;
|
|
1596
|
+
return userText.indexOf("A new file has just been uploaded") === 0 || userText.indexOf("CONTINUE indexing") === 0;
|
|
1597
|
+
}
|
|
1598
|
+
function parseIndexingRequestText(userText) {
|
|
1599
|
+
if (typeof userText !== "string" || !userText) return null;
|
|
1600
|
+
var nameMatch = userText.match(/^- name: (.+)$/m);
|
|
1601
|
+
if (!nameMatch) return null;
|
|
1602
|
+
var mimeMatch = userText.match(/^- mime type: (.+)$/m);
|
|
1603
|
+
var sizeMatch = userText.match(/^- size \(bytes\): (\d+)$/m);
|
|
1604
|
+
var pathMatch = userText.match(/^- storage path: (.+)$/m);
|
|
1605
|
+
return {
|
|
1606
|
+
name: nameMatch[1].trim(),
|
|
1607
|
+
path: pathMatch ? pathMatch[1].trim() : void 0,
|
|
1608
|
+
mime: mimeMatch ? mimeMatch[1].trim() : void 0,
|
|
1609
|
+
size: sizeMatch ? Number(sizeMatch[1]) : void 0,
|
|
1610
|
+
continued: userText.indexOf("CONTINUE indexing") === 0
|
|
1611
|
+
};
|
|
1612
|
+
}
|
|
1350
1613
|
function mapHistoryListToMessages(list, platform, opts) {
|
|
1351
1614
|
var mapped = [], runningItemIds = [];
|
|
1352
1615
|
var extractAssistantText = platform === "openai" ? extractOpenAIText : extractClaudeText;
|
|
@@ -1371,27 +1634,17 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1371
1634
|
var displayContent;
|
|
1372
1635
|
var indexFile = void 0;
|
|
1373
1636
|
if (item._isBgTask) {
|
|
1374
|
-
var
|
|
1375
|
-
if (
|
|
1376
|
-
var mimeMatch = userText.match(/^- mime type: (.+)$/m);
|
|
1377
|
-
var sizeMatch = userText.match(/^- size \(bytes\): (\d+)$/m);
|
|
1378
|
-
var pathMatch = userText.match(/^- storage path: (.+)$/m);
|
|
1379
|
-
var isContinuePass = userText.indexOf("CONTINUE indexing") === 0;
|
|
1637
|
+
var ref = parseIndexingRequestText(userText);
|
|
1638
|
+
if (ref) {
|
|
1380
1639
|
displayContent = opts.formatIndexingLabel(
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1640
|
+
ref.name,
|
|
1641
|
+
ref.mime || "",
|
|
1642
|
+
typeof ref.size === "number" ? ref.size : null,
|
|
1643
|
+
ref.path,
|
|
1385
1644
|
false,
|
|
1386
|
-
|
|
1645
|
+
ref.continued
|
|
1387
1646
|
);
|
|
1388
|
-
indexFile =
|
|
1389
|
-
name: nameMatch[1].trim(),
|
|
1390
|
-
path: pathMatch ? pathMatch[1].trim() : void 0,
|
|
1391
|
-
mime: mimeMatch ? mimeMatch[1].trim() : void 0,
|
|
1392
|
-
size: sizeMatch ? Number(sizeMatch[1]) : void 0,
|
|
1393
|
-
continued: isContinuePass
|
|
1394
|
-
};
|
|
1647
|
+
indexFile = ref;
|
|
1395
1648
|
} else {
|
|
1396
1649
|
displayContent = userText;
|
|
1397
1650
|
}
|
|
@@ -1520,6 +1773,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1520
1773
|
}
|
|
1521
1774
|
|
|
1522
1775
|
// src/engine/session.ts
|
|
1776
|
+
var WORKER_PASS_ADOPT_LIMIT = 20;
|
|
1777
|
+
var WORKER_PASS_ADOPT_ATTEMPTS = [0, 2e3, 6e3];
|
|
1523
1778
|
var _g = typeof globalThis !== "undefined" ? globalThis : {};
|
|
1524
1779
|
function nowMs() {
|
|
1525
1780
|
return _g.performance && typeof _g.performance.now === "function" ? _g.performance.now() : Date.now();
|
|
@@ -1539,6 +1794,35 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1539
1794
|
var ChatSession = class {
|
|
1540
1795
|
constructor(host) {
|
|
1541
1796
|
this.typewriterQueue = Promise.resolve();
|
|
1797
|
+
/**
|
|
1798
|
+
* Pick up indexing passes the WORKER minted, which no client ever dispatched.
|
|
1799
|
+
*
|
|
1800
|
+
* For a PDF (and for text/grid when windowed indexing is on) the worker writes
|
|
1801
|
+
* pass N+1's row itself, inside pass N's invocation, right after saving pass
|
|
1802
|
+
* N's result. That row reaches this client through nothing at all: it is not in
|
|
1803
|
+
* bgTaskQueue (the client never asked for it) and it is not in state.messages
|
|
1804
|
+
* (only a first-page history load maps it in, which happens on mount, project
|
|
1805
|
+
* switch or tab return). So between two worker passes every pass the client
|
|
1806
|
+
* knows about is settled, and the collapsed row renders "Indexed N passes"
|
|
1807
|
+
* with no spinner and no Stop, for a file that is still being read. A user who
|
|
1808
|
+
* believes that then asks questions against a half-indexed file — which is what
|
|
1809
|
+
* this exists to prevent.
|
|
1810
|
+
*
|
|
1811
|
+
* So when a background indexing pass settles, ask the bg queue what is still
|
|
1812
|
+
* unresolved on it and adopt anything unknown as an ordinary BgTaskEntry.
|
|
1813
|
+
* drainBgTaskQueue then treats it exactly like a pass this client dispatched:
|
|
1814
|
+
* same bubble, same `_indexFile` (so it joins the file's collapsed row), same
|
|
1815
|
+
* poll — and that poll settling runs this again, so the chain is followed to
|
|
1816
|
+
* its end. `status`-scoped so the reply carries the live items only, never a
|
|
1817
|
+
* page of finished ones with their bodies.
|
|
1818
|
+
*
|
|
1819
|
+
* Termination: the only trigger is a pass SETTLING, which happens once per
|
|
1820
|
+
* pass. An adopted item that is still running is not re-adopted (its id is
|
|
1821
|
+
* already polled), and when the queue holds nothing unknown the chain stops on
|
|
1822
|
+
* its own. Nothing here is periodic — a timer that re-reads history is the
|
|
1823
|
+
* shape that previously looped fetchHistoryPage after an already-DONE index.
|
|
1824
|
+
*/
|
|
1825
|
+
this._adoptingWorkerPasses = false;
|
|
1542
1826
|
this.host = host;
|
|
1543
1827
|
this.state = {
|
|
1544
1828
|
messages: [],
|
|
@@ -1828,7 +2112,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1828
2112
|
var chatQueue = useBgQueue ? userId + BG_INDEXING_QUEUE_SUFFIX : userId;
|
|
1829
2113
|
if (offChat) {
|
|
1830
2114
|
var offHistory = (this.aiChatHistoryCache[key] ? this.aiChatHistoryCache[key].messages : []).filter(function(m) {
|
|
1831
|
-
return !m.isPending && !m.isPendingQueued && !m.isPendingInProcess && !m.isPendingOlder && !m.isCancelled && !m.isBackgroundTask;
|
|
2115
|
+
return !m.isPending && !m.isPendingQueued && !m.isPendingInProcess && !m.isPendingOlder && !m.isCancelled && !m.isBackgroundTask && !m.isError;
|
|
1832
2116
|
});
|
|
1833
2117
|
var offBounded = buildBoundedChatMessages({
|
|
1834
2118
|
platform: aiPlatform,
|
|
@@ -1863,7 +2147,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1863
2147
|
}
|
|
1864
2148
|
if (isQueuedSend) {
|
|
1865
2149
|
var resolvedHistory = this.state.messages.filter(function(m) {
|
|
1866
|
-
return !m.isPending && !m.isPendingQueued && !m.isPendingInProcess && !m.isPendingOlder && !m.isCancelled && !m.isBackgroundTask;
|
|
2150
|
+
return !m.isPending && !m.isPendingQueued && !m.isPendingInProcess && !m.isPendingOlder && !m.isCancelled && !m.isBackgroundTask && !m.isError;
|
|
1867
2151
|
});
|
|
1868
2152
|
var boundedQ = buildBoundedChatMessages({
|
|
1869
2153
|
platform: aiPlatform,
|
|
@@ -1914,7 +2198,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1914
2198
|
this.state.sending = true;
|
|
1915
2199
|
this.host.scrollToBottom(true);
|
|
1916
2200
|
var historyForLlm = this.state.messages.filter(function(m) {
|
|
1917
|
-
return !m.isCancelled && !m.isBackgroundTask;
|
|
2201
|
+
return !m.isPending && !m.isPendingQueued && !m.isPendingInProcess && !m.isPendingOlder && !m.isCancelled && !m.isBackgroundTask && !m.isError;
|
|
1918
2202
|
});
|
|
1919
2203
|
if (llmComposed !== composed) {
|
|
1920
2204
|
for (var li = historyForLlm.length - 1; li >= 0; li--) {
|
|
@@ -2484,8 +2768,21 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2484
2768
|
}
|
|
2485
2769
|
// --- background-task resolution + drain -------------------------------
|
|
2486
2770
|
handleHistoryItemResolution(itemId, response, platform) {
|
|
2771
|
+
var indexRef = this._indexRefOfItem(itemId);
|
|
2487
2772
|
this.applyHistoryItemResolution(itemId, response, platform);
|
|
2488
2773
|
this.promoteNextBgQueuedToRunning();
|
|
2774
|
+
if (indexRef) this._followWorkerIndexingChain(indexRef.name, indexRef.mime);
|
|
2775
|
+
}
|
|
2776
|
+
/** The file an already-rendered background pass is about, off its request
|
|
2777
|
+
* bubble. Null for an ordinary turn, which is most of them. */
|
|
2778
|
+
_indexRefOfItem(itemId) {
|
|
2779
|
+
if (!itemId) return null;
|
|
2780
|
+
for (var i = 0; i < this.state.messages.length; i++) {
|
|
2781
|
+
var m = this.state.messages[i];
|
|
2782
|
+
if (m._serverItemId !== itemId || m.role !== "user" || !m.isBackgroundTask) continue;
|
|
2783
|
+
return m._indexFile || null;
|
|
2784
|
+
}
|
|
2785
|
+
return null;
|
|
2489
2786
|
}
|
|
2490
2787
|
applyHistoryItemResolution(itemId, response, platform) {
|
|
2491
2788
|
this.historyItemPolls.delete(itemId);
|
|
@@ -2619,6 +2916,116 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2619
2916
|
self.cancelQueuedMessage(t.msg, idx === -1 ? t.idx : idx);
|
|
2620
2917
|
});
|
|
2621
2918
|
}
|
|
2919
|
+
/**
|
|
2920
|
+
* True when the WORKER, not this client, drives the rest of this file's chain.
|
|
2921
|
+
* The mirror image of the early returns in maybeResumeIndexing: whatever that
|
|
2922
|
+
* refuses to continue is exactly what nothing client-side is tracking.
|
|
2923
|
+
*/
|
|
2924
|
+
_isWorkerDrivenIndexing(filename, mime) {
|
|
2925
|
+
if (!isPagedReadFile(filename, mime)) return false;
|
|
2926
|
+
if (isImageVisionFile(filename, mime)) return true;
|
|
2927
|
+
return windowedIndexingEnabled() && isWindowedReadFile(filename, mime);
|
|
2928
|
+
}
|
|
2929
|
+
_adoptWorkerIndexingPasses(attempt) {
|
|
2930
|
+
var self = this;
|
|
2931
|
+
if (this._adoptingWorkerPasses) return;
|
|
2932
|
+
var id = this.host.getIdentity();
|
|
2933
|
+
var platform = id.platform;
|
|
2934
|
+
if (!id.serviceId || platform !== "claude" && platform !== "openai") return;
|
|
2935
|
+
if (this.isPollingPaused() || !this.host.isViewMounted()) return;
|
|
2936
|
+
var svcId = id.serviceId, owner = id.owner;
|
|
2937
|
+
var queue = (id.userId || id.serviceId) + BG_INDEXING_QUEUE_SUFFIX;
|
|
2938
|
+
var ask = function(status) {
|
|
2939
|
+
return Promise.resolve(getChatHistory(
|
|
2940
|
+
{ service: svcId, owner, platform, queue, status },
|
|
2941
|
+
{ limit: WORKER_PASS_ADOPT_LIMIT }
|
|
2942
|
+
)).catch(function() {
|
|
2943
|
+
return null;
|
|
2944
|
+
});
|
|
2945
|
+
};
|
|
2946
|
+
this._adoptingWorkerPasses = true;
|
|
2947
|
+
Promise.all([ask("running"), ask("pending")]).then(function(results) {
|
|
2948
|
+
self._adoptingWorkerPasses = false;
|
|
2949
|
+
var now = self.host.getIdentity();
|
|
2950
|
+
if (now.serviceId !== svcId || now.platform !== platform) return;
|
|
2951
|
+
if (!self.host.isViewMounted()) return;
|
|
2952
|
+
var adoptedIds = [];
|
|
2953
|
+
for (var ri = 0; ri < results.length; ri++) {
|
|
2954
|
+
var list = results[ri] && Array.isArray(results[ri].list) ? results[ri].list : [];
|
|
2955
|
+
for (var i = 0; i < list.length; i++) {
|
|
2956
|
+
if (self._adoptWorkerIndexingItem(list[i], svcId, platform)) adoptedIds.push(list[i].id);
|
|
2957
|
+
}
|
|
2958
|
+
}
|
|
2959
|
+
if (adoptedIds.length) {
|
|
2960
|
+
self.drainBgTaskQueue();
|
|
2961
|
+
if (self._isTrackingAny(adoptedIds)) return;
|
|
2962
|
+
}
|
|
2963
|
+
if (attempt + 1 >= WORKER_PASS_ADOPT_ATTEMPTS.length) return;
|
|
2964
|
+
setTimeout(function() {
|
|
2965
|
+
var later = self.host.getIdentity();
|
|
2966
|
+
if (later.serviceId !== svcId || later.platform !== platform) return;
|
|
2967
|
+
if (self.isPollingPaused() || !self.host.isViewMounted()) return;
|
|
2968
|
+
self._adoptWorkerIndexingPasses(attempt + 1);
|
|
2969
|
+
}, WORKER_PASS_ADOPT_ATTEMPTS[attempt + 1]);
|
|
2970
|
+
}, function() {
|
|
2971
|
+
self._adoptingWorkerPasses = false;
|
|
2972
|
+
});
|
|
2973
|
+
}
|
|
2974
|
+
/** Any of these ids still queued or still polled, i.e. surviving work. */
|
|
2975
|
+
_isTrackingAny(ids) {
|
|
2976
|
+
for (var i = 0; i < ids.length; i++) {
|
|
2977
|
+
if (this.historyItemPolls.has(ids[i])) return true;
|
|
2978
|
+
for (var q = 0; q < this.bgTaskQueue.length; q++) {
|
|
2979
|
+
if (this.bgTaskQueue[q].id === ids[i]) return true;
|
|
2980
|
+
}
|
|
2981
|
+
}
|
|
2982
|
+
return false;
|
|
2983
|
+
}
|
|
2984
|
+
/** One live bg-queue item -> a BgTaskEntry, if it is an indexing pass this
|
|
2985
|
+
* client is not already tracking. Returns whether it was adopted. */
|
|
2986
|
+
_adoptWorkerIndexingItem(item, svcId, platform) {
|
|
2987
|
+
if (!item || typeof item.id !== "string" || !item.id) return false;
|
|
2988
|
+
if (item.status !== "pending" && item.status !== "running") return false;
|
|
2989
|
+
if (typeof item.poll !== "function") return false;
|
|
2990
|
+
if (this.historyItemPolls.has(item.id)) return false;
|
|
2991
|
+
for (var q = 0; q < this.bgTaskQueue.length; q++) {
|
|
2992
|
+
if (this.bgTaskQueue[q].id === item.id) return false;
|
|
2993
|
+
}
|
|
2994
|
+
for (var m = 0; m < this.state.messages.length; m++) {
|
|
2995
|
+
var msg = this.state.messages[m];
|
|
2996
|
+
if (msg._serverItemId !== item.id) continue;
|
|
2997
|
+
if (!(msg.isPending || msg.isPendingInProcess || msg.isPendingQueued)) return false;
|
|
2998
|
+
}
|
|
2999
|
+
var body = item.request_body;
|
|
3000
|
+
if (!body || typeof body !== "object") return false;
|
|
3001
|
+
if (platform === "claude" ? !Array.isArray(body.messages) : !Array.isArray(body.input)) return false;
|
|
3002
|
+
var userText = extractLastUserTextFromRequest(body);
|
|
3003
|
+
if (!isIndexingRequestText(userText)) return false;
|
|
3004
|
+
var ref = parseIndexingRequestText(userText);
|
|
3005
|
+
if (!ref || !ref.name) return false;
|
|
3006
|
+
if (!this._isWorkerDrivenIndexing(ref.name, ref.mime)) return false;
|
|
3007
|
+
this.bgTaskQueue.push({
|
|
3008
|
+
serviceId: svcId,
|
|
3009
|
+
platform,
|
|
3010
|
+
id: item.id,
|
|
3011
|
+
filename: ref.name,
|
|
3012
|
+
storagePath: ref.path,
|
|
3013
|
+
mime: ref.mime,
|
|
3014
|
+
size: ref.size,
|
|
3015
|
+
status: item.status === "running" ? "running" : "pending",
|
|
3016
|
+
poll: item.poll,
|
|
3017
|
+
// Drives the "(continuing)" label and marks this as a continuation for
|
|
3018
|
+
// _applyIndexCancellations, which must not read a CONTINUE pass as the
|
|
3019
|
+
// fresh first pass that lifts a stop.
|
|
3020
|
+
resumePass: ref.continued ? 1 : 0
|
|
3021
|
+
});
|
|
3022
|
+
return true;
|
|
3023
|
+
}
|
|
3024
|
+
/** Follow the chain on from a background indexing pass that just settled. */
|
|
3025
|
+
_followWorkerIndexingChain(filename, mime) {
|
|
3026
|
+
if (!this._isWorkerDrivenIndexing(filename, mime)) return;
|
|
3027
|
+
this._adoptWorkerIndexingPasses(0);
|
|
3028
|
+
}
|
|
2622
3029
|
/** Best-effort server-side cancel of a bg-queue item that has no bubble (so
|
|
2623
3030
|
* cancelQueuedMessage, which drives one, has nothing to act on). */
|
|
2624
3031
|
_cancelServerItem(serverId) {
|
|
@@ -2829,8 +3236,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2829
3236
|
var chatList = history && Array.isArray(history.list) ? history.list : [];
|
|
2830
3237
|
chatList.forEach(function(item) {
|
|
2831
3238
|
if (isBgIndexingQueue(item.queue_name)) {
|
|
2832
|
-
|
|
2833
|
-
if (typeof userText === "string" && (userText.indexOf("A new file has just been uploaded") === 0 || userText.indexOf("CONTINUE indexing") === 0)) item._isBgTask = true;
|
|
3239
|
+
if (isIndexingRequestText(extractLastUserTextFromRequest(item.request_body))) item._isBgTask = true;
|
|
2834
3240
|
else item._isOnBgQueue = true;
|
|
2835
3241
|
}
|
|
2836
3242
|
});
|
|
@@ -3401,7 +3807,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
3401
3807
|
(function() {
|
|
3402
3808
|
var MCP_PROD = "https://mcp.broadwayinc.computer";
|
|
3403
3809
|
var MCP_DEV = "https://mcp-dev.broadwayinc.computer";
|
|
3404
|
-
var BQ_VERSION = "1.8.
|
|
3810
|
+
var BQ_VERSION = "1.8.2" ;
|
|
3405
3811
|
var ATTACHMENT_URL_EXPIRES_SECONDS = 600;
|
|
3406
3812
|
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
3407
3813
|
var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
@@ -5287,13 +5693,9 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
5287
5693
|
var key = filename + "\0" + body;
|
|
5288
5694
|
var existing = fileBlobCache.get(key);
|
|
5289
5695
|
if (existing) return existing;
|
|
5290
|
-
var
|
|
5291
|
-
var
|
|
5292
|
-
var
|
|
5293
|
-
var needsBom = ext === "csv" || ext === "tsv" || ext === "tab";
|
|
5294
|
-
var type = isText ? contentType + "; charset=utf-8" : contentType;
|
|
5295
|
-
var data = needsBom ? "\uFEFF" + body : body;
|
|
5296
|
-
var href = URL.createObjectURL(new Blob([data], { type }));
|
|
5696
|
+
var prepared = prepareDownloadText(filename, body);
|
|
5697
|
+
var type = EXT_CONTENT_TYPES[extOf(filename)] || mimeGetType(filename) || "text/plain; charset=utf-8";
|
|
5698
|
+
var href = URL.createObjectURL(new Blob([prepared.text], { type }));
|
|
5297
5699
|
fileBlobCache.set(key, href);
|
|
5298
5700
|
return href;
|
|
5299
5701
|
}
|
|
@@ -6703,7 +7105,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
6703
7105
|
if (S.aiPlatform === "none") return "No agent configured";
|
|
6704
7106
|
return S.serviceName || "BunnyQuery";
|
|
6705
7107
|
}
|
|
6706
|
-
function
|
|
7108
|
+
function parseAiAgentValue2(value) {
|
|
6707
7109
|
var raw = (value || "").trim();
|
|
6708
7110
|
var platform = raw, model = "";
|
|
6709
7111
|
if (raw.indexOf("#") !== -1) {
|
|
@@ -6717,9 +7119,10 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
6717
7119
|
function applyAgentConfig() {
|
|
6718
7120
|
var conn = S.service || {};
|
|
6719
7121
|
var raw = conn.ai_agent || "";
|
|
6720
|
-
var parsed =
|
|
7122
|
+
var parsed = parseAiAgentValue2(raw);
|
|
6721
7123
|
S.aiPlatform = parsed.platform;
|
|
6722
7124
|
S.aiModel = parsed.model;
|
|
7125
|
+
setProjectContextWindow(S.serviceId, parseAiAgentValue(raw).contextWindow);
|
|
6723
7126
|
S.serviceName = conn.service_name || "";
|
|
6724
7127
|
S.serviceDescription = conn.service_description || "";
|
|
6725
7128
|
}
|
|
@@ -6861,6 +7264,15 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
6861
7264
|
},
|
|
6862
7265
|
mcpBaseUrl: mcpBaseUrl(),
|
|
6863
7266
|
poll: 0,
|
|
7267
|
+
// Server-driven windowed indexing. Off by default in the engine because the
|
|
7268
|
+
// worker must strip `_skapi_window` first, or the provider rejects the whole
|
|
7269
|
+
// call with no retry. `apply_file_windows` is deployed in every region
|
|
7270
|
+
// (verified 2026-07-27 against the deployed ClientSecretKeyPollingWorker in
|
|
7271
|
+
// all 7), so the widget now opts in like agent.vue does. Without it the
|
|
7272
|
+
// widget fell back to the client-driven CONTINUE loop capped at
|
|
7273
|
+
// MAX_INDEXING_RESUME_PASSES, which needs the tab kept open and stops early
|
|
7274
|
+
// on a big file. Pass windowedIndexing: false in init opts to opt back out.
|
|
7275
|
+
windowedIndexing: S.opts.windowedIndexing !== false,
|
|
6864
7276
|
// Client-side attachment parsers (e.g. an .hwp parser) passed via init opts.
|
|
6865
7277
|
attachmentParsers: S.opts.attachmentParsers || void 0
|
|
6866
7278
|
});
|