bunnyquery 1.8.12 → 1.8.14
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 +17 -0
- package/bunnyquery.js +509 -91
- package/dist/engine.cjs +447 -52
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +482 -212
- package/dist/engine.d.ts +482 -212
- package/dist/engine.mjs +443 -53
- package/dist/engine.mjs.map +1 -1
- package/package.json +3 -2
- package/src/engine/history.ts +64 -0
- package/src/engine/image_preview.ts +0 -0
- package/src/engine/index.ts +17 -0
- package/src/engine/link_markup.ts +9 -1
- package/src/engine/links.ts +31 -3
- package/src/engine/scroll_anchor.ts +367 -0
- package/src/engine/session.ts +302 -60
- package/src/widget.css +17 -0
package/dist/engine.mjs
CHANGED
|
@@ -288,7 +288,7 @@ Scope: Only answer questions about this project and its data. Do not answer ques
|
|
|
288
288
|
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.
|
|
289
289
|
Complete answers over stored data: The database holds one record per spreadsheet row, and each uploaded file becomes many records. ONE file is routinely SPLIT ACROSS SEVERAL TABLES - a summary row in one table, its page or row content in another, its extracted photos and other media in "__MEDIA__", and the indexer often invents a differently-named table on each pass. An index or tag filter matches inside ONE table only and requires table_name: on getRecords, an index or tag sent with table_name but no access_group is auto-filled with access_group "authorized" (where the indexer writes; pass access_group explicitly, including 0, to search another group), while an index or tag WITHOUT table_name FAILS with an error instead of answering, so read the error rather than guessing. Reference is the exception: reference ALONE spans EVERY table and EVERY access group, so getRecords with reference "src::<the file's storage path>" is the one call that returns a whole file's records wherever the indexer put them. Adding table_name narrows it to that table; access_group WITHOUT table_name fails with '"table" is required'; table_name on its own returns that whole table across all access groups. For anything NOT scoped to a single file, call getTables FIRST, run the query once per table that could hold the answer, and combine the results. 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 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. If you already answered from one table and then realise another table holds more, do not simply apologise: re-run the sweep and give the complete answer.
|
|
290
290
|
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.
|
|
291
|
-
Embedded values: a search term is often stored inside a larger string. A merchant "
|
|
291
|
+
Embedded values: a search term is often stored inside a larger string. A merchant "BAKSA" appears as "DNH*BAKSA#4070277042", and a card as "5860****5173". 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.
|
|
292
292
|
File attachments: When a user message contains an "Attached files:" section with markdown links, those links point to short-lived signed URLs in this project's db storage and will expire.
|
|
293
293
|
- Image files (.jpg, .jpeg, .png, .gif, .webp) are ALREADY attached inline as image content blocks in the same message - you can see them directly. Do NOT call web_fetch on image URLs; that will fail or return garbage. Just look at the image block and answer.
|
|
294
294
|
- Other attached files (office documents like .docx/.xlsx/.pptx/.hwp/.hwpx/.ods, and text/data/code files like .csv/.tsv/.json/.xml/.txt/.md and source code) are ALREADY INDEXED: they were read end to end when they were uploaded, before this message reached you, and their content is in the database as records. Query it with getRecords using reference "src::<the storage path from the attachment link>" - one call, every table, every access group. Do NOT call web_fetch on their URLs. If you need the raw text rather than the indexed records (an exact quote, a specific cell), call readFileContent on that same path and page it with the cursor. Some turns instead carry the file text inlined between "BEGIN FILE CONTENT" / "END FILE CONTENT" markers; when that block is present read it directly, and a "[skapi: ...]" note inside it means that file could not be extracted.
|
|
@@ -899,18 +899,31 @@ function classifyInlineLink(full, groups, ctx) {
|
|
|
899
899
|
part: { type: "link", label: truncateLabelForDisplay(urlLabel), fullLabel: urlLabel, href: originalHref, expired: false }
|
|
900
900
|
});
|
|
901
901
|
}
|
|
902
|
+
function canonicalizePathForm(value) {
|
|
903
|
+
if (!value) return value;
|
|
904
|
+
try {
|
|
905
|
+
return value.normalize("NFC");
|
|
906
|
+
} catch (e) {
|
|
907
|
+
return value;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
902
910
|
function linkUnavailableKeyForPath(remotePath) {
|
|
903
|
-
return "path:" + (remotePath || "");
|
|
911
|
+
return "path:" + canonicalizePathForm(remotePath || "");
|
|
904
912
|
}
|
|
905
913
|
function linkUnavailableKeyForHref(href) {
|
|
906
|
-
|
|
914
|
+
var carried = readExpiredAttachmentHref(href);
|
|
915
|
+
if (carried) return linkUnavailableKeyForPath(carried);
|
|
916
|
+
return "href:" + canonicalizePathForm(href || "");
|
|
907
917
|
}
|
|
908
918
|
function linkUnavailableKeysForPath(remotePath) {
|
|
909
919
|
if (!remotePath) return [];
|
|
910
|
-
|
|
920
|
+
var keys = [
|
|
911
921
|
linkUnavailableKeyForPath(remotePath),
|
|
912
922
|
linkUnavailableKeyForHref(buildDisplayExpiredAttachmentHref(remotePath))
|
|
913
923
|
];
|
|
924
|
+
return keys.filter(function(k, i) {
|
|
925
|
+
return keys.indexOf(k) === i;
|
|
926
|
+
});
|
|
914
927
|
}
|
|
915
928
|
function isLinkUnavailable(link, map) {
|
|
916
929
|
if (!link || !map) return false;
|
|
@@ -1311,7 +1324,7 @@ function renderInlineLinkHtml(link, opts) {
|
|
|
1311
1324
|
if (link.remotePath) attrs.push('data-bq-remote-path="' + escapeInlineHtml(link.remotePath) + '"');
|
|
1312
1325
|
if (link.fullLabel) attrs.push('data-bq-full-label="' + escapeInlineHtml(link.fullLabel) + '"');
|
|
1313
1326
|
if (!preview) return "<a " + attrs.join(" ") + ">" + escapeInlineHtml(labelText) + "</a>";
|
|
1314
|
-
return "<a " + attrs.join(" ") + '><img class="bq-img-preview" alt="' + escapeInlineHtml(full) + '" data-bq-img-path="' + escapeInlineHtml(link.remotePath || "") + '" data-bq-img-type="' + escapeInlineHtml(link.image ? link.image.contentType : "") + '"
|
|
1327
|
+
return "<a " + attrs.join(" ") + '><img class="bq-img-preview" alt="' + escapeInlineHtml(full) + '" data-bq-img-path="' + escapeInlineHtml(link.remotePath || "") + '" data-bq-img-type="' + escapeInlineHtml(link.image ? link.image.contentType : "") + '" decoding="async"><span class="bq-loader" data-bq-img-loader="1"></span><span class="bq-img-preview-caption" translate="no">' + escapeInlineHtml(labelText) + "</span></a>";
|
|
1315
1328
|
}
|
|
1316
1329
|
|
|
1317
1330
|
// src/engine/image_preview.ts
|
|
@@ -1397,31 +1410,48 @@ function hydrateOne(img, ctx) {
|
|
|
1397
1410
|
var warm = peekImagePreviewUrl(ctx, path);
|
|
1398
1411
|
if (warm) {
|
|
1399
1412
|
img.setAttribute("src", warm);
|
|
1413
|
+
notifyLayoutChange(img, ctx, path);
|
|
1400
1414
|
return;
|
|
1401
1415
|
}
|
|
1402
1416
|
resolveImagePreviewUrl(ctx, path, type).then(function(url) {
|
|
1403
1417
|
if (img.getAttribute("data-bq-img-state") !== "loading") return;
|
|
1404
1418
|
img.setAttribute("src", url);
|
|
1419
|
+
notifyLayoutChange(img, ctx, path);
|
|
1405
1420
|
}, function(e) {
|
|
1406
1421
|
img.setAttribute("data-bq-img-state", "error");
|
|
1422
|
+
notifyLayoutChange(img, ctx, path);
|
|
1407
1423
|
if (ctx.onError) ctx.onError(path, e);
|
|
1408
1424
|
});
|
|
1409
1425
|
}
|
|
1410
1426
|
function onImageError(img, ctx, path, type) {
|
|
1411
1427
|
if (img.getAttribute("data-bq-img-retry") === "1") {
|
|
1412
1428
|
img.setAttribute("data-bq-img-state", "error");
|
|
1429
|
+
notifyLayoutChange(img, ctx, path);
|
|
1413
1430
|
if (ctx.onError) ctx.onError(path, new Error("image preview failed to load"));
|
|
1414
1431
|
return;
|
|
1415
1432
|
}
|
|
1416
1433
|
img.setAttribute("data-bq-img-retry", "1");
|
|
1417
1434
|
img.removeAttribute("src");
|
|
1435
|
+
notifyLayoutChange(img, ctx, path);
|
|
1418
1436
|
resolveImagePreviewUrl(ctx, path, type, true).then(function(url) {
|
|
1419
1437
|
img.setAttribute("src", url);
|
|
1438
|
+
notifyLayoutChange(img, ctx, path);
|
|
1420
1439
|
}, function(e) {
|
|
1421
1440
|
img.setAttribute("data-bq-img-state", "error");
|
|
1441
|
+
notifyLayoutChange(img, ctx, path);
|
|
1422
1442
|
if (ctx.onError) ctx.onError(path, e);
|
|
1423
1443
|
});
|
|
1424
1444
|
}
|
|
1445
|
+
function notifyLayoutChange(img, ctx, path) {
|
|
1446
|
+
if (!ctx.onLayoutChange) return;
|
|
1447
|
+
ctx.onLayoutChange(previewLayoutBox(img), path);
|
|
1448
|
+
}
|
|
1449
|
+
var PREVIEW_LAYOUT_BOX_SELECTOR = "a.bq-link-button.is-image-preview";
|
|
1450
|
+
function previewLayoutBox(img) {
|
|
1451
|
+
var closest = img.closest;
|
|
1452
|
+
if (typeof closest !== "function") return img;
|
|
1453
|
+
return closest.call(img, PREVIEW_LAYOUT_BOX_SELECTOR) || img;
|
|
1454
|
+
}
|
|
1425
1455
|
|
|
1426
1456
|
// src/engine/time.ts
|
|
1427
1457
|
function wallClockNow() {
|
|
@@ -2529,6 +2559,169 @@ function mapHistoryListToMessages(list, platform, opts) {
|
|
|
2529
2559
|
}
|
|
2530
2560
|
return { messages: mapped, runningItemIds };
|
|
2531
2561
|
}
|
|
2562
|
+
function shouldRescueInFlightMessage(m, ctx) {
|
|
2563
|
+
if (!m) return false;
|
|
2564
|
+
if (m.isBackgroundTask) return false;
|
|
2565
|
+
if (m._ownerKey !== void 0 && ctx.loadKey !== void 0 && m._ownerKey !== ctx.loadKey) return false;
|
|
2566
|
+
if (m._serverItemId && ctx.hasServerId(m._serverItemId)) return false;
|
|
2567
|
+
if (m._stageId) return true;
|
|
2568
|
+
if (!m._serverItemId && ctx.pageHasPendingAssistant) return false;
|
|
2569
|
+
if (m.isSendingToServer || m.isPendingQueued || m.isPendingInProcess || m.isPending) return true;
|
|
2570
|
+
if (ctx.sending && m.role === "user") {
|
|
2571
|
+
var next = ctx.next;
|
|
2572
|
+
if (!next || next.isBackgroundTask || !next.isPending) return false;
|
|
2573
|
+
return next._serverItemId === void 0 || next._serverItemId === m._serverItemId;
|
|
2574
|
+
}
|
|
2575
|
+
return false;
|
|
2576
|
+
}
|
|
2577
|
+
|
|
2578
|
+
// src/engine/scroll_anchor.ts
|
|
2579
|
+
var ROW_KEY_ATTR = "data-row-key";
|
|
2580
|
+
var ROW_POS_ATTR = "data-row-pos";
|
|
2581
|
+
var UNKNOWN_ROW_POS = "\0?";
|
|
2582
|
+
function createScrollAnchor(options) {
|
|
2583
|
+
var held = null;
|
|
2584
|
+
var seen = typeof WeakMap === "function" ? /* @__PURE__ */ new WeakMap() : null;
|
|
2585
|
+
function capture() {
|
|
2586
|
+
var box = options.getBox();
|
|
2587
|
+
if (!box || options.isStuck()) return null;
|
|
2588
|
+
var boxTop = box.getBoundingClientRect().top;
|
|
2589
|
+
var kids = box.children;
|
|
2590
|
+
var fallback = null;
|
|
2591
|
+
for (var i = 0; i < kids.length; i++) {
|
|
2592
|
+
var el = kids[i];
|
|
2593
|
+
if (!el || typeof el.getAttribute !== "function") continue;
|
|
2594
|
+
var key = el.getAttribute(ROW_KEY_ATTR);
|
|
2595
|
+
if (!key) continue;
|
|
2596
|
+
var top = el.getBoundingClientRect().top - boxTop;
|
|
2597
|
+
if (top + el.offsetHeight <= 0) continue;
|
|
2598
|
+
if (top >= box.clientHeight) break;
|
|
2599
|
+
var rawPos = el.getAttribute(ROW_POS_ATTR);
|
|
2600
|
+
var pos = rawPos === null ? null : rawPos || UNKNOWN_ROW_POS;
|
|
2601
|
+
var cand = {
|
|
2602
|
+
key,
|
|
2603
|
+
top,
|
|
2604
|
+
pos,
|
|
2605
|
+
scrollTop: box.scrollTop,
|
|
2606
|
+
scrollHeight: box.scrollHeight,
|
|
2607
|
+
el
|
|
2608
|
+
};
|
|
2609
|
+
if (rawPos === null) return cand;
|
|
2610
|
+
if (!fallback) fallback = cand;
|
|
2611
|
+
}
|
|
2612
|
+
return fallback || {
|
|
2613
|
+
key: null,
|
|
2614
|
+
top: 0,
|
|
2615
|
+
pos: null,
|
|
2616
|
+
scrollTop: box.scrollTop,
|
|
2617
|
+
scrollHeight: box.scrollHeight,
|
|
2618
|
+
el: null
|
|
2619
|
+
};
|
|
2620
|
+
}
|
|
2621
|
+
function findRow(box, anchor) {
|
|
2622
|
+
var el = anchor.el;
|
|
2623
|
+
if (el && el.parentNode === box) return el;
|
|
2624
|
+
if (!anchor.key) return null;
|
|
2625
|
+
var kids = box.children;
|
|
2626
|
+
for (var i = 0; i < kids.length; i++) {
|
|
2627
|
+
var kid = kids[i];
|
|
2628
|
+
if (!kid || typeof kid.getAttribute !== "function") continue;
|
|
2629
|
+
if (kid.getAttribute(ROW_KEY_ATTR) === anchor.key) return kid;
|
|
2630
|
+
}
|
|
2631
|
+
return null;
|
|
2632
|
+
}
|
|
2633
|
+
function restore(anchor) {
|
|
2634
|
+
var box = options.getBox();
|
|
2635
|
+
if (!box || !anchor || options.isStuck()) return;
|
|
2636
|
+
var el = findRow(box, anchor);
|
|
2637
|
+
if (el) {
|
|
2638
|
+
var livePos = el.getAttribute(ROW_POS_ATTR) || UNKNOWN_ROW_POS;
|
|
2639
|
+
if (anchor.pos !== null && anchor.pos !== UNKNOWN_ROW_POS && livePos !== UNKNOWN_ROW_POS && livePos !== anchor.pos) {
|
|
2640
|
+
lost(box, anchor);
|
|
2641
|
+
return;
|
|
2642
|
+
}
|
|
2643
|
+
var boxTop = box.getBoundingClientRect().top;
|
|
2644
|
+
var delta = el.getBoundingClientRect().top - boxTop - anchor.top;
|
|
2645
|
+
var slack = Math.abs(box.scrollHeight - anchor.scrollHeight) + box.clientHeight;
|
|
2646
|
+
if (delta > slack || delta < -slack) {
|
|
2647
|
+
lost(box, anchor);
|
|
2648
|
+
return;
|
|
2649
|
+
}
|
|
2650
|
+
if (delta >= 1 || delta <= -1) box.scrollTop += delta;
|
|
2651
|
+
held = {
|
|
2652
|
+
key: anchor.key,
|
|
2653
|
+
top: anchor.top,
|
|
2654
|
+
pos: anchor.pos,
|
|
2655
|
+
scrollTop: box.scrollTop,
|
|
2656
|
+
scrollHeight: box.scrollHeight,
|
|
2657
|
+
el
|
|
2658
|
+
};
|
|
2659
|
+
return;
|
|
2660
|
+
}
|
|
2661
|
+
lost(box, anchor);
|
|
2662
|
+
}
|
|
2663
|
+
function lost(box, anchor) {
|
|
2664
|
+
held = null;
|
|
2665
|
+
var grew = box.scrollHeight - anchor.scrollHeight;
|
|
2666
|
+
if (grew > 0) {
|
|
2667
|
+
box.scrollTop = anchor.scrollTop + grew;
|
|
2668
|
+
return;
|
|
2669
|
+
}
|
|
2670
|
+
if (options.rawFallback) box.scrollTop = anchor.scrollTop;
|
|
2671
|
+
}
|
|
2672
|
+
function preserve(mutate) {
|
|
2673
|
+
var anchor = capture();
|
|
2674
|
+
var result = mutate();
|
|
2675
|
+
restore(anchor);
|
|
2676
|
+
return result;
|
|
2677
|
+
}
|
|
2678
|
+
function remember() {
|
|
2679
|
+
held = capture();
|
|
2680
|
+
}
|
|
2681
|
+
function hold() {
|
|
2682
|
+
var box = options.getBox();
|
|
2683
|
+
if (!box || options.isStuck()) {
|
|
2684
|
+
held = null;
|
|
2685
|
+
return;
|
|
2686
|
+
}
|
|
2687
|
+
if (!held) {
|
|
2688
|
+
held = capture();
|
|
2689
|
+
return;
|
|
2690
|
+
}
|
|
2691
|
+
if (box.scrollTop !== held.scrollTop) {
|
|
2692
|
+
held = capture();
|
|
2693
|
+
return;
|
|
2694
|
+
}
|
|
2695
|
+
restore(held);
|
|
2696
|
+
}
|
|
2697
|
+
function absorb(el) {
|
|
2698
|
+
if (!el) return;
|
|
2699
|
+
var box = options.getBox();
|
|
2700
|
+
if (!box) return;
|
|
2701
|
+
var h = el.offsetHeight;
|
|
2702
|
+
var prev = seen ? seen.get(el) : void 0;
|
|
2703
|
+
if (seen) seen.set(el, h);
|
|
2704
|
+
if (options.isStuck()) return;
|
|
2705
|
+
if (prev === void 0) prev = 0;
|
|
2706
|
+
var delta = h - prev;
|
|
2707
|
+
if (delta === 0) return;
|
|
2708
|
+
if (el.getBoundingClientRect().top >= box.getBoundingClientRect().top) return;
|
|
2709
|
+
box.scrollTop += delta;
|
|
2710
|
+
if (held) held = capture();
|
|
2711
|
+
}
|
|
2712
|
+
function forget() {
|
|
2713
|
+
held = null;
|
|
2714
|
+
}
|
|
2715
|
+
return {
|
|
2716
|
+
capture,
|
|
2717
|
+
restore,
|
|
2718
|
+
preserve,
|
|
2719
|
+
remember,
|
|
2720
|
+
hold,
|
|
2721
|
+
absorb,
|
|
2722
|
+
forget
|
|
2723
|
+
};
|
|
2724
|
+
}
|
|
2532
2725
|
|
|
2533
2726
|
// src/engine/viewport_fill.ts
|
|
2534
2727
|
var HISTORY_FILL_SLACK_PX = 64;
|
|
@@ -2640,6 +2833,7 @@ var WORKER_PASS_ADOPT_LIMIT = 20;
|
|
|
2640
2833
|
var LIVE_INDEX_SNAPSHOT_MAX_AGE_MS = 5e3;
|
|
2641
2834
|
var INDEX_DISPATCH_CLAIM_MS = 2 * 60 * 1e3;
|
|
2642
2835
|
var WORKER_PASS_ADOPT_ATTEMPTS = [0, 2e3, 6e3];
|
|
2836
|
+
var EARLY_PROBE_SCHEDULE_MS = [400, 900, 1700];
|
|
2643
2837
|
var INDEXING_DRAIN_BUSY_POLL_MS = 8e3;
|
|
2644
2838
|
var INDEXING_DRAIN_CONFIRM_POLL_MS = 3e3;
|
|
2645
2839
|
var INDEXING_DRAIN_IDLE_LOOKS = 2;
|
|
@@ -3035,7 +3229,93 @@ var ChatSession = class {
|
|
|
3035
3229
|
*
|
|
3036
3230
|
* `stop` comes from the SDK and may be absent on an older skapi-js, in which case the
|
|
3037
3231
|
* poll simply cannot be stopped and is left running — see pausePolling.
|
|
3232
|
+
*
|
|
3233
|
+
* (This block documents _trackPoll, further down. The two methods below sit between it
|
|
3234
|
+
* and its subject.)
|
|
3038
3235
|
*/
|
|
3236
|
+
/**
|
|
3237
|
+
* Foreground poll with an early-probe race.
|
|
3238
|
+
*
|
|
3239
|
+
* skapi's poll() is a bare setInterval(fn, latency) with NO check at t=0, so the earliest a
|
|
3240
|
+
* reply can be observed is one full POLL_INTERVAL (3s) after dispatch. For a long generation
|
|
3241
|
+
* that granularity is free. For a SHORT one it is nearly pure dead time: a greeting that the
|
|
3242
|
+
* provider finishes in 1s still waits until the 3s tick, which measured as a large share of a
|
|
3243
|
+
* 5s "yo" round trip.
|
|
3244
|
+
*
|
|
3245
|
+
* So keep the 3s interval as the steady state, and additionally point-look-up the item a few
|
|
3246
|
+
* times early, on a widening schedule. Whichever answers first wins and the other is stopped.
|
|
3247
|
+
* The probe uses the csrHistoryItemLookup hook both clients already implement; without it this
|
|
3248
|
+
* degrades to exactly the old behaviour.
|
|
3249
|
+
*
|
|
3250
|
+
* FOREGROUND ONLY. Background indexing polls keep the flat cadence: nobody is watching them,
|
|
3251
|
+
* and they are the ones bounded by MAX_CONCURRENT_BG_POLLS, so adding probes there would spend
|
|
3252
|
+
* the request budget the cap exists to protect.
|
|
3253
|
+
*/
|
|
3254
|
+
attachForegroundPoll(source, itemId, opts) {
|
|
3255
|
+
return this._fgPollWithEarlyProbe(source, itemId, opts);
|
|
3256
|
+
}
|
|
3257
|
+
_fgPollWithEarlyProbe(source, itemId, opts) {
|
|
3258
|
+
var base = source.poll(Object.assign({ latency: POLL_INTERVAL }, opts || {}));
|
|
3259
|
+
var lookup = chatEngineConfig().csrHistoryItemLookup;
|
|
3260
|
+
var ident = this.host.getIdentity();
|
|
3261
|
+
var platform = ident && ident.platform;
|
|
3262
|
+
if (!lookup || !itemId || !ident || !ident.projectId || platform !== "claude" && platform !== "openai") {
|
|
3263
|
+
return base;
|
|
3264
|
+
}
|
|
3265
|
+
var settled = false;
|
|
3266
|
+
var timers = [];
|
|
3267
|
+
var clearProbes = function() {
|
|
3268
|
+
for (var i = 0; i < timers.length; i++) clearTimeout(timers[i]);
|
|
3269
|
+
timers = [];
|
|
3270
|
+
};
|
|
3271
|
+
var stopBase = base && typeof base.stop === "function" ? base.stop.bind(base) : null;
|
|
3272
|
+
var raced = new Promise(function(resolve, reject) {
|
|
3273
|
+
base.then(function(res) {
|
|
3274
|
+
if (settled) return;
|
|
3275
|
+
settled = true;
|
|
3276
|
+
clearProbes();
|
|
3277
|
+
resolve(res);
|
|
3278
|
+
}, function(err) {
|
|
3279
|
+
if (settled) return;
|
|
3280
|
+
settled = true;
|
|
3281
|
+
clearProbes();
|
|
3282
|
+
reject(err);
|
|
3283
|
+
});
|
|
3284
|
+
var fullId = buildHistoryItemFullId(platform, ident.projectId, itemId);
|
|
3285
|
+
EARLY_PROBE_SCHEDULE_MS.forEach(function(delay) {
|
|
3286
|
+
timers.push(setTimeout(function() {
|
|
3287
|
+
if (settled) return;
|
|
3288
|
+
Promise.resolve(lookup(fullId, ident.projectId, ident.owner)).then(function(body) {
|
|
3289
|
+
if (settled || !body || typeof body !== "object") return;
|
|
3290
|
+
if (body.status === "pending" || body.status === "running") return;
|
|
3291
|
+
if (isPollStopped(body)) return;
|
|
3292
|
+
settled = true;
|
|
3293
|
+
clearProbes();
|
|
3294
|
+
if (stopBase) {
|
|
3295
|
+
try {
|
|
3296
|
+
stopBase();
|
|
3297
|
+
} catch (e) {
|
|
3298
|
+
}
|
|
3299
|
+
}
|
|
3300
|
+
if (opts && typeof opts.onResponse === "function") {
|
|
3301
|
+
try {
|
|
3302
|
+
opts.onResponse(body);
|
|
3303
|
+
} catch (e) {
|
|
3304
|
+
}
|
|
3305
|
+
}
|
|
3306
|
+
resolve(body);
|
|
3307
|
+
}, function() {
|
|
3308
|
+
});
|
|
3309
|
+
}, delay));
|
|
3310
|
+
});
|
|
3311
|
+
});
|
|
3312
|
+
raced.stop = function() {
|
|
3313
|
+
settled = true;
|
|
3314
|
+
clearProbes();
|
|
3315
|
+
if (stopBase) stopBase();
|
|
3316
|
+
};
|
|
3317
|
+
return raced;
|
|
3318
|
+
}
|
|
3039
3319
|
_trackPoll(id, kind, p) {
|
|
3040
3320
|
var stop = p && typeof p.stop === "function" ? p.stop.bind(p) : void 0;
|
|
3041
3321
|
if (!stop) {
|
|
@@ -3211,6 +3491,68 @@ var ChatSession = class {
|
|
|
3211
3491
|
startKeyHistory: this.state.historyStartKeyHistory.slice()
|
|
3212
3492
|
};
|
|
3213
3493
|
}
|
|
3494
|
+
/**
|
|
3495
|
+
* Give the immediate-send pair the server's id for their turn, the moment the
|
|
3496
|
+
* dispatch learns it.
|
|
3497
|
+
*
|
|
3498
|
+
* WHY THIS EXISTS. An immediate send pushes its user bubble and its
|
|
3499
|
+
* "Thinking..." placeholder locally, and until now neither ever carried a
|
|
3500
|
+
* _serverItemId — only the QUEUED path stamped one, off its ack. So for the
|
|
3501
|
+
* whole life of the turn there was no way to tell the local copy and the
|
|
3502
|
+
* server's copy of the SAME turn apart, and the history merge fell back to a
|
|
3503
|
+
* heuristic: rescue the local pair unless the freshly-fetched page happens to
|
|
3504
|
+
* contain a pending assistant.
|
|
3505
|
+
*
|
|
3506
|
+
* That heuristic has a hole exactly one poll interval wide. The server settles
|
|
3507
|
+
* the request; for up to POLL_INTERVAL the client has not noticed, so
|
|
3508
|
+
* `state.sending` is still true and the local pair is still on screen — while a
|
|
3509
|
+
* history fetch issued in that window returns the turn ALREADY SETTLED, with no
|
|
3510
|
+
* pending assistant in it. The rescue then re-appends the local pair below the
|
|
3511
|
+
* server's copy (the question, twice), and when the poll finally resolves,
|
|
3512
|
+
* typewriteLatestReply writes the answer into the rescued placeholder because it
|
|
3513
|
+
* is the only pending assistant left (the answer, twice). updateHistoryCache
|
|
3514
|
+
* persists the result, so it survives every later visit.
|
|
3515
|
+
*
|
|
3516
|
+
* Navigating away while waiting and coming back is what lands a fetch in that
|
|
3517
|
+
* window: a remount runs refreshGate -> a fresh first page, at an arbitrary
|
|
3518
|
+
* moment relative to the 3s poll.
|
|
3519
|
+
*
|
|
3520
|
+
* With the id on the bubbles, both clients' rescue loops skip them through the
|
|
3521
|
+
* dedup they already have (`_serverItemId is in this page`), the reply the
|
|
3522
|
+
* dispatch caches inherits the id too, and nothing needs a new special case.
|
|
3523
|
+
*
|
|
3524
|
+
* Matched by _localId, never by index: a file's indexing rows are spliced in
|
|
3525
|
+
* above these bubbles while the request is in flight.
|
|
3526
|
+
*/
|
|
3527
|
+
_stampTurnWithItemId(key, userLid, placeholderLid, itemId) {
|
|
3528
|
+
if (!itemId) return;
|
|
3529
|
+
var changed = false;
|
|
3530
|
+
var stamp = function(list) {
|
|
3531
|
+
var hit = false;
|
|
3532
|
+
for (var i = 0; i < list.length; i++) {
|
|
3533
|
+
var m = list[i];
|
|
3534
|
+
if (!m || !m._localId) continue;
|
|
3535
|
+
if (m._localId !== userLid && m._localId !== placeholderLid) continue;
|
|
3536
|
+
if (m._serverItemId === itemId) continue;
|
|
3537
|
+
list[i] = Object.assign({}, m, { _serverItemId: itemId });
|
|
3538
|
+
hit = true;
|
|
3539
|
+
}
|
|
3540
|
+
return hit;
|
|
3541
|
+
};
|
|
3542
|
+
changed = stamp(this.state.messages);
|
|
3543
|
+
var cached = key ? this.aiChatHistoryCache[key] : void 0;
|
|
3544
|
+
if (cached) {
|
|
3545
|
+
var msgs = cached.messages.slice();
|
|
3546
|
+
if (stamp(msgs)) {
|
|
3547
|
+
this.aiChatHistoryCache[key] = {
|
|
3548
|
+
messages: msgs,
|
|
3549
|
+
endOfList: cached.endOfList,
|
|
3550
|
+
startKeyHistory: cached.startKeyHistory
|
|
3551
|
+
};
|
|
3552
|
+
}
|
|
3553
|
+
}
|
|
3554
|
+
if (changed) this.host.notify();
|
|
3555
|
+
}
|
|
3214
3556
|
/**
|
|
3215
3557
|
* Land a resolved reply in the history cache of a chat that is NOT currently
|
|
3216
3558
|
* visible, without touching state.messages. Mirrors the cache-only path in
|
|
@@ -3292,8 +3634,9 @@ var ChatSession = class {
|
|
|
3292
3634
|
if (initial.id) {
|
|
3293
3635
|
if (dispatchItemId && dispatchItemId !== initial.id) self.historyItemPolls.delete(dispatchItemId);
|
|
3294
3636
|
dispatchItemId = initial.id;
|
|
3637
|
+
if (typeof params.onItemId === "function") params.onItemId(initial.id);
|
|
3295
3638
|
}
|
|
3296
|
-
var dp =
|
|
3639
|
+
var dp = self._fgPollWithEarlyProbe(initial, initial.id);
|
|
3297
3640
|
if (initial.id) self._trackPoll(initial.id, "fg", dp);
|
|
3298
3641
|
return dp;
|
|
3299
3642
|
}
|
|
@@ -3318,28 +3661,9 @@ var ChatSession = class {
|
|
|
3318
3661
|
}).then(function(result) {
|
|
3319
3662
|
delete self.pendingAgentRequests[params.key];
|
|
3320
3663
|
if (dispatchItemId) self.historyItemPolls.delete(dispatchItemId);
|
|
3321
|
-
var existing = self.aiChatHistoryCache[params.key] || { messages: [], endOfList: false, startKeyHistory: [] };
|
|
3322
3664
|
var reply = { role: "assistant", content: result.content, isError: result.isError };
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
for (var i = msgs.length - 1; i >= 0; i--) {
|
|
3326
|
-
var m = msgs[i];
|
|
3327
|
-
if (m && m.isPending && m.role === "assistant" && !m.isBackgroundTask) {
|
|
3328
|
-
idx = i;
|
|
3329
|
-
break;
|
|
3330
|
-
}
|
|
3331
|
-
}
|
|
3332
|
-
if (idx !== -1) {
|
|
3333
|
-
reply._serverItemId = msgs[idx]._serverItemId;
|
|
3334
|
-
msgs[idx] = reply;
|
|
3335
|
-
} else {
|
|
3336
|
-
msgs.push(reply);
|
|
3337
|
-
}
|
|
3338
|
-
self.aiChatHistoryCache[params.key] = {
|
|
3339
|
-
messages: msgs,
|
|
3340
|
-
endOfList: existing.endOfList,
|
|
3341
|
-
startKeyHistory: existing.startKeyHistory
|
|
3342
|
-
};
|
|
3665
|
+
if (dispatchItemId) reply._serverItemId = dispatchItemId;
|
|
3666
|
+
self._applyReplyToCache(params.key, reply, dispatchItemId);
|
|
3343
3667
|
return result;
|
|
3344
3668
|
});
|
|
3345
3669
|
this.pendingAgentRequests[params.key] = run;
|
|
@@ -3728,8 +4052,9 @@ var ChatSession = class {
|
|
|
3728
4052
|
}
|
|
3729
4053
|
this.host.notify();
|
|
3730
4054
|
this.updateHistoryCache();
|
|
3731
|
-
this.
|
|
4055
|
+
this.scrollForDispatch(stageId);
|
|
3732
4056
|
var capturedComposed = composed, capturedPlatform = aiPlatform, capturedKey = key;
|
|
4057
|
+
var capturedQueuedLid = queuedBubble._localId;
|
|
3733
4058
|
Promise.resolve(this._callProviderFor(aiPlatform, composed, boundedQ.messages, systemPrompt, aiModel, chatQueue, extractContent, fileUrls, id.projectId, id.owner)).then(function(result) {
|
|
3734
4059
|
var sendingIdx = self.getHistoryCacheKey() !== capturedKey ? -1 : self.state.messages.findIndex(function(m) {
|
|
3735
4060
|
return m.isSendingToServer && (m.isPendingQueued || m.isPendingInProcess) && m.role === "user" && !m._stageId && (m._ownerKey === void 0 || m._ownerKey === capturedKey);
|
|
@@ -3741,8 +4066,9 @@ var ChatSession = class {
|
|
|
3741
4066
|
self.state.messages[sendingIdx] = upd;
|
|
3742
4067
|
self.host.notify();
|
|
3743
4068
|
}
|
|
4069
|
+
if (serverId) self._stampTurnWithItemId(capturedKey, capturedQueuedLid, void 0, serverId);
|
|
3744
4070
|
if (result && result.poll && (result.status === "pending" || result.status === "running")) {
|
|
3745
|
-
var qp =
|
|
4071
|
+
var qp = self._fgPollWithEarlyProbe(result, serverId);
|
|
3746
4072
|
if (serverId) self._trackPoll(serverId, "fg", qp);
|
|
3747
4073
|
return qp.then(function(res) {
|
|
3748
4074
|
if (isPollStopped(res)) return;
|
|
@@ -3758,7 +4084,7 @@ var ChatSession = class {
|
|
|
3758
4084
|
return;
|
|
3759
4085
|
}
|
|
3760
4086
|
var immediateUser = { role: "user", content: composed, _localId: this._newLocalId(), _ts: wallClockNow(), ...key ? { _ownerKey: key } : {} };
|
|
3761
|
-
var immediatePlaceholder = { role: "assistant", content: "", isPending: true, isPendingInProcess: true, ...key ? { _ownerKey: key } : {} };
|
|
4087
|
+
var immediatePlaceholder = { role: "assistant", content: "", isPending: true, isPendingInProcess: true, _localId: this._newLocalId(), ...key ? { _ownerKey: key } : {} };
|
|
3762
4088
|
var iStage = this._stageIndex(this.state.messages, stageId);
|
|
3763
4089
|
if (iStage !== -1) {
|
|
3764
4090
|
var iEx = this.state.messages[iStage];
|
|
@@ -3772,7 +4098,7 @@ var ChatSession = class {
|
|
|
3772
4098
|
this.host.notify();
|
|
3773
4099
|
this.updateHistoryCache();
|
|
3774
4100
|
this.state.sending = true;
|
|
3775
|
-
this.
|
|
4101
|
+
this.scrollForDispatch(stageId);
|
|
3776
4102
|
var historyForLlm = this.state.messages.filter(function(m) {
|
|
3777
4103
|
if (m === immediateUser) return false;
|
|
3778
4104
|
return !m.isPending && !m.isPendingQueued && !m.isPendingInProcess && !m.isPendingOlder && !m.isCancelled && !m.isBackgroundTask && !m.isError;
|
|
@@ -3785,6 +4111,7 @@ var ChatSession = class {
|
|
|
3785
4111
|
projectId: id.projectId,
|
|
3786
4112
|
history: historyForLlm
|
|
3787
4113
|
});
|
|
4114
|
+
var immediateUserLid = immediateUser._localId, immediatePlaceholderLid = immediatePlaceholder._localId;
|
|
3788
4115
|
var run = this.dispatchAgentRequest({
|
|
3789
4116
|
key,
|
|
3790
4117
|
projectId: id.projectId,
|
|
@@ -3796,7 +4123,15 @@ var ChatSession = class {
|
|
|
3796
4123
|
boundedMessages: bounded.messages,
|
|
3797
4124
|
userId: chatQueue,
|
|
3798
4125
|
extractContent,
|
|
3799
|
-
fileUrls
|
|
4126
|
+
fileUrls,
|
|
4127
|
+
// THE fix for a turn rendering twice after navigate-away-and-back. Until
|
|
4128
|
+
// this existed the immediate-send pair carried no server id for its whole
|
|
4129
|
+
// life (only the QUEUED path stamped one, off its ack), so nothing could
|
|
4130
|
+
// tell the local copy and the server's copy of the same turn apart. See
|
|
4131
|
+
// _stampTurnWithItemId.
|
|
4132
|
+
onItemId: function(itemId) {
|
|
4133
|
+
self._stampTurnWithItemId(key, immediateUserLid, immediatePlaceholderLid, itemId);
|
|
4134
|
+
}
|
|
3800
4135
|
});
|
|
3801
4136
|
Promise.resolve(run).catch(function() {
|
|
3802
4137
|
}).then(function() {
|
|
@@ -3807,6 +4142,28 @@ var ChatSession = class {
|
|
|
3807
4142
|
});
|
|
3808
4143
|
});
|
|
3809
4144
|
}
|
|
4145
|
+
/**
|
|
4146
|
+
* Scroll for a dispatch that is going out NOW, but never for one arriving late.
|
|
4147
|
+
*
|
|
4148
|
+
* A turn with attachments does not dispatch when the user hits Send: it waits out
|
|
4149
|
+
* its uploads and then its whole indexing chain, which is minutes
|
|
4150
|
+
* (awaitIndexingDrained). By then the reader has very often scrolled up into
|
|
4151
|
+
* history to pass the time, and the forcing scrollToBottom yanked them out of it
|
|
4152
|
+
* — and worse, force-pinned stickToBottom, which no-ops every method on the
|
|
4153
|
+
* scroll anchor and re-arms the queue-detect poll whose only bail is
|
|
4154
|
+
* !stickToBottom.
|
|
4155
|
+
*
|
|
4156
|
+
* `stageId` is the exact marker for that case: only the attachment path ever
|
|
4157
|
+
* produces one. The gesture itself was already paid for at stage time, where
|
|
4158
|
+
* stageOutgoingMessage forces the scroll while the user is still looking at the
|
|
4159
|
+
* composer. Deliberately NOT gated on "did we find the staged bubble" — a remount
|
|
4160
|
+
* rebuilds from the cache and the turn is appended rather than replaced, which is
|
|
4161
|
+
* just as late and just as unrequested.
|
|
4162
|
+
*/
|
|
4163
|
+
scrollForDispatch(stageId) {
|
|
4164
|
+
if (stageId) this.host.scrollToBottomIfSticky(true);
|
|
4165
|
+
else this.host.scrollToBottom(true);
|
|
4166
|
+
}
|
|
3810
4167
|
promoteNextBgQueuedToRunning() {
|
|
3811
4168
|
if (this.state.messages.some(function(m) {
|
|
3812
4169
|
return m.isPending && m.role === "assistant" && m.isBackgroundTask;
|
|
@@ -3932,6 +4289,29 @@ var ChatSession = class {
|
|
|
3932
4289
|
else if (targetIdx >= 0) this.state.messages.splice(targetIdx, 0, msg);
|
|
3933
4290
|
else this.state.messages.push(msg);
|
|
3934
4291
|
}
|
|
4292
|
+
/**
|
|
4293
|
+
* The server's OWN copy of this turn is already on screen.
|
|
4294
|
+
*
|
|
4295
|
+
* A first-page fetch can land between the server settling the item and this
|
|
4296
|
+
* poll's tick, and now that the local bubbles carry the item id
|
|
4297
|
+
* (_stampTurnWithItemId) the rescue correctly drops them and renders the
|
|
4298
|
+
* server's settled pair instead. There is then nothing left to resolve: the
|
|
4299
|
+
* -1 fallback would push the answer in a SECOND time at the bottom of the list,
|
|
4300
|
+
* and the positional fallbacks would hijack some other turn's bubble.
|
|
4301
|
+
*
|
|
4302
|
+
* The USER bubble counts, not just a settled assistant: an item whose answer is
|
|
4303
|
+
* empty produces no assistant bubble at all in the mapper, and that variant
|
|
4304
|
+
* would otherwise still bottom-push "No text response received...". While a turn
|
|
4305
|
+
* is genuinely live its user bubble is always pending (the queued branch sets
|
|
4306
|
+
* isPendingQueued, promoteNextQueuedToRunning sets isPendingInProcess), so this
|
|
4307
|
+
* cannot fire early.
|
|
4308
|
+
*/
|
|
4309
|
+
_turnAlreadyRendered(serverId) {
|
|
4310
|
+
if (!serverId) return false;
|
|
4311
|
+
return this.state.messages.some(function(m) {
|
|
4312
|
+
return m._serverItemId === serverId && !m.isPending && !m.isPendingQueued && !m.isPendingInProcess;
|
|
4313
|
+
});
|
|
4314
|
+
}
|
|
3935
4315
|
onQueuedSendResponse(_composed, response, platform, serverId, ownerKey) {
|
|
3936
4316
|
if (serverId) this.historyItemPolls.delete(serverId);
|
|
3937
4317
|
if (ownerKey && this.getHistoryCacheKey() !== ownerKey) {
|
|
@@ -3940,6 +4320,12 @@ var ChatSession = class {
|
|
|
3940
4320
|
if (serverId) this.cancelledServerIds.delete(serverId);
|
|
3941
4321
|
return;
|
|
3942
4322
|
}
|
|
4323
|
+
if (this._turnAlreadyRendered(serverId)) {
|
|
4324
|
+
if (serverId) this.cancelledServerIds.delete(serverId);
|
|
4325
|
+
this.host.notify();
|
|
4326
|
+
this.updateHistoryCache();
|
|
4327
|
+
return;
|
|
4328
|
+
}
|
|
3943
4329
|
var targetIdx = this.resolveQueuedUserBubble(serverId);
|
|
3944
4330
|
if (targetIdx === void 0) {
|
|
3945
4331
|
this.host.notify();
|
|
@@ -3981,6 +4367,12 @@ var ChatSession = class {
|
|
|
3981
4367
|
if (serverId) this.cancelledServerIds.delete(serverId);
|
|
3982
4368
|
return;
|
|
3983
4369
|
}
|
|
4370
|
+
if (this._turnAlreadyRendered(serverId)) {
|
|
4371
|
+
if (serverId) this.cancelledServerIds.delete(serverId);
|
|
4372
|
+
this.host.notify();
|
|
4373
|
+
this.updateHistoryCache();
|
|
4374
|
+
return;
|
|
4375
|
+
}
|
|
3984
4376
|
var isNotExists = err && (err.code === "NOT_EXISTS" || err.body && err.body.code === "NOT_EXISTS");
|
|
3985
4377
|
if (isNotExists) {
|
|
3986
4378
|
var userIdx = serverId ? this.state.messages.findIndex(function(m) {
|
|
@@ -5242,21 +5634,15 @@ var ChatSession = class {
|
|
|
5242
5634
|
var rescued = [];
|
|
5243
5635
|
for (var ri = 0; ri < self.state.messages.length; ri++) {
|
|
5244
5636
|
var mm = self.state.messages[ri];
|
|
5245
|
-
if (mm
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
if (mm.isSendingToServer || mm.isPendingQueued || mm.isPendingInProcess || mm.isPending) rescued.push(mm);
|
|
5255
|
-
else if (self.state.sending && mm.role === "user") {
|
|
5256
|
-
var next = self.state.messages[ri + 1];
|
|
5257
|
-
if (next && !next.isBackgroundTask && next.isPending && !next._serverItemId) rescued.push(mm);
|
|
5258
|
-
}
|
|
5259
|
-
}
|
|
5637
|
+
if (shouldRescueInFlightMessage(mm, {
|
|
5638
|
+
hasServerId: function(sid) {
|
|
5639
|
+
return !!serverIds[sid];
|
|
5640
|
+
},
|
|
5641
|
+
pageHasPendingAssistant: mappedHasPendingAssistant,
|
|
5642
|
+
sending: !!self.state.sending,
|
|
5643
|
+
next: self.state.messages[ri + 1],
|
|
5644
|
+
loadKey
|
|
5645
|
+
})) rescued.push(mm);
|
|
5260
5646
|
}
|
|
5261
5647
|
var oldestInPage1 = void 0;
|
|
5262
5648
|
mapped.forEach(function(m) {
|
|
@@ -5307,6 +5693,9 @@ var ChatSession = class {
|
|
|
5307
5693
|
keptOlderPages = prependOlder.length > 0 || interleave.length > 0;
|
|
5308
5694
|
self.state.messages = prependOlder.length ? prependOlder.concat(page1) : page1;
|
|
5309
5695
|
rescued.forEach(function(m) {
|
|
5696
|
+
if (m._serverItemId && self.state.messages.some(function(x) {
|
|
5697
|
+
return x._serverItemId === m._serverItemId && x.role === m.role;
|
|
5698
|
+
})) return;
|
|
5310
5699
|
self.state.messages.push(m);
|
|
5311
5700
|
});
|
|
5312
5701
|
if (Object.keys(locallyCancelled).length) {
|
|
@@ -5458,8 +5847,8 @@ var ChatSession = class {
|
|
|
5458
5847
|
if ((item._isBgTask || item._isOnBgQueue) && self.isPollingPaused()) return;
|
|
5459
5848
|
if ((item._isBgTask || item._isOnBgQueue) && !bgAllow[item.id]) return;
|
|
5460
5849
|
var capturedId = item.id;
|
|
5461
|
-
var
|
|
5462
|
-
|
|
5850
|
+
var isBg = !!(item._isBgTask || item._isOnBgQueue);
|
|
5851
|
+
var pollOpts = {
|
|
5463
5852
|
onResponse: function(response) {
|
|
5464
5853
|
if (isPollStopped(response)) return;
|
|
5465
5854
|
self.handleHistoryItemResolution(capturedId, response, platform);
|
|
@@ -5474,9 +5863,9 @@ var ChatSession = class {
|
|
|
5474
5863
|
var uIdx = self.state.messages.findIndex(function(m) {
|
|
5475
5864
|
return m.role === "user" && m._serverItemId === capturedId && !m.isCancelled;
|
|
5476
5865
|
});
|
|
5477
|
-
var
|
|
5866
|
+
var isBg2 = aIdx !== -1 && !!self.state.messages[aIdx].isBackgroundTask || uIdx !== -1 && !!self.state.messages[uIdx].isBackgroundTask;
|
|
5478
5867
|
if (aIdx !== -1) self.state.messages.splice(aIdx, 1);
|
|
5479
|
-
if (!
|
|
5868
|
+
if (!isBg2) {
|
|
5480
5869
|
if (uIdx !== -1) {
|
|
5481
5870
|
var ex = self.state.messages[uIdx];
|
|
5482
5871
|
self.state.messages[uIdx] = { role: "user", content: ex.content, isCancelled: true, _serverItemId: ex._serverItemId };
|
|
@@ -5503,7 +5892,8 @@ var ChatSession = class {
|
|
|
5503
5892
|
self.updateHistoryCache();
|
|
5504
5893
|
}
|
|
5505
5894
|
}
|
|
5506
|
-
}
|
|
5895
|
+
};
|
|
5896
|
+
var pp = isBg ? item.poll(Object.assign({ latency: POLL_INTERVAL }, pollOpts)) : self._fgPollWithEarlyProbe(item, capturedId, pollOpts);
|
|
5507
5897
|
self._trackPoll(capturedId, item._isBgTask || item._isOnBgQueue ? "bg" : "fg", pp);
|
|
5508
5898
|
if (pp && pp.catch) pp.catch(function() {
|
|
5509
5899
|
});
|
|
@@ -6134,6 +6524,6 @@ function buildChatDisplayList(messages, opts) {
|
|
|
6134
6524
|
return out;
|
|
6135
6525
|
}
|
|
6136
6526
|
|
|
6137
|
-
export { BG_INDEXING_QUEUE_SUFFIX, BOM, BOM_EXTS, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, ChatSession, DEFAULT_CLAUDE_MODEL, DEFAULT_CONTEXT_WINDOW, DEFAULT_OPENAI_MODEL, EMPTY_INDEXING_REPLY, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, EXT_CONTENT_TYPES, HISTORY_BUDGET_RATIO, HISTORY_FILL_SLACK_PX, HISTORY_TOKEN_BUDGET, HTML_EXTS, HTML_HEAD_WINDOW, IMAGE_PREVIEWS_PER_MESSAGE, INDEXING_COMPLETE_MARKER, INLINE_LINK_GLYPH, INLINE_LINK_UNAVAILABLE_GLYPH, INLINE_LINK_UNAVAILABLE_SUFFIX, INPUT_CAP_RATIO, LINK_LABEL_MAX_DISPLAY_CHARS, LINK_REFRESH_WINDOW_MS, MAX_CONCURRENT_BG_POLLS, MAX_HISTORY_FILL_PAGES, MAX_HISTORY_MESSAGES, MAX_OUTPUT_BY_MODEL, MAX_OUTPUT_TOKENS, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MINT_CACHE_GENERATION, MIN_INPUT_TOKEN_BUDGET, MIN_PER_REQUEST_INPUT_CAP, OUTPUT_TOKEN_RESERVE, POLL_INTERVAL, PRESIGN_SAFETY_MARGIN_MS, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, PREVIEW_URL_EXPIRES_SECONDS, RENDER_FROM_TOKEN, RTF_EXTS, RUN_RECORD_WORKING_STALE_MS, TOOL_AND_RESPONSE_BUFFER, XML_EXTS, __resetSplitHistoryState, applyEncodingDeclaration, bgIndexingQueueName, buildAiAgentValue, buildBoundedChatMessages, buildChatDisplayList, buildChatGreeting, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildHistoryItemFullId, buildIndexingContinueMessage, buildIndexingRenderContinueTemplate, buildIndexingRenderMessage, buildIndexingSystemPrompt, buildIndexingUserMessage, buildIndexingWindowMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, chatEngineConfig, classifyInlineLink, clearAttachmentParsers, clearImagePreviewCache, composeUserMessage, configureChatEngine, contentTypeForExt, createHistoryFiller, createInlineLinkRegex, encodePathSegments, encodingClassForExt, ensureHtmlCharset, ensureXmlEncoding, escapeInlineHtml, escapeRtfNonAscii, estimateMessageTokens, estimateTextTokens, extOf, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, fetchLiveIndexingKeys, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, formatChatTimestamp, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, getInputTokenBudget, getMaxOutputTokens, getModelContextWindow, getProjectContextWindow, getSplitChatHistory, getVisionProfile, groupAttachmentFailures, hasBom, hydrateImagePreviews, indexDoneUniqueId, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isIndexingRequestText, isLinkUnavailable, isNonRetryableRequestError, isOfficeFile, isPreviewableImagePath, isProviderApiKeyError, isServerExtractable, isServiceDbAttachmentHref, linkUnavailableKeyForHref, linkUnavailableKeyForPath, linkUnavailableKeysForPath, listClaudeModels, listOpenAIModels, looksLikeRtf, makeExtractPlaceholder, mapHistoryListToMessages, markImagePreviewStale, mintCacheBustStamp, needsBomForExt, normalizeAttachmentPathCandidate, normalizeExt, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAiAgentValue, parseAttachmentContent, parseIndexingLabel, parseIndexingRequestText, peekImagePreviewUrl, prepareDownloadText, presignExpiryEpochMs, previewImageContentType, previewMintCacheToken, previewableExtOf, readExpiredAttachmentHref, registerAttachmentParser, registerModelContextWindows, renderInlineLinkHtml, repairUrlEntities, repairUrlWhitespace, resolveImagePreviewUrl, runIndexUniqueId, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, upsertIndexRunRecordSafe, wallClockNow };
|
|
6527
|
+
export { BG_INDEXING_QUEUE_SUFFIX, BOM, BOM_EXTS, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, ChatSession, DEFAULT_CLAUDE_MODEL, DEFAULT_CONTEXT_WINDOW, DEFAULT_OPENAI_MODEL, EMPTY_INDEXING_REPLY, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, EXT_CONTENT_TYPES, HISTORY_BUDGET_RATIO, HISTORY_FILL_SLACK_PX, HISTORY_TOKEN_BUDGET, HTML_EXTS, HTML_HEAD_WINDOW, IMAGE_PREVIEWS_PER_MESSAGE, INDEXING_COMPLETE_MARKER, INLINE_LINK_GLYPH, INLINE_LINK_UNAVAILABLE_GLYPH, INLINE_LINK_UNAVAILABLE_SUFFIX, INPUT_CAP_RATIO, LINK_LABEL_MAX_DISPLAY_CHARS, LINK_REFRESH_WINDOW_MS, MAX_CONCURRENT_BG_POLLS, MAX_HISTORY_FILL_PAGES, MAX_HISTORY_MESSAGES, MAX_OUTPUT_BY_MODEL, MAX_OUTPUT_TOKENS, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MINT_CACHE_GENERATION, MIN_INPUT_TOKEN_BUDGET, MIN_PER_REQUEST_INPUT_CAP, OUTPUT_TOKEN_RESERVE, POLL_INTERVAL, PRESIGN_SAFETY_MARGIN_MS, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, PREVIEW_LAYOUT_BOX_SELECTOR, PREVIEW_URL_EXPIRES_SECONDS, RENDER_FROM_TOKEN, RTF_EXTS, RUN_RECORD_WORKING_STALE_MS, TOOL_AND_RESPONSE_BUFFER, XML_EXTS, __resetSplitHistoryState, applyEncodingDeclaration, bgIndexingQueueName, buildAiAgentValue, buildBoundedChatMessages, buildChatDisplayList, buildChatGreeting, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildHistoryItemFullId, buildIndexingContinueMessage, buildIndexingRenderContinueTemplate, buildIndexingRenderMessage, buildIndexingSystemPrompt, buildIndexingUserMessage, buildIndexingWindowMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, canonicalizePathForm, chatEngineConfig, classifyInlineLink, clearAttachmentParsers, clearImagePreviewCache, composeUserMessage, configureChatEngine, contentTypeForExt, createHistoryFiller, createInlineLinkRegex, createScrollAnchor, encodePathSegments, encodingClassForExt, ensureHtmlCharset, ensureXmlEncoding, escapeInlineHtml, escapeRtfNonAscii, estimateMessageTokens, estimateTextTokens, extOf, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, fetchLiveIndexingKeys, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, formatChatTimestamp, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, getInputTokenBudget, getMaxOutputTokens, getModelContextWindow, getProjectContextWindow, getSplitChatHistory, getVisionProfile, groupAttachmentFailures, hasBom, hydrateImagePreviews, indexDoneUniqueId, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isIndexingRequestText, isLinkUnavailable, isNonRetryableRequestError, isOfficeFile, isPreviewableImagePath, isProviderApiKeyError, isServerExtractable, isServiceDbAttachmentHref, linkUnavailableKeyForHref, linkUnavailableKeyForPath, linkUnavailableKeysForPath, listClaudeModels, listOpenAIModels, looksLikeRtf, makeExtractPlaceholder, mapHistoryListToMessages, markImagePreviewStale, mintCacheBustStamp, needsBomForExt, normalizeAttachmentPathCandidate, normalizeExt, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAiAgentValue, parseAttachmentContent, parseIndexingLabel, parseIndexingRequestText, peekImagePreviewUrl, prepareDownloadText, presignExpiryEpochMs, previewImageContentType, previewLayoutBox, previewMintCacheToken, previewableExtOf, readExpiredAttachmentHref, registerAttachmentParser, registerModelContextWindows, renderInlineLinkHtml, repairUrlEntities, repairUrlWhitespace, resolveImagePreviewUrl, runIndexUniqueId, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, shouldRescueInFlightMessage, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, upsertIndexRunRecordSafe, wallClockNow };
|
|
6138
6528
|
//# sourceMappingURL=engine.mjs.map
|
|
6139
6529
|
//# sourceMappingURL=engine.mjs.map
|