@btst/stack 4.1.1 → 4.2.0

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.
Files changed (100) hide show
  1. package/README.md +2 -2
  2. package/dist/packages/stack/src/plugins/ai-chat/api/operations.cjs +15 -9
  3. package/dist/packages/stack/src/plugins/ai-chat/api/operations.mjs +16 -10
  4. package/dist/packages/stack/src/plugins/ai-chat/api/page-tools.cjs +28 -0
  5. package/dist/packages/stack/src/plugins/ai-chat/api/page-tools.mjs +29 -2
  6. package/dist/packages/stack/src/plugins/ai-chat/api/plugin.cjs +5 -0
  7. package/dist/packages/stack/src/plugins/ai-chat/api/plugin.mjs +5 -0
  8. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.cjs +91 -8
  9. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.mjs +91 -8
  10. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.cjs +76 -3
  11. package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-layout.mjs +80 -7
  12. package/dist/packages/stack/src/plugins/ai-chat/client/components/tool-call-display.cjs +17 -0
  13. package/dist/packages/stack/src/plugins/ai-chat/client/components/tool-call-display.mjs +18 -1
  14. package/dist/packages/stack/src/plugins/ai-chat/client/context/chat-analytics.cjs +42 -0
  15. package/dist/packages/stack/src/plugins/ai-chat/client/context/chat-analytics.mjs +39 -0
  16. package/dist/packages/stack/src/plugins/ai-chat/client/plugin.cjs +2 -1
  17. package/dist/packages/stack/src/plugins/ai-chat/client/plugin.mjs +2 -1
  18. package/dist/plugins/ai-chat/api/index.d.cts +2 -2
  19. package/dist/plugins/ai-chat/api/index.d.mts +2 -2
  20. package/dist/plugins/ai-chat/api/index.d.ts +2 -2
  21. package/dist/plugins/ai-chat/client/components/index.d.cts +2 -1
  22. package/dist/plugins/ai-chat/client/components/index.d.mts +2 -1
  23. package/dist/plugins/ai-chat/client/components/index.d.ts +2 -1
  24. package/dist/plugins/ai-chat/client/context/page-ai-context.cjs +4 -0
  25. package/dist/plugins/ai-chat/client/context/page-ai-context.d.cts +6 -2
  26. package/dist/plugins/ai-chat/client/context/page-ai-context.d.mts +6 -2
  27. package/dist/plugins/ai-chat/client/context/page-ai-context.d.ts +6 -2
  28. package/dist/plugins/ai-chat/client/context/page-ai-context.mjs +4 -1
  29. package/dist/plugins/ai-chat/client/hooks/index.d.cts +2 -2
  30. package/dist/plugins/ai-chat/client/hooks/index.d.mts +2 -2
  31. package/dist/plugins/ai-chat/client/hooks/index.d.ts +2 -2
  32. package/dist/plugins/ai-chat/client/index.d.cts +7 -3
  33. package/dist/plugins/ai-chat/client/index.d.mts +7 -3
  34. package/dist/plugins/ai-chat/client/index.d.ts +7 -3
  35. package/dist/plugins/ai-chat/query-keys.d.cts +1 -1
  36. package/dist/plugins/ai-chat/query-keys.d.mts +1 -1
  37. package/dist/plugins/ai-chat/query-keys.d.ts +1 -1
  38. package/dist/plugins/blog/api/index.d.cts +1 -1
  39. package/dist/plugins/blog/api/index.d.mts +1 -1
  40. package/dist/plugins/blog/api/index.d.ts +1 -1
  41. package/dist/plugins/blog/query-keys.d.cts +1 -1
  42. package/dist/plugins/blog/query-keys.d.mts +1 -1
  43. package/dist/plugins/blog/query-keys.d.ts +1 -1
  44. package/dist/plugins/cms/api/index.d.cts +1 -1
  45. package/dist/plugins/cms/api/index.d.mts +1 -1
  46. package/dist/plugins/cms/api/index.d.ts +1 -1
  47. package/dist/plugins/cms/query-keys.d.cts +1 -1
  48. package/dist/plugins/cms/query-keys.d.mts +1 -1
  49. package/dist/plugins/cms/query-keys.d.ts +1 -1
  50. package/dist/plugins/form-builder/api/index.d.cts +1 -1
  51. package/dist/plugins/form-builder/api/index.d.mts +1 -1
  52. package/dist/plugins/form-builder/api/index.d.ts +1 -1
  53. package/dist/plugins/form-builder/client/hooks/index.d.cts +1 -1
  54. package/dist/plugins/form-builder/client/hooks/index.d.mts +1 -1
  55. package/dist/plugins/form-builder/client/hooks/index.d.ts +1 -1
  56. package/dist/plugins/form-builder/query-keys.d.cts +1 -1
  57. package/dist/plugins/form-builder/query-keys.d.mts +1 -1
  58. package/dist/plugins/form-builder/query-keys.d.ts +1 -1
  59. package/dist/plugins/ui-builder/client/hooks/index.d.cts +1 -1
  60. package/dist/plugins/ui-builder/client/hooks/index.d.mts +1 -1
  61. package/dist/plugins/ui-builder/client/hooks/index.d.ts +1 -1
  62. package/dist/plugins/ui-builder/client/index.d.cts +1 -1
  63. package/dist/plugins/ui-builder/client/index.d.mts +1 -1
  64. package/dist/plugins/ui-builder/client/index.d.ts +1 -1
  65. package/dist/plugins/ui-builder/query-keys.d.cts +1 -1
  66. package/dist/plugins/ui-builder/query-keys.d.mts +1 -1
  67. package/dist/plugins/ui-builder/query-keys.d.ts +1 -1
  68. package/dist/shared/{stack.B5yPgHsp.d.mts → stack.B7Mj74BQ.d.mts} +18 -2
  69. package/dist/shared/{stack.rVF0f509.d.mts → stack.C-eQN_Jt.d.ts} +60 -4
  70. package/dist/shared/{stack.rVF0f509.d.ts → stack.C5FZr-jJ.d.mts} +60 -4
  71. package/dist/shared/{stack.3Sr6qRtT.d.ts → stack.DBH1Bjko.d.ts} +18 -2
  72. package/dist/shared/{stack.C4oVW2qK.d.cts → stack.Dgt3MCtW.d.cts} +18 -2
  73. package/dist/shared/{stack.rVF0f509.d.cts → stack.xAiy2VtO.d.cts} +60 -4
  74. package/package.json +1 -1
  75. package/src/plugins/ai-chat/__tests__/authorization.test.ts +106 -1
  76. package/src/plugins/ai-chat/__tests__/classnames-overrides.test.tsx +4 -1
  77. package/src/plugins/ai-chat/__tests__/page-content.test.ts +177 -0
  78. package/src/plugins/ai-chat/__tests__/page-widget.test.tsx +573 -0
  79. package/src/plugins/ai-chat/api/operations.ts +29 -13
  80. package/src/plugins/ai-chat/api/page-tools.ts +56 -2
  81. package/src/plugins/ai-chat/api/plugin.ts +13 -0
  82. package/src/plugins/ai-chat/client/analytics.ts +35 -0
  83. package/src/plugins/ai-chat/client/components/chat-interface.tsx +113 -10
  84. package/src/plugins/ai-chat/client/components/chat-layout.tsx +118 -7
  85. package/src/plugins/ai-chat/client/components/tool-call-display.tsx +29 -1
  86. package/src/plugins/ai-chat/client/context/chat-analytics.tsx +46 -0
  87. package/src/plugins/ai-chat/client/context/page-ai-context.tsx +9 -1
  88. package/src/plugins/ai-chat/client/index.ts +2 -0
  89. package/src/plugins/ai-chat/client/localization/index.ts +8 -0
  90. package/src/plugins/ai-chat/client/overrides.ts +5 -0
  91. package/src/plugins/ai-chat/client/plugin.tsx +3 -0
  92. package/dist/shared/{stack.lsbdKRj7.d.mts → stack.B8PG21zX.d.mts} +16 -16
  93. package/dist/shared/{stack.BuxUQqL8.d.ts → stack.CGmh-Lpe.d.ts} +12 -12
  94. package/dist/shared/{stack.BggTHKlo.d.mts → stack.CTneIW0u.d.mts} +2 -2
  95. package/dist/shared/{stack.CtrnoQgH.d.cts → stack.Ce6V7eDj.d.cts} +2 -2
  96. package/dist/shared/{stack.BMlTZnZp.d.ts → stack.CyalJ0j6.d.ts} +16 -16
  97. package/dist/shared/{stack.lPKwd_v0.d.ts → stack.D5b9l24t.d.ts} +2 -2
  98. package/dist/shared/{stack.BV0uNV4f.d.cts → stack.D6Pg-ufX.d.cts} +16 -16
  99. package/dist/shared/{stack.Egr3RWhA.d.mts → stack.Doy2_wgl.d.mts} +12 -12
  100. package/dist/shared/{stack.CX83YNuS.d.cts → stack.zch70Loj.d.cts} +12 -12
@@ -8,7 +8,7 @@ import '@btst/stack/context';
8
8
  import '../../permissions.js';
9
9
  import 'zod';
10
10
  import '@btst/stack/plugins/client';
11
- import '../../../../shared/stack.BuxUQqL8.js';
11
+ import '../../../../shared/stack.CGmh-Lpe.js';
12
12
  import '@btst/db';
13
13
  import 'zod/v4/core';
14
14
  import 'better-call';
@@ -1,6 +1,6 @@
1
1
  import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
2
2
  import { createApiClient } from '@btst/stack/plugins/client';
3
- import { F as FormsListDiscriminator, S as SubmissionsListDiscriminator, a as FormBuilderApiRouter } from '../../shared/stack.CX83YNuS.cjs';
3
+ import { F as FormsListDiscriminator, S as SubmissionsListDiscriminator, a as FormBuilderApiRouter } from '../../shared/stack.zch70Loj.cjs';
4
4
  import { P as PaginatedForms, S as SerializedForm, a as SerializedFormSubmission, e as PaginatedFormSubmissions, d as SerializedFormSubmissionWithData } from '../../shared/stack.v2O65JqF.cjs';
5
5
  import { StackIdentity } from '@btst/stack/context';
6
6
  import '@btst/stack/plugins/api';
@@ -1,6 +1,6 @@
1
1
  import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
2
2
  import { createApiClient } from '@btst/stack/plugins/client';
3
- import { F as FormsListDiscriminator, S as SubmissionsListDiscriminator, a as FormBuilderApiRouter } from '../../shared/stack.Egr3RWhA.mjs';
3
+ import { F as FormsListDiscriminator, S as SubmissionsListDiscriminator, a as FormBuilderApiRouter } from '../../shared/stack.Doy2_wgl.mjs';
4
4
  import { P as PaginatedForms, S as SerializedForm, a as SerializedFormSubmission, e as PaginatedFormSubmissions, d as SerializedFormSubmissionWithData } from '../../shared/stack.OYT8WU4n.mjs';
5
5
  import { StackIdentity } from '@btst/stack/context';
6
6
  import '@btst/stack/plugins/api';
@@ -1,6 +1,6 @@
1
1
  import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
2
2
  import { createApiClient } from '@btst/stack/plugins/client';
3
- import { F as FormsListDiscriminator, S as SubmissionsListDiscriminator, a as FormBuilderApiRouter } from '../../shared/stack.BuxUQqL8.js';
3
+ import { F as FormsListDiscriminator, S as SubmissionsListDiscriminator, a as FormBuilderApiRouter } from '../../shared/stack.CGmh-Lpe.js';
4
4
  import { P as PaginatedForms, S as SerializedForm, a as SerializedFormSubmission, e as PaginatedFormSubmissions, d as SerializedFormSubmissionWithData } from '../../shared/stack.C3bU9w6L.js';
5
5
  import { StackIdentity } from '@btst/stack/context';
6
6
  import '@btst/stack/plugins/api';
@@ -9,7 +9,7 @@ import '../../../../shared/stack.fdi94T4S.cjs';
9
9
  import 'react-hook-form';
10
10
  import 'react/jsx-runtime';
11
11
  import '@btst/stack/plugins/client';
12
- import '../../../../shared/stack.BV0uNV4f.cjs';
12
+ import '../../../../shared/stack.D6Pg-ufX.cjs';
13
13
  import '@btst/stack/plugins/api';
14
14
  import '../../../../authorization/index.cjs';
15
15
  import 'better-call';
@@ -9,7 +9,7 @@ import '../../../../shared/stack.fdi94T4S.mjs';
9
9
  import 'react-hook-form';
10
10
  import 'react/jsx-runtime';
11
11
  import '@btst/stack/plugins/client';
12
- import '../../../../shared/stack.lsbdKRj7.mjs';
12
+ import '../../../../shared/stack.B8PG21zX.mjs';
13
13
  import '@btst/stack/plugins/api';
14
14
  import '../../../../authorization/index.mjs';
15
15
  import 'better-call';
@@ -9,7 +9,7 @@ import '../../../../shared/stack.fdi94T4S.js';
9
9
  import 'react-hook-form';
10
10
  import 'react/jsx-runtime';
11
11
  import '@btst/stack/plugins/client';
12
- import '../../../../shared/stack.BMlTZnZp.js';
12
+ import '../../../../shared/stack.CyalJ0j6.js';
13
13
  import '@btst/stack/plugins/api';
14
14
  import '../../../../authorization/index.js';
15
15
  import 'better-call';
@@ -15,7 +15,7 @@ import '../../../shared/stack.B3_aDcYL.cjs';
15
15
  import 'react-hook-form';
16
16
  import '@tanstack/react-query';
17
17
  import '@btst/stack/plugins/client/hooks';
18
- import '../../../shared/stack.BV0uNV4f.cjs';
18
+ import '../../../shared/stack.D6Pg-ufX.cjs';
19
19
  import '@btst/stack/plugins/api';
20
20
  import '../../../authorization/index.cjs';
21
21
  import 'better-call';
@@ -15,7 +15,7 @@ import '../../../shared/stack.B3_aDcYL.mjs';
15
15
  import 'react-hook-form';
16
16
  import '@tanstack/react-query';
17
17
  import '@btst/stack/plugins/client/hooks';
18
- import '../../../shared/stack.lsbdKRj7.mjs';
18
+ import '../../../shared/stack.B8PG21zX.mjs';
19
19
  import '@btst/stack/plugins/api';
20
20
  import '../../../authorization/index.mjs';
21
21
  import 'better-call';
@@ -15,7 +15,7 @@ import '../../../shared/stack.B3_aDcYL.js';
15
15
  import 'react-hook-form';
16
16
  import '@tanstack/react-query';
17
17
  import '@btst/stack/plugins/client/hooks';
18
- import '../../../shared/stack.BMlTZnZp.js';
18
+ import '../../../shared/stack.CyalJ0j6.js';
19
19
  import '@btst/stack/plugins/api';
20
20
  import '../../../authorization/index.js';
21
21
  import 'better-call';
@@ -1,6 +1,6 @@
1
1
  import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
2
2
  import { createApiClient } from '@btst/stack/plugins/client';
3
- import { a as ContentListDiscriminator, C as CMSApiRouter } from '../../shared/stack.BV0uNV4f.cjs';
3
+ import { a as ContentListDiscriminator, C as CMSApiRouter } from '../../shared/stack.D6Pg-ufX.cjs';
4
4
  import { C as ComponentLayer, V as Variable } from '../../shared/stack.DmpPDPxA.cjs';
5
5
  import { P as PaginatedContentItems, a as SerializedContentItemWithType } from '../../shared/stack.B3_aDcYL.cjs';
6
6
  import { UIBuilderPageSchemaType, PaginatedUIBuilderPages, SerializedUIBuilderPage } from './index.cjs';
@@ -1,6 +1,6 @@
1
1
  import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
2
2
  import { createApiClient } from '@btst/stack/plugins/client';
3
- import { a as ContentListDiscriminator, C as CMSApiRouter } from '../../shared/stack.lsbdKRj7.mjs';
3
+ import { a as ContentListDiscriminator, C as CMSApiRouter } from '../../shared/stack.B8PG21zX.mjs';
4
4
  import { C as ComponentLayer, V as Variable } from '../../shared/stack.B1srlBud.mjs';
5
5
  import { P as PaginatedContentItems, a as SerializedContentItemWithType } from '../../shared/stack.B3_aDcYL.mjs';
6
6
  import { UIBuilderPageSchemaType, PaginatedUIBuilderPages, SerializedUIBuilderPage } from './index.mjs';
@@ -1,6 +1,6 @@
1
1
  import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
2
2
  import { createApiClient } from '@btst/stack/plugins/client';
3
- import { a as ContentListDiscriminator, C as CMSApiRouter } from '../../shared/stack.BMlTZnZp.js';
3
+ import { a as ContentListDiscriminator, C as CMSApiRouter } from '../../shared/stack.CyalJ0j6.js';
4
4
  import { C as ComponentLayer, V as Variable } from '../../shared/stack.BK9Z2dcL.js';
5
5
  import { P as PaginatedContentItems, a as SerializedContentItemWithType } from '../../shared/stack.B3_aDcYL.js';
6
6
  import { UIBuilderPageSchemaType, PaginatedUIBuilderPages, SerializedUIBuilderPage } from './index.js';
@@ -41,6 +41,20 @@ type SerializedMessage = Omit<Message, "createdAt"> & {
41
41
  createdAt: string;
42
42
  };
43
43
 
44
+ /** Server-owned content loader for the standard readPage tool. */
45
+ interface AiChatPageContentConfig {
46
+ /** Allowed page paths. Validated before resolve; paths are also limited to 16,000 characters. */
47
+ pathSchema: z.ZodType<string, string>;
48
+ /**
49
+ * Load the complete page as JSON-serializable reference material. Enforce
50
+ * publication and access rules here using the request context. Return null
51
+ * when unavailable. BTST never fetches URLs or truncates the returned content.
52
+ */
53
+ resolve: (path: string, context: ChatApiContext<ChatOperationInput>) => unknown | Promise<unknown>;
54
+ /** Optional model-facing description of the supported page types. */
55
+ description?: string;
56
+ }
57
+
44
58
  type AiChatAccess = "authorized" | "public";
45
59
  /** Runtime input for one conversation operation. */
46
60
  declare const ConversationOperationInputSchema: z.ZodObject<{
@@ -208,6 +222,8 @@ interface AiChatBackendConfig {
208
222
  access?: AiChatAccess;
209
223
  systemPrompt?: string;
210
224
  tools?: Record<string, Tool>;
225
+ /** Enable the standard readPage server tool for full page content. Reserves the readPage tool name. */
226
+ pageContent?: AiChatPageContentConfig;
211
227
  enablePageTools?: boolean;
212
228
  clientToolSchemas?: Record<string, Tool>;
213
229
  hooks?: AiChatBackendHooks;
@@ -503,5 +519,5 @@ declare function createAiChatQueryKeys(client: ReturnType<typeof createApiClient
503
519
  }>;
504
520
  type AiChatQueryKeys = ReturnType<typeof createAiChatQueryKeys>;
505
521
 
506
- export { UpdateConversationOperationInputSchema as U, aiChatIdentityKey as d, aiChatResources as e, createAiChatQueryKeys as f, aiChatBackendPlugin as i, AI_CHAT_OPERATION_INVENTORY as n, AI_CHAT_RAW_ESCAPE_HATCH_INVENTORY as o, AiChatOperationError as p, ChatOperationInputSchema as q, ConversationOperationInputSchema as r };
507
- export type { AiChatIdentityPartition as A, ConversationWithMessages as C, Message as M, RenameConversationInput as R, SerializedConversation as S, SerializedMessage as a, CreateConversationInput as b, Conversation as c, AiChatQueryKeys as g, AiChatBackendConfig as h, AiChatApiRouter as j, AiChatAccess as k, AiChatBackendHooks as l, ChatApiContext as m };
522
+ export { UpdateConversationOperationInputSchema as U, aiChatIdentityKey as d, aiChatResources as e, createAiChatQueryKeys as f, aiChatBackendPlugin as i, AI_CHAT_OPERATION_INVENTORY as o, AI_CHAT_RAW_ESCAPE_HATCH_INVENTORY as p, AiChatOperationError as q, ChatOperationInputSchema as r, ConversationOperationInputSchema as s };
523
+ export type { AiChatIdentityPartition as A, ConversationWithMessages as C, Message as M, RenameConversationInput as R, SerializedConversation as S, SerializedMessage as a, CreateConversationInput as b, Conversation as c, AiChatQueryKeys as g, AiChatBackendConfig as h, AiChatApiRouter as j, AiChatPageContentConfig as k, AiChatAccess as l, AiChatBackendHooks as m, ChatApiContext as n };
@@ -1,6 +1,39 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { UIMessage } from 'ai';
3
- import { ComponentType, FormEvent } from 'react';
3
+ import { ComponentType, CSSProperties, FormEvent } from 'react';
4
+ import { PageAIContextConfig } from '../plugins/ai-chat/client/context/page-ai-context.js';
5
+
6
+ /** Content-free usage events. No prompts, answers, page descriptions, or tool outputs. */
7
+ type AiChatAnalyticsEvent = {
8
+ /** Caller-supplied page identity; omit sensitive values. */
9
+ pageKey?: string;
10
+ /** Page label, without its description or document content. */
11
+ routeName?: string;
12
+ /** Registered client mode, not an assertion about the viewer's identity. */
13
+ mode: AiChatMode;
14
+ } & ({
15
+ type: "widget_opened" | "widget_closed" | "intro_tip_shown" | "conversation_cleared";
16
+ } | {
17
+ type: "intro_tip_dismissed";
18
+ reason: "timeout" | "manual" | "open";
19
+ } | {
20
+ type: "suggestion_selected";
21
+ index: number;
22
+ } | {
23
+ type: "message_submitted";
24
+ inputKind: "typed" | "suggestion" | "edit" | "retry";
25
+ inputLength: number;
26
+ messageCount: number;
27
+ attachmentCount: number;
28
+ } | {
29
+ type: "response_completed";
30
+ durationMs: number;
31
+ outputLength: number;
32
+ toolCallCount: number;
33
+ } | {
34
+ type: "response_failed";
35
+ durationMs: number;
36
+ });
4
37
 
5
38
  /**
6
39
  * AI Chat plugin localization strings
@@ -69,6 +102,10 @@ interface AiChatLocalization {
69
102
  A11Y_CLEAR_CHAT: string;
70
103
  A11Y_CLOSE_CHAT: string;
71
104
  A11Y_OPEN_CHAT: string;
105
+ A11Y_DISMISS_CHAT_TIP: string;
106
+ PAGE_CONTENT_READING: string;
107
+ PAGE_CONTENT_READ: string;
108
+ PAGE_CONTENT_ERROR: string;
72
109
  A11Y_OPEN_MENU: string;
73
110
  A11Y_CLOSE_SIDEBAR: string;
74
111
  A11Y_OPEN_SIDEBAR: string;
@@ -94,6 +131,8 @@ type AiChatMode = "authenticated" | "public";
94
131
  interface AiChatProviderConfig {
95
132
  /** Conversation behavior selected by `aiChatClientPlugin()`. */
96
133
  readonly mode: AiChatMode;
134
+ /** Whether built-in page reader permission checks and status UI are enabled. */
135
+ readonly pageContent?: boolean;
97
136
  }
98
137
  /**
99
138
  * State of a tool call execution
@@ -152,6 +191,8 @@ interface RouteContext {
152
191
  * plugin-specific components and behavior.
153
192
  */
154
193
  interface AiChatPluginOverrides {
194
+ /** Optional content-free usage telemetry. Exceptions and rejections are isolated from chat. */
195
+ onAnalyticsEvent?: (event: AiChatAnalyticsEvent) => void | Promise<void>;
155
196
  /**
156
197
  * Function used to upload a file and return its URL.
157
198
  * Called for images, PDFs, text files, and other supported file types.
@@ -255,6 +296,15 @@ interface ChatLayoutBaseProps {
255
296
  conversationId?: string;
256
297
  /** Additional class name for the container */
257
298
  className?: string;
299
+ /** Additional container styles, including application-specific floating offsets. */
300
+ style?: CSSProperties;
301
+ /** Instance-local context. Omit to inherit page registrations; null disables them. */
302
+ pageContext?: PageAIContextConfig | null;
303
+ /**
304
+ * Page identity. Changing it resets the panel, tip, and conversation, stopping
305
+ * an active stream. Supply initialMessages/conversationId for this page only.
306
+ */
307
+ pageKey?: string;
258
308
  /** Whether to show the sidebar */
259
309
  showSidebar?: boolean;
260
310
  /** Initial messages to populate the chat (useful for localStorage persistence in public mode) */
@@ -267,10 +317,16 @@ interface ChatLayoutBaseProps {
267
317
  interface ChatLayoutWidgetProps extends ChatLayoutBaseProps {
268
318
  /** Widget mode: compact embeddable panel with a floating trigger button */
269
319
  layout: "widget";
270
- /** Height of the widget panel. Default: `"600px"` */
320
+ /** Height of the widget panel. Default: `"min(600px, calc(100dvh - 112px))"` */
271
321
  widgetHeight?: string | number;
272
- /** Width of the widget panel. Default: `"380px"` */
322
+ /** Width of the widget panel. Default: `"min(380px, calc(100vw - 32px))"` */
273
323
  widgetWidth?: string | number;
324
+ /** Fix the widget to the bottom right. Default: false (embedded). Override offsets with className or style. */
325
+ floating?: boolean;
326
+ /** Optional dismissible introduction shown while the widget is closed. */
327
+ introTip?: string;
328
+ /** Milliseconds before hiding the introduction. Default: 6000. */
329
+ introTipDuration?: number;
274
330
  /**
275
331
  * Whether the widget panel starts open. Default: `false`.
276
332
  * Set to `true` when embedding inside an already-open container such as a
@@ -355,4 +411,4 @@ declare function ChatInput({ input, handleInputChange, handleSubmit, isLoading,
355
411
  declare function ToolCallDisplay({ toolCallId, toolName, state, input, output, errorText, isLoading, }: ToolCallProps): react_jsx_runtime.JSX.Element;
356
412
 
357
413
  export { ChatInterface as C, DEFAULT_ALLOWED_FILE_TYPES as D, ChatLayout as f, ChatSidebar as h, ChatMessage as i, ChatInput as j, ToolCallDisplay as k };
358
- export type { AiChatMode as A, ToolCallProps as T, AiChatPluginOverrides as a, AiChatProviderConfig as b, AllowedFileType as c, ToolCallState as d, ToolCallRenderer as e, ChatLayoutProps as g };
414
+ export type { AiChatMode as A, ToolCallProps as T, AiChatPluginOverrides as a, AiChatProviderConfig as b, AllowedFileType as c, ToolCallState as d, ToolCallRenderer as e, ChatLayoutProps as g, AiChatAnalyticsEvent as l };
@@ -1,6 +1,39 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { UIMessage } from 'ai';
3
- import { ComponentType, FormEvent } from 'react';
3
+ import { ComponentType, CSSProperties, FormEvent } from 'react';
4
+ import { PageAIContextConfig } from '../plugins/ai-chat/client/context/page-ai-context.mjs';
5
+
6
+ /** Content-free usage events. No prompts, answers, page descriptions, or tool outputs. */
7
+ type AiChatAnalyticsEvent = {
8
+ /** Caller-supplied page identity; omit sensitive values. */
9
+ pageKey?: string;
10
+ /** Page label, without its description or document content. */
11
+ routeName?: string;
12
+ /** Registered client mode, not an assertion about the viewer's identity. */
13
+ mode: AiChatMode;
14
+ } & ({
15
+ type: "widget_opened" | "widget_closed" | "intro_tip_shown" | "conversation_cleared";
16
+ } | {
17
+ type: "intro_tip_dismissed";
18
+ reason: "timeout" | "manual" | "open";
19
+ } | {
20
+ type: "suggestion_selected";
21
+ index: number;
22
+ } | {
23
+ type: "message_submitted";
24
+ inputKind: "typed" | "suggestion" | "edit" | "retry";
25
+ inputLength: number;
26
+ messageCount: number;
27
+ attachmentCount: number;
28
+ } | {
29
+ type: "response_completed";
30
+ durationMs: number;
31
+ outputLength: number;
32
+ toolCallCount: number;
33
+ } | {
34
+ type: "response_failed";
35
+ durationMs: number;
36
+ });
4
37
 
5
38
  /**
6
39
  * AI Chat plugin localization strings
@@ -69,6 +102,10 @@ interface AiChatLocalization {
69
102
  A11Y_CLEAR_CHAT: string;
70
103
  A11Y_CLOSE_CHAT: string;
71
104
  A11Y_OPEN_CHAT: string;
105
+ A11Y_DISMISS_CHAT_TIP: string;
106
+ PAGE_CONTENT_READING: string;
107
+ PAGE_CONTENT_READ: string;
108
+ PAGE_CONTENT_ERROR: string;
72
109
  A11Y_OPEN_MENU: string;
73
110
  A11Y_CLOSE_SIDEBAR: string;
74
111
  A11Y_OPEN_SIDEBAR: string;
@@ -94,6 +131,8 @@ type AiChatMode = "authenticated" | "public";
94
131
  interface AiChatProviderConfig {
95
132
  /** Conversation behavior selected by `aiChatClientPlugin()`. */
96
133
  readonly mode: AiChatMode;
134
+ /** Whether built-in page reader permission checks and status UI are enabled. */
135
+ readonly pageContent?: boolean;
97
136
  }
98
137
  /**
99
138
  * State of a tool call execution
@@ -152,6 +191,8 @@ interface RouteContext {
152
191
  * plugin-specific components and behavior.
153
192
  */
154
193
  interface AiChatPluginOverrides {
194
+ /** Optional content-free usage telemetry. Exceptions and rejections are isolated from chat. */
195
+ onAnalyticsEvent?: (event: AiChatAnalyticsEvent) => void | Promise<void>;
155
196
  /**
156
197
  * Function used to upload a file and return its URL.
157
198
  * Called for images, PDFs, text files, and other supported file types.
@@ -255,6 +296,15 @@ interface ChatLayoutBaseProps {
255
296
  conversationId?: string;
256
297
  /** Additional class name for the container */
257
298
  className?: string;
299
+ /** Additional container styles, including application-specific floating offsets. */
300
+ style?: CSSProperties;
301
+ /** Instance-local context. Omit to inherit page registrations; null disables them. */
302
+ pageContext?: PageAIContextConfig | null;
303
+ /**
304
+ * Page identity. Changing it resets the panel, tip, and conversation, stopping
305
+ * an active stream. Supply initialMessages/conversationId for this page only.
306
+ */
307
+ pageKey?: string;
258
308
  /** Whether to show the sidebar */
259
309
  showSidebar?: boolean;
260
310
  /** Initial messages to populate the chat (useful for localStorage persistence in public mode) */
@@ -267,10 +317,16 @@ interface ChatLayoutBaseProps {
267
317
  interface ChatLayoutWidgetProps extends ChatLayoutBaseProps {
268
318
  /** Widget mode: compact embeddable panel with a floating trigger button */
269
319
  layout: "widget";
270
- /** Height of the widget panel. Default: `"600px"` */
320
+ /** Height of the widget panel. Default: `"min(600px, calc(100dvh - 112px))"` */
271
321
  widgetHeight?: string | number;
272
- /** Width of the widget panel. Default: `"380px"` */
322
+ /** Width of the widget panel. Default: `"min(380px, calc(100vw - 32px))"` */
273
323
  widgetWidth?: string | number;
324
+ /** Fix the widget to the bottom right. Default: false (embedded). Override offsets with className or style. */
325
+ floating?: boolean;
326
+ /** Optional dismissible introduction shown while the widget is closed. */
327
+ introTip?: string;
328
+ /** Milliseconds before hiding the introduction. Default: 6000. */
329
+ introTipDuration?: number;
274
330
  /**
275
331
  * Whether the widget panel starts open. Default: `false`.
276
332
  * Set to `true` when embedding inside an already-open container such as a
@@ -355,4 +411,4 @@ declare function ChatInput({ input, handleInputChange, handleSubmit, isLoading,
355
411
  declare function ToolCallDisplay({ toolCallId, toolName, state, input, output, errorText, isLoading, }: ToolCallProps): react_jsx_runtime.JSX.Element;
356
412
 
357
413
  export { ChatInterface as C, DEFAULT_ALLOWED_FILE_TYPES as D, ChatLayout as f, ChatSidebar as h, ChatMessage as i, ChatInput as j, ToolCallDisplay as k };
358
- export type { AiChatMode as A, ToolCallProps as T, AiChatPluginOverrides as a, AiChatProviderConfig as b, AllowedFileType as c, ToolCallState as d, ToolCallRenderer as e, ChatLayoutProps as g };
414
+ export type { AiChatMode as A, ToolCallProps as T, AiChatPluginOverrides as a, AiChatProviderConfig as b, AllowedFileType as c, ToolCallState as d, ToolCallRenderer as e, ChatLayoutProps as g, AiChatAnalyticsEvent as l };
@@ -41,6 +41,20 @@ type SerializedMessage = Omit<Message, "createdAt"> & {
41
41
  createdAt: string;
42
42
  };
43
43
 
44
+ /** Server-owned content loader for the standard readPage tool. */
45
+ interface AiChatPageContentConfig {
46
+ /** Allowed page paths. Validated before resolve; paths are also limited to 16,000 characters. */
47
+ pathSchema: z.ZodType<string, string>;
48
+ /**
49
+ * Load the complete page as JSON-serializable reference material. Enforce
50
+ * publication and access rules here using the request context. Return null
51
+ * when unavailable. BTST never fetches URLs or truncates the returned content.
52
+ */
53
+ resolve: (path: string, context: ChatApiContext<ChatOperationInput>) => unknown | Promise<unknown>;
54
+ /** Optional model-facing description of the supported page types. */
55
+ description?: string;
56
+ }
57
+
44
58
  type AiChatAccess = "authorized" | "public";
45
59
  /** Runtime input for one conversation operation. */
46
60
  declare const ConversationOperationInputSchema: z.ZodObject<{
@@ -208,6 +222,8 @@ interface AiChatBackendConfig {
208
222
  access?: AiChatAccess;
209
223
  systemPrompt?: string;
210
224
  tools?: Record<string, Tool>;
225
+ /** Enable the standard readPage server tool for full page content. Reserves the readPage tool name. */
226
+ pageContent?: AiChatPageContentConfig;
211
227
  enablePageTools?: boolean;
212
228
  clientToolSchemas?: Record<string, Tool>;
213
229
  hooks?: AiChatBackendHooks;
@@ -503,5 +519,5 @@ declare function createAiChatQueryKeys(client: ReturnType<typeof createApiClient
503
519
  }>;
504
520
  type AiChatQueryKeys = ReturnType<typeof createAiChatQueryKeys>;
505
521
 
506
- export { UpdateConversationOperationInputSchema as U, aiChatIdentityKey as d, aiChatResources as e, createAiChatQueryKeys as f, aiChatBackendPlugin as i, AI_CHAT_OPERATION_INVENTORY as n, AI_CHAT_RAW_ESCAPE_HATCH_INVENTORY as o, AiChatOperationError as p, ChatOperationInputSchema as q, ConversationOperationInputSchema as r };
507
- export type { AiChatIdentityPartition as A, ConversationWithMessages as C, Message as M, RenameConversationInput as R, SerializedConversation as S, SerializedMessage as a, CreateConversationInput as b, Conversation as c, AiChatQueryKeys as g, AiChatBackendConfig as h, AiChatApiRouter as j, AiChatAccess as k, AiChatBackendHooks as l, ChatApiContext as m };
522
+ export { UpdateConversationOperationInputSchema as U, aiChatIdentityKey as d, aiChatResources as e, createAiChatQueryKeys as f, aiChatBackendPlugin as i, AI_CHAT_OPERATION_INVENTORY as o, AI_CHAT_RAW_ESCAPE_HATCH_INVENTORY as p, AiChatOperationError as q, ChatOperationInputSchema as r, ConversationOperationInputSchema as s };
523
+ export type { AiChatIdentityPartition as A, ConversationWithMessages as C, Message as M, RenameConversationInput as R, SerializedConversation as S, SerializedMessage as a, CreateConversationInput as b, Conversation as c, AiChatQueryKeys as g, AiChatBackendConfig as h, AiChatApiRouter as j, AiChatPageContentConfig as k, AiChatAccess as l, AiChatBackendHooks as m, ChatApiContext as n };
@@ -41,6 +41,20 @@ type SerializedMessage = Omit<Message, "createdAt"> & {
41
41
  createdAt: string;
42
42
  };
43
43
 
44
+ /** Server-owned content loader for the standard readPage tool. */
45
+ interface AiChatPageContentConfig {
46
+ /** Allowed page paths. Validated before resolve; paths are also limited to 16,000 characters. */
47
+ pathSchema: z.ZodType<string, string>;
48
+ /**
49
+ * Load the complete page as JSON-serializable reference material. Enforce
50
+ * publication and access rules here using the request context. Return null
51
+ * when unavailable. BTST never fetches URLs or truncates the returned content.
52
+ */
53
+ resolve: (path: string, context: ChatApiContext<ChatOperationInput>) => unknown | Promise<unknown>;
54
+ /** Optional model-facing description of the supported page types. */
55
+ description?: string;
56
+ }
57
+
44
58
  type AiChatAccess = "authorized" | "public";
45
59
  /** Runtime input for one conversation operation. */
46
60
  declare const ConversationOperationInputSchema: z.ZodObject<{
@@ -208,6 +222,8 @@ interface AiChatBackendConfig {
208
222
  access?: AiChatAccess;
209
223
  systemPrompt?: string;
210
224
  tools?: Record<string, Tool>;
225
+ /** Enable the standard readPage server tool for full page content. Reserves the readPage tool name. */
226
+ pageContent?: AiChatPageContentConfig;
211
227
  enablePageTools?: boolean;
212
228
  clientToolSchemas?: Record<string, Tool>;
213
229
  hooks?: AiChatBackendHooks;
@@ -503,5 +519,5 @@ declare function createAiChatQueryKeys(client: ReturnType<typeof createApiClient
503
519
  }>;
504
520
  type AiChatQueryKeys = ReturnType<typeof createAiChatQueryKeys>;
505
521
 
506
- export { UpdateConversationOperationInputSchema as U, aiChatIdentityKey as d, aiChatResources as e, createAiChatQueryKeys as f, aiChatBackendPlugin as i, AI_CHAT_OPERATION_INVENTORY as n, AI_CHAT_RAW_ESCAPE_HATCH_INVENTORY as o, AiChatOperationError as p, ChatOperationInputSchema as q, ConversationOperationInputSchema as r };
507
- export type { AiChatIdentityPartition as A, ConversationWithMessages as C, Message as M, RenameConversationInput as R, SerializedConversation as S, SerializedMessage as a, CreateConversationInput as b, Conversation as c, AiChatQueryKeys as g, AiChatBackendConfig as h, AiChatApiRouter as j, AiChatAccess as k, AiChatBackendHooks as l, ChatApiContext as m };
522
+ export { UpdateConversationOperationInputSchema as U, aiChatIdentityKey as d, aiChatResources as e, createAiChatQueryKeys as f, aiChatBackendPlugin as i, AI_CHAT_OPERATION_INVENTORY as o, AI_CHAT_RAW_ESCAPE_HATCH_INVENTORY as p, AiChatOperationError as q, ChatOperationInputSchema as r, ConversationOperationInputSchema as s };
523
+ export type { AiChatIdentityPartition as A, ConversationWithMessages as C, Message as M, RenameConversationInput as R, SerializedConversation as S, SerializedMessage as a, CreateConversationInput as b, Conversation as c, AiChatQueryKeys as g, AiChatBackendConfig as h, AiChatApiRouter as j, AiChatPageContentConfig as k, AiChatAccess as l, AiChatBackendHooks as m, ChatApiContext as n };
@@ -1,6 +1,39 @@
1
1
  import * as react_jsx_runtime from 'react/jsx-runtime';
2
2
  import { UIMessage } from 'ai';
3
- import { ComponentType, FormEvent } from 'react';
3
+ import { ComponentType, CSSProperties, FormEvent } from 'react';
4
+ import { PageAIContextConfig } from '../plugins/ai-chat/client/context/page-ai-context.cjs';
5
+
6
+ /** Content-free usage events. No prompts, answers, page descriptions, or tool outputs. */
7
+ type AiChatAnalyticsEvent = {
8
+ /** Caller-supplied page identity; omit sensitive values. */
9
+ pageKey?: string;
10
+ /** Page label, without its description or document content. */
11
+ routeName?: string;
12
+ /** Registered client mode, not an assertion about the viewer's identity. */
13
+ mode: AiChatMode;
14
+ } & ({
15
+ type: "widget_opened" | "widget_closed" | "intro_tip_shown" | "conversation_cleared";
16
+ } | {
17
+ type: "intro_tip_dismissed";
18
+ reason: "timeout" | "manual" | "open";
19
+ } | {
20
+ type: "suggestion_selected";
21
+ index: number;
22
+ } | {
23
+ type: "message_submitted";
24
+ inputKind: "typed" | "suggestion" | "edit" | "retry";
25
+ inputLength: number;
26
+ messageCount: number;
27
+ attachmentCount: number;
28
+ } | {
29
+ type: "response_completed";
30
+ durationMs: number;
31
+ outputLength: number;
32
+ toolCallCount: number;
33
+ } | {
34
+ type: "response_failed";
35
+ durationMs: number;
36
+ });
4
37
 
5
38
  /**
6
39
  * AI Chat plugin localization strings
@@ -69,6 +102,10 @@ interface AiChatLocalization {
69
102
  A11Y_CLEAR_CHAT: string;
70
103
  A11Y_CLOSE_CHAT: string;
71
104
  A11Y_OPEN_CHAT: string;
105
+ A11Y_DISMISS_CHAT_TIP: string;
106
+ PAGE_CONTENT_READING: string;
107
+ PAGE_CONTENT_READ: string;
108
+ PAGE_CONTENT_ERROR: string;
72
109
  A11Y_OPEN_MENU: string;
73
110
  A11Y_CLOSE_SIDEBAR: string;
74
111
  A11Y_OPEN_SIDEBAR: string;
@@ -94,6 +131,8 @@ type AiChatMode = "authenticated" | "public";
94
131
  interface AiChatProviderConfig {
95
132
  /** Conversation behavior selected by `aiChatClientPlugin()`. */
96
133
  readonly mode: AiChatMode;
134
+ /** Whether built-in page reader permission checks and status UI are enabled. */
135
+ readonly pageContent?: boolean;
97
136
  }
98
137
  /**
99
138
  * State of a tool call execution
@@ -152,6 +191,8 @@ interface RouteContext {
152
191
  * plugin-specific components and behavior.
153
192
  */
154
193
  interface AiChatPluginOverrides {
194
+ /** Optional content-free usage telemetry. Exceptions and rejections are isolated from chat. */
195
+ onAnalyticsEvent?: (event: AiChatAnalyticsEvent) => void | Promise<void>;
155
196
  /**
156
197
  * Function used to upload a file and return its URL.
157
198
  * Called for images, PDFs, text files, and other supported file types.
@@ -255,6 +296,15 @@ interface ChatLayoutBaseProps {
255
296
  conversationId?: string;
256
297
  /** Additional class name for the container */
257
298
  className?: string;
299
+ /** Additional container styles, including application-specific floating offsets. */
300
+ style?: CSSProperties;
301
+ /** Instance-local context. Omit to inherit page registrations; null disables them. */
302
+ pageContext?: PageAIContextConfig | null;
303
+ /**
304
+ * Page identity. Changing it resets the panel, tip, and conversation, stopping
305
+ * an active stream. Supply initialMessages/conversationId for this page only.
306
+ */
307
+ pageKey?: string;
258
308
  /** Whether to show the sidebar */
259
309
  showSidebar?: boolean;
260
310
  /** Initial messages to populate the chat (useful for localStorage persistence in public mode) */
@@ -267,10 +317,16 @@ interface ChatLayoutBaseProps {
267
317
  interface ChatLayoutWidgetProps extends ChatLayoutBaseProps {
268
318
  /** Widget mode: compact embeddable panel with a floating trigger button */
269
319
  layout: "widget";
270
- /** Height of the widget panel. Default: `"600px"` */
320
+ /** Height of the widget panel. Default: `"min(600px, calc(100dvh - 112px))"` */
271
321
  widgetHeight?: string | number;
272
- /** Width of the widget panel. Default: `"380px"` */
322
+ /** Width of the widget panel. Default: `"min(380px, calc(100vw - 32px))"` */
273
323
  widgetWidth?: string | number;
324
+ /** Fix the widget to the bottom right. Default: false (embedded). Override offsets with className or style. */
325
+ floating?: boolean;
326
+ /** Optional dismissible introduction shown while the widget is closed. */
327
+ introTip?: string;
328
+ /** Milliseconds before hiding the introduction. Default: 6000. */
329
+ introTipDuration?: number;
274
330
  /**
275
331
  * Whether the widget panel starts open. Default: `false`.
276
332
  * Set to `true` when embedding inside an already-open container such as a
@@ -355,4 +411,4 @@ declare function ChatInput({ input, handleInputChange, handleSubmit, isLoading,
355
411
  declare function ToolCallDisplay({ toolCallId, toolName, state, input, output, errorText, isLoading, }: ToolCallProps): react_jsx_runtime.JSX.Element;
356
412
 
357
413
  export { ChatInterface as C, DEFAULT_ALLOWED_FILE_TYPES as D, ChatLayout as f, ChatSidebar as h, ChatMessage as i, ChatInput as j, ToolCallDisplay as k };
358
- export type { AiChatMode as A, ToolCallProps as T, AiChatPluginOverrides as a, AiChatProviderConfig as b, AllowedFileType as c, ToolCallState as d, ToolCallRenderer as e, ChatLayoutProps as g };
414
+ export type { AiChatMode as A, ToolCallProps as T, AiChatPluginOverrides as a, AiChatProviderConfig as b, AllowedFileType as c, ToolCallState as d, ToolCallRenderer as e, ChatLayoutProps as g, AiChatAnalyticsEvent as l };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btst/stack",
3
- "version": "4.1.1",
3
+ "version": "4.2.0",
4
4
  "description": "A composable, plugin-based library for building full-stack applications.",
5
5
  "repository": {
6
6
  "type": "git",