bunnyquery 1.7.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bunnyquery.css +12 -0
- package/bunnyquery.js +337 -41
- package/dist/engine.cjs +351 -35
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +214 -12
- package/dist/engine.d.ts +214 -12
- package/dist/engine.mjs +340 -36
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/ai_agent.ts +80 -0
- package/src/engine/budget.ts +113 -7
- package/src/engine/history.ts +58 -18
- package/src/engine/host.ts +6 -0
- package/src/engine/index.ts +9 -0
- package/src/engine/indexing_groups.ts +50 -11
- package/src/engine/links.ts +35 -1
- package/src/engine/prompts/chat_system_prompt.ts +3 -0
- package/src/engine/requests.ts +11 -2
- package/src/engine/session.ts +220 -6
- package/src/engine/time.ts +34 -0
- package/styles/chat.css +12 -0
package/dist/engine.mjs
CHANGED
|
@@ -284,6 +284,9 @@ function buildChatSystemPrompt(params) {
|
|
|
284
284
|
You are a dedicated assistant for the project ID: "${formattedServiceId}".
|
|
285
285
|
Scope: Only answer questions about this project and its data. Do not answer questions about other projects or topics unrelated to this project. When the user refers to "my database", "my data", or "my files", treat those as references to this project's database and file storage.
|
|
286
286
|
Knowledge lookup: Before saying you don't know or that something isn't in the chat history, ALWAYS query this project's database through the available MCP tools to look for the answer. The user's data is the source of truth - the chat transcript is not. Only respond with "I don't know" or "I couldn't find that" after you have actually searched the project's data and come back empty.
|
|
287
|
+
Complete answers over stored data: The database holds one record per spreadsheet row, and each uploaded file becomes many records that usually share a table. So a question about the data almost always spans many records across several files. For any request that counts, sums, totals, lists every match, compares across records, finds which one, or asks whether something is present or ABSENT (for example "how many", "total spent", "which card", "is there any", "\uC5C6\uC5B4?", "\uD558\uB098\uB3C4 \uC5C6\uB098?"), you MUST read the COMPLETE matching set before answering. Query with fetch_all set to true, or page through getToolResponsePage until pagination.complete is true, across EVERY relevant table and EVERY relevant file. A single default query returns only the first page (about 50 records). That is a SAMPLE. Never treat it as the whole dataset.
|
|
288
|
+
Never assert absence from a partial read. Do not say "there is no X", "none", "not found", or "\uC544\uB2C8\uC694, \uC5C6\uC2B5\uB2C8\uB2E4" until a complete scan has come back empty. If you have not finished scanning every relevant table and file, keep querying instead of guessing. A confident "no" that later turns out wrong is worse than telling the user you are still checking.
|
|
289
|
+
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 and tag filters match only exact values or leading prefixes, not substrings, 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
290
|
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
291
|
- 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
292
|
- Most 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) have ALREADY had their text extracted on the server and inlined in the same message between the "BEGIN FILE CONTENT" / "END FILE CONTENT" markers - read it directly there and do NOT call web_fetch for those files. A "[skapi: ...]" note in that block means the file could not be extracted.
|
|
@@ -618,6 +621,15 @@ function repairUrlWhitespace(href) {
|
|
|
618
621
|
if (/^https?:\/\/[^/\s]+\/download\/[A-Za-z0-9_-]+(\.[A-Za-z0-9_-]+)?$/i.test(stripped)) return stripped;
|
|
619
622
|
return href.trim().replace(/\s/g, "%20");
|
|
620
623
|
}
|
|
624
|
+
function repairUrlEntities(href) {
|
|
625
|
+
if (!href || href.indexOf("&") === -1) return href;
|
|
626
|
+
var out = href, prev = "";
|
|
627
|
+
while (out !== prev) {
|
|
628
|
+
prev = out;
|
|
629
|
+
out = out.replace(/&/gi, "&").replace(/�*38;/g, "&").replace(/�*26;/gi, "&");
|
|
630
|
+
}
|
|
631
|
+
return out;
|
|
632
|
+
}
|
|
621
633
|
function normalizeTrailingInlineToken(value) {
|
|
622
634
|
if (!value) return value;
|
|
623
635
|
var out = value.replace(/[.,;:!?]+$/, "");
|
|
@@ -665,8 +677,9 @@ function classifyInlineLink(full, groups, ctx) {
|
|
|
665
677
|
var tail = full.slice(("src::" + rawPath).length);
|
|
666
678
|
var srcIsUrl = isHttpUrlLike(rawPath);
|
|
667
679
|
if (srcIsUrl && !isDbHost(rawPath) && !readExpiredAttachmentHref(rawPath)) {
|
|
680
|
+
var srcUrl = repairUrlEntities(rawPath);
|
|
668
681
|
return {
|
|
669
|
-
part: { type: "link", label: truncateLabelForDisplay(
|
|
682
|
+
part: { type: "link", label: truncateLabelForDisplay(srcUrl), fullLabel: srcUrl, href: srcUrl, expired: false },
|
|
670
683
|
tail
|
|
671
684
|
};
|
|
672
685
|
}
|
|
@@ -700,6 +713,7 @@ function classifyInlineLink(full, groups, ctx) {
|
|
|
700
713
|
}
|
|
701
714
|
var originalHref = g3 || g6 || "";
|
|
702
715
|
if (!originalHref) return null;
|
|
716
|
+
originalHref = repairUrlEntities(originalHref);
|
|
703
717
|
var urlTail;
|
|
704
718
|
if (!g3 && g6) {
|
|
705
719
|
var trimmedUrl = normalizeTrailingInlineToken(originalHref);
|
|
@@ -741,25 +755,74 @@ function truncateLabelForDisplay(label) {
|
|
|
741
755
|
// src/engine/budget.ts
|
|
742
756
|
var CONTEXT_WINDOW_DEFAULT = { claude: 2e5, openai: 128e3 };
|
|
743
757
|
var CONTEXT_WINDOW_BY_MODEL = {
|
|
744
|
-
|
|
758
|
+
// exact ids
|
|
759
|
+
"claude-opus-5": 1e6,
|
|
760
|
+
"claude-opus-4-8": 1e6,
|
|
761
|
+
"claude-opus-4-7": 1e6,
|
|
762
|
+
"claude-sonnet-5": 1e6,
|
|
763
|
+
"claude-sonnet-4-6": 1e6,
|
|
745
764
|
"claude-sonnet-4": 2e5,
|
|
746
|
-
"
|
|
765
|
+
"claude-haiku-4-5": 2e5,
|
|
766
|
+
"gpt-5.4": 128e3,
|
|
767
|
+
"gpt-5.6-luna": 128e3,
|
|
768
|
+
// family keys
|
|
769
|
+
"claude-opus": 1e6,
|
|
770
|
+
"claude-sonnet": 1e6,
|
|
771
|
+
"claude-haiku": 2e5,
|
|
772
|
+
"gpt-5.6": 128e3,
|
|
773
|
+
"gpt-5": 128e3
|
|
747
774
|
};
|
|
775
|
+
var apiReportedContextWindows = {};
|
|
776
|
+
function registerModelContextWindows(models) {
|
|
777
|
+
if (!Array.isArray(models)) return;
|
|
778
|
+
for (var i = 0; i < models.length; i++) {
|
|
779
|
+
var m = models[i];
|
|
780
|
+
var id = (m && m.id ? String(m.id) : "").trim().toLowerCase();
|
|
781
|
+
var reported = m ? Number(m.max_input_tokens) : NaN;
|
|
782
|
+
if (id && Number.isFinite(reported) && reported > 0) {
|
|
783
|
+
apiReportedContextWindows[id] = Math.floor(reported);
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
var projectContextWindows = {};
|
|
788
|
+
function setProjectContextWindow(serviceId, tokens) {
|
|
789
|
+
var key = (serviceId || "").trim();
|
|
790
|
+
if (!key) return;
|
|
791
|
+
var n = Number(tokens);
|
|
792
|
+
if (Number.isFinite(n) && n > 0) projectContextWindows[key] = Math.floor(n);
|
|
793
|
+
else delete projectContextWindows[key];
|
|
794
|
+
}
|
|
795
|
+
function getProjectContextWindow(serviceId) {
|
|
796
|
+
var key = (serviceId || "").trim();
|
|
797
|
+
return key && projectContextWindows[key] ? projectContextWindows[key] : null;
|
|
798
|
+
}
|
|
748
799
|
var OUTPUT_TOKEN_RESERVE = 22e3;
|
|
749
800
|
var TOOL_AND_RESPONSE_BUFFER = 4e3;
|
|
750
801
|
var MIN_INPUT_TOKEN_BUDGET = 8e3;
|
|
751
802
|
var CLAUDE_PER_REQUEST_INPUT_CAP = 28e3;
|
|
752
803
|
var MAX_HISTORY_MESSAGES = 20;
|
|
753
804
|
var HISTORY_TOKEN_BUDGET = 8e3;
|
|
805
|
+
var CLAUDE_INPUT_CAP_RATIO = 0.16;
|
|
806
|
+
var HISTORY_BUDGET_RATIO = 0.08;
|
|
754
807
|
function estimateTextTokens(text) {
|
|
755
808
|
return Math.ceil((text || "").length / 3);
|
|
756
809
|
}
|
|
757
810
|
function estimateMessageTokens(msg) {
|
|
758
811
|
return estimateTextTokens(msg.content) + estimateTextTokens(msg.role) + 6;
|
|
759
812
|
}
|
|
760
|
-
function getContextWindow(platform, model) {
|
|
813
|
+
function getContextWindow(platform, model, serviceId) {
|
|
814
|
+
var override = serviceId ? getProjectContextWindow(serviceId) : null;
|
|
815
|
+
if (override) return override;
|
|
761
816
|
var normalized = (model || "").trim().toLowerCase();
|
|
762
|
-
if (normalized
|
|
817
|
+
if (normalized) {
|
|
818
|
+
if (apiReportedContextWindows[normalized]) return apiReportedContextWindows[normalized];
|
|
819
|
+
if (CONTEXT_WINDOW_BY_MODEL[normalized]) return CONTEXT_WINDOW_BY_MODEL[normalized];
|
|
820
|
+
var parts = normalized.split("-");
|
|
821
|
+
for (var end = parts.length - 1; end > 0; end--) {
|
|
822
|
+
var family = parts.slice(0, end).join("-");
|
|
823
|
+
if (CONTEXT_WINDOW_BY_MODEL[family]) return CONTEXT_WINDOW_BY_MODEL[family];
|
|
824
|
+
}
|
|
825
|
+
}
|
|
763
826
|
return CONTEXT_WINDOW_DEFAULT[platform];
|
|
764
827
|
}
|
|
765
828
|
function stripFileBlocksFromHistory(content) {
|
|
@@ -767,15 +830,19 @@ function stripFileBlocksFromHistory(content) {
|
|
|
767
830
|
return content.replace(/```([^\n`]+?\.[^\s.`]+)\n[\s\S]*?```/g, "[file previously attached: $1]");
|
|
768
831
|
}
|
|
769
832
|
function buildBoundedChatMessages(options) {
|
|
770
|
-
var contextWindow = getContextWindow(options.platform, options.model);
|
|
833
|
+
var contextWindow = getContextWindow(options.platform, options.model, options.serviceId);
|
|
771
834
|
var contextBasedBudget = Math.max(
|
|
772
835
|
MIN_INPUT_TOKEN_BUDGET,
|
|
773
836
|
contextWindow - OUTPUT_TOKEN_RESERVE - TOOL_AND_RESPONSE_BUFFER
|
|
774
837
|
);
|
|
775
|
-
var
|
|
838
|
+
var scaled = !!(options.serviceId && getProjectContextWindow(options.serviceId));
|
|
839
|
+
var claudeInputCap = scaled ? Math.max(CLAUDE_PER_REQUEST_INPUT_CAP, Math.round(contextBasedBudget * CLAUDE_INPUT_CAP_RATIO)) : CLAUDE_PER_REQUEST_INPUT_CAP;
|
|
840
|
+
var availableInputBudget = options.platform === "claude" ? Math.min(contextBasedBudget, claudeInputCap) : contextBasedBudget;
|
|
776
841
|
var systemCost = estimateTextTokens(options.systemPrompt) + 12;
|
|
777
|
-
var
|
|
778
|
-
var
|
|
842
|
+
var historyAllowance = scaled ? Math.max(HISTORY_TOKEN_BUDGET, Math.round(contextBasedBudget * HISTORY_BUDGET_RATIO)) : HISTORY_TOKEN_BUDGET;
|
|
843
|
+
var budgetForHistory = Math.max(1e3, Math.min(historyAllowance, availableInputBudget - systemCost));
|
|
844
|
+
var maxHistoryMessages = scaled ? Math.max(MAX_HISTORY_MESSAGES, Math.round(MAX_HISTORY_MESSAGES * (budgetForHistory / HISTORY_TOKEN_BUDGET))) : MAX_HISTORY_MESSAGES;
|
|
845
|
+
var windowed = options.history.slice(-maxHistoryMessages);
|
|
779
846
|
var latestIndex = windowed.length - 1;
|
|
780
847
|
var trimmed = windowed.map(function(m, i2) {
|
|
781
848
|
if (i2 === latestIndex) return m;
|
|
@@ -800,6 +867,60 @@ function buildBoundedChatMessages(options) {
|
|
|
800
867
|
};
|
|
801
868
|
}
|
|
802
869
|
|
|
870
|
+
// src/engine/time.ts
|
|
871
|
+
function wallClockNow() {
|
|
872
|
+
return Date.now();
|
|
873
|
+
}
|
|
874
|
+
function formatChatTimestamp(ms) {
|
|
875
|
+
if (typeof ms !== "number" || !isFinite(ms) || ms <= 0) return "";
|
|
876
|
+
try {
|
|
877
|
+
return new Date(ms).toLocaleString(void 0, {
|
|
878
|
+
year: "numeric",
|
|
879
|
+
month: "short",
|
|
880
|
+
day: "numeric",
|
|
881
|
+
hour: "numeric",
|
|
882
|
+
minute: "2-digit",
|
|
883
|
+
second: "2-digit"
|
|
884
|
+
});
|
|
885
|
+
} catch (e) {
|
|
886
|
+
return "";
|
|
887
|
+
}
|
|
888
|
+
}
|
|
889
|
+
|
|
890
|
+
// src/engine/ai_agent.ts
|
|
891
|
+
function normalizePlatform(raw) {
|
|
892
|
+
var p = (raw || "").trim().toLowerCase();
|
|
893
|
+
return p === "claude" || p === "openai" ? p : null;
|
|
894
|
+
}
|
|
895
|
+
function parseAiAgentValue(value) {
|
|
896
|
+
var raw = (value || "").trim();
|
|
897
|
+
if (!raw || raw.toLowerCase() === "none") {
|
|
898
|
+
return { platform: null, model: "", contextWindow: null, hasPlatform: false };
|
|
899
|
+
}
|
|
900
|
+
var firstHash = raw.indexOf("#");
|
|
901
|
+
if (firstHash === -1) {
|
|
902
|
+
var only = normalizePlatform(raw);
|
|
903
|
+
return { platform: only, model: "", contextWindow: null, hasPlatform: !!only };
|
|
904
|
+
}
|
|
905
|
+
var platform = normalizePlatform(raw.slice(0, firstHash));
|
|
906
|
+
var rest = raw.slice(firstHash + 1);
|
|
907
|
+
var secondHash = rest.indexOf("#");
|
|
908
|
+
var model = (secondHash === -1 ? rest : rest.slice(0, secondHash)).trim();
|
|
909
|
+
var windowRaw = secondHash === -1 ? "" : rest.slice(secondHash + 1).trim();
|
|
910
|
+
var parsedWindow = windowRaw ? Number(windowRaw) : NaN;
|
|
911
|
+
var contextWindow = Number.isFinite(parsedWindow) && parsedWindow > 0 ? Math.floor(parsedWindow) : null;
|
|
912
|
+
return { platform, model, contextWindow, hasPlatform: !!platform };
|
|
913
|
+
}
|
|
914
|
+
function buildAiAgentValue(platform, model, contextWindow) {
|
|
915
|
+
var p = (platform || "").trim().toLowerCase();
|
|
916
|
+
if (!p || p === "none") return "none";
|
|
917
|
+
var m = (model || "").trim();
|
|
918
|
+
if (!m) return p;
|
|
919
|
+
var n = Number(contextWindow);
|
|
920
|
+
if (!Number.isFinite(n) || n <= 0) return p + "#" + m;
|
|
921
|
+
return p + "#" + m + "#" + Math.floor(n);
|
|
922
|
+
}
|
|
923
|
+
|
|
803
924
|
// src/engine/requests.ts
|
|
804
925
|
var ANTHROPIC_MESSAGES_API_URL = "https://api.anthropic.com/v1/messages";
|
|
805
926
|
var ANTHROPIC_MODELS_API_URL = "https://api.anthropic.com/v1/models";
|
|
@@ -1308,7 +1429,8 @@ async function getChatHistory(params, fetchOptions) {
|
|
|
1308
1429
|
method: "POST"
|
|
1309
1430
|
},
|
|
1310
1431
|
{ service: params.service, owner: params.owner },
|
|
1311
|
-
params.queue ? { queue: params.queue } : {}
|
|
1432
|
+
params.queue ? { queue: params.queue } : {},
|
|
1433
|
+
params.status ? { status: params.status } : {}
|
|
1312
1434
|
);
|
|
1313
1435
|
return chatEngineConfig().clientSecretRequestHistory(
|
|
1314
1436
|
p,
|
|
@@ -1345,6 +1467,25 @@ function extractLastUserTextFromRequest(requestBody) {
|
|
|
1345
1467
|
}
|
|
1346
1468
|
return "";
|
|
1347
1469
|
}
|
|
1470
|
+
function isIndexingRequestText(userText) {
|
|
1471
|
+
if (typeof userText !== "string") return false;
|
|
1472
|
+
return userText.indexOf("A new file has just been uploaded") === 0 || userText.indexOf("CONTINUE indexing") === 0;
|
|
1473
|
+
}
|
|
1474
|
+
function parseIndexingRequestText(userText) {
|
|
1475
|
+
if (typeof userText !== "string" || !userText) return null;
|
|
1476
|
+
var nameMatch = userText.match(/^- name: (.+)$/m);
|
|
1477
|
+
if (!nameMatch) return null;
|
|
1478
|
+
var mimeMatch = userText.match(/^- mime type: (.+)$/m);
|
|
1479
|
+
var sizeMatch = userText.match(/^- size \(bytes\): (\d+)$/m);
|
|
1480
|
+
var pathMatch = userText.match(/^- storage path: (.+)$/m);
|
|
1481
|
+
return {
|
|
1482
|
+
name: nameMatch[1].trim(),
|
|
1483
|
+
path: pathMatch ? pathMatch[1].trim() : void 0,
|
|
1484
|
+
mime: mimeMatch ? mimeMatch[1].trim() : void 0,
|
|
1485
|
+
size: sizeMatch ? Number(sizeMatch[1]) : void 0,
|
|
1486
|
+
continued: userText.indexOf("CONTINUE indexing") === 0
|
|
1487
|
+
};
|
|
1488
|
+
}
|
|
1348
1489
|
function mapHistoryListToMessages(list, platform, opts) {
|
|
1349
1490
|
var mapped = [], runningItemIds = [];
|
|
1350
1491
|
var extractAssistantText = platform === "openai" ? extractOpenAIText : extractClaudeText;
|
|
@@ -1361,31 +1502,25 @@ function mapHistoryListToMessages(list, platform, opts) {
|
|
|
1361
1502
|
var assistantText = isPending ? "" : (extractAssistantText(response) || "").trim() || "";
|
|
1362
1503
|
var isErrorResponse = !isPending && (isFailed || isErrorResponseBody(response));
|
|
1363
1504
|
var serverItemId = item && typeof item.id === "string" && item.id ? item.id : void 0;
|
|
1505
|
+
var createdTs = Number(item && item.created);
|
|
1506
|
+
var updatedTs = Number(item && item.updated);
|
|
1507
|
+
var userTs = isFinite(createdTs) && createdTs > 0 ? createdTs : isFinite(updatedTs) && updatedTs > 0 ? updatedTs : void 0;
|
|
1508
|
+
var replyTs = isFinite(updatedTs) && updatedTs > 0 ? updatedTs : isFinite(createdTs) && createdTs > 0 ? createdTs : void 0;
|
|
1364
1509
|
if (userText) {
|
|
1365
1510
|
var displayContent;
|
|
1366
1511
|
var indexFile = void 0;
|
|
1367
1512
|
if (item._isBgTask) {
|
|
1368
|
-
var
|
|
1369
|
-
if (
|
|
1370
|
-
var mimeMatch = userText.match(/^- mime type: (.+)$/m);
|
|
1371
|
-
var sizeMatch = userText.match(/^- size \(bytes\): (\d+)$/m);
|
|
1372
|
-
var pathMatch = userText.match(/^- storage path: (.+)$/m);
|
|
1373
|
-
var isContinuePass = userText.indexOf("CONTINUE indexing") === 0;
|
|
1513
|
+
var ref = parseIndexingRequestText(userText);
|
|
1514
|
+
if (ref) {
|
|
1374
1515
|
displayContent = opts.formatIndexingLabel(
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1516
|
+
ref.name,
|
|
1517
|
+
ref.mime || "",
|
|
1518
|
+
typeof ref.size === "number" ? ref.size : null,
|
|
1519
|
+
ref.path,
|
|
1379
1520
|
false,
|
|
1380
|
-
|
|
1521
|
+
ref.continued
|
|
1381
1522
|
);
|
|
1382
|
-
indexFile =
|
|
1383
|
-
name: nameMatch[1].trim(),
|
|
1384
|
-
path: pathMatch ? pathMatch[1].trim() : void 0,
|
|
1385
|
-
mime: mimeMatch ? mimeMatch[1].trim() : void 0,
|
|
1386
|
-
size: sizeMatch ? Number(sizeMatch[1]) : void 0,
|
|
1387
|
-
continued: isContinuePass
|
|
1388
|
-
};
|
|
1523
|
+
indexFile = ref;
|
|
1389
1524
|
} else {
|
|
1390
1525
|
displayContent = userText;
|
|
1391
1526
|
}
|
|
@@ -1400,6 +1535,7 @@ function mapHistoryListToMessages(list, platform, opts) {
|
|
|
1400
1535
|
if (indexFile) userMsg._indexFile = indexFile;
|
|
1401
1536
|
if (item._isOnBgQueue) userMsg._useBgQueue = true;
|
|
1402
1537
|
if (serverItemId !== void 0) userMsg._serverItemId = serverItemId;
|
|
1538
|
+
if (userTs !== void 0) userMsg._ts = userTs;
|
|
1403
1539
|
mapped.push(userMsg);
|
|
1404
1540
|
}
|
|
1405
1541
|
if (isCancelledItem) ; else if (isInProcess) {
|
|
@@ -1414,11 +1550,13 @@ function mapHistoryListToMessages(list, platform, opts) {
|
|
|
1414
1550
|
var em = { role: "assistant", content: getErrorMessage(response), isError: true };
|
|
1415
1551
|
if (item._isBgTask) em.isBackgroundTask = true;
|
|
1416
1552
|
if (serverItemId !== void 0) em._serverItemId = serverItemId;
|
|
1553
|
+
if (replyTs !== void 0) em._ts = replyTs;
|
|
1417
1554
|
mapped.push(em);
|
|
1418
1555
|
} else if (assistantText) {
|
|
1419
1556
|
var okm = { role: "assistant", content: sanitizeAttachmentLinksForHistory(assistantText, opts.serviceId, true) };
|
|
1420
1557
|
if (item._isBgTask) okm.isBackgroundTask = true;
|
|
1421
1558
|
if (serverItemId !== void 0) okm._serverItemId = serverItemId;
|
|
1559
|
+
if (replyTs !== void 0) okm._ts = replyTs;
|
|
1422
1560
|
mapped.push(okm);
|
|
1423
1561
|
}
|
|
1424
1562
|
});
|
|
@@ -1511,6 +1649,8 @@ function createHistoryFiller(base) {
|
|
|
1511
1649
|
}
|
|
1512
1650
|
|
|
1513
1651
|
// src/engine/session.ts
|
|
1652
|
+
var WORKER_PASS_ADOPT_LIMIT = 20;
|
|
1653
|
+
var WORKER_PASS_ADOPT_ATTEMPTS = [0, 2e3, 6e3];
|
|
1514
1654
|
var _g = typeof globalThis !== "undefined" ? globalThis : {};
|
|
1515
1655
|
function nowMs() {
|
|
1516
1656
|
return _g.performance && typeof _g.performance.now === "function" ? _g.performance.now() : Date.now();
|
|
@@ -1530,6 +1670,35 @@ function isPollStopped(res) {
|
|
|
1530
1670
|
var ChatSession = class {
|
|
1531
1671
|
constructor(host) {
|
|
1532
1672
|
this.typewriterQueue = Promise.resolve();
|
|
1673
|
+
/**
|
|
1674
|
+
* Pick up indexing passes the WORKER minted, which no client ever dispatched.
|
|
1675
|
+
*
|
|
1676
|
+
* For a PDF (and for text/grid when windowed indexing is on) the worker writes
|
|
1677
|
+
* pass N+1's row itself, inside pass N's invocation, right after saving pass
|
|
1678
|
+
* N's result. That row reaches this client through nothing at all: it is not in
|
|
1679
|
+
* bgTaskQueue (the client never asked for it) and it is not in state.messages
|
|
1680
|
+
* (only a first-page history load maps it in, which happens on mount, project
|
|
1681
|
+
* switch or tab return). So between two worker passes every pass the client
|
|
1682
|
+
* knows about is settled, and the collapsed row renders "Indexed N passes"
|
|
1683
|
+
* with no spinner and no Stop, for a file that is still being read. A user who
|
|
1684
|
+
* believes that then asks questions against a half-indexed file — which is what
|
|
1685
|
+
* this exists to prevent.
|
|
1686
|
+
*
|
|
1687
|
+
* So when a background indexing pass settles, ask the bg queue what is still
|
|
1688
|
+
* unresolved on it and adopt anything unknown as an ordinary BgTaskEntry.
|
|
1689
|
+
* drainBgTaskQueue then treats it exactly like a pass this client dispatched:
|
|
1690
|
+
* same bubble, same `_indexFile` (so it joins the file's collapsed row), same
|
|
1691
|
+
* poll — and that poll settling runs this again, so the chain is followed to
|
|
1692
|
+
* its end. `status`-scoped so the reply carries the live items only, never a
|
|
1693
|
+
* page of finished ones with their bodies.
|
|
1694
|
+
*
|
|
1695
|
+
* Termination: the only trigger is a pass SETTLING, which happens once per
|
|
1696
|
+
* pass. An adopted item that is still running is not re-adopted (its id is
|
|
1697
|
+
* already polled), and when the queue holds nothing unknown the chain stops on
|
|
1698
|
+
* its own. Nothing here is periodic — a timer that re-reads history is the
|
|
1699
|
+
* shape that previously looped fetchHistoryPage after an already-DONE index.
|
|
1700
|
+
*/
|
|
1701
|
+
this._adoptingWorkerPasses = false;
|
|
1533
1702
|
this.host = host;
|
|
1534
1703
|
this.state = {
|
|
1535
1704
|
messages: [],
|
|
@@ -1831,7 +2000,7 @@ var ChatSession = class {
|
|
|
1831
2000
|
var offExisting = this.aiChatHistoryCache[key] || { messages: [], endOfList: false, startKeyHistory: [] };
|
|
1832
2001
|
this.aiChatHistoryCache[key] = {
|
|
1833
2002
|
messages: offExisting.messages.concat([
|
|
1834
|
-
{ role: "user", content: composed, _ownerKey: key },
|
|
2003
|
+
{ role: "user", content: composed, _ownerKey: key, _ts: wallClockNow() },
|
|
1835
2004
|
{ role: "assistant", content: "", isPending: true, isPendingInProcess: true, _ownerKey: key }
|
|
1836
2005
|
]),
|
|
1837
2006
|
endOfList: offExisting.endOfList,
|
|
@@ -1863,7 +2032,7 @@ var ChatSession = class {
|
|
|
1863
2032
|
serviceId: id.serviceId,
|
|
1864
2033
|
history: resolvedHistory.concat([{ role: "user", content: llmComposed }])
|
|
1865
2034
|
});
|
|
1866
|
-
var queuedBubble = { role: "user", content: composed, isPendingQueued: true, isSendingToServer: true };
|
|
2035
|
+
var queuedBubble = { role: "user", content: composed, isPendingQueued: true, isSendingToServer: true, _ts: wallClockNow() };
|
|
1867
2036
|
if (key) queuedBubble._ownerKey = key;
|
|
1868
2037
|
if (useBgQueue) queuedBubble._useBgQueue = true;
|
|
1869
2038
|
this.state.messages.push(queuedBubble);
|
|
@@ -1898,7 +2067,7 @@ var ChatSession = class {
|
|
|
1898
2067
|
});
|
|
1899
2068
|
return;
|
|
1900
2069
|
}
|
|
1901
|
-
this.state.messages.push({ role: "user", content: composed, ...key ? { _ownerKey: key } : {} });
|
|
2070
|
+
this.state.messages.push({ role: "user", content: composed, _ts: wallClockNow(), ...key ? { _ownerKey: key } : {} });
|
|
1902
2071
|
this.state.messages.push({ role: "assistant", content: "", isPending: true, isPendingInProcess: true, ...key ? { _ownerKey: key } : {} });
|
|
1903
2072
|
this.host.notify();
|
|
1904
2073
|
this.updateHistoryCache();
|
|
@@ -1955,6 +2124,7 @@ var ChatSession = class {
|
|
|
1955
2124
|
var existing = this.state.messages[nextIdx];
|
|
1956
2125
|
var promoted = { role: "user", content: existing.content, isPendingInProcess: true, isBackgroundTask: true };
|
|
1957
2126
|
if (existing._indexFile) promoted._indexFile = existing._indexFile;
|
|
2127
|
+
if (existing._ts !== void 0) promoted._ts = existing._ts;
|
|
1958
2128
|
if (existing._serverItemId !== void 0) promoted._serverItemId = existing._serverItemId;
|
|
1959
2129
|
if (existing._ownerKey !== void 0) promoted._ownerKey = existing._ownerKey;
|
|
1960
2130
|
this.state.messages[nextIdx] = promoted;
|
|
@@ -1976,6 +2146,7 @@ var ChatSession = class {
|
|
|
1976
2146
|
var promoted = { role: "user", content: existing.content, isPendingInProcess: true };
|
|
1977
2147
|
if (existing.isBackgroundTask) promoted.isBackgroundTask = true;
|
|
1978
2148
|
if (existing._indexFile) promoted._indexFile = existing._indexFile;
|
|
2149
|
+
if (existing._ts !== void 0) promoted._ts = existing._ts;
|
|
1979
2150
|
if (existing._serverItemId !== void 0) promoted._serverItemId = existing._serverItemId;
|
|
1980
2151
|
if (existing._ownerKey !== void 0) promoted._ownerKey = existing._ownerKey;
|
|
1981
2152
|
if (existing.isSendingToServer) promoted.isSendingToServer = true;
|
|
@@ -2025,6 +2196,7 @@ var ChatSession = class {
|
|
|
2025
2196
|
var repl = { role: "user", content: exist.content };
|
|
2026
2197
|
if (exist._serverItemId !== void 0) repl._serverItemId = exist._serverItemId;
|
|
2027
2198
|
if (exist._ownerKey !== void 0) repl._ownerKey = exist._ownerKey;
|
|
2199
|
+
if (exist._ts !== void 0) repl._ts = exist._ts;
|
|
2028
2200
|
this.state.messages[userIdx] = repl;
|
|
2029
2201
|
}
|
|
2030
2202
|
var thinkingIdx = userIdx >= 0 ? this.state.messages.findIndex(function(m, i) {
|
|
@@ -2033,6 +2205,7 @@ var ChatSession = class {
|
|
|
2033
2205
|
return thinkingIdx !== -1 ? thinkingIdx : userIdx >= 0 ? userIdx + 1 : -1;
|
|
2034
2206
|
}
|
|
2035
2207
|
insertAtTarget(msg, targetIdx) {
|
|
2208
|
+
if (msg && msg.role === "assistant" && msg._ts === void 0) msg._ts = wallClockNow();
|
|
2036
2209
|
if (targetIdx >= 0 && this.state.messages[targetIdx] && this.state.messages[targetIdx].isPending) this.state.messages[targetIdx] = msg;
|
|
2037
2210
|
else if (targetIdx >= 0) this.state.messages.splice(targetIdx, 0, msg);
|
|
2038
2211
|
else this.state.messages.push(msg);
|
|
@@ -2369,6 +2542,8 @@ var ChatSession = class {
|
|
|
2369
2542
|
}
|
|
2370
2543
|
enqueueTypewrite(idx, fullText, localId) {
|
|
2371
2544
|
var self = this;
|
|
2545
|
+
var target = this.state.messages[idx];
|
|
2546
|
+
if (target && target._ts === void 0) target._ts = wallClockNow();
|
|
2372
2547
|
this.typewriterQueue = this.typewriterQueue.then(function() {
|
|
2373
2548
|
return self.typewriteIntoIndex(idx, fullText, localId);
|
|
2374
2549
|
});
|
|
@@ -2438,6 +2613,7 @@ var ChatSession = class {
|
|
|
2438
2613
|
var u = this.state.messages[uIdx];
|
|
2439
2614
|
var cleaned = { role: "user", content: u.content, _serverItemId: itemId };
|
|
2440
2615
|
if (u.isBackgroundTask) cleaned.isBackgroundTask = true;
|
|
2616
|
+
if (u._ts !== void 0) cleaned._ts = u._ts;
|
|
2441
2617
|
if (u._indexFile) cleaned._indexFile = u._indexFile;
|
|
2442
2618
|
this.state.messages[uIdx] = cleaned;
|
|
2443
2619
|
}
|
|
@@ -2468,8 +2644,21 @@ var ChatSession = class {
|
|
|
2468
2644
|
}
|
|
2469
2645
|
// --- background-task resolution + drain -------------------------------
|
|
2470
2646
|
handleHistoryItemResolution(itemId, response, platform) {
|
|
2647
|
+
var indexRef = this._indexRefOfItem(itemId);
|
|
2471
2648
|
this.applyHistoryItemResolution(itemId, response, platform);
|
|
2472
2649
|
this.promoteNextBgQueuedToRunning();
|
|
2650
|
+
if (indexRef) this._followWorkerIndexingChain(indexRef.name, indexRef.mime);
|
|
2651
|
+
}
|
|
2652
|
+
/** The file an already-rendered background pass is about, off its request
|
|
2653
|
+
* bubble. Null for an ordinary turn, which is most of them. */
|
|
2654
|
+
_indexRefOfItem(itemId) {
|
|
2655
|
+
if (!itemId) return null;
|
|
2656
|
+
for (var i = 0; i < this.state.messages.length; i++) {
|
|
2657
|
+
var m = this.state.messages[i];
|
|
2658
|
+
if (m._serverItemId !== itemId || m.role !== "user" || !m.isBackgroundTask) continue;
|
|
2659
|
+
return m._indexFile || null;
|
|
2660
|
+
}
|
|
2661
|
+
return null;
|
|
2473
2662
|
}
|
|
2474
2663
|
applyHistoryItemResolution(itemId, response, platform) {
|
|
2475
2664
|
this.historyItemPolls.delete(itemId);
|
|
@@ -2510,6 +2699,7 @@ var ChatSession = class {
|
|
|
2510
2699
|
var ex = this.state.messages[userIdx];
|
|
2511
2700
|
var settledUser = { role: "user", content: ex.content, _serverItemId: itemId };
|
|
2512
2701
|
if (ex.isBackgroundTask) settledUser.isBackgroundTask = true;
|
|
2702
|
+
if (ex._ts !== void 0) settledUser._ts = ex._ts;
|
|
2513
2703
|
if (ex._indexFile) settledUser._indexFile = ex._indexFile;
|
|
2514
2704
|
if (ex._useBgQueue) settledUser._useBgQueue = true;
|
|
2515
2705
|
this.state.messages[userIdx] = settledUser;
|
|
@@ -2602,6 +2792,116 @@ var ChatSession = class {
|
|
|
2602
2792
|
self.cancelQueuedMessage(t.msg, idx === -1 ? t.idx : idx);
|
|
2603
2793
|
});
|
|
2604
2794
|
}
|
|
2795
|
+
/**
|
|
2796
|
+
* True when the WORKER, not this client, drives the rest of this file's chain.
|
|
2797
|
+
* The mirror image of the early returns in maybeResumeIndexing: whatever that
|
|
2798
|
+
* refuses to continue is exactly what nothing client-side is tracking.
|
|
2799
|
+
*/
|
|
2800
|
+
_isWorkerDrivenIndexing(filename, mime) {
|
|
2801
|
+
if (!isPagedReadFile(filename, mime)) return false;
|
|
2802
|
+
if (isImageVisionFile(filename, mime)) return true;
|
|
2803
|
+
return windowedIndexingEnabled() && isWindowedReadFile(filename, mime);
|
|
2804
|
+
}
|
|
2805
|
+
_adoptWorkerIndexingPasses(attempt) {
|
|
2806
|
+
var self = this;
|
|
2807
|
+
if (this._adoptingWorkerPasses) return;
|
|
2808
|
+
var id = this.host.getIdentity();
|
|
2809
|
+
var platform = id.platform;
|
|
2810
|
+
if (!id.serviceId || platform !== "claude" && platform !== "openai") return;
|
|
2811
|
+
if (this.isPollingPaused() || !this.host.isViewMounted()) return;
|
|
2812
|
+
var svcId = id.serviceId, owner = id.owner;
|
|
2813
|
+
var queue = (id.userId || id.serviceId) + BG_INDEXING_QUEUE_SUFFIX;
|
|
2814
|
+
var ask = function(status) {
|
|
2815
|
+
return Promise.resolve(getChatHistory(
|
|
2816
|
+
{ service: svcId, owner, platform, queue, status },
|
|
2817
|
+
{ limit: WORKER_PASS_ADOPT_LIMIT }
|
|
2818
|
+
)).catch(function() {
|
|
2819
|
+
return null;
|
|
2820
|
+
});
|
|
2821
|
+
};
|
|
2822
|
+
this._adoptingWorkerPasses = true;
|
|
2823
|
+
Promise.all([ask("running"), ask("pending")]).then(function(results) {
|
|
2824
|
+
self._adoptingWorkerPasses = false;
|
|
2825
|
+
var now = self.host.getIdentity();
|
|
2826
|
+
if (now.serviceId !== svcId || now.platform !== platform) return;
|
|
2827
|
+
if (!self.host.isViewMounted()) return;
|
|
2828
|
+
var adoptedIds = [];
|
|
2829
|
+
for (var ri = 0; ri < results.length; ri++) {
|
|
2830
|
+
var list = results[ri] && Array.isArray(results[ri].list) ? results[ri].list : [];
|
|
2831
|
+
for (var i = 0; i < list.length; i++) {
|
|
2832
|
+
if (self._adoptWorkerIndexingItem(list[i], svcId, platform)) adoptedIds.push(list[i].id);
|
|
2833
|
+
}
|
|
2834
|
+
}
|
|
2835
|
+
if (adoptedIds.length) {
|
|
2836
|
+
self.drainBgTaskQueue();
|
|
2837
|
+
if (self._isTrackingAny(adoptedIds)) return;
|
|
2838
|
+
}
|
|
2839
|
+
if (attempt + 1 >= WORKER_PASS_ADOPT_ATTEMPTS.length) return;
|
|
2840
|
+
setTimeout(function() {
|
|
2841
|
+
var later = self.host.getIdentity();
|
|
2842
|
+
if (later.serviceId !== svcId || later.platform !== platform) return;
|
|
2843
|
+
if (self.isPollingPaused() || !self.host.isViewMounted()) return;
|
|
2844
|
+
self._adoptWorkerIndexingPasses(attempt + 1);
|
|
2845
|
+
}, WORKER_PASS_ADOPT_ATTEMPTS[attempt + 1]);
|
|
2846
|
+
}, function() {
|
|
2847
|
+
self._adoptingWorkerPasses = false;
|
|
2848
|
+
});
|
|
2849
|
+
}
|
|
2850
|
+
/** Any of these ids still queued or still polled, i.e. surviving work. */
|
|
2851
|
+
_isTrackingAny(ids) {
|
|
2852
|
+
for (var i = 0; i < ids.length; i++) {
|
|
2853
|
+
if (this.historyItemPolls.has(ids[i])) return true;
|
|
2854
|
+
for (var q = 0; q < this.bgTaskQueue.length; q++) {
|
|
2855
|
+
if (this.bgTaskQueue[q].id === ids[i]) return true;
|
|
2856
|
+
}
|
|
2857
|
+
}
|
|
2858
|
+
return false;
|
|
2859
|
+
}
|
|
2860
|
+
/** One live bg-queue item -> a BgTaskEntry, if it is an indexing pass this
|
|
2861
|
+
* client is not already tracking. Returns whether it was adopted. */
|
|
2862
|
+
_adoptWorkerIndexingItem(item, svcId, platform) {
|
|
2863
|
+
if (!item || typeof item.id !== "string" || !item.id) return false;
|
|
2864
|
+
if (item.status !== "pending" && item.status !== "running") return false;
|
|
2865
|
+
if (typeof item.poll !== "function") return false;
|
|
2866
|
+
if (this.historyItemPolls.has(item.id)) return false;
|
|
2867
|
+
for (var q = 0; q < this.bgTaskQueue.length; q++) {
|
|
2868
|
+
if (this.bgTaskQueue[q].id === item.id) return false;
|
|
2869
|
+
}
|
|
2870
|
+
for (var m = 0; m < this.state.messages.length; m++) {
|
|
2871
|
+
var msg = this.state.messages[m];
|
|
2872
|
+
if (msg._serverItemId !== item.id) continue;
|
|
2873
|
+
if (!(msg.isPending || msg.isPendingInProcess || msg.isPendingQueued)) return false;
|
|
2874
|
+
}
|
|
2875
|
+
var body = item.request_body;
|
|
2876
|
+
if (!body || typeof body !== "object") return false;
|
|
2877
|
+
if (platform === "claude" ? !Array.isArray(body.messages) : !Array.isArray(body.input)) return false;
|
|
2878
|
+
var userText = extractLastUserTextFromRequest(body);
|
|
2879
|
+
if (!isIndexingRequestText(userText)) return false;
|
|
2880
|
+
var ref = parseIndexingRequestText(userText);
|
|
2881
|
+
if (!ref || !ref.name) return false;
|
|
2882
|
+
if (!this._isWorkerDrivenIndexing(ref.name, ref.mime)) return false;
|
|
2883
|
+
this.bgTaskQueue.push({
|
|
2884
|
+
serviceId: svcId,
|
|
2885
|
+
platform,
|
|
2886
|
+
id: item.id,
|
|
2887
|
+
filename: ref.name,
|
|
2888
|
+
storagePath: ref.path,
|
|
2889
|
+
mime: ref.mime,
|
|
2890
|
+
size: ref.size,
|
|
2891
|
+
status: item.status === "running" ? "running" : "pending",
|
|
2892
|
+
poll: item.poll,
|
|
2893
|
+
// Drives the "(continuing)" label and marks this as a continuation for
|
|
2894
|
+
// _applyIndexCancellations, which must not read a CONTINUE pass as the
|
|
2895
|
+
// fresh first pass that lifts a stop.
|
|
2896
|
+
resumePass: ref.continued ? 1 : 0
|
|
2897
|
+
});
|
|
2898
|
+
return true;
|
|
2899
|
+
}
|
|
2900
|
+
/** Follow the chain on from a background indexing pass that just settled. */
|
|
2901
|
+
_followWorkerIndexingChain(filename, mime) {
|
|
2902
|
+
if (!this._isWorkerDrivenIndexing(filename, mime)) return;
|
|
2903
|
+
this._adoptWorkerIndexingPasses(0);
|
|
2904
|
+
}
|
|
2605
2905
|
/** Best-effort server-side cancel of a bg-queue item that has no bubble (so
|
|
2606
2906
|
* cancelQueuedMessage, which drives one, has nothing to act on). */
|
|
2607
2907
|
_cancelServerItem(serverId) {
|
|
@@ -2812,8 +3112,7 @@ var ChatSession = class {
|
|
|
2812
3112
|
var chatList = history && Array.isArray(history.list) ? history.list : [];
|
|
2813
3113
|
chatList.forEach(function(item) {
|
|
2814
3114
|
if (isBgIndexingQueue(item.queue_name)) {
|
|
2815
|
-
|
|
2816
|
-
if (typeof userText === "string" && (userText.indexOf("A new file has just been uploaded") === 0 || userText.indexOf("CONTINUE indexing") === 0)) item._isBgTask = true;
|
|
3115
|
+
if (isIndexingRequestText(extractLastUserTextFromRequest(item.request_body))) item._isBgTask = true;
|
|
2817
3116
|
else item._isOnBgQueue = true;
|
|
2818
3117
|
}
|
|
2819
3118
|
});
|
|
@@ -3286,7 +3585,10 @@ function buildChatDisplayList(messages, opts) {
|
|
|
3286
3585
|
cancellableIds: [],
|
|
3287
3586
|
cancelling: false,
|
|
3288
3587
|
mayHaveOlder: false,
|
|
3289
|
-
|
|
3588
|
+
// The run's first loaded pass, and never re-stamped: see the file
|
|
3589
|
+
// docstring. `anchorId` is filled in once every member is known.
|
|
3590
|
+
anchorIndex: i,
|
|
3591
|
+
anchorId: ""
|
|
3290
3592
|
};
|
|
3291
3593
|
order.push(runId);
|
|
3292
3594
|
}
|
|
@@ -3300,7 +3602,6 @@ function buildChatDisplayList(messages, opts) {
|
|
|
3300
3602
|
g.passCount++;
|
|
3301
3603
|
}
|
|
3302
3604
|
g.members.push({ msg, index: i });
|
|
3303
|
-
g.anchorIndex = i;
|
|
3304
3605
|
runOfIndex[i] = runId;
|
|
3305
3606
|
if (msg._serverItemId) runByItemId[msg._serverItemId] = runId;
|
|
3306
3607
|
if (ref && ref.name) keyByName[ref.name] = g.key;
|
|
@@ -3362,6 +3663,9 @@ function buildChatDisplayList(messages, opts) {
|
|
|
3362
3663
|
}
|
|
3363
3664
|
}
|
|
3364
3665
|
grp.mayHaveOlder = !sawFirstPass && hasMoreHistory;
|
|
3666
|
+
var anchor = grp.members[0];
|
|
3667
|
+
grp.anchorIndex = anchor.index;
|
|
3668
|
+
grp.anchorId = anchor.msg._serverItemId || anchor.msg._localId || "";
|
|
3365
3669
|
}
|
|
3366
3670
|
var out = [];
|
|
3367
3671
|
for (var j = 0; j < list.length; j++) {
|
|
@@ -3375,6 +3679,6 @@ function buildChatDisplayList(messages, opts) {
|
|
|
3375
3679
|
return out;
|
|
3376
3680
|
}
|
|
3377
3681
|
|
|
3378
|
-
export { BG_INDEXING_QUEUE_SUFFIX, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, ChatSession, DEFAULT_CLAUDE_MODEL, DEFAULT_OPENAI_MODEL, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, HISTORY_FILL_SLACK_PX, HISTORY_TOKEN_BUDGET, LINK_LABEL_MAX_DISPLAY_CHARS, LINK_REFRESH_WINDOW_MS, MAX_HISTORY_FILL_PAGES, MAX_HISTORY_MESSAGES, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MIN_INPUT_TOKEN_BUDGET, OUTPUT_TOKEN_RESERVE, POLL_INTERVAL, RENDER_FROM_TOKEN, TOOL_AND_RESPONSE_BUFFER, buildBoundedChatMessages, buildChatDisplayList, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildIndexingContinueMessage, buildIndexingRenderContinueTemplate, buildIndexingRenderMessage, buildIndexingSystemPrompt, buildIndexingUserMessage, buildIndexingWindowMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, chatEngineConfig, classifyInlineLink, clearAttachmentParsers, composeUserMessage, configureChatEngine, createHistoryFiller, createInlineLinkRegex, encodePathSegments, estimateMessageTokens, estimateTextTokens, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, groupAttachmentFailures, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isNonRetryableRequestError, isOfficeFile, isServerExtractable, isServiceDbAttachmentHref, listClaudeModels, listOpenAIModels, makeExtractPlaceholder, mapHistoryListToMessages, normalizeAttachmentPathCandidate, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAttachmentContent, parseIndexingLabel, readExpiredAttachmentHref, registerAttachmentParser, repairUrlWhitespace, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay };
|
|
3682
|
+
export { BG_INDEXING_QUEUE_SUFFIX, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, ChatSession, DEFAULT_CLAUDE_MODEL, DEFAULT_OPENAI_MODEL, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, HISTORY_BUDGET_RATIO, HISTORY_FILL_SLACK_PX, HISTORY_TOKEN_BUDGET, LINK_LABEL_MAX_DISPLAY_CHARS, LINK_REFRESH_WINDOW_MS, MAX_HISTORY_FILL_PAGES, MAX_HISTORY_MESSAGES, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MIN_INPUT_TOKEN_BUDGET, OUTPUT_TOKEN_RESERVE, POLL_INTERVAL, RENDER_FROM_TOKEN, TOOL_AND_RESPONSE_BUFFER, buildAiAgentValue, buildBoundedChatMessages, buildChatDisplayList, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildIndexingContinueMessage, buildIndexingRenderContinueTemplate, buildIndexingRenderMessage, buildIndexingSystemPrompt, buildIndexingUserMessage, buildIndexingWindowMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, chatEngineConfig, classifyInlineLink, clearAttachmentParsers, composeUserMessage, configureChatEngine, createHistoryFiller, createInlineLinkRegex, encodePathSegments, estimateMessageTokens, estimateTextTokens, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, formatChatTimestamp, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, getProjectContextWindow, groupAttachmentFailures, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isIndexingRequestText, isNonRetryableRequestError, isOfficeFile, isServerExtractable, isServiceDbAttachmentHref, listClaudeModels, listOpenAIModels, makeExtractPlaceholder, mapHistoryListToMessages, normalizeAttachmentPathCandidate, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAiAgentValue, parseAttachmentContent, parseIndexingLabel, parseIndexingRequestText, readExpiredAttachmentHref, registerAttachmentParser, registerModelContextWindows, repairUrlEntities, repairUrlWhitespace, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, wallClockNow };
|
|
3379
3683
|
//# sourceMappingURL=engine.mjs.map
|
|
3380
3684
|
//# sourceMappingURL=engine.mjs.map
|