bunnyquery 1.9.1 → 1.9.4

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
@@ -279,6 +279,18 @@ type ChatSystemPromptParams = {
279
279
  * given when the caller says which one this is.
280
280
  */
281
281
  client?: 'console' | 'widget';
282
+ /**
283
+ * The access group THIS project's indexer writes its records at, from the
284
+ * project's `default_access_group` setting.
285
+ *
286
+ * The MCP auto-fills an index/tag query that names a table but no group with
287
+ * "authorized", which used to be right because every BunnyQuery record was
288
+ * hardcoded to it. Now a project can index at "public" (so an anonymous
289
+ * visitor can read it) or "private", and on those projects the auto-fill
290
+ * silently searches a group the data is not in and answers "nothing found".
291
+ * Defaults to 'authorized', which is what an unset project still uses.
292
+ */
293
+ indexAccessGroup?: string;
282
294
  };
283
295
  declare function buildChatSystemPrompt(params: ChatSystemPromptParams): string;
284
296
 
@@ -297,6 +309,13 @@ type IndexingSystemPromptParams = {
297
309
  serviceName?: string;
298
310
  /** Project description. When present, name + description are appended. */
299
311
  serviceDescription?: string;
312
+ /**
313
+ * Access group every record written during this run must carry. Chosen by the
314
+ * uploader (project default, or a per-upload prompt) and already applied to
315
+ * the "src::" file record before indexing starts. Defaults to "authorized",
316
+ * which is what every record written before this setting existed used.
317
+ */
318
+ accessGroup?: 'public' | 'authorized' | 'private';
300
319
  };
301
320
  declare function buildIndexingSystemPrompt(params: IndexingSystemPromptParams): string;
302
321
 
@@ -321,6 +340,18 @@ type IndexingAttachmentInfo = {
321
340
  size?: number;
322
341
  /** Temporary signed URL the agent/MCP fetches to read the file contents. */
323
342
  url: string;
343
+ /**
344
+ * Access group every record extracted from this file must be written at.
345
+ *
346
+ * The uploader chooses it (project default, or a per-upload prompt), and the
347
+ * `src::` file record is already created at this group before indexing starts.
348
+ * The rows, chapters and summaries the agent writes have to MATCH it: skapi's
349
+ * access group is part of a record's table key, so a public file whose rows
350
+ * were saved as "authorized" is a file an anonymous visitor can see the name
351
+ * of and none of the contents of. Omitted means "authorized", which is what
352
+ * every record written before this setting existed used.
353
+ */
354
+ accessGroup?: 'public' | 'authorized' | 'private';
324
355
  };
325
356
  type BuildIndexingUserMessageOptions = {
326
357
  /**
@@ -343,6 +374,13 @@ type BuildIndexingUserMessageOptions = {
343
374
  */
344
375
  pagedRead?: boolean;
345
376
  };
377
+ /**
378
+ * The access group to write this file's records at. One place, so the user
379
+ * message, the continue message and the system prompt cannot disagree.
380
+ */
381
+ declare function indexingAccessGroup(attachment: {
382
+ accessGroup?: string;
383
+ }): 'public' | 'authorized' | 'private';
346
384
  declare function buildIndexingUserMessage(attachment: IndexingAttachmentInfo, options?: BuildIndexingUserMessageOptions): string;
347
385
  /**
348
386
  * Token the WORKER substitutes with the 1-based first page of the window it is about to
@@ -1248,8 +1286,14 @@ type CallClaudeWithMcpParams = {
1248
1286
  declare const POLL_INTERVAL = 3000;
1249
1287
  declare const MAX_CONCURRENT_BG_POLLS = 6;
1250
1288
  declare function callClaudeWithMcp({ prompt, messages, service, owner, userId, model, maxTokens, system, mcpServer, extractContent, fileUrls, }: CallClaudeWithMcpParams): Promise<any>;
1251
- declare function callClaudeWithPublicMcp(prompt: string, service: string, owner: string, messages?: ClaudeMessage[], system?: string, model?: string, userId?: string, extractContent?: ExtractDirective[], fileUrls?: FileUrlDirective[], onResponse?: (res: any) => void, onError?: (err: any) => void): Promise<any>;
1252
- declare function callOpenAIWithPublicMcp(prompt: string, service: string, owner: string, messages?: OpenAIMessage[], system?: string, model?: string, userId?: string, extractContent?: ExtractDirective[], fileUrls?: FileUrlDirective[], onResponse?: (res: any) => void, onError?: (err: any) => void): Promise<any>;
1289
+ declare function callClaudeWithPublicMcp(prompt: string, service: string, owner: string, messages?: ClaudeMessage[], system?: string, model?: string, userId?: string, extractContent?: ExtractDirective[], fileUrls?: FileUrlDirective[], onResponse?: (res: any) => void, onError?: (err: any) => void, mcpScope?: {
1290
+ anonymous?: boolean;
1291
+ publicProjectId?: string;
1292
+ }): Promise<any>;
1293
+ declare function callOpenAIWithPublicMcp(prompt: string, service: string, owner: string, messages?: OpenAIMessage[], system?: string, model?: string, userId?: string, extractContent?: ExtractDirective[], fileUrls?: FileUrlDirective[], onResponse?: (res: any) => void, onError?: (err: any) => void, mcpScope?: {
1294
+ anonymous?: boolean;
1295
+ publicProjectId?: string;
1296
+ }): Promise<any>;
1253
1297
  type AttachmentSaveInfo = {
1254
1298
  platform: 'claude' | 'openai';
1255
1299
  model?: string;
@@ -1276,6 +1320,13 @@ type AttachmentSaveInfo = {
1276
1320
  mime?: string;
1277
1321
  size?: number;
1278
1322
  url: string;
1323
+ /**
1324
+ * Access group this file's records are written at (the uploader's choice,
1325
+ * already applied to the "src::" record). Threaded into the indexing
1326
+ * prompts so the agent's own records land in the same group; omitted
1327
+ * means "authorized", the group everything used before the setting existed.
1328
+ */
1329
+ accessGroup?: 'public' | 'authorized' | 'private';
1279
1330
  };
1280
1331
  /**
1281
1332
  * Content parsed CLIENT-SIDE by an attachment-parser plugin (e.g. an .hwp
@@ -1453,6 +1504,19 @@ interface ChatIdentity {
1453
1504
  owner: string;
1454
1505
  /** Per-user queue name (falls back to projectId). */
1455
1506
  userId: string;
1507
+ /**
1508
+ * This chat is being used by a visitor with NO account, on a project whose
1509
+ * owner allows that.
1510
+ *
1511
+ * It changes where the MCP tools point. A signed-in turn goes to the MCP
1512
+ * server's root endpoint and authenticates with the caller's own token; an
1513
+ * anonymous turn has no token to send, and an EMPTY one is worse than none
1514
+ * (the server cannot identify a project from it, and an empty credential may
1515
+ * be rejected by the provider before the request is even made). So an
1516
+ * anonymous turn goes to the project-scoped endpoint instead, which is
1517
+ * read-only, restricted to public records, and needs no credential at all.
1518
+ */
1519
+ anonymous?: boolean;
1456
1520
  platform: 'claude' | 'openai' | 'none';
1457
1521
  model?: string;
1458
1522
  serviceName?: string;
@@ -1676,6 +1740,22 @@ interface ChatHost {
1676
1740
  mime?: string;
1677
1741
  size?: number;
1678
1742
  }): Promise<any>;
1743
+ /**
1744
+ * The access group this file's records must be written at: the uploader's
1745
+ * choice, which is the project default or a per-upload answer.
1746
+ *
1747
+ * Asked PER FILE, and asked AFTER ensureFileIndexRecord has run, because the
1748
+ * host is what actually creates the "src::" record and it must report the
1749
+ * group it really used. The engine threads the answer into the indexing
1750
+ * prompts so the agent's own records land in the same group; a record saved
1751
+ * under a different group is in a different table and never comes back with
1752
+ * the rest of the file.
1753
+ *
1754
+ * Optional and may return a promise. A host without it (or one that returns
1755
+ * nothing) gets "authorized", which is what every record used before the
1756
+ * setting existed.
1757
+ */
1758
+ uploadAccessGroup?(storagePath: string): 'public' | 'authorized' | 'private' | undefined | Promise<'public' | 'authorized' | 'private' | undefined>;
1679
1759
  /** Map a relative path to the consumer's db storage key (e.g. uid-prefixed). */
1680
1760
  storagePathFor(relPath: string): string;
1681
1761
  getMimeType(name: string): string | null;
@@ -2714,6 +2794,19 @@ declare class ChatSession {
2714
2794
  */
2715
2795
  resumePolling(reason: string): Promise<void>;
2716
2796
  private _newLocalId;
2797
+ /**
2798
+ * The key every per-chat cache hangs off: the restored message cache, the
2799
+ * hydrated-body memo, the live-index key and the per-file storage-path key.
2800
+ *
2801
+ * It carries the IDENTITY as well as the project and platform. A single
2802
+ * browser can hold more than one conversation on one project without a
2803
+ * reload — an anonymous visitor who signs in, or a dashboard user who logs
2804
+ * out and back in as someone else — and with an identity-free key the
2805
+ * previous conversation stayed in the cache and was re-rendered, and written
2806
+ * back, as the new one's. `userId` is the same value the request queue is
2807
+ * named after, so two identities that share a queue share a cache, which is
2808
+ * exactly right.
2809
+ */
2717
2810
  getHistoryCacheKey(): string;
2718
2811
  private _hydratedBodies;
2719
2812
  private _hydratingItems;
@@ -3120,4 +3213,4 @@ declare class ChatSession {
3120
3213
  bumpGate(): void;
3121
3214
  }
3122
3215
 
3123
- export { type AiAgentPlatform, type AnchorBoxEl, type AnchorRowEl, type AttachmentFailureGroup, type AttachmentParser, type AttachmentSaveInfo, BG_INDEXING_QUEUE_SUFFIX, BOM, BOM_EXTS, type BgTaskEntry, type BoundedChatOptions, type BuildDisplayListOptions, type BuildIndexingUserMessageOptions, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, type CallClaudeWithMcpParams, type ChatEngineConfig, type ChatGreetingParams, type ChatGreetingParts, type ChatHost, type ChatIdentity, type ChatMessage, ChatSession, type ChatState, type ChatSystemPromptParams, type ClaudeMcpServerRequest, type ClaudeMcpToolConfig, type ClaudeMessage, type ClaudeRole, type ComposedUserMessage, DEFAULT_CLAUDE_MODEL, DEFAULT_CONTEXT_WINDOW, DEFAULT_OPENAI_MODEL, type DisplayEntry, EMPTY_INDEXING_REPLY, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, EXT_CONTENT_TYPES, type EncodingClass, type ExtractDirective, type FillHistoryViewportOptions, HISTORY_BUDGET_RATIO, HISTORY_FILL_SLACK_PX, HISTORY_TOKEN_BUDGET, HTML_EXTS, HTML_HEAD_WINDOW, IMAGE_PREVIEWS_PER_MESSAGE, INDEXING_COMPLETE_MARKER, INLINE_LINK_GLYPH, INLINE_LINK_UNAVAILABLE_GLYPH, INLINE_LINK_UNAVAILABLE_SUFFIX, INPUT_CAP_RATIO, type ImagePreviewContext, type IndexRunPatch, type IndexRunStatus, type IndexingAttachmentInfo, type IndexingFileRef, type IndexingGroup, type IndexingGroupStatus, type IndexingRequestRef, type IndexingSystemPromptParams, type InlineLinkContext, type InlineLinkMarkupOptions, type InlineLinkPart, LINK_LABEL_MAX_DISPLAY_CHARS, LINK_REFRESH_WINDOW_MS, MAX_CONCURRENT_BG_POLLS, MAX_HISTORY_FILL_PAGES, MAX_HISTORY_MESSAGES, MAX_OUTPUT_BY_MODEL, MAX_OUTPUT_TOKENS, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MINT_CACHE_GENERATION, MIN_INPUT_TOKEN_BUDGET, MIN_PER_REQUEST_INPUT_CAP, type MapHistoryOptions, OUTPUT_TOKEN_RESERVE, type OpenAIMessage, POLL_INTERVAL, PRESIGN_SAFETY_MARGIN_MS, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, PREVIEW_LAYOUT_BOX_SELECTOR, PREVIEW_URL_EXPIRES_SECONDS, type ParsedAiAgent, type PinnedDispatchContext, type PreviewImageEl, RENDER_FROM_TOKEN, RTF_EXTS, RUN_RECORD_WORKING_STALE_MS, type RenderableInlineLink, type RescueDecisionContext, type RowAnchor, type RunStubInfo, type ScrollAnchor, type ScrollAnchorOptions, TOOL_AND_RESPONSE_BUFFER, type VisionProfile, XML_EXTS, __resetSplitHistoryState, applyEncodingDeclaration, bgIndexingQueueName, buildAiAgentValue, buildBoundedChatMessages, buildChatDisplayList, buildChatGreeting, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildHistoryItemFullId, buildIndexingContinueMessage, buildIndexingRenderContinueTemplate, buildIndexingRenderMessage, buildIndexingSystemPrompt, buildIndexingUserMessage, buildIndexingWindowMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, canonicalizePathForm, chatEngineConfig, classifyInlineLink, clearAttachmentParsers, clearImagePreviewCache, composeUserMessage, configureChatEngine, contentTypeForExt, createHistoryFiller, createInlineLinkRegex, createScrollAnchor, encodePathSegments, encodingClassForExt, ensureHtmlCharset, ensureXmlEncoding, escapeInlineHtml, escapeRtfNonAscii, estimateMessageTokens, estimateTextTokens, extOf, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, fetchLiveIndexingKeys, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, formatChatTimestamp, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, getInputTokenBudget, getMaxOutputTokens, getModelContextWindow, getProjectContextWindow, getSplitChatHistory, getVisionProfile, groupAttachmentFailures, hasBom, hydrateImagePreviews, indexDoneUniqueId, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isIndexingRequestText, isLinkUnavailable, isNonRetryableRequestError, isOfficeFile, isPreviewableImagePath, isProviderApiKeyError, isServerExtractable, isServiceDbAttachmentHref, linkUnavailableKeyForHref, linkUnavailableKeyForPath, linkUnavailableKeysForPath, listClaudeModels, listOpenAIModels, looksLikeRtf, makeExtractPlaceholder, mapHistoryListToMessages, markImagePreviewStale, mintCacheBustStamp, needsBomForExt, normalizeAttachmentPathCandidate, normalizeExt, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAiAgentValue, parseAttachmentContent, parseIndexingLabel, parseIndexingRequestText, peekImagePreviewUrl, prepareDownloadText, presignExpiryEpochMs, previewImageContentType, previewLayoutBox, previewMintCacheToken, previewableExtOf, readExpiredAttachmentHref, registerAttachmentParser, registerModelContextWindows, renderInlineLinkHtml, repairUrlEntities, repairUrlWhitespace, resolveImagePreviewUrl, runIndexUniqueId, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, shouldRescueInFlightMessage, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, upsertIndexRunRecordSafe, wallClockNow };
3216
+ export { type AiAgentPlatform, type AnchorBoxEl, type AnchorRowEl, type AttachmentFailureGroup, type AttachmentParser, type AttachmentSaveInfo, BG_INDEXING_QUEUE_SUFFIX, BOM, BOM_EXTS, type BgTaskEntry, type BoundedChatOptions, type BuildDisplayListOptions, type BuildIndexingUserMessageOptions, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, type CallClaudeWithMcpParams, type ChatEngineConfig, type ChatGreetingParams, type ChatGreetingParts, type ChatHost, type ChatIdentity, type ChatMessage, ChatSession, type ChatState, type ChatSystemPromptParams, type ClaudeMcpServerRequest, type ClaudeMcpToolConfig, type ClaudeMessage, type ClaudeRole, type ComposedUserMessage, DEFAULT_CLAUDE_MODEL, DEFAULT_CONTEXT_WINDOW, DEFAULT_OPENAI_MODEL, type DisplayEntry, EMPTY_INDEXING_REPLY, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, EXT_CONTENT_TYPES, type EncodingClass, type ExtractDirective, type FillHistoryViewportOptions, HISTORY_BUDGET_RATIO, HISTORY_FILL_SLACK_PX, HISTORY_TOKEN_BUDGET, HTML_EXTS, HTML_HEAD_WINDOW, IMAGE_PREVIEWS_PER_MESSAGE, INDEXING_COMPLETE_MARKER, INLINE_LINK_GLYPH, INLINE_LINK_UNAVAILABLE_GLYPH, INLINE_LINK_UNAVAILABLE_SUFFIX, INPUT_CAP_RATIO, type ImagePreviewContext, type IndexRunPatch, type IndexRunStatus, type IndexingAttachmentInfo, type IndexingFileRef, type IndexingGroup, type IndexingGroupStatus, type IndexingRequestRef, type IndexingSystemPromptParams, type InlineLinkContext, type InlineLinkMarkupOptions, type InlineLinkPart, LINK_LABEL_MAX_DISPLAY_CHARS, LINK_REFRESH_WINDOW_MS, MAX_CONCURRENT_BG_POLLS, MAX_HISTORY_FILL_PAGES, MAX_HISTORY_MESSAGES, MAX_OUTPUT_BY_MODEL, MAX_OUTPUT_TOKENS, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MINT_CACHE_GENERATION, MIN_INPUT_TOKEN_BUDGET, MIN_PER_REQUEST_INPUT_CAP, type MapHistoryOptions, OUTPUT_TOKEN_RESERVE, type OpenAIMessage, POLL_INTERVAL, PRESIGN_SAFETY_MARGIN_MS, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, PREVIEW_LAYOUT_BOX_SELECTOR, PREVIEW_URL_EXPIRES_SECONDS, type ParsedAiAgent, type PinnedDispatchContext, type PreviewImageEl, RENDER_FROM_TOKEN, RTF_EXTS, RUN_RECORD_WORKING_STALE_MS, type RenderableInlineLink, type RescueDecisionContext, type RowAnchor, type RunStubInfo, type ScrollAnchor, type ScrollAnchorOptions, TOOL_AND_RESPONSE_BUFFER, type VisionProfile, XML_EXTS, __resetSplitHistoryState, applyEncodingDeclaration, bgIndexingQueueName, buildAiAgentValue, buildBoundedChatMessages, buildChatDisplayList, buildChatGreeting, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildHistoryItemFullId, buildIndexingContinueMessage, buildIndexingRenderContinueTemplate, buildIndexingRenderMessage, buildIndexingSystemPrompt, buildIndexingUserMessage, buildIndexingWindowMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, canonicalizePathForm, chatEngineConfig, classifyInlineLink, clearAttachmentParsers, clearImagePreviewCache, composeUserMessage, configureChatEngine, contentTypeForExt, createHistoryFiller, createInlineLinkRegex, createScrollAnchor, encodePathSegments, encodingClassForExt, ensureHtmlCharset, ensureXmlEncoding, escapeInlineHtml, escapeRtfNonAscii, estimateMessageTokens, estimateTextTokens, extOf, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, fetchLiveIndexingKeys, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, formatChatTimestamp, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, getInputTokenBudget, getMaxOutputTokens, getModelContextWindow, getProjectContextWindow, getSplitChatHistory, getVisionProfile, groupAttachmentFailures, hasBom, hydrateImagePreviews, indexDoneUniqueId, indexingAccessGroup, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isIndexingRequestText, isLinkUnavailable, isNonRetryableRequestError, isOfficeFile, isPreviewableImagePath, isProviderApiKeyError, isServerExtractable, isServiceDbAttachmentHref, linkUnavailableKeyForHref, linkUnavailableKeyForPath, linkUnavailableKeysForPath, listClaudeModels, listOpenAIModels, looksLikeRtf, makeExtractPlaceholder, mapHistoryListToMessages, markImagePreviewStale, mintCacheBustStamp, needsBomForExt, normalizeAttachmentPathCandidate, normalizeExt, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAiAgentValue, parseAttachmentContent, parseIndexingLabel, parseIndexingRequestText, peekImagePreviewUrl, prepareDownloadText, presignExpiryEpochMs, previewImageContentType, previewLayoutBox, previewMintCacheToken, previewableExtOf, readExpiredAttachmentHref, registerAttachmentParser, registerModelContextWindows, renderInlineLinkHtml, repairUrlEntities, repairUrlWhitespace, resolveImagePreviewUrl, runIndexUniqueId, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, shouldRescueInFlightMessage, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, upsertIndexRunRecordSafe, wallClockNow };
package/dist/engine.d.ts CHANGED
@@ -279,6 +279,18 @@ type ChatSystemPromptParams = {
279
279
  * given when the caller says which one this is.
280
280
  */
281
281
  client?: 'console' | 'widget';
282
+ /**
283
+ * The access group THIS project's indexer writes its records at, from the
284
+ * project's `default_access_group` setting.
285
+ *
286
+ * The MCP auto-fills an index/tag query that names a table but no group with
287
+ * "authorized", which used to be right because every BunnyQuery record was
288
+ * hardcoded to it. Now a project can index at "public" (so an anonymous
289
+ * visitor can read it) or "private", and on those projects the auto-fill
290
+ * silently searches a group the data is not in and answers "nothing found".
291
+ * Defaults to 'authorized', which is what an unset project still uses.
292
+ */
293
+ indexAccessGroup?: string;
282
294
  };
283
295
  declare function buildChatSystemPrompt(params: ChatSystemPromptParams): string;
284
296
 
@@ -297,6 +309,13 @@ type IndexingSystemPromptParams = {
297
309
  serviceName?: string;
298
310
  /** Project description. When present, name + description are appended. */
299
311
  serviceDescription?: string;
312
+ /**
313
+ * Access group every record written during this run must carry. Chosen by the
314
+ * uploader (project default, or a per-upload prompt) and already applied to
315
+ * the "src::" file record before indexing starts. Defaults to "authorized",
316
+ * which is what every record written before this setting existed used.
317
+ */
318
+ accessGroup?: 'public' | 'authorized' | 'private';
300
319
  };
301
320
  declare function buildIndexingSystemPrompt(params: IndexingSystemPromptParams): string;
302
321
 
@@ -321,6 +340,18 @@ type IndexingAttachmentInfo = {
321
340
  size?: number;
322
341
  /** Temporary signed URL the agent/MCP fetches to read the file contents. */
323
342
  url: string;
343
+ /**
344
+ * Access group every record extracted from this file must be written at.
345
+ *
346
+ * The uploader chooses it (project default, or a per-upload prompt), and the
347
+ * `src::` file record is already created at this group before indexing starts.
348
+ * The rows, chapters and summaries the agent writes have to MATCH it: skapi's
349
+ * access group is part of a record's table key, so a public file whose rows
350
+ * were saved as "authorized" is a file an anonymous visitor can see the name
351
+ * of and none of the contents of. Omitted means "authorized", which is what
352
+ * every record written before this setting existed used.
353
+ */
354
+ accessGroup?: 'public' | 'authorized' | 'private';
324
355
  };
325
356
  type BuildIndexingUserMessageOptions = {
326
357
  /**
@@ -343,6 +374,13 @@ type BuildIndexingUserMessageOptions = {
343
374
  */
344
375
  pagedRead?: boolean;
345
376
  };
377
+ /**
378
+ * The access group to write this file's records at. One place, so the user
379
+ * message, the continue message and the system prompt cannot disagree.
380
+ */
381
+ declare function indexingAccessGroup(attachment: {
382
+ accessGroup?: string;
383
+ }): 'public' | 'authorized' | 'private';
346
384
  declare function buildIndexingUserMessage(attachment: IndexingAttachmentInfo, options?: BuildIndexingUserMessageOptions): string;
347
385
  /**
348
386
  * Token the WORKER substitutes with the 1-based first page of the window it is about to
@@ -1248,8 +1286,14 @@ type CallClaudeWithMcpParams = {
1248
1286
  declare const POLL_INTERVAL = 3000;
1249
1287
  declare const MAX_CONCURRENT_BG_POLLS = 6;
1250
1288
  declare function callClaudeWithMcp({ prompt, messages, service, owner, userId, model, maxTokens, system, mcpServer, extractContent, fileUrls, }: CallClaudeWithMcpParams): Promise<any>;
1251
- declare function callClaudeWithPublicMcp(prompt: string, service: string, owner: string, messages?: ClaudeMessage[], system?: string, model?: string, userId?: string, extractContent?: ExtractDirective[], fileUrls?: FileUrlDirective[], onResponse?: (res: any) => void, onError?: (err: any) => void): Promise<any>;
1252
- declare function callOpenAIWithPublicMcp(prompt: string, service: string, owner: string, messages?: OpenAIMessage[], system?: string, model?: string, userId?: string, extractContent?: ExtractDirective[], fileUrls?: FileUrlDirective[], onResponse?: (res: any) => void, onError?: (err: any) => void): Promise<any>;
1289
+ declare function callClaudeWithPublicMcp(prompt: string, service: string, owner: string, messages?: ClaudeMessage[], system?: string, model?: string, userId?: string, extractContent?: ExtractDirective[], fileUrls?: FileUrlDirective[], onResponse?: (res: any) => void, onError?: (err: any) => void, mcpScope?: {
1290
+ anonymous?: boolean;
1291
+ publicProjectId?: string;
1292
+ }): Promise<any>;
1293
+ declare function callOpenAIWithPublicMcp(prompt: string, service: string, owner: string, messages?: OpenAIMessage[], system?: string, model?: string, userId?: string, extractContent?: ExtractDirective[], fileUrls?: FileUrlDirective[], onResponse?: (res: any) => void, onError?: (err: any) => void, mcpScope?: {
1294
+ anonymous?: boolean;
1295
+ publicProjectId?: string;
1296
+ }): Promise<any>;
1253
1297
  type AttachmentSaveInfo = {
1254
1298
  platform: 'claude' | 'openai';
1255
1299
  model?: string;
@@ -1276,6 +1320,13 @@ type AttachmentSaveInfo = {
1276
1320
  mime?: string;
1277
1321
  size?: number;
1278
1322
  url: string;
1323
+ /**
1324
+ * Access group this file's records are written at (the uploader's choice,
1325
+ * already applied to the "src::" record). Threaded into the indexing
1326
+ * prompts so the agent's own records land in the same group; omitted
1327
+ * means "authorized", the group everything used before the setting existed.
1328
+ */
1329
+ accessGroup?: 'public' | 'authorized' | 'private';
1279
1330
  };
1280
1331
  /**
1281
1332
  * Content parsed CLIENT-SIDE by an attachment-parser plugin (e.g. an .hwp
@@ -1453,6 +1504,19 @@ interface ChatIdentity {
1453
1504
  owner: string;
1454
1505
  /** Per-user queue name (falls back to projectId). */
1455
1506
  userId: string;
1507
+ /**
1508
+ * This chat is being used by a visitor with NO account, on a project whose
1509
+ * owner allows that.
1510
+ *
1511
+ * It changes where the MCP tools point. A signed-in turn goes to the MCP
1512
+ * server's root endpoint and authenticates with the caller's own token; an
1513
+ * anonymous turn has no token to send, and an EMPTY one is worse than none
1514
+ * (the server cannot identify a project from it, and an empty credential may
1515
+ * be rejected by the provider before the request is even made). So an
1516
+ * anonymous turn goes to the project-scoped endpoint instead, which is
1517
+ * read-only, restricted to public records, and needs no credential at all.
1518
+ */
1519
+ anonymous?: boolean;
1456
1520
  platform: 'claude' | 'openai' | 'none';
1457
1521
  model?: string;
1458
1522
  serviceName?: string;
@@ -1676,6 +1740,22 @@ interface ChatHost {
1676
1740
  mime?: string;
1677
1741
  size?: number;
1678
1742
  }): Promise<any>;
1743
+ /**
1744
+ * The access group this file's records must be written at: the uploader's
1745
+ * choice, which is the project default or a per-upload answer.
1746
+ *
1747
+ * Asked PER FILE, and asked AFTER ensureFileIndexRecord has run, because the
1748
+ * host is what actually creates the "src::" record and it must report the
1749
+ * group it really used. The engine threads the answer into the indexing
1750
+ * prompts so the agent's own records land in the same group; a record saved
1751
+ * under a different group is in a different table and never comes back with
1752
+ * the rest of the file.
1753
+ *
1754
+ * Optional and may return a promise. A host without it (or one that returns
1755
+ * nothing) gets "authorized", which is what every record used before the
1756
+ * setting existed.
1757
+ */
1758
+ uploadAccessGroup?(storagePath: string): 'public' | 'authorized' | 'private' | undefined | Promise<'public' | 'authorized' | 'private' | undefined>;
1679
1759
  /** Map a relative path to the consumer's db storage key (e.g. uid-prefixed). */
1680
1760
  storagePathFor(relPath: string): string;
1681
1761
  getMimeType(name: string): string | null;
@@ -2714,6 +2794,19 @@ declare class ChatSession {
2714
2794
  */
2715
2795
  resumePolling(reason: string): Promise<void>;
2716
2796
  private _newLocalId;
2797
+ /**
2798
+ * The key every per-chat cache hangs off: the restored message cache, the
2799
+ * hydrated-body memo, the live-index key and the per-file storage-path key.
2800
+ *
2801
+ * It carries the IDENTITY as well as the project and platform. A single
2802
+ * browser can hold more than one conversation on one project without a
2803
+ * reload — an anonymous visitor who signs in, or a dashboard user who logs
2804
+ * out and back in as someone else — and with an identity-free key the
2805
+ * previous conversation stayed in the cache and was re-rendered, and written
2806
+ * back, as the new one's. `userId` is the same value the request queue is
2807
+ * named after, so two identities that share a queue share a cache, which is
2808
+ * exactly right.
2809
+ */
2717
2810
  getHistoryCacheKey(): string;
2718
2811
  private _hydratedBodies;
2719
2812
  private _hydratingItems;
@@ -3120,4 +3213,4 @@ declare class ChatSession {
3120
3213
  bumpGate(): void;
3121
3214
  }
3122
3215
 
3123
- export { type AiAgentPlatform, type AnchorBoxEl, type AnchorRowEl, type AttachmentFailureGroup, type AttachmentParser, type AttachmentSaveInfo, BG_INDEXING_QUEUE_SUFFIX, BOM, BOM_EXTS, type BgTaskEntry, type BoundedChatOptions, type BuildDisplayListOptions, type BuildIndexingUserMessageOptions, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, type CallClaudeWithMcpParams, type ChatEngineConfig, type ChatGreetingParams, type ChatGreetingParts, type ChatHost, type ChatIdentity, type ChatMessage, ChatSession, type ChatState, type ChatSystemPromptParams, type ClaudeMcpServerRequest, type ClaudeMcpToolConfig, type ClaudeMessage, type ClaudeRole, type ComposedUserMessage, DEFAULT_CLAUDE_MODEL, DEFAULT_CONTEXT_WINDOW, DEFAULT_OPENAI_MODEL, type DisplayEntry, EMPTY_INDEXING_REPLY, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, EXT_CONTENT_TYPES, type EncodingClass, type ExtractDirective, type FillHistoryViewportOptions, HISTORY_BUDGET_RATIO, HISTORY_FILL_SLACK_PX, HISTORY_TOKEN_BUDGET, HTML_EXTS, HTML_HEAD_WINDOW, IMAGE_PREVIEWS_PER_MESSAGE, INDEXING_COMPLETE_MARKER, INLINE_LINK_GLYPH, INLINE_LINK_UNAVAILABLE_GLYPH, INLINE_LINK_UNAVAILABLE_SUFFIX, INPUT_CAP_RATIO, type ImagePreviewContext, type IndexRunPatch, type IndexRunStatus, type IndexingAttachmentInfo, type IndexingFileRef, type IndexingGroup, type IndexingGroupStatus, type IndexingRequestRef, type IndexingSystemPromptParams, type InlineLinkContext, type InlineLinkMarkupOptions, type InlineLinkPart, LINK_LABEL_MAX_DISPLAY_CHARS, LINK_REFRESH_WINDOW_MS, MAX_CONCURRENT_BG_POLLS, MAX_HISTORY_FILL_PAGES, MAX_HISTORY_MESSAGES, MAX_OUTPUT_BY_MODEL, MAX_OUTPUT_TOKENS, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MINT_CACHE_GENERATION, MIN_INPUT_TOKEN_BUDGET, MIN_PER_REQUEST_INPUT_CAP, type MapHistoryOptions, OUTPUT_TOKEN_RESERVE, type OpenAIMessage, POLL_INTERVAL, PRESIGN_SAFETY_MARGIN_MS, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, PREVIEW_LAYOUT_BOX_SELECTOR, PREVIEW_URL_EXPIRES_SECONDS, type ParsedAiAgent, type PinnedDispatchContext, type PreviewImageEl, RENDER_FROM_TOKEN, RTF_EXTS, RUN_RECORD_WORKING_STALE_MS, type RenderableInlineLink, type RescueDecisionContext, type RowAnchor, type RunStubInfo, type ScrollAnchor, type ScrollAnchorOptions, TOOL_AND_RESPONSE_BUFFER, type VisionProfile, XML_EXTS, __resetSplitHistoryState, applyEncodingDeclaration, bgIndexingQueueName, buildAiAgentValue, buildBoundedChatMessages, buildChatDisplayList, buildChatGreeting, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildHistoryItemFullId, buildIndexingContinueMessage, buildIndexingRenderContinueTemplate, buildIndexingRenderMessage, buildIndexingSystemPrompt, buildIndexingUserMessage, buildIndexingWindowMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, canonicalizePathForm, chatEngineConfig, classifyInlineLink, clearAttachmentParsers, clearImagePreviewCache, composeUserMessage, configureChatEngine, contentTypeForExt, createHistoryFiller, createInlineLinkRegex, createScrollAnchor, encodePathSegments, encodingClassForExt, ensureHtmlCharset, ensureXmlEncoding, escapeInlineHtml, escapeRtfNonAscii, estimateMessageTokens, estimateTextTokens, extOf, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, fetchLiveIndexingKeys, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, formatChatTimestamp, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, getInputTokenBudget, getMaxOutputTokens, getModelContextWindow, getProjectContextWindow, getSplitChatHistory, getVisionProfile, groupAttachmentFailures, hasBom, hydrateImagePreviews, indexDoneUniqueId, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isIndexingRequestText, isLinkUnavailable, isNonRetryableRequestError, isOfficeFile, isPreviewableImagePath, isProviderApiKeyError, isServerExtractable, isServiceDbAttachmentHref, linkUnavailableKeyForHref, linkUnavailableKeyForPath, linkUnavailableKeysForPath, listClaudeModels, listOpenAIModels, looksLikeRtf, makeExtractPlaceholder, mapHistoryListToMessages, markImagePreviewStale, mintCacheBustStamp, needsBomForExt, normalizeAttachmentPathCandidate, normalizeExt, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAiAgentValue, parseAttachmentContent, parseIndexingLabel, parseIndexingRequestText, peekImagePreviewUrl, prepareDownloadText, presignExpiryEpochMs, previewImageContentType, previewLayoutBox, previewMintCacheToken, previewableExtOf, readExpiredAttachmentHref, registerAttachmentParser, registerModelContextWindows, renderInlineLinkHtml, repairUrlEntities, repairUrlWhitespace, resolveImagePreviewUrl, runIndexUniqueId, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, shouldRescueInFlightMessage, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, upsertIndexRunRecordSafe, wallClockNow };
3216
+ export { type AiAgentPlatform, type AnchorBoxEl, type AnchorRowEl, type AttachmentFailureGroup, type AttachmentParser, type AttachmentSaveInfo, BG_INDEXING_QUEUE_SUFFIX, BOM, BOM_EXTS, type BgTaskEntry, type BoundedChatOptions, type BuildDisplayListOptions, type BuildIndexingUserMessageOptions, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, type CallClaudeWithMcpParams, type ChatEngineConfig, type ChatGreetingParams, type ChatGreetingParts, type ChatHost, type ChatIdentity, type ChatMessage, ChatSession, type ChatState, type ChatSystemPromptParams, type ClaudeMcpServerRequest, type ClaudeMcpToolConfig, type ClaudeMessage, type ClaudeRole, type ComposedUserMessage, DEFAULT_CLAUDE_MODEL, DEFAULT_CONTEXT_WINDOW, DEFAULT_OPENAI_MODEL, type DisplayEntry, EMPTY_INDEXING_REPLY, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, EXT_CONTENT_TYPES, type EncodingClass, type ExtractDirective, type FillHistoryViewportOptions, HISTORY_BUDGET_RATIO, HISTORY_FILL_SLACK_PX, HISTORY_TOKEN_BUDGET, HTML_EXTS, HTML_HEAD_WINDOW, IMAGE_PREVIEWS_PER_MESSAGE, INDEXING_COMPLETE_MARKER, INLINE_LINK_GLYPH, INLINE_LINK_UNAVAILABLE_GLYPH, INLINE_LINK_UNAVAILABLE_SUFFIX, INPUT_CAP_RATIO, type ImagePreviewContext, type IndexRunPatch, type IndexRunStatus, type IndexingAttachmentInfo, type IndexingFileRef, type IndexingGroup, type IndexingGroupStatus, type IndexingRequestRef, type IndexingSystemPromptParams, type InlineLinkContext, type InlineLinkMarkupOptions, type InlineLinkPart, LINK_LABEL_MAX_DISPLAY_CHARS, LINK_REFRESH_WINDOW_MS, MAX_CONCURRENT_BG_POLLS, MAX_HISTORY_FILL_PAGES, MAX_HISTORY_MESSAGES, MAX_OUTPUT_BY_MODEL, MAX_OUTPUT_TOKENS, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MINT_CACHE_GENERATION, MIN_INPUT_TOKEN_BUDGET, MIN_PER_REQUEST_INPUT_CAP, type MapHistoryOptions, OUTPUT_TOKEN_RESERVE, type OpenAIMessage, POLL_INTERVAL, PRESIGN_SAFETY_MARGIN_MS, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, PREVIEW_LAYOUT_BOX_SELECTOR, PREVIEW_URL_EXPIRES_SECONDS, type ParsedAiAgent, type PinnedDispatchContext, type PreviewImageEl, RENDER_FROM_TOKEN, RTF_EXTS, RUN_RECORD_WORKING_STALE_MS, type RenderableInlineLink, type RescueDecisionContext, type RowAnchor, type RunStubInfo, type ScrollAnchor, type ScrollAnchorOptions, TOOL_AND_RESPONSE_BUFFER, type VisionProfile, XML_EXTS, __resetSplitHistoryState, applyEncodingDeclaration, bgIndexingQueueName, buildAiAgentValue, buildBoundedChatMessages, buildChatDisplayList, buildChatGreeting, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildHistoryItemFullId, buildIndexingContinueMessage, buildIndexingRenderContinueTemplate, buildIndexingRenderMessage, buildIndexingSystemPrompt, buildIndexingUserMessage, buildIndexingWindowMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, canonicalizePathForm, chatEngineConfig, classifyInlineLink, clearAttachmentParsers, clearImagePreviewCache, composeUserMessage, configureChatEngine, contentTypeForExt, createHistoryFiller, createInlineLinkRegex, createScrollAnchor, encodePathSegments, encodingClassForExt, ensureHtmlCharset, ensureXmlEncoding, escapeInlineHtml, escapeRtfNonAscii, estimateMessageTokens, estimateTextTokens, extOf, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, fetchLiveIndexingKeys, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, formatChatTimestamp, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, getInputTokenBudget, getMaxOutputTokens, getModelContextWindow, getProjectContextWindow, getSplitChatHistory, getVisionProfile, groupAttachmentFailures, hasBom, hydrateImagePreviews, indexDoneUniqueId, indexingAccessGroup, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isIndexingRequestText, isLinkUnavailable, isNonRetryableRequestError, isOfficeFile, isPreviewableImagePath, isProviderApiKeyError, isServerExtractable, isServiceDbAttachmentHref, linkUnavailableKeyForHref, linkUnavailableKeyForPath, linkUnavailableKeysForPath, listClaudeModels, listOpenAIModels, looksLikeRtf, makeExtractPlaceholder, mapHistoryListToMessages, markImagePreviewStale, mintCacheBustStamp, needsBomForExt, normalizeAttachmentPathCandidate, normalizeExt, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAiAgentValue, parseAttachmentContent, parseIndexingLabel, parseIndexingRequestText, peekImagePreviewUrl, prepareDownloadText, presignExpiryEpochMs, previewImageContentType, previewLayoutBox, previewMintCacheToken, previewableExtOf, readExpiredAttachmentHref, registerAttachmentParser, registerModelContextWindows, renderInlineLinkHtml, repairUrlEntities, repairUrlWhitespace, resolveImagePreviewUrl, runIndexUniqueId, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, shouldRescueInFlightMessage, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, upsertIndexRunRecordSafe, wallClockNow };
package/dist/engine.mjs CHANGED
@@ -297,11 +297,13 @@ function groupAttachmentFailures(attachments) {
297
297
  // src/engine/prompts/chat_system_prompt.ts
298
298
  function buildChatSystemPrompt(params) {
299
299
  const { projectId, serviceName, serviceDescription, greeting, canUpload, client } = params;
300
+ const g = params.indexAccessGroup;
301
+ const indexGroupLiteral = typeof g === "number" ? String(g) : g === "public" || g === "private" || g === "authorized" || g === "admin" ? `"${g}"` : '"authorized"';
300
302
  let systemPrompt = `
301
303
  You are a dedicated assistant for the project ID: "${projectId}".
302
304
  Scope: Only answer questions about this project and its data. Do not answer questions about other projects or topics unrelated to this project. When the user refers to "my database", "my data", or "my files", treat those as references to this project's database and file storage. The ONE exception is BunnyQuery itself - what this app is, what it can do, and how to use it - which is always in scope: answer it from the "About BunnyQuery" section at the end of this prompt.
303
305
  Knowledge lookup: Before saying you don't know or that something isn't in the chat history, ALWAYS query this project's database through the available MCP tools to look for the answer. The user's data is the source of truth - the chat transcript is not. Only respond with "I don't know" or "I couldn't find that" after you have actually searched the project's data and come back empty.
304
- Complete answers over stored data: The database holds one record per spreadsheet row, and each uploaded file becomes many records. ONE file is routinely SPLIT ACROSS SEVERAL TABLES - a summary row in one table, its page or row content in another, its extracted photos and other media in "__MEDIA__", and the indexer often invents a differently-named table on each pass. An index or tag filter matches inside ONE table only and requires table_name: on getRecords, an index or tag sent with table_name but no access_group is auto-filled with access_group "authorized" (where the indexer writes; pass access_group explicitly, including 0, to search another group), while an index or tag WITHOUT table_name FAILS with an error instead of answering, so read the error rather than guessing. Reference is the exception: reference ALONE spans EVERY table and EVERY access group, so getRecords with reference "src::<the file's storage path>" is the one call that returns a whole file's records wherever the indexer put them. Adding table_name narrows it to that table; access_group WITHOUT table_name fails with '"table" is required'; table_name on its own returns that whole table across all access groups. For anything NOT scoped to a single file, call getTables FIRST, run the query once per table that could hold the answer, and combine the results. For any request that counts, sums, totals, lists every match, compares across records, finds which one, or asks whether something is present or ABSENT (for example "how many", "total spent", "which card", "is there any", "\uC5C6\uC5B4?", "\uD558\uB098\uB3C4 \uC5C6\uB098?"), you MUST read the COMPLETE matching set before answering. Query with fetch_all set to true, or page through getToolResponsePage until pagination.complete is true, across EVERY table and EVERY relevant file. A single default query returns only the first page (about 50 records). That is a SAMPLE. Never treat it as the whole dataset. If you already answered from one table and then realise another table holds more, do not simply apologise: re-run the sweep and give the complete answer.
306
+ Complete answers over stored data: The database holds one record per spreadsheet row, and each uploaded file becomes many records. ONE file is routinely SPLIT ACROSS SEVERAL TABLES - a summary row in one table, its page or row content in another, its extracted photos and other media in "__MEDIA__", and the indexer often invents a differently-named table on each pass. An index or tag filter matches inside ONE table only and requires table_name: on getRecords, an index or tag sent with table_name but no access_group is auto-filled with access_group "authorized", but THIS project indexes at access_group ${indexGroupLiteral}, so pass access_group ${indexGroupLiteral} EXPLICITLY on every index or tag query here - the auto-fill would search a group this project's data is not in and come back empty. Files uploaded before the project's setting changed may sit at another group, so when a scoped query comes back empty, retry it across the other groups (0, 1, "private") before concluding there is nothing, while an index or tag WITHOUT table_name FAILS with an error instead of answering, so read the error rather than guessing. Reference is the exception: reference ALONE spans EVERY table and EVERY access group, so getRecords with reference "src::<the file's storage path>" is the one call that returns a whole file's records wherever the indexer put them. Adding table_name narrows it to that table; access_group WITHOUT table_name fails with '"table" is required'; table_name on its own returns that whole table across all access groups. For anything NOT scoped to a single file, call getTables FIRST, run the query once per table that could hold the answer, and combine the results. For any request that counts, sums, totals, lists every match, compares across records, finds which one, or asks whether something is present or ABSENT (for example "how many", "total spent", "which card", "is there any", "\uC5C6\uC5B4?", "\uD558\uB098\uB3C4 \uC5C6\uB098?"), you MUST read the COMPLETE matching set before answering. Query with fetch_all set to true, or page through getToolResponsePage until pagination.complete is true, across EVERY table and EVERY relevant file. A single default query returns only the first page (about 50 records). That is a SAMPLE. Never treat it as the whole dataset. If you already answered from one table and then realise another table holds more, do not simply apologise: re-run the sweep and give the complete answer.
305
307
  Never assert absence from a partial read. Do not say "there is no X", "none", "not found", or "\uC544\uB2C8\uC694, \uC5C6\uC2B5\uB2C8\uB2E4" until a complete scan has come back empty. If you have not finished scanning every relevant table and file, keep querying instead of guessing. A confident "no" that later turns out wrong is worse than telling the user you are still checking.
306
308
  Embedded values: a search term is often stored inside a larger string. A merchant "BAKSA" appears as "DNH*BAKSA#4070277042", and a card as "5860****5173". Server-side index filters match only exact values, leading prefixes, or trailing suffixes, and tag filters only EXACT whole-tag values - never a partial or interior substring - so filtering on such a field silently drops rows. When the value you are looking for may be embedded, do not trust a narrow filter to be complete. Fetch the full set with fetch_all and match the substring yourself.
307
309
  File attachments: When a user message contains an "Attached files:" section with markdown links, those links point to short-lived signed URLs in this project's db storage and will expire.
@@ -351,6 +353,7 @@ Project description: """${serviceDescription}"""`;
351
353
  // src/engine/prompts/indexing_system_prompt.ts
352
354
  function buildIndexingSystemPrompt(params) {
353
355
  const { projectId, serviceName, serviceDescription } = params;
356
+ const accessGroup = params.accessGroup === "public" || params.accessGroup === "private" ? params.accessGroup : "authorized";
354
357
  let systemPrompt = `You are a background indexing agent for project ${projectId}.
355
358
  - Image files (.jpg, .jpeg, .png, .gif, .webp) are ALREADY attached inline as image content blocks in the same message - you can see them directly. Do NOT call web_fetch on image URLs; that will fail or return garbage. Just look at the image block and answer.
356
359
  - Most files (office documents like .docx/.xlsx/.pptx/.hwp/.hwpx/.ods, and text/data/code files like .csv/.tsv/.json/.xml/.txt/.md and source code) have ALREADY been extracted on the server and included inline in the user message between the "BEGIN FILE CONTENT" / "END FILE CONTENT" markers - read that directly. If the inline content is a "[skapi: ...]" note, the file could not be extracted - index it from its metadata only.
@@ -359,7 +362,8 @@ function buildIndexingSystemPrompt(params) {
359
362
  - VISION: when the message (a readFileContent window, an embedded PDF page, or an inline attachment) includes IMAGES - scanned/rendered PDF pages, or photos embedded in a spreadsheet next to a row/block - LOOK at them and capture what they show as record data (the reading/values in a scanned table, the part/defect/condition visible in a photo). The image IS part of the data; correlate each photo with its labelled block ("PHOTO A3" markers tie a photo to that grid row).
360
363
  - TRANSCRIBE, DO NOT DESCRIBE. When an image contains ANY text - a label, tag, stamp, form field, serial/part number, handwriting - your FIRST job is to read the characters out and store them VERBATIM, not to describe the scene. A record saying "a red inspection tag with handwritten markings" is worthless: it is unsearchable and every such photo produces the same sentence. Put the characters you can actually read into these EXACT fields, not variations of them: "printed_text" (the pre-printed wording), "handwritten_text" (what a person wrote by hand), and, when you can resolve one, "part_no", "tag_id" and "date". Same reason as the fixed table names: a field called photo_text in one pass and visible_text_notes in the next cannot be queried together. Read PARTIAL values rather than skipping: "500.7402.52__" beats nothing. Only when a character is genuinely unreadable, leave that field null or mark the unreadable span - do NOT invent it, and do NOT replace the whole transcription with a description of what the object looks like. A scene description is a nice extra AFTER the text, never instead of it.
361
364
  - IMAGE FILES uploaded as the file itself: if ANY readable character appears ANYWHERE in the image (a label, a stamp, a sign in the background) it counts as an image WITH text - transcribe it per the rule above, and also capture the layout (what appears where) and every entity named. Only a truly text-free image gets description first: a one-line caption, then the objects present with their attributes (type, color, count, condition, position). Either way, save what you extract onto the file's "src::" record with updateRecords, TAG every entity and identifier visible, and INDEX the one number the image offers (a measured value, an amount, a count).
362
- - Whatever the file type, this file's identity is "src::" + its storage path (the "storage path" metadata line) - never the inline content or a temporary URL. That record ALREADY EXISTS: the upload pipeline creates it in table "file_summaries" (access group "authorized") before indexing starts, so posting it again is rejected as a duplicate unique_id. Reference it from every record you write, and add what you learn to it with updateRecords. If that update unexpectedly reports the record does not exist, post it yourself ONCE with that exact "src::" unique_id (table "file_summaries", access group "authorized") and carry on; this is the ONE exception to the do-NOT-post-the-file-record rules elsewhere in these instructions, because the source identity must never be dropped just because an update failed.
365
+ - Whatever the file type, this file's identity is "src::" + its storage path (the "storage path" metadata line) - never the inline content or a temporary URL. That record ALREADY EXISTS: the upload pipeline creates it in table "file_summaries" (access group "${accessGroup}") before indexing starts, so posting it again is rejected as a duplicate unique_id. Reference it from every record you write, and add what you learn to it with updateRecords. If that update unexpectedly reports the record does not exist, post it yourself ONCE with that exact "src::" unique_id (table "file_summaries", access group "${accessGroup}") and carry on; this is the ONE exception to the do-NOT-post-the-file-record rules elsewhere in these instructions, because the source identity must never be dropped just because an update failed.
366
+ - ACCESS GROUP (hard rule): every record you write for this file - the file record, per-row records, chapters, summaries, intermediates - MUST be posted with access group "${accessGroup}". Pass it explicitly on every postRecords call; do not leave it out and do not vary it between passes of the same file. An access group is part of a record's table key, so records saved under a different group than the file are in a different table and will not come back with the rest of it: a "public" file whose rows were saved as "authorized" is one an anonymous visitor can see the name of and none of the contents of, and a re-index cannot find the strays to clean them up. The one exception is the EXTRACTED MEDIA records in "__MEDIA__", which the pipeline creates for you - leave their group alone and only enrich them.
363
367
  - REACHABILITY (hard rule): every record you write while indexing this file MUST be reachable from the file's "src::<storage path>" record by following reference - either reference that record directly, or reference something that already reaches it. A record with no reference, or one pointing outside this file's chain, is an ORPHAN: deleting or re-indexing the file removes the reachable records and leaves the orphan behind forever, where it keeps turning up in later answers as stale data. If you create an intermediate record that OTHER records reference (a page record that rows hang off, a sheet or section record), set source.can_remove_referencing_records to true on it; the delete cascade passes a delete through a record only when that record carries the flag OR a unique_id starting "src::" (the file record cascades because its unique_id starts with "src::"; the intermediates you create carry no "src::" id, so they need the flag), and it cascades ONE LEVEL AT A TIME, so EVERY intermediate record in a chain needs its own marker - an unmarked link stops the cascade there and everything below it survives as orphans. When in doubt, reference the file record directly and keep the chain flat.
364
368
  - TABULAR data (any spreadsheet - .csv/.tsv/.xlsx/.xls/.ods, or sheet-like rows): you MUST save EVERY data row as its own record (ONE record per row) with that row's actual column values in the record's "data", keyed by the header names, in a table named EXACTLY "spreadsheet_rows". Do NOT summarize, sample only a few rows, or save just file metadata - index the whole sheet, window by window, until it ends. Make MULTIPLE postRecords calls in batches (e.g. 30-50 rows per call) rather than one oversized call. This per-row completeness OVERRIDES brevity. The file-level "src::" record ALREADY EXISTS - the upload pipeline creates it before indexing starts - so do NOT create it. Link EVERY per-row record to it via reference (set each row record's reference to exactly "src::" + the storage path, with NO sheet/window/summary suffix added; the row records themselves do NOT carry a src:: unique_id). Enrich that same record with sheet name(s), column headers and total row count via updateRecords rather than posting another one. The per-row records AND this reference linkage are BOTH mandatory: the linkage is what lets the whole sheet be found and cleaned up together when the file is re-indexed. INDEX each row record on the row's most useful NUMERIC column (named by its header) so rows sort and range-query; when the row has no numeric column, index the grid row number instead. TAG each row record with the sheet name, the file name, and the row's categorical values (a status, a category, a type) - tags are how rows are filtered without scanning the table.
365
369
  - ONE RECORD PER GRID ROW, ALWAYS. "Row" means the numbered row of the sheet (R37 is one record), never a visual block, item, section or left/right pair. Sheets that repeat the same columns side by side (an A/B block beside a C/D block, "paired" or "mirrored" layouts) still get ONE record per grid row, holding BOTH sides - suffix the keys to keep them apart (PART_NO_A / PART_NO_B). Collapsing a 16-row window into 2 or 3 "block" records is the single most damaging mistake here: it silently loses most of the cells and makes every later total wrong, because some windows were counted per row and others per block. If a window shows rows R37 to R52, you save records for R37..R52 and the count you report is the number of grid rows you actually wrote.
@@ -380,6 +384,10 @@ Project description: """${serviceDescription}"""`;
380
384
  }
381
385
 
382
386
  // src/engine/prompts/indexing_user_message.ts
387
+ function indexingAccessGroup(attachment) {
388
+ const g = attachment && attachment.accessGroup;
389
+ return g === "public" || g === "private" ? g : "authorized";
390
+ }
383
391
  function buildIndexingUserMessage(attachment, options) {
384
392
  const head = `A new file has just been uploaded. Index it now.
385
393
 
@@ -388,7 +396,11 @@ File metadata:
388
396
  - storage path: ${attachment.storagePath}
389
397
  ` + (attachment.mime ? `- mime type: ${attachment.mime}
390
398
  ` : "") + (typeof attachment.size === "number" ? `- size (bytes): ${attachment.size}
391
- ` : "");
399
+ ` : "") + // Stated in the metadata block as well as the system prompt because this is
400
+ // the per-FILE value: one project can hold public and private files at once,
401
+ // and the system prompt is what is constant across the run.
402
+ `- access group (use this for EVERY record you write for this file): ${indexingAccessGroup(attachment)}
403
+ `;
392
404
  if (options?.inlineContent) {
393
405
  return head + `
394
406
  The file's content was parsed by the client and is provided inline below. Read it directly - do NOT fetch any URL for this file. Set every record's reference to exactly "src::" + the storage path above (not this content). That file record already exists, so enrich it with updateRecords rather than posting it.
@@ -436,7 +448,8 @@ function buildRenderMeta(attachment) {
436
448
  - name: ${attachment.name}
437
449
  - storage path: ${attachment.storagePath}
438
450
  ` + (attachment.mime ? `- mime type: ${attachment.mime}
439
- ` : "");
451
+ ` : "") + `- access group (use this for EVERY record you write for this file): ${indexingAccessGroup(attachment)}
452
+ `;
440
453
  }
441
454
  function buildRenderDatafy(placeholder) {
442
455
  return `
@@ -479,7 +492,8 @@ File metadata:
479
492
  - name: ${attachment.name}
480
493
  - storage path: ${attachment.storagePath}
481
494
  ` + (attachment.mime ? `- mime type: ${attachment.mime}
482
- ` : "") + `
495
+ ` : "") + `- access group (use this for EVERY record you write for this file): ${indexingAccessGroup(attachment)}
496
+
483
497
  Records for the earlier windows/pages of this file are ALREADY saved (they reference "${src}"). First call getRecords with reference "${src}" to see how far the previous pass got (the furthest row/window already saved). The reference ALONE is the whole query: it returns every record written from this file across ALL tables and ALL access groups, so do NOT add table_name or access_group to narrow it. The response is PAGED, so keep fetching pages until it reports there are no more, and take the furthest point from the WHOLE set, never from the first page. Then call readFileContent with the storage path above and a CURSOR that RESUMES just after that point - do NOT start at the beginning. The cursor is derivable from what you already saved:
484
498
  - Spreadsheet: the cursor is "<sheetIndex>:<nextRow>" (0-based sheet index, 1-based row). If you saved up to row R of sheet S, use cursor="S:R+1".
485
499
  - Text: the cursor is the character offset already read.
@@ -1540,6 +1554,11 @@ var MCP_NAME = "BunnyQuery";
1540
1554
  var DEFAULT_CLAUDE_MODEL = "claude-sonnet-5";
1541
1555
  var DEFAULT_OPENAI_MODEL = "gpt-5.6-luna";
1542
1556
  var mcpUrl = () => chatEngineConfig().mcpBaseUrl;
1557
+ function mcpEndpointFor(anonymous, publicProjectId, service) {
1558
+ if (!anonymous) return { url: mcpUrl(), token: "$ACCESS_TOKEN" };
1559
+ const project = publicProjectId || service;
1560
+ return { url: String(mcpUrl()).replace(/\/+$/, "") + "/p/" + project };
1561
+ }
1543
1562
  var clientSecretRequest = (opts) => chatEngineConfig().clientSecretRequest(opts);
1544
1563
  var VARIANT_IMAGE_DETAIL = "original";
1545
1564
  var VARIANT_TEXT_VERBOSITY = "high";
@@ -1765,7 +1784,8 @@ async function callClaudeWithMcp({
1765
1784
  }
1766
1785
  });
1767
1786
  }
1768
- async function callClaudeWithPublicMcp(prompt, service, owner, messages, system, model, userId, extractContent, fileUrls, onResponse, onError) {
1787
+ async function callClaudeWithPublicMcp(prompt, service, owner, messages, system, model, userId, extractContent, fileUrls, onResponse, onError, mcpScope) {
1788
+ const endpoint = mcpEndpointFor(mcpScope?.anonymous, mcpScope?.publicProjectId, service);
1769
1789
  return callClaudeWithMcp({
1770
1790
  prompt,
1771
1791
  messages,
@@ -1779,11 +1799,14 @@ async function callClaudeWithPublicMcp(prompt, service, owner, messages, system,
1779
1799
  fileUrls,
1780
1800
  mcpServer: {
1781
1801
  name: MCP_NAME,
1782
- url: mcpUrl(),
1783
- authorizationToken: "$ACCESS_TOKEN"
1802
+ url: endpoint.url,
1803
+ // Omitted entirely for an anonymous turn; the `if (mcpServer.authorizationToken)`
1804
+ // guard below drops the key rather than sending an empty one.
1805
+ authorizationToken: endpoint.token
1784
1806
  }});
1785
1807
  }
1786
- async function callOpenAIWithPublicMcp(prompt, service, owner, messages, system, model, userId, extractContent, fileUrls, onResponse, onError) {
1808
+ async function callOpenAIWithPublicMcp(prompt, service, owner, messages, system, model, userId, extractContent, fileUrls, onResponse, onError, mcpScope) {
1809
+ const endpoint = mcpEndpointFor(mcpScope?.anonymous, mcpScope?.publicProjectId, service);
1787
1810
  const resolvedModel = model || DEFAULT_OPENAI_MODEL;
1788
1811
  const imageDetail = getOpenAIImageDetail(resolvedModel);
1789
1812
  const messageList = messages && messages.length ? prepareOpenAIMessages(messages, imageDetail) : [
@@ -1826,11 +1849,12 @@ async function callOpenAIWithPublicMcp(prompt, service, owner, messages, system,
1826
1849
  {
1827
1850
  type: "mcp",
1828
1851
  server_label: MCP_NAME,
1829
- server_url: mcpUrl(),
1852
+ server_url: endpoint.url,
1830
1853
  require_approval: "never",
1831
- headers: {
1832
- Authorization: "Bearer $ACCESS_TOKEN"
1833
- }
1854
+ // No `headers` at all for an anonymous turn: `Bearer ` with an
1855
+ // empty token is a credential the MCP server rejects, and the
1856
+ // project-scoped endpoint needs none.
1857
+ ...endpoint.token ? { headers: { Authorization: "Bearer " + endpoint.token } } : {}
1834
1858
  },
1835
1859
  ...[
1836
1860
  {
@@ -1941,7 +1965,10 @@ async function notifyAgentSaveAttachment(info) {
1941
1965
  // by the tools' schema pattern.
1942
1966
  projectId: info.publicProjectId || service,
1943
1967
  serviceName: info.serviceName,
1944
- serviceDescription: info.serviceDescription
1968
+ serviceDescription: info.serviceDescription,
1969
+ // Per-FILE, not per-project: one project holds public and private files at
1970
+ // once, so this travels on the attachment rather than the identity.
1971
+ accessGroup: attachment.accessGroup
1945
1972
  });
1946
1973
  if (platform === "openai") {
1947
1974
  const resolvedModel2 = info.model || DEFAULT_OPENAI_MODEL;
@@ -3523,10 +3550,23 @@ var ChatSession = class {
3523
3550
  this._lidSeq += 1;
3524
3551
  return "lid_" + this._lidSeq;
3525
3552
  }
3553
+ /**
3554
+ * The key every per-chat cache hangs off: the restored message cache, the
3555
+ * hydrated-body memo, the live-index key and the per-file storage-path key.
3556
+ *
3557
+ * It carries the IDENTITY as well as the project and platform. A single
3558
+ * browser can hold more than one conversation on one project without a
3559
+ * reload — an anonymous visitor who signs in, or a dashboard user who logs
3560
+ * out and back in as someone else — and with an identity-free key the
3561
+ * previous conversation stayed in the cache and was re-rendered, and written
3562
+ * back, as the new one's. `userId` is the same value the request queue is
3563
+ * named after, so two identities that share a queue share a cache, which is
3564
+ * exactly right.
3565
+ */
3526
3566
  getHistoryCacheKey() {
3527
3567
  var id = this.host.getIdentity();
3528
3568
  if (!id.projectId || id.platform === "none") return "";
3529
- return id.projectId + "#" + id.platform;
3569
+ return id.projectId + "#" + id.platform + "#" + (id.userId || "");
3530
3570
  }
3531
3571
  /** Re-apply memoized hydrated texts onto freshly-mapped messages. Both
3532
3572
  * clients call this right after their mapper runs (loadHistory does it
@@ -3729,7 +3769,9 @@ var ChatSession = class {
3729
3769
  if (projectId === void 0) projectId = id.projectId;
3730
3770
  if (owner === void 0) owner = id.owner;
3731
3771
  }
3732
- return platform === "openai" ? callOpenAIWithPublicMcp(prompt, projectId, owner, messages, system, model, userId, extractContent, fileUrls) : callClaudeWithPublicMcp(prompt, projectId, owner, messages, system, model, userId, extractContent, fileUrls);
3772
+ var liveId = this.host.getIdentity();
3773
+ var mcpScope = { anonymous: liveId.anonymous, publicProjectId: liveId.publicProjectId };
3774
+ return platform === "openai" ? callOpenAIWithPublicMcp(prompt, projectId, owner, messages, system, model, userId, extractContent, fileUrls, void 0, void 0, mcpScope) : callClaudeWithPublicMcp(prompt, projectId, owner, messages, system, model, userId, extractContent, fileUrls, void 0, void 0, mcpScope);
3733
3775
  }
3734
3776
  dispatchAgentRequest(params) {
3735
3777
  var self = this;
@@ -6117,6 +6159,15 @@ var ChatSession = class {
6117
6159
  })).catch(function() {
6118
6160
  });
6119
6161
  });
6162
+ var accessGroup;
6163
+ preIndex = preIndex.then(function() {
6164
+ if (alreadyIndexing) return;
6165
+ if (typeof self.host.uploadAccessGroup !== "function") return;
6166
+ return Promise.resolve(self.host.uploadAccessGroup(member.storagePath)).then(function(g) {
6167
+ accessGroup = g || void 0;
6168
+ }).catch(function() {
6169
+ });
6170
+ });
6120
6171
  return preIndex.then(function() {
6121
6172
  return parseAttachmentContent(member.file, member.file.name, mime || void 0);
6122
6173
  }).then(function(parsedContent) {
@@ -6135,7 +6186,8 @@ var ChatSession = class {
6135
6186
  storagePath: member.storagePath,
6136
6187
  mime: mime || void 0,
6137
6188
  size: member.file.size,
6138
- url
6189
+ url,
6190
+ accessGroup
6139
6191
  },
6140
6192
  parsedContent: parsedContent || void 0
6141
6193
  }).then(function(ack) {
@@ -6662,6 +6714,6 @@ function buildChatDisplayList(messages, opts) {
6662
6714
  return out;
6663
6715
  }
6664
6716
 
6665
- export { BG_INDEXING_QUEUE_SUFFIX, BOM, BOM_EXTS, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, ChatSession, DEFAULT_CLAUDE_MODEL, DEFAULT_CONTEXT_WINDOW, DEFAULT_OPENAI_MODEL, EMPTY_INDEXING_REPLY, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, EXT_CONTENT_TYPES, HISTORY_BUDGET_RATIO, HISTORY_FILL_SLACK_PX, HISTORY_TOKEN_BUDGET, HTML_EXTS, HTML_HEAD_WINDOW, IMAGE_PREVIEWS_PER_MESSAGE, INDEXING_COMPLETE_MARKER, INLINE_LINK_GLYPH, INLINE_LINK_UNAVAILABLE_GLYPH, INLINE_LINK_UNAVAILABLE_SUFFIX, INPUT_CAP_RATIO, LINK_LABEL_MAX_DISPLAY_CHARS, LINK_REFRESH_WINDOW_MS, MAX_CONCURRENT_BG_POLLS, MAX_HISTORY_FILL_PAGES, MAX_HISTORY_MESSAGES, MAX_OUTPUT_BY_MODEL, MAX_OUTPUT_TOKENS, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MINT_CACHE_GENERATION, MIN_INPUT_TOKEN_BUDGET, MIN_PER_REQUEST_INPUT_CAP, OUTPUT_TOKEN_RESERVE, POLL_INTERVAL, PRESIGN_SAFETY_MARGIN_MS, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, PREVIEW_LAYOUT_BOX_SELECTOR, PREVIEW_URL_EXPIRES_SECONDS, RENDER_FROM_TOKEN, RTF_EXTS, RUN_RECORD_WORKING_STALE_MS, TOOL_AND_RESPONSE_BUFFER, XML_EXTS, __resetSplitHistoryState, applyEncodingDeclaration, bgIndexingQueueName, buildAiAgentValue, buildBoundedChatMessages, buildChatDisplayList, buildChatGreeting, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildHistoryItemFullId, buildIndexingContinueMessage, buildIndexingRenderContinueTemplate, buildIndexingRenderMessage, buildIndexingSystemPrompt, buildIndexingUserMessage, buildIndexingWindowMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, canonicalizePathForm, chatEngineConfig, classifyInlineLink, clearAttachmentParsers, clearImagePreviewCache, composeUserMessage, configureChatEngine, contentTypeForExt, createHistoryFiller, createInlineLinkRegex, createScrollAnchor, encodePathSegments, encodingClassForExt, ensureHtmlCharset, ensureXmlEncoding, escapeInlineHtml, escapeRtfNonAscii, estimateMessageTokens, estimateTextTokens, extOf, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, fetchLiveIndexingKeys, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, formatChatTimestamp, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, getInputTokenBudget, getMaxOutputTokens, getModelContextWindow, getProjectContextWindow, getSplitChatHistory, getVisionProfile, groupAttachmentFailures, hasBom, hydrateImagePreviews, indexDoneUniqueId, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isIndexingRequestText, isLinkUnavailable, isNonRetryableRequestError, isOfficeFile, isPreviewableImagePath, isProviderApiKeyError, isServerExtractable, isServiceDbAttachmentHref, linkUnavailableKeyForHref, linkUnavailableKeyForPath, linkUnavailableKeysForPath, listClaudeModels, listOpenAIModels, looksLikeRtf, makeExtractPlaceholder, mapHistoryListToMessages, markImagePreviewStale, mintCacheBustStamp, needsBomForExt, normalizeAttachmentPathCandidate, normalizeExt, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAiAgentValue, parseAttachmentContent, parseIndexingLabel, parseIndexingRequestText, peekImagePreviewUrl, prepareDownloadText, presignExpiryEpochMs, previewImageContentType, previewLayoutBox, previewMintCacheToken, previewableExtOf, readExpiredAttachmentHref, registerAttachmentParser, registerModelContextWindows, renderInlineLinkHtml, repairUrlEntities, repairUrlWhitespace, resolveImagePreviewUrl, runIndexUniqueId, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, shouldRescueInFlightMessage, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, upsertIndexRunRecordSafe, wallClockNow };
6717
+ export { BG_INDEXING_QUEUE_SUFFIX, BOM, BOM_EXTS, CLAUDE_INPUT_CAP_RATIO, CLAUDE_PER_REQUEST_INPUT_CAP, CONTEXT_WINDOW_BY_MODEL, CONTEXT_WINDOW_DEFAULT, ChatSession, DEFAULT_CLAUDE_MODEL, DEFAULT_CONTEXT_WINDOW, DEFAULT_OPENAI_MODEL, EMPTY_INDEXING_REPLY, EXPIRED_ATTACHMENT_URL_HOST, EXPIRED_ATTACHMENT_URL_ORIGIN, EXPIRED_LINK_REFRESH_EXPIRES_SECONDS, EXT_CONTENT_TYPES, HISTORY_BUDGET_RATIO, HISTORY_FILL_SLACK_PX, HISTORY_TOKEN_BUDGET, HTML_EXTS, HTML_HEAD_WINDOW, IMAGE_PREVIEWS_PER_MESSAGE, INDEXING_COMPLETE_MARKER, INLINE_LINK_GLYPH, INLINE_LINK_UNAVAILABLE_GLYPH, INLINE_LINK_UNAVAILABLE_SUFFIX, INPUT_CAP_RATIO, LINK_LABEL_MAX_DISPLAY_CHARS, LINK_REFRESH_WINDOW_MS, MAX_CONCURRENT_BG_POLLS, MAX_HISTORY_FILL_PAGES, MAX_HISTORY_MESSAGES, MAX_OUTPUT_BY_MODEL, MAX_OUTPUT_TOKENS, MAX_PARSED_CONTENT_CHARS, MCP_NAME, MINT_CACHE_GENERATION, MIN_INPUT_TOKEN_BUDGET, MIN_PER_REQUEST_INPUT_CAP, OUTPUT_TOKEN_RESERVE, POLL_INTERVAL, PRESIGN_SAFETY_MARGIN_MS, PREVIEWABLE_IMAGE_CONTENT_TYPES, PREVIEW_BROWSER_CACHE_SECONDS, PREVIEW_LAYOUT_BOX_SELECTOR, PREVIEW_URL_EXPIRES_SECONDS, RENDER_FROM_TOKEN, RTF_EXTS, RUN_RECORD_WORKING_STALE_MS, TOOL_AND_RESPONSE_BUFFER, XML_EXTS, __resetSplitHistoryState, applyEncodingDeclaration, bgIndexingQueueName, buildAiAgentValue, buildBoundedChatMessages, buildChatDisplayList, buildChatGreeting, buildChatSystemPrompt, buildDisplayExpiredAttachmentHref, buildHistoryItemFullId, buildIndexingContinueMessage, buildIndexingRenderContinueTemplate, buildIndexingRenderMessage, buildIndexingSystemPrompt, buildIndexingUserMessage, buildIndexingWindowMessage, callClaudeWithMcp, callClaudeWithPublicMcp, callOpenAIWithPublicMcp, canonicalizePathForm, chatEngineConfig, classifyInlineLink, clearAttachmentParsers, clearImagePreviewCache, composeUserMessage, configureChatEngine, contentTypeForExt, createHistoryFiller, createInlineLinkRegex, createScrollAnchor, encodePathSegments, encodingClassForExt, ensureHtmlCharset, ensureXmlEncoding, escapeInlineHtml, escapeRtfNonAscii, estimateMessageTokens, estimateTextTokens, extOf, extractClaudeText, extractLastUserTextFromRequest, extractOpenAIText, extractRemotePathFromAttachmentHref, fetchLiveIndexingKeys, fillHistoryViewport, filterListByClearHorizon, findAttachmentParser, formatChatTimestamp, getAttachmentParsers, getChatHistory, getContextWindow, getErrorMessage, getExpiredAttachmentVisiblePath, getInputTokenBudget, getMaxOutputTokens, getModelContextWindow, getProjectContextWindow, getSplitChatHistory, getVisionProfile, groupAttachmentFailures, hasBom, hydrateImagePreviews, indexDoneUniqueId, indexingAccessGroup, isAuthExpiredError, isBgIndexingQueue, isErrorResponseBody, isHttpUrlLike, isIndexingRequestText, isLinkUnavailable, isNonRetryableRequestError, isOfficeFile, isPreviewableImagePath, isProviderApiKeyError, isServerExtractable, isServiceDbAttachmentHref, linkUnavailableKeyForHref, linkUnavailableKeyForPath, linkUnavailableKeysForPath, listClaudeModels, listOpenAIModels, looksLikeRtf, makeExtractPlaceholder, mapHistoryListToMessages, markImagePreviewStale, mintCacheBustStamp, needsBomForExt, normalizeAttachmentPathCandidate, normalizeExt, normalizeTextContent, normalizeTrailingInlineToken, notifyAgentSaveAttachment, parseAiAgentValue, parseAttachmentContent, parseIndexingLabel, parseIndexingRequestText, peekImagePreviewUrl, prepareDownloadText, presignExpiryEpochMs, previewImageContentType, previewLayoutBox, previewMintCacheToken, previewableExtOf, readExpiredAttachmentHref, registerAttachmentParser, registerModelContextWindows, renderInlineLinkHtml, repairUrlEntities, repairUrlWhitespace, resolveImagePreviewUrl, runIndexUniqueId, safeDecodeURIComponent, sanitizeAttachmentLinksForHistory, setProjectContextWindow, shouldRescueInFlightMessage, stripFileBlocksFromHistory, transformContentWithImages, transformContentWithOpenAIImages, truncateLabelForDisplay, upsertIndexRunRecordSafe, wallClockNow };
6666
6718
  //# sourceMappingURL=engine.mjs.map
6667
6719
  //# sourceMappingURL=engine.mjs.map