bunnyquery 1.8.5 → 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/README.md +1 -1
- package/bunnyquery.css +168 -26
- package/bunnyquery.js +1429 -118
- package/dist/engine.cjs +1019 -79
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +507 -164
- package/dist/engine.d.ts +507 -164
- package/dist/engine.mjs +1003 -80
- 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 +165 -12
- package/src/engine/session.ts +544 -35
- package/src/widget.css +54 -18
- package/styles/chat.css +114 -8
package/bunnyquery.js
CHANGED
|
@@ -211,7 +211,7 @@
|
|
|
211
211
|
if (isImageVisionFile(name, mime)) return false;
|
|
212
212
|
return isPagedReadFile(name, mime);
|
|
213
213
|
}
|
|
214
|
-
function composeUserMessage(text, attachmentUrls) {
|
|
214
|
+
function composeUserMessage(text, attachmentUrls, opts) {
|
|
215
215
|
let composed = text;
|
|
216
216
|
let composedForLlm = composed;
|
|
217
217
|
if (attachmentUrls.length > 0) {
|
|
@@ -225,7 +225,7 @@ ${lines.join("\n")}`;
|
|
|
225
225
|
let extractContent;
|
|
226
226
|
let fileUrls;
|
|
227
227
|
if (attachmentUrls.length > 0) {
|
|
228
|
-
const extractFiles =
|
|
228
|
+
const extractFiles = [];
|
|
229
229
|
if (extractFiles.length > 0) {
|
|
230
230
|
const directives = [];
|
|
231
231
|
const sections = extractFiles.map((u) => {
|
|
@@ -286,7 +286,7 @@ Never assert absence from a partial read. Do not say "there is no X", "none", "n
|
|
|
286
286
|
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.
|
|
287
287
|
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.
|
|
288
288
|
- 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.
|
|
289
|
-
-
|
|
289
|
+
- 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.
|
|
290
290
|
- 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.
|
|
291
291
|
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.
|
|
292
292
|
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.
|
|
@@ -828,24 +828,71 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
828
828
|
// src/engine/budget.ts
|
|
829
829
|
var CONTEXT_WINDOW_DEFAULT = { claude: 2e5, openai: 128e3 };
|
|
830
830
|
var CONTEXT_WINDOW_BY_MODEL = {
|
|
831
|
-
// exact ids
|
|
831
|
+
// claude, exact ids
|
|
832
|
+
"claude-fable-5": 1e6,
|
|
832
833
|
"claude-opus-5": 1e6,
|
|
833
834
|
"claude-opus-4-8": 1e6,
|
|
834
835
|
"claude-opus-4-7": 1e6,
|
|
836
|
+
"claude-opus-4-6": 1e6,
|
|
837
|
+
"claude-opus-4-5": 2e5,
|
|
835
838
|
"claude-sonnet-5": 1e6,
|
|
836
839
|
"claude-sonnet-4-6": 1e6,
|
|
840
|
+
"claude-sonnet-4-5": 1e6,
|
|
837
841
|
"claude-sonnet-4": 2e5,
|
|
838
842
|
"claude-haiku-4-5": 2e5,
|
|
839
|
-
"
|
|
840
|
-
|
|
843
|
+
"claude-3-5-sonnet": 2e5,
|
|
844
|
+
// openai, exact ids
|
|
845
|
+
"gpt-5.6-sol": 105e4,
|
|
846
|
+
"gpt-5.6-terra": 105e4,
|
|
847
|
+
"gpt-5.6-luna": 105e4,
|
|
848
|
+
"gpt-5.5": 1e6,
|
|
849
|
+
"gpt-5.4": 105e4,
|
|
850
|
+
"gpt-5.4-mini": 4e5,
|
|
851
|
+
"gpt-5.4-nano": 4e5,
|
|
852
|
+
"gpt-4.1": 104e4,
|
|
853
|
+
"gpt-4o": 128e3,
|
|
854
|
+
"o1": 2e5,
|
|
855
|
+
"o1-pro": 2e5,
|
|
841
856
|
// family keys
|
|
857
|
+
"claude-fable": 1e6,
|
|
842
858
|
"claude-opus": 1e6,
|
|
843
859
|
"claude-sonnet": 1e6,
|
|
844
860
|
"claude-haiku": 2e5,
|
|
861
|
+
"gpt-5.6": 105e4,
|
|
862
|
+
"gpt-5": 128e3
|
|
863
|
+
};
|
|
864
|
+
var MAX_OUTPUT_BY_MODEL = {
|
|
865
|
+
// claude
|
|
866
|
+
"claude-fable-5": 128e3,
|
|
867
|
+
"claude-opus-5": 128e3,
|
|
868
|
+
"claude-opus-4-8": 128e3,
|
|
869
|
+
"claude-sonnet-5": 128e3,
|
|
870
|
+
"claude-sonnet-4-6": 64e3,
|
|
871
|
+
"claude-haiku-4-5": 64e3,
|
|
872
|
+
"claude-3-5-sonnet": 8e3,
|
|
873
|
+
// openai
|
|
874
|
+
"gpt-5.6-sol": 128e3,
|
|
875
|
+
"gpt-5.6-terra": 128e3,
|
|
876
|
+
"gpt-5.6-luna": 128e3,
|
|
877
|
+
"gpt-5.5": 128e3,
|
|
878
|
+
"gpt-5.4": 128e3,
|
|
879
|
+
"gpt-5.4-mini": 128e3,
|
|
880
|
+
"gpt-5.4-nano": 128e3,
|
|
881
|
+
"gpt-4.1": 16e3,
|
|
882
|
+
"gpt-4o": 4e3,
|
|
883
|
+
"o1": 1e5,
|
|
884
|
+
"o1-pro": 1e5,
|
|
885
|
+
// family keys
|
|
886
|
+
"claude-fable": 128e3,
|
|
887
|
+
"claude-opus": 128e3,
|
|
888
|
+
"claude-sonnet": 64e3,
|
|
889
|
+
"claude-haiku": 64e3,
|
|
845
890
|
"gpt-5.6": 128e3,
|
|
846
891
|
"gpt-5": 128e3
|
|
847
892
|
};
|
|
893
|
+
var DEFAULT_CONTEXT_WINDOW = 88e4;
|
|
848
894
|
var apiReportedContextWindows = {};
|
|
895
|
+
var apiReportedMaxOutput = {};
|
|
849
896
|
var projectContextWindows = {};
|
|
850
897
|
function setProjectContextWindow(projectId, tokens) {
|
|
851
898
|
var key = (projectId || "").trim();
|
|
@@ -858,13 +905,13 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
858
905
|
var key = (projectId || "").trim();
|
|
859
906
|
return key && projectContextWindows[key] ? projectContextWindows[key] : null;
|
|
860
907
|
}
|
|
861
|
-
var
|
|
908
|
+
var MAX_OUTPUT_TOKENS = 25e3;
|
|
862
909
|
var TOOL_AND_RESPONSE_BUFFER = 4e3;
|
|
863
910
|
var MIN_INPUT_TOKEN_BUDGET = 8e3;
|
|
864
|
-
var
|
|
911
|
+
var MIN_PER_REQUEST_INPUT_CAP = 28e3;
|
|
865
912
|
var MAX_HISTORY_MESSAGES = 20;
|
|
866
913
|
var HISTORY_TOKEN_BUDGET = 8e3;
|
|
867
|
-
var
|
|
914
|
+
var INPUT_CAP_RATIO = 0.16;
|
|
868
915
|
var HISTORY_BUDGET_RATIO = 0.08;
|
|
869
916
|
function estimateTextTokens(text) {
|
|
870
917
|
return Math.ceil((text || "").length / 3);
|
|
@@ -872,38 +919,61 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
872
919
|
function estimateMessageTokens(msg) {
|
|
873
920
|
return estimateTextTokens(msg.content) + estimateTextTokens(msg.role) + 6;
|
|
874
921
|
}
|
|
922
|
+
function resolveByModelId(apiTable, staticTable, model) {
|
|
923
|
+
var normalized = (model || "").trim().toLowerCase();
|
|
924
|
+
if (!normalized) return 0;
|
|
925
|
+
if (apiTable[normalized]) return apiTable[normalized];
|
|
926
|
+
if (staticTable[normalized]) return staticTable[normalized];
|
|
927
|
+
var parts = normalized.split("-");
|
|
928
|
+
for (var end = parts.length - 1; end > 0; end--) {
|
|
929
|
+
var family = parts.slice(0, end).join("-");
|
|
930
|
+
if (staticTable[family]) return staticTable[family];
|
|
931
|
+
}
|
|
932
|
+
return 0;
|
|
933
|
+
}
|
|
934
|
+
function getModelContextWindow(platform, model) {
|
|
935
|
+
return resolveByModelId(apiReportedContextWindows, CONTEXT_WINDOW_BY_MODEL, model) || CONTEXT_WINDOW_DEFAULT[platform];
|
|
936
|
+
}
|
|
937
|
+
function getMaxOutputTokens(platform, model) {
|
|
938
|
+
var cap = resolveByModelId(apiReportedMaxOutput, MAX_OUTPUT_BY_MODEL, model);
|
|
939
|
+
return cap ? Math.min(MAX_OUTPUT_TOKENS, cap) : MAX_OUTPUT_TOKENS;
|
|
940
|
+
}
|
|
875
941
|
function getContextWindow(platform, model, projectId) {
|
|
942
|
+
var ceiling = getModelContextWindow(platform, model);
|
|
876
943
|
var override = projectId ? getProjectContextWindow(projectId) : null;
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
return
|
|
944
|
+
return Math.min(override || DEFAULT_CONTEXT_WINDOW, ceiling);
|
|
945
|
+
}
|
|
946
|
+
function contextBasedBudgetFor(platform, model, projectId) {
|
|
947
|
+
var contextWindow = getContextWindow(platform, model, projectId);
|
|
948
|
+
return Math.max(
|
|
949
|
+
MIN_INPUT_TOKEN_BUDGET,
|
|
950
|
+
contextWindow - getMaxOutputTokens(platform, model) - TOOL_AND_RESPONSE_BUFFER
|
|
951
|
+
);
|
|
952
|
+
}
|
|
953
|
+
function getInputTokenBudget(platform, model, projectId) {
|
|
954
|
+
var contextBasedBudget = contextBasedBudgetFor(platform, model, projectId);
|
|
955
|
+
return Math.min(
|
|
956
|
+
contextBasedBudget,
|
|
957
|
+
Math.max(MIN_PER_REQUEST_INPUT_CAP, Math.round(contextBasedBudget * INPUT_CAP_RATIO))
|
|
958
|
+
);
|
|
889
959
|
}
|
|
890
960
|
function stripFileBlocksFromHistory(content) {
|
|
891
961
|
if (!content) return content;
|
|
892
962
|
return content.replace(/```([^\n`]+?\.[^\s.`]+)\n[\s\S]*?```/g, "[file previously attached: $1]");
|
|
893
963
|
}
|
|
894
964
|
function buildBoundedChatMessages(options) {
|
|
895
|
-
var
|
|
896
|
-
var
|
|
897
|
-
MIN_INPUT_TOKEN_BUDGET,
|
|
898
|
-
contextWindow - OUTPUT_TOKEN_RESERVE - TOOL_AND_RESPONSE_BUFFER
|
|
899
|
-
);
|
|
900
|
-
var scaled = !!(options.projectId && getProjectContextWindow(options.projectId));
|
|
901
|
-
var claudeInputCap = scaled ? Math.max(CLAUDE_PER_REQUEST_INPUT_CAP, Math.round(contextBasedBudget * CLAUDE_INPUT_CAP_RATIO)) : CLAUDE_PER_REQUEST_INPUT_CAP;
|
|
902
|
-
var availableInputBudget = options.platform === "claude" ? Math.min(contextBasedBudget, claudeInputCap) : contextBasedBudget;
|
|
965
|
+
var contextBasedBudget = contextBasedBudgetFor(options.platform, options.model, options.projectId);
|
|
966
|
+
var availableInputBudget = getInputTokenBudget(options.platform, options.model, options.projectId);
|
|
903
967
|
var systemCost = estimateTextTokens(options.systemPrompt) + 12;
|
|
904
|
-
var historyAllowance =
|
|
968
|
+
var historyAllowance = Math.max(
|
|
969
|
+
HISTORY_TOKEN_BUDGET,
|
|
970
|
+
Math.round(contextBasedBudget * HISTORY_BUDGET_RATIO)
|
|
971
|
+
);
|
|
905
972
|
var budgetForHistory = Math.max(1e3, Math.min(historyAllowance, availableInputBudget - systemCost));
|
|
906
|
-
var maxHistoryMessages =
|
|
973
|
+
var maxHistoryMessages = Math.max(
|
|
974
|
+
MAX_HISTORY_MESSAGES,
|
|
975
|
+
Math.round(MAX_HISTORY_MESSAGES * (budgetForHistory / HISTORY_TOKEN_BUDGET))
|
|
976
|
+
);
|
|
907
977
|
var windowed = options.history.slice(-maxHistoryMessages);
|
|
908
978
|
var latestIndex = windowed.length - 1;
|
|
909
979
|
var trimmed = windowed.map(function(m, i2) {
|
|
@@ -1276,11 +1346,10 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1276
1346
|
var WEB_FETCH_MAX_USES = 40;
|
|
1277
1347
|
var WEB_FETCH_MAX_CONTENT_TOKENS = 2e5;
|
|
1278
1348
|
var OPENAI_RESPONSES_API_URL = "https://api.openai.com/v1/responses";
|
|
1279
|
-
var MAX_TOKENS = 25e3;
|
|
1280
1349
|
var DEFAULT_OPENAI_IMAGE_DETAIL = "auto";
|
|
1281
1350
|
var OPENAI_WEB_SEARCH_EXTERNAL_WEB_ACCESS = true;
|
|
1282
1351
|
var MCP_NAME = "BunnyQuery";
|
|
1283
|
-
var DEFAULT_CLAUDE_MODEL = "claude-sonnet-
|
|
1352
|
+
var DEFAULT_CLAUDE_MODEL = "claude-sonnet-5";
|
|
1284
1353
|
var DEFAULT_OPENAI_MODEL = "gpt-5.6-luna";
|
|
1285
1354
|
var mcpUrl = () => chatEngineConfig().mcpBaseUrl;
|
|
1286
1355
|
var clientSecretRequest = (opts) => chatEngineConfig().clientSecretRequest(opts);
|
|
@@ -1516,7 +1585,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1516
1585
|
owner,
|
|
1517
1586
|
userId,
|
|
1518
1587
|
model: model || DEFAULT_CLAUDE_MODEL,
|
|
1519
|
-
maxTokens:
|
|
1588
|
+
maxTokens: getMaxOutputTokens("claude", model || DEFAULT_CLAUDE_MODEL),
|
|
1520
1589
|
system,
|
|
1521
1590
|
extractContent,
|
|
1522
1591
|
fileUrls,
|
|
@@ -1561,7 +1630,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1561
1630
|
},
|
|
1562
1631
|
data: {
|
|
1563
1632
|
model: resolvedModel,
|
|
1564
|
-
max_output_tokens:
|
|
1633
|
+
max_output_tokens: getMaxOutputTokens("openai", resolvedModel),
|
|
1565
1634
|
...extractContent && extractContent.length ? { _skapi_extract: extractContent } : {},
|
|
1566
1635
|
...fileUrls && fileUrls.length ? { _skapi_file_urls: fileUrls } : {},
|
|
1567
1636
|
input: responseInput,
|
|
@@ -1591,6 +1660,29 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1591
1660
|
async function notifyAgentSaveAttachment(info) {
|
|
1592
1661
|
const { platform, service, owner, attachment, parsedContent } = info;
|
|
1593
1662
|
const continuing = !!info.continueIndexing;
|
|
1663
|
+
if (!continuing) {
|
|
1664
|
+
upsertIndexRunRecordSafe(service, attachment.storagePath, {
|
|
1665
|
+
status: "working",
|
|
1666
|
+
filename: attachment.name,
|
|
1667
|
+
started: Date.now(),
|
|
1668
|
+
queue: bgIndexingQueueName(info.userId, service),
|
|
1669
|
+
platform
|
|
1670
|
+
});
|
|
1671
|
+
}
|
|
1672
|
+
const tapDispatchFailure = (p) => {
|
|
1673
|
+
if (continuing) return p;
|
|
1674
|
+
return p.then(
|
|
1675
|
+
(ack) => ack,
|
|
1676
|
+
(err) => {
|
|
1677
|
+
upsertIndexRunRecordSafe(service, attachment.storagePath, {
|
|
1678
|
+
status: "error",
|
|
1679
|
+
finished: Date.now(),
|
|
1680
|
+
error: err && (err.message || String(err)) || "The indexing request could not be enqueued."
|
|
1681
|
+
});
|
|
1682
|
+
throw err;
|
|
1683
|
+
}
|
|
1684
|
+
);
|
|
1685
|
+
};
|
|
1594
1686
|
const visionFile = !parsedContent && isImageVisionFile(attachment.name, attachment.mime);
|
|
1595
1687
|
const renderFrom = Math.max(0, info.renderFrom || 0);
|
|
1596
1688
|
const renderPlaceholder = visionFile ? makeRenderPlaceholder(attachment.storagePath) : void 0;
|
|
@@ -1650,6 +1742,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1650
1742
|
save_media: !continuing
|
|
1651
1743
|
}))
|
|
1652
1744
|
} : {};
|
|
1745
|
+
const skapiFileUrls = attachment.url && attachment.storagePath ? { _skapi_file_urls: [{ path: attachment.storagePath, url: attachment.url }] } : {};
|
|
1653
1746
|
const userMessage = visionFile && renderPlaceholder ? buildIndexingRenderMessage(attachment, renderPlaceholder, renderFrom) : windowedRead && windowPlaceholder ? buildIndexingWindowMessage(attachment, windowPlaceholder, false) : continuing ? buildIndexingContinueMessage(attachment) : buildIndexingUserMessage(
|
|
1654
1747
|
attachment,
|
|
1655
1748
|
parsedContent ? { inlineContent: parsedContent } : placeholder ? { inlineContentPlaceholder: placeholder } : pagedRead ? { pagedRead: true } : void 0
|
|
@@ -1665,7 +1758,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1665
1758
|
if (platform === "openai") {
|
|
1666
1759
|
const resolvedModel2 = info.model || DEFAULT_OPENAI_MODEL;
|
|
1667
1760
|
const imageDetail = getOpenAIImageDetail(resolvedModel2);
|
|
1668
|
-
return clientSecretRequest({
|
|
1761
|
+
return tapDispatchFailure(clientSecretRequest({
|
|
1669
1762
|
clientSecretName: "openai",
|
|
1670
1763
|
queue: bgIndexingQueueName(info.userId, service),
|
|
1671
1764
|
service,
|
|
@@ -1679,12 +1772,13 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1679
1772
|
},
|
|
1680
1773
|
data: {
|
|
1681
1774
|
model: resolvedModel2,
|
|
1682
|
-
max_output_tokens:
|
|
1775
|
+
max_output_tokens: getMaxOutputTokens("openai", resolvedModel2),
|
|
1683
1776
|
// Nano-only transcription knobs. Indexing only; see variantIndexingOptions.
|
|
1684
1777
|
...variantIndexingOptions(resolvedModel2),
|
|
1685
1778
|
...skapiExtract,
|
|
1686
1779
|
...skapiRender,
|
|
1687
1780
|
...skapiWindow,
|
|
1781
|
+
...skapiFileUrls,
|
|
1688
1782
|
input: [
|
|
1689
1783
|
{ role: "system", content: systemPrompt },
|
|
1690
1784
|
{
|
|
@@ -1708,10 +1802,10 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1708
1802
|
]
|
|
1709
1803
|
]
|
|
1710
1804
|
}
|
|
1711
|
-
});
|
|
1805
|
+
}));
|
|
1712
1806
|
}
|
|
1713
1807
|
const resolvedModel = info.model || DEFAULT_CLAUDE_MODEL;
|
|
1714
|
-
return clientSecretRequest({
|
|
1808
|
+
return tapDispatchFailure(clientSecretRequest({
|
|
1715
1809
|
clientSecretName: "claude",
|
|
1716
1810
|
queue: bgIndexingQueueName(info.userId, service),
|
|
1717
1811
|
service,
|
|
@@ -1727,10 +1821,11 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1727
1821
|
},
|
|
1728
1822
|
data: {
|
|
1729
1823
|
model: resolvedModel,
|
|
1730
|
-
max_tokens:
|
|
1824
|
+
max_tokens: getMaxOutputTokens("claude", resolvedModel),
|
|
1731
1825
|
...skapiExtract,
|
|
1732
1826
|
...skapiRender,
|
|
1733
1827
|
...skapiWindow,
|
|
1828
|
+
...skapiFileUrls,
|
|
1734
1829
|
system: [
|
|
1735
1830
|
{
|
|
1736
1831
|
type: "text",
|
|
@@ -1766,7 +1861,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1766
1861
|
}
|
|
1767
1862
|
]
|
|
1768
1863
|
}
|
|
1769
|
-
});
|
|
1864
|
+
}));
|
|
1770
1865
|
}
|
|
1771
1866
|
function extractClaudeText(response) {
|
|
1772
1867
|
if (!Array.isArray(response?.content)) {
|
|
@@ -1802,6 +1897,21 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1802
1897
|
return "";
|
|
1803
1898
|
}
|
|
1804
1899
|
var BG_INDEXING_QUEUE_SUFFIX = "-bg";
|
|
1900
|
+
function indexDoneUniqueId(storagePath) {
|
|
1901
|
+
return "done::" + storagePath;
|
|
1902
|
+
}
|
|
1903
|
+
function runIndexUniqueId(storagePath) {
|
|
1904
|
+
return "run::" + storagePath;
|
|
1905
|
+
}
|
|
1906
|
+
function upsertIndexRunRecordSafe(service, storagePath, patch) {
|
|
1907
|
+
if (!service || !storagePath) return;
|
|
1908
|
+
try {
|
|
1909
|
+
const hook = chatEngineConfig().upsertIndexRunRecord;
|
|
1910
|
+
if (typeof hook !== "function") return;
|
|
1911
|
+
hook({ service, storagePath, patch });
|
|
1912
|
+
} catch (e) {
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1805
1915
|
function bgIndexingQueueName(userId, service) {
|
|
1806
1916
|
return (userId || service || "") + BG_INDEXING_QUEUE_SUFFIX;
|
|
1807
1917
|
}
|
|
@@ -1825,13 +1935,20 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1825
1935
|
},
|
|
1826
1936
|
{ service: params.service, owner: params.owner },
|
|
1827
1937
|
params.queue ? { queue: params.queue } : {},
|
|
1828
|
-
params.status ? { status: params.status } : {}
|
|
1938
|
+
params.status ? { status: params.status } : {},
|
|
1939
|
+
params.queue_exact ? { queue_exact: true } : {},
|
|
1940
|
+
params.compact ? { compact: true } : {},
|
|
1941
|
+
params.queue_exclude ? { queue_exclude: params.queue_exclude } : {}
|
|
1829
1942
|
);
|
|
1830
1943
|
return chatEngineConfig().clientSecretRequestHistory(
|
|
1831
1944
|
p,
|
|
1832
1945
|
Object.assign({ ascending: false, limit: CHAT_HISTORY_PAGE_LIMIT }, fetchOptions)
|
|
1833
1946
|
);
|
|
1834
1947
|
}
|
|
1948
|
+
function buildHistoryItemFullId(platform, service, itemId) {
|
|
1949
|
+
const url = platform === "claude" ? ANTHROPIC_MESSAGES_API_URL : OPENAI_RESPONSES_API_URL;
|
|
1950
|
+
return `[POST]${url.toLowerCase()}#${service}:${itemId}`;
|
|
1951
|
+
}
|
|
1835
1952
|
|
|
1836
1953
|
// src/engine/history.ts
|
|
1837
1954
|
function filterListByClearHorizon(list, clearedAt) {
|
|
@@ -1881,6 +1998,244 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1881
1998
|
continued: userText.indexOf("CONTINUE indexing") === 0
|
|
1882
1999
|
};
|
|
1883
2000
|
}
|
|
2001
|
+
var BG_PROBE_TTL_MS = 4e3;
|
|
2002
|
+
var bgProbeCache = {};
|
|
2003
|
+
var bgProbeInflight = {};
|
|
2004
|
+
function probeBgQueue(params, opts) {
|
|
2005
|
+
const key = [params.service, params.owner, params.platform, params.queue, params.status, params.limit].join("|");
|
|
2006
|
+
const maxAge = opts && typeof opts.maxAgeMs === "number" ? opts.maxAgeMs : 0;
|
|
2007
|
+
const cached = bgProbeCache[key];
|
|
2008
|
+
if (maxAge > 0 && cached && Date.now() - cached.at < maxAge) {
|
|
2009
|
+
return Promise.resolve(cached);
|
|
2010
|
+
}
|
|
2011
|
+
const inflight = bgProbeInflight[key];
|
|
2012
|
+
if (inflight) return inflight;
|
|
2013
|
+
const p = Promise.resolve(getChatHistory(
|
|
2014
|
+
{ service: params.service, owner: params.owner, platform: params.platform, queue: params.queue, status: params.status },
|
|
2015
|
+
{ limit: params.limit, fetchMore: false }
|
|
2016
|
+
)).then(function(result) {
|
|
2017
|
+
const entry = { result, at: Date.now() };
|
|
2018
|
+
bgProbeCache[key] = entry;
|
|
2019
|
+
return entry;
|
|
2020
|
+
});
|
|
2021
|
+
bgProbeInflight[key] = p;
|
|
2022
|
+
p.then(function() {
|
|
2023
|
+
delete bgProbeInflight[key];
|
|
2024
|
+
}, function() {
|
|
2025
|
+
delete bgProbeInflight[key];
|
|
2026
|
+
});
|
|
2027
|
+
return p;
|
|
2028
|
+
}
|
|
2029
|
+
var BG_COVERAGE_MAX_PAGES = 2;
|
|
2030
|
+
var splitHistoryStates = {};
|
|
2031
|
+
var splitHistoryLocks = {};
|
|
2032
|
+
function freshSplitState() {
|
|
2033
|
+
return { bgBuffer: [], bgEnd: false, bgStarted: false, surfaceEnd: false, pendingSurface: null, surfaceCarry: [], lastSurfaceKeys: [], newestBgId: "" };
|
|
2034
|
+
}
|
|
2035
|
+
function noteBgIds(state, list) {
|
|
2036
|
+
for (const it of list) {
|
|
2037
|
+
const id = it && typeof it.id === "string" ? it.id : "";
|
|
2038
|
+
if (id && id > state.newestBgId) state.newestBgId = id;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
2041
|
+
var createdOf = (it) => {
|
|
2042
|
+
const c = Number(it && it.created);
|
|
2043
|
+
return isFinite(c) && c > 0 ? c : NaN;
|
|
2044
|
+
};
|
|
2045
|
+
var oldestCreated = (lst) => {
|
|
2046
|
+
let m = Infinity;
|
|
2047
|
+
for (const it of lst) {
|
|
2048
|
+
const c = createdOf(it);
|
|
2049
|
+
if (!isNaN(c) && c < m) m = c;
|
|
2050
|
+
}
|
|
2051
|
+
return m;
|
|
2052
|
+
};
|
|
2053
|
+
var SURFACE_EMPTY_MAX_PAGES = 10;
|
|
2054
|
+
async function getSplitChatHistory(params, fetchOptions, _fetchImpl) {
|
|
2055
|
+
const key = [params.service, params.owner, params.platform, params.userId || ""].join("|");
|
|
2056
|
+
const prev = splitHistoryLocks[key] || Promise.resolve();
|
|
2057
|
+
let releaseLock;
|
|
2058
|
+
const lockTail = new Promise((r) => {
|
|
2059
|
+
releaseLock = r;
|
|
2060
|
+
});
|
|
2061
|
+
const run = () => _getSplitChatHistoryLocked(key, params, fetchOptions, releaseLock);
|
|
2062
|
+
const p = prev.then(run, run);
|
|
2063
|
+
p.then((res) => {
|
|
2064
|
+
if (!res || !res.bgPending) releaseLock();
|
|
2065
|
+
}, () => releaseLock());
|
|
2066
|
+
splitHistoryLocks[key] = p.then(() => lockTail, () => lockTail);
|
|
2067
|
+
return p;
|
|
2068
|
+
}
|
|
2069
|
+
async function _getSplitChatHistoryLocked(key, params, fetchOptions, releaseLock, _fetchImpl) {
|
|
2070
|
+
const fetch2 = getChatHistory;
|
|
2071
|
+
const bgQueue = bgIndexingQueueName(params.userId, params.service);
|
|
2072
|
+
const base = { service: params.service, owner: params.owner, platform: params.platform };
|
|
2073
|
+
const fetchMore = !!(fetchOptions && fetchOptions.fetchMore);
|
|
2074
|
+
const limit = fetchOptions && fetchOptions.limit;
|
|
2075
|
+
const firstLoad = !splitHistoryStates[key];
|
|
2076
|
+
let headRefresh = false;
|
|
2077
|
+
if (!splitHistoryStates[key]) {
|
|
2078
|
+
splitHistoryStates[key] = freshSplitState();
|
|
2079
|
+
} else if (!fetchMore) {
|
|
2080
|
+
const prev = splitHistoryStates[key];
|
|
2081
|
+
if (prev.surfaceEnd && prev.bgEnd) {
|
|
2082
|
+
headRefresh = true;
|
|
2083
|
+
prev.pendingSurface = null;
|
|
2084
|
+
prev.surfaceCarry = [];
|
|
2085
|
+
prev.bgBuffer = [];
|
|
2086
|
+
} else {
|
|
2087
|
+
splitHistoryStates[key] = freshSplitState();
|
|
2088
|
+
}
|
|
2089
|
+
}
|
|
2090
|
+
const state = splitHistoryStates[key];
|
|
2091
|
+
if (state.pendingSurface && state.pendingSurface.forFetchMore !== fetchMore) {
|
|
2092
|
+
state.pendingSurface = null;
|
|
2093
|
+
}
|
|
2094
|
+
if (!state.pendingSurface) {
|
|
2095
|
+
if (state.surfaceEnd && !headRefresh) {
|
|
2096
|
+
state.pendingSurface = { list: [], endOfList: true, startKeyHistory: state.lastSurfaceKeys, forFetchMore: fetchMore };
|
|
2097
|
+
} else {
|
|
2098
|
+
const sOpts = { fetchMore };
|
|
2099
|
+
if (limit) sOpts.limit = limit;
|
|
2100
|
+
let s = await fetch2({ ...base, queue_exclude: bgQueue }, sOpts);
|
|
2101
|
+
let hops = 0;
|
|
2102
|
+
while (s && !s.endOfList && !(s.list || []).length && hops < SURFACE_EMPTY_MAX_PAGES) {
|
|
2103
|
+
hops++;
|
|
2104
|
+
const nOpts = { fetchMore: true };
|
|
2105
|
+
if (limit) nOpts.limit = limit;
|
|
2106
|
+
s = await fetch2({ ...base, queue_exclude: bgQueue }, nOpts);
|
|
2107
|
+
}
|
|
2108
|
+
state.pendingSurface = {
|
|
2109
|
+
list: s && Array.isArray(s.list) ? s.list : [],
|
|
2110
|
+
endOfList: !!(s && s.endOfList),
|
|
2111
|
+
startKeyHistory: s && Array.isArray(s.startKeyHistory) ? s.startKeyHistory : [],
|
|
2112
|
+
forFetchMore: fetchMore
|
|
2113
|
+
};
|
|
2114
|
+
}
|
|
2115
|
+
}
|
|
2116
|
+
const surface = state.pendingSurface;
|
|
2117
|
+
if (fetchOptions && fetchOptions.deferBg && (!state.bgEnd || headRefresh)) {
|
|
2118
|
+
const surfaceList0 = state.surfaceCarry.length ? state.surfaceCarry.concat(surface.list) : surface.list.slice();
|
|
2119
|
+
state.surfaceCarry = [];
|
|
2120
|
+
const emitNow = surfaceList0.concat(state.bgBuffer);
|
|
2121
|
+
state.bgBuffer = [];
|
|
2122
|
+
if (!headRefresh) state.surfaceEnd = surface.endOfList;
|
|
2123
|
+
state.lastSurfaceKeys = surface.startKeyHistory;
|
|
2124
|
+
state.pendingSurface = null;
|
|
2125
|
+
const bgPending = (async () => {
|
|
2126
|
+
try {
|
|
2127
|
+
const batch = [];
|
|
2128
|
+
if (headRefresh) {
|
|
2129
|
+
const bOpts = { fetchMore: false };
|
|
2130
|
+
if (limit) bOpts.limit = limit;
|
|
2131
|
+
const b = await fetch2({ ...base, queue: bgQueue, queue_exact: true, compact: true }, bOpts);
|
|
2132
|
+
const bList = b && Array.isArray(b.list) ? b.list : [];
|
|
2133
|
+
for (const it of bList) {
|
|
2134
|
+
if (it && typeof it === "object") it._fromBgChain = true;
|
|
2135
|
+
batch.push(it);
|
|
2136
|
+
}
|
|
2137
|
+
const prevNewest = state.newestBgId;
|
|
2138
|
+
noteBgIds(state, bList);
|
|
2139
|
+
if (prevNewest && !(b && b.endOfList) && !bList.some((it) => it && it.id === prevNewest)) {
|
|
2140
|
+
state.bgEnd = false;
|
|
2141
|
+
state.bgStarted = true;
|
|
2142
|
+
}
|
|
2143
|
+
} else {
|
|
2144
|
+
let hops = 0;
|
|
2145
|
+
while (!state.bgEnd && hops < BG_COVERAGE_MAX_PAGES) {
|
|
2146
|
+
hops++;
|
|
2147
|
+
const bOpts = { fetchMore: state.bgStarted };
|
|
2148
|
+
if (limit) bOpts.limit = limit;
|
|
2149
|
+
const b = await fetch2({ ...base, queue: bgQueue, queue_exact: true, compact: true }, bOpts);
|
|
2150
|
+
state.bgStarted = true;
|
|
2151
|
+
const bList = b && Array.isArray(b.list) ? b.list : [];
|
|
2152
|
+
for (const it of bList) {
|
|
2153
|
+
if (it && typeof it === "object") it._fromBgChain = true;
|
|
2154
|
+
batch.push(it);
|
|
2155
|
+
}
|
|
2156
|
+
noteBgIds(state, bList);
|
|
2157
|
+
state.bgEnd = !!(b && b.endOfList);
|
|
2158
|
+
if (!bList.length && !state.bgEnd) break;
|
|
2159
|
+
if (state.bgEnd) break;
|
|
2160
|
+
}
|
|
2161
|
+
}
|
|
2162
|
+
return { list: batch, endOfList: state.surfaceEnd && state.bgEnd };
|
|
2163
|
+
} finally {
|
|
2164
|
+
releaseLock();
|
|
2165
|
+
}
|
|
2166
|
+
})();
|
|
2167
|
+
return {
|
|
2168
|
+
list: emitNow,
|
|
2169
|
+
// A head-refreshed ended chain KNOWS it is still ended — reporting
|
|
2170
|
+
// the hardcoded false here was what un-gated the fill loop on every
|
|
2171
|
+
// tab return. Mid-walk it computes to false exactly as before (this
|
|
2172
|
+
// branch is only entered with bgEnd false then); the bg batch still
|
|
2173
|
+
// carries the final word for that case.
|
|
2174
|
+
endOfList: state.surfaceEnd && state.bgEnd,
|
|
2175
|
+
startKeyHistory: surface.startKeyHistory,
|
|
2176
|
+
firstLoad,
|
|
2177
|
+
bgPending
|
|
2178
|
+
};
|
|
2179
|
+
}
|
|
2180
|
+
const surfaceList = state.surfaceCarry.length ? state.surfaceCarry.concat(surface.list) : surface.list.slice();
|
|
2181
|
+
const boundary = surface.endOfList ? -Infinity : oldestCreated(surfaceList);
|
|
2182
|
+
if (headRefresh) {
|
|
2183
|
+
const hOpts = { fetchMore: false };
|
|
2184
|
+
if (limit) hOpts.limit = limit;
|
|
2185
|
+
const hb = await fetch2({ ...base, queue: bgQueue, queue_exact: true, compact: true }, hOpts);
|
|
2186
|
+
const hbList = hb && Array.isArray(hb.list) ? hb.list : [];
|
|
2187
|
+
for (const it of hbList) {
|
|
2188
|
+
if (it && typeof it === "object") it._fromBgChain = true;
|
|
2189
|
+
state.bgBuffer.push(it);
|
|
2190
|
+
}
|
|
2191
|
+
const prevNewestH = state.newestBgId;
|
|
2192
|
+
noteBgIds(state, hbList);
|
|
2193
|
+
if (prevNewestH && !(hb && hb.endOfList) && !hbList.some((it) => it && it.id === prevNewestH)) {
|
|
2194
|
+
state.bgEnd = false;
|
|
2195
|
+
state.bgStarted = true;
|
|
2196
|
+
}
|
|
2197
|
+
} else if (boundary !== Infinity || surface.endOfList) {
|
|
2198
|
+
let hops = 0;
|
|
2199
|
+
while (!state.bgEnd && hops < BG_COVERAGE_MAX_PAGES) {
|
|
2200
|
+
const bufOldest = state.bgBuffer.length ? oldestCreated(state.bgBuffer) : Infinity;
|
|
2201
|
+
if (state.bgBuffer.length && bufOldest <= boundary) break;
|
|
2202
|
+
hops++;
|
|
2203
|
+
const bOpts = { fetchMore: state.bgStarted };
|
|
2204
|
+
if (limit) bOpts.limit = limit;
|
|
2205
|
+
const b = await fetch2({ ...base, queue: bgQueue, queue_exact: true, compact: true }, bOpts);
|
|
2206
|
+
state.bgStarted = true;
|
|
2207
|
+
const bList = b && Array.isArray(b.list) ? b.list : [];
|
|
2208
|
+
for (const it of bList) {
|
|
2209
|
+
if (it && typeof it === "object") it._fromBgChain = true;
|
|
2210
|
+
state.bgBuffer.push(it);
|
|
2211
|
+
}
|
|
2212
|
+
noteBgIds(state, bList);
|
|
2213
|
+
state.bgEnd = !!(b && b.endOfList);
|
|
2214
|
+
if (!bList.length && !state.bgEnd) break;
|
|
2215
|
+
if (state.bgEnd) break;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
const emitSurface = surfaceList;
|
|
2219
|
+
state.surfaceCarry = [];
|
|
2220
|
+
const emitBg = state.bgBuffer;
|
|
2221
|
+
state.bgBuffer = [];
|
|
2222
|
+
const seen = {};
|
|
2223
|
+
for (const it of emitSurface) {
|
|
2224
|
+
if (it && typeof it.id === "string") seen[it.id] = true;
|
|
2225
|
+
}
|
|
2226
|
+
const merged = emitSurface.concat(emitBg.filter((it) => !(it && typeof it.id === "string" && seen[it.id])));
|
|
2227
|
+
if (!headRefresh) state.surfaceEnd = surface.endOfList;
|
|
2228
|
+
state.lastSurfaceKeys = surface.startKeyHistory;
|
|
2229
|
+
state.pendingSurface = null;
|
|
2230
|
+
return {
|
|
2231
|
+
list: merged,
|
|
2232
|
+
endOfList: state.surfaceEnd && state.bgEnd && state.bgBuffer.length === 0 && state.surfaceCarry.length === 0,
|
|
2233
|
+
// Bookkeeping only (both the consumers and the SDK treat it opaquely);
|
|
2234
|
+
// the real cursors are the SDK's internal ones plus this module's state.
|
|
2235
|
+
startKeyHistory: surface.startKeyHistory,
|
|
2236
|
+
firstLoad
|
|
2237
|
+
};
|
|
2238
|
+
}
|
|
1884
2239
|
function mapHistoryListToMessages(list, platform, opts) {
|
|
1885
2240
|
var mapped = [], runningItemIds = [];
|
|
1886
2241
|
var extractAssistantText = platform === "openai" ? extractOpenAIText : extractClaudeText;
|
|
@@ -1893,10 +2248,11 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1893
2248
|
var isPending = isInProcess || isQueued;
|
|
1894
2249
|
var isFailed = item && item.status === "failed";
|
|
1895
2250
|
var response = isFailed ? item.error != null ? item.error : item.response_body : item && item.response_body != null ? item.response_body : item && item.error;
|
|
1896
|
-
var
|
|
1897
|
-
var
|
|
1898
|
-
var
|
|
1899
|
-
var
|
|
2251
|
+
var isCompact = !!(item && item.compact);
|
|
2252
|
+
var userText = isCompact ? typeof item.request_text === "string" ? item.request_text : "" : extractLastUserTextFromRequest(requestBody);
|
|
2253
|
+
var assistantText = isPending ? "" : isCompact ? (typeof item.response_text === "string" ? item.response_text : "").trim() : (extractAssistantText(response) || "").trim() || "";
|
|
2254
|
+
var isErrorResponse = !isPending && (isFailed || !isCompact && isErrorResponseBody(response));
|
|
2255
|
+
var reportedComplete = !!(item && item._isBgTask) && !isErrorResponse && (isCompact ? item.response_complete_marker === true : !!assistantText && assistantText.indexOf(INDEXING_COMPLETE_MARKER) !== -1);
|
|
1900
2256
|
if (reportedComplete) assistantText = assistantText.split(INDEXING_COMPLETE_MARKER).join("").trim();
|
|
1901
2257
|
var serverItemId = item && typeof item.id === "string" && item.id ? item.id : void 0;
|
|
1902
2258
|
var createdTs = Number(item && item.created);
|
|
@@ -1925,9 +2281,11 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1925
2281
|
displayContent = sanitizeAttachmentLinksForHistory(userText, opts.projectId);
|
|
1926
2282
|
}
|
|
1927
2283
|
var userMsg = { role: "user", content: displayContent };
|
|
2284
|
+
if (item._fromBgChain) userMsg._fromBgChain = true;
|
|
1928
2285
|
if (isInProcess) userMsg.isPendingInProcess = true;
|
|
1929
2286
|
if (isQueued) userMsg.isPendingQueued = true;
|
|
1930
2287
|
if (isCancelledItem) userMsg.isCancelled = true;
|
|
2288
|
+
if (isCompact) userMsg._compact = true;
|
|
1931
2289
|
if (item._isBgTask) userMsg.isBackgroundTask = true;
|
|
1932
2290
|
if (indexFile) userMsg._indexFile = indexFile;
|
|
1933
2291
|
if (item._isOnBgQueue) userMsg._useBgQueue = true;
|
|
@@ -1937,6 +2295,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1937
2295
|
}
|
|
1938
2296
|
if (isCancelledItem) ; else if (isInProcess) {
|
|
1939
2297
|
var ph = { role: "assistant", content: "", isPending: true, isPendingInProcess: true };
|
|
2298
|
+
if (userTs !== void 0) ph._ts = userTs;
|
|
2299
|
+
if (item._fromBgChain) ph._fromBgChain = true;
|
|
1940
2300
|
if (item._isBgTask) ph.isBackgroundTask = true;
|
|
1941
2301
|
if (serverItemId !== void 0) {
|
|
1942
2302
|
ph._serverItemId = serverItemId;
|
|
@@ -1945,19 +2305,26 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
1945
2305
|
mapped.push(ph);
|
|
1946
2306
|
} else if (isQueued) ; else if (isErrorResponse) {
|
|
1947
2307
|
var em = { role: "assistant", content: getErrorMessage(response), isError: true };
|
|
2308
|
+
if (item._fromBgChain) em._fromBgChain = true;
|
|
1948
2309
|
if (item._isBgTask) em.isBackgroundTask = true;
|
|
1949
2310
|
if (serverItemId !== void 0) em._serverItemId = serverItemId;
|
|
1950
2311
|
if (replyTs !== void 0) em._ts = replyTs;
|
|
1951
2312
|
mapped.push(em);
|
|
1952
2313
|
} else if (assistantText || reportedComplete) {
|
|
1953
2314
|
var okm = { role: "assistant", content: sanitizeAttachmentLinksForHistory(assistantText, opts.projectId, true) || EMPTY_INDEXING_REPLY };
|
|
2315
|
+
if (item._fromBgChain) okm._fromBgChain = true;
|
|
1954
2316
|
if (item._isBgTask) okm.isBackgroundTask = true;
|
|
2317
|
+
if (isCompact) okm._compact = true;
|
|
1955
2318
|
if (serverItemId !== void 0) okm._serverItemId = serverItemId;
|
|
1956
2319
|
if (replyTs !== void 0) okm._ts = replyTs;
|
|
1957
2320
|
if (reportedComplete) okm._indexComplete = true;
|
|
1958
2321
|
mapped.push(okm);
|
|
1959
2322
|
}
|
|
1960
2323
|
});
|
|
2324
|
+
if (opts.projectId) {
|
|
2325
|
+
var ownerKey = opts.projectId + "#" + platform;
|
|
2326
|
+
for (var oi = 0; oi < mapped.length; oi++) mapped[oi]._ownerKey = ownerKey;
|
|
2327
|
+
}
|
|
1961
2328
|
return { messages: mapped, runningItemIds };
|
|
1962
2329
|
}
|
|
1963
2330
|
|
|
@@ -2075,6 +2442,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2075
2442
|
var INDEXING_DRAIN_CONFIRM_POLL_MS = 3e3;
|
|
2076
2443
|
var INDEXING_DRAIN_IDLE_LOOKS = 2;
|
|
2077
2444
|
var INDEXING_DRAIN_MIN_MS = 8e3;
|
|
2445
|
+
var _bgHistoryBatchSeq = 0;
|
|
2078
2446
|
var INDEXING_DRAIN_TIMEOUT_MS = 15 * 60 * 1e3;
|
|
2079
2447
|
var INDEXING_DRAIN_LOOK_TIMEOUT_MS = 45e3;
|
|
2080
2448
|
var INDEXING_DRAIN_NUDGE_MIN_GAP_MS = 1500;
|
|
@@ -2096,6 +2464,14 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2096
2464
|
}
|
|
2097
2465
|
var ChatSession = class {
|
|
2098
2466
|
constructor(host) {
|
|
2467
|
+
// ─── compact-stub hydration ─────────────────────────────────────────────
|
|
2468
|
+
// Split-fetch bg pages arrive as label stubs (no bodies). When the user
|
|
2469
|
+
// expands a row, the real reply text is fetched per item (csr-poll point
|
|
2470
|
+
// lookup) and MEMOIZED per chat: every later remap (first-page refresh,
|
|
2471
|
+
// queue-detect tick, cache restore) re-applies the memo, so a hydrated
|
|
2472
|
+
// bubble can never silently revert to its 200-char head.
|
|
2473
|
+
this._hydratedBodies = {};
|
|
2474
|
+
this._hydratingItems = {};
|
|
2099
2475
|
this.typewriterQueue = Promise.resolve();
|
|
2100
2476
|
/**
|
|
2101
2477
|
* Pick up indexing passes the WORKER minted, which no client ever dispatched.
|
|
@@ -2136,6 +2512,9 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2136
2512
|
typingAbort: false,
|
|
2137
2513
|
loadingHistory: false,
|
|
2138
2514
|
loadingOlderHistory: false,
|
|
2515
|
+
// A deferred bg stub batch (first-paint split) is still in flight; the
|
|
2516
|
+
// views show a small 'loading indexing history' hint while true.
|
|
2517
|
+
bgHistoryLoading: false,
|
|
2139
2518
|
historyEndOfList: false,
|
|
2140
2519
|
historyStartKeyHistory: [],
|
|
2141
2520
|
historyRequestToken: 0,
|
|
@@ -2285,10 +2664,12 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2285
2664
|
}
|
|
2286
2665
|
var queue = bgIndexingQueueName(id.userId, id.projectId);
|
|
2287
2666
|
var ask = function(status) {
|
|
2288
|
-
return Promise.resolve(
|
|
2289
|
-
{ service: id.projectId, owner: id.owner, platform, queue, status },
|
|
2290
|
-
{
|
|
2291
|
-
)).
|
|
2667
|
+
return Promise.resolve(probeBgQueue(
|
|
2668
|
+
{ service: id.projectId, owner: id.owner, platform, queue, status, limit: WORKER_PASS_ADOPT_LIMIT },
|
|
2669
|
+
{ maxAgeMs: BG_PROBE_TTL_MS }
|
|
2670
|
+
)).then(function(entry) {
|
|
2671
|
+
return entry.result;
|
|
2672
|
+
}).catch(function() {
|
|
2292
2673
|
return null;
|
|
2293
2674
|
});
|
|
2294
2675
|
};
|
|
@@ -2380,7 +2761,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2380
2761
|
* instead of merely unconfirmed.
|
|
2381
2762
|
*/
|
|
2382
2763
|
refreshLiveIndexState() {
|
|
2383
|
-
this._adoptWorkerIndexingPasses(0);
|
|
2764
|
+
this._adoptWorkerIndexingPasses(0, true);
|
|
2384
2765
|
}
|
|
2385
2766
|
/** Forget what we know about which files are indexing — but ONLY when the
|
|
2386
2767
|
* snapshot was taken for a different chat than the one on screen now. For a
|
|
@@ -2557,6 +2938,66 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2557
2938
|
if (!id.projectId || id.platform === "none") return "";
|
|
2558
2939
|
return id.projectId + "#" + id.platform;
|
|
2559
2940
|
}
|
|
2941
|
+
/** Re-apply memoized hydrated texts onto freshly-mapped messages. Both
|
|
2942
|
+
* clients call this right after their mapper runs (loadHistory does it
|
|
2943
|
+
* internally); it mutates the given array's items in place. */
|
|
2944
|
+
applyHydratedBodies(messages) {
|
|
2945
|
+
var key = this.getHistoryCacheKey();
|
|
2946
|
+
var memo = key ? this._hydratedBodies[key] : null;
|
|
2947
|
+
if (!memo) return;
|
|
2948
|
+
var id = this.host.getIdentity();
|
|
2949
|
+
for (var i = 0; i < messages.length; i++) {
|
|
2950
|
+
var m = messages[i];
|
|
2951
|
+
if (!m || !m._compact || m.role !== "assistant" || !m._serverItemId) continue;
|
|
2952
|
+
var text = memo[m._serverItemId];
|
|
2953
|
+
if (typeof text !== "string") continue;
|
|
2954
|
+
m.content = sanitizeAttachmentLinksForHistory(text, id.projectId, true) || EMPTY_INDEXING_REPLY;
|
|
2955
|
+
delete m._compact;
|
|
2956
|
+
}
|
|
2957
|
+
}
|
|
2958
|
+
/** Fetch the real response bodies for compact history stubs (one csr-poll
|
|
2959
|
+
* point lookup per item id), memoize, and swap them into the live list.
|
|
2960
|
+
* Best-effort: a failed lookup leaves the stub (its head + fallback line
|
|
2961
|
+
* still render) and a later expand retries. */
|
|
2962
|
+
hydrateCompactItems(itemIds) {
|
|
2963
|
+
var self = this;
|
|
2964
|
+
var lookup = chatEngineConfig().csrHistoryItemLookup;
|
|
2965
|
+
if (!lookup || !itemIds || !itemIds.length) return Promise.resolve();
|
|
2966
|
+
var id = this.host.getIdentity();
|
|
2967
|
+
var platform = id.platform;
|
|
2968
|
+
if (!id.projectId || platform !== "claude" && platform !== "openai") return Promise.resolve();
|
|
2969
|
+
var chatKey = this.getHistoryCacheKey();
|
|
2970
|
+
if (!chatKey) return Promise.resolve();
|
|
2971
|
+
var jobs = itemIds.map(function(itemId) {
|
|
2972
|
+
if (!itemId) return Promise.resolve();
|
|
2973
|
+
var already = self._hydratedBodies[chatKey] && self._hydratedBodies[chatKey][itemId] !== void 0;
|
|
2974
|
+
var inflightKey = chatKey + "|" + itemId;
|
|
2975
|
+
if (already || self._hydratingItems[inflightKey]) return Promise.resolve();
|
|
2976
|
+
self._hydratingItems[inflightKey] = true;
|
|
2977
|
+
return Promise.resolve(lookup(buildHistoryItemFullId(platform, id.projectId, itemId), id.projectId, id.owner)).then(function(body) {
|
|
2978
|
+
var text = ((platform === "openai" ? extractOpenAIText(body) : extractClaudeText(body)) || "").trim();
|
|
2979
|
+
if (text.indexOf(INDEXING_COMPLETE_MARKER) !== -1) text = text.split(INDEXING_COMPLETE_MARKER).join("").trim();
|
|
2980
|
+
if (!self._hydratedBodies[chatKey]) self._hydratedBodies[chatKey] = {};
|
|
2981
|
+
self._hydratedBodies[chatKey][itemId] = text;
|
|
2982
|
+
if (self.getHistoryCacheKey() !== chatKey) return;
|
|
2983
|
+
for (var i = 0; i < self.state.messages.length; i++) {
|
|
2984
|
+
var m = self.state.messages[i];
|
|
2985
|
+
if (m && m._compact && m.role === "assistant" && m._serverItemId === itemId) {
|
|
2986
|
+
m.content = sanitizeAttachmentLinksForHistory(text, id.projectId, true) || EMPTY_INDEXING_REPLY;
|
|
2987
|
+
delete m._compact;
|
|
2988
|
+
}
|
|
2989
|
+
}
|
|
2990
|
+
}).catch(function() {
|
|
2991
|
+
}).then(function() {
|
|
2992
|
+
delete self._hydratingItems[inflightKey];
|
|
2993
|
+
});
|
|
2994
|
+
});
|
|
2995
|
+
return Promise.all(jobs).then(function() {
|
|
2996
|
+
if (self.getHistoryCacheKey() !== chatKey) return;
|
|
2997
|
+
self.host.notify();
|
|
2998
|
+
self.updateHistoryCache();
|
|
2999
|
+
});
|
|
3000
|
+
}
|
|
2560
3001
|
updateHistoryCache() {
|
|
2561
3002
|
var key = this.getHistoryCacheKey();
|
|
2562
3003
|
if (!key) return;
|
|
@@ -2879,11 +3320,11 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
2879
3320
|
bail = setTimeout(function() {
|
|
2880
3321
|
settle(null);
|
|
2881
3322
|
}, INDEXING_DRAIN_LOOK_TIMEOUT_MS);
|
|
2882
|
-
Promise.resolve(
|
|
2883
|
-
{ service: svcId, owner, platform, queue, status },
|
|
2884
|
-
{
|
|
2885
|
-
)).then(function(
|
|
2886
|
-
settle(
|
|
3323
|
+
Promise.resolve(probeBgQueue(
|
|
3324
|
+
{ service: svcId, owner, platform, queue, status, limit: WORKER_PASS_ADOPT_LIMIT },
|
|
3325
|
+
{ maxAgeMs: 0 }
|
|
3326
|
+
)).then(function(entry) {
|
|
3327
|
+
settle(entry.result);
|
|
2887
3328
|
}, function() {
|
|
2888
3329
|
settle(null);
|
|
2889
3330
|
});
|
|
@@ -3510,6 +3951,32 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
3510
3951
|
if (e && e.id && self._indexKeyOf(e) === scoped) stoppedIds[e.id] = true;
|
|
3511
3952
|
});
|
|
3512
3953
|
this.state.stoppedIndexIds = stoppedIds;
|
|
3954
|
+
var runPath = group.path || "";
|
|
3955
|
+
if (!runPath) {
|
|
3956
|
+
(group.members || []).some(function(m) {
|
|
3957
|
+
var p = m && m.msg && m.msg._indexFile && m.msg._indexFile.path;
|
|
3958
|
+
if (p) {
|
|
3959
|
+
runPath = p;
|
|
3960
|
+
return true;
|
|
3961
|
+
}
|
|
3962
|
+
return false;
|
|
3963
|
+
});
|
|
3964
|
+
}
|
|
3965
|
+
if (!runPath) {
|
|
3966
|
+
this.bgTaskQueue.some(function(e) {
|
|
3967
|
+
if (e && e.storagePath && self._indexKeyOf(e) === scoped) {
|
|
3968
|
+
runPath = e.storagePath;
|
|
3969
|
+
return true;
|
|
3970
|
+
}
|
|
3971
|
+
return false;
|
|
3972
|
+
});
|
|
3973
|
+
}
|
|
3974
|
+
if (runPath) {
|
|
3975
|
+
var ident = this.host.getIdentity();
|
|
3976
|
+
if (ident && ident.projectId) {
|
|
3977
|
+
upsertIndexRunRecordSafe(ident.projectId, runPath, { status: "cancelled", finished: Date.now() });
|
|
3978
|
+
}
|
|
3979
|
+
}
|
|
3513
3980
|
}
|
|
3514
3981
|
this._adoptWorkerIndexingPasses(0);
|
|
3515
3982
|
var ids = group.cancellableIds || [];
|
|
@@ -4023,7 +4490,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4023
4490
|
if (isImageVisionFile(filename, mime)) return true;
|
|
4024
4491
|
return windowedIndexingEnabled() && isWindowedReadFile(filename, mime);
|
|
4025
4492
|
}
|
|
4026
|
-
_adoptWorkerIndexingPasses(attempt) {
|
|
4493
|
+
_adoptWorkerIndexingPasses(attempt, passive) {
|
|
4027
4494
|
var self = this;
|
|
4028
4495
|
if (this._adoptingWorkerPasses) return;
|
|
4029
4496
|
var id = this.host.getIdentity();
|
|
@@ -4033,10 +4500,12 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4033
4500
|
var svcId = id.projectId, owner = id.owner;
|
|
4034
4501
|
var queue = bgIndexingQueueName(id.userId, id.projectId);
|
|
4035
4502
|
var ask = function(status) {
|
|
4036
|
-
return Promise.resolve(
|
|
4037
|
-
{ service: svcId, owner, platform, queue, status },
|
|
4038
|
-
{
|
|
4039
|
-
)).
|
|
4503
|
+
return Promise.resolve(probeBgQueue(
|
|
4504
|
+
{ service: svcId, owner, platform, queue, status, limit: WORKER_PASS_ADOPT_LIMIT },
|
|
4505
|
+
{ maxAgeMs: 0 }
|
|
4506
|
+
)).then(function(entry) {
|
|
4507
|
+
return entry.result;
|
|
4508
|
+
}).catch(function() {
|
|
4040
4509
|
return null;
|
|
4041
4510
|
});
|
|
4042
4511
|
};
|
|
@@ -4058,6 +4527,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4058
4527
|
self.drainBgTaskQueue();
|
|
4059
4528
|
if (self._isTrackingAny(adoptedIds)) return;
|
|
4060
4529
|
}
|
|
4530
|
+
if (passive && !self._hasLiveIndexEvidence(svcId)) return;
|
|
4061
4531
|
if (attempt + 1 >= WORKER_PASS_ADOPT_ATTEMPTS.length) {
|
|
4062
4532
|
self._nudgeIndexingDrain();
|
|
4063
4533
|
return;
|
|
@@ -4066,12 +4536,30 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4066
4536
|
var later = self.host.getIdentity();
|
|
4067
4537
|
if (later.projectId !== svcId || later.platform !== platform) return;
|
|
4068
4538
|
if (self.isPollingPaused() || !self.host.isViewMounted()) return;
|
|
4069
|
-
self._adoptWorkerIndexingPasses(attempt + 1);
|
|
4539
|
+
self._adoptWorkerIndexingPasses(attempt + 1, passive);
|
|
4070
4540
|
}, WORKER_PASS_ADOPT_ATTEMPTS[attempt + 1]);
|
|
4071
4541
|
}, function() {
|
|
4072
4542
|
self._adoptingWorkerPasses = false;
|
|
4073
4543
|
});
|
|
4074
4544
|
}
|
|
4545
|
+
/** Anything at all suggesting THIS project's indexing may be live: a queued
|
|
4546
|
+
* local entry, a recorded live key (the adopt look just wrote them), or an
|
|
4547
|
+
* attached poll. Gates the passive adopt ladder's climb. */
|
|
4548
|
+
_hasLiveIndexEvidence(svcId) {
|
|
4549
|
+
for (var i = 0; i < this.bgTaskQueue.length; i++) {
|
|
4550
|
+
var e = this.bgTaskQueue[i];
|
|
4551
|
+
if (e && e.projectId === svcId) return true;
|
|
4552
|
+
}
|
|
4553
|
+
var keys = this.state.liveIndexKeys || {};
|
|
4554
|
+
for (var k in keys) {
|
|
4555
|
+
if (keys[k]) return true;
|
|
4556
|
+
}
|
|
4557
|
+
var found = false;
|
|
4558
|
+
this.historyItemPolls.forEach(function(h) {
|
|
4559
|
+
if (h && h.kind === "bg") found = true;
|
|
4560
|
+
});
|
|
4561
|
+
return found;
|
|
4562
|
+
}
|
|
4075
4563
|
/** Any of these ids still queued or still polled, i.e. surviving work. */
|
|
4076
4564
|
_isTrackingAny(ids) {
|
|
4077
4565
|
for (var i = 0; i < ids.length; i++) {
|
|
@@ -4162,7 +4650,10 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4162
4650
|
for (var i = this.bgTaskQueue.length - 1; i >= 0; i--) {
|
|
4163
4651
|
var e = this.bgTaskQueue[i];
|
|
4164
4652
|
if (e.projectId !== svcId || e.platform !== plat) continue;
|
|
4165
|
-
if (presentIds[e.id] && !pendingIds[e.id])
|
|
4653
|
+
if (presentIds[e.id] && !pendingIds[e.id]) {
|
|
4654
|
+
this._flipRunFromSettledEntry(e);
|
|
4655
|
+
this.bgTaskQueue.splice(i, 1);
|
|
4656
|
+
}
|
|
4166
4657
|
}
|
|
4167
4658
|
var bgPollBudget = MAX_CONCURRENT_BG_POLLS - this._countBgPolls();
|
|
4168
4659
|
var injectedAny = false;
|
|
@@ -4236,6 +4727,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4236
4727
|
self.host.notify();
|
|
4237
4728
|
self.updateHistoryCache();
|
|
4238
4729
|
if (!self._isWorkerDrivenIndexing(capturedEntry.filename, capturedEntry.mime)) {
|
|
4730
|
+
if (isNotExists) self._flipRunRecord(capturedEntry, "cancelled");
|
|
4731
|
+
else self._flipRunRecord(capturedEntry, "error", self._runErrorText(err));
|
|
4239
4732
|
self._nudgeIndexingDrain();
|
|
4240
4733
|
}
|
|
4241
4734
|
}).then(function() {
|
|
@@ -4267,6 +4760,74 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4267
4760
|
// memory (a reload or a closed tab ended it), and it stopped whenever the model claimed
|
|
4268
4761
|
// completion, which on an 88-page file happened at page 15. Continuing to dispatch here
|
|
4269
4762
|
// as well would now double-index every window.
|
|
4763
|
+
/** Fire the consumer's done::-marker hook for a run whose completion this
|
|
4764
|
+
* client knows DETERMINISTICALLY (see the two call sites in
|
|
4765
|
+
* maybeResumeIndexing). Best-effort by contract; identity-checked so a
|
|
4766
|
+
* project switch mid-settle cannot stamp the wrong service. */
|
|
4767
|
+
_mintDoneMarker(entry) {
|
|
4768
|
+
try {
|
|
4769
|
+
var mint = chatEngineConfig().mintIndexDoneMarker;
|
|
4770
|
+
if (!mint || !entry || !entry.storagePath || !entry.projectId) return;
|
|
4771
|
+
var id = this.host.getIdentity();
|
|
4772
|
+
if (!id || id.projectId !== entry.projectId) return;
|
|
4773
|
+
mint({ service: entry.projectId, storagePath: entry.storagePath });
|
|
4774
|
+
} catch (_e) {
|
|
4775
|
+
}
|
|
4776
|
+
}
|
|
4777
|
+
/** Short, storable form of an error body for the run:: record. */
|
|
4778
|
+
_runErrorText(response) {
|
|
4779
|
+
var msg = "";
|
|
4780
|
+
try {
|
|
4781
|
+
msg = String(getErrorMessage(response) || "");
|
|
4782
|
+
} catch (_e) {
|
|
4783
|
+
}
|
|
4784
|
+
msg = msg.replace(/\s+/g, " ").trim();
|
|
4785
|
+
return msg ? msg.slice(0, 300) : "Indexing failed.";
|
|
4786
|
+
}
|
|
4787
|
+
/** Close the records of a run whose pass settled OFF-POLL — the answer came
|
|
4788
|
+
* back as history (hidden tab, dead poll, resume refetch), so none of the
|
|
4789
|
+
* poll-side settle handlers ran. Only for SINGLE-PASS files, where one
|
|
4790
|
+
* settled pass is deterministically the whole run (the same contract as
|
|
4791
|
+
* maybeResumeIndexing's single-pass branch); paged files stay with their
|
|
4792
|
+
* drivers. Outcome is read from the settled bubbles' own flags, which is
|
|
4793
|
+
* all the history mapping left us. Best-effort and idempotent throughout. */
|
|
4794
|
+
_flipRunFromSettledEntry(entry) {
|
|
4795
|
+
try {
|
|
4796
|
+
if (!entry || !entry.storagePath || !entry.id || !entry.projectId) return;
|
|
4797
|
+
if (isPagedReadFile(entry.filename, entry.mime)) return;
|
|
4798
|
+
if (this.cancelledIndexKeys.has(this._indexKeyOf(entry))) return;
|
|
4799
|
+
if (this.state.stoppedIndexIds[entry.id]) return;
|
|
4800
|
+
var userMsg = null, replyMsg = null;
|
|
4801
|
+
this.state.messages.forEach(function(m) {
|
|
4802
|
+
if (m._serverItemId !== entry.id) return;
|
|
4803
|
+
if (m.role === "user") {
|
|
4804
|
+
if (!userMsg) userMsg = m;
|
|
4805
|
+
} else if (!replyMsg) replyMsg = m;
|
|
4806
|
+
});
|
|
4807
|
+
if (userMsg && userMsg.isCancelled || replyMsg && replyMsg.isCancelled) {
|
|
4808
|
+
this._flipRunRecord(entry, "cancelled");
|
|
4809
|
+
} else if (replyMsg && replyMsg.isError) {
|
|
4810
|
+
var errText = typeof replyMsg.content === "string" ? replyMsg.content.replace(/\s+/g, " ").trim().slice(0, 300) : "";
|
|
4811
|
+
this._flipRunRecord(entry, "error", errText || "Indexing failed.");
|
|
4812
|
+
} else if (replyMsg) {
|
|
4813
|
+
this._mintDoneMarker(entry);
|
|
4814
|
+
this._flipRunRecord(entry, "done");
|
|
4815
|
+
}
|
|
4816
|
+
} catch (_e) {
|
|
4817
|
+
}
|
|
4818
|
+
}
|
|
4819
|
+
/** Close the durable run:: record for an ending THIS client observed.
|
|
4820
|
+
* service comes from the ENTRY, not the current identity: unlike the done::
|
|
4821
|
+
* mint above, a status flip must land even if the user switched projects
|
|
4822
|
+
* mid-settle — otherwise the record lies 'working' forever. Best-effort
|
|
4823
|
+
* through upsertIndexRunRecordSafe; the consumer's precedence guard keeps
|
|
4824
|
+
* repeats and races harmless. */
|
|
4825
|
+
_flipRunRecord(entry, status, error) {
|
|
4826
|
+
if (!entry || !entry.storagePath || !entry.projectId) return;
|
|
4827
|
+
var patch = { status, finished: Date.now() };
|
|
4828
|
+
if (error) patch.error = error;
|
|
4829
|
+
upsertIndexRunRecordSafe(entry.projectId, entry.storagePath, patch);
|
|
4830
|
+
}
|
|
4270
4831
|
maybeResumeIndexing(entry, response, platform) {
|
|
4271
4832
|
var self = this;
|
|
4272
4833
|
var endOfClientChain = function() {
|
|
@@ -4276,27 +4837,43 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4276
4837
|
if (!entry || !entry.storagePath) return;
|
|
4277
4838
|
if (this.cancelledIndexKeys.has(this._indexKeyOf(entry))) return;
|
|
4278
4839
|
if (!isPagedReadFile(entry.filename, entry.mime)) {
|
|
4840
|
+
if (!isErrorResponseBody(response) && !this._isCancelledPollResult(response)) {
|
|
4841
|
+
this._mintDoneMarker(entry);
|
|
4842
|
+
this._flipRunRecord(entry, "done");
|
|
4843
|
+
} else if (this._isCancelledPollResult(response)) {
|
|
4844
|
+
this._flipRunRecord(entry, "cancelled");
|
|
4845
|
+
} else {
|
|
4846
|
+
this._flipRunRecord(entry, "error", this._runErrorText(response));
|
|
4847
|
+
}
|
|
4279
4848
|
endOfClientChain();
|
|
4280
4849
|
return;
|
|
4281
4850
|
}
|
|
4282
4851
|
if (isImageVisionFile(entry.filename, entry.mime)) return;
|
|
4283
4852
|
if (windowedIndexingEnabled() && isWindowedReadFile(entry.filename, entry.mime)) return;
|
|
4284
4853
|
if (isErrorResponseBody(response)) {
|
|
4854
|
+
this._flipRunRecord(entry, "error", this._runErrorText(response));
|
|
4285
4855
|
endOfClientChain();
|
|
4286
4856
|
return;
|
|
4287
4857
|
}
|
|
4288
4858
|
var answer = (platform === "openai" ? extractOpenAIText(response) : extractClaudeText(response)) || "";
|
|
4289
4859
|
if (answer.indexOf(INDEXING_COMPLETE_MARKER) !== -1) {
|
|
4860
|
+
this._mintDoneMarker(entry);
|
|
4861
|
+
this._flipRunRecord(entry, "done");
|
|
4290
4862
|
endOfClientChain();
|
|
4291
4863
|
return;
|
|
4292
4864
|
}
|
|
4293
4865
|
var pass = (entry.resumePass || 0) + 1;
|
|
4294
4866
|
if (pass > MAX_INDEXING_RESUME_PASSES) {
|
|
4867
|
+
this._flipRunRecord(entry, "error", "Stopped after " + MAX_INDEXING_RESUME_PASSES + " passes without finishing.");
|
|
4295
4868
|
endOfClientChain();
|
|
4296
4869
|
return;
|
|
4297
4870
|
}
|
|
4298
4871
|
var id = this.host.getIdentity();
|
|
4299
|
-
if (!id || id.platform === "none" || id.projectId !== entry.projectId)
|
|
4872
|
+
if (!id || id.platform === "none" || id.projectId !== entry.projectId) {
|
|
4873
|
+
this._flipRunRecord(entry, "error", "Indexing stopped: the session or project changed before the file finished.");
|
|
4874
|
+
endOfClientChain();
|
|
4875
|
+
return;
|
|
4876
|
+
}
|
|
4300
4877
|
this.trackIndexDispatch(notifyAgentContinueIndexing({
|
|
4301
4878
|
platform: id.platform,
|
|
4302
4879
|
model: id.model,
|
|
@@ -4373,8 +4950,9 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4373
4950
|
var projectId = id.projectId, owner = id.owner;
|
|
4374
4951
|
var options = { fetchMore };
|
|
4375
4952
|
if (fetchMore && this.state.historyStartKeyHistory.length) options.startKeyHistory = this.state.historyStartKeyHistory.slice();
|
|
4953
|
+
if (!fetchMore) options.deferBg = true;
|
|
4376
4954
|
var fetchHistory = function() {
|
|
4377
|
-
return
|
|
4955
|
+
return getSplitChatHistory({ service: projectId, owner, platform, userId: id.userId }, options);
|
|
4378
4956
|
};
|
|
4379
4957
|
return Promise.resolve().then(fetchHistory).catch(function(err) {
|
|
4380
4958
|
if (isAuthExpiredError(err) && !isNonRetryableRequestError(err)) return self.host.refreshSession().then(fetchHistory);
|
|
@@ -4384,7 +4962,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4384
4962
|
var chatList = history && Array.isArray(history.list) ? history.list : [];
|
|
4385
4963
|
chatList.forEach(function(item) {
|
|
4386
4964
|
if (isBgIndexingQueue(item.queue_name)) {
|
|
4387
|
-
|
|
4965
|
+
var clsText = item.compact ? item.request_text : extractLastUserTextFromRequest(item.request_body);
|
|
4966
|
+
if (isIndexingRequestText(clsText)) item._isBgTask = true;
|
|
4388
4967
|
else item._isOnBgQueue = true;
|
|
4389
4968
|
}
|
|
4390
4969
|
});
|
|
@@ -4397,15 +4976,55 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4397
4976
|
projectId: id.projectId,
|
|
4398
4977
|
formatIndexingLabel: self.host.formatIndexingLabel
|
|
4399
4978
|
}).messages;
|
|
4979
|
+
self.applyHydratedBodies(mapped);
|
|
4400
4980
|
var keptOlderPages = false;
|
|
4981
|
+
var keptScreenAwaitingBg = false;
|
|
4401
4982
|
if (fetchMore) {
|
|
4402
|
-
|
|
4983
|
+
var incomingKeys = {};
|
|
4984
|
+
mapped.forEach(function(m) {
|
|
4985
|
+
if (m._serverItemId) incomingKeys[m._serverItemId + "|" + m.role] = m;
|
|
4986
|
+
});
|
|
4987
|
+
var existing = self.state.messages.filter(function(m) {
|
|
4988
|
+
if (!m._serverItemId) return true;
|
|
4989
|
+
var inc = incomingKeys[m._serverItemId + "|" + m.role];
|
|
4990
|
+
if (!inc) return true;
|
|
4991
|
+
if (m._cancelling) inc._cancelling = m._cancelling;
|
|
4992
|
+
if (m._cancelError) inc._cancelError = m._cancelError;
|
|
4993
|
+
return false;
|
|
4994
|
+
});
|
|
4995
|
+
var mergedList = [];
|
|
4996
|
+
var pi = 0, ei = 0;
|
|
4997
|
+
while (pi < mapped.length && ei < existing.length) {
|
|
4998
|
+
var pm = mapped[pi], em = existing[ei];
|
|
4999
|
+
var eid = em._serverItemId;
|
|
5000
|
+
if (typeof eid !== "string") break;
|
|
5001
|
+
var pid = pm._serverItemId;
|
|
5002
|
+
if (typeof pid !== "string" || pid <= eid) {
|
|
5003
|
+
mergedList.push(pm);
|
|
5004
|
+
pi++;
|
|
5005
|
+
} else {
|
|
5006
|
+
mergedList.push(em);
|
|
5007
|
+
ei++;
|
|
5008
|
+
}
|
|
5009
|
+
}
|
|
5010
|
+
while (pi < mapped.length) mergedList.push(mapped[pi++]);
|
|
5011
|
+
while (ei < existing.length) mergedList.push(existing[ei++]);
|
|
5012
|
+
self.state.messages = mergedList;
|
|
5013
|
+
} else if (!mapped.length && history && (history.endOfList === false || history.bgPending) && self.state.messages.some(function(m) {
|
|
5014
|
+
return m._ownerKey === void 0 || m._ownerKey === loadKey;
|
|
5015
|
+
})) {
|
|
5016
|
+
if (history.endOfList !== false) keptScreenAwaitingBg = true;
|
|
4403
5017
|
} else {
|
|
4404
5018
|
if (self.state.typing) self.state.typingAbort = true;
|
|
4405
5019
|
var serverIds = {};
|
|
4406
5020
|
mapped.forEach(function(m) {
|
|
4407
5021
|
if (m._serverItemId) serverIds[m._serverItemId] = 1;
|
|
4408
5022
|
});
|
|
5023
|
+
var surfaceOldestId = void 0;
|
|
5024
|
+
mapped.forEach(function(m) {
|
|
5025
|
+
if (typeof m._serverItemId !== "string" || m._fromBgChain) return;
|
|
5026
|
+
if (surfaceOldestId === void 0 || m._serverItemId < surfaceOldestId) surfaceOldestId = m._serverItemId;
|
|
5027
|
+
});
|
|
4409
5028
|
var locallyCancelled = {};
|
|
4410
5029
|
self.state.messages.forEach(function(m) {
|
|
4411
5030
|
if (m.isCancelled && m._serverItemId) locallyCancelled[m._serverItemId] = m;
|
|
@@ -4446,13 +5065,45 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4446
5065
|
var sharesPage1 = self.state.messages.some(function(m) {
|
|
4447
5066
|
return typeof m._serverItemId === "string" && !!serverIds[m._serverItemId];
|
|
4448
5067
|
});
|
|
4449
|
-
var
|
|
5068
|
+
var deferredBg = !!(history && history.bgPending);
|
|
5069
|
+
var retainBoundary = surfaceOldestId !== void 0 ? surfaceOldestId : oldestInPage1;
|
|
5070
|
+
var retainedOlder = !sharesPage1 || retainBoundary === void 0 ? [] : self.state.messages.filter(function(m) {
|
|
4450
5071
|
if (typeof m._serverItemId !== "string") return false;
|
|
4451
5072
|
if (m._ownerKey !== void 0 && m._ownerKey !== loadKey) return false;
|
|
4452
|
-
|
|
5073
|
+
if (deferredBg && m.isBackgroundTask) return true;
|
|
5074
|
+
if (m._fromBgChain) return true;
|
|
5075
|
+
return m._serverItemId < retainBoundary;
|
|
5076
|
+
});
|
|
5077
|
+
var prependOlder = [];
|
|
5078
|
+
var interleave = [];
|
|
5079
|
+
retainedOlder.forEach(function(m) {
|
|
5080
|
+
var sid = m._serverItemId;
|
|
5081
|
+
if (serverIds[sid]) return;
|
|
5082
|
+
if (retainBoundary !== void 0 && sid < retainBoundary) prependOlder.push(m);
|
|
5083
|
+
else interleave.push(m);
|
|
4453
5084
|
});
|
|
4454
|
-
|
|
4455
|
-
|
|
5085
|
+
var page1 = mapped;
|
|
5086
|
+
if (interleave.length) {
|
|
5087
|
+
var mergedP = [];
|
|
5088
|
+
var ii2 = 0, mi2 = 0;
|
|
5089
|
+
while (ii2 < interleave.length && mi2 < mapped.length) {
|
|
5090
|
+
var iv = interleave[ii2], mv = mapped[mi2];
|
|
5091
|
+
var mid2 = typeof mv._serverItemId === "string" ? mv._serverItemId : void 0;
|
|
5092
|
+
if (mid2 === void 0) break;
|
|
5093
|
+
if (iv._serverItemId <= mid2) {
|
|
5094
|
+
mergedP.push(iv);
|
|
5095
|
+
ii2++;
|
|
5096
|
+
} else {
|
|
5097
|
+
mergedP.push(mv);
|
|
5098
|
+
mi2++;
|
|
5099
|
+
}
|
|
5100
|
+
}
|
|
5101
|
+
while (ii2 < interleave.length) mergedP.push(interleave[ii2++]);
|
|
5102
|
+
while (mi2 < mapped.length) mergedP.push(mapped[mi2++]);
|
|
5103
|
+
page1 = mergedP;
|
|
5104
|
+
}
|
|
5105
|
+
keptOlderPages = prependOlder.length > 0 || interleave.length > 0;
|
|
5106
|
+
self.state.messages = prependOlder.length ? prependOlder.concat(page1) : page1;
|
|
4456
5107
|
rescued.forEach(function(m) {
|
|
4457
5108
|
self.state.messages.push(m);
|
|
4458
5109
|
});
|
|
@@ -4488,9 +5139,14 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4488
5139
|
self.state.historyEndOfList = !!(history && history.endOfList);
|
|
4489
5140
|
self.state.historyStartKeyHistory = history && Array.isArray(history.startKeyHistory) ? history.startKeyHistory : [];
|
|
4490
5141
|
var clearedAt = self.host.getClearedAt();
|
|
4491
|
-
if (clearedAt
|
|
4492
|
-
var
|
|
4493
|
-
|
|
5142
|
+
if (clearedAt) {
|
|
5143
|
+
var surfaceItems = chatList.filter(function(it) {
|
|
5144
|
+
return !(it && it._fromBgChain);
|
|
5145
|
+
});
|
|
5146
|
+
if (surfaceItems.length > 0) {
|
|
5147
|
+
var oldestUpdated = Number(surfaceItems[surfaceItems.length - 1] && surfaceItems[surfaceItems.length - 1].updated);
|
|
5148
|
+
if (isFinite(oldestUpdated) && oldestUpdated <= clearedAt) self.state.historyEndOfList = true;
|
|
5149
|
+
}
|
|
4494
5150
|
}
|
|
4495
5151
|
}
|
|
4496
5152
|
if (self.state.historyRequestToken === token) {
|
|
@@ -4499,6 +5155,85 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4499
5155
|
}
|
|
4500
5156
|
self.updateHistoryCache();
|
|
4501
5157
|
self.host.notify();
|
|
5158
|
+
var bgPending = !fetchMore && history && history.bgPending;
|
|
5159
|
+
if (bgPending) {
|
|
5160
|
+
var batchId = ++_bgHistoryBatchSeq;
|
|
5161
|
+
if (history.endOfList !== true && history.firstLoad === true) {
|
|
5162
|
+
self.state.bgHistoryLoading = true;
|
|
5163
|
+
self.host.notify();
|
|
5164
|
+
}
|
|
5165
|
+
var releaseBgFlag = function() {
|
|
5166
|
+
if (_bgHistoryBatchSeq === batchId) self.state.bgHistoryLoading = false;
|
|
5167
|
+
};
|
|
5168
|
+
bgPending.then(function(batch) {
|
|
5169
|
+
if (token !== self.state.gateRefreshToken) {
|
|
5170
|
+
releaseBgFlag();
|
|
5171
|
+
return;
|
|
5172
|
+
}
|
|
5173
|
+
var bList = batch && Array.isArray(batch.list) ? batch.list : [];
|
|
5174
|
+
bList.forEach(function(item) {
|
|
5175
|
+
if (isBgIndexingQueue(item.queue_name)) {
|
|
5176
|
+
var t = item.compact ? item.request_text : extractLastUserTextFromRequest(item.request_body);
|
|
5177
|
+
if (isIndexingRequestText(t)) item._isBgTask = true;
|
|
5178
|
+
else item._isOnBgQueue = true;
|
|
5179
|
+
}
|
|
5180
|
+
});
|
|
5181
|
+
var sorted = bList.sort(function(a, b) {
|
|
5182
|
+
var ai = typeof a.id === "string" ? a.id : "", bi = typeof b.id === "string" ? b.id : "";
|
|
5183
|
+
return ai > bi ? -1 : ai < bi ? 1 : 0;
|
|
5184
|
+
});
|
|
5185
|
+
var m2 = mapHistoryListToMessages(sorted, platform, {
|
|
5186
|
+
clearedAt: self.host.getClearedAt(),
|
|
5187
|
+
projectId: id.projectId,
|
|
5188
|
+
formatIndexingLabel: self.host.formatIndexingLabel
|
|
5189
|
+
}).messages;
|
|
5190
|
+
self.applyHydratedBodies(m2);
|
|
5191
|
+
if (keptScreenAwaitingBg && !m2.length && batch && batch.endOfList === true) {
|
|
5192
|
+
self.state.messages = self.state.messages.filter(function(m) {
|
|
5193
|
+
if (typeof m._serverItemId !== "string") return true;
|
|
5194
|
+
if (m._ownerKey !== void 0 && m._ownerKey !== loadKey) return true;
|
|
5195
|
+
return false;
|
|
5196
|
+
});
|
|
5197
|
+
self.state.historyEndOfList = true;
|
|
5198
|
+
releaseBgFlag();
|
|
5199
|
+
self.updateHistoryCache();
|
|
5200
|
+
self.host.notify();
|
|
5201
|
+
return;
|
|
5202
|
+
}
|
|
5203
|
+
var incoming = {};
|
|
5204
|
+
m2.forEach(function(m) {
|
|
5205
|
+
if (m._serverItemId) incoming[m._serverItemId + "|" + m.role] = true;
|
|
5206
|
+
});
|
|
5207
|
+
var baseList = self.state.messages.filter(function(m) {
|
|
5208
|
+
return !(m._serverItemId && incoming[m._serverItemId + "|" + m.role]);
|
|
5209
|
+
});
|
|
5210
|
+
var mergedList2 = [];
|
|
5211
|
+
var pi2 = 0, ei2 = 0;
|
|
5212
|
+
while (pi2 < m2.length && ei2 < baseList.length) {
|
|
5213
|
+
var pm2 = m2[pi2], em2 = baseList[ei2];
|
|
5214
|
+
var eid2 = em2._serverItemId;
|
|
5215
|
+
if (typeof eid2 !== "string") break;
|
|
5216
|
+
var pid2 = pm2._serverItemId;
|
|
5217
|
+
if (typeof pid2 !== "string" || pid2 <= eid2) {
|
|
5218
|
+
mergedList2.push(pm2);
|
|
5219
|
+
pi2++;
|
|
5220
|
+
} else {
|
|
5221
|
+
mergedList2.push(em2);
|
|
5222
|
+
ei2++;
|
|
5223
|
+
}
|
|
5224
|
+
}
|
|
5225
|
+
while (pi2 < m2.length) mergedList2.push(m2[pi2++]);
|
|
5226
|
+
while (ei2 < baseList.length) mergedList2.push(baseList[ei2++]);
|
|
5227
|
+
self.state.messages = mergedList2;
|
|
5228
|
+
if (batch && batch.endOfList === true) self.state.historyEndOfList = true;
|
|
5229
|
+
releaseBgFlag();
|
|
5230
|
+
self.updateHistoryCache();
|
|
5231
|
+
self.host.notify();
|
|
5232
|
+
}, function() {
|
|
5233
|
+
releaseBgFlag();
|
|
5234
|
+
self.host.notify();
|
|
5235
|
+
});
|
|
5236
|
+
}
|
|
4502
5237
|
if (!fetchMore) {
|
|
4503
5238
|
var bgAllow = {};
|
|
4504
5239
|
var bgHistBudget = MAX_CONCURRENT_BG_POLLS - self._countBgPolls();
|
|
@@ -4595,7 +5330,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4595
5330
|
var self = this;
|
|
4596
5331
|
var id = this.host.getIdentity();
|
|
4597
5332
|
att.status = "uploading";
|
|
4598
|
-
att.progress =
|
|
5333
|
+
att.progress = null;
|
|
4599
5334
|
att.errorMessage = "";
|
|
4600
5335
|
att.errorCode = "";
|
|
4601
5336
|
att.errorDetail = "";
|
|
@@ -4826,6 +5561,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4826
5561
|
};
|
|
4827
5562
|
|
|
4828
5563
|
// src/engine/indexing_groups.ts
|
|
5564
|
+
var RUN_RECORD_WORKING_STALE_MS = 6 * 60 * 60 * 1e3;
|
|
4829
5565
|
var INDEXING_LABEL_RE = /^(Re)?[Ii]ndexing(\s*\(continuing\))?\s*:?\s+(.+)$/;
|
|
4830
5566
|
var LEADING_MD_LINK_RE = /^\[([^\]]+)\]\(([^)]+)\)/;
|
|
4831
5567
|
function parseIndexingLabel(content) {
|
|
@@ -4880,10 +5616,12 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
4880
5616
|
var list = Array.isArray(messages) ? messages : [];
|
|
4881
5617
|
var liveIndexKeys = opts && opts.liveIndexKeys || {};
|
|
4882
5618
|
var liveIndexChecked = !!(opts && opts.liveIndexChecked);
|
|
5619
|
+
var doneKeys = opts && opts.doneKeys || {};
|
|
4883
5620
|
var stoppedIndexIds = opts && opts.stoppedIndexIds || {};
|
|
4884
5621
|
var windowedIndexing = opts && opts.windowedIndexing !== void 0 ? !!opts.windowedIndexing : windowedIndexingEnabled();
|
|
4885
5622
|
var hasMoreHistory = !!(opts && opts.hasMoreHistory);
|
|
4886
5623
|
var loadingOlderHistory = !!(opts && opts.loadingOlderHistory);
|
|
5624
|
+
var stubPlatform = opts && opts.stubPlatform;
|
|
4887
5625
|
var groups = {};
|
|
4888
5626
|
var order = [];
|
|
4889
5627
|
var runOfIndex = new Array(list.length);
|
|
@@ -5056,11 +5794,11 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
5056
5794
|
} else if (grp.driver === "client") {
|
|
5057
5795
|
grp.finished = sawComplete || grp.status === "error" || grp.passCount >= MAX_INDEXING_RESUME_PASSES;
|
|
5058
5796
|
} else {
|
|
5059
|
-
grp.finished = !newestRunOfKey[order[oi]] || liveIndexChecked && !liveIndexKeys[grp.key];
|
|
5797
|
+
grp.finished = !newestRunOfKey[order[oi]] || !!doneKeys[grp.key] && !liveIndexKeys[grp.key] || liveIndexChecked && !liveIndexKeys[grp.key];
|
|
5060
5798
|
}
|
|
5061
5799
|
if (grp.status !== "done") {
|
|
5062
5800
|
grp.resolving = false;
|
|
5063
|
-
} else if (grp.mayHaveOlder && loadingOlderHistory && !liveIndexKeys[grp.key] && newestRunOfKey[order[oi]]) {
|
|
5801
|
+
} else if (grp.mayHaveOlder && loadingOlderHistory && !liveIndexKeys[grp.key] && !doneKeys[grp.key] && newestRunOfKey[order[oi]]) {
|
|
5064
5802
|
grp.resolving = true;
|
|
5065
5803
|
grp.resolvingReason = "history";
|
|
5066
5804
|
} else if (!grp.finished && grp.driver === "worker" && !liveIndexChecked && !liveIndexKeys[grp.key]) {
|
|
@@ -5070,14 +5808,126 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
5070
5808
|
grp.resolving = false;
|
|
5071
5809
|
}
|
|
5072
5810
|
}
|
|
5811
|
+
var stubList = [];
|
|
5812
|
+
var runStubs = opts && opts.runStubs;
|
|
5813
|
+
if (runStubs) {
|
|
5814
|
+
var coveredPaths = {};
|
|
5815
|
+
var coveredPathlessNames = {};
|
|
5816
|
+
for (var ci = 0; ci < order.length; ci++) {
|
|
5817
|
+
var cg = groups[order[ci]];
|
|
5818
|
+
if (cg.path) {
|
|
5819
|
+
coveredPaths[cg.path] = true;
|
|
5820
|
+
if (cg.key) coveredPaths[cg.key] = true;
|
|
5821
|
+
} else if (cg.name) coveredPathlessNames[cg.name] = true;
|
|
5822
|
+
else if (cg.key) coveredPaths[cg.key] = true;
|
|
5823
|
+
}
|
|
5824
|
+
var now = opts && typeof opts.now === "number" ? opts.now : Date.now();
|
|
5825
|
+
var stubClearedAt = opts && typeof opts.stubClearedAt === "number" && opts.stubClearedAt > 0 ? opts.stubClearedAt : 0;
|
|
5826
|
+
for (var sp in runStubs) {
|
|
5827
|
+
var rec = runStubs[sp];
|
|
5828
|
+
if (!sp || !rec || !rec.status || coveredPaths[sp]) continue;
|
|
5829
|
+
var fname = rec.filename || sp.split("/").pop() || sp;
|
|
5830
|
+
if (coveredPathlessNames[fname]) continue;
|
|
5831
|
+
if (stubPlatform && rec.platform && rec.platform !== stubPlatform) continue;
|
|
5832
|
+
var live = !!liveIndexKeys[sp] || !!liveIndexKeys[fname];
|
|
5833
|
+
var recWhen = typeof rec.finished === "number" ? rec.finished : typeof rec.started === "number" ? rec.started : void 0;
|
|
5834
|
+
if (stubClearedAt && !live && recWhen !== void 0 && recWhen <= stubClearedAt) continue;
|
|
5835
|
+
var st = "active";
|
|
5836
|
+
var fin = false;
|
|
5837
|
+
var res = false;
|
|
5838
|
+
var reason;
|
|
5839
|
+
if (!live) {
|
|
5840
|
+
if (rec.status === "done" || doneKeys[sp] || doneKeys[fname]) {
|
|
5841
|
+
st = "done";
|
|
5842
|
+
fin = true;
|
|
5843
|
+
} else if (rec.status === "error") {
|
|
5844
|
+
st = "error";
|
|
5845
|
+
fin = true;
|
|
5846
|
+
} else if (rec.status === "cancelled") {
|
|
5847
|
+
st = "cancelled";
|
|
5848
|
+
fin = true;
|
|
5849
|
+
} else if (liveIndexChecked) {
|
|
5850
|
+
st = "done";
|
|
5851
|
+
fin = true;
|
|
5852
|
+
} else if (typeof rec.started === "number" && now - rec.started > RUN_RECORD_WORKING_STALE_MS) {
|
|
5853
|
+
st = "error";
|
|
5854
|
+
fin = true;
|
|
5855
|
+
} else {
|
|
5856
|
+
res = true;
|
|
5857
|
+
reason = "status";
|
|
5858
|
+
}
|
|
5859
|
+
}
|
|
5860
|
+
var sg = {
|
|
5861
|
+
key: sp,
|
|
5862
|
+
// ONE identity for the run whether it renders from the record or
|
|
5863
|
+
// from its loaded passes: the views key the DOM off runKey, so a
|
|
5864
|
+
// 'stub:'-prefixed key meant every handoff was an unmount plus a
|
|
5865
|
+
// remount somewhere else. Named after the record's start, which
|
|
5866
|
+
// the real group below reuses when it has one.
|
|
5867
|
+
runKey: "run:" + sp + "#" + (typeof rec.started === "number" ? rec.started : "n"),
|
|
5868
|
+
name: fname,
|
|
5869
|
+
path: sp,
|
|
5870
|
+
mime: void 0,
|
|
5871
|
+
size: void 0,
|
|
5872
|
+
isReindex: false,
|
|
5873
|
+
members: [],
|
|
5874
|
+
passCount: 0,
|
|
5875
|
+
status: st,
|
|
5876
|
+
cancellableIds: [],
|
|
5877
|
+
cancelling: false,
|
|
5878
|
+
stopped: st === "cancelled",
|
|
5879
|
+
mayHaveOlder: hasMoreHistory,
|
|
5880
|
+
anchorIndex: -1,
|
|
5881
|
+
anchorId: "",
|
|
5882
|
+
visibleMembers: [],
|
|
5883
|
+
driver: !isPagedReadFile(fname, void 0) ? "single" : isImageVisionFile(fname, void 0) ? "worker" : windowedIndexing ? "worker" : "client",
|
|
5884
|
+
finished: fin,
|
|
5885
|
+
resolving: res,
|
|
5886
|
+
resolvingReason: reason,
|
|
5887
|
+
stub: true,
|
|
5888
|
+
stubError: rec.error || (st === "error" && !rec.error ? "Indexing did not finish." : void 0)
|
|
5889
|
+
};
|
|
5890
|
+
stubList.push({ started: typeof rec.started === "number" ? rec.started : Infinity, group: sg });
|
|
5891
|
+
}
|
|
5892
|
+
}
|
|
5893
|
+
var suppressAnchor = {};
|
|
5894
|
+
if (runStubs) {
|
|
5895
|
+
for (var ti2 = 0; ti2 < order.length; ti2++) {
|
|
5896
|
+
var tg = groups[order[ti2]];
|
|
5897
|
+
if (!newestRunOfKey[order[ti2]]) continue;
|
|
5898
|
+
var trec = tg.path && runStubs[tg.path] || runStubs[tg.key];
|
|
5899
|
+
if (!trec || typeof trec.started !== "number") continue;
|
|
5900
|
+
if (stubPlatform && trec.platform && trec.platform !== stubPlatform) continue;
|
|
5901
|
+
suppressAnchor[order[ti2]] = true;
|
|
5902
|
+
tg.runKey = "run:" + (tg.path || tg.key) + "#" + trec.started;
|
|
5903
|
+
stubList.push({ started: trec.started, group: tg });
|
|
5904
|
+
}
|
|
5905
|
+
}
|
|
5906
|
+
stubList.sort(function(a, b) {
|
|
5907
|
+
return a.started - b.started;
|
|
5908
|
+
});
|
|
5073
5909
|
var out = [];
|
|
5910
|
+
var si = 0;
|
|
5074
5911
|
for (var j = 0; j < list.length; j++) {
|
|
5912
|
+
var mts = list[j] && typeof list[j]._ts === "number" ? list[j]._ts : void 0;
|
|
5913
|
+
if (mts !== void 0) {
|
|
5914
|
+
while (si < stubList.length && stubList[si].started <= mts) {
|
|
5915
|
+
out.push({ kind: "indexing", group: stubList[si].group, index: -1 - si });
|
|
5916
|
+
si++;
|
|
5917
|
+
}
|
|
5918
|
+
}
|
|
5075
5919
|
var r = runOfIndex[j];
|
|
5076
5920
|
if (r === void 0) {
|
|
5077
5921
|
out.push({ kind: "message", msg: list[j], index: j });
|
|
5078
5922
|
continue;
|
|
5079
5923
|
}
|
|
5080
|
-
if (groups[r].anchorIndex === j
|
|
5924
|
+
if (groups[r].anchorIndex === j && !suppressAnchor[r]) {
|
|
5925
|
+
out.push({ kind: "indexing", group: groups[r], index: j });
|
|
5926
|
+
}
|
|
5927
|
+
}
|
|
5928
|
+
while (si < stubList.length) {
|
|
5929
|
+
out.push({ kind: "indexing", group: stubList[si].group, index: -1 - si });
|
|
5930
|
+
si++;
|
|
5081
5931
|
}
|
|
5082
5932
|
return out;
|
|
5083
5933
|
}
|
|
@@ -5086,7 +5936,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
5086
5936
|
(function() {
|
|
5087
5937
|
var MCP_PROD = "https://mcp.broadwayinc.computer";
|
|
5088
5938
|
var MCP_DEV = "https://mcp-dev.broadwayinc.computer";
|
|
5089
|
-
var BQ_VERSION = "1.8.
|
|
5939
|
+
var BQ_VERSION = "1.8.8" ;
|
|
5090
5940
|
var ATTACHMENT_URL_EXPIRES_SECONDS = 600;
|
|
5091
5941
|
var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
5092
5942
|
var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
|
|
@@ -5325,6 +6175,16 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
5325
6175
|
var node = builder();
|
|
5326
6176
|
if (node) S.root.appendChild(node);
|
|
5327
6177
|
}
|
|
6178
|
+
function brandTitleEl() {
|
|
6179
|
+
return h(
|
|
6180
|
+
"div",
|
|
6181
|
+
{ class: "bq-title-left bq-brand" },
|
|
6182
|
+
h("img", { class: "bq-brand-icon", src: BQ_LOGO_URI, alt: "", "aria-hidden": "true" }),
|
|
6183
|
+
h("span", { class: "bq-brand-name", text: "BunnyQuery" }),
|
|
6184
|
+
S.serviceName ? h("span", { class: "bq-brand-sep", text: "\xB7" }) : null,
|
|
6185
|
+
S.serviceName ? h("span", { class: "bq-brand-project", title: S.serviceName, text: S.serviceName }) : null
|
|
6186
|
+
);
|
|
6187
|
+
}
|
|
5328
6188
|
function pageRoot(content) {
|
|
5329
6189
|
return h(
|
|
5330
6190
|
"div",
|
|
@@ -5332,15 +6192,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
5332
6192
|
h(
|
|
5333
6193
|
"div",
|
|
5334
6194
|
{ class: "bq-section-title" },
|
|
5335
|
-
h(
|
|
5336
|
-
"div",
|
|
5337
|
-
{ class: "bq-title-row" },
|
|
5338
|
-
h(
|
|
5339
|
-
"div",
|
|
5340
|
-
{ class: "bq-title-left" },
|
|
5341
|
-
h("span", { class: "bq-agent-badge", text: agentBadgeText() })
|
|
5342
|
-
)
|
|
5343
|
-
)
|
|
6195
|
+
h("div", { class: "bq-title-row" }, brandTitleEl())
|
|
5344
6196
|
),
|
|
5345
6197
|
h(
|
|
5346
6198
|
"div",
|
|
@@ -5366,6 +6218,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
5366
6218
|
}
|
|
5367
6219
|
var BUNNY_FRAME_A = ' (\\(\\\n ( - -)\n c(")(")';
|
|
5368
6220
|
var BUNNY_FRAME_B = ' /)/)\n ( . .)\nc(")(")';
|
|
6221
|
+
var BQ_LOGO_URI = "data:image/png;base64,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";
|
|
5369
6222
|
function bunnyLoader(label, overlay) {
|
|
5370
6223
|
return h(
|
|
5371
6224
|
"div",
|
|
@@ -6992,6 +7845,9 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
6992
7845
|
inputEl.value = "";
|
|
6993
7846
|
autoGrowInput(inputEl);
|
|
6994
7847
|
}
|
|
7848
|
+
updateComposerControls();
|
|
7849
|
+
CS.drafting = false;
|
|
7850
|
+
syncDraftingIndicator();
|
|
6995
7851
|
if (!hasAttachments) {
|
|
6996
7852
|
session.dispatchComposedMessage(text, false);
|
|
6997
7853
|
return;
|
|
@@ -7075,7 +7931,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
7075
7931
|
}
|
|
7076
7932
|
});
|
|
7077
7933
|
}
|
|
7078
|
-
function parseMsgPartsHtml(content) {
|
|
7934
|
+
function parseMsgPartsHtml(content, opts) {
|
|
7935
|
+
var noPreviews = !!(opts && opts.imagePreviews === false);
|
|
7079
7936
|
var placeholderHtml = [];
|
|
7080
7937
|
var PH = function(idx) {
|
|
7081
7938
|
return "\uE000BQ" + idx + "\uE001";
|
|
@@ -7103,7 +7960,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
7103
7960
|
codeMasks.push(match);
|
|
7104
7961
|
return "\uE002C" + idx + "\uE003";
|
|
7105
7962
|
});
|
|
7106
|
-
var previewsLeft = IMAGE_PREVIEWS_PER_MESSAGE;
|
|
7963
|
+
var previewsLeft = noPreviews ? 0 : IMAGE_PREVIEWS_PER_MESSAGE;
|
|
7107
7964
|
var linkRe = createInlineLinkRegex();
|
|
7108
7965
|
working = working.replace(linkRe, function(full) {
|
|
7109
7966
|
var args = Array.prototype.slice.call(arguments, 1, 7);
|
|
@@ -7214,9 +8071,118 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
7214
8071
|
}
|
|
7215
8072
|
function deleteFileIndexRecordDb(storagePath) {
|
|
7216
8073
|
if (!storagePath || !S.skapi || typeof S.skapi.deleteRecords !== "function") return Promise.resolve();
|
|
8074
|
+
var doneDelete = S.skapi.deleteRecords({ service: S.projectId, unique_id: indexDoneUniqueId(storagePath) }).catch(function() {
|
|
8075
|
+
});
|
|
8076
|
+
var runDelete = S.skapi.deleteRecords({ service: S.projectId, unique_id: runIndexUniqueId(storagePath) }).catch(function() {
|
|
8077
|
+
});
|
|
7217
8078
|
return S.skapi.deleteRecords({ service: S.projectId, unique_id: "src::" + storagePath }).catch(function() {
|
|
8079
|
+
}).then(function() {
|
|
8080
|
+
return doneDelete;
|
|
8081
|
+
}).then(function() {
|
|
8082
|
+
return runDelete;
|
|
7218
8083
|
});
|
|
7219
8084
|
}
|
|
8085
|
+
function mintIndexDoneMarkerDb(service, storagePath) {
|
|
8086
|
+
if (!service || !storagePath || !S.skapi || typeof S.skapi.postRecord !== "function") return Promise.resolve();
|
|
8087
|
+
return Promise.resolve(S.skapi.postRecord(null, {
|
|
8088
|
+
service,
|
|
8089
|
+
unique_id: indexDoneUniqueId(storagePath),
|
|
8090
|
+
table: { name: "__INDEXING__", access_group: "authorized" },
|
|
8091
|
+
reference: "src::" + storagePath,
|
|
8092
|
+
data: { source: storagePath, completed_at: Date.now() }
|
|
8093
|
+
})).catch(function(err) {
|
|
8094
|
+
var msg = String(err && err.message || err || "");
|
|
8095
|
+
if (msg.indexOf("is already taken") === -1) {
|
|
8096
|
+
console.warn("[bunnyquery] mintIndexDoneMarker failed (non-fatal)", storagePath, msg);
|
|
8097
|
+
}
|
|
8098
|
+
});
|
|
8099
|
+
}
|
|
8100
|
+
function upsertIndexRunRecordDb(service, storagePath, patch) {
|
|
8101
|
+
if (!service || !storagePath || !patch || !patch.status) return Promise.resolve();
|
|
8102
|
+
if (!S.skapi || typeof S.skapi.postRecord !== "function") return Promise.resolve();
|
|
8103
|
+
var uid = runIndexUniqueId(storagePath);
|
|
8104
|
+
var TERMINAL = { done: true, error: true, cancelled: true };
|
|
8105
|
+
function patchData(base) {
|
|
8106
|
+
var d = {};
|
|
8107
|
+
for (var k in base || {}) d[k] = base[k];
|
|
8108
|
+
d.source = storagePath;
|
|
8109
|
+
d.status = patch.status;
|
|
8110
|
+
if (patch.filename) d.filename = patch.filename;
|
|
8111
|
+
if (typeof patch.started === "number") d.started = patch.started;
|
|
8112
|
+
if (typeof patch.finished === "number") d.finished = patch.finished;
|
|
8113
|
+
if (patch.error) d.error = patch.error;
|
|
8114
|
+
if (patch.queue) d.queue = patch.queue;
|
|
8115
|
+
if (patch.platform) d.platform = patch.platform;
|
|
8116
|
+
return d;
|
|
8117
|
+
}
|
|
8118
|
+
function createWith(reference) {
|
|
8119
|
+
var cfg = {
|
|
8120
|
+
service,
|
|
8121
|
+
unique_id: uid,
|
|
8122
|
+
table: { name: "__INDEXING__", access_group: "authorized" },
|
|
8123
|
+
data: patchData(null)
|
|
8124
|
+
};
|
|
8125
|
+
if (reference) cfg.reference = "src::" + storagePath;
|
|
8126
|
+
return S.skapi.postRecord(null, cfg);
|
|
8127
|
+
}
|
|
8128
|
+
function lookup() {
|
|
8129
|
+
return Promise.resolve(S.skapi.getRecords({ service, unique_id: uid })).then(function(found) {
|
|
8130
|
+
return found && found.list && found.list[0] || null;
|
|
8131
|
+
}).catch(function() {
|
|
8132
|
+
return null;
|
|
8133
|
+
});
|
|
8134
|
+
}
|
|
8135
|
+
function updateExisting(rec) {
|
|
8136
|
+
var existing = rec.data || {};
|
|
8137
|
+
if (patch.status === "working" && TERMINAL[String(existing.status)]) {
|
|
8138
|
+
var endedAt = typeof existing.finished === "number" ? existing.finished : typeof existing.started === "number" ? existing.started : 0;
|
|
8139
|
+
if (!(typeof patch.started === "number" && patch.started > endedAt)) return Promise.resolve(null);
|
|
8140
|
+
}
|
|
8141
|
+
if (patch.status !== "working" && String(existing.status) === patch.status) return Promise.resolve(null);
|
|
8142
|
+
return Promise.resolve(S.skapi.postRecord(null, {
|
|
8143
|
+
service,
|
|
8144
|
+
record_id: rec.record_id,
|
|
8145
|
+
data: patchData(existing)
|
|
8146
|
+
}));
|
|
8147
|
+
}
|
|
8148
|
+
function settleAsUpdate() {
|
|
8149
|
+
return lookup().then(function(rec) {
|
|
8150
|
+
if (rec && rec.record_id) return updateExisting(rec);
|
|
8151
|
+
return null;
|
|
8152
|
+
}).catch(function(err) {
|
|
8153
|
+
console.warn("[bunnyquery] upsertIndexRunRecord update failed (non-fatal)", storagePath, String(err && err.message || err || ""));
|
|
8154
|
+
});
|
|
8155
|
+
}
|
|
8156
|
+
function createChain() {
|
|
8157
|
+
return Promise.resolve(createWith(true)).catch(function(err) {
|
|
8158
|
+
var msg = String(err && err.message || err || "");
|
|
8159
|
+
if (msg.indexOf("is already taken") === -1) {
|
|
8160
|
+
return ensureFileIndexRecordDb(storagePath).then(function() {
|
|
8161
|
+
return createWith(true);
|
|
8162
|
+
}).catch(function(errRef) {
|
|
8163
|
+
var msgRef = String(errRef && errRef.message || errRef || "");
|
|
8164
|
+
if (msgRef.indexOf("is already taken") !== -1) return settleAsUpdate();
|
|
8165
|
+
return Promise.resolve(createWith(false)).catch(function(err2) {
|
|
8166
|
+
var msg2 = String(err2 && err2.message || err2 || "");
|
|
8167
|
+
if (msg2.indexOf("is already taken") === -1) {
|
|
8168
|
+
console.warn("[bunnyquery] upsertIndexRunRecord create failed (non-fatal)", storagePath, msg2);
|
|
8169
|
+
return null;
|
|
8170
|
+
}
|
|
8171
|
+
return settleAsUpdate();
|
|
8172
|
+
});
|
|
8173
|
+
});
|
|
8174
|
+
}
|
|
8175
|
+
return settleAsUpdate();
|
|
8176
|
+
});
|
|
8177
|
+
}
|
|
8178
|
+
if (patch.status !== "working") {
|
|
8179
|
+
return lookup().then(function(rec) {
|
|
8180
|
+
if (rec && rec.record_id) return updateExisting(rec);
|
|
8181
|
+
return createChain();
|
|
8182
|
+
});
|
|
8183
|
+
}
|
|
8184
|
+
return createChain();
|
|
8185
|
+
}
|
|
7220
8186
|
function ensureFileIndexRecordDb(storagePath, meta) {
|
|
7221
8187
|
if (!storagePath || !S.skapi || typeof S.skapi.postRecord !== "function") return Promise.resolve();
|
|
7222
8188
|
return Promise.resolve(S.skapi.postRecord(null, {
|
|
@@ -7310,9 +8276,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
7310
8276
|
function currentInputTokenBudget() {
|
|
7311
8277
|
var platform = S.aiPlatform;
|
|
7312
8278
|
if (platform !== "claude" && platform !== "openai") return 0;
|
|
7313
|
-
|
|
7314
|
-
var contextBased = Math.max(MIN_INPUT_TOKEN_BUDGET, contextWindow - OUTPUT_TOKEN_RESERVE - TOOL_AND_RESPONSE_BUFFER);
|
|
7315
|
-
return platform === "claude" ? Math.min(contextBased, CLAUDE_PER_REQUEST_INPUT_CAP) : contextBased;
|
|
8279
|
+
return getInputTokenBudget(platform, S.aiModel, S.projectId);
|
|
7316
8280
|
}
|
|
7317
8281
|
function formatTokenCount(tokens) {
|
|
7318
8282
|
if (tokens >= 1e3) {
|
|
@@ -7589,21 +8553,25 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
7589
8553
|
shown.forEach(function(att) {
|
|
7590
8554
|
var isFolder = att.kind === "folder";
|
|
7591
8555
|
var clickable = att.status === "done" && !isFolder && !!att.uploadedUrl;
|
|
8556
|
+
var finalizing = att.status === "uploading" && (att.progress || 0) >= 100;
|
|
8557
|
+
var preparing = att.status === "uploading" && att.progress == null;
|
|
7592
8558
|
var cls = "bq-attachment";
|
|
7593
8559
|
if (att.status === "uploading") cls += " is-uploading";
|
|
8560
|
+
if (preparing) cls += " is-preparing";
|
|
8561
|
+
else if (finalizing) cls += " is-finalizing";
|
|
7594
8562
|
else if (att.status === "error") cls += " is-error";
|
|
7595
8563
|
else if (att.status === "indexError") cls += " is-index-error";
|
|
7596
8564
|
else if (att.status === "done") cls += " is-done";
|
|
7597
8565
|
if (clickable) cls += " is-clickable";
|
|
7598
8566
|
var chip = h("div", { class: cls });
|
|
7599
|
-
if (att.status === "uploading") chip.style.setProperty("--att-progress",
|
|
8567
|
+
if (att.status === "uploading" && att.progress != null) chip.style.setProperty("--att-progress", att.progress + "%");
|
|
7600
8568
|
chip.title = att.status === "error" ? "File upload has failed" : att.status === "indexError" ? "File indexing failed" : clickable ? "Open " + att.name : isFolder ? att.name + "/ \u2014 " + (att.files ? att.files.length : 0) + " file(s)" : att.name;
|
|
7601
8569
|
if (clickable) chip.addEventListener("click", function() {
|
|
7602
8570
|
window.open(att.uploadedUrl, "_blank", "noopener,noreferrer");
|
|
7603
8571
|
});
|
|
7604
8572
|
chip.appendChild(h("span", { class: "bq-attachment-icon", html: isFolder ? FOLDER_ICON_SVG : FILE_ICON_SVG }));
|
|
7605
8573
|
chip.appendChild(h("span", { class: "bq-attachment-name", text: att.name, title: att.name }));
|
|
7606
|
-
var meta = att.status === "error" ? "(Failed)" : att.status === "indexError" ? "(Error)" : att.status === "uploading" ?
|
|
8574
|
+
var meta = att.status === "error" ? "(Failed)" : att.status === "indexError" ? "(Error)" : preparing ? "Preparing" : finalizing ? "Finalizing" : att.status === "uploading" ? att.progress + "%" : isFolder ? "(" + (att.files ? att.files.length : 0) + ")" : formatBytes(att.file ? att.file.size : att.size);
|
|
7607
8575
|
chip.appendChild(h("span", { class: "bq-attachment-meta", text: meta }));
|
|
7608
8576
|
if (clickable) chip.appendChild(h("span", { class: "bq-attachment-arrow", text: "\u2197" }));
|
|
7609
8577
|
if (att.status !== "uploading" && att.status !== "done") {
|
|
@@ -7663,7 +8631,13 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
7663
8631
|
function updateComposerControls() {
|
|
7664
8632
|
if (CS.attachBtnEl) CS.attachBtnEl.disabled = false;
|
|
7665
8633
|
if (CS.inputEl) CS.inputEl.disabled = false;
|
|
7666
|
-
if (CS.sendBtnEl)
|
|
8634
|
+
if (CS.sendBtnEl) {
|
|
8635
|
+
var hasText = !!(CS.inputEl && CS.inputEl.value.trim());
|
|
8636
|
+
var hasSendableAttachment = composerAttachments().some(function(a) {
|
|
8637
|
+
return a.status !== "done";
|
|
8638
|
+
});
|
|
8639
|
+
CS.sendBtnEl.disabled = !!CS.attachmentWarning || !hasText && !hasSendableAttachment;
|
|
8640
|
+
}
|
|
7667
8641
|
}
|
|
7668
8642
|
function onAttachInputChange(inputEl) {
|
|
7669
8643
|
if (inputEl && inputEl.files && inputEl.files.length) addFilesToAttachments(inputEl.files);
|
|
@@ -7903,7 +8877,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
7903
8877
|
}
|
|
7904
8878
|
function messagesBoxCanScroll() {
|
|
7905
8879
|
if (!CS.messagesBox || CS.chatSettingsOpen) return true;
|
|
7906
|
-
|
|
8880
|
+
var drafting = CS.draftingEl && CS.draftingEl.parentNode === CS.messagesBox ? CS.draftingEl.offsetHeight : 0;
|
|
8881
|
+
return CS.messagesBox.scrollHeight - drafting - CS.messagesBox.clientHeight > HISTORY_FILL_SLACK_PX;
|
|
7907
8882
|
}
|
|
7908
8883
|
function topVisibleRowKey() {
|
|
7909
8884
|
var box = CS.messagesBox;
|
|
@@ -8098,11 +9073,168 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8098
9073
|
return indexGroupVerb(group) + " " + nameLabel;
|
|
8099
9074
|
}
|
|
8100
9075
|
function indexGroupCount(group) {
|
|
9076
|
+
if (group.stub) return "";
|
|
8101
9077
|
if (group.passCount <= 1 && !group.mayHaveOlder) return "";
|
|
8102
9078
|
return group.passCount + (group.mayHaveOlder ? "+" : "") + " passes";
|
|
8103
9079
|
}
|
|
9080
|
+
var markerSweep = { svc: "", at: 0, gen: 0, done: {}, runs: {}, partial: false, inflight: null };
|
|
9081
|
+
var MARKER_SWEEP_TTL_MS = 3e4;
|
|
9082
|
+
var MARKER_SWEEP_MAX_PAGES = 10;
|
|
9083
|
+
function sweepIndexMarkersDb() {
|
|
9084
|
+
if (!S.skapi || !S.projectId || typeof S.skapi.getRecords !== "function") return Promise.resolve(null);
|
|
9085
|
+
var svc = S.projectId;
|
|
9086
|
+
if (markerSweep.svc === svc && markerSweep.at && Date.now() - markerSweep.at < MARKER_SWEEP_TTL_MS) {
|
|
9087
|
+
return Promise.resolve(markerSweep);
|
|
9088
|
+
}
|
|
9089
|
+
if (markerSweep.inflight) {
|
|
9090
|
+
if (!markerSweep.at) {
|
|
9091
|
+
return markerSweep.inflight.then(function() {
|
|
9092
|
+
return sweepIndexMarkersDb();
|
|
9093
|
+
});
|
|
9094
|
+
}
|
|
9095
|
+
return markerSweep.inflight;
|
|
9096
|
+
}
|
|
9097
|
+
var gen = markerSweep.gen;
|
|
9098
|
+
var done = {};
|
|
9099
|
+
var runs = {};
|
|
9100
|
+
var partial = false;
|
|
9101
|
+
function page(fetchMore, n) {
|
|
9102
|
+
return Promise.resolve(S.skapi.getRecords(
|
|
9103
|
+
{ service: svc, table: { name: "__INDEXING__", access_group: "authorized" } },
|
|
9104
|
+
{ limit: 1e3, fetchMore, ascending: false }
|
|
9105
|
+
)).then(function(res) {
|
|
9106
|
+
var list = res && res.list || [];
|
|
9107
|
+
for (var i = 0; i < list.length; i++) {
|
|
9108
|
+
var uid = String(list[i] && list[i].unique_id || "");
|
|
9109
|
+
if (uid.indexOf("done::") === 0) {
|
|
9110
|
+
done[uid.slice(6)] = true;
|
|
9111
|
+
} else if (uid.indexOf("run::") === 0) {
|
|
9112
|
+
var path = uid.slice(5);
|
|
9113
|
+
var d = list[i] && list[i].data || {};
|
|
9114
|
+
var st = String(d.status || "");
|
|
9115
|
+
if (path && !runs[path] && (st === "working" || st === "done" || st === "error" || st === "cancelled")) {
|
|
9116
|
+
runs[path] = {
|
|
9117
|
+
status: st,
|
|
9118
|
+
filename: typeof d.filename === "string" ? d.filename : void 0,
|
|
9119
|
+
started: typeof d.started === "number" ? d.started : void 0,
|
|
9120
|
+
finished: typeof d.finished === "number" ? d.finished : void 0,
|
|
9121
|
+
error: typeof d.error === "string" ? d.error : void 0,
|
|
9122
|
+
platform: d.platform === "claude" || d.platform === "openai" ? d.platform : void 0,
|
|
9123
|
+
owner: list[i] && typeof list[i].user_id === "string" ? list[i].user_id : void 0
|
|
9124
|
+
};
|
|
9125
|
+
}
|
|
9126
|
+
}
|
|
9127
|
+
}
|
|
9128
|
+
if (res && res.endOfList === false) {
|
|
9129
|
+
if (n < MARKER_SWEEP_MAX_PAGES - 1) return page(true, n + 1);
|
|
9130
|
+
partial = true;
|
|
9131
|
+
}
|
|
9132
|
+
return null;
|
|
9133
|
+
});
|
|
9134
|
+
}
|
|
9135
|
+
var p = page(false, 0).then(function() {
|
|
9136
|
+
if (S.projectId !== svc || markerSweep.gen !== gen) return markerSweep;
|
|
9137
|
+
markerSweep.svc = svc;
|
|
9138
|
+
markerSweep.at = Date.now();
|
|
9139
|
+
markerSweep.done = done;
|
|
9140
|
+
markerSweep.runs = runs;
|
|
9141
|
+
markerSweep.partial = partial;
|
|
9142
|
+
return markerSweep;
|
|
9143
|
+
});
|
|
9144
|
+
markerSweep.inflight = p;
|
|
9145
|
+
p.then(function() {
|
|
9146
|
+
markerSweep.inflight = null;
|
|
9147
|
+
}, function() {
|
|
9148
|
+
markerSweep.inflight = null;
|
|
9149
|
+
});
|
|
9150
|
+
return p;
|
|
9151
|
+
}
|
|
9152
|
+
function invalidateIndexMarkerSweep() {
|
|
9153
|
+
markerSweep.at = 0;
|
|
9154
|
+
markerSweep.gen++;
|
|
9155
|
+
}
|
|
9156
|
+
var STUB_RECHECK_MS = 3e4;
|
|
9157
|
+
var STUB_RECHECK_MAX_ROUNDS = 5;
|
|
9158
|
+
var stubRecheckTimer = null;
|
|
9159
|
+
var stubRecheckSig = "";
|
|
9160
|
+
var stubRecheckRounds = 0;
|
|
9161
|
+
var markerSweepSettled = false;
|
|
9162
|
+
function armStubRecheck() {
|
|
9163
|
+
if (stubRecheckTimer !== null) return;
|
|
9164
|
+
stubRecheckTimer = setTimeout(function() {
|
|
9165
|
+
stubRecheckTimer = null;
|
|
9166
|
+
if (typeof document !== "undefined" && document.visibilityState === "hidden") return;
|
|
9167
|
+
stubRecheckRounds++;
|
|
9168
|
+
void refreshIndexMarkers();
|
|
9169
|
+
}, STUB_RECHECK_MS);
|
|
9170
|
+
}
|
|
9171
|
+
function maybeArmStubRecheck() {
|
|
9172
|
+
try {
|
|
9173
|
+
if (markerSweep.svc !== S.projectId) return;
|
|
9174
|
+
var lk = session && session.getLiveIndexState().keys || {};
|
|
9175
|
+
var sig = [];
|
|
9176
|
+
for (var pth in markerSweep.runs) {
|
|
9177
|
+
var r = markerSweep.runs[pth];
|
|
9178
|
+
if (!r || r.status !== "working" || lk[pth]) continue;
|
|
9179
|
+
var fn = r.filename || pth.split("/").pop() || pth;
|
|
9180
|
+
if (markerSweep.done[pth] || markerSweep.done[fn]) continue;
|
|
9181
|
+
sig.push(pth);
|
|
9182
|
+
}
|
|
9183
|
+
if (!sig.length) {
|
|
9184
|
+
stubRecheckSig = "";
|
|
9185
|
+
stubRecheckRounds = 0;
|
|
9186
|
+
if (stubRecheckTimer !== null) {
|
|
9187
|
+
clearTimeout(stubRecheckTimer);
|
|
9188
|
+
stubRecheckTimer = null;
|
|
9189
|
+
}
|
|
9190
|
+
return;
|
|
9191
|
+
}
|
|
9192
|
+
var s = sig.sort().join("|");
|
|
9193
|
+
if (s !== stubRecheckSig) {
|
|
9194
|
+
stubRecheckSig = s;
|
|
9195
|
+
stubRecheckRounds = 0;
|
|
9196
|
+
}
|
|
9197
|
+
if (stubRecheckRounds >= STUB_RECHECK_MAX_ROUNDS) return;
|
|
9198
|
+
armStubRecheck();
|
|
9199
|
+
} catch (e) {
|
|
9200
|
+
}
|
|
9201
|
+
}
|
|
9202
|
+
function refreshIndexMarkers(invalidate) {
|
|
9203
|
+
if (invalidate) invalidateIndexMarkerSweep();
|
|
9204
|
+
return sweepIndexMarkersDb().then(function(res) {
|
|
9205
|
+
markerSweepSettled = true;
|
|
9206
|
+
if (res) {
|
|
9207
|
+
maybeArmStubRecheck();
|
|
9208
|
+
renderMessages();
|
|
9209
|
+
}
|
|
9210
|
+
return res;
|
|
9211
|
+
}).catch(function() {
|
|
9212
|
+
markerSweepSettled = true;
|
|
9213
|
+
renderMessages();
|
|
9214
|
+
return null;
|
|
9215
|
+
});
|
|
9216
|
+
}
|
|
8104
9217
|
function displayListOptions() {
|
|
8105
9218
|
var liveIndex = session.getLiveIndexState();
|
|
9219
|
+
var fresh = markerSweep.svc === S.projectId;
|
|
9220
|
+
var stubs = void 0;
|
|
9221
|
+
if (fresh) {
|
|
9222
|
+
stubs = {};
|
|
9223
|
+
var myId = S.user && S.user.user_id || "";
|
|
9224
|
+
for (var rp in markerSweep.runs) {
|
|
9225
|
+
var rr = markerSweep.runs[rp];
|
|
9226
|
+
if (rr && rr.owner && myId && rr.owner !== myId) continue;
|
|
9227
|
+
stubs[rp] = {
|
|
9228
|
+
status: rr.status,
|
|
9229
|
+
filename: rr.filename,
|
|
9230
|
+
started: rr.started,
|
|
9231
|
+
finished: rr.finished,
|
|
9232
|
+
error: rr.error,
|
|
9233
|
+
platform: rr.platform,
|
|
9234
|
+
owner: rr.owner
|
|
9235
|
+
};
|
|
9236
|
+
}
|
|
9237
|
+
}
|
|
8106
9238
|
return {
|
|
8107
9239
|
hasMoreHistory: !CS.historyEndOfList,
|
|
8108
9240
|
// Older pages coming in RIGHT NOW. CS.historyFilling, not just the
|
|
@@ -8115,12 +9247,22 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8115
9247
|
// Runs the user stopped. A stop that landed on a RUNNING pass leaves no
|
|
8116
9248
|
// cancelled bubble behind (that pass finishes and answers normally), so
|
|
8117
9249
|
// without this the row reports the stop as a finished "Indexed".
|
|
8118
|
-
stoppedIndexIds: session.getStoppedIndexIds()
|
|
9250
|
+
stoppedIndexIds: session.getStoppedIndexIds(),
|
|
9251
|
+
// Durable completion markers + run records (one sweep, see above).
|
|
9252
|
+
// doneKeys settle worker-run greens without a queue round trip;
|
|
9253
|
+
// runStubs paint rows for runs whose passes are not loaded yet.
|
|
9254
|
+
doneKeys: fresh ? markerSweep.done : void 0,
|
|
9255
|
+
runStubs: stubs,
|
|
9256
|
+
// Records are service-wide and horizon-blind; without this every
|
|
9257
|
+
// "Clear chat history" resurrected one row per indexed file.
|
|
9258
|
+
stubClearedAt: getClearedAt(),
|
|
9259
|
+
// A run:: record is per FILE; a chat is per (project, PLATFORM).
|
|
9260
|
+
stubPlatform: S.aiPlatform === "claude" || S.aiPlatform === "openai" ? S.aiPlatform : void 0
|
|
8119
9261
|
};
|
|
8120
9262
|
}
|
|
8121
9263
|
var stopIndexState = { runKey: "", fileKey: "", handle: null };
|
|
8122
9264
|
function indexGroupStoppable(group) {
|
|
8123
|
-
return !!group && !group.finished && !group.resolving && !group.stopped && !group.cancelling;
|
|
9265
|
+
return !!group && !group.stub && !group.finished && !group.resolving && !group.stopped && !group.cancelling;
|
|
8124
9266
|
}
|
|
8125
9267
|
function findCancellableIndexGroup(runKey, fileKey) {
|
|
8126
9268
|
if (!runKey) return null;
|
|
@@ -8199,10 +9341,71 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8199
9341
|
}
|
|
8200
9342
|
function toggleIndexGroup(key) {
|
|
8201
9343
|
if (CS.indexGroupsOpen[key]) delete CS.indexGroupsOpen[key];
|
|
8202
|
-
else
|
|
9344
|
+
else {
|
|
9345
|
+
CS.indexGroupsOpen[key] = true;
|
|
9346
|
+
hydrateCompactIndexGroup(key);
|
|
9347
|
+
void loadIndexGroupHistory(key);
|
|
9348
|
+
}
|
|
8203
9349
|
renderMessages();
|
|
8204
9350
|
ensureHistoryFillsViewport();
|
|
8205
9351
|
}
|
|
9352
|
+
var INDEX_GROUP_FETCH_MAX_PAGES = 40;
|
|
9353
|
+
var indexGroupFetching = {};
|
|
9354
|
+
function groupNeedsHistory(key) {
|
|
9355
|
+
if (CS.historyEndOfList) return false;
|
|
9356
|
+
try {
|
|
9357
|
+
var entries = buildChatDisplayList(CS.messages, displayListOptions());
|
|
9358
|
+
for (var i = 0; i < entries.length; i++) {
|
|
9359
|
+
if (entries[i].kind !== "indexing" || entries[i].group.key !== key) continue;
|
|
9360
|
+
return !!(entries[i].group.stub || entries[i].group.mayHaveOlder);
|
|
9361
|
+
}
|
|
9362
|
+
} catch (e) {
|
|
9363
|
+
}
|
|
9364
|
+
return false;
|
|
9365
|
+
}
|
|
9366
|
+
function loadIndexGroupHistory(key) {
|
|
9367
|
+
if (indexGroupFetching[key]) return Promise.resolve();
|
|
9368
|
+
if (!groupNeedsHistory(key)) return Promise.resolve();
|
|
9369
|
+
indexGroupFetching[key] = true;
|
|
9370
|
+
renderMessages();
|
|
9371
|
+
var pages = 0, waits = 0;
|
|
9372
|
+
function step() {
|
|
9373
|
+
if (!CS.indexGroupsOpen[key]) return null;
|
|
9374
|
+
if (!groupNeedsHistory(key)) return null;
|
|
9375
|
+
if (pages >= INDEX_GROUP_FETCH_MAX_PAGES) return null;
|
|
9376
|
+
if (CS.loadingOlderHistory || CS.historyFilling || session.state.bgHistoryLoading) {
|
|
9377
|
+
if (++waits > 240) return null;
|
|
9378
|
+
return new Promise(function(r) {
|
|
9379
|
+
setTimeout(r, 250);
|
|
9380
|
+
}).then(step);
|
|
9381
|
+
}
|
|
9382
|
+
pages++;
|
|
9383
|
+
return fetchOlderHistoryIfNeeded().then(step);
|
|
9384
|
+
}
|
|
9385
|
+
return Promise.resolve(step()).catch(function() {
|
|
9386
|
+
}).then(function() {
|
|
9387
|
+
delete indexGroupFetching[key];
|
|
9388
|
+
hydrateCompactIndexGroup(key);
|
|
9389
|
+
renderMessages();
|
|
9390
|
+
});
|
|
9391
|
+
}
|
|
9392
|
+
function hydrateCompactIndexGroup(key) {
|
|
9393
|
+
try {
|
|
9394
|
+
var entries = buildChatDisplayList(session.state.messages, displayListOptions());
|
|
9395
|
+
for (var i = 0; i < entries.length; i++) {
|
|
9396
|
+
var en = entries[i];
|
|
9397
|
+
if (en.kind !== "indexing" || en.group.key !== key) continue;
|
|
9398
|
+
var ids = [];
|
|
9399
|
+
for (var mi = 0; mi < en.group.members.length; mi++) {
|
|
9400
|
+
var m = en.group.members[mi].msg;
|
|
9401
|
+
if (m && m._compact && m.role === "assistant" && m._serverItemId && !m.isError && !m.isPending) ids.push(m._serverItemId);
|
|
9402
|
+
}
|
|
9403
|
+
if (ids.length) session.hydrateCompactItems(ids);
|
|
9404
|
+
return;
|
|
9405
|
+
}
|
|
9406
|
+
} catch (e) {
|
|
9407
|
+
}
|
|
9408
|
+
}
|
|
8206
9409
|
function buildIndexGroupEl(group, isOpen) {
|
|
8207
9410
|
var cls = ["bq-index-group"];
|
|
8208
9411
|
if (group.resolving) cls.push("is-resolving");
|
|
@@ -8210,7 +9413,11 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8210
9413
|
if (!group.resolving && group.finished && group.status === "done") cls.push("is-indexed");
|
|
8211
9414
|
if (group.finished && group.status === "error") cls.push("is-error");
|
|
8212
9415
|
if (isOpen) cls.push("is-open");
|
|
8213
|
-
var label = h(
|
|
9416
|
+
var label = h(
|
|
9417
|
+
"span",
|
|
9418
|
+
{ class: "bq-index-label" },
|
|
9419
|
+
h("span", { class: "bq-md", html: parseMsgPartsHtml(indexGroupLabel(group), { imagePreviews: false }) })
|
|
9420
|
+
);
|
|
8214
9421
|
label.addEventListener("click", function(e) {
|
|
8215
9422
|
if (e.target && e.target.closest && e.target.closest("a")) e.stopPropagation();
|
|
8216
9423
|
onBubbleLinkClick(e);
|
|
@@ -8253,6 +9460,9 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8253
9460
|
h("span", { class: "bq-index-icon", html: indexGroupIcon(group) }),
|
|
8254
9461
|
label,
|
|
8255
9462
|
indexGroupCount(group) ? h("span", { class: "bq-index-count", text: indexGroupCount(group) }) : null,
|
|
9463
|
+
// Spinning arrows while this row's history is being paged in —
|
|
9464
|
+
// separate from the status icon, so a green (done) row spins too.
|
|
9465
|
+
indexGroupFetching[group.key] ? h("span", { class: "bq-index-fetch", html: INDEX_ICON_ACTIVE, title: "Fetching this file's indexing history" }) : null,
|
|
8256
9466
|
cancelBtn,
|
|
8257
9467
|
h("span", { class: "bq-index-chevron", text: "\u25B6" })
|
|
8258
9468
|
);
|
|
@@ -8263,10 +9473,23 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8263
9473
|
text: "Could not stop this file: " + group.cancelError
|
|
8264
9474
|
}));
|
|
8265
9475
|
}
|
|
8266
|
-
if (isOpen && group.
|
|
9476
|
+
if (isOpen && indexGroupFetching[group.key]) {
|
|
9477
|
+
el.appendChild(h(
|
|
9478
|
+
"div",
|
|
9479
|
+
{ class: "bq-index-note" },
|
|
9480
|
+
h("span", { text: "Loading this file's indexing history" }),
|
|
9481
|
+
h("span", { class: "bq-loader" })
|
|
9482
|
+
));
|
|
9483
|
+
} else if (isOpen && group.mayHaveOlder) {
|
|
9484
|
+
var loadingNow = group.resolvingReason === "history" || group.stub && session.state.bgHistoryLoading;
|
|
9485
|
+
el.appendChild(h("div", {
|
|
9486
|
+
class: "bq-index-note",
|
|
9487
|
+
text: "Earlier passes of this file are further back in the conversation. " + (loadingNow ? "Loading them now." : "Scroll up to load them.")
|
|
9488
|
+
}));
|
|
9489
|
+
} else if (isOpen && !group.visibleMembers.length) {
|
|
8267
9490
|
el.appendChild(h("div", {
|
|
8268
9491
|
class: "bq-index-note",
|
|
8269
|
-
text: "
|
|
9492
|
+
text: "This file's indexing steps aren't in this chat's history. They may belong to another chat or platform, or the conversation was cleared."
|
|
8270
9493
|
}));
|
|
8271
9494
|
}
|
|
8272
9495
|
return el;
|
|
@@ -8329,32 +9552,99 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8329
9552
|
}
|
|
8330
9553
|
box.scrollTop = anchor.scrollTop;
|
|
8331
9554
|
}
|
|
9555
|
+
function syncDraftingIndicator() {
|
|
9556
|
+
if (!CS.messagesBox) return;
|
|
9557
|
+
if (CS.drafting && !CS.chatSettingsOpen) {
|
|
9558
|
+
if (!CS.draftingEl) {
|
|
9559
|
+
CS.draftingEl = h(
|
|
9560
|
+
"div",
|
|
9561
|
+
{ class: "bq-message is-user bq-user-drafting", "aria-hidden": "true" },
|
|
9562
|
+
h("div", { class: "bq-bubble" }, h("span", { class: "bq-loader" }))
|
|
9563
|
+
);
|
|
9564
|
+
}
|
|
9565
|
+
CS.messagesBox.appendChild(CS.draftingEl);
|
|
9566
|
+
} else if (CS.draftingEl && CS.draftingEl.parentNode) {
|
|
9567
|
+
CS.draftingEl.parentNode.removeChild(CS.draftingEl);
|
|
9568
|
+
}
|
|
9569
|
+
}
|
|
8332
9570
|
function renderMessages() {
|
|
8333
9571
|
syncStopIndexModal();
|
|
9572
|
+
var _lk = session.getLiveIndexState().keys || {};
|
|
9573
|
+
var _lc = 0;
|
|
9574
|
+
for (var _k in _lk) _lc++;
|
|
9575
|
+
if (CS._lastLiveKeyCount > 0 && _lc < CS._lastLiveKeyCount) void refreshIndexMarkers(true);
|
|
9576
|
+
CS._lastLiveKeyCount = _lc;
|
|
8334
9577
|
if (!CS.messagesBox) return;
|
|
8335
9578
|
if (CS.chatSettingsOpen) return;
|
|
8336
9579
|
var anchor = captureScrollAnchor();
|
|
8337
9580
|
clear(CS.messagesBox);
|
|
8338
9581
|
CS.messageEls = [];
|
|
8339
9582
|
if (CS.loadingOlderHistory) CS.messagesBox.appendChild(historyLoadingEl(false));
|
|
9583
|
+
else if (session.state.bgHistoryLoading) {
|
|
9584
|
+
CS.messagesBox.appendChild(h(
|
|
9585
|
+
"div",
|
|
9586
|
+
{ class: "bq-history-loading" },
|
|
9587
|
+
h("span", { text: "Loading indexing history" }),
|
|
9588
|
+
h("span", { class: "bq-loader" })
|
|
9589
|
+
));
|
|
9590
|
+
}
|
|
8340
9591
|
if (!CS.messages.length) {
|
|
8341
9592
|
if (CS.loadingHistory && !CS.loadingOlderHistory) {
|
|
8342
9593
|
CS.messagesBox.appendChild(historyLoadingEl(true));
|
|
9594
|
+
syncDraftingIndicator();
|
|
8343
9595
|
return;
|
|
8344
9596
|
}
|
|
8345
|
-
var
|
|
8346
|
-
|
|
8347
|
-
|
|
8348
|
-
|
|
9597
|
+
var emptyStubEls = [];
|
|
9598
|
+
try {
|
|
9599
|
+
var emptyEntries = buildChatDisplayList([], displayListOptions());
|
|
9600
|
+
for (var ge = 0; ge < emptyEntries.length; ge++) {
|
|
9601
|
+
if (emptyEntries[ge].kind !== "indexing") continue;
|
|
9602
|
+
var sg = emptyEntries[ge].group;
|
|
9603
|
+
emptyStubEls.push(buildIndexGroupEl(sg, !!CS.indexGroupsOpen[sg.key]));
|
|
9604
|
+
}
|
|
9605
|
+
} catch (e) {
|
|
9606
|
+
}
|
|
9607
|
+
if (!emptyStubEls.length && !session.state.bgHistoryLoading && markerSweepSettled) {
|
|
9608
|
+
CS.messagesBox.appendChild(h(
|
|
8349
9609
|
"div",
|
|
8350
|
-
{ class: "bq-
|
|
8351
|
-
|
|
8352
|
-
|
|
8353
|
-
|
|
8354
|
-
|
|
9610
|
+
{ class: "bq-message is-assistant bq-empty-greeting" },
|
|
9611
|
+
h(
|
|
9612
|
+
"div",
|
|
9613
|
+
{ class: "bq-bubble" },
|
|
9614
|
+
document.createTextNode("Hi! Ask me anything about " + (S.serviceName ? '"' + S.serviceName + '"' : "your project") + ".")
|
|
9615
|
+
)
|
|
9616
|
+
));
|
|
9617
|
+
}
|
|
9618
|
+
for (var gse = 0; gse < emptyStubEls.length; gse++) CS.messagesBox.appendChild(emptyStubEls[gse]);
|
|
9619
|
+
syncDraftingIndicator();
|
|
8355
9620
|
return;
|
|
8356
9621
|
}
|
|
8357
9622
|
var rows = buildChatDisplayList(CS.messages, displayListOptions());
|
|
9623
|
+
try {
|
|
9624
|
+
if (markerSweep.svc === S.projectId) {
|
|
9625
|
+
var moSeen = S._mintObserved || (S._mintObserved = {});
|
|
9626
|
+
for (var moi = 0; moi < rows.length; moi++) {
|
|
9627
|
+
var moe = rows[moi];
|
|
9628
|
+
if (moe.kind !== "indexing" || moe.group.stub) continue;
|
|
9629
|
+
var mog = moe.group;
|
|
9630
|
+
if (!mog.path || !mog.finished || mog.status !== "done" || mog.resolving) continue;
|
|
9631
|
+
if (!(mog.driver === "single" || markerSweep.done[mog.path])) continue;
|
|
9632
|
+
var morec = markerSweep.runs[mog.path];
|
|
9633
|
+
if ((!morec || morec.status === "working") && !moSeen[mog.path]) {
|
|
9634
|
+
moSeen[mog.path] = true;
|
|
9635
|
+
var moFirst = mog.members && mog.members[0] && mog.members[0].msg && mog.members[0].msg._ts;
|
|
9636
|
+
var moLast = mog.members && mog.members.length && mog.members[mog.members.length - 1].msg && mog.members[mog.members.length - 1].msg._ts;
|
|
9637
|
+
var moPatch = { status: "done", finished: typeof moLast === "number" ? moLast : Date.now() };
|
|
9638
|
+
if (typeof moFirst === "number") moPatch.started = moFirst;
|
|
9639
|
+
if (mog.name) moPatch.filename = mog.name;
|
|
9640
|
+
void upsertIndexRunRecordDb(S.projectId, mog.path, moPatch);
|
|
9641
|
+
if (morec) morec.status = "done";
|
|
9642
|
+
else markerSweep.runs[mog.path] = { status: "done" };
|
|
9643
|
+
}
|
|
9644
|
+
}
|
|
9645
|
+
}
|
|
9646
|
+
} catch (e) {
|
|
9647
|
+
}
|
|
8358
9648
|
rows.forEach(function(row) {
|
|
8359
9649
|
if (row.kind === "indexing") {
|
|
8360
9650
|
var isOpen = !!CS.indexGroupsOpen[row.group.key];
|
|
@@ -8383,6 +9673,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8383
9673
|
CS.messageEls[row.index] = el;
|
|
8384
9674
|
CS.messagesBox.appendChild(el);
|
|
8385
9675
|
});
|
|
9676
|
+
syncDraftingIndicator();
|
|
8386
9677
|
restoreScrollAnchor(anchor);
|
|
8387
9678
|
hydrateMessageImagePreviews();
|
|
8388
9679
|
}
|
|
@@ -8405,6 +9696,8 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8405
9696
|
CS.sending = false;
|
|
8406
9697
|
CS.typing = false;
|
|
8407
9698
|
CS.typingAbort = true;
|
|
9699
|
+
CS.drafting = false;
|
|
9700
|
+
CS.draftingEl = null;
|
|
8408
9701
|
CS.historyEndOfList = false;
|
|
8409
9702
|
CS.historyStartKeyHistory = [];
|
|
8410
9703
|
CS.stickToBottom = true;
|
|
@@ -8431,6 +9724,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8431
9724
|
class: "bq-icon-btn",
|
|
8432
9725
|
type: "button",
|
|
8433
9726
|
title: "Settings",
|
|
9727
|
+
"aria-label": "Settings",
|
|
8434
9728
|
html: '<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-4 0v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1 0-4h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 4 0v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>',
|
|
8435
9729
|
onclick: function() {
|
|
8436
9730
|
toggleChatSettings();
|
|
@@ -8443,7 +9737,7 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8443
9737
|
h(
|
|
8444
9738
|
"div",
|
|
8445
9739
|
{ class: "bq-title-row" },
|
|
8446
|
-
|
|
9740
|
+
brandTitleEl(),
|
|
8447
9741
|
h("div", { class: "bq-title-right" }, settingsBtn)
|
|
8448
9742
|
)
|
|
8449
9743
|
);
|
|
@@ -8483,11 +9777,17 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8483
9777
|
autoGrowInput(input);
|
|
8484
9778
|
var prev = CS.attachmentWarning;
|
|
8485
9779
|
recomputeAttachmentWarning();
|
|
9780
|
+
updateComposerControls();
|
|
8486
9781
|
if (CS.attachmentWarning !== prev) {
|
|
8487
9782
|
renderAttachmentChips();
|
|
8488
|
-
updateComposerControls();
|
|
8489
9783
|
scheduleAttachmentOverflowRecompute();
|
|
8490
9784
|
}
|
|
9785
|
+
var drafting = !!input.value.trim();
|
|
9786
|
+
if (drafting !== CS.drafting) {
|
|
9787
|
+
CS.drafting = drafting;
|
|
9788
|
+
syncDraftingIndicator();
|
|
9789
|
+
scrollToBottomIfSticky(false);
|
|
9790
|
+
}
|
|
8491
9791
|
});
|
|
8492
9792
|
input.addEventListener("keydown", function(e) {
|
|
8493
9793
|
if (e.key === "Enter" && !e.shiftKey && !composing) {
|
|
@@ -8530,10 +9830,12 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8530
9830
|
chatArea = h("div", { class: "bq-chat" }, box, composer);
|
|
8531
9831
|
CS.chatEl = chatArea;
|
|
8532
9832
|
CS.composerEl = composer;
|
|
9833
|
+
updateComposerControls();
|
|
8533
9834
|
if (!attachDisabled) setupDragAndDrop(chatArea);
|
|
8534
9835
|
return h("div", { class: "bq-meta" }, header, chatArea);
|
|
8535
9836
|
});
|
|
8536
9837
|
if (S.aiPlatform === "none") return;
|
|
9838
|
+
void refreshIndexMarkers();
|
|
8537
9839
|
loadMarked().then(function() {
|
|
8538
9840
|
renderMessages();
|
|
8539
9841
|
return session.loadHistory(false, CS.gateRefreshToken);
|
|
@@ -8650,10 +9952,6 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8650
9952
|
);
|
|
8651
9953
|
});
|
|
8652
9954
|
}
|
|
8653
|
-
function agentBadgeText() {
|
|
8654
|
-
if (S.aiPlatform === "none") return "No agent configured";
|
|
8655
|
-
return S.serviceName || "BunnyQuery";
|
|
8656
|
-
}
|
|
8657
9955
|
function parseAiAgentValue2(value) {
|
|
8658
9956
|
var raw = (value || "").trim();
|
|
8659
9957
|
var platform = raw, model = "";
|
|
@@ -8811,6 +10109,19 @@ Index the REMAINING windows - one record per row/item, looking at any page image
|
|
|
8811
10109
|
clientSecretRequestHistory: function(p, f) {
|
|
8812
10110
|
return S.skapi.clientSecretRequestHistory(p, f);
|
|
8813
10111
|
},
|
|
10112
|
+
// Single-item csr-poll point lookup: how the engine hydrates a
|
|
10113
|
+
// compact history stub's real body when an indexing row expands.
|
|
10114
|
+
csrHistoryItemLookup: function(fullId, service, owner) {
|
|
10115
|
+
return S.skapi.util.request("csr-poll", { id: fullId, service, owner }, { auth: true });
|
|
10116
|
+
},
|
|
10117
|
+
// Durable index markers. Both read S lazily at call time — S.skapi /
|
|
10118
|
+
// S.projectId are not set yet when init() runs.
|
|
10119
|
+
mintIndexDoneMarker: function(info) {
|
|
10120
|
+
void mintIndexDoneMarkerDb(info.service, info.storagePath);
|
|
10121
|
+
},
|
|
10122
|
+
upsertIndexRunRecord: function(info) {
|
|
10123
|
+
void upsertIndexRunRecordDb(info.service, info.storagePath, info.patch);
|
|
10124
|
+
},
|
|
8814
10125
|
mcpBaseUrl: mcpBaseUrl(),
|
|
8815
10126
|
poll: 0,
|
|
8816
10127
|
// Server-driven windowed indexing. Off by default in the engine because the
|