bunnyquery 1.8.6 → 1.8.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bunnyquery.css +95 -5
- package/bunnyquery.js +1470 -111
- package/dist/engine.cjs +1071 -80
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +608 -169
- package/dist/engine.d.ts +608 -169
- package/dist/engine.mjs +1048 -81
- package/dist/engine.mjs.map +1 -1
- package/package.json +1 -1
- package/src/engine/budget.ts +207 -68
- package/src/engine/config.ts +48 -0
- package/src/engine/errors.ts +38 -0
- package/src/engine/history.ts +546 -6
- package/src/engine/host.ts +7 -0
- package/src/engine/image_preview.ts +0 -0
- package/src/engine/index.ts +15 -1
- package/src/engine/indexing_groups.ts +248 -3
- package/src/engine/links.ts +143 -6
- package/src/engine/office.ts +24 -1
- package/src/engine/prompts/chat_system_prompt.ts +1 -1
- package/src/engine/requests.ts +164 -11
- package/src/engine/session.ts +544 -35
- package/src/widget.css +35 -1
- package/styles/chat.css +60 -4
package/dist/engine.d.mts
CHANGED
|
@@ -95,6 +95,57 @@ interface ChatEngineConfig {
|
|
|
95
95
|
* until the worker is deployed, then flip it per environment.
|
|
96
96
|
*/
|
|
97
97
|
windowedIndexing?: boolean;
|
|
98
|
+
/**
|
|
99
|
+
* Mint the durable "indexing finished" marker record ("done::<path>",
|
|
100
|
+
* reference "src::<path>", table __INDEXING__ — same shape the backend
|
|
101
|
+
* worker writes via /internal/index-complete) for runs whose completion
|
|
102
|
+
* THIS CLIENT knows deterministically: a single-pass file's settled pass,
|
|
103
|
+
* or a client-driven chain whose reply carried the completion token.
|
|
104
|
+
* Worker-driven chains are NOT minted from the client (their completion is
|
|
105
|
+
* only ever inferred here); the worker writes their marker itself.
|
|
106
|
+
* Must be best-effort: tolerate the marker already existing and never
|
|
107
|
+
* throw. Optional so older consumers keep the pre-marker inference.
|
|
108
|
+
*/
|
|
109
|
+
mintIndexDoneMarker?: (info: {
|
|
110
|
+
service: string;
|
|
111
|
+
storagePath: string;
|
|
112
|
+
}) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Create-or-update the per-file indexing RUN record ("run::<path>",
|
|
115
|
+
* reference "src::<path>", table __INDEXING__). The record is the durable
|
|
116
|
+
* "a run exists and this is its status" signal that lets chat rows and
|
|
117
|
+
* files-page badges paint without scanning bg history.
|
|
118
|
+
*
|
|
119
|
+
* The consumer implements upsert semantics (the records API has none):
|
|
120
|
+
* create, and on "is already taken" look the record up by unique_id and
|
|
121
|
+
* re-post with its record_id, merging `patch` over the stored data.
|
|
122
|
+
* Status precedence is the consumer's job too: 'working' must NEVER
|
|
123
|
+
* overwrite a terminal status (done/error/cancelled) — a late create from
|
|
124
|
+
* a slow enqueue must not resurrect a run another writer already closed.
|
|
125
|
+
* Must be best-effort and never throw. Optional: without it the engine
|
|
126
|
+
* behaves exactly as before (legacy scan/probe path).
|
|
127
|
+
*/
|
|
128
|
+
upsertIndexRunRecord?: (info: {
|
|
129
|
+
service: string;
|
|
130
|
+
storagePath: string;
|
|
131
|
+
patch: {
|
|
132
|
+
status: 'working' | 'done' | 'error' | 'cancelled';
|
|
133
|
+
filename?: string;
|
|
134
|
+
started?: number;
|
|
135
|
+
finished?: number;
|
|
136
|
+
error?: string;
|
|
137
|
+
queue?: string;
|
|
138
|
+
};
|
|
139
|
+
}) => void;
|
|
140
|
+
/**
|
|
141
|
+
* Single-item csr-poll point lookup (skapi.util.request('csr-poll', {id,
|
|
142
|
+
* service, owner}, {auth:true})). For a RESOLVED item the backend returns
|
|
143
|
+
* the provider response body itself; for a failed one, the resolved error.
|
|
144
|
+
* Used by ChatSession.hydrateCompactItems to fetch the real bodies of
|
|
145
|
+
* compact history stubs when the user expands an indexing row. Optional:
|
|
146
|
+
* without it, stubs keep their server-extracted heads.
|
|
147
|
+
*/
|
|
148
|
+
csrHistoryItemLookup?: (fullId: string, service: string, owner: string) => Promise<any>;
|
|
98
149
|
}
|
|
99
150
|
declare function configureChatEngine(config: ChatEngineConfig): void;
|
|
100
151
|
declare function chatEngineConfig(): ChatEngineConfig;
|
|
@@ -149,7 +200,26 @@ declare function composeUserMessage(text: string, attachmentUrls: Array<{
|
|
|
149
200
|
name: string;
|
|
150
201
|
url: string;
|
|
151
202
|
storagePath?: string;
|
|
152
|
-
}
|
|
203
|
+
}>, opts?: {
|
|
204
|
+
/**
|
|
205
|
+
* Inline each server-extractable attachment's whole text into the
|
|
206
|
+
* prompt (the `_skapi_extract` directives + BEGIN/END FILE CONTENT
|
|
207
|
+
* block). Default true, which is right when the file's content is
|
|
208
|
+
* nowhere else yet.
|
|
209
|
+
*
|
|
210
|
+
* Pass FALSE when the turn is dispatched AFTER the file's indexing run
|
|
211
|
+
* has drained. Extraction is the same server-side download+parse the
|
|
212
|
+
* indexing pass already performed, so inlining repeats it: the worker
|
|
213
|
+
* fetches and re-parses every attachment a second time (which reads,
|
|
214
|
+
* from the outside, exactly like the file being indexed again), and the
|
|
215
|
+
* whole file text is re-sent as prompt tokens. It is also the WORSE
|
|
216
|
+
* copy for anything large, because inline extraction truncates at
|
|
217
|
+
* MAX_EXTRACTED_CHARS while the indexed records cover the file end to
|
|
218
|
+
* end. The model reaches the content through the records
|
|
219
|
+
* (getRecords with reference "src::<path>") or readFileContent.
|
|
220
|
+
*/
|
|
221
|
+
inlineExtractedContent?: boolean;
|
|
222
|
+
}): ComposedUserMessage;
|
|
153
223
|
|
|
154
224
|
/**
|
|
155
225
|
* Attachment helpers shared by every consumer's view layer.
|
|
@@ -309,26 +379,56 @@ declare function getErrorMessage(input: any): string;
|
|
|
309
379
|
declare function isErrorResponseBody(response: any): boolean;
|
|
310
380
|
declare function isNonRetryableRequestError(input: any): boolean;
|
|
311
381
|
declare function isAuthExpiredError(input: any): boolean;
|
|
382
|
+
/**
|
|
383
|
+
* True when the AI PROVIDER rejected the project's own API key.
|
|
384
|
+
*
|
|
385
|
+
* Deliberately narrow, and deliberately NOT the same question as
|
|
386
|
+
* isAuthExpiredError: that one is about OUR session/MCP bearer going stale,
|
|
387
|
+
* which the client fixes by refreshing and resending. This one means the key
|
|
388
|
+
* the project owner pasted is wrong or revoked, which only a human can fix.
|
|
389
|
+
*
|
|
390
|
+
* A bare 401 is NOT enough to conclude it (the MCP bearer expiring is also a
|
|
391
|
+
* 401), so this matches only the provider's key-specific markers:
|
|
392
|
+
* Anthropic -> `authentication_error`, "invalid x-api-key"
|
|
393
|
+
* OpenAI -> `invalid_api_key`, "Incorrect API key provided"
|
|
394
|
+
* Accepts a response object, a thrown error, or the message string those get
|
|
395
|
+
* reduced to by getErrorMessage, because the view usually only keeps the text.
|
|
396
|
+
*/
|
|
397
|
+
declare function isProviderApiKeyError(input: any): boolean;
|
|
312
398
|
|
|
313
399
|
declare var CONTEXT_WINDOW_DEFAULT: Record<string, number>;
|
|
314
400
|
declare var CONTEXT_WINDOW_BY_MODEL: Record<string, number>;
|
|
401
|
+
declare var MAX_OUTPUT_BY_MODEL: Record<string, number>;
|
|
402
|
+
declare var DEFAULT_CONTEXT_WINDOW: number;
|
|
315
403
|
/**
|
|
316
|
-
* Record context windows from a provider models listing.
|
|
317
|
-
*
|
|
318
|
-
* so passing an OpenAI listing is a no-op rather than an error.
|
|
404
|
+
* Record context windows and output caps from a provider models listing. Reads
|
|
405
|
+
* `max_input_tokens` and `max_tokens` (Anthropic); items without them are
|
|
406
|
+
* skipped, so passing an OpenAI listing is a no-op rather than an error.
|
|
407
|
+
*
|
|
408
|
+
* Note the asymmetry against the static table: Anthropic reports
|
|
409
|
+
* `max_input_tokens` (input only) where CONTEXT_WINDOW_BY_MODEL holds totals, so
|
|
410
|
+
* a registered Claude window is treated as a total and loses its output cap
|
|
411
|
+
* worth of budget. That is deliberate — under-spending the window is safe, and
|
|
412
|
+
* with no compaction beta enabled overrunning it is a hard error.
|
|
319
413
|
*/
|
|
320
414
|
declare function registerModelContextWindows(models: Array<{
|
|
321
415
|
id?: string;
|
|
322
416
|
max_input_tokens?: number;
|
|
417
|
+
max_tokens?: number;
|
|
323
418
|
}> | null | undefined): void;
|
|
324
419
|
declare function setProjectContextWindow(projectId: string, tokens: number | null | undefined): void;
|
|
325
420
|
declare function getProjectContextWindow(projectId: string): number | null;
|
|
421
|
+
declare var MAX_OUTPUT_TOKENS: number;
|
|
326
422
|
declare var OUTPUT_TOKEN_RESERVE: number;
|
|
327
423
|
declare var TOOL_AND_RESPONSE_BUFFER: number;
|
|
328
424
|
declare var MIN_INPUT_TOKEN_BUDGET: number;
|
|
425
|
+
declare var MIN_PER_REQUEST_INPUT_CAP: number;
|
|
426
|
+
/** @deprecated renamed to {@link MIN_PER_REQUEST_INPUT_CAP} (no longer Claude-only). */
|
|
329
427
|
declare var CLAUDE_PER_REQUEST_INPUT_CAP: number;
|
|
330
428
|
declare var MAX_HISTORY_MESSAGES: number;
|
|
331
429
|
declare var HISTORY_TOKEN_BUDGET: number;
|
|
430
|
+
declare var INPUT_CAP_RATIO: number;
|
|
431
|
+
/** @deprecated renamed to {@link INPUT_CAP_RATIO} (no longer Claude-only). */
|
|
332
432
|
declare var CLAUDE_INPUT_CAP_RATIO: number;
|
|
333
433
|
declare var HISTORY_BUDGET_RATIO: number;
|
|
334
434
|
declare function estimateTextTokens(text: string): number;
|
|
@@ -336,20 +436,21 @@ declare function estimateMessageTokens(msg: {
|
|
|
336
436
|
role: string;
|
|
337
437
|
content: string;
|
|
338
438
|
}): number;
|
|
439
|
+
declare function getModelContextWindow(platform: string, model?: string): number;
|
|
339
440
|
/**
|
|
340
|
-
*
|
|
341
|
-
*
|
|
342
|
-
*
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
*
|
|
347
|
-
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
350
|
-
* stops at the first hit, so a more specific entry always wins over its family.
|
|
441
|
+
* How many output tokens to ask for. We never want more than MAX_OUTPUT_TOKENS,
|
|
442
|
+
* but a model whose own cap is lower rejects the request outright, so clamp to
|
|
443
|
+
* whichever is smaller. Models with no known cap keep MAX_OUTPUT_TOKENS.
|
|
444
|
+
*/
|
|
445
|
+
declare function getMaxOutputTokens(platform: string, model?: string): number;
|
|
446
|
+
/**
|
|
447
|
+
* The window a request is actually budgeted at: the per-project override when
|
|
448
|
+
* one is set, otherwise DEFAULT_CONTEXT_WINDOW. Both are clamped to the model's
|
|
449
|
+
* hard ceiling, because a budget above the ceiling builds a request the provider
|
|
450
|
+
* rejects, and a stored override outlives the model it was chosen under.
|
|
351
451
|
*/
|
|
352
452
|
declare function getContextWindow(platform: string, model?: string, projectId?: string): number;
|
|
453
|
+
declare function getInputTokenBudget(platform: string, model?: string, projectId?: string): number;
|
|
353
454
|
declare function stripFileBlocksFromHistory(content: string): string;
|
|
354
455
|
type BoundedChatOptions = {
|
|
355
456
|
platform: string;
|
|
@@ -488,6 +589,23 @@ declare var LINK_LABEL_MAX_DISPLAY_CHARS: number;
|
|
|
488
589
|
* not, which is precisely the kind of divergence a shared constant exists to stop.
|
|
489
590
|
*/
|
|
490
591
|
declare var EXPIRED_LINK_REFRESH_EXPIRES_SECONDS: number;
|
|
592
|
+
/**
|
|
593
|
+
* Lifetime of the url minted for an inline image PREVIEW.
|
|
594
|
+
*
|
|
595
|
+
* Longer than the click url above, and for a different reason. A click hands the
|
|
596
|
+
* user a url they may keep, so it stays short. A preview url is consumed by the
|
|
597
|
+
* page itself and never leaves it, and it is the ONE lever on how long the
|
|
598
|
+
* downloaded picture stays reusable: get_signed_url will not cache a mint for
|
|
599
|
+
* longer than the credential inside it survives, so `browser_cache` cannot buy
|
|
600
|
+
* local availability that `expires` has not paid for. Twenty minutes meant every
|
|
601
|
+
* image re-downloaded three times an hour of ordinary reading.
|
|
602
|
+
*
|
|
603
|
+
* An hour, giving 55 minutes of cache once the server's five minute headroom is
|
|
604
|
+
* taken off. Short enough that a leaked preview url is not a standing grant, long
|
|
605
|
+
* enough that a conversation does not re-fetch its own pictures while the user is
|
|
606
|
+
* still reading it.
|
|
607
|
+
*/
|
|
608
|
+
declare var PREVIEW_URL_EXPIRES_SECONDS: number;
|
|
491
609
|
/**
|
|
492
610
|
* Seconds the browser may reuse a minted preview url (`browser_cache`).
|
|
493
611
|
*
|
|
@@ -497,12 +615,17 @@ declare var EXPIRED_LINK_REFRESH_EXPIRES_SECONDS: number;
|
|
|
497
615
|
* url comes back out of the browser cache, so the body already on disk stays
|
|
498
616
|
* addressable.
|
|
499
617
|
*
|
|
500
|
-
*
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
*
|
|
618
|
+
* A CEILING, not a promise. get_signed_url caps what it grants at the lifetime of
|
|
619
|
+
* the url inside the response (expires minus headroom, so 15 minutes for the
|
|
620
|
+
* platform's 20 minute url), because a mint cached for longer than its own
|
|
621
|
+
* credential is a guaranteed 403 that the browser keeps serving from its own
|
|
622
|
+
* store. Asking for the week is still right: it says what this client would
|
|
623
|
+
* reuse if the url were stable by construction, and the server decides.
|
|
624
|
+
*
|
|
625
|
+
* What keeps an image painting is the cached BODY, not a live url. Once the
|
|
626
|
+
* browser evicts that body it refetches with a url that has since expired, gets a
|
|
627
|
+
* 403, and the error path re-mints with `refresh` and mintCacheBustStamp. That
|
|
628
|
+
* path is load-bearing, not a rare fallback.
|
|
506
629
|
*
|
|
507
630
|
* A week is the platform default for reading a private file, not a number chosen
|
|
508
631
|
* here: skapi-js reads every private record file with
|
|
@@ -525,6 +648,69 @@ declare var PREVIEW_BROWSER_CACHE_SECONDS: number;
|
|
|
525
648
|
* dead url with no way to notice; deriving it makes that unrepresentable.
|
|
526
649
|
*/
|
|
527
650
|
declare var LINK_REFRESH_WINDOW_MS: number;
|
|
651
|
+
/**
|
|
652
|
+
* Cache generation for the mint request url. BUMP THIS to abandon every mint
|
|
653
|
+
* response browsers are currently holding.
|
|
654
|
+
*
|
|
655
|
+
* Generation 2 retires the entries written before 2026-08-11. Those were stored
|
|
656
|
+
* with `max-age=604800` around a presign that dies in twenty minutes, so from
|
|
657
|
+
* minute 21 each one is a guaranteed 403 that the browser keeps serving from its
|
|
658
|
+
* own store for the rest of the week. The server no longer grants a lifetime a
|
|
659
|
+
* url cannot back (get_signed_url resolve_browser_cache), but that fixes what is
|
|
660
|
+
* written from now on and cannot reach what is already stored on a user's
|
|
661
|
+
* device. Changing the url is the only thing that can: an entry nobody requests
|
|
662
|
+
* again is an entry that cannot answer again.
|
|
663
|
+
*/
|
|
664
|
+
declare var MINT_CACHE_GENERATION: number;
|
|
665
|
+
/**
|
|
666
|
+
* Window stamp for a REFRESH mint.
|
|
667
|
+
*
|
|
668
|
+
* WINDOWED, not Date.now(): a per-call stamp is a new cache key per image per
|
|
669
|
+
* retry, which is what made the original `nocache` parameter worse than the
|
|
670
|
+
* disease. One stamp per refresh window means every repair inside those minutes
|
|
671
|
+
* shares a single entry, and it rotates before the url it carries can die.
|
|
672
|
+
*/
|
|
673
|
+
declare function mintCacheBustStamp(now?: number): number;
|
|
674
|
+
/**
|
|
675
|
+
* The `nocache` value for a preview mint: the generation, plus a window stamp
|
|
676
|
+
* when this mint is a repair.
|
|
677
|
+
*
|
|
678
|
+
* A repair MUST reach the origin, and the request header the clients used to
|
|
679
|
+
* rely on cannot do it. `Cache-Control: no-cache` is not a CORS-safelisted
|
|
680
|
+
* request header, and the record gateway's preflight answers
|
|
681
|
+
* `Access-Control-Allow-Headers` WITHOUT it (verified against the live api on
|
|
682
|
+
* 2026-08-11), so a mint carrying that header is rejected by the browser before
|
|
683
|
+
* it is ever sent. Every repair therefore failed, in every browser, and the chip
|
|
684
|
+
* went straight to "(unavailable)". Only a phone noticed, because only a phone
|
|
685
|
+
* drops image bodies often enough to need the repair at all.
|
|
686
|
+
*
|
|
687
|
+
* A query parameter has no such problem: it is part of the url, so it needs no
|
|
688
|
+
* preflight and no cooperation from the cache.
|
|
689
|
+
*/
|
|
690
|
+
declare function previewMintCacheToken(refresh?: boolean): string;
|
|
691
|
+
/**
|
|
692
|
+
* How long before a presign dies we stop handing it out.
|
|
693
|
+
*
|
|
694
|
+
* A url served with one second left is a 403 with extra steps: the request still
|
|
695
|
+
* has to reach S3, and an image body still has to start arriving.
|
|
696
|
+
*/
|
|
697
|
+
declare var PRESIGN_SAFETY_MARGIN_MS: number;
|
|
698
|
+
/**
|
|
699
|
+
* When the url in hand actually dies, read out of the url itself, or null if it
|
|
700
|
+
* carries no expiry we recognise.
|
|
701
|
+
*
|
|
702
|
+
* Every client-side cache here ages a url from the moment it ARRIVED, which is
|
|
703
|
+
* only the same thing as its lifetime when the mint went to the network. Once
|
|
704
|
+
* mint responses are cacheable that assumption breaks: a mint answered from the
|
|
705
|
+
* browser's store can be nearly as old as its own max-age, and the client then
|
|
706
|
+
* adds its own reuse window on top, so a 20 minute credential can be handed to an
|
|
707
|
+
* <img> half an hour after it was signed. Asking the url when it dies removes the
|
|
708
|
+
* stacking instead of trying to budget for it.
|
|
709
|
+
*
|
|
710
|
+
* Both signature versions, because the platform mints SigV2 through the host
|
|
711
|
+
* bucket and SigV4 elsewhere.
|
|
712
|
+
*/
|
|
713
|
+
declare function presignExpiryEpochMs(url: string): number | null;
|
|
528
714
|
declare function createInlineLinkRegex(): RegExp;
|
|
529
715
|
declare function safeDecodeURIComponent(v: string): string;
|
|
530
716
|
declare function encodePathSegments(path: string): string;
|
|
@@ -681,6 +867,17 @@ declare function classifyInlineLink(full: string, groups: Array<string | undefin
|
|
|
681
867
|
*/
|
|
682
868
|
declare function linkUnavailableKeyForPath(remotePath: string): string;
|
|
683
869
|
declare function linkUnavailableKeyForHref(href: string): string;
|
|
870
|
+
/**
|
|
871
|
+
* Every key a stored file can be marked under, given only its path.
|
|
872
|
+
*
|
|
873
|
+
* Marking writes ONE key (whichever identifier the failing call had) and the
|
|
874
|
+
* lookup ORs all of them, which is fine in one direction and wrong in the other:
|
|
875
|
+
* a view that later learns the file is reachable knows only the path, and
|
|
876
|
+
* clearing `path:` alone leaves a chip greyed by a failed CLICK (which marks
|
|
877
|
+
* `href:` too) exactly as dead as before. The placeholder href is derived from
|
|
878
|
+
* the path, so both keys can be rebuilt from it.
|
|
879
|
+
*/
|
|
880
|
+
declare function linkUnavailableKeysForPath(remotePath: string): string[];
|
|
684
881
|
declare function isLinkUnavailable(link: {
|
|
685
882
|
href?: string;
|
|
686
883
|
expiredHref?: string;
|
|
@@ -889,153 +1086,6 @@ type ParsedAiAgent = {
|
|
|
889
1086
|
declare function parseAiAgentValue(value: string | null | undefined): ParsedAiAgent;
|
|
890
1087
|
declare function buildAiAgentValue(platform: string | null | undefined, model?: string | null, contextWindow?: number | null): string;
|
|
891
1088
|
|
|
892
|
-
declare function filterListByClearHorizon(list: any[], clearedAt: number): any[];
|
|
893
|
-
declare function normalizeTextContent(content: any): string;
|
|
894
|
-
declare function extractLastUserTextFromRequest(requestBody: any): string;
|
|
895
|
-
/** The two openings an indexing prompt can have. A bg-queue item that starts with
|
|
896
|
-
* neither is an ordinary chat that happened to be routed onto that queue. */
|
|
897
|
-
declare function isIndexingRequestText(userText: any): boolean;
|
|
898
|
-
type IndexingRequestRef = {
|
|
899
|
-
name: string;
|
|
900
|
-
path?: string;
|
|
901
|
-
mime?: string;
|
|
902
|
-
size?: number;
|
|
903
|
-
/** A CONTINUE pass rather than the run's first. */
|
|
904
|
-
continued: boolean;
|
|
905
|
-
};
|
|
906
|
-
/**
|
|
907
|
-
* The file an indexing prompt is about, read back out of the prompt itself.
|
|
908
|
-
*
|
|
909
|
-
* The prompt is the only description of the pass that survives on the server, so
|
|
910
|
-
* this is how BOTH a history rebuild and a worker-minted pass the client never
|
|
911
|
-
* dispatched (ChatSession._adoptWorkerIndexingPasses) recover the file. Shared so
|
|
912
|
-
* the two produce the same `_indexFile`, which is what makes them group together.
|
|
913
|
-
*/
|
|
914
|
-
declare function parseIndexingRequestText(userText: any): IndexingRequestRef | null;
|
|
915
|
-
type MapHistoryOptions = {
|
|
916
|
-
clearedAt: number;
|
|
917
|
-
projectId: string;
|
|
918
|
-
/** View-side display formatter for "Indexing:/Reindexing: …" bubbles. */
|
|
919
|
-
formatIndexingLabel: (name: string, mime?: string, size?: number | null, storagePath?: string, reindex?: boolean, continued?: boolean) => string;
|
|
920
|
-
};
|
|
921
|
-
declare function mapHistoryListToMessages(list: any[], platform: 'claude' | 'openai', opts: MapHistoryOptions): {
|
|
922
|
-
messages: any[];
|
|
923
|
-
runningItemIds: string[];
|
|
924
|
-
};
|
|
925
|
-
|
|
926
|
-
/**
|
|
927
|
-
* Keep older history REACHABLE by paging until the message box actually gains
|
|
928
|
-
* something to scroll to.
|
|
929
|
-
*
|
|
930
|
-
* Older history is paged in by one trigger only: the user scrolling to the top
|
|
931
|
-
* of the message box. That trigger has two ways to die, and collapsed indexing
|
|
932
|
-
* rows cause both:
|
|
933
|
-
*
|
|
934
|
-
* 1. The box never scrolls. A file's every indexing pass (the first plus every
|
|
935
|
-
* CONTINUE pass, request AND response bubble each) folds into ONE row, so a
|
|
936
|
-
* full history page — twenty-plus messages — can render as a single line.
|
|
937
|
-
* Content shorter than the viewport fires no scroll event, so page 2 is
|
|
938
|
-
* never requested and any conversation the user had before that upload is
|
|
939
|
-
* permanently out of reach.
|
|
940
|
-
* 2. The fetched page adds no height. A page that is entirely the same file's
|
|
941
|
-
* earlier passes joins the collapsed row already on screen and renders
|
|
942
|
-
* nothing new. The user, sitting at scrollTop 0, scrolls up again — and
|
|
943
|
-
* because the position never changed, no further scroll event fires.
|
|
944
|
-
*
|
|
945
|
-
* Both are the same shape: fetch, re-measure, and keep going until the user
|
|
946
|
-
* genuinely gained reachable content, history ran out, or the pager stopped
|
|
947
|
-
* advancing. `isSatisfied` is what differs between the two (can the box scroll
|
|
948
|
-
* at all / did it grow), so the loop below takes it as a predicate.
|
|
949
|
-
*
|
|
950
|
-
* DOM-free like the rest of the engine — the caller supplies the measurement and
|
|
951
|
-
* awaits its own render before measuring, so agent.vue and the widget run the
|
|
952
|
-
* identical loop over their own pagers.
|
|
953
|
-
*/
|
|
954
|
-
/** Overflow (px) that counts as "the user can scroll here". Comfortably more
|
|
955
|
-
* than the 60px top threshold that triggers the next page, so a filled box has
|
|
956
|
-
* real room to scroll rather than sitting one pixel from the trigger. */
|
|
957
|
-
declare const HISTORY_FILL_SLACK_PX = 64;
|
|
958
|
-
/** Pages one fill pass will request before giving up. Reached only by a chat
|
|
959
|
-
* whose history really is dozens of pages of one file's indexing passes; the
|
|
960
|
-
* cap exists so a pager that stops advancing can never spin forever. */
|
|
961
|
-
declare const MAX_HISTORY_FILL_PAGES = 24;
|
|
962
|
-
type FillHistoryViewportOptions = {
|
|
963
|
-
/** The user has reachable content and paging can stop. Called AFTER the
|
|
964
|
-
* caller's own render has settled (nextTick / rAF), since only the caller
|
|
965
|
-
* knows when its view has painted — hence the allowance for a promise. */
|
|
966
|
-
isSatisfied: () => boolean | Promise<boolean>;
|
|
967
|
-
/** All history is loaded — nothing left to page in. */
|
|
968
|
-
isEndOfList: () => boolean;
|
|
969
|
-
/** A history request is already in flight. Waited out, not treated as a stop
|
|
970
|
-
* condition: a background first-page refresh (the queue-detect tick fires one
|
|
971
|
-
* every couple of seconds while a file is indexing) would otherwise swallow
|
|
972
|
-
* the user's scroll-up entirely, and scrolling up again from scrollTop 0
|
|
973
|
-
* produces no second event to retry with. */
|
|
974
|
-
isLoading: () => boolean;
|
|
975
|
-
/** Messages currently loaded. Used to detect a page that added nothing, which
|
|
976
|
-
* means the pager is not advancing and looping would never terminate. */
|
|
977
|
-
messageCount: () => number;
|
|
978
|
-
/** Fetch ONE older page (the caller's own fetchMore path, scroll-restore and
|
|
979
|
-
* all). Return `false` when the request was NOT issued (the caller's own
|
|
980
|
-
* single-flight guard swallowed it) so the loop retries instead of reading
|
|
981
|
-
* the unchanged message count as an exhausted pager. Anything else, including
|
|
982
|
-
* undefined, means it was attempted. */
|
|
983
|
-
fetchOlder: () => Promise<boolean | void | any>;
|
|
984
|
-
/** The chat this fill was started for is gone (project switched, view
|
|
985
|
-
* unmounted, gate token bumped). Checked between pages so a stale fill can
|
|
986
|
-
* never keep paging another chat's history. */
|
|
987
|
-
isStale?: () => boolean;
|
|
988
|
-
maxPages?: number;
|
|
989
|
-
};
|
|
990
|
-
/**
|
|
991
|
-
* Page older history until `isSatisfied`, until history runs out, or until the
|
|
992
|
-
* pager stops advancing. Never throws: a failed page ends the fill, and the
|
|
993
|
-
* user's own scrolling remains the fallback trigger.
|
|
994
|
-
*/
|
|
995
|
-
declare function fillHistoryViewport(opts: FillHistoryViewportOptions): Promise<void>;
|
|
996
|
-
/**
|
|
997
|
-
* One fill loop per view, with predicates COMBINED rather than dropped.
|
|
998
|
-
*
|
|
999
|
-
* Fills come from several places at once — a first page finishing, a window
|
|
1000
|
-
* resize, a row being collapsed, and the user's own scroll to the top — and a
|
|
1001
|
-
* plain "one at a time, drop the rest" guard picks the wrong winner: a resize
|
|
1002
|
-
* fill (satisfied the moment the box can scroll at all) would swallow the user's
|
|
1003
|
-
* scroll-up (which needs content specifically ABOVE them), and the scroll-up
|
|
1004
|
-
* cannot be retried, because a reader parked at scrollTop 0 produces no further
|
|
1005
|
-
* scroll event. Dropping the guard entirely is no better: every frame of a
|
|
1006
|
-
* window drag would start its own 24-page loop.
|
|
1007
|
-
*
|
|
1008
|
-
* So a request that arrives mid-loop ANDs its predicate into the running one:
|
|
1009
|
-
* the loop then keeps paging until EVERY caller is satisfied. Predicates that
|
|
1010
|
-
* come true are dropped as it goes, so the cost stays flat.
|
|
1011
|
-
*/
|
|
1012
|
-
declare function createHistoryFiller(base: Omit<FillHistoryViewportOptions, 'isSatisfied'> & {
|
|
1013
|
-
/** Fired when the loop starts FETCHING and when it stops, and only on a real
|
|
1014
|
-
* change.
|
|
1015
|
-
*
|
|
1016
|
-
* This — not the caller's own per-request `isLoading` — is what "older
|
|
1017
|
-
* history is still coming in" means to a view. A fill is many pages, and
|
|
1018
|
-
* `isLoading` drops to false between every one of them, so anything
|
|
1019
|
-
* rendered off it flickers once per page for the whole loop. A collapsed
|
|
1020
|
-
* indexing row whose run begins above the loaded window renders exactly
|
|
1021
|
-
* that ("still loading this run" vs a status it cannot know yet), which is
|
|
1022
|
-
* why the loop has to publish its own span.
|
|
1023
|
-
*
|
|
1024
|
-
* Fetching, NOT requested. Most fills fetch nothing: they are fired on every
|
|
1025
|
-
* window resize, every row a user collapses, and every first-page load, and
|
|
1026
|
-
* the overwhelmingly common outcome is `isSatisfied` returning true on the
|
|
1027
|
-
* first look. Announcing at request time published a true/false pair for
|
|
1028
|
-
* each of those, and the widget's own satisfied-check spans two animation
|
|
1029
|
-
* frames — long enough for the browser to PAINT the intermediate state. Every
|
|
1030
|
-
* collapsed row strobed through "loading" on every resize tick. So the span
|
|
1031
|
-
* opens at the first actual page request, which is also the first moment the
|
|
1032
|
-
* claim is true. */
|
|
1033
|
-
onRunningChange?: (running: boolean) => void;
|
|
1034
|
-
}): {
|
|
1035
|
-
fill: (isSatisfied: () => boolean | Promise<boolean>) => Promise<void>;
|
|
1036
|
-
isRunning: () => boolean;
|
|
1037
|
-
};
|
|
1038
|
-
|
|
1039
1089
|
declare const MCP_NAME = "BunnyQuery";
|
|
1040
1090
|
declare const DEFAULT_CLAUDE_MODEL = "claude-sonnet-5";
|
|
1041
1091
|
declare const DEFAULT_OPENAI_MODEL = "gpt-5.6-luna";
|
|
@@ -1160,6 +1210,61 @@ declare function extractOpenAIText(response: any): any;
|
|
|
1160
1210
|
declare function listClaudeModels(service: string, owner: string): Promise<any>;
|
|
1161
1211
|
declare function listOpenAIModels(service: string, owner: string): Promise<any>;
|
|
1162
1212
|
declare const BG_INDEXING_QUEUE_SUFFIX = "-bg";
|
|
1213
|
+
/**
|
|
1214
|
+
* unique_id of the durable "indexing finished" marker record for a stored file.
|
|
1215
|
+
*
|
|
1216
|
+
* Written by the BACKEND (the polling worker calls the MCP server's
|
|
1217
|
+
* /internal/index-complete at the end of an auto_continue chain whose final
|
|
1218
|
+
* window reported no more content) and, for completions a client knows
|
|
1219
|
+
* deterministically (single-pass settle, client-chain completion token), by the
|
|
1220
|
+
* consumer's mintIndexDoneMarker hook — never by the model.
|
|
1221
|
+
* The marker record carries `reference: "src::<path>"`, so the reindex flow's
|
|
1222
|
+
* delete of the src:: record cascades to it and a re-run starts unmarked.
|
|
1223
|
+
*
|
|
1224
|
+
* Existence semantics: present = the whole file was read to the end. Absent =
|
|
1225
|
+
* unknown (still running, failed partway, indexed before this marker existed,
|
|
1226
|
+
* or a single-pass run minted before the client hook existed) - callers must
|
|
1227
|
+
* fall back to the live-queue probe (fetchLiveIndexingKeys) before reading
|
|
1228
|
+
* absence as anything.
|
|
1229
|
+
*/
|
|
1230
|
+
declare function indexDoneUniqueId(storagePath: string): string;
|
|
1231
|
+
/**
|
|
1232
|
+
* unique_id of the per-file indexing RUN record.
|
|
1233
|
+
*
|
|
1234
|
+
* One record per storage path, newest run wins (a reindex's delete-then-repost
|
|
1235
|
+
* of src:: cascade-deletes the old record first, exactly like done::). Minted
|
|
1236
|
+
* status='working' by the client the moment it enqueues a run's FIRST pass, and
|
|
1237
|
+
* closed (done/error/cancelled) by whichever side observes the ending: the
|
|
1238
|
+
* worker via the MCP internal routes for worker-driven chains, the client for
|
|
1239
|
+
* deterministic settles, cancels, and dispatch failures. It exists so chat rows
|
|
1240
|
+
* and files-page badges can answer "which runs exist and how did they end"
|
|
1241
|
+
* from ONE records query instead of scanning bg history.
|
|
1242
|
+
*
|
|
1243
|
+
* A 'working' record is a claim, not proof: a chain that dies without reaching
|
|
1244
|
+
* any error path leaves it dangling, so readers must treat a stale 'working'
|
|
1245
|
+
* (old `started`, no live-queue confirmation) as unknown, never as live.
|
|
1246
|
+
*/
|
|
1247
|
+
declare function runIndexUniqueId(storagePath: string): string;
|
|
1248
|
+
type IndexRunStatus = 'working' | 'done' | 'error' | 'cancelled';
|
|
1249
|
+
type IndexRunPatch = {
|
|
1250
|
+
status: IndexRunStatus;
|
|
1251
|
+
filename?: string;
|
|
1252
|
+
started?: number;
|
|
1253
|
+
finished?: number;
|
|
1254
|
+
error?: string;
|
|
1255
|
+
queue?: string;
|
|
1256
|
+
/** Chat that owns this run. A run:: record is keyed by storage path alone,
|
|
1257
|
+
* but a chat is per (project, platform) — without this the Claude chat's
|
|
1258
|
+
* runs surfaced as rows in the same project's ChatGPT chat, where their
|
|
1259
|
+
* passes can never load and the queue probe can never see them. */
|
|
1260
|
+
platform?: 'claude' | 'openai';
|
|
1261
|
+
};
|
|
1262
|
+
/**
|
|
1263
|
+
* Fire-and-forget wrapper over the consumer's upsertIndexRunRecord hook.
|
|
1264
|
+
* Safe everywhere: missing hook, unconfigured engine, and consumer throws all
|
|
1265
|
+
* reduce to a no-op — a run record must never be able to break the run itself.
|
|
1266
|
+
*/
|
|
1267
|
+
declare function upsertIndexRunRecordSafe(service: string, storagePath: string, patch: IndexRunPatch): void;
|
|
1163
1268
|
/**
|
|
1164
1269
|
* The one place the background-indexing queue name is spelled out. The backend
|
|
1165
1270
|
* serialises requests sharing a queue name and runs different names in PARALLEL,
|
|
@@ -1217,7 +1322,234 @@ declare function getChatHistory(params: {
|
|
|
1217
1322
|
platform: 'claude' | 'openai';
|
|
1218
1323
|
queue?: string;
|
|
1219
1324
|
status?: 'pending' | 'running' | 'resolved' | 'failed';
|
|
1325
|
+
/** Exact-queue listing: without it the qid range is a PREFIX match, so
|
|
1326
|
+
* queue "u1" also returns "u1-bg" rows. Requires the updated polling
|
|
1327
|
+
* lambda; older backends ignore it (harmless, wider results). */
|
|
1328
|
+
queue_exact?: boolean;
|
|
1329
|
+
/** Label/marker STUBS instead of full bodies (see the polling lambda).
|
|
1330
|
+
* Older backends ignore it and return full items. */
|
|
1331
|
+
compact?: boolean;
|
|
1332
|
+
/** Drop one queue's rows from an id-prefix listing — how the surface
|
|
1333
|
+
* chat is fetched WITHOUT the bg-indexing queue while legacy items on
|
|
1334
|
+
* odd queue names survive. Older backends ignore it. */
|
|
1335
|
+
queue_exclude?: string;
|
|
1220
1336
|
}, fetchOptions: Record<string, any>): Promise<any>;
|
|
1337
|
+
/** Full server-side id of one history item, for a csr-poll POINT LOOKUP (the
|
|
1338
|
+
* single-item path returns the item WITH bodies — how an expanded row fetches
|
|
1339
|
+
* the passes a compact listing stubbed out). Mirrors the id the SDK builds:
|
|
1340
|
+
* `[METHOD]url#service:` + the item's own `stamp:entropy` id. */
|
|
1341
|
+
declare function buildHistoryItemFullId(platform: 'claude' | 'openai', service: string, itemId: string): string;
|
|
1342
|
+
|
|
1343
|
+
/**
|
|
1344
|
+
* History mapping (pure). Moved verbatim from the chatbox. The clear-horizon
|
|
1345
|
+
* timestamp and the "Indexing: …" display label are INJECTED (clearedAt param,
|
|
1346
|
+
* formatIndexingLabel callback) so the engine touches neither localStorage nor
|
|
1347
|
+
* view-specific display formatting. projectId is passed for link sanitization.
|
|
1348
|
+
*/
|
|
1349
|
+
|
|
1350
|
+
declare function filterListByClearHorizon(list: any[], clearedAt: number): any[];
|
|
1351
|
+
declare function normalizeTextContent(content: any): string;
|
|
1352
|
+
declare function extractLastUserTextFromRequest(requestBody: any): string;
|
|
1353
|
+
/** The two openings an indexing prompt can have. A bg-queue item that starts with
|
|
1354
|
+
* neither is an ordinary chat that happened to be routed onto that queue. */
|
|
1355
|
+
declare function isIndexingRequestText(userText: any): boolean;
|
|
1356
|
+
type IndexingRequestRef = {
|
|
1357
|
+
name: string;
|
|
1358
|
+
path?: string;
|
|
1359
|
+
mime?: string;
|
|
1360
|
+
size?: number;
|
|
1361
|
+
/** A CONTINUE pass rather than the run's first. */
|
|
1362
|
+
continued: boolean;
|
|
1363
|
+
};
|
|
1364
|
+
/**
|
|
1365
|
+
* The file an indexing prompt is about, read back out of the prompt itself.
|
|
1366
|
+
*
|
|
1367
|
+
* The prompt is the only description of the pass that survives on the server, so
|
|
1368
|
+
* this is how BOTH a history rebuild and a worker-minted pass the client never
|
|
1369
|
+
* dispatched (ChatSession._adoptWorkerIndexingPasses) recover the file. Shared so
|
|
1370
|
+
* the two produce the same `_indexFile`, which is what makes them group together.
|
|
1371
|
+
*/
|
|
1372
|
+
declare function parseIndexingRequestText(userText: any): IndexingRequestRef | null;
|
|
1373
|
+
/**
|
|
1374
|
+
* One bounded look at the background-indexing queue: which files still have a
|
|
1375
|
+
* pass pending or running? This is the same negative signal ChatSession's
|
|
1376
|
+
* display layer relies on - for a worker-driven (auto_continue) run, only the
|
|
1377
|
+
* queue can say the run is over, because the worker enqueues continuation
|
|
1378
|
+
* passes the client never dispatched.
|
|
1379
|
+
*
|
|
1380
|
+
* Returns every storage path AND file name found on live passes (both, because
|
|
1381
|
+
* older prompts may lack the storage-path line), plus `checked`: false when a
|
|
1382
|
+
* page came back full, in which case absence from `keys` proves nothing and
|
|
1383
|
+
* the caller must keep whatever state it already had.
|
|
1384
|
+
*
|
|
1385
|
+
* SCOPE: the probed queue is "<userId>-bg" - THIS user's dispatches only. A
|
|
1386
|
+
* chain launched by another collaborator or a widget end-user lives on their
|
|
1387
|
+
* queue and is invisible here, so "idle" must never be read as "nobody is
|
|
1388
|
+
* indexing this file", only as "this user's runs are over". The durable done::
|
|
1389
|
+
* marker (indexDoneUniqueId) is the cross-user signal.
|
|
1390
|
+
*/
|
|
1391
|
+
declare function fetchLiveIndexingKeys(params: {
|
|
1392
|
+
service: string;
|
|
1393
|
+
owner: string;
|
|
1394
|
+
platform: 'claude' | 'openai';
|
|
1395
|
+
/** Same value the dispatch used - see bgIndexingQueueName. */
|
|
1396
|
+
userId?: string;
|
|
1397
|
+
}): Promise<{
|
|
1398
|
+
keys: Set<string>;
|
|
1399
|
+
checked: boolean;
|
|
1400
|
+
at: number;
|
|
1401
|
+
}>;
|
|
1402
|
+
/** Test hook: drop split-fetch state (all keys, or one). */
|
|
1403
|
+
declare function __resetSplitHistoryState(key?: string): void;
|
|
1404
|
+
type SplitHistoryResult = {
|
|
1405
|
+
list: any[];
|
|
1406
|
+
endOfList: boolean;
|
|
1407
|
+
startKeyHistory: any[];
|
|
1408
|
+
/** True when this chat had never been walked in this session — the first
|
|
1409
|
+
* paint. Consumers gate the "Loading indexing history" hint on it: a
|
|
1410
|
+
* mid-walk tab return restarts the walk for cursor safety but must stay
|
|
1411
|
+
* silent (flashing the hint on every return was the reported bug). */
|
|
1412
|
+
firstLoad?: boolean;
|
|
1413
|
+
/** Present only when `deferBg` was requested AND bg work remains: resolves
|
|
1414
|
+
* with the stub batch fetched in the background (the per-key lock is held
|
|
1415
|
+
* until it settles, so no other history call can interleave). The caller
|
|
1416
|
+
* merges the batch by timestamp — the same path older pages use. */
|
|
1417
|
+
bgPending?: Promise<{
|
|
1418
|
+
list: any[];
|
|
1419
|
+
endOfList: boolean;
|
|
1420
|
+
}>;
|
|
1421
|
+
};
|
|
1422
|
+
declare function getSplitChatHistory(params: {
|
|
1423
|
+
service: string;
|
|
1424
|
+
owner: string;
|
|
1425
|
+
platform: 'claude' | 'openai';
|
|
1426
|
+
userId?: string;
|
|
1427
|
+
}, fetchOptions: Record<string, any>,
|
|
1428
|
+
/** Test seam: replaces getChatHistory. Not for production callers. */
|
|
1429
|
+
_fetchImpl?: typeof getChatHistory): Promise<SplitHistoryResult>;
|
|
1430
|
+
type MapHistoryOptions = {
|
|
1431
|
+
clearedAt: number;
|
|
1432
|
+
projectId: string;
|
|
1433
|
+
/** View-side display formatter for "Indexing:/Reindexing: …" bubbles. */
|
|
1434
|
+
formatIndexingLabel: (name: string, mime?: string, size?: number | null, storagePath?: string, reindex?: boolean, continued?: boolean) => string;
|
|
1435
|
+
};
|
|
1436
|
+
declare function mapHistoryListToMessages(list: any[], platform: 'claude' | 'openai', opts: MapHistoryOptions): {
|
|
1437
|
+
messages: any[];
|
|
1438
|
+
runningItemIds: string[];
|
|
1439
|
+
};
|
|
1440
|
+
|
|
1441
|
+
/**
|
|
1442
|
+
* Keep older history REACHABLE by paging until the message box actually gains
|
|
1443
|
+
* something to scroll to.
|
|
1444
|
+
*
|
|
1445
|
+
* Older history is paged in by one trigger only: the user scrolling to the top
|
|
1446
|
+
* of the message box. That trigger has two ways to die, and collapsed indexing
|
|
1447
|
+
* rows cause both:
|
|
1448
|
+
*
|
|
1449
|
+
* 1. The box never scrolls. A file's every indexing pass (the first plus every
|
|
1450
|
+
* CONTINUE pass, request AND response bubble each) folds into ONE row, so a
|
|
1451
|
+
* full history page — twenty-plus messages — can render as a single line.
|
|
1452
|
+
* Content shorter than the viewport fires no scroll event, so page 2 is
|
|
1453
|
+
* never requested and any conversation the user had before that upload is
|
|
1454
|
+
* permanently out of reach.
|
|
1455
|
+
* 2. The fetched page adds no height. A page that is entirely the same file's
|
|
1456
|
+
* earlier passes joins the collapsed row already on screen and renders
|
|
1457
|
+
* nothing new. The user, sitting at scrollTop 0, scrolls up again — and
|
|
1458
|
+
* because the position never changed, no further scroll event fires.
|
|
1459
|
+
*
|
|
1460
|
+
* Both are the same shape: fetch, re-measure, and keep going until the user
|
|
1461
|
+
* genuinely gained reachable content, history ran out, or the pager stopped
|
|
1462
|
+
* advancing. `isSatisfied` is what differs between the two (can the box scroll
|
|
1463
|
+
* at all / did it grow), so the loop below takes it as a predicate.
|
|
1464
|
+
*
|
|
1465
|
+
* DOM-free like the rest of the engine — the caller supplies the measurement and
|
|
1466
|
+
* awaits its own render before measuring, so agent.vue and the widget run the
|
|
1467
|
+
* identical loop over their own pagers.
|
|
1468
|
+
*/
|
|
1469
|
+
/** Overflow (px) that counts as "the user can scroll here". Comfortably more
|
|
1470
|
+
* than the 60px top threshold that triggers the next page, so a filled box has
|
|
1471
|
+
* real room to scroll rather than sitting one pixel from the trigger. */
|
|
1472
|
+
declare const HISTORY_FILL_SLACK_PX = 64;
|
|
1473
|
+
/** Pages one fill pass will request before giving up. Reached only by a chat
|
|
1474
|
+
* whose history really is dozens of pages of one file's indexing passes; the
|
|
1475
|
+
* cap exists so a pager that stops advancing can never spin forever. */
|
|
1476
|
+
declare const MAX_HISTORY_FILL_PAGES = 24;
|
|
1477
|
+
type FillHistoryViewportOptions = {
|
|
1478
|
+
/** The user has reachable content and paging can stop. Called AFTER the
|
|
1479
|
+
* caller's own render has settled (nextTick / rAF), since only the caller
|
|
1480
|
+
* knows when its view has painted — hence the allowance for a promise. */
|
|
1481
|
+
isSatisfied: () => boolean | Promise<boolean>;
|
|
1482
|
+
/** All history is loaded — nothing left to page in. */
|
|
1483
|
+
isEndOfList: () => boolean;
|
|
1484
|
+
/** A history request is already in flight. Waited out, not treated as a stop
|
|
1485
|
+
* condition: a background first-page refresh (the queue-detect tick fires one
|
|
1486
|
+
* every couple of seconds while a file is indexing) would otherwise swallow
|
|
1487
|
+
* the user's scroll-up entirely, and scrolling up again from scrollTop 0
|
|
1488
|
+
* produces no second event to retry with. */
|
|
1489
|
+
isLoading: () => boolean;
|
|
1490
|
+
/** Messages currently loaded. Used to detect a page that added nothing, which
|
|
1491
|
+
* means the pager is not advancing and looping would never terminate. */
|
|
1492
|
+
messageCount: () => number;
|
|
1493
|
+
/** Fetch ONE older page (the caller's own fetchMore path, scroll-restore and
|
|
1494
|
+
* all). Return `false` when the request was NOT issued (the caller's own
|
|
1495
|
+
* single-flight guard swallowed it) so the loop retries instead of reading
|
|
1496
|
+
* the unchanged message count as an exhausted pager. Anything else, including
|
|
1497
|
+
* undefined, means it was attempted. */
|
|
1498
|
+
fetchOlder: () => Promise<boolean | void | any>;
|
|
1499
|
+
/** The chat this fill was started for is gone (project switched, view
|
|
1500
|
+
* unmounted, gate token bumped). Checked between pages so a stale fill can
|
|
1501
|
+
* never keep paging another chat's history. */
|
|
1502
|
+
isStale?: () => boolean;
|
|
1503
|
+
maxPages?: number;
|
|
1504
|
+
};
|
|
1505
|
+
/**
|
|
1506
|
+
* Page older history until `isSatisfied`, until history runs out, or until the
|
|
1507
|
+
* pager stops advancing. Never throws: a failed page ends the fill, and the
|
|
1508
|
+
* user's own scrolling remains the fallback trigger.
|
|
1509
|
+
*/
|
|
1510
|
+
declare function fillHistoryViewport(opts: FillHistoryViewportOptions): Promise<void>;
|
|
1511
|
+
/**
|
|
1512
|
+
* One fill loop per view, with predicates COMBINED rather than dropped.
|
|
1513
|
+
*
|
|
1514
|
+
* Fills come from several places at once — a first page finishing, a window
|
|
1515
|
+
* resize, a row being collapsed, and the user's own scroll to the top — and a
|
|
1516
|
+
* plain "one at a time, drop the rest" guard picks the wrong winner: a resize
|
|
1517
|
+
* fill (satisfied the moment the box can scroll at all) would swallow the user's
|
|
1518
|
+
* scroll-up (which needs content specifically ABOVE them), and the scroll-up
|
|
1519
|
+
* cannot be retried, because a reader parked at scrollTop 0 produces no further
|
|
1520
|
+
* scroll event. Dropping the guard entirely is no better: every frame of a
|
|
1521
|
+
* window drag would start its own 24-page loop.
|
|
1522
|
+
*
|
|
1523
|
+
* So a request that arrives mid-loop ANDs its predicate into the running one:
|
|
1524
|
+
* the loop then keeps paging until EVERY caller is satisfied. Predicates that
|
|
1525
|
+
* come true are dropped as it goes, so the cost stays flat.
|
|
1526
|
+
*/
|
|
1527
|
+
declare function createHistoryFiller(base: Omit<FillHistoryViewportOptions, 'isSatisfied'> & {
|
|
1528
|
+
/** Fired when the loop starts FETCHING and when it stops, and only on a real
|
|
1529
|
+
* change.
|
|
1530
|
+
*
|
|
1531
|
+
* This — not the caller's own per-request `isLoading` — is what "older
|
|
1532
|
+
* history is still coming in" means to a view. A fill is many pages, and
|
|
1533
|
+
* `isLoading` drops to false between every one of them, so anything
|
|
1534
|
+
* rendered off it flickers once per page for the whole loop. A collapsed
|
|
1535
|
+
* indexing row whose run begins above the loaded window renders exactly
|
|
1536
|
+
* that ("still loading this run" vs a status it cannot know yet), which is
|
|
1537
|
+
* why the loop has to publish its own span.
|
|
1538
|
+
*
|
|
1539
|
+
* Fetching, NOT requested. Most fills fetch nothing: they are fired on every
|
|
1540
|
+
* window resize, every row a user collapses, and every first-page load, and
|
|
1541
|
+
* the overwhelmingly common outcome is `isSatisfied` returning true on the
|
|
1542
|
+
* first look. Announcing at request time published a true/false pair for
|
|
1543
|
+
* each of those, and the widget's own satisfied-check spans two animation
|
|
1544
|
+
* frames — long enough for the browser to PAINT the intermediate state. Every
|
|
1545
|
+
* collapsed row strobed through "loading" on every resize tick. So the span
|
|
1546
|
+
* opens at the first actual page request, which is also the first moment the
|
|
1547
|
+
* claim is true. */
|
|
1548
|
+
onRunningChange?: (running: boolean) => void;
|
|
1549
|
+
}): {
|
|
1550
|
+
fill: (isSatisfied: () => boolean | Promise<boolean>) => Promise<void>;
|
|
1551
|
+
isRunning: () => boolean;
|
|
1552
|
+
};
|
|
1221
1553
|
|
|
1222
1554
|
/**
|
|
1223
1555
|
* ChatSession host adapter + state types.
|
|
@@ -1314,6 +1646,10 @@ interface ChatMessage {
|
|
|
1314
1646
|
* which is how an 88-page file once "finished" at page 15. */
|
|
1315
1647
|
_indexComplete?: boolean;
|
|
1316
1648
|
_useBgQueue?: boolean;
|
|
1649
|
+
/** Mapped from an item delivered by the bg chain of the split history fetch
|
|
1650
|
+
* (stubs or deferred chats). Surface-frontier logic (retention boundary,
|
|
1651
|
+
* clear-horizon) skips these — their ids reach arbitrarily deep. */
|
|
1652
|
+
_fromBgChain?: boolean;
|
|
1317
1653
|
/** Local id of a turn STAGED at Send time while its attachments upload. The
|
|
1318
1654
|
* bubble exists before any server request does, so it is never matched by
|
|
1319
1655
|
* _serverItemId and is never promoted/cancelled by the queue machinery —
|
|
@@ -1353,6 +1689,9 @@ interface ChatState {
|
|
|
1353
1689
|
typingAbort: boolean;
|
|
1354
1690
|
loadingHistory: boolean;
|
|
1355
1691
|
loadingOlderHistory: boolean;
|
|
1692
|
+
/** A deferred bg stub batch (first-paint split fetch) is still in flight;
|
|
1693
|
+
* views show a small 'loading indexing history' hint while true. */
|
|
1694
|
+
bgHistoryLoading: boolean;
|
|
1356
1695
|
historyEndOfList: boolean;
|
|
1357
1696
|
historyStartKeyHistory: string[];
|
|
1358
1697
|
historyRequestToken: number;
|
|
@@ -1656,6 +1995,15 @@ type IndexingGroup = {
|
|
|
1656
1995
|
* loaded ones. 'status': the queue has not yet said whether this file is still
|
|
1657
1996
|
* being worked on, which is the only thing that can end a worker-driven run. */
|
|
1658
1997
|
resolvingReason?: 'history' | 'status';
|
|
1998
|
+
/** Synthesized from a durable run:: record: none of the run's passes are
|
|
1999
|
+
* among the loaded messages (bg history still deferred, or the run is older
|
|
2000
|
+
* than the paging cap). Header-and-status only — members/visibleMembers are
|
|
2001
|
+
* empty and there is nothing to cancel; the row is replaced by the real
|
|
2002
|
+
* group the moment actual passes load (same `key`, so expansion state
|
|
2003
|
+
* carries over). */
|
|
2004
|
+
stub?: boolean;
|
|
2005
|
+
/** The run:: record's stored error text, for a stub row's meta line. */
|
|
2006
|
+
stubError?: string;
|
|
1659
2007
|
};
|
|
1660
2008
|
type DisplayEntry = {
|
|
1661
2009
|
kind: 'message';
|
|
@@ -1686,6 +2034,16 @@ type BuildDisplayListOptions = {
|
|
|
1686
2034
|
/** Whether `liveIndexKeys` has been answered at least once for this chat. False
|
|
1687
2035
|
* is "we do not know", and a worker-driven run stays unfinished on it. */
|
|
1688
2036
|
liveIndexChecked?: boolean;
|
|
2037
|
+
/** Files carrying the durable done:: completion marker (one prefix sweep),
|
|
2038
|
+
* keyed like IndexingGroup.key (storage path; the bare-name fallback keys
|
|
2039
|
+
* of very old prompts simply never match — they keep the queue inference).
|
|
2040
|
+
* A marker is PROOF the file was read to the end: it settles a worker-run
|
|
2041
|
+
* green without waiting for the queue answer, and it is never withheld by
|
|
2042
|
+
* the resolving logic. A live queue hit still outranks it (a re-index in
|
|
2043
|
+
* flight whose marker-cascade delete lagged). */
|
|
2044
|
+
doneKeys?: {
|
|
2045
|
+
[fileKey: string]: boolean;
|
|
2046
|
+
};
|
|
1689
2047
|
/** Server item ids of passes that were on a row when the user STOPPED it
|
|
1690
2048
|
* (ChatSession.state.stoppedIndexIds). A run holding any of them is a run the
|
|
1691
2049
|
* user stopped — see the status derivation for why a stop usually leaves no
|
|
@@ -1698,7 +2056,51 @@ type BuildDisplayListOptions = {
|
|
|
1698
2056
|
* windowedIndexing). Passed in rather than read from config so this stays a
|
|
1699
2057
|
* pure function of its inputs and can be exercised for both settings. */
|
|
1700
2058
|
windowedIndexing?: boolean;
|
|
2059
|
+
/** Durable run:: records, keyed by STORAGE PATH (the consumer's marker
|
|
2060
|
+
* sweep). Each key with no real group in the loaded messages gets a
|
|
2061
|
+
* synthesized header-only row (see IndexingGroup.stub), placed by its
|
|
2062
|
+
* `started` timestamp. A real group for the same file — matched by key,
|
|
2063
|
+
* path, or name — always suppresses the stub: loaded passes are evidence,
|
|
2064
|
+
* the record is only a summary. */
|
|
2065
|
+
runStubs?: {
|
|
2066
|
+
[storagePath: string]: RunStubInfo;
|
|
2067
|
+
};
|
|
2068
|
+
/** The platform whose chat this list is for. run:: records are per-FILE,
|
|
2069
|
+
* but a chat is per (project, platform): a run started under Claude has
|
|
2070
|
+
* its passes in the Claude conversation and is invisible to the
|
|
2071
|
+
* OpenAI-scoped queue probe, so its stub could never be covered and never
|
|
2072
|
+
* be confirmed — it just sat there, in a chat it did not belong to.
|
|
2073
|
+
* Records minted before this was stamped carry no platform and are shown
|
|
2074
|
+
* in both, which keeps the leak to the historical set. */
|
|
2075
|
+
stubPlatform?: 'claude' | 'openai';
|
|
2076
|
+
/** The chat's clear-history horizon (ms epoch). Run records are service-
|
|
2077
|
+
* wide and know nothing about a cleared chat, so without this every
|
|
2078
|
+
* "Clear chat history" resurrects one row per indexed file. A stub whose
|
|
2079
|
+
* run ended (or, unfinished, began) at or before this moment is dropped —
|
|
2080
|
+
* unless the queue says the file is live RIGHT NOW, which no horizon can
|
|
2081
|
+
* make untrue. */
|
|
2082
|
+
stubClearedAt?: number;
|
|
2083
|
+
/** Clock injection for tests; defaults to Date.now(). Only run-stub
|
|
2084
|
+
* staleness reads it. */
|
|
2085
|
+
now?: number;
|
|
2086
|
+
};
|
|
2087
|
+
/** The display-relevant fields of a run:: record (see requests.ts
|
|
2088
|
+
* runIndexUniqueId for the record's contract). */
|
|
2089
|
+
type RunStubInfo = {
|
|
2090
|
+
status: 'working' | 'done' | 'error' | 'cancelled';
|
|
2091
|
+
filename?: string;
|
|
2092
|
+
started?: number;
|
|
2093
|
+
finished?: number;
|
|
2094
|
+
error?: string;
|
|
2095
|
+
/** Chat that owns this run. Absent on records minted before it was
|
|
2096
|
+
* stamped; see BuildDisplayListOptions.stubPlatform. */
|
|
2097
|
+
platform?: 'claude' | 'openai';
|
|
1701
2098
|
};
|
|
2099
|
+
/** A 'working' run record older than this with no live-queue confirmation is
|
|
2100
|
+
* treated as unknown rather than live: a chain that died without reaching any
|
|
2101
|
+
* error path leaves 'working' dangling, and a row must not spin forever on a
|
|
2102
|
+
* claim nothing can end. */
|
|
2103
|
+
declare const RUN_RECORD_WORKING_STALE_MS: number;
|
|
1702
2104
|
declare function parseIndexingLabel(content: string): {
|
|
1703
2105
|
name: string;
|
|
1704
2106
|
path?: string;
|
|
@@ -2004,6 +2406,17 @@ declare class ChatSession {
|
|
|
2004
2406
|
resumePolling(reason: string): Promise<void>;
|
|
2005
2407
|
private _newLocalId;
|
|
2006
2408
|
getHistoryCacheKey(): string;
|
|
2409
|
+
private _hydratedBodies;
|
|
2410
|
+
private _hydratingItems;
|
|
2411
|
+
/** Re-apply memoized hydrated texts onto freshly-mapped messages. Both
|
|
2412
|
+
* clients call this right after their mapper runs (loadHistory does it
|
|
2413
|
+
* internally); it mutates the given array's items in place. */
|
|
2414
|
+
applyHydratedBodies(messages: ChatMessage[]): void;
|
|
2415
|
+
/** Fetch the real response bodies for compact history stubs (one csr-poll
|
|
2416
|
+
* point lookup per item id), memoize, and swap them into the live list.
|
|
2417
|
+
* Best-effort: a failed lookup leaves the stub (its head + fallback line
|
|
2418
|
+
* still render) and a later expand retries. */
|
|
2419
|
+
hydrateCompactItems(itemIds: string[]): Promise<void>;
|
|
2007
2420
|
updateHistoryCache(): void;
|
|
2008
2421
|
/**
|
|
2009
2422
|
* Land a resolved reply in the history cache of a chat that is NOT currently
|
|
@@ -2274,6 +2687,10 @@ declare class ChatSession {
|
|
|
2274
2687
|
*/
|
|
2275
2688
|
private _adoptingWorkerPasses;
|
|
2276
2689
|
private _adoptWorkerIndexingPasses;
|
|
2690
|
+
/** Anything at all suggesting THIS project's indexing may be live: a queued
|
|
2691
|
+
* local entry, a recorded live key (the adopt look just wrote them), or an
|
|
2692
|
+
* attached poll. Gates the passive adopt ladder's climb. */
|
|
2693
|
+
private _hasLiveIndexEvidence;
|
|
2277
2694
|
/** Any of these ids still queued or still polled, i.e. surviving work. */
|
|
2278
2695
|
private _isTrackingAny;
|
|
2279
2696
|
/** One live bg-queue item -> a BgTaskEntry, if it is an indexing pass this
|
|
@@ -2285,6 +2702,28 @@ declare class ChatSession {
|
|
|
2285
2702
|
* cancelQueuedMessage, which drives one, has nothing to act on). */
|
|
2286
2703
|
private _cancelServerItem;
|
|
2287
2704
|
drainBgTaskQueue(): void;
|
|
2705
|
+
/** Fire the consumer's done::-marker hook for a run whose completion this
|
|
2706
|
+
* client knows DETERMINISTICALLY (see the two call sites in
|
|
2707
|
+
* maybeResumeIndexing). Best-effort by contract; identity-checked so a
|
|
2708
|
+
* project switch mid-settle cannot stamp the wrong service. */
|
|
2709
|
+
_mintDoneMarker(entry: BgTaskEntry): void;
|
|
2710
|
+
/** Short, storable form of an error body for the run:: record. */
|
|
2711
|
+
_runErrorText(response: any): string;
|
|
2712
|
+
/** Close the records of a run whose pass settled OFF-POLL — the answer came
|
|
2713
|
+
* back as history (hidden tab, dead poll, resume refetch), so none of the
|
|
2714
|
+
* poll-side settle handlers ran. Only for SINGLE-PASS files, where one
|
|
2715
|
+
* settled pass is deterministically the whole run (the same contract as
|
|
2716
|
+
* maybeResumeIndexing's single-pass branch); paged files stay with their
|
|
2717
|
+
* drivers. Outcome is read from the settled bubbles' own flags, which is
|
|
2718
|
+
* all the history mapping left us. Best-effort and idempotent throughout. */
|
|
2719
|
+
_flipRunFromSettledEntry(entry: BgTaskEntry): void;
|
|
2720
|
+
/** Close the durable run:: record for an ending THIS client observed.
|
|
2721
|
+
* service comes from the ENTRY, not the current identity: unlike the done::
|
|
2722
|
+
* mint above, a status flip must land even if the user switched projects
|
|
2723
|
+
* mid-settle — otherwise the record lies 'working' forever. Best-effort
|
|
2724
|
+
* through upsertIndexRunRecordSafe; the consumer's precedence guard keeps
|
|
2725
|
+
* repeats and races harmless. */
|
|
2726
|
+
_flipRunRecord(entry: BgTaskEntry, status: IndexRunStatus, error?: string): void;
|
|
2288
2727
|
maybeResumeIndexing(entry: BgTaskEntry, response: any, platform: string): void;
|
|
2289
2728
|
loadHistory(fetchMore?: boolean, token?: number): Promise<void>;
|
|
2290
2729
|
uploadSingleAttachment(att: any, stageId?: string): Promise<Array<{
|
|
@@ -2301,4 +2740,4 @@ declare class ChatSession {
|
|
|
2301
2740
|
bumpGate(): void;
|
|
2302
2741
|
}
|
|
2303
2742
|
|
|
2304
|
-
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 ChatHost, type ChatIdentity, type ChatMessage, ChatSession, type ChatState, type ChatSystemPromptParams, type ClaudeMcpServerRequest, type ClaudeMcpToolConfig, type ClaudeMessage, type ClaudeRole, type ComposedUserMessage, DEFAULT_CLAUDE_MODEL, 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, type ImagePreviewContext, 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_PARSED_CONTENT_CHARS, MCP_NAME, MIN_INPUT_TOKEN_BUDGET, type MapHistoryOptions, OUTPUT_TOKEN_RESERVE, type OpenAIMessage, POLL_INTERVAL, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, type ParsedAiAgent, type PinnedDispatchContext, type PreviewImageEl, RENDER_FROM_TOKEN, RTF_EXTS, type RenderableInlineLink, TOOL_AND_RESPONSE_BUFFER, type VisionProfile, XML_EXTS, applyEncodingDeclaration, bgIndexingQueueName, buildAiAgentValue, buildBoundedChatMessages, buildChatDisplayList, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, 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, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, formatChatTimestamp, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, getProjectContextWindow, getVisionProfile, groupAttachmentFailures, hasBom, hydrateImagePreviews, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isIndexingRequestText, isLinkUnavailable, isNonRetryableRequestError, isOfficeFile, isPreviewableImagePath, isServerExtractable, isServiceDbAttachmentHref, linkUnavailableKeyForHref, linkUnavailableKeyForPath, listClaudeModels, listOpenAIModels, looksLikeRtf, makeExtractPlaceholder, mapHistoryListToMessages, markImagePreviewStale, needsBomForExt, normalizeAttachmentPathCandidate, normalizeExt, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAiAgentValue, parseAttachmentContent, parseIndexingLabel, parseIndexingRequestText, peekImagePreviewUrl, prepareDownloadText, previewImageContentType, previewableExtOf, readExpiredAttachmentHref, registerAttachmentParser, registerModelContextWindows, renderInlineLinkHtml, repairUrlEntities, repairUrlWhitespace, resolveImagePreviewUrl, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, wallClockNow };
|
|
2743
|
+
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 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, 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 };
|