bunnyquery 1.8.6 → 1.8.8
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 +1400 -101
- package/dist/engine.cjs +1018 -78
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +506 -163
- package/dist/engine.d.ts +506 -163
- package/dist/engine.mjs +1002 -79
- 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/index.ts +15 -1
- package/src/engine/indexing_groups.ts +248 -3
- 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.cjs
CHANGED
|
@@ -217,7 +217,8 @@ function isWindowedReadFile(name, mime) {
|
|
|
217
217
|
if (isImageVisionFile(name, mime)) return false;
|
|
218
218
|
return isPagedReadFile(name, mime);
|
|
219
219
|
}
|
|
220
|
-
function composeUserMessage(text, attachmentUrls) {
|
|
220
|
+
function composeUserMessage(text, attachmentUrls, opts) {
|
|
221
|
+
const inlineExtracted = opts?.inlineExtractedContent !== false;
|
|
221
222
|
let composed = text;
|
|
222
223
|
let composedForLlm = composed;
|
|
223
224
|
if (attachmentUrls.length > 0) {
|
|
@@ -231,7 +232,7 @@ ${lines.join("\n")}`;
|
|
|
231
232
|
let extractContent;
|
|
232
233
|
let fileUrls;
|
|
233
234
|
if (attachmentUrls.length > 0) {
|
|
234
|
-
const extractFiles = attachmentUrls.filter((u) => isServerExtractable(u.name));
|
|
235
|
+
const extractFiles = inlineExtracted ? attachmentUrls.filter((u) => isServerExtractable(u.name)) : [];
|
|
235
236
|
if (extractFiles.length > 0) {
|
|
236
237
|
const directives = [];
|
|
237
238
|
const sections = extractFiles.map((u) => {
|
|
@@ -292,7 +293,7 @@ Never assert absence from a partial read. Do not say "there is no X", "none", "n
|
|
|
292
293
|
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.
|
|
293
294
|
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.
|
|
294
295
|
- 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.
|
|
295
|
-
-
|
|
296
|
+
- 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.
|
|
296
297
|
- 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.
|
|
297
298
|
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.
|
|
298
299
|
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.
|
|
@@ -566,6 +567,36 @@ function isAuthExpiredError(input) {
|
|
|
566
567
|
if (!hay) return false;
|
|
567
568
|
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;
|
|
568
569
|
}
|
|
570
|
+
function isProviderApiKeyError(input) {
|
|
571
|
+
if (!input) return false;
|
|
572
|
+
var blobs = [];
|
|
573
|
+
var push = function(v) {
|
|
574
|
+
if (typeof v === "string" && v) blobs.push(v);
|
|
575
|
+
};
|
|
576
|
+
if (typeof input === "string") push(input);
|
|
577
|
+
else {
|
|
578
|
+
push(input.message);
|
|
579
|
+
push(input.code);
|
|
580
|
+
push(input.type);
|
|
581
|
+
if (input.error) {
|
|
582
|
+
push(input.error.message);
|
|
583
|
+
push(input.error.code);
|
|
584
|
+
push(input.error.type);
|
|
585
|
+
}
|
|
586
|
+
if (input.body) {
|
|
587
|
+
push(input.body.message);
|
|
588
|
+
push(input.body.type);
|
|
589
|
+
if (input.body.error) {
|
|
590
|
+
push(input.body.error.message);
|
|
591
|
+
push(input.body.error.code);
|
|
592
|
+
push(input.body.error.type);
|
|
593
|
+
}
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
var hay = blobs.join(" | ").toLowerCase();
|
|
597
|
+
if (!hay) return false;
|
|
598
|
+
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;
|
|
599
|
+
}
|
|
569
600
|
|
|
570
601
|
// src/engine/links.ts
|
|
571
602
|
var EXPIRED_ATTACHMENT_URL_HOST = "_expired_.url";
|
|
@@ -840,33 +871,85 @@ function truncateLabelForDisplay(label) {
|
|
|
840
871
|
// src/engine/budget.ts
|
|
841
872
|
var CONTEXT_WINDOW_DEFAULT = { claude: 2e5, openai: 128e3 };
|
|
842
873
|
var CONTEXT_WINDOW_BY_MODEL = {
|
|
843
|
-
// exact ids
|
|
874
|
+
// claude, exact ids
|
|
875
|
+
"claude-fable-5": 1e6,
|
|
844
876
|
"claude-opus-5": 1e6,
|
|
845
877
|
"claude-opus-4-8": 1e6,
|
|
846
878
|
"claude-opus-4-7": 1e6,
|
|
879
|
+
"claude-opus-4-6": 1e6,
|
|
880
|
+
"claude-opus-4-5": 2e5,
|
|
847
881
|
"claude-sonnet-5": 1e6,
|
|
848
882
|
"claude-sonnet-4-6": 1e6,
|
|
883
|
+
"claude-sonnet-4-5": 1e6,
|
|
849
884
|
"claude-sonnet-4": 2e5,
|
|
850
885
|
"claude-haiku-4-5": 2e5,
|
|
851
|
-
"
|
|
852
|
-
|
|
886
|
+
"claude-3-5-sonnet": 2e5,
|
|
887
|
+
// openai, exact ids
|
|
888
|
+
"gpt-5.6-sol": 105e4,
|
|
889
|
+
"gpt-5.6-terra": 105e4,
|
|
890
|
+
"gpt-5.6-luna": 105e4,
|
|
891
|
+
"gpt-5.5": 1e6,
|
|
892
|
+
"gpt-5.4": 105e4,
|
|
893
|
+
"gpt-5.4-mini": 4e5,
|
|
894
|
+
"gpt-5.4-nano": 4e5,
|
|
895
|
+
"gpt-4.1": 104e4,
|
|
896
|
+
"gpt-4o": 128e3,
|
|
897
|
+
"o1": 2e5,
|
|
898
|
+
"o1-pro": 2e5,
|
|
853
899
|
// family keys
|
|
900
|
+
"claude-fable": 1e6,
|
|
854
901
|
"claude-opus": 1e6,
|
|
855
902
|
"claude-sonnet": 1e6,
|
|
856
903
|
"claude-haiku": 2e5,
|
|
904
|
+
"gpt-5.6": 105e4,
|
|
905
|
+
"gpt-5": 128e3
|
|
906
|
+
};
|
|
907
|
+
var MAX_OUTPUT_BY_MODEL = {
|
|
908
|
+
// claude
|
|
909
|
+
"claude-fable-5": 128e3,
|
|
910
|
+
"claude-opus-5": 128e3,
|
|
911
|
+
"claude-opus-4-8": 128e3,
|
|
912
|
+
"claude-sonnet-5": 128e3,
|
|
913
|
+
"claude-sonnet-4-6": 64e3,
|
|
914
|
+
"claude-haiku-4-5": 64e3,
|
|
915
|
+
"claude-3-5-sonnet": 8e3,
|
|
916
|
+
// openai
|
|
917
|
+
"gpt-5.6-sol": 128e3,
|
|
918
|
+
"gpt-5.6-terra": 128e3,
|
|
919
|
+
"gpt-5.6-luna": 128e3,
|
|
920
|
+
"gpt-5.5": 128e3,
|
|
921
|
+
"gpt-5.4": 128e3,
|
|
922
|
+
"gpt-5.4-mini": 128e3,
|
|
923
|
+
"gpt-5.4-nano": 128e3,
|
|
924
|
+
"gpt-4.1": 16e3,
|
|
925
|
+
"gpt-4o": 4e3,
|
|
926
|
+
"o1": 1e5,
|
|
927
|
+
"o1-pro": 1e5,
|
|
928
|
+
// family keys
|
|
929
|
+
"claude-fable": 128e3,
|
|
930
|
+
"claude-opus": 128e3,
|
|
931
|
+
"claude-sonnet": 64e3,
|
|
932
|
+
"claude-haiku": 64e3,
|
|
857
933
|
"gpt-5.6": 128e3,
|
|
858
934
|
"gpt-5": 128e3
|
|
859
935
|
};
|
|
936
|
+
var DEFAULT_CONTEXT_WINDOW = 88e4;
|
|
860
937
|
var apiReportedContextWindows = {};
|
|
938
|
+
var apiReportedMaxOutput = {};
|
|
861
939
|
function registerModelContextWindows(models) {
|
|
862
940
|
if (!Array.isArray(models)) return;
|
|
863
941
|
for (var i = 0; i < models.length; i++) {
|
|
864
942
|
var m = models[i];
|
|
865
943
|
var id = (m && m.id ? String(m.id) : "").trim().toLowerCase();
|
|
944
|
+
if (!id) continue;
|
|
866
945
|
var reported = m ? Number(m.max_input_tokens) : NaN;
|
|
867
|
-
if (
|
|
946
|
+
if (Number.isFinite(reported) && reported > 0) {
|
|
868
947
|
apiReportedContextWindows[id] = Math.floor(reported);
|
|
869
948
|
}
|
|
949
|
+
var out = m ? Number(m.max_tokens) : NaN;
|
|
950
|
+
if (Number.isFinite(out) && out > 0) {
|
|
951
|
+
apiReportedMaxOutput[id] = Math.floor(out);
|
|
952
|
+
}
|
|
870
953
|
}
|
|
871
954
|
}
|
|
872
955
|
var projectContextWindows = {};
|
|
@@ -881,13 +964,16 @@ function getProjectContextWindow(projectId) {
|
|
|
881
964
|
var key = (projectId || "").trim();
|
|
882
965
|
return key && projectContextWindows[key] ? projectContextWindows[key] : null;
|
|
883
966
|
}
|
|
884
|
-
var
|
|
967
|
+
var MAX_OUTPUT_TOKENS = 25e3;
|
|
968
|
+
var OUTPUT_TOKEN_RESERVE = MAX_OUTPUT_TOKENS;
|
|
885
969
|
var TOOL_AND_RESPONSE_BUFFER = 4e3;
|
|
886
970
|
var MIN_INPUT_TOKEN_BUDGET = 8e3;
|
|
887
|
-
var
|
|
971
|
+
var MIN_PER_REQUEST_INPUT_CAP = 28e3;
|
|
972
|
+
var CLAUDE_PER_REQUEST_INPUT_CAP = MIN_PER_REQUEST_INPUT_CAP;
|
|
888
973
|
var MAX_HISTORY_MESSAGES = 20;
|
|
889
974
|
var HISTORY_TOKEN_BUDGET = 8e3;
|
|
890
|
-
var
|
|
975
|
+
var INPUT_CAP_RATIO = 0.16;
|
|
976
|
+
var CLAUDE_INPUT_CAP_RATIO = INPUT_CAP_RATIO;
|
|
891
977
|
var HISTORY_BUDGET_RATIO = 0.08;
|
|
892
978
|
function estimateTextTokens(text) {
|
|
893
979
|
return Math.ceil((text || "").length / 3);
|
|
@@ -895,38 +981,61 @@ function estimateTextTokens(text) {
|
|
|
895
981
|
function estimateMessageTokens(msg) {
|
|
896
982
|
return estimateTextTokens(msg.content) + estimateTextTokens(msg.role) + 6;
|
|
897
983
|
}
|
|
984
|
+
function resolveByModelId(apiTable, staticTable, model) {
|
|
985
|
+
var normalized = (model || "").trim().toLowerCase();
|
|
986
|
+
if (!normalized) return 0;
|
|
987
|
+
if (apiTable[normalized]) return apiTable[normalized];
|
|
988
|
+
if (staticTable[normalized]) return staticTable[normalized];
|
|
989
|
+
var parts = normalized.split("-");
|
|
990
|
+
for (var end = parts.length - 1; end > 0; end--) {
|
|
991
|
+
var family = parts.slice(0, end).join("-");
|
|
992
|
+
if (staticTable[family]) return staticTable[family];
|
|
993
|
+
}
|
|
994
|
+
return 0;
|
|
995
|
+
}
|
|
996
|
+
function getModelContextWindow(platform, model) {
|
|
997
|
+
return resolveByModelId(apiReportedContextWindows, CONTEXT_WINDOW_BY_MODEL, model) || CONTEXT_WINDOW_DEFAULT[platform];
|
|
998
|
+
}
|
|
999
|
+
function getMaxOutputTokens(platform, model) {
|
|
1000
|
+
var cap = resolveByModelId(apiReportedMaxOutput, MAX_OUTPUT_BY_MODEL, model);
|
|
1001
|
+
return cap ? Math.min(MAX_OUTPUT_TOKENS, cap) : MAX_OUTPUT_TOKENS;
|
|
1002
|
+
}
|
|
898
1003
|
function getContextWindow(platform, model, projectId) {
|
|
1004
|
+
var ceiling = getModelContextWindow(platform, model);
|
|
899
1005
|
var override = projectId ? getProjectContextWindow(projectId) : null;
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
return
|
|
1006
|
+
return Math.min(override || DEFAULT_CONTEXT_WINDOW, ceiling);
|
|
1007
|
+
}
|
|
1008
|
+
function contextBasedBudgetFor(platform, model, projectId) {
|
|
1009
|
+
var contextWindow = getContextWindow(platform, model, projectId);
|
|
1010
|
+
return Math.max(
|
|
1011
|
+
MIN_INPUT_TOKEN_BUDGET,
|
|
1012
|
+
contextWindow - getMaxOutputTokens(platform, model) - TOOL_AND_RESPONSE_BUFFER
|
|
1013
|
+
);
|
|
1014
|
+
}
|
|
1015
|
+
function getInputTokenBudget(platform, model, projectId) {
|
|
1016
|
+
var contextBasedBudget = contextBasedBudgetFor(platform, model, projectId);
|
|
1017
|
+
return Math.min(
|
|
1018
|
+
contextBasedBudget,
|
|
1019
|
+
Math.max(MIN_PER_REQUEST_INPUT_CAP, Math.round(contextBasedBudget * INPUT_CAP_RATIO))
|
|
1020
|
+
);
|
|
912
1021
|
}
|
|
913
1022
|
function stripFileBlocksFromHistory(content) {
|
|
914
1023
|
if (!content) return content;
|
|
915
1024
|
return content.replace(/```([^\n`]+?\.[^\s.`]+)\n[\s\S]*?```/g, "[file previously attached: $1]");
|
|
916
1025
|
}
|
|
917
1026
|
function buildBoundedChatMessages(options) {
|
|
918
|
-
var
|
|
919
|
-
var
|
|
920
|
-
MIN_INPUT_TOKEN_BUDGET,
|
|
921
|
-
contextWindow - OUTPUT_TOKEN_RESERVE - TOOL_AND_RESPONSE_BUFFER
|
|
922
|
-
);
|
|
923
|
-
var scaled = !!(options.projectId && getProjectContextWindow(options.projectId));
|
|
924
|
-
var claudeInputCap = scaled ? Math.max(CLAUDE_PER_REQUEST_INPUT_CAP, Math.round(contextBasedBudget * CLAUDE_INPUT_CAP_RATIO)) : CLAUDE_PER_REQUEST_INPUT_CAP;
|
|
925
|
-
var availableInputBudget = options.platform === "claude" ? Math.min(contextBasedBudget, claudeInputCap) : contextBasedBudget;
|
|
1027
|
+
var contextBasedBudget = contextBasedBudgetFor(options.platform, options.model, options.projectId);
|
|
1028
|
+
var availableInputBudget = getInputTokenBudget(options.platform, options.model, options.projectId);
|
|
926
1029
|
var systemCost = estimateTextTokens(options.systemPrompt) + 12;
|
|
927
|
-
var historyAllowance =
|
|
1030
|
+
var historyAllowance = Math.max(
|
|
1031
|
+
HISTORY_TOKEN_BUDGET,
|
|
1032
|
+
Math.round(contextBasedBudget * HISTORY_BUDGET_RATIO)
|
|
1033
|
+
);
|
|
928
1034
|
var budgetForHistory = Math.max(1e3, Math.min(historyAllowance, availableInputBudget - systemCost));
|
|
929
|
-
var maxHistoryMessages =
|
|
1035
|
+
var maxHistoryMessages = Math.max(
|
|
1036
|
+
MAX_HISTORY_MESSAGES,
|
|
1037
|
+
Math.round(MAX_HISTORY_MESSAGES * (budgetForHistory / HISTORY_TOKEN_BUDGET))
|
|
1038
|
+
);
|
|
930
1039
|
var windowed = options.history.slice(-maxHistoryMessages);
|
|
931
1040
|
var latestIndex = windowed.length - 1;
|
|
932
1041
|
var trimmed = windowed.map(function(m, i2) {
|
|
@@ -1319,7 +1428,6 @@ var WEB_FETCH_MAX_USES = 40;
|
|
|
1319
1428
|
var WEB_FETCH_MAX_CONTENT_TOKENS = 2e5;
|
|
1320
1429
|
var OPENAI_RESPONSES_API_URL = "https://api.openai.com/v1/responses";
|
|
1321
1430
|
var OPENAI_MODELS_API_URL = "https://api.openai.com/v1/models";
|
|
1322
|
-
var MAX_TOKENS = 25e3;
|
|
1323
1431
|
var DEFAULT_OPENAI_IMAGE_DETAIL = "auto";
|
|
1324
1432
|
var OPENAI_WEB_SEARCH_EXTERNAL_WEB_ACCESS = true;
|
|
1325
1433
|
var MCP_NAME = "BunnyQuery";
|
|
@@ -1559,7 +1667,7 @@ async function callClaudeWithPublicMcp(prompt, service, owner, messages, system,
|
|
|
1559
1667
|
owner,
|
|
1560
1668
|
userId,
|
|
1561
1669
|
model: model || DEFAULT_CLAUDE_MODEL,
|
|
1562
|
-
maxTokens:
|
|
1670
|
+
maxTokens: getMaxOutputTokens("claude", model || DEFAULT_CLAUDE_MODEL),
|
|
1563
1671
|
system,
|
|
1564
1672
|
extractContent,
|
|
1565
1673
|
fileUrls,
|
|
@@ -1604,7 +1712,7 @@ async function callOpenAIWithPublicMcp(prompt, service, owner, messages, system,
|
|
|
1604
1712
|
},
|
|
1605
1713
|
data: {
|
|
1606
1714
|
model: resolvedModel,
|
|
1607
|
-
max_output_tokens:
|
|
1715
|
+
max_output_tokens: getMaxOutputTokens("openai", resolvedModel),
|
|
1608
1716
|
...extractContent && extractContent.length ? { _skapi_extract: extractContent } : {},
|
|
1609
1717
|
...fileUrls && fileUrls.length ? { _skapi_file_urls: fileUrls } : {},
|
|
1610
1718
|
input: responseInput,
|
|
@@ -1634,6 +1742,29 @@ async function notifyAgentContinueIndexing(info) {
|
|
|
1634
1742
|
async function notifyAgentSaveAttachment(info) {
|
|
1635
1743
|
const { platform, service, owner, attachment, parsedContent } = info;
|
|
1636
1744
|
const continuing = !!info.continueIndexing;
|
|
1745
|
+
if (!continuing) {
|
|
1746
|
+
upsertIndexRunRecordSafe(service, attachment.storagePath, {
|
|
1747
|
+
status: "working",
|
|
1748
|
+
filename: attachment.name,
|
|
1749
|
+
started: Date.now(),
|
|
1750
|
+
queue: bgIndexingQueueName(info.userId, service),
|
|
1751
|
+
platform
|
|
1752
|
+
});
|
|
1753
|
+
}
|
|
1754
|
+
const tapDispatchFailure = (p) => {
|
|
1755
|
+
if (continuing) return p;
|
|
1756
|
+
return p.then(
|
|
1757
|
+
(ack) => ack,
|
|
1758
|
+
(err) => {
|
|
1759
|
+
upsertIndexRunRecordSafe(service, attachment.storagePath, {
|
|
1760
|
+
status: "error",
|
|
1761
|
+
finished: Date.now(),
|
|
1762
|
+
error: err && (err.message || String(err)) || "The indexing request could not be enqueued."
|
|
1763
|
+
});
|
|
1764
|
+
throw err;
|
|
1765
|
+
}
|
|
1766
|
+
);
|
|
1767
|
+
};
|
|
1637
1768
|
const visionFile = !parsedContent && isImageVisionFile(attachment.name, attachment.mime);
|
|
1638
1769
|
const renderFrom = Math.max(0, info.renderFrom || 0);
|
|
1639
1770
|
const renderPlaceholder = visionFile ? makeRenderPlaceholder(attachment.storagePath) : void 0;
|
|
@@ -1693,6 +1824,7 @@ async function notifyAgentSaveAttachment(info) {
|
|
|
1693
1824
|
save_media: !continuing
|
|
1694
1825
|
}))
|
|
1695
1826
|
} : {};
|
|
1827
|
+
const skapiFileUrls = attachment.url && attachment.storagePath ? { _skapi_file_urls: [{ path: attachment.storagePath, url: attachment.url }] } : {};
|
|
1696
1828
|
const userMessage = visionFile && renderPlaceholder ? buildIndexingRenderMessage(attachment, renderPlaceholder, renderFrom) : windowedRead && windowPlaceholder ? buildIndexingWindowMessage(attachment, windowPlaceholder, false) : continuing ? buildIndexingContinueMessage(attachment) : buildIndexingUserMessage(
|
|
1697
1829
|
attachment,
|
|
1698
1830
|
parsedContent ? { inlineContent: parsedContent } : placeholder ? { inlineContentPlaceholder: placeholder } : pagedRead ? { pagedRead: true } : void 0
|
|
@@ -1708,7 +1840,7 @@ async function notifyAgentSaveAttachment(info) {
|
|
|
1708
1840
|
if (platform === "openai") {
|
|
1709
1841
|
const resolvedModel2 = info.model || DEFAULT_OPENAI_MODEL;
|
|
1710
1842
|
const imageDetail = getOpenAIImageDetail(resolvedModel2);
|
|
1711
|
-
return clientSecretRequest({
|
|
1843
|
+
return tapDispatchFailure(clientSecretRequest({
|
|
1712
1844
|
clientSecretName: "openai",
|
|
1713
1845
|
queue: bgIndexingQueueName(info.userId, service),
|
|
1714
1846
|
service,
|
|
@@ -1722,12 +1854,13 @@ async function notifyAgentSaveAttachment(info) {
|
|
|
1722
1854
|
},
|
|
1723
1855
|
data: {
|
|
1724
1856
|
model: resolvedModel2,
|
|
1725
|
-
max_output_tokens:
|
|
1857
|
+
max_output_tokens: getMaxOutputTokens("openai", resolvedModel2),
|
|
1726
1858
|
// Nano-only transcription knobs. Indexing only; see variantIndexingOptions.
|
|
1727
1859
|
...variantIndexingOptions(resolvedModel2),
|
|
1728
1860
|
...skapiExtract,
|
|
1729
1861
|
...skapiRender,
|
|
1730
1862
|
...skapiWindow,
|
|
1863
|
+
...skapiFileUrls,
|
|
1731
1864
|
input: [
|
|
1732
1865
|
{ role: "system", content: systemPrompt },
|
|
1733
1866
|
{
|
|
@@ -1751,10 +1884,10 @@ async function notifyAgentSaveAttachment(info) {
|
|
|
1751
1884
|
]
|
|
1752
1885
|
]
|
|
1753
1886
|
}
|
|
1754
|
-
});
|
|
1887
|
+
}));
|
|
1755
1888
|
}
|
|
1756
1889
|
const resolvedModel = info.model || DEFAULT_CLAUDE_MODEL;
|
|
1757
|
-
return clientSecretRequest({
|
|
1890
|
+
return tapDispatchFailure(clientSecretRequest({
|
|
1758
1891
|
clientSecretName: "claude",
|
|
1759
1892
|
queue: bgIndexingQueueName(info.userId, service),
|
|
1760
1893
|
service,
|
|
@@ -1770,10 +1903,11 @@ async function notifyAgentSaveAttachment(info) {
|
|
|
1770
1903
|
},
|
|
1771
1904
|
data: {
|
|
1772
1905
|
model: resolvedModel,
|
|
1773
|
-
max_tokens:
|
|
1906
|
+
max_tokens: getMaxOutputTokens("claude", resolvedModel),
|
|
1774
1907
|
...skapiExtract,
|
|
1775
1908
|
...skapiRender,
|
|
1776
1909
|
...skapiWindow,
|
|
1910
|
+
...skapiFileUrls,
|
|
1777
1911
|
system: [
|
|
1778
1912
|
{
|
|
1779
1913
|
type: "text",
|
|
@@ -1809,7 +1943,7 @@ async function notifyAgentSaveAttachment(info) {
|
|
|
1809
1943
|
}
|
|
1810
1944
|
]
|
|
1811
1945
|
}
|
|
1812
|
-
});
|
|
1946
|
+
}));
|
|
1813
1947
|
}
|
|
1814
1948
|
function extractClaudeText(response) {
|
|
1815
1949
|
if (!Array.isArray(response?.content)) {
|
|
@@ -1870,6 +2004,21 @@ async function listOpenAIModels(service, owner) {
|
|
|
1870
2004
|
});
|
|
1871
2005
|
}
|
|
1872
2006
|
var BG_INDEXING_QUEUE_SUFFIX = "-bg";
|
|
2007
|
+
function indexDoneUniqueId(storagePath) {
|
|
2008
|
+
return "done::" + storagePath;
|
|
2009
|
+
}
|
|
2010
|
+
function runIndexUniqueId(storagePath) {
|
|
2011
|
+
return "run::" + storagePath;
|
|
2012
|
+
}
|
|
2013
|
+
function upsertIndexRunRecordSafe(service, storagePath, patch) {
|
|
2014
|
+
if (!service || !storagePath) return;
|
|
2015
|
+
try {
|
|
2016
|
+
const hook = chatEngineConfig().upsertIndexRunRecord;
|
|
2017
|
+
if (typeof hook !== "function") return;
|
|
2018
|
+
hook({ service, storagePath, patch });
|
|
2019
|
+
} catch (e) {
|
|
2020
|
+
}
|
|
2021
|
+
}
|
|
1873
2022
|
function bgIndexingQueueName(userId, service) {
|
|
1874
2023
|
return (userId || service || "") + BG_INDEXING_QUEUE_SUFFIX;
|
|
1875
2024
|
}
|
|
@@ -1893,13 +2042,20 @@ async function getChatHistory(params, fetchOptions) {
|
|
|
1893
2042
|
},
|
|
1894
2043
|
{ service: params.service, owner: params.owner },
|
|
1895
2044
|
params.queue ? { queue: params.queue } : {},
|
|
1896
|
-
params.status ? { status: params.status } : {}
|
|
2045
|
+
params.status ? { status: params.status } : {},
|
|
2046
|
+
params.queue_exact ? { queue_exact: true } : {},
|
|
2047
|
+
params.compact ? { compact: true } : {},
|
|
2048
|
+
params.queue_exclude ? { queue_exclude: params.queue_exclude } : {}
|
|
1897
2049
|
);
|
|
1898
2050
|
return chatEngineConfig().clientSecretRequestHistory(
|
|
1899
2051
|
p,
|
|
1900
2052
|
Object.assign({ ascending: false, limit: CHAT_HISTORY_PAGE_LIMIT }, fetchOptions)
|
|
1901
2053
|
);
|
|
1902
2054
|
}
|
|
2055
|
+
function buildHistoryItemFullId(platform, service, itemId) {
|
|
2056
|
+
const url = platform === "claude" ? ANTHROPIC_MESSAGES_API_URL : OPENAI_RESPONSES_API_URL;
|
|
2057
|
+
return `[POST]${url.toLowerCase()}#${service}:${itemId}`;
|
|
2058
|
+
}
|
|
1903
2059
|
|
|
1904
2060
|
// src/engine/history.ts
|
|
1905
2061
|
function filterListByClearHorizon(list, clearedAt) {
|
|
@@ -1949,6 +2105,278 @@ function parseIndexingRequestText(userText) {
|
|
|
1949
2105
|
continued: userText.indexOf("CONTINUE indexing") === 0
|
|
1950
2106
|
};
|
|
1951
2107
|
}
|
|
2108
|
+
var LIVE_INDEX_PROBE_LIMIT = 20;
|
|
2109
|
+
var BG_PROBE_TTL_MS = 4e3;
|
|
2110
|
+
var bgProbeCache = {};
|
|
2111
|
+
var bgProbeInflight = {};
|
|
2112
|
+
function probeBgQueue(params, opts) {
|
|
2113
|
+
const key = [params.service, params.owner, params.platform, params.queue, params.status, params.limit].join("|");
|
|
2114
|
+
const maxAge = opts && typeof opts.maxAgeMs === "number" ? opts.maxAgeMs : 0;
|
|
2115
|
+
const cached = bgProbeCache[key];
|
|
2116
|
+
if (maxAge > 0 && cached && Date.now() - cached.at < maxAge) {
|
|
2117
|
+
return Promise.resolve(cached);
|
|
2118
|
+
}
|
|
2119
|
+
const inflight = bgProbeInflight[key];
|
|
2120
|
+
if (inflight) return inflight;
|
|
2121
|
+
const p = Promise.resolve(getChatHistory(
|
|
2122
|
+
{ service: params.service, owner: params.owner, platform: params.platform, queue: params.queue, status: params.status },
|
|
2123
|
+
{ limit: params.limit, fetchMore: false }
|
|
2124
|
+
)).then(function(result) {
|
|
2125
|
+
const entry = { result, at: Date.now() };
|
|
2126
|
+
bgProbeCache[key] = entry;
|
|
2127
|
+
return entry;
|
|
2128
|
+
});
|
|
2129
|
+
bgProbeInflight[key] = p;
|
|
2130
|
+
p.then(function() {
|
|
2131
|
+
delete bgProbeInflight[key];
|
|
2132
|
+
}, function() {
|
|
2133
|
+
delete bgProbeInflight[key];
|
|
2134
|
+
});
|
|
2135
|
+
return p;
|
|
2136
|
+
}
|
|
2137
|
+
async function fetchLiveIndexingKeys(params) {
|
|
2138
|
+
const queue = bgIndexingQueueName(params.userId, params.service);
|
|
2139
|
+
const base = { service: params.service, owner: params.owner, platform: params.platform, queue };
|
|
2140
|
+
const [pending, running] = await Promise.all([
|
|
2141
|
+
probeBgQueue({ ...base, status: "pending", limit: LIVE_INDEX_PROBE_LIMIT }, { maxAgeMs: BG_PROBE_TTL_MS }),
|
|
2142
|
+
probeBgQueue({ ...base, status: "running", limit: LIVE_INDEX_PROBE_LIMIT }, { maxAgeMs: BG_PROBE_TTL_MS })
|
|
2143
|
+
]);
|
|
2144
|
+
const keys = /* @__PURE__ */ new Set();
|
|
2145
|
+
let truncated = false;
|
|
2146
|
+
for (const entry of [pending, running]) {
|
|
2147
|
+
const res = entry.result;
|
|
2148
|
+
const list = res && Array.isArray(res.list) ? res.list : [];
|
|
2149
|
+
if (list.length >= LIVE_INDEX_PROBE_LIMIT) truncated = true;
|
|
2150
|
+
for (const item of list) {
|
|
2151
|
+
const text = extractLastUserTextFromRequest(item && item.request_body);
|
|
2152
|
+
if (!text || !isIndexingRequestText(text)) continue;
|
|
2153
|
+
const ref = parseIndexingRequestText(text);
|
|
2154
|
+
if (!ref) continue;
|
|
2155
|
+
if (ref.path) keys.add(ref.path);
|
|
2156
|
+
if (ref.name) keys.add(ref.name);
|
|
2157
|
+
}
|
|
2158
|
+
}
|
|
2159
|
+
return { keys, checked: !truncated, at: Math.min(pending.at, running.at) };
|
|
2160
|
+
}
|
|
2161
|
+
var BG_COVERAGE_MAX_PAGES = 2;
|
|
2162
|
+
var splitHistoryStates = {};
|
|
2163
|
+
var splitHistoryLocks = {};
|
|
2164
|
+
function freshSplitState() {
|
|
2165
|
+
return { bgBuffer: [], bgEnd: false, bgStarted: false, surfaceEnd: false, pendingSurface: null, surfaceCarry: [], lastSurfaceKeys: [], newestBgId: "" };
|
|
2166
|
+
}
|
|
2167
|
+
function noteBgIds(state, list) {
|
|
2168
|
+
for (const it of list) {
|
|
2169
|
+
const id = it && typeof it.id === "string" ? it.id : "";
|
|
2170
|
+
if (id && id > state.newestBgId) state.newestBgId = id;
|
|
2171
|
+
}
|
|
2172
|
+
}
|
|
2173
|
+
function __resetSplitHistoryState(key) {
|
|
2174
|
+
if (key !== void 0) {
|
|
2175
|
+
delete splitHistoryStates[key];
|
|
2176
|
+
delete splitHistoryLocks[key];
|
|
2177
|
+
return;
|
|
2178
|
+
}
|
|
2179
|
+
for (const k in splitHistoryStates) delete splitHistoryStates[k];
|
|
2180
|
+
for (const k in splitHistoryLocks) delete splitHistoryLocks[k];
|
|
2181
|
+
}
|
|
2182
|
+
var createdOf = (it) => {
|
|
2183
|
+
const c = Number(it && it.created);
|
|
2184
|
+
return isFinite(c) && c > 0 ? c : NaN;
|
|
2185
|
+
};
|
|
2186
|
+
var oldestCreated = (lst) => {
|
|
2187
|
+
let m = Infinity;
|
|
2188
|
+
for (const it of lst) {
|
|
2189
|
+
const c = createdOf(it);
|
|
2190
|
+
if (!isNaN(c) && c < m) m = c;
|
|
2191
|
+
}
|
|
2192
|
+
return m;
|
|
2193
|
+
};
|
|
2194
|
+
var SURFACE_EMPTY_MAX_PAGES = 10;
|
|
2195
|
+
async function getSplitChatHistory(params, fetchOptions, _fetchImpl) {
|
|
2196
|
+
const key = [params.service, params.owner, params.platform, params.userId || ""].join("|");
|
|
2197
|
+
const prev = splitHistoryLocks[key] || Promise.resolve();
|
|
2198
|
+
let releaseLock;
|
|
2199
|
+
const lockTail = new Promise((r) => {
|
|
2200
|
+
releaseLock = r;
|
|
2201
|
+
});
|
|
2202
|
+
const run = () => _getSplitChatHistoryLocked(key, params, fetchOptions, releaseLock, _fetchImpl);
|
|
2203
|
+
const p = prev.then(run, run);
|
|
2204
|
+
p.then((res) => {
|
|
2205
|
+
if (!res || !res.bgPending) releaseLock();
|
|
2206
|
+
}, () => releaseLock());
|
|
2207
|
+
splitHistoryLocks[key] = p.then(() => lockTail, () => lockTail);
|
|
2208
|
+
return p;
|
|
2209
|
+
}
|
|
2210
|
+
async function _getSplitChatHistoryLocked(key, params, fetchOptions, releaseLock, _fetchImpl) {
|
|
2211
|
+
const fetch = _fetchImpl || getChatHistory;
|
|
2212
|
+
const bgQueue = bgIndexingQueueName(params.userId, params.service);
|
|
2213
|
+
const base = { service: params.service, owner: params.owner, platform: params.platform };
|
|
2214
|
+
const fetchMore = !!(fetchOptions && fetchOptions.fetchMore);
|
|
2215
|
+
const limit = fetchOptions && fetchOptions.limit;
|
|
2216
|
+
const firstLoad = !splitHistoryStates[key];
|
|
2217
|
+
let headRefresh = false;
|
|
2218
|
+
if (!splitHistoryStates[key]) {
|
|
2219
|
+
splitHistoryStates[key] = freshSplitState();
|
|
2220
|
+
} else if (!fetchMore) {
|
|
2221
|
+
const prev = splitHistoryStates[key];
|
|
2222
|
+
if (prev.surfaceEnd && prev.bgEnd) {
|
|
2223
|
+
headRefresh = true;
|
|
2224
|
+
prev.pendingSurface = null;
|
|
2225
|
+
prev.surfaceCarry = [];
|
|
2226
|
+
prev.bgBuffer = [];
|
|
2227
|
+
} else {
|
|
2228
|
+
splitHistoryStates[key] = freshSplitState();
|
|
2229
|
+
}
|
|
2230
|
+
}
|
|
2231
|
+
const state = splitHistoryStates[key];
|
|
2232
|
+
if (state.pendingSurface && state.pendingSurface.forFetchMore !== fetchMore) {
|
|
2233
|
+
state.pendingSurface = null;
|
|
2234
|
+
}
|
|
2235
|
+
if (!state.pendingSurface) {
|
|
2236
|
+
if (state.surfaceEnd && !headRefresh) {
|
|
2237
|
+
state.pendingSurface = { list: [], endOfList: true, startKeyHistory: state.lastSurfaceKeys, forFetchMore: fetchMore };
|
|
2238
|
+
} else {
|
|
2239
|
+
const sOpts = { fetchMore };
|
|
2240
|
+
if (limit) sOpts.limit = limit;
|
|
2241
|
+
let s = await fetch({ ...base, queue_exclude: bgQueue }, sOpts);
|
|
2242
|
+
let hops = 0;
|
|
2243
|
+
while (s && !s.endOfList && !(s.list || []).length && hops < SURFACE_EMPTY_MAX_PAGES) {
|
|
2244
|
+
hops++;
|
|
2245
|
+
const nOpts = { fetchMore: true };
|
|
2246
|
+
if (limit) nOpts.limit = limit;
|
|
2247
|
+
s = await fetch({ ...base, queue_exclude: bgQueue }, nOpts);
|
|
2248
|
+
}
|
|
2249
|
+
state.pendingSurface = {
|
|
2250
|
+
list: s && Array.isArray(s.list) ? s.list : [],
|
|
2251
|
+
endOfList: !!(s && s.endOfList),
|
|
2252
|
+
startKeyHistory: s && Array.isArray(s.startKeyHistory) ? s.startKeyHistory : [],
|
|
2253
|
+
forFetchMore: fetchMore
|
|
2254
|
+
};
|
|
2255
|
+
}
|
|
2256
|
+
}
|
|
2257
|
+
const surface = state.pendingSurface;
|
|
2258
|
+
if (fetchOptions && fetchOptions.deferBg && (!state.bgEnd || headRefresh)) {
|
|
2259
|
+
const surfaceList0 = state.surfaceCarry.length ? state.surfaceCarry.concat(surface.list) : surface.list.slice();
|
|
2260
|
+
state.surfaceCarry = [];
|
|
2261
|
+
const emitNow = surfaceList0.concat(state.bgBuffer);
|
|
2262
|
+
state.bgBuffer = [];
|
|
2263
|
+
if (!headRefresh) state.surfaceEnd = surface.endOfList;
|
|
2264
|
+
state.lastSurfaceKeys = surface.startKeyHistory;
|
|
2265
|
+
state.pendingSurface = null;
|
|
2266
|
+
const bgPending = (async () => {
|
|
2267
|
+
try {
|
|
2268
|
+
const batch = [];
|
|
2269
|
+
if (headRefresh) {
|
|
2270
|
+
const bOpts = { fetchMore: false };
|
|
2271
|
+
if (limit) bOpts.limit = limit;
|
|
2272
|
+
const b = await fetch({ ...base, queue: bgQueue, queue_exact: true, compact: true }, bOpts);
|
|
2273
|
+
const bList = b && Array.isArray(b.list) ? b.list : [];
|
|
2274
|
+
for (const it of bList) {
|
|
2275
|
+
if (it && typeof it === "object") it._fromBgChain = true;
|
|
2276
|
+
batch.push(it);
|
|
2277
|
+
}
|
|
2278
|
+
const prevNewest = state.newestBgId;
|
|
2279
|
+
noteBgIds(state, bList);
|
|
2280
|
+
if (prevNewest && !(b && b.endOfList) && !bList.some((it) => it && it.id === prevNewest)) {
|
|
2281
|
+
state.bgEnd = false;
|
|
2282
|
+
state.bgStarted = true;
|
|
2283
|
+
}
|
|
2284
|
+
} else {
|
|
2285
|
+
let hops = 0;
|
|
2286
|
+
while (!state.bgEnd && hops < BG_COVERAGE_MAX_PAGES) {
|
|
2287
|
+
hops++;
|
|
2288
|
+
const bOpts = { fetchMore: state.bgStarted };
|
|
2289
|
+
if (limit) bOpts.limit = limit;
|
|
2290
|
+
const b = await fetch({ ...base, queue: bgQueue, queue_exact: true, compact: true }, bOpts);
|
|
2291
|
+
state.bgStarted = true;
|
|
2292
|
+
const bList = b && Array.isArray(b.list) ? b.list : [];
|
|
2293
|
+
for (const it of bList) {
|
|
2294
|
+
if (it && typeof it === "object") it._fromBgChain = true;
|
|
2295
|
+
batch.push(it);
|
|
2296
|
+
}
|
|
2297
|
+
noteBgIds(state, bList);
|
|
2298
|
+
state.bgEnd = !!(b && b.endOfList);
|
|
2299
|
+
if (!bList.length && !state.bgEnd) break;
|
|
2300
|
+
if (state.bgEnd) break;
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
return { list: batch, endOfList: state.surfaceEnd && state.bgEnd };
|
|
2304
|
+
} finally {
|
|
2305
|
+
releaseLock();
|
|
2306
|
+
}
|
|
2307
|
+
})();
|
|
2308
|
+
return {
|
|
2309
|
+
list: emitNow,
|
|
2310
|
+
// A head-refreshed ended chain KNOWS it is still ended — reporting
|
|
2311
|
+
// the hardcoded false here was what un-gated the fill loop on every
|
|
2312
|
+
// tab return. Mid-walk it computes to false exactly as before (this
|
|
2313
|
+
// branch is only entered with bgEnd false then); the bg batch still
|
|
2314
|
+
// carries the final word for that case.
|
|
2315
|
+
endOfList: state.surfaceEnd && state.bgEnd,
|
|
2316
|
+
startKeyHistory: surface.startKeyHistory,
|
|
2317
|
+
firstLoad,
|
|
2318
|
+
bgPending
|
|
2319
|
+
};
|
|
2320
|
+
}
|
|
2321
|
+
const surfaceList = state.surfaceCarry.length ? state.surfaceCarry.concat(surface.list) : surface.list.slice();
|
|
2322
|
+
const boundary = surface.endOfList ? -Infinity : oldestCreated(surfaceList);
|
|
2323
|
+
if (headRefresh) {
|
|
2324
|
+
const hOpts = { fetchMore: false };
|
|
2325
|
+
if (limit) hOpts.limit = limit;
|
|
2326
|
+
const hb = await fetch({ ...base, queue: bgQueue, queue_exact: true, compact: true }, hOpts);
|
|
2327
|
+
const hbList = hb && Array.isArray(hb.list) ? hb.list : [];
|
|
2328
|
+
for (const it of hbList) {
|
|
2329
|
+
if (it && typeof it === "object") it._fromBgChain = true;
|
|
2330
|
+
state.bgBuffer.push(it);
|
|
2331
|
+
}
|
|
2332
|
+
const prevNewestH = state.newestBgId;
|
|
2333
|
+
noteBgIds(state, hbList);
|
|
2334
|
+
if (prevNewestH && !(hb && hb.endOfList) && !hbList.some((it) => it && it.id === prevNewestH)) {
|
|
2335
|
+
state.bgEnd = false;
|
|
2336
|
+
state.bgStarted = true;
|
|
2337
|
+
}
|
|
2338
|
+
} else if (boundary !== Infinity || surface.endOfList) {
|
|
2339
|
+
let hops = 0;
|
|
2340
|
+
while (!state.bgEnd && hops < BG_COVERAGE_MAX_PAGES) {
|
|
2341
|
+
const bufOldest = state.bgBuffer.length ? oldestCreated(state.bgBuffer) : Infinity;
|
|
2342
|
+
if (state.bgBuffer.length && bufOldest <= boundary) break;
|
|
2343
|
+
hops++;
|
|
2344
|
+
const bOpts = { fetchMore: state.bgStarted };
|
|
2345
|
+
if (limit) bOpts.limit = limit;
|
|
2346
|
+
const b = await fetch({ ...base, queue: bgQueue, queue_exact: true, compact: true }, bOpts);
|
|
2347
|
+
state.bgStarted = true;
|
|
2348
|
+
const bList = b && Array.isArray(b.list) ? b.list : [];
|
|
2349
|
+
for (const it of bList) {
|
|
2350
|
+
if (it && typeof it === "object") it._fromBgChain = true;
|
|
2351
|
+
state.bgBuffer.push(it);
|
|
2352
|
+
}
|
|
2353
|
+
noteBgIds(state, bList);
|
|
2354
|
+
state.bgEnd = !!(b && b.endOfList);
|
|
2355
|
+
if (!bList.length && !state.bgEnd) break;
|
|
2356
|
+
if (state.bgEnd) break;
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
2359
|
+
const emitSurface = surfaceList;
|
|
2360
|
+
state.surfaceCarry = [];
|
|
2361
|
+
const emitBg = state.bgBuffer;
|
|
2362
|
+
state.bgBuffer = [];
|
|
2363
|
+
const seen = {};
|
|
2364
|
+
for (const it of emitSurface) {
|
|
2365
|
+
if (it && typeof it.id === "string") seen[it.id] = true;
|
|
2366
|
+
}
|
|
2367
|
+
const merged = emitSurface.concat(emitBg.filter((it) => !(it && typeof it.id === "string" && seen[it.id])));
|
|
2368
|
+
if (!headRefresh) state.surfaceEnd = surface.endOfList;
|
|
2369
|
+
state.lastSurfaceKeys = surface.startKeyHistory;
|
|
2370
|
+
state.pendingSurface = null;
|
|
2371
|
+
return {
|
|
2372
|
+
list: merged,
|
|
2373
|
+
endOfList: state.surfaceEnd && state.bgEnd && state.bgBuffer.length === 0 && state.surfaceCarry.length === 0,
|
|
2374
|
+
// Bookkeeping only (both the consumers and the SDK treat it opaquely);
|
|
2375
|
+
// the real cursors are the SDK's internal ones plus this module's state.
|
|
2376
|
+
startKeyHistory: surface.startKeyHistory,
|
|
2377
|
+
firstLoad
|
|
2378
|
+
};
|
|
2379
|
+
}
|
|
1952
2380
|
function mapHistoryListToMessages(list, platform, opts) {
|
|
1953
2381
|
var mapped = [], runningItemIds = [];
|
|
1954
2382
|
var extractAssistantText = platform === "openai" ? extractOpenAIText : extractClaudeText;
|
|
@@ -1961,10 +2389,11 @@ function mapHistoryListToMessages(list, platform, opts) {
|
|
|
1961
2389
|
var isPending = isInProcess || isQueued;
|
|
1962
2390
|
var isFailed = item && item.status === "failed";
|
|
1963
2391
|
var response = isFailed ? item.error != null ? item.error : item.response_body : item && item.response_body != null ? item.response_body : item && item.error;
|
|
1964
|
-
var
|
|
1965
|
-
var
|
|
1966
|
-
var
|
|
1967
|
-
var
|
|
2392
|
+
var isCompact = !!(item && item.compact);
|
|
2393
|
+
var userText = isCompact ? typeof item.request_text === "string" ? item.request_text : "" : extractLastUserTextFromRequest(requestBody);
|
|
2394
|
+
var assistantText = isPending ? "" : isCompact ? (typeof item.response_text === "string" ? item.response_text : "").trim() : (extractAssistantText(response) || "").trim() || "";
|
|
2395
|
+
var isErrorResponse = !isPending && (isFailed || !isCompact && isErrorResponseBody(response));
|
|
2396
|
+
var reportedComplete = !!(item && item._isBgTask) && !isErrorResponse && (isCompact ? item.response_complete_marker === true : !!assistantText && assistantText.indexOf(INDEXING_COMPLETE_MARKER) !== -1);
|
|
1968
2397
|
if (reportedComplete) assistantText = assistantText.split(INDEXING_COMPLETE_MARKER).join("").trim();
|
|
1969
2398
|
var serverItemId = item && typeof item.id === "string" && item.id ? item.id : void 0;
|
|
1970
2399
|
var createdTs = Number(item && item.created);
|
|
@@ -1993,9 +2422,11 @@ function mapHistoryListToMessages(list, platform, opts) {
|
|
|
1993
2422
|
displayContent = sanitizeAttachmentLinksForHistory(userText, opts.projectId);
|
|
1994
2423
|
}
|
|
1995
2424
|
var userMsg = { role: "user", content: displayContent };
|
|
2425
|
+
if (item._fromBgChain) userMsg._fromBgChain = true;
|
|
1996
2426
|
if (isInProcess) userMsg.isPendingInProcess = true;
|
|
1997
2427
|
if (isQueued) userMsg.isPendingQueued = true;
|
|
1998
2428
|
if (isCancelledItem) userMsg.isCancelled = true;
|
|
2429
|
+
if (isCompact) userMsg._compact = true;
|
|
1999
2430
|
if (item._isBgTask) userMsg.isBackgroundTask = true;
|
|
2000
2431
|
if (indexFile) userMsg._indexFile = indexFile;
|
|
2001
2432
|
if (item._isOnBgQueue) userMsg._useBgQueue = true;
|
|
@@ -2005,6 +2436,8 @@ function mapHistoryListToMessages(list, platform, opts) {
|
|
|
2005
2436
|
}
|
|
2006
2437
|
if (isCancelledItem) ; else if (isInProcess) {
|
|
2007
2438
|
var ph = { role: "assistant", content: "", isPending: true, isPendingInProcess: true };
|
|
2439
|
+
if (userTs !== void 0) ph._ts = userTs;
|
|
2440
|
+
if (item._fromBgChain) ph._fromBgChain = true;
|
|
2008
2441
|
if (item._isBgTask) ph.isBackgroundTask = true;
|
|
2009
2442
|
if (serverItemId !== void 0) {
|
|
2010
2443
|
ph._serverItemId = serverItemId;
|
|
@@ -2013,19 +2446,26 @@ function mapHistoryListToMessages(list, platform, opts) {
|
|
|
2013
2446
|
mapped.push(ph);
|
|
2014
2447
|
} else if (isQueued) ; else if (isErrorResponse) {
|
|
2015
2448
|
var em = { role: "assistant", content: getErrorMessage(response), isError: true };
|
|
2449
|
+
if (item._fromBgChain) em._fromBgChain = true;
|
|
2016
2450
|
if (item._isBgTask) em.isBackgroundTask = true;
|
|
2017
2451
|
if (serverItemId !== void 0) em._serverItemId = serverItemId;
|
|
2018
2452
|
if (replyTs !== void 0) em._ts = replyTs;
|
|
2019
2453
|
mapped.push(em);
|
|
2020
2454
|
} else if (assistantText || reportedComplete) {
|
|
2021
2455
|
var okm = { role: "assistant", content: sanitizeAttachmentLinksForHistory(assistantText, opts.projectId, true) || EMPTY_INDEXING_REPLY };
|
|
2456
|
+
if (item._fromBgChain) okm._fromBgChain = true;
|
|
2022
2457
|
if (item._isBgTask) okm.isBackgroundTask = true;
|
|
2458
|
+
if (isCompact) okm._compact = true;
|
|
2023
2459
|
if (serverItemId !== void 0) okm._serverItemId = serverItemId;
|
|
2024
2460
|
if (replyTs !== void 0) okm._ts = replyTs;
|
|
2025
2461
|
if (reportedComplete) okm._indexComplete = true;
|
|
2026
2462
|
mapped.push(okm);
|
|
2027
2463
|
}
|
|
2028
2464
|
});
|
|
2465
|
+
if (opts.projectId) {
|
|
2466
|
+
var ownerKey = opts.projectId + "#" + platform;
|
|
2467
|
+
for (var oi = 0; oi < mapped.length; oi++) mapped[oi]._ownerKey = ownerKey;
|
|
2468
|
+
}
|
|
2029
2469
|
return { messages: mapped, runningItemIds };
|
|
2030
2470
|
}
|
|
2031
2471
|
|
|
@@ -2143,6 +2583,7 @@ var INDEXING_DRAIN_BUSY_POLL_MS = 8e3;
|
|
|
2143
2583
|
var INDEXING_DRAIN_CONFIRM_POLL_MS = 3e3;
|
|
2144
2584
|
var INDEXING_DRAIN_IDLE_LOOKS = 2;
|
|
2145
2585
|
var INDEXING_DRAIN_MIN_MS = 8e3;
|
|
2586
|
+
var _bgHistoryBatchSeq = 0;
|
|
2146
2587
|
var INDEXING_DRAIN_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
2147
2588
|
var INDEXING_DRAIN_LOOK_TIMEOUT_MS = 45e3;
|
|
2148
2589
|
var INDEXING_DRAIN_NUDGE_MIN_GAP_MS = 1500;
|
|
@@ -2164,6 +2605,14 @@ function isPollStopped(res) {
|
|
|
2164
2605
|
}
|
|
2165
2606
|
var ChatSession = class {
|
|
2166
2607
|
constructor(host) {
|
|
2608
|
+
// ─── compact-stub hydration ─────────────────────────────────────────────
|
|
2609
|
+
// Split-fetch bg pages arrive as label stubs (no bodies). When the user
|
|
2610
|
+
// expands a row, the real reply text is fetched per item (csr-poll point
|
|
2611
|
+
// lookup) and MEMOIZED per chat: every later remap (first-page refresh,
|
|
2612
|
+
// queue-detect tick, cache restore) re-applies the memo, so a hydrated
|
|
2613
|
+
// bubble can never silently revert to its 200-char head.
|
|
2614
|
+
this._hydratedBodies = {};
|
|
2615
|
+
this._hydratingItems = {};
|
|
2167
2616
|
this.typewriterQueue = Promise.resolve();
|
|
2168
2617
|
/**
|
|
2169
2618
|
* Pick up indexing passes the WORKER minted, which no client ever dispatched.
|
|
@@ -2204,6 +2653,9 @@ var ChatSession = class {
|
|
|
2204
2653
|
typingAbort: false,
|
|
2205
2654
|
loadingHistory: false,
|
|
2206
2655
|
loadingOlderHistory: false,
|
|
2656
|
+
// A deferred bg stub batch (first-paint split) is still in flight; the
|
|
2657
|
+
// views show a small 'loading indexing history' hint while true.
|
|
2658
|
+
bgHistoryLoading: false,
|
|
2207
2659
|
historyEndOfList: false,
|
|
2208
2660
|
historyStartKeyHistory: [],
|
|
2209
2661
|
historyRequestToken: 0,
|
|
@@ -2353,10 +2805,12 @@ var ChatSession = class {
|
|
|
2353
2805
|
}
|
|
2354
2806
|
var queue = bgIndexingQueueName(id.userId, id.projectId);
|
|
2355
2807
|
var ask = function(status) {
|
|
2356
|
-
return Promise.resolve(
|
|
2357
|
-
{ service: id.projectId, owner: id.owner, platform, queue, status },
|
|
2358
|
-
{
|
|
2359
|
-
)).
|
|
2808
|
+
return Promise.resolve(probeBgQueue(
|
|
2809
|
+
{ service: id.projectId, owner: id.owner, platform, queue, status, limit: WORKER_PASS_ADOPT_LIMIT },
|
|
2810
|
+
{ maxAgeMs: BG_PROBE_TTL_MS }
|
|
2811
|
+
)).then(function(entry) {
|
|
2812
|
+
return entry.result;
|
|
2813
|
+
}).catch(function() {
|
|
2360
2814
|
return null;
|
|
2361
2815
|
});
|
|
2362
2816
|
};
|
|
@@ -2448,7 +2902,7 @@ var ChatSession = class {
|
|
|
2448
2902
|
* instead of merely unconfirmed.
|
|
2449
2903
|
*/
|
|
2450
2904
|
refreshLiveIndexState() {
|
|
2451
|
-
this._adoptWorkerIndexingPasses(0);
|
|
2905
|
+
this._adoptWorkerIndexingPasses(0, true);
|
|
2452
2906
|
}
|
|
2453
2907
|
/** Forget what we know about which files are indexing — but ONLY when the
|
|
2454
2908
|
* snapshot was taken for a different chat than the one on screen now. For a
|
|
@@ -2625,6 +3079,66 @@ var ChatSession = class {
|
|
|
2625
3079
|
if (!id.projectId || id.platform === "none") return "";
|
|
2626
3080
|
return id.projectId + "#" + id.platform;
|
|
2627
3081
|
}
|
|
3082
|
+
/** Re-apply memoized hydrated texts onto freshly-mapped messages. Both
|
|
3083
|
+
* clients call this right after their mapper runs (loadHistory does it
|
|
3084
|
+
* internally); it mutates the given array's items in place. */
|
|
3085
|
+
applyHydratedBodies(messages) {
|
|
3086
|
+
var key = this.getHistoryCacheKey();
|
|
3087
|
+
var memo = key ? this._hydratedBodies[key] : null;
|
|
3088
|
+
if (!memo) return;
|
|
3089
|
+
var id = this.host.getIdentity();
|
|
3090
|
+
for (var i = 0; i < messages.length; i++) {
|
|
3091
|
+
var m = messages[i];
|
|
3092
|
+
if (!m || !m._compact || m.role !== "assistant" || !m._serverItemId) continue;
|
|
3093
|
+
var text = memo[m._serverItemId];
|
|
3094
|
+
if (typeof text !== "string") continue;
|
|
3095
|
+
m.content = sanitizeAttachmentLinksForHistory(text, id.projectId, true) || EMPTY_INDEXING_REPLY;
|
|
3096
|
+
delete m._compact;
|
|
3097
|
+
}
|
|
3098
|
+
}
|
|
3099
|
+
/** Fetch the real response bodies for compact history stubs (one csr-poll
|
|
3100
|
+
* point lookup per item id), memoize, and swap them into the live list.
|
|
3101
|
+
* Best-effort: a failed lookup leaves the stub (its head + fallback line
|
|
3102
|
+
* still render) and a later expand retries. */
|
|
3103
|
+
hydrateCompactItems(itemIds) {
|
|
3104
|
+
var self = this;
|
|
3105
|
+
var lookup = chatEngineConfig().csrHistoryItemLookup;
|
|
3106
|
+
if (!lookup || !itemIds || !itemIds.length) return Promise.resolve();
|
|
3107
|
+
var id = this.host.getIdentity();
|
|
3108
|
+
var platform = id.platform;
|
|
3109
|
+
if (!id.projectId || platform !== "claude" && platform !== "openai") return Promise.resolve();
|
|
3110
|
+
var chatKey = this.getHistoryCacheKey();
|
|
3111
|
+
if (!chatKey) return Promise.resolve();
|
|
3112
|
+
var jobs = itemIds.map(function(itemId) {
|
|
3113
|
+
if (!itemId) return Promise.resolve();
|
|
3114
|
+
var already = self._hydratedBodies[chatKey] && self._hydratedBodies[chatKey][itemId] !== void 0;
|
|
3115
|
+
var inflightKey = chatKey + "|" + itemId;
|
|
3116
|
+
if (already || self._hydratingItems[inflightKey]) return Promise.resolve();
|
|
3117
|
+
self._hydratingItems[inflightKey] = true;
|
|
3118
|
+
return Promise.resolve(lookup(buildHistoryItemFullId(platform, id.projectId, itemId), id.projectId, id.owner)).then(function(body) {
|
|
3119
|
+
var text = ((platform === "openai" ? extractOpenAIText(body) : extractClaudeText(body)) || "").trim();
|
|
3120
|
+
if (text.indexOf(INDEXING_COMPLETE_MARKER) !== -1) text = text.split(INDEXING_COMPLETE_MARKER).join("").trim();
|
|
3121
|
+
if (!self._hydratedBodies[chatKey]) self._hydratedBodies[chatKey] = {};
|
|
3122
|
+
self._hydratedBodies[chatKey][itemId] = text;
|
|
3123
|
+
if (self.getHistoryCacheKey() !== chatKey) return;
|
|
3124
|
+
for (var i = 0; i < self.state.messages.length; i++) {
|
|
3125
|
+
var m = self.state.messages[i];
|
|
3126
|
+
if (m && m._compact && m.role === "assistant" && m._serverItemId === itemId) {
|
|
3127
|
+
m.content = sanitizeAttachmentLinksForHistory(text, id.projectId, true) || EMPTY_INDEXING_REPLY;
|
|
3128
|
+
delete m._compact;
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
}).catch(function() {
|
|
3132
|
+
}).then(function() {
|
|
3133
|
+
delete self._hydratingItems[inflightKey];
|
|
3134
|
+
});
|
|
3135
|
+
});
|
|
3136
|
+
return Promise.all(jobs).then(function() {
|
|
3137
|
+
if (self.getHistoryCacheKey() !== chatKey) return;
|
|
3138
|
+
self.host.notify();
|
|
3139
|
+
self.updateHistoryCache();
|
|
3140
|
+
});
|
|
3141
|
+
}
|
|
2628
3142
|
updateHistoryCache() {
|
|
2629
3143
|
var key = this.getHistoryCacheKey();
|
|
2630
3144
|
if (!key) return;
|
|
@@ -2947,11 +3461,11 @@ var ChatSession = class {
|
|
|
2947
3461
|
bail = setTimeout(function() {
|
|
2948
3462
|
settle(null);
|
|
2949
3463
|
}, INDEXING_DRAIN_LOOK_TIMEOUT_MS);
|
|
2950
|
-
Promise.resolve(
|
|
2951
|
-
{ service: svcId, owner, platform, queue, status },
|
|
2952
|
-
{
|
|
2953
|
-
)).then(function(
|
|
2954
|
-
settle(
|
|
3464
|
+
Promise.resolve(probeBgQueue(
|
|
3465
|
+
{ service: svcId, owner, platform, queue, status, limit: WORKER_PASS_ADOPT_LIMIT },
|
|
3466
|
+
{ maxAgeMs: 0 }
|
|
3467
|
+
)).then(function(entry) {
|
|
3468
|
+
settle(entry.result);
|
|
2955
3469
|
}, function() {
|
|
2956
3470
|
settle(null);
|
|
2957
3471
|
});
|
|
@@ -3578,6 +4092,32 @@ var ChatSession = class {
|
|
|
3578
4092
|
if (e && e.id && self._indexKeyOf(e) === scoped) stoppedIds[e.id] = true;
|
|
3579
4093
|
});
|
|
3580
4094
|
this.state.stoppedIndexIds = stoppedIds;
|
|
4095
|
+
var runPath = group.path || "";
|
|
4096
|
+
if (!runPath) {
|
|
4097
|
+
(group.members || []).some(function(m) {
|
|
4098
|
+
var p = m && m.msg && m.msg._indexFile && m.msg._indexFile.path;
|
|
4099
|
+
if (p) {
|
|
4100
|
+
runPath = p;
|
|
4101
|
+
return true;
|
|
4102
|
+
}
|
|
4103
|
+
return false;
|
|
4104
|
+
});
|
|
4105
|
+
}
|
|
4106
|
+
if (!runPath) {
|
|
4107
|
+
this.bgTaskQueue.some(function(e) {
|
|
4108
|
+
if (e && e.storagePath && self._indexKeyOf(e) === scoped) {
|
|
4109
|
+
runPath = e.storagePath;
|
|
4110
|
+
return true;
|
|
4111
|
+
}
|
|
4112
|
+
return false;
|
|
4113
|
+
});
|
|
4114
|
+
}
|
|
4115
|
+
if (runPath) {
|
|
4116
|
+
var ident = this.host.getIdentity();
|
|
4117
|
+
if (ident && ident.projectId) {
|
|
4118
|
+
upsertIndexRunRecordSafe(ident.projectId, runPath, { status: "cancelled", finished: Date.now() });
|
|
4119
|
+
}
|
|
4120
|
+
}
|
|
3581
4121
|
}
|
|
3582
4122
|
this._adoptWorkerIndexingPasses(0);
|
|
3583
4123
|
var ids = group.cancellableIds || [];
|
|
@@ -4091,7 +4631,7 @@ var ChatSession = class {
|
|
|
4091
4631
|
if (isImageVisionFile(filename, mime)) return true;
|
|
4092
4632
|
return windowedIndexingEnabled() && isWindowedReadFile(filename, mime);
|
|
4093
4633
|
}
|
|
4094
|
-
_adoptWorkerIndexingPasses(attempt) {
|
|
4634
|
+
_adoptWorkerIndexingPasses(attempt, passive) {
|
|
4095
4635
|
var self = this;
|
|
4096
4636
|
if (this._adoptingWorkerPasses) return;
|
|
4097
4637
|
var id = this.host.getIdentity();
|
|
@@ -4101,10 +4641,12 @@ var ChatSession = class {
|
|
|
4101
4641
|
var svcId = id.projectId, owner = id.owner;
|
|
4102
4642
|
var queue = bgIndexingQueueName(id.userId, id.projectId);
|
|
4103
4643
|
var ask = function(status) {
|
|
4104
|
-
return Promise.resolve(
|
|
4105
|
-
{ service: svcId, owner, platform, queue, status },
|
|
4106
|
-
{
|
|
4107
|
-
)).
|
|
4644
|
+
return Promise.resolve(probeBgQueue(
|
|
4645
|
+
{ service: svcId, owner, platform, queue, status, limit: WORKER_PASS_ADOPT_LIMIT },
|
|
4646
|
+
{ maxAgeMs: 0 }
|
|
4647
|
+
)).then(function(entry) {
|
|
4648
|
+
return entry.result;
|
|
4649
|
+
}).catch(function() {
|
|
4108
4650
|
return null;
|
|
4109
4651
|
});
|
|
4110
4652
|
};
|
|
@@ -4126,6 +4668,7 @@ var ChatSession = class {
|
|
|
4126
4668
|
self.drainBgTaskQueue();
|
|
4127
4669
|
if (self._isTrackingAny(adoptedIds)) return;
|
|
4128
4670
|
}
|
|
4671
|
+
if (passive && !self._hasLiveIndexEvidence(svcId)) return;
|
|
4129
4672
|
if (attempt + 1 >= WORKER_PASS_ADOPT_ATTEMPTS.length) {
|
|
4130
4673
|
self._nudgeIndexingDrain();
|
|
4131
4674
|
return;
|
|
@@ -4134,12 +4677,30 @@ var ChatSession = class {
|
|
|
4134
4677
|
var later = self.host.getIdentity();
|
|
4135
4678
|
if (later.projectId !== svcId || later.platform !== platform) return;
|
|
4136
4679
|
if (self.isPollingPaused() || !self.host.isViewMounted()) return;
|
|
4137
|
-
self._adoptWorkerIndexingPasses(attempt + 1);
|
|
4680
|
+
self._adoptWorkerIndexingPasses(attempt + 1, passive);
|
|
4138
4681
|
}, WORKER_PASS_ADOPT_ATTEMPTS[attempt + 1]);
|
|
4139
4682
|
}, function() {
|
|
4140
4683
|
self._adoptingWorkerPasses = false;
|
|
4141
4684
|
});
|
|
4142
4685
|
}
|
|
4686
|
+
/** Anything at all suggesting THIS project's indexing may be live: a queued
|
|
4687
|
+
* local entry, a recorded live key (the adopt look just wrote them), or an
|
|
4688
|
+
* attached poll. Gates the passive adopt ladder's climb. */
|
|
4689
|
+
_hasLiveIndexEvidence(svcId) {
|
|
4690
|
+
for (var i = 0; i < this.bgTaskQueue.length; i++) {
|
|
4691
|
+
var e = this.bgTaskQueue[i];
|
|
4692
|
+
if (e && e.projectId === svcId) return true;
|
|
4693
|
+
}
|
|
4694
|
+
var keys = this.state.liveIndexKeys || {};
|
|
4695
|
+
for (var k in keys) {
|
|
4696
|
+
if (keys[k]) return true;
|
|
4697
|
+
}
|
|
4698
|
+
var found = false;
|
|
4699
|
+
this.historyItemPolls.forEach(function(h) {
|
|
4700
|
+
if (h && h.kind === "bg") found = true;
|
|
4701
|
+
});
|
|
4702
|
+
return found;
|
|
4703
|
+
}
|
|
4143
4704
|
/** Any of these ids still queued or still polled, i.e. surviving work. */
|
|
4144
4705
|
_isTrackingAny(ids) {
|
|
4145
4706
|
for (var i = 0; i < ids.length; i++) {
|
|
@@ -4230,7 +4791,10 @@ var ChatSession = class {
|
|
|
4230
4791
|
for (var i = this.bgTaskQueue.length - 1; i >= 0; i--) {
|
|
4231
4792
|
var e = this.bgTaskQueue[i];
|
|
4232
4793
|
if (e.projectId !== svcId || e.platform !== plat) continue;
|
|
4233
|
-
if (presentIds[e.id] && !pendingIds[e.id])
|
|
4794
|
+
if (presentIds[e.id] && !pendingIds[e.id]) {
|
|
4795
|
+
this._flipRunFromSettledEntry(e);
|
|
4796
|
+
this.bgTaskQueue.splice(i, 1);
|
|
4797
|
+
}
|
|
4234
4798
|
}
|
|
4235
4799
|
var bgPollBudget = MAX_CONCURRENT_BG_POLLS - this._countBgPolls();
|
|
4236
4800
|
var injectedAny = false;
|
|
@@ -4304,6 +4868,8 @@ var ChatSession = class {
|
|
|
4304
4868
|
self.host.notify();
|
|
4305
4869
|
self.updateHistoryCache();
|
|
4306
4870
|
if (!self._isWorkerDrivenIndexing(capturedEntry.filename, capturedEntry.mime)) {
|
|
4871
|
+
if (isNotExists) self._flipRunRecord(capturedEntry, "cancelled");
|
|
4872
|
+
else self._flipRunRecord(capturedEntry, "error", self._runErrorText(err));
|
|
4307
4873
|
self._nudgeIndexingDrain();
|
|
4308
4874
|
}
|
|
4309
4875
|
}).then(function() {
|
|
@@ -4335,6 +4901,74 @@ var ChatSession = class {
|
|
|
4335
4901
|
// memory (a reload or a closed tab ended it), and it stopped whenever the model claimed
|
|
4336
4902
|
// completion, which on an 88-page file happened at page 15. Continuing to dispatch here
|
|
4337
4903
|
// as well would now double-index every window.
|
|
4904
|
+
/** Fire the consumer's done::-marker hook for a run whose completion this
|
|
4905
|
+
* client knows DETERMINISTICALLY (see the two call sites in
|
|
4906
|
+
* maybeResumeIndexing). Best-effort by contract; identity-checked so a
|
|
4907
|
+
* project switch mid-settle cannot stamp the wrong service. */
|
|
4908
|
+
_mintDoneMarker(entry) {
|
|
4909
|
+
try {
|
|
4910
|
+
var mint = chatEngineConfig().mintIndexDoneMarker;
|
|
4911
|
+
if (!mint || !entry || !entry.storagePath || !entry.projectId) return;
|
|
4912
|
+
var id = this.host.getIdentity();
|
|
4913
|
+
if (!id || id.projectId !== entry.projectId) return;
|
|
4914
|
+
mint({ service: entry.projectId, storagePath: entry.storagePath });
|
|
4915
|
+
} catch (_e) {
|
|
4916
|
+
}
|
|
4917
|
+
}
|
|
4918
|
+
/** Short, storable form of an error body for the run:: record. */
|
|
4919
|
+
_runErrorText(response) {
|
|
4920
|
+
var msg = "";
|
|
4921
|
+
try {
|
|
4922
|
+
msg = String(getErrorMessage(response) || "");
|
|
4923
|
+
} catch (_e) {
|
|
4924
|
+
}
|
|
4925
|
+
msg = msg.replace(/\s+/g, " ").trim();
|
|
4926
|
+
return msg ? msg.slice(0, 300) : "Indexing failed.";
|
|
4927
|
+
}
|
|
4928
|
+
/** Close the records of a run whose pass settled OFF-POLL — the answer came
|
|
4929
|
+
* back as history (hidden tab, dead poll, resume refetch), so none of the
|
|
4930
|
+
* poll-side settle handlers ran. Only for SINGLE-PASS files, where one
|
|
4931
|
+
* settled pass is deterministically the whole run (the same contract as
|
|
4932
|
+
* maybeResumeIndexing's single-pass branch); paged files stay with their
|
|
4933
|
+
* drivers. Outcome is read from the settled bubbles' own flags, which is
|
|
4934
|
+
* all the history mapping left us. Best-effort and idempotent throughout. */
|
|
4935
|
+
_flipRunFromSettledEntry(entry) {
|
|
4936
|
+
try {
|
|
4937
|
+
if (!entry || !entry.storagePath || !entry.id || !entry.projectId) return;
|
|
4938
|
+
if (isPagedReadFile(entry.filename, entry.mime)) return;
|
|
4939
|
+
if (this.cancelledIndexKeys.has(this._indexKeyOf(entry))) return;
|
|
4940
|
+
if (this.state.stoppedIndexIds[entry.id]) return;
|
|
4941
|
+
var userMsg = null, replyMsg = null;
|
|
4942
|
+
this.state.messages.forEach(function(m) {
|
|
4943
|
+
if (m._serverItemId !== entry.id) return;
|
|
4944
|
+
if (m.role === "user") {
|
|
4945
|
+
if (!userMsg) userMsg = m;
|
|
4946
|
+
} else if (!replyMsg) replyMsg = m;
|
|
4947
|
+
});
|
|
4948
|
+
if (userMsg && userMsg.isCancelled || replyMsg && replyMsg.isCancelled) {
|
|
4949
|
+
this._flipRunRecord(entry, "cancelled");
|
|
4950
|
+
} else if (replyMsg && replyMsg.isError) {
|
|
4951
|
+
var errText = typeof replyMsg.content === "string" ? replyMsg.content.replace(/\s+/g, " ").trim().slice(0, 300) : "";
|
|
4952
|
+
this._flipRunRecord(entry, "error", errText || "Indexing failed.");
|
|
4953
|
+
} else if (replyMsg) {
|
|
4954
|
+
this._mintDoneMarker(entry);
|
|
4955
|
+
this._flipRunRecord(entry, "done");
|
|
4956
|
+
}
|
|
4957
|
+
} catch (_e) {
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
/** Close the durable run:: record for an ending THIS client observed.
|
|
4961
|
+
* service comes from the ENTRY, not the current identity: unlike the done::
|
|
4962
|
+
* mint above, a status flip must land even if the user switched projects
|
|
4963
|
+
* mid-settle — otherwise the record lies 'working' forever. Best-effort
|
|
4964
|
+
* through upsertIndexRunRecordSafe; the consumer's precedence guard keeps
|
|
4965
|
+
* repeats and races harmless. */
|
|
4966
|
+
_flipRunRecord(entry, status, error) {
|
|
4967
|
+
if (!entry || !entry.storagePath || !entry.projectId) return;
|
|
4968
|
+
var patch = { status, finished: Date.now() };
|
|
4969
|
+
if (error) patch.error = error;
|
|
4970
|
+
upsertIndexRunRecordSafe(entry.projectId, entry.storagePath, patch);
|
|
4971
|
+
}
|
|
4338
4972
|
maybeResumeIndexing(entry, response, platform) {
|
|
4339
4973
|
var self = this;
|
|
4340
4974
|
var endOfClientChain = function() {
|
|
@@ -4344,27 +4978,43 @@ var ChatSession = class {
|
|
|
4344
4978
|
if (!entry || !entry.storagePath) return;
|
|
4345
4979
|
if (this.cancelledIndexKeys.has(this._indexKeyOf(entry))) return;
|
|
4346
4980
|
if (!isPagedReadFile(entry.filename, entry.mime)) {
|
|
4981
|
+
if (!isErrorResponseBody(response) && !this._isCancelledPollResult(response)) {
|
|
4982
|
+
this._mintDoneMarker(entry);
|
|
4983
|
+
this._flipRunRecord(entry, "done");
|
|
4984
|
+
} else if (this._isCancelledPollResult(response)) {
|
|
4985
|
+
this._flipRunRecord(entry, "cancelled");
|
|
4986
|
+
} else {
|
|
4987
|
+
this._flipRunRecord(entry, "error", this._runErrorText(response));
|
|
4988
|
+
}
|
|
4347
4989
|
endOfClientChain();
|
|
4348
4990
|
return;
|
|
4349
4991
|
}
|
|
4350
4992
|
if (isImageVisionFile(entry.filename, entry.mime)) return;
|
|
4351
4993
|
if (windowedIndexingEnabled() && isWindowedReadFile(entry.filename, entry.mime)) return;
|
|
4352
4994
|
if (isErrorResponseBody(response)) {
|
|
4995
|
+
this._flipRunRecord(entry, "error", this._runErrorText(response));
|
|
4353
4996
|
endOfClientChain();
|
|
4354
4997
|
return;
|
|
4355
4998
|
}
|
|
4356
4999
|
var answer = (platform === "openai" ? extractOpenAIText(response) : extractClaudeText(response)) || "";
|
|
4357
5000
|
if (answer.indexOf(INDEXING_COMPLETE_MARKER) !== -1) {
|
|
5001
|
+
this._mintDoneMarker(entry);
|
|
5002
|
+
this._flipRunRecord(entry, "done");
|
|
4358
5003
|
endOfClientChain();
|
|
4359
5004
|
return;
|
|
4360
5005
|
}
|
|
4361
5006
|
var pass = (entry.resumePass || 0) + 1;
|
|
4362
5007
|
if (pass > MAX_INDEXING_RESUME_PASSES) {
|
|
5008
|
+
this._flipRunRecord(entry, "error", "Stopped after " + MAX_INDEXING_RESUME_PASSES + " passes without finishing.");
|
|
4363
5009
|
endOfClientChain();
|
|
4364
5010
|
return;
|
|
4365
5011
|
}
|
|
4366
5012
|
var id = this.host.getIdentity();
|
|
4367
|
-
if (!id || id.platform === "none" || id.projectId !== entry.projectId)
|
|
5013
|
+
if (!id || id.platform === "none" || id.projectId !== entry.projectId) {
|
|
5014
|
+
this._flipRunRecord(entry, "error", "Indexing stopped: the session or project changed before the file finished.");
|
|
5015
|
+
endOfClientChain();
|
|
5016
|
+
return;
|
|
5017
|
+
}
|
|
4368
5018
|
this.trackIndexDispatch(notifyAgentContinueIndexing({
|
|
4369
5019
|
platform: id.platform,
|
|
4370
5020
|
model: id.model,
|
|
@@ -4441,8 +5091,9 @@ var ChatSession = class {
|
|
|
4441
5091
|
var projectId = id.projectId, owner = id.owner;
|
|
4442
5092
|
var options = { fetchMore };
|
|
4443
5093
|
if (fetchMore && this.state.historyStartKeyHistory.length) options.startKeyHistory = this.state.historyStartKeyHistory.slice();
|
|
5094
|
+
if (!fetchMore) options.deferBg = true;
|
|
4444
5095
|
var fetchHistory = function() {
|
|
4445
|
-
return
|
|
5096
|
+
return getSplitChatHistory({ service: projectId, owner, platform, userId: id.userId }, options);
|
|
4446
5097
|
};
|
|
4447
5098
|
return Promise.resolve().then(fetchHistory).catch(function(err) {
|
|
4448
5099
|
if (isAuthExpiredError(err) && !isNonRetryableRequestError(err)) return self.host.refreshSession().then(fetchHistory);
|
|
@@ -4452,7 +5103,8 @@ var ChatSession = class {
|
|
|
4452
5103
|
var chatList = history && Array.isArray(history.list) ? history.list : [];
|
|
4453
5104
|
chatList.forEach(function(item) {
|
|
4454
5105
|
if (isBgIndexingQueue(item.queue_name)) {
|
|
4455
|
-
|
|
5106
|
+
var clsText = item.compact ? item.request_text : extractLastUserTextFromRequest(item.request_body);
|
|
5107
|
+
if (isIndexingRequestText(clsText)) item._isBgTask = true;
|
|
4456
5108
|
else item._isOnBgQueue = true;
|
|
4457
5109
|
}
|
|
4458
5110
|
});
|
|
@@ -4465,15 +5117,55 @@ var ChatSession = class {
|
|
|
4465
5117
|
projectId: id.projectId,
|
|
4466
5118
|
formatIndexingLabel: self.host.formatIndexingLabel
|
|
4467
5119
|
}).messages;
|
|
5120
|
+
self.applyHydratedBodies(mapped);
|
|
4468
5121
|
var keptOlderPages = false;
|
|
5122
|
+
var keptScreenAwaitingBg = false;
|
|
4469
5123
|
if (fetchMore) {
|
|
4470
|
-
|
|
5124
|
+
var incomingKeys = {};
|
|
5125
|
+
mapped.forEach(function(m) {
|
|
5126
|
+
if (m._serverItemId) incomingKeys[m._serverItemId + "|" + m.role] = m;
|
|
5127
|
+
});
|
|
5128
|
+
var existing = self.state.messages.filter(function(m) {
|
|
5129
|
+
if (!m._serverItemId) return true;
|
|
5130
|
+
var inc = incomingKeys[m._serverItemId + "|" + m.role];
|
|
5131
|
+
if (!inc) return true;
|
|
5132
|
+
if (m._cancelling) inc._cancelling = m._cancelling;
|
|
5133
|
+
if (m._cancelError) inc._cancelError = m._cancelError;
|
|
5134
|
+
return false;
|
|
5135
|
+
});
|
|
5136
|
+
var mergedList = [];
|
|
5137
|
+
var pi = 0, ei = 0;
|
|
5138
|
+
while (pi < mapped.length && ei < existing.length) {
|
|
5139
|
+
var pm = mapped[pi], em = existing[ei];
|
|
5140
|
+
var eid = em._serverItemId;
|
|
5141
|
+
if (typeof eid !== "string") break;
|
|
5142
|
+
var pid = pm._serverItemId;
|
|
5143
|
+
if (typeof pid !== "string" || pid <= eid) {
|
|
5144
|
+
mergedList.push(pm);
|
|
5145
|
+
pi++;
|
|
5146
|
+
} else {
|
|
5147
|
+
mergedList.push(em);
|
|
5148
|
+
ei++;
|
|
5149
|
+
}
|
|
5150
|
+
}
|
|
5151
|
+
while (pi < mapped.length) mergedList.push(mapped[pi++]);
|
|
5152
|
+
while (ei < existing.length) mergedList.push(existing[ei++]);
|
|
5153
|
+
self.state.messages = mergedList;
|
|
5154
|
+
} else if (!mapped.length && history && (history.endOfList === false || history.bgPending) && self.state.messages.some(function(m) {
|
|
5155
|
+
return m._ownerKey === void 0 || m._ownerKey === loadKey;
|
|
5156
|
+
})) {
|
|
5157
|
+
if (history.endOfList !== false) keptScreenAwaitingBg = true;
|
|
4471
5158
|
} else {
|
|
4472
5159
|
if (self.state.typing) self.state.typingAbort = true;
|
|
4473
5160
|
var serverIds = {};
|
|
4474
5161
|
mapped.forEach(function(m) {
|
|
4475
5162
|
if (m._serverItemId) serverIds[m._serverItemId] = 1;
|
|
4476
5163
|
});
|
|
5164
|
+
var surfaceOldestId = void 0;
|
|
5165
|
+
mapped.forEach(function(m) {
|
|
5166
|
+
if (typeof m._serverItemId !== "string" || m._fromBgChain) return;
|
|
5167
|
+
if (surfaceOldestId === void 0 || m._serverItemId < surfaceOldestId) surfaceOldestId = m._serverItemId;
|
|
5168
|
+
});
|
|
4477
5169
|
var locallyCancelled = {};
|
|
4478
5170
|
self.state.messages.forEach(function(m) {
|
|
4479
5171
|
if (m.isCancelled && m._serverItemId) locallyCancelled[m._serverItemId] = m;
|
|
@@ -4514,13 +5206,45 @@ var ChatSession = class {
|
|
|
4514
5206
|
var sharesPage1 = self.state.messages.some(function(m) {
|
|
4515
5207
|
return typeof m._serverItemId === "string" && !!serverIds[m._serverItemId];
|
|
4516
5208
|
});
|
|
4517
|
-
var
|
|
5209
|
+
var deferredBg = !!(history && history.bgPending);
|
|
5210
|
+
var retainBoundary = surfaceOldestId !== void 0 ? surfaceOldestId : oldestInPage1;
|
|
5211
|
+
var retainedOlder = !sharesPage1 || retainBoundary === void 0 ? [] : self.state.messages.filter(function(m) {
|
|
4518
5212
|
if (typeof m._serverItemId !== "string") return false;
|
|
4519
5213
|
if (m._ownerKey !== void 0 && m._ownerKey !== loadKey) return false;
|
|
4520
|
-
|
|
5214
|
+
if (deferredBg && m.isBackgroundTask) return true;
|
|
5215
|
+
if (m._fromBgChain) return true;
|
|
5216
|
+
return m._serverItemId < retainBoundary;
|
|
4521
5217
|
});
|
|
4522
|
-
|
|
4523
|
-
|
|
5218
|
+
var prependOlder = [];
|
|
5219
|
+
var interleave = [];
|
|
5220
|
+
retainedOlder.forEach(function(m) {
|
|
5221
|
+
var sid = m._serverItemId;
|
|
5222
|
+
if (serverIds[sid]) return;
|
|
5223
|
+
if (retainBoundary !== void 0 && sid < retainBoundary) prependOlder.push(m);
|
|
5224
|
+
else interleave.push(m);
|
|
5225
|
+
});
|
|
5226
|
+
var page1 = mapped;
|
|
5227
|
+
if (interleave.length) {
|
|
5228
|
+
var mergedP = [];
|
|
5229
|
+
var ii2 = 0, mi2 = 0;
|
|
5230
|
+
while (ii2 < interleave.length && mi2 < mapped.length) {
|
|
5231
|
+
var iv = interleave[ii2], mv = mapped[mi2];
|
|
5232
|
+
var mid2 = typeof mv._serverItemId === "string" ? mv._serverItemId : void 0;
|
|
5233
|
+
if (mid2 === void 0) break;
|
|
5234
|
+
if (iv._serverItemId <= mid2) {
|
|
5235
|
+
mergedP.push(iv);
|
|
5236
|
+
ii2++;
|
|
5237
|
+
} else {
|
|
5238
|
+
mergedP.push(mv);
|
|
5239
|
+
mi2++;
|
|
5240
|
+
}
|
|
5241
|
+
}
|
|
5242
|
+
while (ii2 < interleave.length) mergedP.push(interleave[ii2++]);
|
|
5243
|
+
while (mi2 < mapped.length) mergedP.push(mapped[mi2++]);
|
|
5244
|
+
page1 = mergedP;
|
|
5245
|
+
}
|
|
5246
|
+
keptOlderPages = prependOlder.length > 0 || interleave.length > 0;
|
|
5247
|
+
self.state.messages = prependOlder.length ? prependOlder.concat(page1) : page1;
|
|
4524
5248
|
rescued.forEach(function(m) {
|
|
4525
5249
|
self.state.messages.push(m);
|
|
4526
5250
|
});
|
|
@@ -4556,9 +5280,14 @@ var ChatSession = class {
|
|
|
4556
5280
|
self.state.historyEndOfList = !!(history && history.endOfList);
|
|
4557
5281
|
self.state.historyStartKeyHistory = history && Array.isArray(history.startKeyHistory) ? history.startKeyHistory : [];
|
|
4558
5282
|
var clearedAt = self.host.getClearedAt();
|
|
4559
|
-
if (clearedAt
|
|
4560
|
-
var
|
|
4561
|
-
|
|
5283
|
+
if (clearedAt) {
|
|
5284
|
+
var surfaceItems = chatList.filter(function(it) {
|
|
5285
|
+
return !(it && it._fromBgChain);
|
|
5286
|
+
});
|
|
5287
|
+
if (surfaceItems.length > 0) {
|
|
5288
|
+
var oldestUpdated = Number(surfaceItems[surfaceItems.length - 1] && surfaceItems[surfaceItems.length - 1].updated);
|
|
5289
|
+
if (isFinite(oldestUpdated) && oldestUpdated <= clearedAt) self.state.historyEndOfList = true;
|
|
5290
|
+
}
|
|
4562
5291
|
}
|
|
4563
5292
|
}
|
|
4564
5293
|
if (self.state.historyRequestToken === token) {
|
|
@@ -4567,6 +5296,85 @@ var ChatSession = class {
|
|
|
4567
5296
|
}
|
|
4568
5297
|
self.updateHistoryCache();
|
|
4569
5298
|
self.host.notify();
|
|
5299
|
+
var bgPending = !fetchMore && history && history.bgPending;
|
|
5300
|
+
if (bgPending) {
|
|
5301
|
+
var batchId = ++_bgHistoryBatchSeq;
|
|
5302
|
+
if (history.endOfList !== true && history.firstLoad === true) {
|
|
5303
|
+
self.state.bgHistoryLoading = true;
|
|
5304
|
+
self.host.notify();
|
|
5305
|
+
}
|
|
5306
|
+
var releaseBgFlag = function() {
|
|
5307
|
+
if (_bgHistoryBatchSeq === batchId) self.state.bgHistoryLoading = false;
|
|
5308
|
+
};
|
|
5309
|
+
bgPending.then(function(batch) {
|
|
5310
|
+
if (token !== self.state.gateRefreshToken) {
|
|
5311
|
+
releaseBgFlag();
|
|
5312
|
+
return;
|
|
5313
|
+
}
|
|
5314
|
+
var bList = batch && Array.isArray(batch.list) ? batch.list : [];
|
|
5315
|
+
bList.forEach(function(item) {
|
|
5316
|
+
if (isBgIndexingQueue(item.queue_name)) {
|
|
5317
|
+
var t = item.compact ? item.request_text : extractLastUserTextFromRequest(item.request_body);
|
|
5318
|
+
if (isIndexingRequestText(t)) item._isBgTask = true;
|
|
5319
|
+
else item._isOnBgQueue = true;
|
|
5320
|
+
}
|
|
5321
|
+
});
|
|
5322
|
+
var sorted = bList.sort(function(a, b) {
|
|
5323
|
+
var ai = typeof a.id === "string" ? a.id : "", bi = typeof b.id === "string" ? b.id : "";
|
|
5324
|
+
return ai > bi ? -1 : ai < bi ? 1 : 0;
|
|
5325
|
+
});
|
|
5326
|
+
var m2 = mapHistoryListToMessages(sorted, platform, {
|
|
5327
|
+
clearedAt: self.host.getClearedAt(),
|
|
5328
|
+
projectId: id.projectId,
|
|
5329
|
+
formatIndexingLabel: self.host.formatIndexingLabel
|
|
5330
|
+
}).messages;
|
|
5331
|
+
self.applyHydratedBodies(m2);
|
|
5332
|
+
if (keptScreenAwaitingBg && !m2.length && batch && batch.endOfList === true) {
|
|
5333
|
+
self.state.messages = self.state.messages.filter(function(m) {
|
|
5334
|
+
if (typeof m._serverItemId !== "string") return true;
|
|
5335
|
+
if (m._ownerKey !== void 0 && m._ownerKey !== loadKey) return true;
|
|
5336
|
+
return false;
|
|
5337
|
+
});
|
|
5338
|
+
self.state.historyEndOfList = true;
|
|
5339
|
+
releaseBgFlag();
|
|
5340
|
+
self.updateHistoryCache();
|
|
5341
|
+
self.host.notify();
|
|
5342
|
+
return;
|
|
5343
|
+
}
|
|
5344
|
+
var incoming = {};
|
|
5345
|
+
m2.forEach(function(m) {
|
|
5346
|
+
if (m._serverItemId) incoming[m._serverItemId + "|" + m.role] = true;
|
|
5347
|
+
});
|
|
5348
|
+
var baseList = self.state.messages.filter(function(m) {
|
|
5349
|
+
return !(m._serverItemId && incoming[m._serverItemId + "|" + m.role]);
|
|
5350
|
+
});
|
|
5351
|
+
var mergedList2 = [];
|
|
5352
|
+
var pi2 = 0, ei2 = 0;
|
|
5353
|
+
while (pi2 < m2.length && ei2 < baseList.length) {
|
|
5354
|
+
var pm2 = m2[pi2], em2 = baseList[ei2];
|
|
5355
|
+
var eid2 = em2._serverItemId;
|
|
5356
|
+
if (typeof eid2 !== "string") break;
|
|
5357
|
+
var pid2 = pm2._serverItemId;
|
|
5358
|
+
if (typeof pid2 !== "string" || pid2 <= eid2) {
|
|
5359
|
+
mergedList2.push(pm2);
|
|
5360
|
+
pi2++;
|
|
5361
|
+
} else {
|
|
5362
|
+
mergedList2.push(em2);
|
|
5363
|
+
ei2++;
|
|
5364
|
+
}
|
|
5365
|
+
}
|
|
5366
|
+
while (pi2 < m2.length) mergedList2.push(m2[pi2++]);
|
|
5367
|
+
while (ei2 < baseList.length) mergedList2.push(baseList[ei2++]);
|
|
5368
|
+
self.state.messages = mergedList2;
|
|
5369
|
+
if (batch && batch.endOfList === true) self.state.historyEndOfList = true;
|
|
5370
|
+
releaseBgFlag();
|
|
5371
|
+
self.updateHistoryCache();
|
|
5372
|
+
self.host.notify();
|
|
5373
|
+
}, function() {
|
|
5374
|
+
releaseBgFlag();
|
|
5375
|
+
self.host.notify();
|
|
5376
|
+
});
|
|
5377
|
+
}
|
|
4570
5378
|
if (!fetchMore) {
|
|
4571
5379
|
var bgAllow = {};
|
|
4572
5380
|
var bgHistBudget = MAX_CONCURRENT_BG_POLLS - self._countBgPolls();
|
|
@@ -4663,7 +5471,7 @@ var ChatSession = class {
|
|
|
4663
5471
|
var self = this;
|
|
4664
5472
|
var id = this.host.getIdentity();
|
|
4665
5473
|
att.status = "uploading";
|
|
4666
|
-
att.progress =
|
|
5474
|
+
att.progress = null;
|
|
4667
5475
|
att.errorMessage = "";
|
|
4668
5476
|
att.errorCode = "";
|
|
4669
5477
|
att.errorDetail = "";
|
|
@@ -4894,6 +5702,7 @@ var ChatSession = class {
|
|
|
4894
5702
|
};
|
|
4895
5703
|
|
|
4896
5704
|
// src/engine/indexing_groups.ts
|
|
5705
|
+
var RUN_RECORD_WORKING_STALE_MS = 6 * 60 * 60 * 1e3;
|
|
4897
5706
|
var INDEXING_LABEL_RE = /^(Re)?[Ii]ndexing(\s*\(continuing\))?\s*:?\s+(.+)$/;
|
|
4898
5707
|
var LEADING_MD_LINK_RE = /^\[([^\]]+)\]\(([^)]+)\)/;
|
|
4899
5708
|
function parseIndexingLabel(content) {
|
|
@@ -4948,10 +5757,12 @@ function buildChatDisplayList(messages, opts) {
|
|
|
4948
5757
|
var list = Array.isArray(messages) ? messages : [];
|
|
4949
5758
|
var liveIndexKeys = opts && opts.liveIndexKeys || {};
|
|
4950
5759
|
var liveIndexChecked = !!(opts && opts.liveIndexChecked);
|
|
5760
|
+
var doneKeys = opts && opts.doneKeys || {};
|
|
4951
5761
|
var stoppedIndexIds = opts && opts.stoppedIndexIds || {};
|
|
4952
5762
|
var windowedIndexing = opts && opts.windowedIndexing !== void 0 ? !!opts.windowedIndexing : windowedIndexingEnabled();
|
|
4953
5763
|
var hasMoreHistory = !!(opts && opts.hasMoreHistory);
|
|
4954
5764
|
var loadingOlderHistory = !!(opts && opts.loadingOlderHistory);
|
|
5765
|
+
var stubPlatform = opts && opts.stubPlatform;
|
|
4955
5766
|
var groups = {};
|
|
4956
5767
|
var order = [];
|
|
4957
5768
|
var runOfIndex = new Array(list.length);
|
|
@@ -5124,11 +5935,11 @@ function buildChatDisplayList(messages, opts) {
|
|
|
5124
5935
|
} else if (grp.driver === "client") {
|
|
5125
5936
|
grp.finished = sawComplete || grp.status === "error" || grp.passCount >= MAX_INDEXING_RESUME_PASSES;
|
|
5126
5937
|
} else {
|
|
5127
|
-
grp.finished = !newestRunOfKey[order[oi]] || liveIndexChecked && !liveIndexKeys[grp.key];
|
|
5938
|
+
grp.finished = !newestRunOfKey[order[oi]] || !!doneKeys[grp.key] && !liveIndexKeys[grp.key] || liveIndexChecked && !liveIndexKeys[grp.key];
|
|
5128
5939
|
}
|
|
5129
5940
|
if (grp.status !== "done") {
|
|
5130
5941
|
grp.resolving = false;
|
|
5131
|
-
} else if (grp.mayHaveOlder && loadingOlderHistory && !liveIndexKeys[grp.key] && newestRunOfKey[order[oi]]) {
|
|
5942
|
+
} else if (grp.mayHaveOlder && loadingOlderHistory && !liveIndexKeys[grp.key] && !doneKeys[grp.key] && newestRunOfKey[order[oi]]) {
|
|
5132
5943
|
grp.resolving = true;
|
|
5133
5944
|
grp.resolvingReason = "history";
|
|
5134
5945
|
} else if (!grp.finished && grp.driver === "worker" && !liveIndexChecked && !liveIndexKeys[grp.key]) {
|
|
@@ -5138,14 +5949,126 @@ function buildChatDisplayList(messages, opts) {
|
|
|
5138
5949
|
grp.resolving = false;
|
|
5139
5950
|
}
|
|
5140
5951
|
}
|
|
5952
|
+
var stubList = [];
|
|
5953
|
+
var runStubs = opts && opts.runStubs;
|
|
5954
|
+
if (runStubs) {
|
|
5955
|
+
var coveredPaths = {};
|
|
5956
|
+
var coveredPathlessNames = {};
|
|
5957
|
+
for (var ci = 0; ci < order.length; ci++) {
|
|
5958
|
+
var cg = groups[order[ci]];
|
|
5959
|
+
if (cg.path) {
|
|
5960
|
+
coveredPaths[cg.path] = true;
|
|
5961
|
+
if (cg.key) coveredPaths[cg.key] = true;
|
|
5962
|
+
} else if (cg.name) coveredPathlessNames[cg.name] = true;
|
|
5963
|
+
else if (cg.key) coveredPaths[cg.key] = true;
|
|
5964
|
+
}
|
|
5965
|
+
var now = opts && typeof opts.now === "number" ? opts.now : Date.now();
|
|
5966
|
+
var stubClearedAt = opts && typeof opts.stubClearedAt === "number" && opts.stubClearedAt > 0 ? opts.stubClearedAt : 0;
|
|
5967
|
+
for (var sp in runStubs) {
|
|
5968
|
+
var rec = runStubs[sp];
|
|
5969
|
+
if (!sp || !rec || !rec.status || coveredPaths[sp]) continue;
|
|
5970
|
+
var fname = rec.filename || sp.split("/").pop() || sp;
|
|
5971
|
+
if (coveredPathlessNames[fname]) continue;
|
|
5972
|
+
if (stubPlatform && rec.platform && rec.platform !== stubPlatform) continue;
|
|
5973
|
+
var live = !!liveIndexKeys[sp] || !!liveIndexKeys[fname];
|
|
5974
|
+
var recWhen = typeof rec.finished === "number" ? rec.finished : typeof rec.started === "number" ? rec.started : void 0;
|
|
5975
|
+
if (stubClearedAt && !live && recWhen !== void 0 && recWhen <= stubClearedAt) continue;
|
|
5976
|
+
var st = "active";
|
|
5977
|
+
var fin = false;
|
|
5978
|
+
var res = false;
|
|
5979
|
+
var reason;
|
|
5980
|
+
if (!live) {
|
|
5981
|
+
if (rec.status === "done" || doneKeys[sp] || doneKeys[fname]) {
|
|
5982
|
+
st = "done";
|
|
5983
|
+
fin = true;
|
|
5984
|
+
} else if (rec.status === "error") {
|
|
5985
|
+
st = "error";
|
|
5986
|
+
fin = true;
|
|
5987
|
+
} else if (rec.status === "cancelled") {
|
|
5988
|
+
st = "cancelled";
|
|
5989
|
+
fin = true;
|
|
5990
|
+
} else if (liveIndexChecked) {
|
|
5991
|
+
st = "done";
|
|
5992
|
+
fin = true;
|
|
5993
|
+
} else if (typeof rec.started === "number" && now - rec.started > RUN_RECORD_WORKING_STALE_MS) {
|
|
5994
|
+
st = "error";
|
|
5995
|
+
fin = true;
|
|
5996
|
+
} else {
|
|
5997
|
+
res = true;
|
|
5998
|
+
reason = "status";
|
|
5999
|
+
}
|
|
6000
|
+
}
|
|
6001
|
+
var sg = {
|
|
6002
|
+
key: sp,
|
|
6003
|
+
// ONE identity for the run whether it renders from the record or
|
|
6004
|
+
// from its loaded passes: the views key the DOM off runKey, so a
|
|
6005
|
+
// 'stub:'-prefixed key meant every handoff was an unmount plus a
|
|
6006
|
+
// remount somewhere else. Named after the record's start, which
|
|
6007
|
+
// the real group below reuses when it has one.
|
|
6008
|
+
runKey: "run:" + sp + "#" + (typeof rec.started === "number" ? rec.started : "n"),
|
|
6009
|
+
name: fname,
|
|
6010
|
+
path: sp,
|
|
6011
|
+
mime: void 0,
|
|
6012
|
+
size: void 0,
|
|
6013
|
+
isReindex: false,
|
|
6014
|
+
members: [],
|
|
6015
|
+
passCount: 0,
|
|
6016
|
+
status: st,
|
|
6017
|
+
cancellableIds: [],
|
|
6018
|
+
cancelling: false,
|
|
6019
|
+
stopped: st === "cancelled",
|
|
6020
|
+
mayHaveOlder: hasMoreHistory,
|
|
6021
|
+
anchorIndex: -1,
|
|
6022
|
+
anchorId: "",
|
|
6023
|
+
visibleMembers: [],
|
|
6024
|
+
driver: !isPagedReadFile(fname, void 0) ? "single" : isImageVisionFile(fname, void 0) ? "worker" : windowedIndexing ? "worker" : "client",
|
|
6025
|
+
finished: fin,
|
|
6026
|
+
resolving: res,
|
|
6027
|
+
resolvingReason: reason,
|
|
6028
|
+
stub: true,
|
|
6029
|
+
stubError: rec.error || (st === "error" && !rec.error ? "Indexing did not finish." : void 0)
|
|
6030
|
+
};
|
|
6031
|
+
stubList.push({ started: typeof rec.started === "number" ? rec.started : Infinity, group: sg });
|
|
6032
|
+
}
|
|
6033
|
+
}
|
|
6034
|
+
var suppressAnchor = {};
|
|
6035
|
+
if (runStubs) {
|
|
6036
|
+
for (var ti2 = 0; ti2 < order.length; ti2++) {
|
|
6037
|
+
var tg = groups[order[ti2]];
|
|
6038
|
+
if (!newestRunOfKey[order[ti2]]) continue;
|
|
6039
|
+
var trec = tg.path && runStubs[tg.path] || runStubs[tg.key];
|
|
6040
|
+
if (!trec || typeof trec.started !== "number") continue;
|
|
6041
|
+
if (stubPlatform && trec.platform && trec.platform !== stubPlatform) continue;
|
|
6042
|
+
suppressAnchor[order[ti2]] = true;
|
|
6043
|
+
tg.runKey = "run:" + (tg.path || tg.key) + "#" + trec.started;
|
|
6044
|
+
stubList.push({ started: trec.started, group: tg });
|
|
6045
|
+
}
|
|
6046
|
+
}
|
|
6047
|
+
stubList.sort(function(a, b) {
|
|
6048
|
+
return a.started - b.started;
|
|
6049
|
+
});
|
|
5141
6050
|
var out = [];
|
|
6051
|
+
var si = 0;
|
|
5142
6052
|
for (var j = 0; j < list.length; j++) {
|
|
6053
|
+
var mts = list[j] && typeof list[j]._ts === "number" ? list[j]._ts : void 0;
|
|
6054
|
+
if (mts !== void 0) {
|
|
6055
|
+
while (si < stubList.length && stubList[si].started <= mts) {
|
|
6056
|
+
out.push({ kind: "indexing", group: stubList[si].group, index: -1 - si });
|
|
6057
|
+
si++;
|
|
6058
|
+
}
|
|
6059
|
+
}
|
|
5143
6060
|
var r = runOfIndex[j];
|
|
5144
6061
|
if (r === void 0) {
|
|
5145
6062
|
out.push({ kind: "message", msg: list[j], index: j });
|
|
5146
6063
|
continue;
|
|
5147
6064
|
}
|
|
5148
|
-
if (groups[r].anchorIndex === j
|
|
6065
|
+
if (groups[r].anchorIndex === j && !suppressAnchor[r]) {
|
|
6066
|
+
out.push({ kind: "indexing", group: groups[r], index: j });
|
|
6067
|
+
}
|
|
6068
|
+
}
|
|
6069
|
+
while (si < stubList.length) {
|
|
6070
|
+
out.push({ kind: "indexing", group: stubList[si].group, index: -1 - si });
|
|
6071
|
+
si++;
|
|
5149
6072
|
}
|
|
5150
6073
|
return out;
|
|
5151
6074
|
}
|
|
@@ -5159,6 +6082,7 @@ exports.CONTEXT_WINDOW_BY_MODEL = CONTEXT_WINDOW_BY_MODEL;
|
|
|
5159
6082
|
exports.CONTEXT_WINDOW_DEFAULT = CONTEXT_WINDOW_DEFAULT;
|
|
5160
6083
|
exports.ChatSession = ChatSession;
|
|
5161
6084
|
exports.DEFAULT_CLAUDE_MODEL = DEFAULT_CLAUDE_MODEL;
|
|
6085
|
+
exports.DEFAULT_CONTEXT_WINDOW = DEFAULT_CONTEXT_WINDOW;
|
|
5162
6086
|
exports.DEFAULT_OPENAI_MODEL = DEFAULT_OPENAI_MODEL;
|
|
5163
6087
|
exports.EMPTY_INDEXING_REPLY = EMPTY_INDEXING_REPLY;
|
|
5164
6088
|
exports.EXPIRED_ATTACHMENT_URL_HOST = EXPIRED_ATTACHMENT_URL_HOST;
|
|
@@ -5175,22 +6099,28 @@ exports.INDEXING_COMPLETE_MARKER = INDEXING_COMPLETE_MARKER;
|
|
|
5175
6099
|
exports.INLINE_LINK_GLYPH = INLINE_LINK_GLYPH;
|
|
5176
6100
|
exports.INLINE_LINK_UNAVAILABLE_GLYPH = INLINE_LINK_UNAVAILABLE_GLYPH;
|
|
5177
6101
|
exports.INLINE_LINK_UNAVAILABLE_SUFFIX = INLINE_LINK_UNAVAILABLE_SUFFIX;
|
|
6102
|
+
exports.INPUT_CAP_RATIO = INPUT_CAP_RATIO;
|
|
5178
6103
|
exports.LINK_LABEL_MAX_DISPLAY_CHARS = LINK_LABEL_MAX_DISPLAY_CHARS;
|
|
5179
6104
|
exports.LINK_REFRESH_WINDOW_MS = LINK_REFRESH_WINDOW_MS;
|
|
5180
6105
|
exports.MAX_CONCURRENT_BG_POLLS = MAX_CONCURRENT_BG_POLLS;
|
|
5181
6106
|
exports.MAX_HISTORY_FILL_PAGES = MAX_HISTORY_FILL_PAGES;
|
|
5182
6107
|
exports.MAX_HISTORY_MESSAGES = MAX_HISTORY_MESSAGES;
|
|
6108
|
+
exports.MAX_OUTPUT_BY_MODEL = MAX_OUTPUT_BY_MODEL;
|
|
6109
|
+
exports.MAX_OUTPUT_TOKENS = MAX_OUTPUT_TOKENS;
|
|
5183
6110
|
exports.MAX_PARSED_CONTENT_CHARS = MAX_PARSED_CONTENT_CHARS;
|
|
5184
6111
|
exports.MCP_NAME = MCP_NAME;
|
|
5185
6112
|
exports.MIN_INPUT_TOKEN_BUDGET = MIN_INPUT_TOKEN_BUDGET;
|
|
6113
|
+
exports.MIN_PER_REQUEST_INPUT_CAP = MIN_PER_REQUEST_INPUT_CAP;
|
|
5186
6114
|
exports.OUTPUT_TOKEN_RESERVE = OUTPUT_TOKEN_RESERVE;
|
|
5187
6115
|
exports.POLL_INTERVAL = POLL_INTERVAL;
|
|
5188
6116
|
exports.PREVIEWABLE_IMAGE_CONTENT_TYPES = PREVIEWABLE_IMAGE_CONTENT_TYPES;
|
|
5189
6117
|
exports.PREVIEW_BROWSER_CACHE_SECONDS = PREVIEW_BROWSER_CACHE_SECONDS;
|
|
5190
6118
|
exports.RENDER_FROM_TOKEN = RENDER_FROM_TOKEN;
|
|
5191
6119
|
exports.RTF_EXTS = RTF_EXTS;
|
|
6120
|
+
exports.RUN_RECORD_WORKING_STALE_MS = RUN_RECORD_WORKING_STALE_MS;
|
|
5192
6121
|
exports.TOOL_AND_RESPONSE_BUFFER = TOOL_AND_RESPONSE_BUFFER;
|
|
5193
6122
|
exports.XML_EXTS = XML_EXTS;
|
|
6123
|
+
exports.__resetSplitHistoryState = __resetSplitHistoryState;
|
|
5194
6124
|
exports.applyEncodingDeclaration = applyEncodingDeclaration;
|
|
5195
6125
|
exports.bgIndexingQueueName = bgIndexingQueueName;
|
|
5196
6126
|
exports.buildAiAgentValue = buildAiAgentValue;
|
|
@@ -5198,6 +6128,7 @@ exports.buildBoundedChatMessages = buildBoundedChatMessages;
|
|
|
5198
6128
|
exports.buildChatDisplayList = buildChatDisplayList;
|
|
5199
6129
|
exports.buildChatSystemPrompt = buildChatSystemPrompt;
|
|
5200
6130
|
exports.buildDisplayExpiredAttachmentHref = buildDisplayExpiredAttachmentHref;
|
|
6131
|
+
exports.buildHistoryItemFullId = buildHistoryItemFullId;
|
|
5201
6132
|
exports.buildIndexingContinueMessage = buildIndexingContinueMessage;
|
|
5202
6133
|
exports.buildIndexingRenderContinueTemplate = buildIndexingRenderContinueTemplate;
|
|
5203
6134
|
exports.buildIndexingRenderMessage = buildIndexingRenderMessage;
|
|
@@ -5229,6 +6160,7 @@ exports.extractClaudeText = extractClaudeText;
|
|
|
5229
6160
|
exports.extractLastUserTextFromRequest = extractLastUserTextFromRequest;
|
|
5230
6161
|
exports.extractOpenAIText = extractOpenAIText;
|
|
5231
6162
|
exports.extractRemotePathFromAttachmentHref = extractRemotePathFromAttachmentHref;
|
|
6163
|
+
exports.fetchLiveIndexingKeys = fetchLiveIndexingKeys;
|
|
5232
6164
|
exports.fillHistoryViewport = fillHistoryViewport;
|
|
5233
6165
|
exports.filterListByClearHorizon = filterListByClearHorizon;
|
|
5234
6166
|
exports.findAttachmentParser = findAttachmentParser;
|
|
@@ -5238,11 +6170,16 @@ exports.getChatHistory = getChatHistory;
|
|
|
5238
6170
|
exports.getContextWindow = getContextWindow;
|
|
5239
6171
|
exports.getErrorMessage = getErrorMessage;
|
|
5240
6172
|
exports.getExpiredAttachmentVisiblePath = getExpiredAttachmentVisiblePath;
|
|
6173
|
+
exports.getInputTokenBudget = getInputTokenBudget;
|
|
6174
|
+
exports.getMaxOutputTokens = getMaxOutputTokens;
|
|
6175
|
+
exports.getModelContextWindow = getModelContextWindow;
|
|
5241
6176
|
exports.getProjectContextWindow = getProjectContextWindow;
|
|
6177
|
+
exports.getSplitChatHistory = getSplitChatHistory;
|
|
5242
6178
|
exports.getVisionProfile = getVisionProfile;
|
|
5243
6179
|
exports.groupAttachmentFailures = groupAttachmentFailures;
|
|
5244
6180
|
exports.hasBom = hasBom;
|
|
5245
6181
|
exports.hydrateImagePreviews = hydrateImagePreviews;
|
|
6182
|
+
exports.indexDoneUniqueId = indexDoneUniqueId;
|
|
5246
6183
|
exports.isAuthExpiredError = isAuthExpiredError;
|
|
5247
6184
|
exports.isBgIndexingQueue = isBgIndexingQueue;
|
|
5248
6185
|
exports.isErrorResponseBody = isErrorResponseBody;
|
|
@@ -5252,6 +6189,7 @@ exports.isLinkUnavailable = isLinkUnavailable;
|
|
|
5252
6189
|
exports.isNonRetryableRequestError = isNonRetryableRequestError;
|
|
5253
6190
|
exports.isOfficeFile = isOfficeFile;
|
|
5254
6191
|
exports.isPreviewableImagePath = isPreviewableImagePath;
|
|
6192
|
+
exports.isProviderApiKeyError = isProviderApiKeyError;
|
|
5255
6193
|
exports.isServerExtractable = isServerExtractable;
|
|
5256
6194
|
exports.isServiceDbAttachmentHref = isServiceDbAttachmentHref;
|
|
5257
6195
|
exports.linkUnavailableKeyForHref = linkUnavailableKeyForHref;
|
|
@@ -5283,6 +6221,7 @@ exports.renderInlineLinkHtml = renderInlineLinkHtml;
|
|
|
5283
6221
|
exports.repairUrlEntities = repairUrlEntities;
|
|
5284
6222
|
exports.repairUrlWhitespace = repairUrlWhitespace;
|
|
5285
6223
|
exports.resolveImagePreviewUrl = resolveImagePreviewUrl;
|
|
6224
|
+
exports.runIndexUniqueId = runIndexUniqueId;
|
|
5286
6225
|
exports.safeDecodeURIComponent = safeDecodeURIComponent;
|
|
5287
6226
|
exports.sanitizeAttachmentLinksForHistory = sanitizeAttachmentLinksForHistory;
|
|
5288
6227
|
exports.setProjectContextWindow = setProjectContextWindow;
|
|
@@ -5290,6 +6229,7 @@ exports.stripFileBlocksFromHistory = stripFileBlocksFromHistory;
|
|
|
5290
6229
|
exports.transformContentWithImages = transformContentWithImages;
|
|
5291
6230
|
exports.transformContentWithOpenAIImages = transformContentWithOpenAIImages;
|
|
5292
6231
|
exports.truncateLabelForDisplay = truncateLabelForDisplay;
|
|
6232
|
+
exports.upsertIndexRunRecordSafe = upsertIndexRunRecordSafe;
|
|
5293
6233
|
exports.wallClockNow = wallClockNow;
|
|
5294
6234
|
//# sourceMappingURL=engine.cjs.map
|
|
5295
6235
|
//# sourceMappingURL=engine.cjs.map
|