bunnyquery 1.8.13 → 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/dist/engine.d.mts CHANGED
@@ -920,6 +920,20 @@ declare function classifyInlineLink(full: string, groups: Array<string | undefin
920
920
  * the placeholder href), so marking writes one key and the lookup tries all of
921
921
  * them.
922
922
  */
923
+ /**
924
+ * Unicode form is not stable across the places a storage path travels through.
925
+ *
926
+ * macOS hands the browser a DECOMPOSED (NFD) filename, so a Korean name like
927
+ * 운전면허-김대현.jpg arrives as 24 codepoints where the composed (NFC) form is 12.
928
+ * Nothing in this engine normalized either way, so the SAME file could be keyed under
929
+ * two different strings depending on which path it travelled: a mark left by a failed
930
+ * mint under one form would never be cleared by a successful load under the other, and
931
+ * the chip stayed greyed out as "(unavailable)" forever.
932
+ *
933
+ * NFC is the canonical choice: it is what the Unicode standard recommends for
934
+ * interchange, and it is the shorter, more common form on the wire.
935
+ */
936
+ declare function canonicalizePathForm(value: string): string;
923
937
  declare function linkUnavailableKeyForPath(remotePath: string): string;
924
938
  declare function linkUnavailableKeyForHref(href: string): string;
925
939
  /**
@@ -1040,6 +1054,26 @@ interface ImagePreviewContext {
1040
1054
  onLoad?: (remotePath: string) => void;
1041
1055
  /** A preview gave up. The caption chip is now the whole answer. */
1042
1056
  onError?: (remotePath: string, err: unknown) => void;
1057
+ /**
1058
+ * This element's box just changed size, and nothing asked it to.
1059
+ *
1060
+ * Fires at the points where a preview resizes with NO DOM event of its own to
1061
+ * announce it: the src lands (a src-less <img> is hidden, so this is where it
1062
+ * starts taking space), and the src is dropped for a retry, or a mint fails
1063
+ * outright, where an already-painted picture collapses back to nothing.
1064
+ * `load` and `error` are deliberately NOT routed through here — both views
1065
+ * listen for those on the message box itself, which is also how they cover the
1066
+ * images this module never sees, a markdown `![alt](url)` among them.
1067
+ *
1068
+ * Each of these slides everything below the element, and for a reader scrolled
1069
+ * up into history that is a jump in the middle of a sentence. onLoad cannot
1070
+ * stand in for it: both views answer that with a scroll-to-bottom-if-pinned,
1071
+ * which by definition does nothing for the reader this hurts.
1072
+ *
1073
+ * Views wire this to their scroll anchor's absorb(). Called SYNCHRONOUSLY, so
1074
+ * the measurement it takes is the one the reader is looking at.
1075
+ */
1076
+ onLayoutChange?: (img: PreviewImageEl, remotePath: string) => void;
1043
1077
  }
1044
1078
  /**
1045
1079
  * Drop cached preview urls, for one project or all of them.
@@ -1091,6 +1125,9 @@ declare function markImagePreviewStale(scope: string, remotePath: string): void;
1091
1125
  * querySelectorAll to an IntersectionObserver without an engine change.
1092
1126
  */
1093
1127
  declare function hydrateImagePreviews(imgs: ArrayLike<PreviewImageEl>, ctx: ImagePreviewContext): void;
1128
+ declare var PREVIEW_LAYOUT_BOX_SELECTOR: string;
1129
+ /** The element whose height a preview's own transitions actually change. */
1130
+ declare function previewLayoutBox<T extends PreviewImageEl>(img: T): T;
1094
1131
 
1095
1132
  /**
1096
1133
  * Chat timestamp formatting, shared so agent.vue and the widget render an
@@ -1395,217 +1432,6 @@ declare function getChatHistory(params: {
1395
1432
  * `[METHOD]url#service:` + the item's own `stamp:entropy` id. */
1396
1433
  declare function buildHistoryItemFullId(platform: 'claude' | 'openai', service: string, itemId: string): string;
1397
1434
 
1398
- /**
1399
- * History mapping (pure). Moved verbatim from the chatbox. The clear-horizon
1400
- * timestamp and the "Indexing: …" display label are INJECTED (clearedAt param,
1401
- * formatIndexingLabel callback) so the engine touches neither localStorage nor
1402
- * view-specific display formatting. projectId is passed for link sanitization.
1403
- */
1404
-
1405
- declare function filterListByClearHorizon(list: any[], clearedAt: number): any[];
1406
- declare function normalizeTextContent(content: any): string;
1407
- declare function extractLastUserTextFromRequest(requestBody: any): string;
1408
- /** The two openings an indexing prompt can have. A bg-queue item that starts with
1409
- * neither is an ordinary chat that happened to be routed onto that queue. */
1410
- declare function isIndexingRequestText(userText: any): boolean;
1411
- type IndexingRequestRef = {
1412
- name: string;
1413
- path?: string;
1414
- mime?: string;
1415
- size?: number;
1416
- /** A CONTINUE pass rather than the run's first. */
1417
- continued: boolean;
1418
- };
1419
- /**
1420
- * The file an indexing prompt is about, read back out of the prompt itself.
1421
- *
1422
- * The prompt is the only description of the pass that survives on the server, so
1423
- * this is how BOTH a history rebuild and a worker-minted pass the client never
1424
- * dispatched (ChatSession._adoptWorkerIndexingPasses) recover the file. Shared so
1425
- * the two produce the same `_indexFile`, which is what makes them group together.
1426
- */
1427
- declare function parseIndexingRequestText(userText: any): IndexingRequestRef | null;
1428
- /**
1429
- * One bounded look at the background-indexing queue: which files still have a
1430
- * pass pending or running? This is the same negative signal ChatSession's
1431
- * display layer relies on - for a worker-driven (auto_continue) run, only the
1432
- * queue can say the run is over, because the worker enqueues continuation
1433
- * passes the client never dispatched.
1434
- *
1435
- * Returns every storage path AND file name found on live passes (both, because
1436
- * older prompts may lack the storage-path line), plus `checked`: false when a
1437
- * page came back full, in which case absence from `keys` proves nothing and
1438
- * the caller must keep whatever state it already had.
1439
- *
1440
- * SCOPE: the probed queue is "<userId>-bg" - THIS user's dispatches only. A
1441
- * chain launched by another collaborator or a widget end-user lives on their
1442
- * queue and is invisible here, so "idle" must never be read as "nobody is
1443
- * indexing this file", only as "this user's runs are over". The durable done::
1444
- * marker (indexDoneUniqueId) is the cross-user signal.
1445
- */
1446
- declare function fetchLiveIndexingKeys(params: {
1447
- service: string;
1448
- owner: string;
1449
- platform: 'claude' | 'openai';
1450
- /** Same value the dispatch used - see bgIndexingQueueName. */
1451
- userId?: string;
1452
- }): Promise<{
1453
- keys: Set<string>;
1454
- checked: boolean;
1455
- at: number;
1456
- }>;
1457
- /** Test hook: drop split-fetch state (all keys, or one). */
1458
- declare function __resetSplitHistoryState(key?: string): void;
1459
- type SplitHistoryResult = {
1460
- list: any[];
1461
- endOfList: boolean;
1462
- startKeyHistory: any[];
1463
- /** True when this chat had never been walked in this session — the first
1464
- * paint. Consumers gate the "Loading indexing history" hint on it: a
1465
- * mid-walk tab return restarts the walk for cursor safety but must stay
1466
- * silent (flashing the hint on every return was the reported bug). */
1467
- firstLoad?: boolean;
1468
- /** Present only when `deferBg` was requested AND bg work remains: resolves
1469
- * with the stub batch fetched in the background (the per-key lock is held
1470
- * until it settles, so no other history call can interleave). The caller
1471
- * merges the batch by timestamp — the same path older pages use. */
1472
- bgPending?: Promise<{
1473
- list: any[];
1474
- endOfList: boolean;
1475
- }>;
1476
- };
1477
- declare function getSplitChatHistory(params: {
1478
- service: string;
1479
- owner: string;
1480
- platform: 'claude' | 'openai';
1481
- userId?: string;
1482
- }, fetchOptions: Record<string, any>,
1483
- /** Test seam: replaces getChatHistory. Not for production callers. */
1484
- _fetchImpl?: typeof getChatHistory): Promise<SplitHistoryResult>;
1485
- type MapHistoryOptions = {
1486
- clearedAt: number;
1487
- projectId: string;
1488
- /** View-side display formatter for "Indexing:/Reindexing: …" bubbles. */
1489
- formatIndexingLabel: (name: string, mime?: string, size?: number | null, storagePath?: string, reindex?: boolean, continued?: boolean) => string;
1490
- };
1491
- declare function mapHistoryListToMessages(list: any[], platform: 'claude' | 'openai', opts: MapHistoryOptions): {
1492
- messages: any[];
1493
- runningItemIds: string[];
1494
- };
1495
-
1496
- /**
1497
- * Keep older history REACHABLE by paging until the message box actually gains
1498
- * something to scroll to.
1499
- *
1500
- * Older history is paged in by one trigger only: the user scrolling to the top
1501
- * of the message box. That trigger has two ways to die, and collapsed indexing
1502
- * rows cause both:
1503
- *
1504
- * 1. The box never scrolls. A file's every indexing pass (the first plus every
1505
- * CONTINUE pass, request AND response bubble each) folds into ONE row, so a
1506
- * full history page — twenty-plus messages — can render as a single line.
1507
- * Content shorter than the viewport fires no scroll event, so page 2 is
1508
- * never requested and any conversation the user had before that upload is
1509
- * permanently out of reach.
1510
- * 2. The fetched page adds no height. A page that is entirely the same file's
1511
- * earlier passes joins the collapsed row already on screen and renders
1512
- * nothing new. The user, sitting at scrollTop 0, scrolls up again — and
1513
- * because the position never changed, no further scroll event fires.
1514
- *
1515
- * Both are the same shape: fetch, re-measure, and keep going until the user
1516
- * genuinely gained reachable content, history ran out, or the pager stopped
1517
- * advancing. `isSatisfied` is what differs between the two (can the box scroll
1518
- * at all / did it grow), so the loop below takes it as a predicate.
1519
- *
1520
- * DOM-free like the rest of the engine — the caller supplies the measurement and
1521
- * awaits its own render before measuring, so agent.vue and the widget run the
1522
- * identical loop over their own pagers.
1523
- */
1524
- /** Overflow (px) that counts as "the user can scroll here". Comfortably more
1525
- * than the 60px top threshold that triggers the next page, so a filled box has
1526
- * real room to scroll rather than sitting one pixel from the trigger. */
1527
- declare const HISTORY_FILL_SLACK_PX = 64;
1528
- /** Pages one fill pass will request before giving up. Reached only by a chat
1529
- * whose history really is dozens of pages of one file's indexing passes; the
1530
- * cap exists so a pager that stops advancing can never spin forever. */
1531
- declare const MAX_HISTORY_FILL_PAGES = 24;
1532
- type FillHistoryViewportOptions = {
1533
- /** The user has reachable content and paging can stop. Called AFTER the
1534
- * caller's own render has settled (nextTick / rAF), since only the caller
1535
- * knows when its view has painted — hence the allowance for a promise. */
1536
- isSatisfied: () => boolean | Promise<boolean>;
1537
- /** All history is loaded — nothing left to page in. */
1538
- isEndOfList: () => boolean;
1539
- /** A history request is already in flight. Waited out, not treated as a stop
1540
- * condition: a background first-page refresh (the queue-detect tick fires one
1541
- * every couple of seconds while a file is indexing) would otherwise swallow
1542
- * the user's scroll-up entirely, and scrolling up again from scrollTop 0
1543
- * produces no second event to retry with. */
1544
- isLoading: () => boolean;
1545
- /** Messages currently loaded. Used to detect a page that added nothing, which
1546
- * means the pager is not advancing and looping would never terminate. */
1547
- messageCount: () => number;
1548
- /** Fetch ONE older page (the caller's own fetchMore path, scroll-restore and
1549
- * all). Return `false` when the request was NOT issued (the caller's own
1550
- * single-flight guard swallowed it) so the loop retries instead of reading
1551
- * the unchanged message count as an exhausted pager. Anything else, including
1552
- * undefined, means it was attempted. */
1553
- fetchOlder: () => Promise<boolean | void | any>;
1554
- /** The chat this fill was started for is gone (project switched, view
1555
- * unmounted, gate token bumped). Checked between pages so a stale fill can
1556
- * never keep paging another chat's history. */
1557
- isStale?: () => boolean;
1558
- maxPages?: number;
1559
- };
1560
- /**
1561
- * Page older history until `isSatisfied`, until history runs out, or until the
1562
- * pager stops advancing. Never throws: a failed page ends the fill, and the
1563
- * user's own scrolling remains the fallback trigger.
1564
- */
1565
- declare function fillHistoryViewport(opts: FillHistoryViewportOptions): Promise<void>;
1566
- /**
1567
- * One fill loop per view, with predicates COMBINED rather than dropped.
1568
- *
1569
- * Fills come from several places at once — a first page finishing, a window
1570
- * resize, a row being collapsed, and the user's own scroll to the top — and a
1571
- * plain "one at a time, drop the rest" guard picks the wrong winner: a resize
1572
- * fill (satisfied the moment the box can scroll at all) would swallow the user's
1573
- * scroll-up (which needs content specifically ABOVE them), and the scroll-up
1574
- * cannot be retried, because a reader parked at scrollTop 0 produces no further
1575
- * scroll event. Dropping the guard entirely is no better: every frame of a
1576
- * window drag would start its own 24-page loop.
1577
- *
1578
- * So a request that arrives mid-loop ANDs its predicate into the running one:
1579
- * the loop then keeps paging until EVERY caller is satisfied. Predicates that
1580
- * come true are dropped as it goes, so the cost stays flat.
1581
- */
1582
- declare function createHistoryFiller(base: Omit<FillHistoryViewportOptions, 'isSatisfied'> & {
1583
- /** Fired when the loop starts FETCHING and when it stops, and only on a real
1584
- * change.
1585
- *
1586
- * This — not the caller's own per-request `isLoading` — is what "older
1587
- * history is still coming in" means to a view. A fill is many pages, and
1588
- * `isLoading` drops to false between every one of them, so anything
1589
- * rendered off it flickers once per page for the whole loop. A collapsed
1590
- * indexing row whose run begins above the loaded window renders exactly
1591
- * that ("still loading this run" vs a status it cannot know yet), which is
1592
- * why the loop has to publish its own span.
1593
- *
1594
- * Fetching, NOT requested. Most fills fetch nothing: they are fired on every
1595
- * window resize, every row a user collapses, and every first-page load, and
1596
- * the overwhelmingly common outcome is `isSatisfied` returning true on the
1597
- * first look. Announcing at request time published a true/false pair for
1598
- * each of those, and the widget's own satisfied-check spans two animation
1599
- * frames — long enough for the browser to PAINT the intermediate state. Every
1600
- * collapsed row strobed through "loading" on every resize tick. So the span
1601
- * opens at the first actual page request, which is also the first moment the
1602
- * claim is true. */
1603
- onRunningChange?: (running: boolean) => void;
1604
- }): {
1605
- fill: (isSatisfied: () => boolean | Promise<boolean>) => Promise<void>;
1606
- isRunning: () => boolean;
1607
- };
1608
-
1609
1435
  /**
1610
1436
  * ChatSession host adapter + state types.
1611
1437
  *
@@ -1849,6 +1675,356 @@ interface ChatHost {
1849
1675
  updateComposerControls(): void;
1850
1676
  }
1851
1677
 
1678
+ /**
1679
+ * History mapping (pure). Moved verbatim from the chatbox. The clear-horizon
1680
+ * timestamp and the "Indexing: …" display label are INJECTED (clearedAt param,
1681
+ * formatIndexingLabel callback) so the engine touches neither localStorage nor
1682
+ * view-specific display formatting. projectId is passed for link sanitization.
1683
+ */
1684
+
1685
+ declare function filterListByClearHorizon(list: any[], clearedAt: number): any[];
1686
+ declare function normalizeTextContent(content: any): string;
1687
+ declare function extractLastUserTextFromRequest(requestBody: any): string;
1688
+ /** The two openings an indexing prompt can have. A bg-queue item that starts with
1689
+ * neither is an ordinary chat that happened to be routed onto that queue. */
1690
+ declare function isIndexingRequestText(userText: any): boolean;
1691
+ type IndexingRequestRef = {
1692
+ name: string;
1693
+ path?: string;
1694
+ mime?: string;
1695
+ size?: number;
1696
+ /** A CONTINUE pass rather than the run's first. */
1697
+ continued: boolean;
1698
+ };
1699
+ /**
1700
+ * The file an indexing prompt is about, read back out of the prompt itself.
1701
+ *
1702
+ * The prompt is the only description of the pass that survives on the server, so
1703
+ * this is how BOTH a history rebuild and a worker-minted pass the client never
1704
+ * dispatched (ChatSession._adoptWorkerIndexingPasses) recover the file. Shared so
1705
+ * the two produce the same `_indexFile`, which is what makes them group together.
1706
+ */
1707
+ declare function parseIndexingRequestText(userText: any): IndexingRequestRef | null;
1708
+ /**
1709
+ * One bounded look at the background-indexing queue: which files still have a
1710
+ * pass pending or running? This is the same negative signal ChatSession's
1711
+ * display layer relies on - for a worker-driven (auto_continue) run, only the
1712
+ * queue can say the run is over, because the worker enqueues continuation
1713
+ * passes the client never dispatched.
1714
+ *
1715
+ * Returns every storage path AND file name found on live passes (both, because
1716
+ * older prompts may lack the storage-path line), plus `checked`: false when a
1717
+ * page came back full, in which case absence from `keys` proves nothing and
1718
+ * the caller must keep whatever state it already had.
1719
+ *
1720
+ * SCOPE: the probed queue is "<userId>-bg" - THIS user's dispatches only. A
1721
+ * chain launched by another collaborator or a widget end-user lives on their
1722
+ * queue and is invisible here, so "idle" must never be read as "nobody is
1723
+ * indexing this file", only as "this user's runs are over". The durable done::
1724
+ * marker (indexDoneUniqueId) is the cross-user signal.
1725
+ */
1726
+ declare function fetchLiveIndexingKeys(params: {
1727
+ service: string;
1728
+ owner: string;
1729
+ platform: 'claude' | 'openai';
1730
+ /** Same value the dispatch used - see bgIndexingQueueName. */
1731
+ userId?: string;
1732
+ }): Promise<{
1733
+ keys: Set<string>;
1734
+ checked: boolean;
1735
+ at: number;
1736
+ }>;
1737
+ /** Test hook: drop split-fetch state (all keys, or one). */
1738
+ declare function __resetSplitHistoryState(key?: string): void;
1739
+ type SplitHistoryResult = {
1740
+ list: any[];
1741
+ endOfList: boolean;
1742
+ startKeyHistory: any[];
1743
+ /** True when this chat had never been walked in this session — the first
1744
+ * paint. Consumers gate the "Loading indexing history" hint on it: a
1745
+ * mid-walk tab return restarts the walk for cursor safety but must stay
1746
+ * silent (flashing the hint on every return was the reported bug). */
1747
+ firstLoad?: boolean;
1748
+ /** Present only when `deferBg` was requested AND bg work remains: resolves
1749
+ * with the stub batch fetched in the background (the per-key lock is held
1750
+ * until it settles, so no other history call can interleave). The caller
1751
+ * merges the batch by timestamp — the same path older pages use. */
1752
+ bgPending?: Promise<{
1753
+ list: any[];
1754
+ endOfList: boolean;
1755
+ }>;
1756
+ };
1757
+ declare function getSplitChatHistory(params: {
1758
+ service: string;
1759
+ owner: string;
1760
+ platform: 'claude' | 'openai';
1761
+ userId?: string;
1762
+ }, fetchOptions: Record<string, any>,
1763
+ /** Test seam: replaces getChatHistory. Not for production callers. */
1764
+ _fetchImpl?: typeof getChatHistory): Promise<SplitHistoryResult>;
1765
+ type MapHistoryOptions = {
1766
+ clearedAt: number;
1767
+ projectId: string;
1768
+ /** View-side display formatter for "Indexing:/Reindexing: …" bubbles. */
1769
+ formatIndexingLabel: (name: string, mime?: string, size?: number | null, storagePath?: string, reindex?: boolean, continued?: boolean) => string;
1770
+ };
1771
+ declare function mapHistoryListToMessages(list: any[], platform: 'claude' | 'openai', opts: MapHistoryOptions): {
1772
+ messages: any[];
1773
+ runningItemIds: string[];
1774
+ };
1775
+ interface RescueDecisionContext {
1776
+ /** Is this `_serverItemId` in the page that was just fetched? */
1777
+ hasServerId: (id: string) => boolean;
1778
+ /**
1779
+ * The fetched page already shows a non-background pending assistant.
1780
+ *
1781
+ * Only meaningful for a bubble with NO server id, where it is the sole
1782
+ * available answer to "is this turn already represented?". For a bubble that
1783
+ * HAS one, hasServerId answers exactly the same question exactly, and applying
1784
+ * this on top of it would drop an in-flight turn whose server copy simply is
1785
+ * not in the page that was fetched.
1786
+ */
1787
+ pageHasPendingAssistant: boolean;
1788
+ /** state.sending: an immediate send is in flight for this chat. */
1789
+ sending: boolean;
1790
+ /** The bubble directly after this one in the local list. */
1791
+ next?: ChatMessage | null;
1792
+ /** The chat this fetch is FOR. A bubble stamped for another must not cross. */
1793
+ loadKey?: string;
1794
+ }
1795
+ declare function shouldRescueInFlightMessage(m: ChatMessage, ctx: RescueDecisionContext): boolean;
1796
+
1797
+ /**
1798
+ * Hold the reader's place in the message list.
1799
+ *
1800
+ * A chat box mutates constantly WITHOUT the reader asking for it: an older page
1801
+ * prepends, a poll resolves, an indexing row splices in or changes label, a link
1802
+ * chip goes grey, an image preview finishes decoding, the "Fetching history..."
1803
+ * bar appears and disappears. Every one of those changes the height of something
1804
+ * that may sit ABOVE the viewport, and the browser answers by keeping scrollTop —
1805
+ * which slides the sentence the user was reading out from under them.
1806
+ *
1807
+ * Both clients had their own copy of a row anchor for the ONE case each could
1808
+ * bracket (agent.vue watched its row-key list, the widget bracketed its full
1809
+ * re-render). Everything else — anything that changed a height without changing
1810
+ * the row SET, and everything asynchronous — was uncovered in both. This is the
1811
+ * single implementation, and it covers both shapes:
1812
+ *
1813
+ * preserve(fn) / capture() + restore(a)
1814
+ * A mutation you can bracket. Measures immediately before and immediately
1815
+ * after, so it is exact even when the mutation tears the list down.
1816
+ *
1817
+ * remember() + hold()
1818
+ * A layout change you CANNOT bracket — an image decoding, a font arriving,
1819
+ * a re-parse triggered from a promise. `remember()` runs from the view's
1820
+ * scroll handler, so the anchor is always the reader's own last position;
1821
+ * `hold()` puts that position back whenever something settles.
1822
+ *
1823
+ * The staleness rule is what makes the unbracketed half safe. A layout change
1824
+ * above the viewport does NOT change scrollTop — the browser preserves it, which
1825
+ * is precisely why the content appears to jump. So a remembered anchor is still
1826
+ * valid exactly while `box.scrollTop` equals the value it was captured at. If it
1827
+ * differs, something moved the box on purpose (the user scrolled, a clamp fired,
1828
+ * or the browser's own scroll anchoring already compensated), and `hold()`
1829
+ * re-captures rather than dragging the reader back to a position they left.
1830
+ *
1831
+ * DOM-free like the rest of the engine: the element shapes below are structural,
1832
+ * so real DOM nodes satisfy them while this file imports nothing from lib.dom.
1833
+ */
1834
+ interface AnchorRect {
1835
+ top: number;
1836
+ }
1837
+ interface AnchorRowEl {
1838
+ getAttribute(name: string): string | null;
1839
+ getBoundingClientRect(): AnchorRect;
1840
+ offsetHeight: number;
1841
+ parentNode: unknown;
1842
+ }
1843
+ /** Anything inside the list that resizes on its own schedule. See absorb(). */
1844
+ interface AnchorGrowableEl {
1845
+ getBoundingClientRect(): AnchorRect;
1846
+ offsetHeight: number;
1847
+ }
1848
+ interface AnchorBoxEl {
1849
+ children: ArrayLike<AnchorRowEl>;
1850
+ getBoundingClientRect(): AnchorRect;
1851
+ scrollTop: number;
1852
+ scrollHeight: number;
1853
+ clientHeight: number;
1854
+ }
1855
+ interface RowAnchor {
1856
+ /** data-row-key of the anchored row, or null when nothing was anchorable. */
1857
+ key: string | null;
1858
+ /** Offset of that row from the top of the viewport. Negative above the fold. */
1859
+ top: number;
1860
+ /** data-row-pos, present only on rows that can RELOCATE (see below). */
1861
+ pos: string | null;
1862
+ /** scrollTop at capture time. The staleness check, and the raw fallback. */
1863
+ scrollTop: number;
1864
+ /**
1865
+ * scrollHeight at capture time. How much the list GREW is the best available
1866
+ * answer when the anchored row itself cannot be found again, and the bound on
1867
+ * how far a correction can legitimately be.
1868
+ */
1869
+ scrollHeight: number;
1870
+ /**
1871
+ * The anchored element itself. A view that patches in place (Vue) keeps the
1872
+ * same node across an update, so restore is one rect read instead of a scan;
1873
+ * a view that rebuilds the list (the widget) drops it and falls back to the
1874
+ * key. Never trusted without re-checking that it is still in the box.
1875
+ */
1876
+ el: AnchorRowEl | null;
1877
+ }
1878
+ interface ScrollAnchorOptions {
1879
+ /** The scrolling message box, or null when it is not mounted. */
1880
+ getBox: () => AnchorBoxEl | null;
1881
+ /**
1882
+ * The reader is pinned to the bottom. There the bottom IS the anchor and the
1883
+ * scrollToBottom* paths own the position, so every method here no-ops.
1884
+ */
1885
+ isStuck: () => boolean;
1886
+ /**
1887
+ * Fall back to the raw scrollTop when the anchored row cannot be found again.
1888
+ *
1889
+ * For a view that REBUILDS the list (the widget's renderMessages), detaching
1890
+ * every child collapses scrollHeight and the browser clamps scrollTop to 0,
1891
+ * so the raw offset is strictly better than the clamp it would otherwise be
1892
+ * left with. For a view that patches in place (Vue) the browser has already
1893
+ * kept a sane position and re-imposing a stale offset is worse than nothing.
1894
+ */
1895
+ rawFallback?: boolean;
1896
+ }
1897
+ interface ScrollAnchor {
1898
+ /** Measure the reader's current place. Null while pinned to the bottom. */
1899
+ capture: () => RowAnchor | null;
1900
+ /** Put a captured place back. Safe to call with null. */
1901
+ restore: (anchor: RowAnchor | null) => void;
1902
+ /** capture -> mutate -> restore, for a mutation you can bracket. */
1903
+ preserve: <T>(mutate: () => T) => T;
1904
+ /** Record the reader's place. Call from the box's scroll handler. */
1905
+ remember: () => void;
1906
+ /** Put the remembered place back, if it is still the reader's own. */
1907
+ hold: () => void;
1908
+ /** Absorb one element's own resize. See below. */
1909
+ absorb: (el: AnchorGrowableEl | null | undefined) => void;
1910
+ /** Drop the remembered place (chat switch, unmount). */
1911
+ forget: () => void;
1912
+ }
1913
+ declare function createScrollAnchor(options: ScrollAnchorOptions): ScrollAnchor;
1914
+
1915
+ /**
1916
+ * Keep older history REACHABLE by paging until the message box actually gains
1917
+ * something to scroll to.
1918
+ *
1919
+ * Older history is paged in by one trigger only: the user scrolling to the top
1920
+ * of the message box. That trigger has two ways to die, and collapsed indexing
1921
+ * rows cause both:
1922
+ *
1923
+ * 1. The box never scrolls. A file's every indexing pass (the first plus every
1924
+ * CONTINUE pass, request AND response bubble each) folds into ONE row, so a
1925
+ * full history page — twenty-plus messages — can render as a single line.
1926
+ * Content shorter than the viewport fires no scroll event, so page 2 is
1927
+ * never requested and any conversation the user had before that upload is
1928
+ * permanently out of reach.
1929
+ * 2. The fetched page adds no height. A page that is entirely the same file's
1930
+ * earlier passes joins the collapsed row already on screen and renders
1931
+ * nothing new. The user, sitting at scrollTop 0, scrolls up again — and
1932
+ * because the position never changed, no further scroll event fires.
1933
+ *
1934
+ * Both are the same shape: fetch, re-measure, and keep going until the user
1935
+ * genuinely gained reachable content, history ran out, or the pager stopped
1936
+ * advancing. `isSatisfied` is what differs between the two (can the box scroll
1937
+ * at all / did it grow), so the loop below takes it as a predicate.
1938
+ *
1939
+ * DOM-free like the rest of the engine — the caller supplies the measurement and
1940
+ * awaits its own render before measuring, so agent.vue and the widget run the
1941
+ * identical loop over their own pagers.
1942
+ */
1943
+ /** Overflow (px) that counts as "the user can scroll here". Comfortably more
1944
+ * than the 60px top threshold that triggers the next page, so a filled box has
1945
+ * real room to scroll rather than sitting one pixel from the trigger. */
1946
+ declare const HISTORY_FILL_SLACK_PX = 64;
1947
+ /** Pages one fill pass will request before giving up. Reached only by a chat
1948
+ * whose history really is dozens of pages of one file's indexing passes; the
1949
+ * cap exists so a pager that stops advancing can never spin forever. */
1950
+ declare const MAX_HISTORY_FILL_PAGES = 24;
1951
+ type FillHistoryViewportOptions = {
1952
+ /** The user has reachable content and paging can stop. Called AFTER the
1953
+ * caller's own render has settled (nextTick / rAF), since only the caller
1954
+ * knows when its view has painted — hence the allowance for a promise. */
1955
+ isSatisfied: () => boolean | Promise<boolean>;
1956
+ /** All history is loaded — nothing left to page in. */
1957
+ isEndOfList: () => boolean;
1958
+ /** A history request is already in flight. Waited out, not treated as a stop
1959
+ * condition: a background first-page refresh (the queue-detect tick fires one
1960
+ * every couple of seconds while a file is indexing) would otherwise swallow
1961
+ * the user's scroll-up entirely, and scrolling up again from scrollTop 0
1962
+ * produces no second event to retry with. */
1963
+ isLoading: () => boolean;
1964
+ /** Messages currently loaded. Used to detect a page that added nothing, which
1965
+ * means the pager is not advancing and looping would never terminate. */
1966
+ messageCount: () => number;
1967
+ /** Fetch ONE older page (the caller's own fetchMore path, scroll-restore and
1968
+ * all). Return `false` when the request was NOT issued (the caller's own
1969
+ * single-flight guard swallowed it) so the loop retries instead of reading
1970
+ * the unchanged message count as an exhausted pager. Anything else, including
1971
+ * undefined, means it was attempted. */
1972
+ fetchOlder: () => Promise<boolean | void | any>;
1973
+ /** The chat this fill was started for is gone (project switched, view
1974
+ * unmounted, gate token bumped). Checked between pages so a stale fill can
1975
+ * never keep paging another chat's history. */
1976
+ isStale?: () => boolean;
1977
+ maxPages?: number;
1978
+ };
1979
+ /**
1980
+ * Page older history until `isSatisfied`, until history runs out, or until the
1981
+ * pager stops advancing. Never throws: a failed page ends the fill, and the
1982
+ * user's own scrolling remains the fallback trigger.
1983
+ */
1984
+ declare function fillHistoryViewport(opts: FillHistoryViewportOptions): Promise<void>;
1985
+ /**
1986
+ * One fill loop per view, with predicates COMBINED rather than dropped.
1987
+ *
1988
+ * Fills come from several places at once — a first page finishing, a window
1989
+ * resize, a row being collapsed, and the user's own scroll to the top — and a
1990
+ * plain "one at a time, drop the rest" guard picks the wrong winner: a resize
1991
+ * fill (satisfied the moment the box can scroll at all) would swallow the user's
1992
+ * scroll-up (which needs content specifically ABOVE them), and the scroll-up
1993
+ * cannot be retried, because a reader parked at scrollTop 0 produces no further
1994
+ * scroll event. Dropping the guard entirely is no better: every frame of a
1995
+ * window drag would start its own 24-page loop.
1996
+ *
1997
+ * So a request that arrives mid-loop ANDs its predicate into the running one:
1998
+ * the loop then keeps paging until EVERY caller is satisfied. Predicates that
1999
+ * come true are dropped as it goes, so the cost stays flat.
2000
+ */
2001
+ declare function createHistoryFiller(base: Omit<FillHistoryViewportOptions, 'isSatisfied'> & {
2002
+ /** Fired when the loop starts FETCHING and when it stops, and only on a real
2003
+ * change.
2004
+ *
2005
+ * This — not the caller's own per-request `isLoading` — is what "older
2006
+ * history is still coming in" means to a view. A fill is many pages, and
2007
+ * `isLoading` drops to false between every one of them, so anything
2008
+ * rendered off it flickers once per page for the whole loop. A collapsed
2009
+ * indexing row whose run begins above the loaded window renders exactly
2010
+ * that ("still loading this run" vs a status it cannot know yet), which is
2011
+ * why the loop has to publish its own span.
2012
+ *
2013
+ * Fetching, NOT requested. Most fills fetch nothing: they are fired on every
2014
+ * window resize, every row a user collapses, and every first-page load, and
2015
+ * the overwhelmingly common outcome is `isSatisfied` returning true on the
2016
+ * first look. Announcing at request time published a true/false pair for
2017
+ * each of those, and the widget's own satisfied-check spans two animation
2018
+ * frames — long enough for the browser to PAINT the intermediate state. Every
2019
+ * collapsed row strobed through "loading" on every resize tick. So the span
2020
+ * opens at the first actual page request, which is also the first moment the
2021
+ * claim is true. */
2022
+ onRunningChange?: (running: boolean) => void;
2023
+ }): {
2024
+ fill: (isSatisfied: () => boolean | Promise<boolean>) => Promise<void>;
2025
+ isRunning: () => boolean;
2026
+ };
2027
+
1852
2028
  /**
1853
2029
  * Background file-indexing turns, collapsed into ONE row per file.
1854
2030
  *
@@ -2419,7 +2595,30 @@ declare class ChatSession {
2419
2595
  *
2420
2596
  * `stop` comes from the SDK and may be absent on an older skapi-js, in which case the
2421
2597
  * poll simply cannot be stopped and is left running — see pausePolling.
2598
+ *
2599
+ * (This block documents _trackPoll, further down. The two methods below sit between it
2600
+ * and its subject.)
2422
2601
  */
2602
+ /**
2603
+ * Foreground poll with an early-probe race.
2604
+ *
2605
+ * skapi's poll() is a bare setInterval(fn, latency) with NO check at t=0, so the earliest a
2606
+ * reply can be observed is one full POLL_INTERVAL (3s) after dispatch. For a long generation
2607
+ * that granularity is free. For a SHORT one it is nearly pure dead time: a greeting that the
2608
+ * provider finishes in 1s still waits until the 3s tick, which measured as a large share of a
2609
+ * 5s "yo" round trip.
2610
+ *
2611
+ * So keep the 3s interval as the steady state, and additionally point-look-up the item a few
2612
+ * times early, on a widening schedule. Whichever answers first wins and the other is stopped.
2613
+ * The probe uses the csrHistoryItemLookup hook both clients already implement; without it this
2614
+ * degrades to exactly the old behaviour.
2615
+ *
2616
+ * FOREGROUND ONLY. Background indexing polls keep the flat cadence: nobody is watching them,
2617
+ * and they are the ones bounded by MAX_CONCURRENT_BG_POLLS, so adding probes there would spend
2618
+ * the request budget the cap exists to protect.
2619
+ */
2620
+ attachForegroundPoll(source: any, itemId: string, opts?: any): any;
2621
+ private _fgPollWithEarlyProbe;
2423
2622
  private _trackPoll;
2424
2623
  /** Background polls currently attached, for the MAX_CONCURRENT_BG_POLLS budget.
2425
2624
  * Counts the registry rather than a separate tally so it cannot drift: every
@@ -2473,6 +2672,40 @@ declare class ChatSession {
2473
2672
  * still render) and a later expand retries. */
2474
2673
  hydrateCompactItems(itemIds: string[]): Promise<void>;
2475
2674
  updateHistoryCache(): void;
2675
+ /**
2676
+ * Give the immediate-send pair the server's id for their turn, the moment the
2677
+ * dispatch learns it.
2678
+ *
2679
+ * WHY THIS EXISTS. An immediate send pushes its user bubble and its
2680
+ * "Thinking..." placeholder locally, and until now neither ever carried a
2681
+ * _serverItemId — only the QUEUED path stamped one, off its ack. So for the
2682
+ * whole life of the turn there was no way to tell the local copy and the
2683
+ * server's copy of the SAME turn apart, and the history merge fell back to a
2684
+ * heuristic: rescue the local pair unless the freshly-fetched page happens to
2685
+ * contain a pending assistant.
2686
+ *
2687
+ * That heuristic has a hole exactly one poll interval wide. The server settles
2688
+ * the request; for up to POLL_INTERVAL the client has not noticed, so
2689
+ * `state.sending` is still true and the local pair is still on screen — while a
2690
+ * history fetch issued in that window returns the turn ALREADY SETTLED, with no
2691
+ * pending assistant in it. The rescue then re-appends the local pair below the
2692
+ * server's copy (the question, twice), and when the poll finally resolves,
2693
+ * typewriteLatestReply writes the answer into the rescued placeholder because it
2694
+ * is the only pending assistant left (the answer, twice). updateHistoryCache
2695
+ * persists the result, so it survives every later visit.
2696
+ *
2697
+ * Navigating away while waiting and coming back is what lands a fetch in that
2698
+ * window: a remount runs refreshGate -> a fresh first page, at an arbitrary
2699
+ * moment relative to the 3s poll.
2700
+ *
2701
+ * With the id on the bubbles, both clients' rescue loops skip them through the
2702
+ * dedup they already have (`_serverItemId is in this page`), the reply the
2703
+ * dispatch caches inherits the id too, and nothing needs a new special case.
2704
+ *
2705
+ * Matched by _localId, never by index: a file's indexing rows are spliced in
2706
+ * above these bubbles while the request is in flight.
2707
+ */
2708
+ private _stampTurnWithItemId;
2476
2709
  /**
2477
2710
  * Land a resolved reply in the history cache of a chat that is NOT currently
2478
2711
  * visible, without touching state.messages. Mirrors the cache-only path in
@@ -2575,6 +2808,25 @@ declare class ChatSession {
2575
2808
  */
2576
2809
  settleStagedMessage(stageId: string): void;
2577
2810
  dispatchComposedMessage(composed: string, useBgQueue?: boolean, composedForLlm?: string, extractContent?: any, fileUrls?: any, pinned?: PinnedDispatchContext): void;
2811
+ /**
2812
+ * Scroll for a dispatch that is going out NOW, but never for one arriving late.
2813
+ *
2814
+ * A turn with attachments does not dispatch when the user hits Send: it waits out
2815
+ * its uploads and then its whole indexing chain, which is minutes
2816
+ * (awaitIndexingDrained). By then the reader has very often scrolled up into
2817
+ * history to pass the time, and the forcing scrollToBottom yanked them out of it
2818
+ * — and worse, force-pinned stickToBottom, which no-ops every method on the
2819
+ * scroll anchor and re-arms the queue-detect poll whose only bail is
2820
+ * !stickToBottom.
2821
+ *
2822
+ * `stageId` is the exact marker for that case: only the attachment path ever
2823
+ * produces one. The gesture itself was already paid for at stage time, where
2824
+ * stageOutgoingMessage forces the scroll while the user is still looking at the
2825
+ * composer. Deliberately NOT gated on "did we find the staged bubble" — a remount
2826
+ * rebuilds from the cache and the turn is appended rather than replaced, which is
2827
+ * just as late and just as unrequested.
2828
+ */
2829
+ private scrollForDispatch;
2578
2830
  promoteNextBgQueuedToRunning(): void;
2579
2831
  promoteNextQueuedToRunning(): void;
2580
2832
  /**
@@ -2593,6 +2845,24 @@ declare class ChatSession {
2593
2845
  private _ownThinkingIndex;
2594
2846
  resolveQueuedUserBubble(serverId?: string): number | undefined;
2595
2847
  insertAtTarget(msg: ChatMessage, targetIdx: number): void;
2848
+ /**
2849
+ * The server's OWN copy of this turn is already on screen.
2850
+ *
2851
+ * A first-page fetch can land between the server settling the item and this
2852
+ * poll's tick, and now that the local bubbles carry the item id
2853
+ * (_stampTurnWithItemId) the rescue correctly drops them and renders the
2854
+ * server's settled pair instead. There is then nothing left to resolve: the
2855
+ * -1 fallback would push the answer in a SECOND time at the bottom of the list,
2856
+ * and the positional fallbacks would hijack some other turn's bubble.
2857
+ *
2858
+ * The USER bubble counts, not just a settled assistant: an item whose answer is
2859
+ * empty produces no assistant bubble at all in the mapper, and that variant
2860
+ * would otherwise still bottom-push "No text response received...". While a turn
2861
+ * is genuinely live its user bubble is always pending (the queued branch sets
2862
+ * isPendingQueued, promoteNextQueuedToRunning sets isPendingInProcess), so this
2863
+ * cannot fire early.
2864
+ */
2865
+ private _turnAlreadyRendered;
2596
2866
  onQueuedSendResponse(_composed: string, response: any, platform: string, serverId?: string, ownerKey?: string): void;
2597
2867
  onQueuedSendError(_composed: string, err: any, serverId?: string, ownerKey?: string): void;
2598
2868
  cancelQueuedMessage(msg: ChatMessage, idx: number): void;
@@ -2795,4 +3065,4 @@ declare class ChatSession {
2795
3065
  bumpGate(): void;
2796
3066
  }
2797
3067
 
2798
- export { type AiAgentPlatform, type AttachmentFailureGroup, type AttachmentParser, type AttachmentSaveInfo, BG_INDEXING_QUEUE_SUFFIX, BOM, BOM_EXTS, type BgTaskEntry, type BoundedChatOptions, type BuildDisplayListOptions, type BuildIndexingUserMessageOptions, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, type CallClaudeWithMcpParams, type ChatEngineConfig, type ChatGreetingParams, type ChatGreetingParts, type ChatHost, type ChatIdentity, type ChatMessage, ChatSession, type ChatState, type ChatSystemPromptParams, type ClaudeMcpServerRequest, type ClaudeMcpToolConfig, type ClaudeMessage, type ClaudeRole, type ComposedUserMessage, DEFAULT_CLAUDE_MODEL, DEFAULT_CONTEXT_WINDOW, DEFAULT_OPENAI_MODEL, type DisplayEntry, EMPTY_INDEXING_REPLY, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, EXT_CONTENT_TYPES, type EncodingClass, type ExtractDirective, type FillHistoryViewportOptions, 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, type ImagePreviewContext, type IndexRunPatch, type IndexRunStatus, type IndexingAttachmentInfo, type IndexingFileRef, type IndexingGroup, type IndexingGroupStatus, type IndexingRequestRef, type IndexingSystemPromptParams, type InlineLinkContext, type InlineLinkMarkupOptions, type InlineLinkPart, 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, type MapHistoryOptions, OUTPUT_TOKEN_RESERVE, type OpenAIMessage, POLL_INTERVAL, PRESIGN_SAFETY_MARGIN_MS, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, PREVIEW_URL_EXPIRES_SECONDS, type ParsedAiAgent, type PinnedDispatchContext, type PreviewImageEl, RENDER_FROM_TOKEN, RTF_EXTS, RUN_RECORD_WORKING_STALE_MS, type RenderableInlineLink, type RunStubInfo, TOOL_AND_RESPONSE_BUFFER, type VisionProfile, 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 };
3068
+ export { type AiAgentPlatform, type AnchorBoxEl, type AnchorRowEl, type AttachmentFailureGroup, type AttachmentParser, type AttachmentSaveInfo, BG_INDEXING_QUEUE_SUFFIX, BOM, BOM_EXTS, type BgTaskEntry, type BoundedChatOptions, type BuildDisplayListOptions, type BuildIndexingUserMessageOptions, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, type CallClaudeWithMcpParams, type ChatEngineConfig, type ChatGreetingParams, type ChatGreetingParts, type ChatHost, type ChatIdentity, type ChatMessage, ChatSession, type ChatState, type ChatSystemPromptParams, type ClaudeMcpServerRequest, type ClaudeMcpToolConfig, type ClaudeMessage, type ClaudeRole, type ComposedUserMessage, DEFAULT_CLAUDE_MODEL, DEFAULT_CONTEXT_WINDOW, DEFAULT_OPENAI_MODEL, type DisplayEntry, EMPTY_INDEXING_REPLY, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, EXT_CONTENT_TYPES, type EncodingClass, type ExtractDirective, type FillHistoryViewportOptions, 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, type ImagePreviewContext, type IndexRunPatch, type IndexRunStatus, type IndexingAttachmentInfo, type IndexingFileRef, type IndexingGroup, type IndexingGroupStatus, type IndexingRequestRef, type IndexingSystemPromptParams, type InlineLinkContext, type InlineLinkMarkupOptions, type InlineLinkPart, 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, type MapHistoryOptions, OUTPUT_TOKEN_RESERVE, type OpenAIMessage, POLL_INTERVAL, PRESIGN_SAFETY_MARGIN_MS, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, PREVIEW_LAYOUT_BOX_SELECTOR, PREVIEW_URL_EXPIRES_SECONDS, type ParsedAiAgent, type PinnedDispatchContext, type PreviewImageEl, RENDER_FROM_TOKEN, RTF_EXTS, RUN_RECORD_WORKING_STALE_MS, type RenderableInlineLink, type RescueDecisionContext, type RowAnchor, type RunStubInfo, type ScrollAnchor, type ScrollAnchorOptions, TOOL_AND_RESPONSE_BUFFER, type VisionProfile, 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 };