bunnyquery 1.8.6 → 1.8.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/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
- }>): ComposedUserMessage;
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. Accepts the raw list
317
- * items and reads `max_input_tokens` (Anthropic); items without it are skipped,
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
- * Resolve a model's context window, most specific source first:
341
- * 1. per-project override (project settings)
342
- * 2. the provider's own models listing (Anthropic `max_input_tokens`)
343
- * 3. an exact entry in CONTEXT_WINDOW_BY_MODEL
344
- * 4. a family entry, by dropping trailing '-' segments off the id
345
- * 5. the platform default
346
- *
347
- * Step 4 is why a new or suffixed id no longer drops straight to the platform
348
- * default: 'gpt-5.6-luna' resolves via 'gpt-5.6', and a dated Claude snapshot
349
- * such as 'claude-opus-4-7-20260101' resolves via 'claude-opus-4-7'. The walk
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;
@@ -889,153 +990,6 @@ type ParsedAiAgent = {
889
990
  declare function parseAiAgentValue(value: string | null | undefined): ParsedAiAgent;
890
991
  declare function buildAiAgentValue(platform: string | null | undefined, model?: string | null, contextWindow?: number | null): string;
891
992
 
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
993
  declare const MCP_NAME = "BunnyQuery";
1040
994
  declare const DEFAULT_CLAUDE_MODEL = "claude-sonnet-5";
1041
995
  declare const DEFAULT_OPENAI_MODEL = "gpt-5.6-luna";
@@ -1160,6 +1114,61 @@ declare function extractOpenAIText(response: any): any;
1160
1114
  declare function listClaudeModels(service: string, owner: string): Promise<any>;
1161
1115
  declare function listOpenAIModels(service: string, owner: string): Promise<any>;
1162
1116
  declare const BG_INDEXING_QUEUE_SUFFIX = "-bg";
1117
+ /**
1118
+ * unique_id of the durable "indexing finished" marker record for a stored file.
1119
+ *
1120
+ * Written by the BACKEND (the polling worker calls the MCP server's
1121
+ * /internal/index-complete at the end of an auto_continue chain whose final
1122
+ * window reported no more content) and, for completions a client knows
1123
+ * deterministically (single-pass settle, client-chain completion token), by the
1124
+ * consumer's mintIndexDoneMarker hook — never by the model.
1125
+ * The marker record carries `reference: "src::<path>"`, so the reindex flow's
1126
+ * delete of the src:: record cascades to it and a re-run starts unmarked.
1127
+ *
1128
+ * Existence semantics: present = the whole file was read to the end. Absent =
1129
+ * unknown (still running, failed partway, indexed before this marker existed,
1130
+ * or a single-pass run minted before the client hook existed) - callers must
1131
+ * fall back to the live-queue probe (fetchLiveIndexingKeys) before reading
1132
+ * absence as anything.
1133
+ */
1134
+ declare function indexDoneUniqueId(storagePath: string): string;
1135
+ /**
1136
+ * unique_id of the per-file indexing RUN record.
1137
+ *
1138
+ * One record per storage path, newest run wins (a reindex's delete-then-repost
1139
+ * of src:: cascade-deletes the old record first, exactly like done::). Minted
1140
+ * status='working' by the client the moment it enqueues a run's FIRST pass, and
1141
+ * closed (done/error/cancelled) by whichever side observes the ending: the
1142
+ * worker via the MCP internal routes for worker-driven chains, the client for
1143
+ * deterministic settles, cancels, and dispatch failures. It exists so chat rows
1144
+ * and files-page badges can answer "which runs exist and how did they end"
1145
+ * from ONE records query instead of scanning bg history.
1146
+ *
1147
+ * A 'working' record is a claim, not proof: a chain that dies without reaching
1148
+ * any error path leaves it dangling, so readers must treat a stale 'working'
1149
+ * (old `started`, no live-queue confirmation) as unknown, never as live.
1150
+ */
1151
+ declare function runIndexUniqueId(storagePath: string): string;
1152
+ type IndexRunStatus = 'working' | 'done' | 'error' | 'cancelled';
1153
+ type IndexRunPatch = {
1154
+ status: IndexRunStatus;
1155
+ filename?: string;
1156
+ started?: number;
1157
+ finished?: number;
1158
+ error?: string;
1159
+ queue?: string;
1160
+ /** Chat that owns this run. A run:: record is keyed by storage path alone,
1161
+ * but a chat is per (project, platform) — without this the Claude chat's
1162
+ * runs surfaced as rows in the same project's ChatGPT chat, where their
1163
+ * passes can never load and the queue probe can never see them. */
1164
+ platform?: 'claude' | 'openai';
1165
+ };
1166
+ /**
1167
+ * Fire-and-forget wrapper over the consumer's upsertIndexRunRecord hook.
1168
+ * Safe everywhere: missing hook, unconfigured engine, and consumer throws all
1169
+ * reduce to a no-op — a run record must never be able to break the run itself.
1170
+ */
1171
+ declare function upsertIndexRunRecordSafe(service: string, storagePath: string, patch: IndexRunPatch): void;
1163
1172
  /**
1164
1173
  * The one place the background-indexing queue name is spelled out. The backend
1165
1174
  * serialises requests sharing a queue name and runs different names in PARALLEL,
@@ -1217,7 +1226,234 @@ declare function getChatHistory(params: {
1217
1226
  platform: 'claude' | 'openai';
1218
1227
  queue?: string;
1219
1228
  status?: 'pending' | 'running' | 'resolved' | 'failed';
1229
+ /** Exact-queue listing: without it the qid range is a PREFIX match, so
1230
+ * queue "u1" also returns "u1-bg" rows. Requires the updated polling
1231
+ * lambda; older backends ignore it (harmless, wider results). */
1232
+ queue_exact?: boolean;
1233
+ /** Label/marker STUBS instead of full bodies (see the polling lambda).
1234
+ * Older backends ignore it and return full items. */
1235
+ compact?: boolean;
1236
+ /** Drop one queue's rows from an id-prefix listing — how the surface
1237
+ * chat is fetched WITHOUT the bg-indexing queue while legacy items on
1238
+ * odd queue names survive. Older backends ignore it. */
1239
+ queue_exclude?: string;
1220
1240
  }, fetchOptions: Record<string, any>): Promise<any>;
1241
+ /** Full server-side id of one history item, for a csr-poll POINT LOOKUP (the
1242
+ * single-item path returns the item WITH bodies — how an expanded row fetches
1243
+ * the passes a compact listing stubbed out). Mirrors the id the SDK builds:
1244
+ * `[METHOD]url#service:` + the item's own `stamp:entropy` id. */
1245
+ declare function buildHistoryItemFullId(platform: 'claude' | 'openai', service: string, itemId: string): string;
1246
+
1247
+ /**
1248
+ * History mapping (pure). Moved verbatim from the chatbox. The clear-horizon
1249
+ * timestamp and the "Indexing: …" display label are INJECTED (clearedAt param,
1250
+ * formatIndexingLabel callback) so the engine touches neither localStorage nor
1251
+ * view-specific display formatting. projectId is passed for link sanitization.
1252
+ */
1253
+
1254
+ declare function filterListByClearHorizon(list: any[], clearedAt: number): any[];
1255
+ declare function normalizeTextContent(content: any): string;
1256
+ declare function extractLastUserTextFromRequest(requestBody: any): string;
1257
+ /** The two openings an indexing prompt can have. A bg-queue item that starts with
1258
+ * neither is an ordinary chat that happened to be routed onto that queue. */
1259
+ declare function isIndexingRequestText(userText: any): boolean;
1260
+ type IndexingRequestRef = {
1261
+ name: string;
1262
+ path?: string;
1263
+ mime?: string;
1264
+ size?: number;
1265
+ /** A CONTINUE pass rather than the run's first. */
1266
+ continued: boolean;
1267
+ };
1268
+ /**
1269
+ * The file an indexing prompt is about, read back out of the prompt itself.
1270
+ *
1271
+ * The prompt is the only description of the pass that survives on the server, so
1272
+ * this is how BOTH a history rebuild and a worker-minted pass the client never
1273
+ * dispatched (ChatSession._adoptWorkerIndexingPasses) recover the file. Shared so
1274
+ * the two produce the same `_indexFile`, which is what makes them group together.
1275
+ */
1276
+ declare function parseIndexingRequestText(userText: any): IndexingRequestRef | null;
1277
+ /**
1278
+ * One bounded look at the background-indexing queue: which files still have a
1279
+ * pass pending or running? This is the same negative signal ChatSession's
1280
+ * display layer relies on - for a worker-driven (auto_continue) run, only the
1281
+ * queue can say the run is over, because the worker enqueues continuation
1282
+ * passes the client never dispatched.
1283
+ *
1284
+ * Returns every storage path AND file name found on live passes (both, because
1285
+ * older prompts may lack the storage-path line), plus `checked`: false when a
1286
+ * page came back full, in which case absence from `keys` proves nothing and
1287
+ * the caller must keep whatever state it already had.
1288
+ *
1289
+ * SCOPE: the probed queue is "<userId>-bg" - THIS user's dispatches only. A
1290
+ * chain launched by another collaborator or a widget end-user lives on their
1291
+ * queue and is invisible here, so "idle" must never be read as "nobody is
1292
+ * indexing this file", only as "this user's runs are over". The durable done::
1293
+ * marker (indexDoneUniqueId) is the cross-user signal.
1294
+ */
1295
+ declare function fetchLiveIndexingKeys(params: {
1296
+ service: string;
1297
+ owner: string;
1298
+ platform: 'claude' | 'openai';
1299
+ /** Same value the dispatch used - see bgIndexingQueueName. */
1300
+ userId?: string;
1301
+ }): Promise<{
1302
+ keys: Set<string>;
1303
+ checked: boolean;
1304
+ at: number;
1305
+ }>;
1306
+ /** Test hook: drop split-fetch state (all keys, or one). */
1307
+ declare function __resetSplitHistoryState(key?: string): void;
1308
+ type SplitHistoryResult = {
1309
+ list: any[];
1310
+ endOfList: boolean;
1311
+ startKeyHistory: any[];
1312
+ /** True when this chat had never been walked in this session — the first
1313
+ * paint. Consumers gate the "Loading indexing history" hint on it: a
1314
+ * mid-walk tab return restarts the walk for cursor safety but must stay
1315
+ * silent (flashing the hint on every return was the reported bug). */
1316
+ firstLoad?: boolean;
1317
+ /** Present only when `deferBg` was requested AND bg work remains: resolves
1318
+ * with the stub batch fetched in the background (the per-key lock is held
1319
+ * until it settles, so no other history call can interleave). The caller
1320
+ * merges the batch by timestamp — the same path older pages use. */
1321
+ bgPending?: Promise<{
1322
+ list: any[];
1323
+ endOfList: boolean;
1324
+ }>;
1325
+ };
1326
+ declare function getSplitChatHistory(params: {
1327
+ service: string;
1328
+ owner: string;
1329
+ platform: 'claude' | 'openai';
1330
+ userId?: string;
1331
+ }, fetchOptions: Record<string, any>,
1332
+ /** Test seam: replaces getChatHistory. Not for production callers. */
1333
+ _fetchImpl?: typeof getChatHistory): Promise<SplitHistoryResult>;
1334
+ type MapHistoryOptions = {
1335
+ clearedAt: number;
1336
+ projectId: string;
1337
+ /** View-side display formatter for "Indexing:/Reindexing: …" bubbles. */
1338
+ formatIndexingLabel: (name: string, mime?: string, size?: number | null, storagePath?: string, reindex?: boolean, continued?: boolean) => string;
1339
+ };
1340
+ declare function mapHistoryListToMessages(list: any[], platform: 'claude' | 'openai', opts: MapHistoryOptions): {
1341
+ messages: any[];
1342
+ runningItemIds: string[];
1343
+ };
1344
+
1345
+ /**
1346
+ * Keep older history REACHABLE by paging until the message box actually gains
1347
+ * something to scroll to.
1348
+ *
1349
+ * Older history is paged in by one trigger only: the user scrolling to the top
1350
+ * of the message box. That trigger has two ways to die, and collapsed indexing
1351
+ * rows cause both:
1352
+ *
1353
+ * 1. The box never scrolls. A file's every indexing pass (the first plus every
1354
+ * CONTINUE pass, request AND response bubble each) folds into ONE row, so a
1355
+ * full history page — twenty-plus messages — can render as a single line.
1356
+ * Content shorter than the viewport fires no scroll event, so page 2 is
1357
+ * never requested and any conversation the user had before that upload is
1358
+ * permanently out of reach.
1359
+ * 2. The fetched page adds no height. A page that is entirely the same file's
1360
+ * earlier passes joins the collapsed row already on screen and renders
1361
+ * nothing new. The user, sitting at scrollTop 0, scrolls up again — and
1362
+ * because the position never changed, no further scroll event fires.
1363
+ *
1364
+ * Both are the same shape: fetch, re-measure, and keep going until the user
1365
+ * genuinely gained reachable content, history ran out, or the pager stopped
1366
+ * advancing. `isSatisfied` is what differs between the two (can the box scroll
1367
+ * at all / did it grow), so the loop below takes it as a predicate.
1368
+ *
1369
+ * DOM-free like the rest of the engine — the caller supplies the measurement and
1370
+ * awaits its own render before measuring, so agent.vue and the widget run the
1371
+ * identical loop over their own pagers.
1372
+ */
1373
+ /** Overflow (px) that counts as "the user can scroll here". Comfortably more
1374
+ * than the 60px top threshold that triggers the next page, so a filled box has
1375
+ * real room to scroll rather than sitting one pixel from the trigger. */
1376
+ declare const HISTORY_FILL_SLACK_PX = 64;
1377
+ /** Pages one fill pass will request before giving up. Reached only by a chat
1378
+ * whose history really is dozens of pages of one file's indexing passes; the
1379
+ * cap exists so a pager that stops advancing can never spin forever. */
1380
+ declare const MAX_HISTORY_FILL_PAGES = 24;
1381
+ type FillHistoryViewportOptions = {
1382
+ /** The user has reachable content and paging can stop. Called AFTER the
1383
+ * caller's own render has settled (nextTick / rAF), since only the caller
1384
+ * knows when its view has painted — hence the allowance for a promise. */
1385
+ isSatisfied: () => boolean | Promise<boolean>;
1386
+ /** All history is loaded — nothing left to page in. */
1387
+ isEndOfList: () => boolean;
1388
+ /** A history request is already in flight. Waited out, not treated as a stop
1389
+ * condition: a background first-page refresh (the queue-detect tick fires one
1390
+ * every couple of seconds while a file is indexing) would otherwise swallow
1391
+ * the user's scroll-up entirely, and scrolling up again from scrollTop 0
1392
+ * produces no second event to retry with. */
1393
+ isLoading: () => boolean;
1394
+ /** Messages currently loaded. Used to detect a page that added nothing, which
1395
+ * means the pager is not advancing and looping would never terminate. */
1396
+ messageCount: () => number;
1397
+ /** Fetch ONE older page (the caller's own fetchMore path, scroll-restore and
1398
+ * all). Return `false` when the request was NOT issued (the caller's own
1399
+ * single-flight guard swallowed it) so the loop retries instead of reading
1400
+ * the unchanged message count as an exhausted pager. Anything else, including
1401
+ * undefined, means it was attempted. */
1402
+ fetchOlder: () => Promise<boolean | void | any>;
1403
+ /** The chat this fill was started for is gone (project switched, view
1404
+ * unmounted, gate token bumped). Checked between pages so a stale fill can
1405
+ * never keep paging another chat's history. */
1406
+ isStale?: () => boolean;
1407
+ maxPages?: number;
1408
+ };
1409
+ /**
1410
+ * Page older history until `isSatisfied`, until history runs out, or until the
1411
+ * pager stops advancing. Never throws: a failed page ends the fill, and the
1412
+ * user's own scrolling remains the fallback trigger.
1413
+ */
1414
+ declare function fillHistoryViewport(opts: FillHistoryViewportOptions): Promise<void>;
1415
+ /**
1416
+ * One fill loop per view, with predicates COMBINED rather than dropped.
1417
+ *
1418
+ * Fills come from several places at once — a first page finishing, a window
1419
+ * resize, a row being collapsed, and the user's own scroll to the top — and a
1420
+ * plain "one at a time, drop the rest" guard picks the wrong winner: a resize
1421
+ * fill (satisfied the moment the box can scroll at all) would swallow the user's
1422
+ * scroll-up (which needs content specifically ABOVE them), and the scroll-up
1423
+ * cannot be retried, because a reader parked at scrollTop 0 produces no further
1424
+ * scroll event. Dropping the guard entirely is no better: every frame of a
1425
+ * window drag would start its own 24-page loop.
1426
+ *
1427
+ * So a request that arrives mid-loop ANDs its predicate into the running one:
1428
+ * the loop then keeps paging until EVERY caller is satisfied. Predicates that
1429
+ * come true are dropped as it goes, so the cost stays flat.
1430
+ */
1431
+ declare function createHistoryFiller(base: Omit<FillHistoryViewportOptions, 'isSatisfied'> & {
1432
+ /** Fired when the loop starts FETCHING and when it stops, and only on a real
1433
+ * change.
1434
+ *
1435
+ * This — not the caller's own per-request `isLoading` — is what "older
1436
+ * history is still coming in" means to a view. A fill is many pages, and
1437
+ * `isLoading` drops to false between every one of them, so anything
1438
+ * rendered off it flickers once per page for the whole loop. A collapsed
1439
+ * indexing row whose run begins above the loaded window renders exactly
1440
+ * that ("still loading this run" vs a status it cannot know yet), which is
1441
+ * why the loop has to publish its own span.
1442
+ *
1443
+ * Fetching, NOT requested. Most fills fetch nothing: they are fired on every
1444
+ * window resize, every row a user collapses, and every first-page load, and
1445
+ * the overwhelmingly common outcome is `isSatisfied` returning true on the
1446
+ * first look. Announcing at request time published a true/false pair for
1447
+ * each of those, and the widget's own satisfied-check spans two animation
1448
+ * frames — long enough for the browser to PAINT the intermediate state. Every
1449
+ * collapsed row strobed through "loading" on every resize tick. So the span
1450
+ * opens at the first actual page request, which is also the first moment the
1451
+ * claim is true. */
1452
+ onRunningChange?: (running: boolean) => void;
1453
+ }): {
1454
+ fill: (isSatisfied: () => boolean | Promise<boolean>) => Promise<void>;
1455
+ isRunning: () => boolean;
1456
+ };
1221
1457
 
1222
1458
  /**
1223
1459
  * ChatSession host adapter + state types.
@@ -1314,6 +1550,10 @@ interface ChatMessage {
1314
1550
  * which is how an 88-page file once "finished" at page 15. */
1315
1551
  _indexComplete?: boolean;
1316
1552
  _useBgQueue?: boolean;
1553
+ /** Mapped from an item delivered by the bg chain of the split history fetch
1554
+ * (stubs or deferred chats). Surface-frontier logic (retention boundary,
1555
+ * clear-horizon) skips these — their ids reach arbitrarily deep. */
1556
+ _fromBgChain?: boolean;
1317
1557
  /** Local id of a turn STAGED at Send time while its attachments upload. The
1318
1558
  * bubble exists before any server request does, so it is never matched by
1319
1559
  * _serverItemId and is never promoted/cancelled by the queue machinery —
@@ -1353,6 +1593,9 @@ interface ChatState {
1353
1593
  typingAbort: boolean;
1354
1594
  loadingHistory: boolean;
1355
1595
  loadingOlderHistory: boolean;
1596
+ /** A deferred bg stub batch (first-paint split fetch) is still in flight;
1597
+ * views show a small 'loading indexing history' hint while true. */
1598
+ bgHistoryLoading: boolean;
1356
1599
  historyEndOfList: boolean;
1357
1600
  historyStartKeyHistory: string[];
1358
1601
  historyRequestToken: number;
@@ -1656,6 +1899,15 @@ type IndexingGroup = {
1656
1899
  * loaded ones. 'status': the queue has not yet said whether this file is still
1657
1900
  * being worked on, which is the only thing that can end a worker-driven run. */
1658
1901
  resolvingReason?: 'history' | 'status';
1902
+ /** Synthesized from a durable run:: record: none of the run's passes are
1903
+ * among the loaded messages (bg history still deferred, or the run is older
1904
+ * than the paging cap). Header-and-status only — members/visibleMembers are
1905
+ * empty and there is nothing to cancel; the row is replaced by the real
1906
+ * group the moment actual passes load (same `key`, so expansion state
1907
+ * carries over). */
1908
+ stub?: boolean;
1909
+ /** The run:: record's stored error text, for a stub row's meta line. */
1910
+ stubError?: string;
1659
1911
  };
1660
1912
  type DisplayEntry = {
1661
1913
  kind: 'message';
@@ -1686,6 +1938,16 @@ type BuildDisplayListOptions = {
1686
1938
  /** Whether `liveIndexKeys` has been answered at least once for this chat. False
1687
1939
  * is "we do not know", and a worker-driven run stays unfinished on it. */
1688
1940
  liveIndexChecked?: boolean;
1941
+ /** Files carrying the durable done:: completion marker (one prefix sweep),
1942
+ * keyed like IndexingGroup.key (storage path; the bare-name fallback keys
1943
+ * of very old prompts simply never match — they keep the queue inference).
1944
+ * A marker is PROOF the file was read to the end: it settles a worker-run
1945
+ * green without waiting for the queue answer, and it is never withheld by
1946
+ * the resolving logic. A live queue hit still outranks it (a re-index in
1947
+ * flight whose marker-cascade delete lagged). */
1948
+ doneKeys?: {
1949
+ [fileKey: string]: boolean;
1950
+ };
1689
1951
  /** Server item ids of passes that were on a row when the user STOPPED it
1690
1952
  * (ChatSession.state.stoppedIndexIds). A run holding any of them is a run the
1691
1953
  * user stopped — see the status derivation for why a stop usually leaves no
@@ -1698,7 +1960,51 @@ type BuildDisplayListOptions = {
1698
1960
  * windowedIndexing). Passed in rather than read from config so this stays a
1699
1961
  * pure function of its inputs and can be exercised for both settings. */
1700
1962
  windowedIndexing?: boolean;
1963
+ /** Durable run:: records, keyed by STORAGE PATH (the consumer's marker
1964
+ * sweep). Each key with no real group in the loaded messages gets a
1965
+ * synthesized header-only row (see IndexingGroup.stub), placed by its
1966
+ * `started` timestamp. A real group for the same file — matched by key,
1967
+ * path, or name — always suppresses the stub: loaded passes are evidence,
1968
+ * the record is only a summary. */
1969
+ runStubs?: {
1970
+ [storagePath: string]: RunStubInfo;
1971
+ };
1972
+ /** The platform whose chat this list is for. run:: records are per-FILE,
1973
+ * but a chat is per (project, platform): a run started under Claude has
1974
+ * its passes in the Claude conversation and is invisible to the
1975
+ * OpenAI-scoped queue probe, so its stub could never be covered and never
1976
+ * be confirmed — it just sat there, in a chat it did not belong to.
1977
+ * Records minted before this was stamped carry no platform and are shown
1978
+ * in both, which keeps the leak to the historical set. */
1979
+ stubPlatform?: 'claude' | 'openai';
1980
+ /** The chat's clear-history horizon (ms epoch). Run records are service-
1981
+ * wide and know nothing about a cleared chat, so without this every
1982
+ * "Clear chat history" resurrects one row per indexed file. A stub whose
1983
+ * run ended (or, unfinished, began) at or before this moment is dropped —
1984
+ * unless the queue says the file is live RIGHT NOW, which no horizon can
1985
+ * make untrue. */
1986
+ stubClearedAt?: number;
1987
+ /** Clock injection for tests; defaults to Date.now(). Only run-stub
1988
+ * staleness reads it. */
1989
+ now?: number;
1990
+ };
1991
+ /** The display-relevant fields of a run:: record (see requests.ts
1992
+ * runIndexUniqueId for the record's contract). */
1993
+ type RunStubInfo = {
1994
+ status: 'working' | 'done' | 'error' | 'cancelled';
1995
+ filename?: string;
1996
+ started?: number;
1997
+ finished?: number;
1998
+ error?: string;
1999
+ /** Chat that owns this run. Absent on records minted before it was
2000
+ * stamped; see BuildDisplayListOptions.stubPlatform. */
2001
+ platform?: 'claude' | 'openai';
1701
2002
  };
2003
+ /** A 'working' run record older than this with no live-queue confirmation is
2004
+ * treated as unknown rather than live: a chain that died without reaching any
2005
+ * error path leaves 'working' dangling, and a row must not spin forever on a
2006
+ * claim nothing can end. */
2007
+ declare const RUN_RECORD_WORKING_STALE_MS: number;
1702
2008
  declare function parseIndexingLabel(content: string): {
1703
2009
  name: string;
1704
2010
  path?: string;
@@ -2004,6 +2310,17 @@ declare class ChatSession {
2004
2310
  resumePolling(reason: string): Promise<void>;
2005
2311
  private _newLocalId;
2006
2312
  getHistoryCacheKey(): string;
2313
+ private _hydratedBodies;
2314
+ private _hydratingItems;
2315
+ /** Re-apply memoized hydrated texts onto freshly-mapped messages. Both
2316
+ * clients call this right after their mapper runs (loadHistory does it
2317
+ * internally); it mutates the given array's items in place. */
2318
+ applyHydratedBodies(messages: ChatMessage[]): void;
2319
+ /** Fetch the real response bodies for compact history stubs (one csr-poll
2320
+ * point lookup per item id), memoize, and swap them into the live list.
2321
+ * Best-effort: a failed lookup leaves the stub (its head + fallback line
2322
+ * still render) and a later expand retries. */
2323
+ hydrateCompactItems(itemIds: string[]): Promise<void>;
2007
2324
  updateHistoryCache(): void;
2008
2325
  /**
2009
2326
  * Land a resolved reply in the history cache of a chat that is NOT currently
@@ -2274,6 +2591,10 @@ declare class ChatSession {
2274
2591
  */
2275
2592
  private _adoptingWorkerPasses;
2276
2593
  private _adoptWorkerIndexingPasses;
2594
+ /** Anything at all suggesting THIS project's indexing may be live: a queued
2595
+ * local entry, a recorded live key (the adopt look just wrote them), or an
2596
+ * attached poll. Gates the passive adopt ladder's climb. */
2597
+ private _hasLiveIndexEvidence;
2277
2598
  /** Any of these ids still queued or still polled, i.e. surviving work. */
2278
2599
  private _isTrackingAny;
2279
2600
  /** One live bg-queue item -> a BgTaskEntry, if it is an indexing pass this
@@ -2285,6 +2606,28 @@ declare class ChatSession {
2285
2606
  * cancelQueuedMessage, which drives one, has nothing to act on). */
2286
2607
  private _cancelServerItem;
2287
2608
  drainBgTaskQueue(): void;
2609
+ /** Fire the consumer's done::-marker hook for a run whose completion this
2610
+ * client knows DETERMINISTICALLY (see the two call sites in
2611
+ * maybeResumeIndexing). Best-effort by contract; identity-checked so a
2612
+ * project switch mid-settle cannot stamp the wrong service. */
2613
+ _mintDoneMarker(entry: BgTaskEntry): void;
2614
+ /** Short, storable form of an error body for the run:: record. */
2615
+ _runErrorText(response: any): string;
2616
+ /** Close the records of a run whose pass settled OFF-POLL — the answer came
2617
+ * back as history (hidden tab, dead poll, resume refetch), so none of the
2618
+ * poll-side settle handlers ran. Only for SINGLE-PASS files, where one
2619
+ * settled pass is deterministically the whole run (the same contract as
2620
+ * maybeResumeIndexing's single-pass branch); paged files stay with their
2621
+ * drivers. Outcome is read from the settled bubbles' own flags, which is
2622
+ * all the history mapping left us. Best-effort and idempotent throughout. */
2623
+ _flipRunFromSettledEntry(entry: BgTaskEntry): void;
2624
+ /** Close the durable run:: record for an ending THIS client observed.
2625
+ * service comes from the ENTRY, not the current identity: unlike the done::
2626
+ * mint above, a status flip must land even if the user switched projects
2627
+ * mid-settle — otherwise the record lies 'working' forever. Best-effort
2628
+ * through upsertIndexRunRecordSafe; the consumer's precedence guard keeps
2629
+ * repeats and races harmless. */
2630
+ _flipRunRecord(entry: BgTaskEntry, status: IndexRunStatus, error?: string): void;
2288
2631
  maybeResumeIndexing(entry: BgTaskEntry, response: any, platform: string): void;
2289
2632
  loadHistory(fetchMore?: boolean, token?: number): Promise<void>;
2290
2633
  uploadSingleAttachment(att: any, stageId?: string): Promise<Array<{
@@ -2301,4 +2644,4 @@ declare class ChatSession {
2301
2644
  bumpGate(): void;
2302
2645
  }
2303
2646
 
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 };
2647
+ 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, MIN_INPUT_TOKEN_BUDGET, MIN_PER_REQUEST_INPUT_CAP, 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, 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, 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, runIndexUniqueId, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, upsertIndexRunRecordSafe, wallClockNow };