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/bunnyquery.css +95 -5
- package/bunnyquery.js +1400 -101
- package/dist/engine.cjs +1018 -78
- package/dist/engine.cjs.map +1 -1
- package/dist/engine.d.mts +506 -163
- package/dist/engine.d.ts +506 -163
- package/dist/engine.mjs +1002 -79
- 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/index.ts +15 -1
- package/src/engine/indexing_groups.ts +248 -3
- 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;
|
|
@@ -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 };
|