bunnyquery 1.8.6 → 1.8.9
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.css +95 -5
- package/bunnyquery.js +1470 -111
- package/dist/engine.cjs +1071 -80
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +608 -169
- package/dist/engine.d.ts +608 -169
- package/dist/engine.mjs +1048 -81
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/budget.ts +207 -68
- package/src/engine/config.ts +48 -0
- package/src/engine/errors.ts +38 -0
- package/src/engine/history.ts +546 -6
- package/src/engine/host.ts +7 -0
- package/src/engine/image_preview.ts +0 -0
- package/src/engine/index.ts +15 -1
- package/src/engine/indexing_groups.ts +248 -3
- package/src/engine/links.ts +143 -6
- package/src/engine/office.ts +24 -1
- package/src/engine/prompts/chat_system_prompt.ts +1 -1
- package/src/engine/requests.ts +164 -11
- package/src/engine/session.ts +544 -35
- package/src/widget.css +35 -1
- package/styles/chat.css +60 -4
package/dist/engine.mjs
CHANGED
|
@@ -215,7 +215,8 @@ function isWindowedReadFile(name, mime) {
|
|
|
215
215
|
if (isImageVisionFile(name, mime)) return false;
|
|
216
216
|
return isPagedReadFile(name, mime);
|
|
217
217
|
}
|
|
218
|
-
function composeUserMessage(text, attachmentUrls) {
|
|
218
|
+
function composeUserMessage(text, attachmentUrls, opts) {
|
|
219
|
+
const inlineExtracted = opts?.inlineExtractedContent !== false;
|
|
219
220
|
let composed = text;
|
|
220
221
|
let composedForLlm = composed;
|
|
221
222
|
if (attachmentUrls.length > 0) {
|
|
@@ -229,7 +230,7 @@ ${lines.join("\n")}`;
|
|
|
229
230
|
let extractContent;
|
|
230
231
|
let fileUrls;
|
|
231
232
|
if (attachmentUrls.length > 0) {
|
|
232
|
-
const extractFiles = attachmentUrls.filter((u) => isServerExtractable(u.name));
|
|
233
|
+
const extractFiles = inlineExtracted ? attachmentUrls.filter((u) => isServerExtractable(u.name)) : [];
|
|
233
234
|
if (extractFiles.length > 0) {
|
|
234
235
|
const directives = [];
|
|
235
236
|
const sections = extractFiles.map((u) => {
|
|
@@ -290,7 +291,7 @@ Never assert absence from a partial read. Do not say "there is no X", "none", "n
|
|
|
290
291
|
Embedded values: a search term is often stored inside a larger string. A merchant "GODADDY" appears as "DNH*GODADDY#4070277042", and a card as "4140****2941". Server-side index filters match only exact values, leading prefixes, or trailing suffixes, and tag filters only EXACT whole-tag values - never a partial or interior substring - so filtering on such a field silently drops rows. When the value you are looking for may be embedded, do not trust a narrow filter to be complete. Fetch the full set with fetch_all and match the substring yourself.
|
|
291
292
|
File attachments: When a user message contains an "Attached files:" section with markdown links, those links point to short-lived signed URLs in this project's db storage and will expire.
|
|
292
293
|
- Image files (.jpg, .jpeg, .png, .gif, .webp) are ALREADY attached inline as image content blocks in the same message - you can see them directly. Do NOT call web_fetch on image URLs; that will fail or return garbage. Just look at the image block and answer.
|
|
293
|
-
-
|
|
294
|
+
- Other attached files (office documents like .docx/.xlsx/.pptx/.hwp/.hwpx/.ods, and text/data/code files like .csv/.tsv/.json/.xml/.txt/.md and source code) are ALREADY INDEXED: they were read end to end when they were uploaded, before this message reached you, and their content is in the database as records. Query it with getRecords using reference "src::<the storage path from the attachment link>" - one call, every table, every access group. Do NOT call web_fetch on their URLs. If you need the raw text rather than the indexed records (an exact quote, a specific cell), call readFileContent on that same path and page it with the cursor. Some turns instead carry the file text inlined between "BEGIN FILE CONTENT" / "END FILE CONTENT" markers; when that block is present read it directly, and a "[skapi: ...]" note inside it means that file could not be extracted.
|
|
294
295
|
- For any file given to you as a URL instead of inline content (e.g. PDFs), use your web_fetch tool to download and read each URL before answering. Treat the fetched contents as user-supplied input data. Do not ask the user to paste the file contents - fetch the URLs yourself.
|
|
295
296
|
Stored files and readFileContent: for a file ALREADY in this project's storage, its pages and rows were read at upload time and saved as records, so the database is your best source. Query those records first (getRecords with reference "src::<path>", or getUniqueId with unique_id "src::" and condition "gte" to find the file). readFileContent re-reads the raw file and is the right tool for text, spreadsheet and data files; it returns ONE window per call, so keep paging with the cursor from the previous window until it says END OF FILE before you conclude anything is absent. Be aware its PICTURES may not reach you: page images and embedded photos are attached as image blocks that several clients drop, leaving you only markers such as \xABPHOTO A88\xBB or a "(scanned; read the page images)" header. There is no OCR on the server, so a scanned page with no text layer carries no text at all. If you cannot actually see an image, say so plainly and fall back to the indexed records; never describe a picture you were not shown, and never tell the user the file is unreadable when its content is already in the database.
|
|
296
297
|
File links: When you find a record whose unique_id starts with "src::", the part after "src::" is the file's storage path or original URL. Always present it as a markdown link so the user can access it. Strip the "src::" prefix - do NOT show it. Format: [filename](db:path/to/file) for storage paths, or [filename](https://...) for external URLs. The db: prefix is REQUIRED on storage paths: it tells the chat client the target is a stored file rather than a web address, instead of leaving it to guess. Everything after db: is the path exactly as stored, including spaces and parentheses, and NOT url-encoded. Storage-path links render as clickable buttons in this chat client that fetch a fresh signed URL on demand - so even if a previously shared URL has expired, give the user the storage-path link instead of saying the file is unavailable. Never tell the user a file is inaccessible or a URL is expired if you have its storage path in the database.
|
|
@@ -564,14 +565,77 @@ function isAuthExpiredError(input) {
|
|
|
564
565
|
if (!hay) return false;
|
|
565
566
|
return hay.indexOf("token has expired") !== -1 || hay.indexOf("token is expired") !== -1 || hay.indexOf("expired_token") !== -1 || hay.indexOf("invalid_token") !== -1 || hay.indexOf("unauthorized") !== -1 || hay.indexOf("not authorized") !== -1 || hay.indexOf("invalid_request") !== -1 && hay.indexOf("token") !== -1;
|
|
566
567
|
}
|
|
568
|
+
function isProviderApiKeyError(input) {
|
|
569
|
+
if (!input) return false;
|
|
570
|
+
var blobs = [];
|
|
571
|
+
var push = function(v) {
|
|
572
|
+
if (typeof v === "string" && v) blobs.push(v);
|
|
573
|
+
};
|
|
574
|
+
if (typeof input === "string") push(input);
|
|
575
|
+
else {
|
|
576
|
+
push(input.message);
|
|
577
|
+
push(input.code);
|
|
578
|
+
push(input.type);
|
|
579
|
+
if (input.error) {
|
|
580
|
+
push(input.error.message);
|
|
581
|
+
push(input.error.code);
|
|
582
|
+
push(input.error.type);
|
|
583
|
+
}
|
|
584
|
+
if (input.body) {
|
|
585
|
+
push(input.body.message);
|
|
586
|
+
push(input.body.type);
|
|
587
|
+
if (input.body.error) {
|
|
588
|
+
push(input.body.error.message);
|
|
589
|
+
push(input.body.error.code);
|
|
590
|
+
push(input.body.error.type);
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
var hay = blobs.join(" | ").toLowerCase();
|
|
595
|
+
if (!hay) return false;
|
|
596
|
+
return hay.indexOf("authentication_error") !== -1 || hay.indexOf("invalid_api_key") !== -1 || hay.indexOf("invalid x-api-key") !== -1 || hay.indexOf("incorrect api key") !== -1 || hay.indexOf("invalid api key") !== -1 || hay.indexOf("no api key provided") !== -1;
|
|
597
|
+
}
|
|
567
598
|
|
|
568
599
|
// src/engine/links.ts
|
|
569
600
|
var EXPIRED_ATTACHMENT_URL_HOST = "_expired_.url";
|
|
570
601
|
var EXPIRED_ATTACHMENT_URL_ORIGIN = "https://" + EXPIRED_ATTACHMENT_URL_HOST;
|
|
571
602
|
var LINK_LABEL_MAX_DISPLAY_CHARS = 32;
|
|
572
603
|
var EXPIRED_LINK_REFRESH_EXPIRES_SECONDS = 20 * 60;
|
|
604
|
+
var PREVIEW_URL_EXPIRES_SECONDS = 60 * 60;
|
|
573
605
|
var PREVIEW_BROWSER_CACHE_SECONDS = 7 * 24 * 60 * 60;
|
|
574
606
|
var LINK_REFRESH_WINDOW_MS = (EXPIRED_LINK_REFRESH_EXPIRES_SECONDS - 5 * 60) * 1e3;
|
|
607
|
+
var MINT_CACHE_GENERATION = 2;
|
|
608
|
+
function mintCacheBustStamp(now) {
|
|
609
|
+
return Math.floor((now == null ? Date.now() : now) / LINK_REFRESH_WINDOW_MS);
|
|
610
|
+
}
|
|
611
|
+
function previewMintCacheToken(refresh) {
|
|
612
|
+
if (!refresh) return String(MINT_CACHE_GENERATION);
|
|
613
|
+
return MINT_CACHE_GENERATION + "." + mintCacheBustStamp();
|
|
614
|
+
}
|
|
615
|
+
var PRESIGN_SAFETY_MARGIN_MS = 60 * 1e3;
|
|
616
|
+
function presignExpiryEpochMs(url) {
|
|
617
|
+
if (!url) return null;
|
|
618
|
+
var q = url.indexOf("?");
|
|
619
|
+
if (q < 0) return null;
|
|
620
|
+
var params;
|
|
621
|
+
try {
|
|
622
|
+
params = new URLSearchParams(url.slice(q + 1));
|
|
623
|
+
} catch (e) {
|
|
624
|
+
return null;
|
|
625
|
+
}
|
|
626
|
+
var v2 = params.get("Expires");
|
|
627
|
+
if (v2 && /^\d+$/.test(v2)) return parseInt(v2, 10) * 1e3;
|
|
628
|
+
var signed = params.get("X-Amz-Date");
|
|
629
|
+
var lifetime = params.get("X-Amz-Expires");
|
|
630
|
+
if (signed && lifetime && /^\d+$/.test(lifetime)) {
|
|
631
|
+
var m = /^(\d{4})(\d{2})(\d{2})T(\d{2})(\d{2})(\d{2})Z$/.exec(signed);
|
|
632
|
+
if (m) {
|
|
633
|
+
var at = Date.UTC(+m[1], +m[2] - 1, +m[3], +m[4], +m[5], +m[6]);
|
|
634
|
+
return at + parseInt(lifetime, 10) * 1e3;
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
return null;
|
|
638
|
+
}
|
|
575
639
|
function createInlineLinkRegex() {
|
|
576
640
|
return /src::(\S+)|\[([^\]\n]+)\]\((https?:\/\/(?:[^\s()]|\([^\s()]*\))+)\)|\[([^\]\n]+)\]\(((?:[^()\n]|\([^()\n]*\))+)\)|(https?:\/\/[^\s<>"']+)/g;
|
|
577
641
|
}
|
|
@@ -822,6 +886,13 @@ function linkUnavailableKeyForPath(remotePath) {
|
|
|
822
886
|
function linkUnavailableKeyForHref(href) {
|
|
823
887
|
return "href:" + (href || "");
|
|
824
888
|
}
|
|
889
|
+
function linkUnavailableKeysForPath(remotePath) {
|
|
890
|
+
if (!remotePath) return [];
|
|
891
|
+
return [
|
|
892
|
+
linkUnavailableKeyForPath(remotePath),
|
|
893
|
+
linkUnavailableKeyForHref(buildDisplayExpiredAttachmentHref(remotePath))
|
|
894
|
+
];
|
|
895
|
+
}
|
|
825
896
|
function isLinkUnavailable(link, map) {
|
|
826
897
|
if (!link || !map) return false;
|
|
827
898
|
if (link.remotePath && map[linkUnavailableKeyForPath(link.remotePath)]) return true;
|
|
@@ -838,33 +909,85 @@ function truncateLabelForDisplay(label) {
|
|
|
838
909
|
// src/engine/budget.ts
|
|
839
910
|
var CONTEXT_WINDOW_DEFAULT = { claude: 2e5, openai: 128e3 };
|
|
840
911
|
var CONTEXT_WINDOW_BY_MODEL = {
|
|
841
|
-
// exact ids
|
|
912
|
+
// claude, exact ids
|
|
913
|
+
"claude-fable-5": 1e6,
|
|
842
914
|
"claude-opus-5": 1e6,
|
|
843
915
|
"claude-opus-4-8": 1e6,
|
|
844
916
|
"claude-opus-4-7": 1e6,
|
|
917
|
+
"claude-opus-4-6": 1e6,
|
|
918
|
+
"claude-opus-4-5": 2e5,
|
|
845
919
|
"claude-sonnet-5": 1e6,
|
|
846
920
|
"claude-sonnet-4-6": 1e6,
|
|
921
|
+
"claude-sonnet-4-5": 1e6,
|
|
847
922
|
"claude-sonnet-4": 2e5,
|
|
848
923
|
"claude-haiku-4-5": 2e5,
|
|
849
|
-
"
|
|
850
|
-
|
|
924
|
+
"claude-3-5-sonnet": 2e5,
|
|
925
|
+
// openai, exact ids
|
|
926
|
+
"gpt-5.6-sol": 105e4,
|
|
927
|
+
"gpt-5.6-terra": 105e4,
|
|
928
|
+
"gpt-5.6-luna": 105e4,
|
|
929
|
+
"gpt-5.5": 1e6,
|
|
930
|
+
"gpt-5.4": 105e4,
|
|
931
|
+
"gpt-5.4-mini": 4e5,
|
|
932
|
+
"gpt-5.4-nano": 4e5,
|
|
933
|
+
"gpt-4.1": 104e4,
|
|
934
|
+
"gpt-4o": 128e3,
|
|
935
|
+
"o1": 2e5,
|
|
936
|
+
"o1-pro": 2e5,
|
|
851
937
|
// family keys
|
|
938
|
+
"claude-fable": 1e6,
|
|
852
939
|
"claude-opus": 1e6,
|
|
853
940
|
"claude-sonnet": 1e6,
|
|
854
941
|
"claude-haiku": 2e5,
|
|
942
|
+
"gpt-5.6": 105e4,
|
|
943
|
+
"gpt-5": 128e3
|
|
944
|
+
};
|
|
945
|
+
var MAX_OUTPUT_BY_MODEL = {
|
|
946
|
+
// claude
|
|
947
|
+
"claude-fable-5": 128e3,
|
|
948
|
+
"claude-opus-5": 128e3,
|
|
949
|
+
"claude-opus-4-8": 128e3,
|
|
950
|
+
"claude-sonnet-5": 128e3,
|
|
951
|
+
"claude-sonnet-4-6": 64e3,
|
|
952
|
+
"claude-haiku-4-5": 64e3,
|
|
953
|
+
"claude-3-5-sonnet": 8e3,
|
|
954
|
+
// openai
|
|
955
|
+
"gpt-5.6-sol": 128e3,
|
|
956
|
+
"gpt-5.6-terra": 128e3,
|
|
957
|
+
"gpt-5.6-luna": 128e3,
|
|
958
|
+
"gpt-5.5": 128e3,
|
|
959
|
+
"gpt-5.4": 128e3,
|
|
960
|
+
"gpt-5.4-mini": 128e3,
|
|
961
|
+
"gpt-5.4-nano": 128e3,
|
|
962
|
+
"gpt-4.1": 16e3,
|
|
963
|
+
"gpt-4o": 4e3,
|
|
964
|
+
"o1": 1e5,
|
|
965
|
+
"o1-pro": 1e5,
|
|
966
|
+
// family keys
|
|
967
|
+
"claude-fable": 128e3,
|
|
968
|
+
"claude-opus": 128e3,
|
|
969
|
+
"claude-sonnet": 64e3,
|
|
970
|
+
"claude-haiku": 64e3,
|
|
855
971
|
"gpt-5.6": 128e3,
|
|
856
972
|
"gpt-5": 128e3
|
|
857
973
|
};
|
|
974
|
+
var DEFAULT_CONTEXT_WINDOW = 88e4;
|
|
858
975
|
var apiReportedContextWindows = {};
|
|
976
|
+
var apiReportedMaxOutput = {};
|
|
859
977
|
function registerModelContextWindows(models) {
|
|
860
978
|
if (!Array.isArray(models)) return;
|
|
861
979
|
for (var i = 0; i < models.length; i++) {
|
|
862
980
|
var m = models[i];
|
|
863
981
|
var id = (m && m.id ? String(m.id) : "").trim().toLowerCase();
|
|
982
|
+
if (!id) continue;
|
|
864
983
|
var reported = m ? Number(m.max_input_tokens) : NaN;
|
|
865
|
-
if (
|
|
984
|
+
if (Number.isFinite(reported) && reported > 0) {
|
|
866
985
|
apiReportedContextWindows[id] = Math.floor(reported);
|
|
867
986
|
}
|
|
987
|
+
var out = m ? Number(m.max_tokens) : NaN;
|
|
988
|
+
if (Number.isFinite(out) && out > 0) {
|
|
989
|
+
apiReportedMaxOutput[id] = Math.floor(out);
|
|
990
|
+
}
|
|
868
991
|
}
|
|
869
992
|
}
|
|
870
993
|
var projectContextWindows = {};
|
|
@@ -879,13 +1002,16 @@ function getProjectContextWindow(projectId) {
|
|
|
879
1002
|
var key = (projectId || "").trim();
|
|
880
1003
|
return key && projectContextWindows[key] ? projectContextWindows[key] : null;
|
|
881
1004
|
}
|
|
882
|
-
var
|
|
1005
|
+
var MAX_OUTPUT_TOKENS = 25e3;
|
|
1006
|
+
var OUTPUT_TOKEN_RESERVE = MAX_OUTPUT_TOKENS;
|
|
883
1007
|
var TOOL_AND_RESPONSE_BUFFER = 4e3;
|
|
884
1008
|
var MIN_INPUT_TOKEN_BUDGET = 8e3;
|
|
885
|
-
var
|
|
1009
|
+
var MIN_PER_REQUEST_INPUT_CAP = 28e3;
|
|
1010
|
+
var CLAUDE_PER_REQUEST_INPUT_CAP = MIN_PER_REQUEST_INPUT_CAP;
|
|
886
1011
|
var MAX_HISTORY_MESSAGES = 20;
|
|
887
1012
|
var HISTORY_TOKEN_BUDGET = 8e3;
|
|
888
|
-
var
|
|
1013
|
+
var INPUT_CAP_RATIO = 0.16;
|
|
1014
|
+
var CLAUDE_INPUT_CAP_RATIO = INPUT_CAP_RATIO;
|
|
889
1015
|
var HISTORY_BUDGET_RATIO = 0.08;
|
|
890
1016
|
function estimateTextTokens(text) {
|
|
891
1017
|
return Math.ceil((text || "").length / 3);
|
|
@@ -893,38 +1019,61 @@ function estimateTextTokens(text) {
|
|
|
893
1019
|
function estimateMessageTokens(msg) {
|
|
894
1020
|
return estimateTextTokens(msg.content) + estimateTextTokens(msg.role) + 6;
|
|
895
1021
|
}
|
|
1022
|
+
function resolveByModelId(apiTable, staticTable, model) {
|
|
1023
|
+
var normalized = (model || "").trim().toLowerCase();
|
|
1024
|
+
if (!normalized) return 0;
|
|
1025
|
+
if (apiTable[normalized]) return apiTable[normalized];
|
|
1026
|
+
if (staticTable[normalized]) return staticTable[normalized];
|
|
1027
|
+
var parts = normalized.split("-");
|
|
1028
|
+
for (var end = parts.length - 1; end > 0; end--) {
|
|
1029
|
+
var family = parts.slice(0, end).join("-");
|
|
1030
|
+
if (staticTable[family]) return staticTable[family];
|
|
1031
|
+
}
|
|
1032
|
+
return 0;
|
|
1033
|
+
}
|
|
1034
|
+
function getModelContextWindow(platform, model) {
|
|
1035
|
+
return resolveByModelId(apiReportedContextWindows, CONTEXT_WINDOW_BY_MODEL, model) || CONTEXT_WINDOW_DEFAULT[platform];
|
|
1036
|
+
}
|
|
1037
|
+
function getMaxOutputTokens(platform, model) {
|
|
1038
|
+
var cap = resolveByModelId(apiReportedMaxOutput, MAX_OUTPUT_BY_MODEL, model);
|
|
1039
|
+
return cap ? Math.min(MAX_OUTPUT_TOKENS, cap) : MAX_OUTPUT_TOKENS;
|
|
1040
|
+
}
|
|
896
1041
|
function getContextWindow(platform, model, projectId) {
|
|
1042
|
+
var ceiling = getModelContextWindow(platform, model);
|
|
897
1043
|
var override = projectId ? getProjectContextWindow(projectId) : null;
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
return
|
|
1044
|
+
return Math.min(override || DEFAULT_CONTEXT_WINDOW, ceiling);
|
|
1045
|
+
}
|
|
1046
|
+
function contextBasedBudgetFor(platform, model, projectId) {
|
|
1047
|
+
var contextWindow = getContextWindow(platform, model, projectId);
|
|
1048
|
+
return Math.max(
|
|
1049
|
+
MIN_INPUT_TOKEN_BUDGET,
|
|
1050
|
+
contextWindow - getMaxOutputTokens(platform, model) - TOOL_AND_RESPONSE_BUFFER
|
|
1051
|
+
);
|
|
1052
|
+
}
|
|
1053
|
+
function getInputTokenBudget(platform, model, projectId) {
|
|
1054
|
+
var contextBasedBudget = contextBasedBudgetFor(platform, model, projectId);
|
|
1055
|
+
return Math.min(
|
|
1056
|
+
contextBasedBudget,
|
|
1057
|
+
Math.max(MIN_PER_REQUEST_INPUT_CAP, Math.round(contextBasedBudget * INPUT_CAP_RATIO))
|
|
1058
|
+
);
|
|
910
1059
|
}
|
|
911
1060
|
function stripFileBlocksFromHistory(content) {
|
|
912
1061
|
if (!content) return content;
|
|
913
1062
|
return content.replace(/```([^\n`]+?\.[^\s.`]+)\n[\s\S]*?```/g, "[file previously attached: $1]");
|
|
914
1063
|
}
|
|
915
1064
|
function buildBoundedChatMessages(options) {
|
|
916
|
-
var
|
|
917
|
-
var
|
|
918
|
-
MIN_INPUT_TOKEN_BUDGET,
|
|
919
|
-
contextWindow - OUTPUT_TOKEN_RESERVE - TOOL_AND_RESPONSE_BUFFER
|
|
920
|
-
);
|
|
921
|
-
var scaled = !!(options.projectId && getProjectContextWindow(options.projectId));
|
|
922
|
-
var claudeInputCap = scaled ? Math.max(CLAUDE_PER_REQUEST_INPUT_CAP, Math.round(contextBasedBudget * CLAUDE_INPUT_CAP_RATIO)) : CLAUDE_PER_REQUEST_INPUT_CAP;
|
|
923
|
-
var availableInputBudget = options.platform === "claude" ? Math.min(contextBasedBudget, claudeInputCap) : contextBasedBudget;
|
|
1065
|
+
var contextBasedBudget = contextBasedBudgetFor(options.platform, options.model, options.projectId);
|
|
1066
|
+
var availableInputBudget = getInputTokenBudget(options.platform, options.model, options.projectId);
|
|
924
1067
|
var systemCost = estimateTextTokens(options.systemPrompt) + 12;
|
|
925
|
-
var historyAllowance =
|
|
1068
|
+
var historyAllowance = Math.max(
|
|
1069
|
+
HISTORY_TOKEN_BUDGET,
|
|
1070
|
+
Math.round(contextBasedBudget * HISTORY_BUDGET_RATIO)
|
|
1071
|
+
);
|
|
926
1072
|
var budgetForHistory = Math.max(1e3, Math.min(historyAllowance, availableInputBudget - systemCost));
|
|
927
|
-
var maxHistoryMessages =
|
|
1073
|
+
var maxHistoryMessages = Math.max(
|
|
1074
|
+
MAX_HISTORY_MESSAGES,
|
|
1075
|
+
Math.round(MAX_HISTORY_MESSAGES * (budgetForHistory / HISTORY_TOKEN_BUDGET))
|
|
1076
|
+
);
|
|
928
1077
|
var windowed = options.history.slice(-maxHistoryMessages);
|
|
929
1078
|
var latestIndex = windowed.length - 1;
|
|
930
1079
|
var trimmed = windowed.map(function(m, i2) {
|
|
@@ -1166,8 +1315,11 @@ function clearImagePreviewCache(scope) {
|
|
|
1166
1315
|
}
|
|
1167
1316
|
function peekImagePreviewUrl(ctx, remotePath) {
|
|
1168
1317
|
var hit = previewUrlCache[cacheKey(ctx.scope, remotePath)];
|
|
1169
|
-
if (hit
|
|
1170
|
-
return null;
|
|
1318
|
+
if (!hit) return null;
|
|
1319
|
+
if (Date.now() - hit.at >= LINK_REFRESH_WINDOW_MS) return null;
|
|
1320
|
+
var dies = presignExpiryEpochMs(hit.url);
|
|
1321
|
+
if (dies !== null && Date.now() >= dies - PRESIGN_SAFETY_MARGIN_MS) return null;
|
|
1322
|
+
return hit.url;
|
|
1171
1323
|
}
|
|
1172
1324
|
function resolveImagePreviewUrl(ctx, remotePath, contentType, refresh) {
|
|
1173
1325
|
var key = cacheKey(ctx.scope, remotePath);
|
|
@@ -1217,6 +1369,7 @@ function hydrateOne(img, ctx) {
|
|
|
1217
1369
|
img.setAttribute("data-bq-img-state", "loading");
|
|
1218
1370
|
img.addEventListener("load", function() {
|
|
1219
1371
|
img.setAttribute("data-bq-img-state", "ready");
|
|
1372
|
+
img.removeAttribute("data-bq-img-retry");
|
|
1220
1373
|
if (ctx.onLoad) ctx.onLoad(path);
|
|
1221
1374
|
});
|
|
1222
1375
|
img.addEventListener("error", function() {
|
|
@@ -1317,7 +1470,6 @@ var WEB_FETCH_MAX_USES = 40;
|
|
|
1317
1470
|
var WEB_FETCH_MAX_CONTENT_TOKENS = 2e5;
|
|
1318
1471
|
var OPENAI_RESPONSES_API_URL = "https://api.openai.com/v1/responses";
|
|
1319
1472
|
var OPENAI_MODELS_API_URL = "https://api.openai.com/v1/models";
|
|
1320
|
-
var MAX_TOKENS = 25e3;
|
|
1321
1473
|
var DEFAULT_OPENAI_IMAGE_DETAIL = "auto";
|
|
1322
1474
|
var OPENAI_WEB_SEARCH_EXTERNAL_WEB_ACCESS = true;
|
|
1323
1475
|
var MCP_NAME = "BunnyQuery";
|
|
@@ -1557,7 +1709,7 @@ async function callClaudeWithPublicMcp(prompt, service, owner, messages, system,
|
|
|
1557
1709
|
owner,
|
|
1558
1710
|
userId,
|
|
1559
1711
|
model: model || DEFAULT_CLAUDE_MODEL,
|
|
1560
|
-
maxTokens:
|
|
1712
|
+
maxTokens: getMaxOutputTokens("claude", model || DEFAULT_CLAUDE_MODEL),
|
|
1561
1713
|
system,
|
|
1562
1714
|
extractContent,
|
|
1563
1715
|
fileUrls,
|
|
@@ -1602,7 +1754,7 @@ async function callOpenAIWithPublicMcp(prompt, service, owner, messages, system,
|
|
|
1602
1754
|
},
|
|
1603
1755
|
data: {
|
|
1604
1756
|
model: resolvedModel,
|
|
1605
|
-
max_output_tokens:
|
|
1757
|
+
max_output_tokens: getMaxOutputTokens("openai", resolvedModel),
|
|
1606
1758
|
...extractContent && extractContent.length ? { _skapi_extract: extractContent } : {},
|
|
1607
1759
|
...fileUrls && fileUrls.length ? { _skapi_file_urls: fileUrls } : {},
|
|
1608
1760
|
input: responseInput,
|
|
@@ -1632,6 +1784,29 @@ async function notifyAgentContinueIndexing(info) {
|
|
|
1632
1784
|
async function notifyAgentSaveAttachment(info) {
|
|
1633
1785
|
const { platform, service, owner, attachment, parsedContent } = info;
|
|
1634
1786
|
const continuing = !!info.continueIndexing;
|
|
1787
|
+
if (!continuing) {
|
|
1788
|
+
upsertIndexRunRecordSafe(service, attachment.storagePath, {
|
|
1789
|
+
status: "working",
|
|
1790
|
+
filename: attachment.name,
|
|
1791
|
+
started: Date.now(),
|
|
1792
|
+
queue: bgIndexingQueueName(info.userId, service),
|
|
1793
|
+
platform
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
const tapDispatchFailure = (p) => {
|
|
1797
|
+
if (continuing) return p;
|
|
1798
|
+
return p.then(
|
|
1799
|
+
(ack) => ack,
|
|
1800
|
+
(err) => {
|
|
1801
|
+
upsertIndexRunRecordSafe(service, attachment.storagePath, {
|
|
1802
|
+
status: "error",
|
|
1803
|
+
finished: Date.now(),
|
|
1804
|
+
error: err && (err.message || String(err)) || "The indexing request could not be enqueued."
|
|
1805
|
+
});
|
|
1806
|
+
throw err;
|
|
1807
|
+
}
|
|
1808
|
+
);
|
|
1809
|
+
};
|
|
1635
1810
|
const visionFile = !parsedContent && isImageVisionFile(attachment.name, attachment.mime);
|
|
1636
1811
|
const renderFrom = Math.max(0, info.renderFrom || 0);
|
|
1637
1812
|
const renderPlaceholder = visionFile ? makeRenderPlaceholder(attachment.storagePath) : void 0;
|
|
@@ -1691,6 +1866,7 @@ async function notifyAgentSaveAttachment(info) {
|
|
|
1691
1866
|
save_media: !continuing
|
|
1692
1867
|
}))
|
|
1693
1868
|
} : {};
|
|
1869
|
+
const skapiFileUrls = attachment.url && attachment.storagePath ? { _skapi_file_urls: [{ path: attachment.storagePath, url: attachment.url }] } : {};
|
|
1694
1870
|
const userMessage = visionFile && renderPlaceholder ? buildIndexingRenderMessage(attachment, renderPlaceholder, renderFrom) : windowedRead && windowPlaceholder ? buildIndexingWindowMessage(attachment, windowPlaceholder, false) : continuing ? buildIndexingContinueMessage(attachment) : buildIndexingUserMessage(
|
|
1695
1871
|
attachment,
|
|
1696
1872
|
parsedContent ? { inlineContent: parsedContent } : placeholder ? { inlineContentPlaceholder: placeholder } : pagedRead ? { pagedRead: true } : void 0
|
|
@@ -1706,7 +1882,7 @@ async function notifyAgentSaveAttachment(info) {
|
|
|
1706
1882
|
if (platform === "openai") {
|
|
1707
1883
|
const resolvedModel2 = info.model || DEFAULT_OPENAI_MODEL;
|
|
1708
1884
|
const imageDetail = getOpenAIImageDetail(resolvedModel2);
|
|
1709
|
-
return clientSecretRequest({
|
|
1885
|
+
return tapDispatchFailure(clientSecretRequest({
|
|
1710
1886
|
clientSecretName: "openai",
|
|
1711
1887
|
queue: bgIndexingQueueName(info.userId, service),
|
|
1712
1888
|
service,
|
|
@@ -1720,12 +1896,13 @@ async function notifyAgentSaveAttachment(info) {
|
|
|
1720
1896
|
},
|
|
1721
1897
|
data: {
|
|
1722
1898
|
model: resolvedModel2,
|
|
1723
|
-
max_output_tokens:
|
|
1899
|
+
max_output_tokens: getMaxOutputTokens("openai", resolvedModel2),
|
|
1724
1900
|
// Nano-only transcription knobs. Indexing only; see variantIndexingOptions.
|
|
1725
1901
|
...variantIndexingOptions(resolvedModel2),
|
|
1726
1902
|
...skapiExtract,
|
|
1727
1903
|
...skapiRender,
|
|
1728
1904
|
...skapiWindow,
|
|
1905
|
+
...skapiFileUrls,
|
|
1729
1906
|
input: [
|
|
1730
1907
|
{ role: "system", content: systemPrompt },
|
|
1731
1908
|
{
|
|
@@ -1749,10 +1926,10 @@ async function notifyAgentSaveAttachment(info) {
|
|
|
1749
1926
|
]
|
|
1750
1927
|
]
|
|
1751
1928
|
}
|
|
1752
|
-
});
|
|
1929
|
+
}));
|
|
1753
1930
|
}
|
|
1754
1931
|
const resolvedModel = info.model || DEFAULT_CLAUDE_MODEL;
|
|
1755
|
-
return clientSecretRequest({
|
|
1932
|
+
return tapDispatchFailure(clientSecretRequest({
|
|
1756
1933
|
clientSecretName: "claude",
|
|
1757
1934
|
queue: bgIndexingQueueName(info.userId, service),
|
|
1758
1935
|
service,
|
|
@@ -1768,10 +1945,11 @@ async function notifyAgentSaveAttachment(info) {
|
|
|
1768
1945
|
},
|
|
1769
1946
|
data: {
|
|
1770
1947
|
model: resolvedModel,
|
|
1771
|
-
max_tokens:
|
|
1948
|
+
max_tokens: getMaxOutputTokens("claude", resolvedModel),
|
|
1772
1949
|
...skapiExtract,
|
|
1773
1950
|
...skapiRender,
|
|
1774
1951
|
...skapiWindow,
|
|
1952
|
+
...skapiFileUrls,
|
|
1775
1953
|
system: [
|
|
1776
1954
|
{
|
|
1777
1955
|
type: "text",
|
|
@@ -1807,7 +1985,7 @@ async function notifyAgentSaveAttachment(info) {
|
|
|
1807
1985
|
}
|
|
1808
1986
|
]
|
|
1809
1987
|
}
|
|
1810
|
-
});
|
|
1988
|
+
}));
|
|
1811
1989
|
}
|
|
1812
1990
|
function extractClaudeText(response) {
|
|
1813
1991
|
if (!Array.isArray(response?.content)) {
|
|
@@ -1868,6 +2046,21 @@ async function listOpenAIModels(service, owner) {
|
|
|
1868
2046
|
});
|
|
1869
2047
|
}
|
|
1870
2048
|
var BG_INDEXING_QUEUE_SUFFIX = "-bg";
|
|
2049
|
+
function indexDoneUniqueId(storagePath) {
|
|
2050
|
+
return "done::" + storagePath;
|
|
2051
|
+
}
|
|
2052
|
+
function runIndexUniqueId(storagePath) {
|
|
2053
|
+
return "run::" + storagePath;
|
|
2054
|
+
}
|
|
2055
|
+
function upsertIndexRunRecordSafe(service, storagePath, patch) {
|
|
2056
|
+
if (!service || !storagePath) return;
|
|
2057
|
+
try {
|
|
2058
|
+
const hook = chatEngineConfig().upsertIndexRunRecord;
|
|
2059
|
+
if (typeof hook !== "function") return;
|
|
2060
|
+
hook({ service, storagePath, patch });
|
|
2061
|
+
} catch (e) {
|
|
2062
|
+
}
|
|
2063
|
+
}
|
|
1871
2064
|
function bgIndexingQueueName(userId, service) {
|
|
1872
2065
|
return (userId || service || "") + BG_INDEXING_QUEUE_SUFFIX;
|
|
1873
2066
|
}
|
|
@@ -1891,13 +2084,20 @@ async function getChatHistory(params, fetchOptions) {
|
|
|
1891
2084
|
},
|
|
1892
2085
|
{ service: params.service, owner: params.owner },
|
|
1893
2086
|
params.queue ? { queue: params.queue } : {},
|
|
1894
|
-
params.status ? { status: params.status } : {}
|
|
2087
|
+
params.status ? { status: params.status } : {},
|
|
2088
|
+
params.queue_exact ? { queue_exact: true } : {},
|
|
2089
|
+
params.compact ? { compact: true } : {},
|
|
2090
|
+
params.queue_exclude ? { queue_exclude: params.queue_exclude } : {}
|
|
1895
2091
|
);
|
|
1896
2092
|
return chatEngineConfig().clientSecretRequestHistory(
|
|
1897
2093
|
p,
|
|
1898
2094
|
Object.assign({ ascending: false, limit: CHAT_HISTORY_PAGE_LIMIT }, fetchOptions)
|
|
1899
2095
|
);
|
|
1900
2096
|
}
|
|
2097
|
+
function buildHistoryItemFullId(platform, service, itemId) {
|
|
2098
|
+
const url = platform === "claude" ? ANTHROPIC_MESSAGES_API_URL : OPENAI_RESPONSES_API_URL;
|
|
2099
|
+
return `[POST]${url.toLowerCase()}#${service}:${itemId}`;
|
|
2100
|
+
}
|
|
1901
2101
|
|
|
1902
2102
|
// src/engine/history.ts
|
|
1903
2103
|
function filterListByClearHorizon(list, clearedAt) {
|
|
@@ -1947,6 +2147,278 @@ function parseIndexingRequestText(userText) {
|
|
|
1947
2147
|
continued: userText.indexOf("CONTINUE indexing") === 0
|
|
1948
2148
|
};
|
|
1949
2149
|
}
|
|
2150
|
+
var LIVE_INDEX_PROBE_LIMIT = 20;
|
|
2151
|
+
var BG_PROBE_TTL_MS = 4e3;
|
|
2152
|
+
var bgProbeCache = {};
|
|
2153
|
+
var bgProbeInflight = {};
|
|
2154
|
+
function probeBgQueue(params, opts) {
|
|
2155
|
+
const key = [params.service, params.owner, params.platform, params.queue, params.status, params.limit].join("|");
|
|
2156
|
+
const maxAge = opts && typeof opts.maxAgeMs === "number" ? opts.maxAgeMs : 0;
|
|
2157
|
+
const cached = bgProbeCache[key];
|
|
2158
|
+
if (maxAge > 0 && cached && Date.now() - cached.at < maxAge) {
|
|
2159
|
+
return Promise.resolve(cached);
|
|
2160
|
+
}
|
|
2161
|
+
const inflight = bgProbeInflight[key];
|
|
2162
|
+
if (inflight) return inflight;
|
|
2163
|
+
const p = Promise.resolve(getChatHistory(
|
|
2164
|
+
{ service: params.service, owner: params.owner, platform: params.platform, queue: params.queue, status: params.status },
|
|
2165
|
+
{ limit: params.limit, fetchMore: false }
|
|
2166
|
+
)).then(function(result) {
|
|
2167
|
+
const entry = { result, at: Date.now() };
|
|
2168
|
+
bgProbeCache[key] = entry;
|
|
2169
|
+
return entry;
|
|
2170
|
+
});
|
|
2171
|
+
bgProbeInflight[key] = p;
|
|
2172
|
+
p.then(function() {
|
|
2173
|
+
delete bgProbeInflight[key];
|
|
2174
|
+
}, function() {
|
|
2175
|
+
delete bgProbeInflight[key];
|
|
2176
|
+
});
|
|
2177
|
+
return p;
|
|
2178
|
+
}
|
|
2179
|
+
async function fetchLiveIndexingKeys(params) {
|
|
2180
|
+
const queue = bgIndexingQueueName(params.userId, params.service);
|
|
2181
|
+
const base = { service: params.service, owner: params.owner, platform: params.platform, queue };
|
|
2182
|
+
const [pending, running] = await Promise.all([
|
|
2183
|
+
probeBgQueue({ ...base, status: "pending", limit: LIVE_INDEX_PROBE_LIMIT }, { maxAgeMs: BG_PROBE_TTL_MS }),
|
|
2184
|
+
probeBgQueue({ ...base, status: "running", limit: LIVE_INDEX_PROBE_LIMIT }, { maxAgeMs: BG_PROBE_TTL_MS })
|
|
2185
|
+
]);
|
|
2186
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2187
|
+
let truncated = false;
|
|
2188
|
+
for (const entry of [pending, running]) {
|
|
2189
|
+
const res = entry.result;
|
|
2190
|
+
const list = res && Array.isArray(res.list) ? res.list : [];
|
|
2191
|
+
if (list.length >= LIVE_INDEX_PROBE_LIMIT) truncated = true;
|
|
2192
|
+
for (const item of list) {
|
|
2193
|
+
const text = extractLastUserTextFromRequest(item && item.request_body);
|
|
2194
|
+
if (!text || !isIndexingRequestText(text)) continue;
|
|
2195
|
+
const ref = parseIndexingRequestText(text);
|
|
2196
|
+
if (!ref) continue;
|
|
2197
|
+
if (ref.path) keys.add(ref.path);
|
|
2198
|
+
if (ref.name) keys.add(ref.name);
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
return { keys, checked: !truncated, at: Math.min(pending.at, running.at) };
|
|
2202
|
+
}
|
|
2203
|
+
var BG_COVERAGE_MAX_PAGES = 2;
|
|
2204
|
+
var splitHistoryStates = {};
|
|
2205
|
+
var splitHistoryLocks = {};
|
|
2206
|
+
function freshSplitState() {
|
|
2207
|
+
return { bgBuffer: [], bgEnd: false, bgStarted: false, surfaceEnd: false, pendingSurface: null, surfaceCarry: [], lastSurfaceKeys: [], newestBgId: "" };
|
|
2208
|
+
}
|
|
2209
|
+
function noteBgIds(state, list) {
|
|
2210
|
+
for (const it of list) {
|
|
2211
|
+
const id = it && typeof it.id === "string" ? it.id : "";
|
|
2212
|
+
if (id && id > state.newestBgId) state.newestBgId = id;
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
function __resetSplitHistoryState(key) {
|
|
2216
|
+
if (key !== void 0) {
|
|
2217
|
+
delete splitHistoryStates[key];
|
|
2218
|
+
delete splitHistoryLocks[key];
|
|
2219
|
+
return;
|
|
2220
|
+
}
|
|
2221
|
+
for (const k in splitHistoryStates) delete splitHistoryStates[k];
|
|
2222
|
+
for (const k in splitHistoryLocks) delete splitHistoryLocks[k];
|
|
2223
|
+
}
|
|
2224
|
+
var createdOf = (it) => {
|
|
2225
|
+
const c = Number(it && it.created);
|
|
2226
|
+
return isFinite(c) && c > 0 ? c : NaN;
|
|
2227
|
+
};
|
|
2228
|
+
var oldestCreated = (lst) => {
|
|
2229
|
+
let m = Infinity;
|
|
2230
|
+
for (const it of lst) {
|
|
2231
|
+
const c = createdOf(it);
|
|
2232
|
+
if (!isNaN(c) && c < m) m = c;
|
|
2233
|
+
}
|
|
2234
|
+
return m;
|
|
2235
|
+
};
|
|
2236
|
+
var SURFACE_EMPTY_MAX_PAGES = 10;
|
|
2237
|
+
async function getSplitChatHistory(params, fetchOptions, _fetchImpl) {
|
|
2238
|
+
const key = [params.service, params.owner, params.platform, params.userId || ""].join("|");
|
|
2239
|
+
const prev = splitHistoryLocks[key] || Promise.resolve();
|
|
2240
|
+
let releaseLock;
|
|
2241
|
+
const lockTail = new Promise((r) => {
|
|
2242
|
+
releaseLock = r;
|
|
2243
|
+
});
|
|
2244
|
+
const run = () => _getSplitChatHistoryLocked(key, params, fetchOptions, releaseLock, _fetchImpl);
|
|
2245
|
+
const p = prev.then(run, run);
|
|
2246
|
+
p.then((res) => {
|
|
2247
|
+
if (!res || !res.bgPending) releaseLock();
|
|
2248
|
+
}, () => releaseLock());
|
|
2249
|
+
splitHistoryLocks[key] = p.then(() => lockTail, () => lockTail);
|
|
2250
|
+
return p;
|
|
2251
|
+
}
|
|
2252
|
+
async function _getSplitChatHistoryLocked(key, params, fetchOptions, releaseLock, _fetchImpl) {
|
|
2253
|
+
const fetch = _fetchImpl || getChatHistory;
|
|
2254
|
+
const bgQueue = bgIndexingQueueName(params.userId, params.service);
|
|
2255
|
+
const base = { service: params.service, owner: params.owner, platform: params.platform };
|
|
2256
|
+
const fetchMore = !!(fetchOptions && fetchOptions.fetchMore);
|
|
2257
|
+
const limit = fetchOptions && fetchOptions.limit;
|
|
2258
|
+
const firstLoad = !splitHistoryStates[key];
|
|
2259
|
+
let headRefresh = false;
|
|
2260
|
+
if (!splitHistoryStates[key]) {
|
|
2261
|
+
splitHistoryStates[key] = freshSplitState();
|
|
2262
|
+
} else if (!fetchMore) {
|
|
2263
|
+
const prev = splitHistoryStates[key];
|
|
2264
|
+
if (prev.surfaceEnd && prev.bgEnd) {
|
|
2265
|
+
headRefresh = true;
|
|
2266
|
+
prev.pendingSurface = null;
|
|
2267
|
+
prev.surfaceCarry = [];
|
|
2268
|
+
prev.bgBuffer = [];
|
|
2269
|
+
} else {
|
|
2270
|
+
splitHistoryStates[key] = freshSplitState();
|
|
2271
|
+
}
|
|
2272
|
+
}
|
|
2273
|
+
const state = splitHistoryStates[key];
|
|
2274
|
+
if (state.pendingSurface && state.pendingSurface.forFetchMore !== fetchMore) {
|
|
2275
|
+
state.pendingSurface = null;
|
|
2276
|
+
}
|
|
2277
|
+
if (!state.pendingSurface) {
|
|
2278
|
+
if (state.surfaceEnd && !headRefresh) {
|
|
2279
|
+
state.pendingSurface = { list: [], endOfList: true, startKeyHistory: state.lastSurfaceKeys, forFetchMore: fetchMore };
|
|
2280
|
+
} else {
|
|
2281
|
+
const sOpts = { fetchMore };
|
|
2282
|
+
if (limit) sOpts.limit = limit;
|
|
2283
|
+
let s = await fetch({ ...base, queue_exclude: bgQueue }, sOpts);
|
|
2284
|
+
let hops = 0;
|
|
2285
|
+
while (s && !s.endOfList && !(s.list || []).length && hops < SURFACE_EMPTY_MAX_PAGES) {
|
|
2286
|
+
hops++;
|
|
2287
|
+
const nOpts = { fetchMore: true };
|
|
2288
|
+
if (limit) nOpts.limit = limit;
|
|
2289
|
+
s = await fetch({ ...base, queue_exclude: bgQueue }, nOpts);
|
|
2290
|
+
}
|
|
2291
|
+
state.pendingSurface = {
|
|
2292
|
+
list: s && Array.isArray(s.list) ? s.list : [],
|
|
2293
|
+
endOfList: !!(s && s.endOfList),
|
|
2294
|
+
startKeyHistory: s && Array.isArray(s.startKeyHistory) ? s.startKeyHistory : [],
|
|
2295
|
+
forFetchMore: fetchMore
|
|
2296
|
+
};
|
|
2297
|
+
}
|
|
2298
|
+
}
|
|
2299
|
+
const surface = state.pendingSurface;
|
|
2300
|
+
if (fetchOptions && fetchOptions.deferBg && (!state.bgEnd || headRefresh)) {
|
|
2301
|
+
const surfaceList0 = state.surfaceCarry.length ? state.surfaceCarry.concat(surface.list) : surface.list.slice();
|
|
2302
|
+
state.surfaceCarry = [];
|
|
2303
|
+
const emitNow = surfaceList0.concat(state.bgBuffer);
|
|
2304
|
+
state.bgBuffer = [];
|
|
2305
|
+
if (!headRefresh) state.surfaceEnd = surface.endOfList;
|
|
2306
|
+
state.lastSurfaceKeys = surface.startKeyHistory;
|
|
2307
|
+
state.pendingSurface = null;
|
|
2308
|
+
const bgPending = (async () => {
|
|
2309
|
+
try {
|
|
2310
|
+
const batch = [];
|
|
2311
|
+
if (headRefresh) {
|
|
2312
|
+
const bOpts = { fetchMore: false };
|
|
2313
|
+
if (limit) bOpts.limit = limit;
|
|
2314
|
+
const b = await fetch({ ...base, queue: bgQueue, queue_exact: true, compact: true }, bOpts);
|
|
2315
|
+
const bList = b && Array.isArray(b.list) ? b.list : [];
|
|
2316
|
+
for (const it of bList) {
|
|
2317
|
+
if (it && typeof it === "object") it._fromBgChain = true;
|
|
2318
|
+
batch.push(it);
|
|
2319
|
+
}
|
|
2320
|
+
const prevNewest = state.newestBgId;
|
|
2321
|
+
noteBgIds(state, bList);
|
|
2322
|
+
if (prevNewest && !(b && b.endOfList) && !bList.some((it) => it && it.id === prevNewest)) {
|
|
2323
|
+
state.bgEnd = false;
|
|
2324
|
+
state.bgStarted = true;
|
|
2325
|
+
}
|
|
2326
|
+
} else {
|
|
2327
|
+
let hops = 0;
|
|
2328
|
+
while (!state.bgEnd && hops < BG_COVERAGE_MAX_PAGES) {
|
|
2329
|
+
hops++;
|
|
2330
|
+
const bOpts = { fetchMore: state.bgStarted };
|
|
2331
|
+
if (limit) bOpts.limit = limit;
|
|
2332
|
+
const b = await fetch({ ...base, queue: bgQueue, queue_exact: true, compact: true }, bOpts);
|
|
2333
|
+
state.bgStarted = true;
|
|
2334
|
+
const bList = b && Array.isArray(b.list) ? b.list : [];
|
|
2335
|
+
for (const it of bList) {
|
|
2336
|
+
if (it && typeof it === "object") it._fromBgChain = true;
|
|
2337
|
+
batch.push(it);
|
|
2338
|
+
}
|
|
2339
|
+
noteBgIds(state, bList);
|
|
2340
|
+
state.bgEnd = !!(b && b.endOfList);
|
|
2341
|
+
if (!bList.length && !state.bgEnd) break;
|
|
2342
|
+
if (state.bgEnd) break;
|
|
2343
|
+
}
|
|
2344
|
+
}
|
|
2345
|
+
return { list: batch, endOfList: state.surfaceEnd && state.bgEnd };
|
|
2346
|
+
} finally {
|
|
2347
|
+
releaseLock();
|
|
2348
|
+
}
|
|
2349
|
+
})();
|
|
2350
|
+
return {
|
|
2351
|
+
list: emitNow,
|
|
2352
|
+
// A head-refreshed ended chain KNOWS it is still ended — reporting
|
|
2353
|
+
// the hardcoded false here was what un-gated the fill loop on every
|
|
2354
|
+
// tab return. Mid-walk it computes to false exactly as before (this
|
|
2355
|
+
// branch is only entered with bgEnd false then); the bg batch still
|
|
2356
|
+
// carries the final word for that case.
|
|
2357
|
+
endOfList: state.surfaceEnd && state.bgEnd,
|
|
2358
|
+
startKeyHistory: surface.startKeyHistory,
|
|
2359
|
+
firstLoad,
|
|
2360
|
+
bgPending
|
|
2361
|
+
};
|
|
2362
|
+
}
|
|
2363
|
+
const surfaceList = state.surfaceCarry.length ? state.surfaceCarry.concat(surface.list) : surface.list.slice();
|
|
2364
|
+
const boundary = surface.endOfList ? -Infinity : oldestCreated(surfaceList);
|
|
2365
|
+
if (headRefresh) {
|
|
2366
|
+
const hOpts = { fetchMore: false };
|
|
2367
|
+
if (limit) hOpts.limit = limit;
|
|
2368
|
+
const hb = await fetch({ ...base, queue: bgQueue, queue_exact: true, compact: true }, hOpts);
|
|
2369
|
+
const hbList = hb && Array.isArray(hb.list) ? hb.list : [];
|
|
2370
|
+
for (const it of hbList) {
|
|
2371
|
+
if (it && typeof it === "object") it._fromBgChain = true;
|
|
2372
|
+
state.bgBuffer.push(it);
|
|
2373
|
+
}
|
|
2374
|
+
const prevNewestH = state.newestBgId;
|
|
2375
|
+
noteBgIds(state, hbList);
|
|
2376
|
+
if (prevNewestH && !(hb && hb.endOfList) && !hbList.some((it) => it && it.id === prevNewestH)) {
|
|
2377
|
+
state.bgEnd = false;
|
|
2378
|
+
state.bgStarted = true;
|
|
2379
|
+
}
|
|
2380
|
+
} else if (boundary !== Infinity || surface.endOfList) {
|
|
2381
|
+
let hops = 0;
|
|
2382
|
+
while (!state.bgEnd && hops < BG_COVERAGE_MAX_PAGES) {
|
|
2383
|
+
const bufOldest = state.bgBuffer.length ? oldestCreated(state.bgBuffer) : Infinity;
|
|
2384
|
+
if (state.bgBuffer.length && bufOldest <= boundary) break;
|
|
2385
|
+
hops++;
|
|
2386
|
+
const bOpts = { fetchMore: state.bgStarted };
|
|
2387
|
+
if (limit) bOpts.limit = limit;
|
|
2388
|
+
const b = await fetch({ ...base, queue: bgQueue, queue_exact: true, compact: true }, bOpts);
|
|
2389
|
+
state.bgStarted = true;
|
|
2390
|
+
const bList = b && Array.isArray(b.list) ? b.list : [];
|
|
2391
|
+
for (const it of bList) {
|
|
2392
|
+
if (it && typeof it === "object") it._fromBgChain = true;
|
|
2393
|
+
state.bgBuffer.push(it);
|
|
2394
|
+
}
|
|
2395
|
+
noteBgIds(state, bList);
|
|
2396
|
+
state.bgEnd = !!(b && b.endOfList);
|
|
2397
|
+
if (!bList.length && !state.bgEnd) break;
|
|
2398
|
+
if (state.bgEnd) break;
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
const emitSurface = surfaceList;
|
|
2402
|
+
state.surfaceCarry = [];
|
|
2403
|
+
const emitBg = state.bgBuffer;
|
|
2404
|
+
state.bgBuffer = [];
|
|
2405
|
+
const seen = {};
|
|
2406
|
+
for (const it of emitSurface) {
|
|
2407
|
+
if (it && typeof it.id === "string") seen[it.id] = true;
|
|
2408
|
+
}
|
|
2409
|
+
const merged = emitSurface.concat(emitBg.filter((it) => !(it && typeof it.id === "string" && seen[it.id])));
|
|
2410
|
+
if (!headRefresh) state.surfaceEnd = surface.endOfList;
|
|
2411
|
+
state.lastSurfaceKeys = surface.startKeyHistory;
|
|
2412
|
+
state.pendingSurface = null;
|
|
2413
|
+
return {
|
|
2414
|
+
list: merged,
|
|
2415
|
+
endOfList: state.surfaceEnd && state.bgEnd && state.bgBuffer.length === 0 && state.surfaceCarry.length === 0,
|
|
2416
|
+
// Bookkeeping only (both the consumers and the SDK treat it opaquely);
|
|
2417
|
+
// the real cursors are the SDK's internal ones plus this module's state.
|
|
2418
|
+
startKeyHistory: surface.startKeyHistory,
|
|
2419
|
+
firstLoad
|
|
2420
|
+
};
|
|
2421
|
+
}
|
|
1950
2422
|
function mapHistoryListToMessages(list, platform, opts) {
|
|
1951
2423
|
var mapped = [], runningItemIds = [];
|
|
1952
2424
|
var extractAssistantText = platform === "openai" ? extractOpenAIText : extractClaudeText;
|
|
@@ -1959,10 +2431,11 @@ function mapHistoryListToMessages(list, platform, opts) {
|
|
|
1959
2431
|
var isPending = isInProcess || isQueued;
|
|
1960
2432
|
var isFailed = item && item.status === "failed";
|
|
1961
2433
|
var response = isFailed ? item.error != null ? item.error : item.response_body : item && item.response_body != null ? item.response_body : item && item.error;
|
|
1962
|
-
var
|
|
1963
|
-
var
|
|
1964
|
-
var
|
|
1965
|
-
var
|
|
2434
|
+
var isCompact = !!(item && item.compact);
|
|
2435
|
+
var userText = isCompact ? typeof item.request_text === "string" ? item.request_text : "" : extractLastUserTextFromRequest(requestBody);
|
|
2436
|
+
var assistantText = isPending ? "" : isCompact ? (typeof item.response_text === "string" ? item.response_text : "").trim() : (extractAssistantText(response) || "").trim() || "";
|
|
2437
|
+
var isErrorResponse = !isPending && (isFailed || !isCompact && isErrorResponseBody(response));
|
|
2438
|
+
var reportedComplete = !!(item && item._isBgTask) && !isErrorResponse && (isCompact ? item.response_complete_marker === true : !!assistantText && assistantText.indexOf(INDEXING_COMPLETE_MARKER) !== -1);
|
|
1966
2439
|
if (reportedComplete) assistantText = assistantText.split(INDEXING_COMPLETE_MARKER).join("").trim();
|
|
1967
2440
|
var serverItemId = item && typeof item.id === "string" && item.id ? item.id : void 0;
|
|
1968
2441
|
var createdTs = Number(item && item.created);
|
|
@@ -1991,9 +2464,11 @@ function mapHistoryListToMessages(list, platform, opts) {
|
|
|
1991
2464
|
displayContent = sanitizeAttachmentLinksForHistory(userText, opts.projectId);
|
|
1992
2465
|
}
|
|
1993
2466
|
var userMsg = { role: "user", content: displayContent };
|
|
2467
|
+
if (item._fromBgChain) userMsg._fromBgChain = true;
|
|
1994
2468
|
if (isInProcess) userMsg.isPendingInProcess = true;
|
|
1995
2469
|
if (isQueued) userMsg.isPendingQueued = true;
|
|
1996
2470
|
if (isCancelledItem) userMsg.isCancelled = true;
|
|
2471
|
+
if (isCompact) userMsg._compact = true;
|
|
1997
2472
|
if (item._isBgTask) userMsg.isBackgroundTask = true;
|
|
1998
2473
|
if (indexFile) userMsg._indexFile = indexFile;
|
|
1999
2474
|
if (item._isOnBgQueue) userMsg._useBgQueue = true;
|
|
@@ -2003,6 +2478,8 @@ function mapHistoryListToMessages(list, platform, opts) {
|
|
|
2003
2478
|
}
|
|
2004
2479
|
if (isCancelledItem) ; else if (isInProcess) {
|
|
2005
2480
|
var ph = { role: "assistant", content: "", isPending: true, isPendingInProcess: true };
|
|
2481
|
+
if (userTs !== void 0) ph._ts = userTs;
|
|
2482
|
+
if (item._fromBgChain) ph._fromBgChain = true;
|
|
2006
2483
|
if (item._isBgTask) ph.isBackgroundTask = true;
|
|
2007
2484
|
if (serverItemId !== void 0) {
|
|
2008
2485
|
ph._serverItemId = serverItemId;
|
|
@@ -2011,19 +2488,26 @@ function mapHistoryListToMessages(list, platform, opts) {
|
|
|
2011
2488
|
mapped.push(ph);
|
|
2012
2489
|
} else if (isQueued) ; else if (isErrorResponse) {
|
|
2013
2490
|
var em = { role: "assistant", content: getErrorMessage(response), isError: true };
|
|
2491
|
+
if (item._fromBgChain) em._fromBgChain = true;
|
|
2014
2492
|
if (item._isBgTask) em.isBackgroundTask = true;
|
|
2015
2493
|
if (serverItemId !== void 0) em._serverItemId = serverItemId;
|
|
2016
2494
|
if (replyTs !== void 0) em._ts = replyTs;
|
|
2017
2495
|
mapped.push(em);
|
|
2018
2496
|
} else if (assistantText || reportedComplete) {
|
|
2019
2497
|
var okm = { role: "assistant", content: sanitizeAttachmentLinksForHistory(assistantText, opts.projectId, true) || EMPTY_INDEXING_REPLY };
|
|
2498
|
+
if (item._fromBgChain) okm._fromBgChain = true;
|
|
2020
2499
|
if (item._isBgTask) okm.isBackgroundTask = true;
|
|
2500
|
+
if (isCompact) okm._compact = true;
|
|
2021
2501
|
if (serverItemId !== void 0) okm._serverItemId = serverItemId;
|
|
2022
2502
|
if (replyTs !== void 0) okm._ts = replyTs;
|
|
2023
2503
|
if (reportedComplete) okm._indexComplete = true;
|
|
2024
2504
|
mapped.push(okm);
|
|
2025
2505
|
}
|
|
2026
2506
|
});
|
|
2507
|
+
if (opts.projectId) {
|
|
2508
|
+
var ownerKey = opts.projectId + "#" + platform;
|
|
2509
|
+
for (var oi = 0; oi < mapped.length; oi++) mapped[oi]._ownerKey = ownerKey;
|
|
2510
|
+
}
|
|
2027
2511
|
return { messages: mapped, runningItemIds };
|
|
2028
2512
|
}
|
|
2029
2513
|
|
|
@@ -2141,6 +2625,7 @@ var INDEXING_DRAIN_BUSY_POLL_MS = 8e3;
|
|
|
2141
2625
|
var INDEXING_DRAIN_CONFIRM_POLL_MS = 3e3;
|
|
2142
2626
|
var INDEXING_DRAIN_IDLE_LOOKS = 2;
|
|
2143
2627
|
var INDEXING_DRAIN_MIN_MS = 8e3;
|
|
2628
|
+
var _bgHistoryBatchSeq = 0;
|
|
2144
2629
|
var INDEXING_DRAIN_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
2145
2630
|
var INDEXING_DRAIN_LOOK_TIMEOUT_MS = 45e3;
|
|
2146
2631
|
var INDEXING_DRAIN_NUDGE_MIN_GAP_MS = 1500;
|
|
@@ -2162,6 +2647,14 @@ function isPollStopped(res) {
|
|
|
2162
2647
|
}
|
|
2163
2648
|
var ChatSession = class {
|
|
2164
2649
|
constructor(host) {
|
|
2650
|
+
// ─── compact-stub hydration ─────────────────────────────────────────────
|
|
2651
|
+
// Split-fetch bg pages arrive as label stubs (no bodies). When the user
|
|
2652
|
+
// expands a row, the real reply text is fetched per item (csr-poll point
|
|
2653
|
+
// lookup) and MEMOIZED per chat: every later remap (first-page refresh,
|
|
2654
|
+
// queue-detect tick, cache restore) re-applies the memo, so a hydrated
|
|
2655
|
+
// bubble can never silently revert to its 200-char head.
|
|
2656
|
+
this._hydratedBodies = {};
|
|
2657
|
+
this._hydratingItems = {};
|
|
2165
2658
|
this.typewriterQueue = Promise.resolve();
|
|
2166
2659
|
/**
|
|
2167
2660
|
* Pick up indexing passes the WORKER minted, which no client ever dispatched.
|
|
@@ -2202,6 +2695,9 @@ var ChatSession = class {
|
|
|
2202
2695
|
typingAbort: false,
|
|
2203
2696
|
loadingHistory: false,
|
|
2204
2697
|
loadingOlderHistory: false,
|
|
2698
|
+
// A deferred bg stub batch (first-paint split) is still in flight; the
|
|
2699
|
+
// views show a small 'loading indexing history' hint while true.
|
|
2700
|
+
bgHistoryLoading: false,
|
|
2205
2701
|
historyEndOfList: false,
|
|
2206
2702
|
historyStartKeyHistory: [],
|
|
2207
2703
|
historyRequestToken: 0,
|
|
@@ -2351,10 +2847,12 @@ var ChatSession = class {
|
|
|
2351
2847
|
}
|
|
2352
2848
|
var queue = bgIndexingQueueName(id.userId, id.projectId);
|
|
2353
2849
|
var ask = function(status) {
|
|
2354
|
-
return Promise.resolve(
|
|
2355
|
-
{ service: id.projectId, owner: id.owner, platform, queue, status },
|
|
2356
|
-
{
|
|
2357
|
-
)).
|
|
2850
|
+
return Promise.resolve(probeBgQueue(
|
|
2851
|
+
{ service: id.projectId, owner: id.owner, platform, queue, status, limit: WORKER_PASS_ADOPT_LIMIT },
|
|
2852
|
+
{ maxAgeMs: BG_PROBE_TTL_MS }
|
|
2853
|
+
)).then(function(entry) {
|
|
2854
|
+
return entry.result;
|
|
2855
|
+
}).catch(function() {
|
|
2358
2856
|
return null;
|
|
2359
2857
|
});
|
|
2360
2858
|
};
|
|
@@ -2446,7 +2944,7 @@ var ChatSession = class {
|
|
|
2446
2944
|
* instead of merely unconfirmed.
|
|
2447
2945
|
*/
|
|
2448
2946
|
refreshLiveIndexState() {
|
|
2449
|
-
this._adoptWorkerIndexingPasses(0);
|
|
2947
|
+
this._adoptWorkerIndexingPasses(0, true);
|
|
2450
2948
|
}
|
|
2451
2949
|
/** Forget what we know about which files are indexing — but ONLY when the
|
|
2452
2950
|
* snapshot was taken for a different chat than the one on screen now. For a
|
|
@@ -2623,6 +3121,66 @@ var ChatSession = class {
|
|
|
2623
3121
|
if (!id.projectId || id.platform === "none") return "";
|
|
2624
3122
|
return id.projectId + "#" + id.platform;
|
|
2625
3123
|
}
|
|
3124
|
+
/** Re-apply memoized hydrated texts onto freshly-mapped messages. Both
|
|
3125
|
+
* clients call this right after their mapper runs (loadHistory does it
|
|
3126
|
+
* internally); it mutates the given array's items in place. */
|
|
3127
|
+
applyHydratedBodies(messages) {
|
|
3128
|
+
var key = this.getHistoryCacheKey();
|
|
3129
|
+
var memo = key ? this._hydratedBodies[key] : null;
|
|
3130
|
+
if (!memo) return;
|
|
3131
|
+
var id = this.host.getIdentity();
|
|
3132
|
+
for (var i = 0; i < messages.length; i++) {
|
|
3133
|
+
var m = messages[i];
|
|
3134
|
+
if (!m || !m._compact || m.role !== "assistant" || !m._serverItemId) continue;
|
|
3135
|
+
var text = memo[m._serverItemId];
|
|
3136
|
+
if (typeof text !== "string") continue;
|
|
3137
|
+
m.content = sanitizeAttachmentLinksForHistory(text, id.projectId, true) || EMPTY_INDEXING_REPLY;
|
|
3138
|
+
delete m._compact;
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
/** Fetch the real response bodies for compact history stubs (one csr-poll
|
|
3142
|
+
* point lookup per item id), memoize, and swap them into the live list.
|
|
3143
|
+
* Best-effort: a failed lookup leaves the stub (its head + fallback line
|
|
3144
|
+
* still render) and a later expand retries. */
|
|
3145
|
+
hydrateCompactItems(itemIds) {
|
|
3146
|
+
var self = this;
|
|
3147
|
+
var lookup = chatEngineConfig().csrHistoryItemLookup;
|
|
3148
|
+
if (!lookup || !itemIds || !itemIds.length) return Promise.resolve();
|
|
3149
|
+
var id = this.host.getIdentity();
|
|
3150
|
+
var platform = id.platform;
|
|
3151
|
+
if (!id.projectId || platform !== "claude" && platform !== "openai") return Promise.resolve();
|
|
3152
|
+
var chatKey = this.getHistoryCacheKey();
|
|
3153
|
+
if (!chatKey) return Promise.resolve();
|
|
3154
|
+
var jobs = itemIds.map(function(itemId) {
|
|
3155
|
+
if (!itemId) return Promise.resolve();
|
|
3156
|
+
var already = self._hydratedBodies[chatKey] && self._hydratedBodies[chatKey][itemId] !== void 0;
|
|
3157
|
+
var inflightKey = chatKey + "|" + itemId;
|
|
3158
|
+
if (already || self._hydratingItems[inflightKey]) return Promise.resolve();
|
|
3159
|
+
self._hydratingItems[inflightKey] = true;
|
|
3160
|
+
return Promise.resolve(lookup(buildHistoryItemFullId(platform, id.projectId, itemId), id.projectId, id.owner)).then(function(body) {
|
|
3161
|
+
var text = ((platform === "openai" ? extractOpenAIText(body) : extractClaudeText(body)) || "").trim();
|
|
3162
|
+
if (text.indexOf(INDEXING_COMPLETE_MARKER) !== -1) text = text.split(INDEXING_COMPLETE_MARKER).join("").trim();
|
|
3163
|
+
if (!self._hydratedBodies[chatKey]) self._hydratedBodies[chatKey] = {};
|
|
3164
|
+
self._hydratedBodies[chatKey][itemId] = text;
|
|
3165
|
+
if (self.getHistoryCacheKey() !== chatKey) return;
|
|
3166
|
+
for (var i = 0; i < self.state.messages.length; i++) {
|
|
3167
|
+
var m = self.state.messages[i];
|
|
3168
|
+
if (m && m._compact && m.role === "assistant" && m._serverItemId === itemId) {
|
|
3169
|
+
m.content = sanitizeAttachmentLinksForHistory(text, id.projectId, true) || EMPTY_INDEXING_REPLY;
|
|
3170
|
+
delete m._compact;
|
|
3171
|
+
}
|
|
3172
|
+
}
|
|
3173
|
+
}).catch(function() {
|
|
3174
|
+
}).then(function() {
|
|
3175
|
+
delete self._hydratingItems[inflightKey];
|
|
3176
|
+
});
|
|
3177
|
+
});
|
|
3178
|
+
return Promise.all(jobs).then(function() {
|
|
3179
|
+
if (self.getHistoryCacheKey() !== chatKey) return;
|
|
3180
|
+
self.host.notify();
|
|
3181
|
+
self.updateHistoryCache();
|
|
3182
|
+
});
|
|
3183
|
+
}
|
|
2626
3184
|
updateHistoryCache() {
|
|
2627
3185
|
var key = this.getHistoryCacheKey();
|
|
2628
3186
|
if (!key) return;
|
|
@@ -2945,11 +3503,11 @@ var ChatSession = class {
|
|
|
2945
3503
|
bail = setTimeout(function() {
|
|
2946
3504
|
settle(null);
|
|
2947
3505
|
}, INDEXING_DRAIN_LOOK_TIMEOUT_MS);
|
|
2948
|
-
Promise.resolve(
|
|
2949
|
-
{ service: svcId, owner, platform, queue, status },
|
|
2950
|
-
{
|
|
2951
|
-
)).then(function(
|
|
2952
|
-
settle(
|
|
3506
|
+
Promise.resolve(probeBgQueue(
|
|
3507
|
+
{ service: svcId, owner, platform, queue, status, limit: WORKER_PASS_ADOPT_LIMIT },
|
|
3508
|
+
{ maxAgeMs: 0 }
|
|
3509
|
+
)).then(function(entry) {
|
|
3510
|
+
settle(entry.result);
|
|
2953
3511
|
}, function() {
|
|
2954
3512
|
settle(null);
|
|
2955
3513
|
});
|
|
@@ -3576,6 +4134,32 @@ var ChatSession = class {
|
|
|
3576
4134
|
if (e && e.id && self._indexKeyOf(e) === scoped) stoppedIds[e.id] = true;
|
|
3577
4135
|
});
|
|
3578
4136
|
this.state.stoppedIndexIds = stoppedIds;
|
|
4137
|
+
var runPath = group.path || "";
|
|
4138
|
+
if (!runPath) {
|
|
4139
|
+
(group.members || []).some(function(m) {
|
|
4140
|
+
var p = m && m.msg && m.msg._indexFile && m.msg._indexFile.path;
|
|
4141
|
+
if (p) {
|
|
4142
|
+
runPath = p;
|
|
4143
|
+
return true;
|
|
4144
|
+
}
|
|
4145
|
+
return false;
|
|
4146
|
+
});
|
|
4147
|
+
}
|
|
4148
|
+
if (!runPath) {
|
|
4149
|
+
this.bgTaskQueue.some(function(e) {
|
|
4150
|
+
if (e && e.storagePath && self._indexKeyOf(e) === scoped) {
|
|
4151
|
+
runPath = e.storagePath;
|
|
4152
|
+
return true;
|
|
4153
|
+
}
|
|
4154
|
+
return false;
|
|
4155
|
+
});
|
|
4156
|
+
}
|
|
4157
|
+
if (runPath) {
|
|
4158
|
+
var ident = this.host.getIdentity();
|
|
4159
|
+
if (ident && ident.projectId) {
|
|
4160
|
+
upsertIndexRunRecordSafe(ident.projectId, runPath, { status: "cancelled", finished: Date.now() });
|
|
4161
|
+
}
|
|
4162
|
+
}
|
|
3579
4163
|
}
|
|
3580
4164
|
this._adoptWorkerIndexingPasses(0);
|
|
3581
4165
|
var ids = group.cancellableIds || [];
|
|
@@ -4089,7 +4673,7 @@ var ChatSession = class {
|
|
|
4089
4673
|
if (isImageVisionFile(filename, mime)) return true;
|
|
4090
4674
|
return windowedIndexingEnabled() && isWindowedReadFile(filename, mime);
|
|
4091
4675
|
}
|
|
4092
|
-
_adoptWorkerIndexingPasses(attempt) {
|
|
4676
|
+
_adoptWorkerIndexingPasses(attempt, passive) {
|
|
4093
4677
|
var self = this;
|
|
4094
4678
|
if (this._adoptingWorkerPasses) return;
|
|
4095
4679
|
var id = this.host.getIdentity();
|
|
@@ -4099,10 +4683,12 @@ var ChatSession = class {
|
|
|
4099
4683
|
var svcId = id.projectId, owner = id.owner;
|
|
4100
4684
|
var queue = bgIndexingQueueName(id.userId, id.projectId);
|
|
4101
4685
|
var ask = function(status) {
|
|
4102
|
-
return Promise.resolve(
|
|
4103
|
-
{ service: svcId, owner, platform, queue, status },
|
|
4104
|
-
{
|
|
4105
|
-
)).
|
|
4686
|
+
return Promise.resolve(probeBgQueue(
|
|
4687
|
+
{ service: svcId, owner, platform, queue, status, limit: WORKER_PASS_ADOPT_LIMIT },
|
|
4688
|
+
{ maxAgeMs: 0 }
|
|
4689
|
+
)).then(function(entry) {
|
|
4690
|
+
return entry.result;
|
|
4691
|
+
}).catch(function() {
|
|
4106
4692
|
return null;
|
|
4107
4693
|
});
|
|
4108
4694
|
};
|
|
@@ -4124,6 +4710,7 @@ var ChatSession = class {
|
|
|
4124
4710
|
self.drainBgTaskQueue();
|
|
4125
4711
|
if (self._isTrackingAny(adoptedIds)) return;
|
|
4126
4712
|
}
|
|
4713
|
+
if (passive && !self._hasLiveIndexEvidence(svcId)) return;
|
|
4127
4714
|
if (attempt + 1 >= WORKER_PASS_ADOPT_ATTEMPTS.length) {
|
|
4128
4715
|
self._nudgeIndexingDrain();
|
|
4129
4716
|
return;
|
|
@@ -4132,12 +4719,30 @@ var ChatSession = class {
|
|
|
4132
4719
|
var later = self.host.getIdentity();
|
|
4133
4720
|
if (later.projectId !== svcId || later.platform !== platform) return;
|
|
4134
4721
|
if (self.isPollingPaused() || !self.host.isViewMounted()) return;
|
|
4135
|
-
self._adoptWorkerIndexingPasses(attempt + 1);
|
|
4722
|
+
self._adoptWorkerIndexingPasses(attempt + 1, passive);
|
|
4136
4723
|
}, WORKER_PASS_ADOPT_ATTEMPTS[attempt + 1]);
|
|
4137
4724
|
}, function() {
|
|
4138
4725
|
self._adoptingWorkerPasses = false;
|
|
4139
4726
|
});
|
|
4140
4727
|
}
|
|
4728
|
+
/** Anything at all suggesting THIS project's indexing may be live: a queued
|
|
4729
|
+
* local entry, a recorded live key (the adopt look just wrote them), or an
|
|
4730
|
+
* attached poll. Gates the passive adopt ladder's climb. */
|
|
4731
|
+
_hasLiveIndexEvidence(svcId) {
|
|
4732
|
+
for (var i = 0; i < this.bgTaskQueue.length; i++) {
|
|
4733
|
+
var e = this.bgTaskQueue[i];
|
|
4734
|
+
if (e && e.projectId === svcId) return true;
|
|
4735
|
+
}
|
|
4736
|
+
var keys = this.state.liveIndexKeys || {};
|
|
4737
|
+
for (var k in keys) {
|
|
4738
|
+
if (keys[k]) return true;
|
|
4739
|
+
}
|
|
4740
|
+
var found = false;
|
|
4741
|
+
this.historyItemPolls.forEach(function(h) {
|
|
4742
|
+
if (h && h.kind === "bg") found = true;
|
|
4743
|
+
});
|
|
4744
|
+
return found;
|
|
4745
|
+
}
|
|
4141
4746
|
/** Any of these ids still queued or still polled, i.e. surviving work. */
|
|
4142
4747
|
_isTrackingAny(ids) {
|
|
4143
4748
|
for (var i = 0; i < ids.length; i++) {
|
|
@@ -4228,7 +4833,10 @@ var ChatSession = class {
|
|
|
4228
4833
|
for (var i = this.bgTaskQueue.length - 1; i >= 0; i--) {
|
|
4229
4834
|
var e = this.bgTaskQueue[i];
|
|
4230
4835
|
if (e.projectId !== svcId || e.platform !== plat) continue;
|
|
4231
|
-
if (presentIds[e.id] && !pendingIds[e.id])
|
|
4836
|
+
if (presentIds[e.id] && !pendingIds[e.id]) {
|
|
4837
|
+
this._flipRunFromSettledEntry(e);
|
|
4838
|
+
this.bgTaskQueue.splice(i, 1);
|
|
4839
|
+
}
|
|
4232
4840
|
}
|
|
4233
4841
|
var bgPollBudget = MAX_CONCURRENT_BG_POLLS - this._countBgPolls();
|
|
4234
4842
|
var injectedAny = false;
|
|
@@ -4302,6 +4910,8 @@ var ChatSession = class {
|
|
|
4302
4910
|
self.host.notify();
|
|
4303
4911
|
self.updateHistoryCache();
|
|
4304
4912
|
if (!self._isWorkerDrivenIndexing(capturedEntry.filename, capturedEntry.mime)) {
|
|
4913
|
+
if (isNotExists) self._flipRunRecord(capturedEntry, "cancelled");
|
|
4914
|
+
else self._flipRunRecord(capturedEntry, "error", self._runErrorText(err));
|
|
4305
4915
|
self._nudgeIndexingDrain();
|
|
4306
4916
|
}
|
|
4307
4917
|
}).then(function() {
|
|
@@ -4333,6 +4943,74 @@ var ChatSession = class {
|
|
|
4333
4943
|
// memory (a reload or a closed tab ended it), and it stopped whenever the model claimed
|
|
4334
4944
|
// completion, which on an 88-page file happened at page 15. Continuing to dispatch here
|
|
4335
4945
|
// as well would now double-index every window.
|
|
4946
|
+
/** Fire the consumer's done::-marker hook for a run whose completion this
|
|
4947
|
+
* client knows DETERMINISTICALLY (see the two call sites in
|
|
4948
|
+
* maybeResumeIndexing). Best-effort by contract; identity-checked so a
|
|
4949
|
+
* project switch mid-settle cannot stamp the wrong service. */
|
|
4950
|
+
_mintDoneMarker(entry) {
|
|
4951
|
+
try {
|
|
4952
|
+
var mint = chatEngineConfig().mintIndexDoneMarker;
|
|
4953
|
+
if (!mint || !entry || !entry.storagePath || !entry.projectId) return;
|
|
4954
|
+
var id = this.host.getIdentity();
|
|
4955
|
+
if (!id || id.projectId !== entry.projectId) return;
|
|
4956
|
+
mint({ service: entry.projectId, storagePath: entry.storagePath });
|
|
4957
|
+
} catch (_e) {
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
/** Short, storable form of an error body for the run:: record. */
|
|
4961
|
+
_runErrorText(response) {
|
|
4962
|
+
var msg = "";
|
|
4963
|
+
try {
|
|
4964
|
+
msg = String(getErrorMessage(response) || "");
|
|
4965
|
+
} catch (_e) {
|
|
4966
|
+
}
|
|
4967
|
+
msg = msg.replace(/\s+/g, " ").trim();
|
|
4968
|
+
return msg ? msg.slice(0, 300) : "Indexing failed.";
|
|
4969
|
+
}
|
|
4970
|
+
/** Close the records of a run whose pass settled OFF-POLL — the answer came
|
|
4971
|
+
* back as history (hidden tab, dead poll, resume refetch), so none of the
|
|
4972
|
+
* poll-side settle handlers ran. Only for SINGLE-PASS files, where one
|
|
4973
|
+
* settled pass is deterministically the whole run (the same contract as
|
|
4974
|
+
* maybeResumeIndexing's single-pass branch); paged files stay with their
|
|
4975
|
+
* drivers. Outcome is read from the settled bubbles' own flags, which is
|
|
4976
|
+
* all the history mapping left us. Best-effort and idempotent throughout. */
|
|
4977
|
+
_flipRunFromSettledEntry(entry) {
|
|
4978
|
+
try {
|
|
4979
|
+
if (!entry || !entry.storagePath || !entry.id || !entry.projectId) return;
|
|
4980
|
+
if (isPagedReadFile(entry.filename, entry.mime)) return;
|
|
4981
|
+
if (this.cancelledIndexKeys.has(this._indexKeyOf(entry))) return;
|
|
4982
|
+
if (this.state.stoppedIndexIds[entry.id]) return;
|
|
4983
|
+
var userMsg = null, replyMsg = null;
|
|
4984
|
+
this.state.messages.forEach(function(m) {
|
|
4985
|
+
if (m._serverItemId !== entry.id) return;
|
|
4986
|
+
if (m.role === "user") {
|
|
4987
|
+
if (!userMsg) userMsg = m;
|
|
4988
|
+
} else if (!replyMsg) replyMsg = m;
|
|
4989
|
+
});
|
|
4990
|
+
if (userMsg && userMsg.isCancelled || replyMsg && replyMsg.isCancelled) {
|
|
4991
|
+
this._flipRunRecord(entry, "cancelled");
|
|
4992
|
+
} else if (replyMsg && replyMsg.isError) {
|
|
4993
|
+
var errText = typeof replyMsg.content === "string" ? replyMsg.content.replace(/\s+/g, " ").trim().slice(0, 300) : "";
|
|
4994
|
+
this._flipRunRecord(entry, "error", errText || "Indexing failed.");
|
|
4995
|
+
} else if (replyMsg) {
|
|
4996
|
+
this._mintDoneMarker(entry);
|
|
4997
|
+
this._flipRunRecord(entry, "done");
|
|
4998
|
+
}
|
|
4999
|
+
} catch (_e) {
|
|
5000
|
+
}
|
|
5001
|
+
}
|
|
5002
|
+
/** Close the durable run:: record for an ending THIS client observed.
|
|
5003
|
+
* service comes from the ENTRY, not the current identity: unlike the done::
|
|
5004
|
+
* mint above, a status flip must land even if the user switched projects
|
|
5005
|
+
* mid-settle — otherwise the record lies 'working' forever. Best-effort
|
|
5006
|
+
* through upsertIndexRunRecordSafe; the consumer's precedence guard keeps
|
|
5007
|
+
* repeats and races harmless. */
|
|
5008
|
+
_flipRunRecord(entry, status, error) {
|
|
5009
|
+
if (!entry || !entry.storagePath || !entry.projectId) return;
|
|
5010
|
+
var patch = { status, finished: Date.now() };
|
|
5011
|
+
if (error) patch.error = error;
|
|
5012
|
+
upsertIndexRunRecordSafe(entry.projectId, entry.storagePath, patch);
|
|
5013
|
+
}
|
|
4336
5014
|
maybeResumeIndexing(entry, response, platform) {
|
|
4337
5015
|
var self = this;
|
|
4338
5016
|
var endOfClientChain = function() {
|
|
@@ -4342,27 +5020,43 @@ var ChatSession = class {
|
|
|
4342
5020
|
if (!entry || !entry.storagePath) return;
|
|
4343
5021
|
if (this.cancelledIndexKeys.has(this._indexKeyOf(entry))) return;
|
|
4344
5022
|
if (!isPagedReadFile(entry.filename, entry.mime)) {
|
|
5023
|
+
if (!isErrorResponseBody(response) && !this._isCancelledPollResult(response)) {
|
|
5024
|
+
this._mintDoneMarker(entry);
|
|
5025
|
+
this._flipRunRecord(entry, "done");
|
|
5026
|
+
} else if (this._isCancelledPollResult(response)) {
|
|
5027
|
+
this._flipRunRecord(entry, "cancelled");
|
|
5028
|
+
} else {
|
|
5029
|
+
this._flipRunRecord(entry, "error", this._runErrorText(response));
|
|
5030
|
+
}
|
|
4345
5031
|
endOfClientChain();
|
|
4346
5032
|
return;
|
|
4347
5033
|
}
|
|
4348
5034
|
if (isImageVisionFile(entry.filename, entry.mime)) return;
|
|
4349
5035
|
if (windowedIndexingEnabled() && isWindowedReadFile(entry.filename, entry.mime)) return;
|
|
4350
5036
|
if (isErrorResponseBody(response)) {
|
|
5037
|
+
this._flipRunRecord(entry, "error", this._runErrorText(response));
|
|
4351
5038
|
endOfClientChain();
|
|
4352
5039
|
return;
|
|
4353
5040
|
}
|
|
4354
5041
|
var answer = (platform === "openai" ? extractOpenAIText(response) : extractClaudeText(response)) || "";
|
|
4355
5042
|
if (answer.indexOf(INDEXING_COMPLETE_MARKER) !== -1) {
|
|
5043
|
+
this._mintDoneMarker(entry);
|
|
5044
|
+
this._flipRunRecord(entry, "done");
|
|
4356
5045
|
endOfClientChain();
|
|
4357
5046
|
return;
|
|
4358
5047
|
}
|
|
4359
5048
|
var pass = (entry.resumePass || 0) + 1;
|
|
4360
5049
|
if (pass > MAX_INDEXING_RESUME_PASSES) {
|
|
5050
|
+
this._flipRunRecord(entry, "error", "Stopped after " + MAX_INDEXING_RESUME_PASSES + " passes without finishing.");
|
|
4361
5051
|
endOfClientChain();
|
|
4362
5052
|
return;
|
|
4363
5053
|
}
|
|
4364
5054
|
var id = this.host.getIdentity();
|
|
4365
|
-
if (!id || id.platform === "none" || id.projectId !== entry.projectId)
|
|
5055
|
+
if (!id || id.platform === "none" || id.projectId !== entry.projectId) {
|
|
5056
|
+
this._flipRunRecord(entry, "error", "Indexing stopped: the session or project changed before the file finished.");
|
|
5057
|
+
endOfClientChain();
|
|
5058
|
+
return;
|
|
5059
|
+
}
|
|
4366
5060
|
this.trackIndexDispatch(notifyAgentContinueIndexing({
|
|
4367
5061
|
platform: id.platform,
|
|
4368
5062
|
model: id.model,
|
|
@@ -4439,8 +5133,9 @@ var ChatSession = class {
|
|
|
4439
5133
|
var projectId = id.projectId, owner = id.owner;
|
|
4440
5134
|
var options = { fetchMore };
|
|
4441
5135
|
if (fetchMore && this.state.historyStartKeyHistory.length) options.startKeyHistory = this.state.historyStartKeyHistory.slice();
|
|
5136
|
+
if (!fetchMore) options.deferBg = true;
|
|
4442
5137
|
var fetchHistory = function() {
|
|
4443
|
-
return
|
|
5138
|
+
return getSplitChatHistory({ service: projectId, owner, platform, userId: id.userId }, options);
|
|
4444
5139
|
};
|
|
4445
5140
|
return Promise.resolve().then(fetchHistory).catch(function(err) {
|
|
4446
5141
|
if (isAuthExpiredError(err) && !isNonRetryableRequestError(err)) return self.host.refreshSession().then(fetchHistory);
|
|
@@ -4450,7 +5145,8 @@ var ChatSession = class {
|
|
|
4450
5145
|
var chatList = history && Array.isArray(history.list) ? history.list : [];
|
|
4451
5146
|
chatList.forEach(function(item) {
|
|
4452
5147
|
if (isBgIndexingQueue(item.queue_name)) {
|
|
4453
|
-
|
|
5148
|
+
var clsText = item.compact ? item.request_text : extractLastUserTextFromRequest(item.request_body);
|
|
5149
|
+
if (isIndexingRequestText(clsText)) item._isBgTask = true;
|
|
4454
5150
|
else item._isOnBgQueue = true;
|
|
4455
5151
|
}
|
|
4456
5152
|
});
|
|
@@ -4463,15 +5159,55 @@ var ChatSession = class {
|
|
|
4463
5159
|
projectId: id.projectId,
|
|
4464
5160
|
formatIndexingLabel: self.host.formatIndexingLabel
|
|
4465
5161
|
}).messages;
|
|
5162
|
+
self.applyHydratedBodies(mapped);
|
|
4466
5163
|
var keptOlderPages = false;
|
|
5164
|
+
var keptScreenAwaitingBg = false;
|
|
4467
5165
|
if (fetchMore) {
|
|
4468
|
-
|
|
5166
|
+
var incomingKeys = {};
|
|
5167
|
+
mapped.forEach(function(m) {
|
|
5168
|
+
if (m._serverItemId) incomingKeys[m._serverItemId + "|" + m.role] = m;
|
|
5169
|
+
});
|
|
5170
|
+
var existing = self.state.messages.filter(function(m) {
|
|
5171
|
+
if (!m._serverItemId) return true;
|
|
5172
|
+
var inc = incomingKeys[m._serverItemId + "|" + m.role];
|
|
5173
|
+
if (!inc) return true;
|
|
5174
|
+
if (m._cancelling) inc._cancelling = m._cancelling;
|
|
5175
|
+
if (m._cancelError) inc._cancelError = m._cancelError;
|
|
5176
|
+
return false;
|
|
5177
|
+
});
|
|
5178
|
+
var mergedList = [];
|
|
5179
|
+
var pi = 0, ei = 0;
|
|
5180
|
+
while (pi < mapped.length && ei < existing.length) {
|
|
5181
|
+
var pm = mapped[pi], em = existing[ei];
|
|
5182
|
+
var eid = em._serverItemId;
|
|
5183
|
+
if (typeof eid !== "string") break;
|
|
5184
|
+
var pid = pm._serverItemId;
|
|
5185
|
+
if (typeof pid !== "string" || pid <= eid) {
|
|
5186
|
+
mergedList.push(pm);
|
|
5187
|
+
pi++;
|
|
5188
|
+
} else {
|
|
5189
|
+
mergedList.push(em);
|
|
5190
|
+
ei++;
|
|
5191
|
+
}
|
|
5192
|
+
}
|
|
5193
|
+
while (pi < mapped.length) mergedList.push(mapped[pi++]);
|
|
5194
|
+
while (ei < existing.length) mergedList.push(existing[ei++]);
|
|
5195
|
+
self.state.messages = mergedList;
|
|
5196
|
+
} else if (!mapped.length && history && (history.endOfList === false || history.bgPending) && self.state.messages.some(function(m) {
|
|
5197
|
+
return m._ownerKey === void 0 || m._ownerKey === loadKey;
|
|
5198
|
+
})) {
|
|
5199
|
+
if (history.endOfList !== false) keptScreenAwaitingBg = true;
|
|
4469
5200
|
} else {
|
|
4470
5201
|
if (self.state.typing) self.state.typingAbort = true;
|
|
4471
5202
|
var serverIds = {};
|
|
4472
5203
|
mapped.forEach(function(m) {
|
|
4473
5204
|
if (m._serverItemId) serverIds[m._serverItemId] = 1;
|
|
4474
5205
|
});
|
|
5206
|
+
var surfaceOldestId = void 0;
|
|
5207
|
+
mapped.forEach(function(m) {
|
|
5208
|
+
if (typeof m._serverItemId !== "string" || m._fromBgChain) return;
|
|
5209
|
+
if (surfaceOldestId === void 0 || m._serverItemId < surfaceOldestId) surfaceOldestId = m._serverItemId;
|
|
5210
|
+
});
|
|
4475
5211
|
var locallyCancelled = {};
|
|
4476
5212
|
self.state.messages.forEach(function(m) {
|
|
4477
5213
|
if (m.isCancelled && m._serverItemId) locallyCancelled[m._serverItemId] = m;
|
|
@@ -4512,13 +5248,45 @@ var ChatSession = class {
|
|
|
4512
5248
|
var sharesPage1 = self.state.messages.some(function(m) {
|
|
4513
5249
|
return typeof m._serverItemId === "string" && !!serverIds[m._serverItemId];
|
|
4514
5250
|
});
|
|
4515
|
-
var
|
|
5251
|
+
var deferredBg = !!(history && history.bgPending);
|
|
5252
|
+
var retainBoundary = surfaceOldestId !== void 0 ? surfaceOldestId : oldestInPage1;
|
|
5253
|
+
var retainedOlder = !sharesPage1 || retainBoundary === void 0 ? [] : self.state.messages.filter(function(m) {
|
|
4516
5254
|
if (typeof m._serverItemId !== "string") return false;
|
|
4517
5255
|
if (m._ownerKey !== void 0 && m._ownerKey !== loadKey) return false;
|
|
4518
|
-
|
|
5256
|
+
if (deferredBg && m.isBackgroundTask) return true;
|
|
5257
|
+
if (m._fromBgChain) return true;
|
|
5258
|
+
return m._serverItemId < retainBoundary;
|
|
5259
|
+
});
|
|
5260
|
+
var prependOlder = [];
|
|
5261
|
+
var interleave = [];
|
|
5262
|
+
retainedOlder.forEach(function(m) {
|
|
5263
|
+
var sid = m._serverItemId;
|
|
5264
|
+
if (serverIds[sid]) return;
|
|
5265
|
+
if (retainBoundary !== void 0 && sid < retainBoundary) prependOlder.push(m);
|
|
5266
|
+
else interleave.push(m);
|
|
4519
5267
|
});
|
|
4520
|
-
|
|
4521
|
-
|
|
5268
|
+
var page1 = mapped;
|
|
5269
|
+
if (interleave.length) {
|
|
5270
|
+
var mergedP = [];
|
|
5271
|
+
var ii2 = 0, mi2 = 0;
|
|
5272
|
+
while (ii2 < interleave.length && mi2 < mapped.length) {
|
|
5273
|
+
var iv = interleave[ii2], mv = mapped[mi2];
|
|
5274
|
+
var mid2 = typeof mv._serverItemId === "string" ? mv._serverItemId : void 0;
|
|
5275
|
+
if (mid2 === void 0) break;
|
|
5276
|
+
if (iv._serverItemId <= mid2) {
|
|
5277
|
+
mergedP.push(iv);
|
|
5278
|
+
ii2++;
|
|
5279
|
+
} else {
|
|
5280
|
+
mergedP.push(mv);
|
|
5281
|
+
mi2++;
|
|
5282
|
+
}
|
|
5283
|
+
}
|
|
5284
|
+
while (ii2 < interleave.length) mergedP.push(interleave[ii2++]);
|
|
5285
|
+
while (mi2 < mapped.length) mergedP.push(mapped[mi2++]);
|
|
5286
|
+
page1 = mergedP;
|
|
5287
|
+
}
|
|
5288
|
+
keptOlderPages = prependOlder.length > 0 || interleave.length > 0;
|
|
5289
|
+
self.state.messages = prependOlder.length ? prependOlder.concat(page1) : page1;
|
|
4522
5290
|
rescued.forEach(function(m) {
|
|
4523
5291
|
self.state.messages.push(m);
|
|
4524
5292
|
});
|
|
@@ -4554,9 +5322,14 @@ var ChatSession = class {
|
|
|
4554
5322
|
self.state.historyEndOfList = !!(history && history.endOfList);
|
|
4555
5323
|
self.state.historyStartKeyHistory = history && Array.isArray(history.startKeyHistory) ? history.startKeyHistory : [];
|
|
4556
5324
|
var clearedAt = self.host.getClearedAt();
|
|
4557
|
-
if (clearedAt
|
|
4558
|
-
var
|
|
4559
|
-
|
|
5325
|
+
if (clearedAt) {
|
|
5326
|
+
var surfaceItems = chatList.filter(function(it) {
|
|
5327
|
+
return !(it && it._fromBgChain);
|
|
5328
|
+
});
|
|
5329
|
+
if (surfaceItems.length > 0) {
|
|
5330
|
+
var oldestUpdated = Number(surfaceItems[surfaceItems.length - 1] && surfaceItems[surfaceItems.length - 1].updated);
|
|
5331
|
+
if (isFinite(oldestUpdated) && oldestUpdated <= clearedAt) self.state.historyEndOfList = true;
|
|
5332
|
+
}
|
|
4560
5333
|
}
|
|
4561
5334
|
}
|
|
4562
5335
|
if (self.state.historyRequestToken === token) {
|
|
@@ -4565,6 +5338,85 @@ var ChatSession = class {
|
|
|
4565
5338
|
}
|
|
4566
5339
|
self.updateHistoryCache();
|
|
4567
5340
|
self.host.notify();
|
|
5341
|
+
var bgPending = !fetchMore && history && history.bgPending;
|
|
5342
|
+
if (bgPending) {
|
|
5343
|
+
var batchId = ++_bgHistoryBatchSeq;
|
|
5344
|
+
if (history.endOfList !== true && history.firstLoad === true) {
|
|
5345
|
+
self.state.bgHistoryLoading = true;
|
|
5346
|
+
self.host.notify();
|
|
5347
|
+
}
|
|
5348
|
+
var releaseBgFlag = function() {
|
|
5349
|
+
if (_bgHistoryBatchSeq === batchId) self.state.bgHistoryLoading = false;
|
|
5350
|
+
};
|
|
5351
|
+
bgPending.then(function(batch) {
|
|
5352
|
+
if (token !== self.state.gateRefreshToken) {
|
|
5353
|
+
releaseBgFlag();
|
|
5354
|
+
return;
|
|
5355
|
+
}
|
|
5356
|
+
var bList = batch && Array.isArray(batch.list) ? batch.list : [];
|
|
5357
|
+
bList.forEach(function(item) {
|
|
5358
|
+
if (isBgIndexingQueue(item.queue_name)) {
|
|
5359
|
+
var t = item.compact ? item.request_text : extractLastUserTextFromRequest(item.request_body);
|
|
5360
|
+
if (isIndexingRequestText(t)) item._isBgTask = true;
|
|
5361
|
+
else item._isOnBgQueue = true;
|
|
5362
|
+
}
|
|
5363
|
+
});
|
|
5364
|
+
var sorted = bList.sort(function(a, b) {
|
|
5365
|
+
var ai = typeof a.id === "string" ? a.id : "", bi = typeof b.id === "string" ? b.id : "";
|
|
5366
|
+
return ai > bi ? -1 : ai < bi ? 1 : 0;
|
|
5367
|
+
});
|
|
5368
|
+
var m2 = mapHistoryListToMessages(sorted, platform, {
|
|
5369
|
+
clearedAt: self.host.getClearedAt(),
|
|
5370
|
+
projectId: id.projectId,
|
|
5371
|
+
formatIndexingLabel: self.host.formatIndexingLabel
|
|
5372
|
+
}).messages;
|
|
5373
|
+
self.applyHydratedBodies(m2);
|
|
5374
|
+
if (keptScreenAwaitingBg && !m2.length && batch && batch.endOfList === true) {
|
|
5375
|
+
self.state.messages = self.state.messages.filter(function(m) {
|
|
5376
|
+
if (typeof m._serverItemId !== "string") return true;
|
|
5377
|
+
if (m._ownerKey !== void 0 && m._ownerKey !== loadKey) return true;
|
|
5378
|
+
return false;
|
|
5379
|
+
});
|
|
5380
|
+
self.state.historyEndOfList = true;
|
|
5381
|
+
releaseBgFlag();
|
|
5382
|
+
self.updateHistoryCache();
|
|
5383
|
+
self.host.notify();
|
|
5384
|
+
return;
|
|
5385
|
+
}
|
|
5386
|
+
var incoming = {};
|
|
5387
|
+
m2.forEach(function(m) {
|
|
5388
|
+
if (m._serverItemId) incoming[m._serverItemId + "|" + m.role] = true;
|
|
5389
|
+
});
|
|
5390
|
+
var baseList = self.state.messages.filter(function(m) {
|
|
5391
|
+
return !(m._serverItemId && incoming[m._serverItemId + "|" + m.role]);
|
|
5392
|
+
});
|
|
5393
|
+
var mergedList2 = [];
|
|
5394
|
+
var pi2 = 0, ei2 = 0;
|
|
5395
|
+
while (pi2 < m2.length && ei2 < baseList.length) {
|
|
5396
|
+
var pm2 = m2[pi2], em2 = baseList[ei2];
|
|
5397
|
+
var eid2 = em2._serverItemId;
|
|
5398
|
+
if (typeof eid2 !== "string") break;
|
|
5399
|
+
var pid2 = pm2._serverItemId;
|
|
5400
|
+
if (typeof pid2 !== "string" || pid2 <= eid2) {
|
|
5401
|
+
mergedList2.push(pm2);
|
|
5402
|
+
pi2++;
|
|
5403
|
+
} else {
|
|
5404
|
+
mergedList2.push(em2);
|
|
5405
|
+
ei2++;
|
|
5406
|
+
}
|
|
5407
|
+
}
|
|
5408
|
+
while (pi2 < m2.length) mergedList2.push(m2[pi2++]);
|
|
5409
|
+
while (ei2 < baseList.length) mergedList2.push(baseList[ei2++]);
|
|
5410
|
+
self.state.messages = mergedList2;
|
|
5411
|
+
if (batch && batch.endOfList === true) self.state.historyEndOfList = true;
|
|
5412
|
+
releaseBgFlag();
|
|
5413
|
+
self.updateHistoryCache();
|
|
5414
|
+
self.host.notify();
|
|
5415
|
+
}, function() {
|
|
5416
|
+
releaseBgFlag();
|
|
5417
|
+
self.host.notify();
|
|
5418
|
+
});
|
|
5419
|
+
}
|
|
4568
5420
|
if (!fetchMore) {
|
|
4569
5421
|
var bgAllow = {};
|
|
4570
5422
|
var bgHistBudget = MAX_CONCURRENT_BG_POLLS - self._countBgPolls();
|
|
@@ -4661,7 +5513,7 @@ var ChatSession = class {
|
|
|
4661
5513
|
var self = this;
|
|
4662
5514
|
var id = this.host.getIdentity();
|
|
4663
5515
|
att.status = "uploading";
|
|
4664
|
-
att.progress =
|
|
5516
|
+
att.progress = null;
|
|
4665
5517
|
att.errorMessage = "";
|
|
4666
5518
|
att.errorCode = "";
|
|
4667
5519
|
att.errorDetail = "";
|
|
@@ -4892,6 +5744,7 @@ var ChatSession = class {
|
|
|
4892
5744
|
};
|
|
4893
5745
|
|
|
4894
5746
|
// src/engine/indexing_groups.ts
|
|
5747
|
+
var RUN_RECORD_WORKING_STALE_MS = 6 * 60 * 60 * 1e3;
|
|
4895
5748
|
var INDEXING_LABEL_RE = /^(Re)?[Ii]ndexing(\s*\(continuing\))?\s*:?\s+(.+)$/;
|
|
4896
5749
|
var LEADING_MD_LINK_RE = /^\[([^\]]+)\]\(([^)]+)\)/;
|
|
4897
5750
|
function parseIndexingLabel(content) {
|
|
@@ -4946,10 +5799,12 @@ function buildChatDisplayList(messages, opts) {
|
|
|
4946
5799
|
var list = Array.isArray(messages) ? messages : [];
|
|
4947
5800
|
var liveIndexKeys = opts && opts.liveIndexKeys || {};
|
|
4948
5801
|
var liveIndexChecked = !!(opts && opts.liveIndexChecked);
|
|
5802
|
+
var doneKeys = opts && opts.doneKeys || {};
|
|
4949
5803
|
var stoppedIndexIds = opts && opts.stoppedIndexIds || {};
|
|
4950
5804
|
var windowedIndexing = opts && opts.windowedIndexing !== void 0 ? !!opts.windowedIndexing : windowedIndexingEnabled();
|
|
4951
5805
|
var hasMoreHistory = !!(opts && opts.hasMoreHistory);
|
|
4952
5806
|
var loadingOlderHistory = !!(opts && opts.loadingOlderHistory);
|
|
5807
|
+
var stubPlatform = opts && opts.stubPlatform;
|
|
4953
5808
|
var groups = {};
|
|
4954
5809
|
var order = [];
|
|
4955
5810
|
var runOfIndex = new Array(list.length);
|
|
@@ -5122,11 +5977,11 @@ function buildChatDisplayList(messages, opts) {
|
|
|
5122
5977
|
} else if (grp.driver === "client") {
|
|
5123
5978
|
grp.finished = sawComplete || grp.status === "error" || grp.passCount >= MAX_INDEXING_RESUME_PASSES;
|
|
5124
5979
|
} else {
|
|
5125
|
-
grp.finished = !newestRunOfKey[order[oi]] || liveIndexChecked && !liveIndexKeys[grp.key];
|
|
5980
|
+
grp.finished = !newestRunOfKey[order[oi]] || !!doneKeys[grp.key] && !liveIndexKeys[grp.key] || liveIndexChecked && !liveIndexKeys[grp.key];
|
|
5126
5981
|
}
|
|
5127
5982
|
if (grp.status !== "done") {
|
|
5128
5983
|
grp.resolving = false;
|
|
5129
|
-
} else if (grp.mayHaveOlder && loadingOlderHistory && !liveIndexKeys[grp.key] && newestRunOfKey[order[oi]]) {
|
|
5984
|
+
} else if (grp.mayHaveOlder && loadingOlderHistory && !liveIndexKeys[grp.key] && !doneKeys[grp.key] && newestRunOfKey[order[oi]]) {
|
|
5130
5985
|
grp.resolving = true;
|
|
5131
5986
|
grp.resolvingReason = "history";
|
|
5132
5987
|
} else if (!grp.finished && grp.driver === "worker" && !liveIndexChecked && !liveIndexKeys[grp.key]) {
|
|
@@ -5136,18 +5991,130 @@ function buildChatDisplayList(messages, opts) {
|
|
|
5136
5991
|
grp.resolving = false;
|
|
5137
5992
|
}
|
|
5138
5993
|
}
|
|
5994
|
+
var stubList = [];
|
|
5995
|
+
var runStubs = opts && opts.runStubs;
|
|
5996
|
+
if (runStubs) {
|
|
5997
|
+
var coveredPaths = {};
|
|
5998
|
+
var coveredPathlessNames = {};
|
|
5999
|
+
for (var ci = 0; ci < order.length; ci++) {
|
|
6000
|
+
var cg = groups[order[ci]];
|
|
6001
|
+
if (cg.path) {
|
|
6002
|
+
coveredPaths[cg.path] = true;
|
|
6003
|
+
if (cg.key) coveredPaths[cg.key] = true;
|
|
6004
|
+
} else if (cg.name) coveredPathlessNames[cg.name] = true;
|
|
6005
|
+
else if (cg.key) coveredPaths[cg.key] = true;
|
|
6006
|
+
}
|
|
6007
|
+
var now = opts && typeof opts.now === "number" ? opts.now : Date.now();
|
|
6008
|
+
var stubClearedAt = opts && typeof opts.stubClearedAt === "number" && opts.stubClearedAt > 0 ? opts.stubClearedAt : 0;
|
|
6009
|
+
for (var sp in runStubs) {
|
|
6010
|
+
var rec = runStubs[sp];
|
|
6011
|
+
if (!sp || !rec || !rec.status || coveredPaths[sp]) continue;
|
|
6012
|
+
var fname = rec.filename || sp.split("/").pop() || sp;
|
|
6013
|
+
if (coveredPathlessNames[fname]) continue;
|
|
6014
|
+
if (stubPlatform && rec.platform && rec.platform !== stubPlatform) continue;
|
|
6015
|
+
var live = !!liveIndexKeys[sp] || !!liveIndexKeys[fname];
|
|
6016
|
+
var recWhen = typeof rec.finished === "number" ? rec.finished : typeof rec.started === "number" ? rec.started : void 0;
|
|
6017
|
+
if (stubClearedAt && !live && recWhen !== void 0 && recWhen <= stubClearedAt) continue;
|
|
6018
|
+
var st = "active";
|
|
6019
|
+
var fin = false;
|
|
6020
|
+
var res = false;
|
|
6021
|
+
var reason;
|
|
6022
|
+
if (!live) {
|
|
6023
|
+
if (rec.status === "done" || doneKeys[sp] || doneKeys[fname]) {
|
|
6024
|
+
st = "done";
|
|
6025
|
+
fin = true;
|
|
6026
|
+
} else if (rec.status === "error") {
|
|
6027
|
+
st = "error";
|
|
6028
|
+
fin = true;
|
|
6029
|
+
} else if (rec.status === "cancelled") {
|
|
6030
|
+
st = "cancelled";
|
|
6031
|
+
fin = true;
|
|
6032
|
+
} else if (liveIndexChecked) {
|
|
6033
|
+
st = "done";
|
|
6034
|
+
fin = true;
|
|
6035
|
+
} else if (typeof rec.started === "number" && now - rec.started > RUN_RECORD_WORKING_STALE_MS) {
|
|
6036
|
+
st = "error";
|
|
6037
|
+
fin = true;
|
|
6038
|
+
} else {
|
|
6039
|
+
res = true;
|
|
6040
|
+
reason = "status";
|
|
6041
|
+
}
|
|
6042
|
+
}
|
|
6043
|
+
var sg = {
|
|
6044
|
+
key: sp,
|
|
6045
|
+
// ONE identity for the run whether it renders from the record or
|
|
6046
|
+
// from its loaded passes: the views key the DOM off runKey, so a
|
|
6047
|
+
// 'stub:'-prefixed key meant every handoff was an unmount plus a
|
|
6048
|
+
// remount somewhere else. Named after the record's start, which
|
|
6049
|
+
// the real group below reuses when it has one.
|
|
6050
|
+
runKey: "run:" + sp + "#" + (typeof rec.started === "number" ? rec.started : "n"),
|
|
6051
|
+
name: fname,
|
|
6052
|
+
path: sp,
|
|
6053
|
+
mime: void 0,
|
|
6054
|
+
size: void 0,
|
|
6055
|
+
isReindex: false,
|
|
6056
|
+
members: [],
|
|
6057
|
+
passCount: 0,
|
|
6058
|
+
status: st,
|
|
6059
|
+
cancellableIds: [],
|
|
6060
|
+
cancelling: false,
|
|
6061
|
+
stopped: st === "cancelled",
|
|
6062
|
+
mayHaveOlder: hasMoreHistory,
|
|
6063
|
+
anchorIndex: -1,
|
|
6064
|
+
anchorId: "",
|
|
6065
|
+
visibleMembers: [],
|
|
6066
|
+
driver: !isPagedReadFile(fname, void 0) ? "single" : isImageVisionFile(fname, void 0) ? "worker" : windowedIndexing ? "worker" : "client",
|
|
6067
|
+
finished: fin,
|
|
6068
|
+
resolving: res,
|
|
6069
|
+
resolvingReason: reason,
|
|
6070
|
+
stub: true,
|
|
6071
|
+
stubError: rec.error || (st === "error" && !rec.error ? "Indexing did not finish." : void 0)
|
|
6072
|
+
};
|
|
6073
|
+
stubList.push({ started: typeof rec.started === "number" ? rec.started : Infinity, group: sg });
|
|
6074
|
+
}
|
|
6075
|
+
}
|
|
6076
|
+
var suppressAnchor = {};
|
|
6077
|
+
if (runStubs) {
|
|
6078
|
+
for (var ti2 = 0; ti2 < order.length; ti2++) {
|
|
6079
|
+
var tg = groups[order[ti2]];
|
|
6080
|
+
if (!newestRunOfKey[order[ti2]]) continue;
|
|
6081
|
+
var trec = tg.path && runStubs[tg.path] || runStubs[tg.key];
|
|
6082
|
+
if (!trec || typeof trec.started !== "number") continue;
|
|
6083
|
+
if (stubPlatform && trec.platform && trec.platform !== stubPlatform) continue;
|
|
6084
|
+
suppressAnchor[order[ti2]] = true;
|
|
6085
|
+
tg.runKey = "run:" + (tg.path || tg.key) + "#" + trec.started;
|
|
6086
|
+
stubList.push({ started: trec.started, group: tg });
|
|
6087
|
+
}
|
|
6088
|
+
}
|
|
6089
|
+
stubList.sort(function(a, b) {
|
|
6090
|
+
return a.started - b.started;
|
|
6091
|
+
});
|
|
5139
6092
|
var out = [];
|
|
6093
|
+
var si = 0;
|
|
5140
6094
|
for (var j = 0; j < list.length; j++) {
|
|
6095
|
+
var mts = list[j] && typeof list[j]._ts === "number" ? list[j]._ts : void 0;
|
|
6096
|
+
if (mts !== void 0) {
|
|
6097
|
+
while (si < stubList.length && stubList[si].started <= mts) {
|
|
6098
|
+
out.push({ kind: "indexing", group: stubList[si].group, index: -1 - si });
|
|
6099
|
+
si++;
|
|
6100
|
+
}
|
|
6101
|
+
}
|
|
5141
6102
|
var r = runOfIndex[j];
|
|
5142
6103
|
if (r === void 0) {
|
|
5143
6104
|
out.push({ kind: "message", msg: list[j], index: j });
|
|
5144
6105
|
continue;
|
|
5145
6106
|
}
|
|
5146
|
-
if (groups[r].anchorIndex === j
|
|
6107
|
+
if (groups[r].anchorIndex === j && !suppressAnchor[r]) {
|
|
6108
|
+
out.push({ kind: "indexing", group: groups[r], index: j });
|
|
6109
|
+
}
|
|
6110
|
+
}
|
|
6111
|
+
while (si < stubList.length) {
|
|
6112
|
+
out.push({ kind: "indexing", group: stubList[si].group, index: -1 - si });
|
|
6113
|
+
si++;
|
|
5147
6114
|
}
|
|
5148
6115
|
return out;
|
|
5149
6116
|
}
|
|
5150
6117
|
|
|
5151
|
-
export { BG_INDEXING_QUEUE_SUFFIX, BOM, BOM_EXTS, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, ChatSession, DEFAULT_CLAUDE_MODEL, DEFAULT_OPENAI_MODEL, EMPTY_INDEXING_REPLY, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, EXT_CONTENT_TYPES, HISTORY_BUDGET_RATIO, HISTORY_FILL_SLACK_PX, HISTORY_TOKEN_BUDGET, HTML_EXTS, HTML_HEAD_WINDOW, IMAGE_PREVIEWS_PER_MESSAGE, INDEXING_COMPLETE_MARKER, INLINE_LINK_GLYPH, INLINE_LINK_UNAVAILABLE_GLYPH, INLINE_LINK_UNAVAILABLE_SUFFIX, LINK_LABEL_MAX_DISPLAY_CHARS, LINK_REFRESH_WINDOW_MS, MAX_CONCURRENT_BG_POLLS, MAX_HISTORY_FILL_PAGES, MAX_HISTORY_MESSAGES, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MIN_INPUT_TOKEN_BUDGET, OUTPUT_TOKEN_RESERVE, POLL_INTERVAL, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, RENDER_FROM_TOKEN, RTF_EXTS, TOOL_AND_RESPONSE_BUFFER, XML_EXTS, applyEncodingDeclaration, bgIndexingQueueName, buildAiAgentValue, buildBoundedChatMessages, buildChatDisplayList, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildIndexingContinueMessage, buildIndexingRenderContinueTemplate, buildIndexingRenderMessage, buildIndexingSystemPrompt, buildIndexingUserMessage, buildIndexingWindowMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, chatEngineConfig, classifyInlineLink, clearAttachmentParsers, clearImagePreviewCache, composeUserMessage, configureChatEngine, contentTypeForExt, createHistoryFiller, createInlineLinkRegex, encodePathSegments, encodingClassForExt, ensureHtmlCharset, ensureXmlEncoding, escapeInlineHtml, escapeRtfNonAscii, estimateMessageTokens, estimateTextTokens, extOf, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, formatChatTimestamp, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, getProjectContextWindow, getVisionProfile, groupAttachmentFailures, hasBom, hydrateImagePreviews, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isIndexingRequestText, isLinkUnavailable, isNonRetryableRequestError, isOfficeFile, isPreviewableImagePath, isServerExtractable, isServiceDbAttachmentHref, linkUnavailableKeyForHref, linkUnavailableKeyForPath, listClaudeModels, listOpenAIModels, looksLikeRtf, makeExtractPlaceholder, mapHistoryListToMessages, markImagePreviewStale, needsBomForExt, normalizeAttachmentPathCandidate, normalizeExt, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAiAgentValue, parseAttachmentContent, parseIndexingLabel, parseIndexingRequestText, peekImagePreviewUrl, prepareDownloadText, previewImageContentType, previewableExtOf, readExpiredAttachmentHref, registerAttachmentParser, registerModelContextWindows, renderInlineLinkHtml, repairUrlEntities, repairUrlWhitespace, resolveImagePreviewUrl, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, wallClockNow };
|
|
6118
|
+
export { BG_INDEXING_QUEUE_SUFFIX, BOM, BOM_EXTS, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, ChatSession, DEFAULT_CLAUDE_MODEL, DEFAULT_CONTEXT_WINDOW, DEFAULT_OPENAI_MODEL, EMPTY_INDEXING_REPLY, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, EXT_CONTENT_TYPES, HISTORY_BUDGET_RATIO, HISTORY_FILL_SLACK_PX, HISTORY_TOKEN_BUDGET, HTML_EXTS, HTML_HEAD_WINDOW, IMAGE_PREVIEWS_PER_MESSAGE, INDEXING_COMPLETE_MARKER, INLINE_LINK_GLYPH, INLINE_LINK_UNAVAILABLE_GLYPH, INLINE_LINK_UNAVAILABLE_SUFFIX, INPUT_CAP_RATIO, LINK_LABEL_MAX_DISPLAY_CHARS, LINK_REFRESH_WINDOW_MS, MAX_CONCURRENT_BG_POLLS, MAX_HISTORY_FILL_PAGES, MAX_HISTORY_MESSAGES, MAX_OUTPUT_BY_MODEL, MAX_OUTPUT_TOKENS, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MINT_CACHE_GENERATION, MIN_INPUT_TOKEN_BUDGET, MIN_PER_REQUEST_INPUT_CAP, OUTPUT_TOKEN_RESERVE, POLL_INTERVAL, PRESIGN_SAFETY_MARGIN_MS, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, PREVIEW_URL_EXPIRES_SECONDS, RENDER_FROM_TOKEN, RTF_EXTS, RUN_RECORD_WORKING_STALE_MS, TOOL_AND_RESPONSE_BUFFER, XML_EXTS, __resetSplitHistoryState, applyEncodingDeclaration, bgIndexingQueueName, buildAiAgentValue, buildBoundedChatMessages, buildChatDisplayList, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildHistoryItemFullId, buildIndexingContinueMessage, buildIndexingRenderContinueTemplate, buildIndexingRenderMessage, buildIndexingSystemPrompt, buildIndexingUserMessage, buildIndexingWindowMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, chatEngineConfig, classifyInlineLink, clearAttachmentParsers, clearImagePreviewCache, composeUserMessage, configureChatEngine, contentTypeForExt, createHistoryFiller, createInlineLinkRegex, encodePathSegments, encodingClassForExt, ensureHtmlCharset, ensureXmlEncoding, escapeInlineHtml, escapeRtfNonAscii, estimateMessageTokens, estimateTextTokens, extOf, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, fetchLiveIndexingKeys, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, formatChatTimestamp, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, getInputTokenBudget, getMaxOutputTokens, getModelContextWindow, getProjectContextWindow, getSplitChatHistory, getVisionProfile, groupAttachmentFailures, hasBom, hydrateImagePreviews, indexDoneUniqueId, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isIndexingRequestText, isLinkUnavailable, isNonRetryableRequestError, isOfficeFile, isPreviewableImagePath, isProviderApiKeyError, isServerExtractable, isServiceDbAttachmentHref, linkUnavailableKeyForHref, linkUnavailableKeyForPath, linkUnavailableKeysForPath, listClaudeModels, listOpenAIModels, looksLikeRtf, makeExtractPlaceholder, mapHistoryListToMessages, markImagePreviewStale, mintCacheBustStamp, needsBomForExt, normalizeAttachmentPathCandidate, normalizeExt, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAiAgentValue, parseAttachmentContent, parseIndexingLabel, parseIndexingRequestText, peekImagePreviewUrl, prepareDownloadText, presignExpiryEpochMs, previewImageContentType, previewMintCacheToken, previewableExtOf, readExpiredAttachmentHref, registerAttachmentParser, registerModelContextWindows, renderInlineLinkHtml, repairUrlEntities, repairUrlWhitespace, resolveImagePreviewUrl, runIndexUniqueId, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, upsertIndexRunRecordSafe, wallClockNow };
|
|
5152
6119
|
//# sourceMappingURL=engine.mjs.map
|
|
5153
6120
|
//# sourceMappingURL=engine.mjs.map
|