@btst/stack 4.1.0 → 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 (82) 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 +2 -2
  39. package/dist/plugins/blog/api/index.d.mts +2 -2
  40. package/dist/plugins/blog/api/index.d.ts +2 -2
  41. package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
  42. package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
  43. package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
  44. package/dist/plugins/blog/client/index.d.cts +2 -2
  45. package/dist/plugins/blog/client/index.d.mts +2 -2
  46. package/dist/plugins/blog/client/index.d.ts +2 -2
  47. package/dist/plugins/blog/query-keys.d.cts +2 -2
  48. package/dist/plugins/blog/query-keys.d.mts +2 -2
  49. package/dist/plugins/blog/query-keys.d.ts +2 -2
  50. package/dist/shared/{stack.CjCSDfWy.d.mts → stack.94kN3pEK.d.mts} +5 -5
  51. package/dist/shared/{stack.B5yPgHsp.d.mts → stack.B7Mj74BQ.d.mts} +18 -2
  52. package/dist/shared/{stack.BGzgFx3R.d.cts → stack.BTStHVTy.d.cts} +5 -5
  53. package/dist/shared/{stack.rVF0f509.d.mts → stack.C-eQN_Jt.d.ts} +60 -4
  54. package/dist/shared/{stack.rVF0f509.d.ts → stack.C5FZr-jJ.d.mts} +60 -4
  55. package/dist/shared/{stack.Dvy7FmaA.d.mts → stack.CTneIW0u.d.mts} +37 -37
  56. package/dist/shared/{stack.BOZRaVpu.d.cts → stack.Ce6V7eDj.d.cts} +37 -37
  57. package/dist/shared/{stack.DlBoNqYu.d.ts → stack.D5b9l24t.d.ts} +37 -37
  58. package/dist/shared/{stack.3Sr6qRtT.d.ts → stack.DBH1Bjko.d.ts} +18 -2
  59. package/dist/shared/{stack.Dpg_JjIo.d.ts → stack.DfrRYOIl.d.ts} +5 -5
  60. package/dist/shared/{stack.C4oVW2qK.d.cts → stack.Dgt3MCtW.d.cts} +18 -2
  61. package/dist/shared/{stack.rVF0f509.d.cts → stack.xAiy2VtO.d.cts} +60 -4
  62. package/package.json +1 -1
  63. package/src/plugins/ai-chat/__tests__/authorization.test.ts +106 -1
  64. package/src/plugins/ai-chat/__tests__/classnames-overrides.test.tsx +4 -1
  65. package/src/plugins/ai-chat/__tests__/page-content.test.ts +177 -0
  66. package/src/plugins/ai-chat/__tests__/page-widget.test.tsx +573 -0
  67. package/src/plugins/ai-chat/api/operations.ts +29 -13
  68. package/src/plugins/ai-chat/api/page-tools.ts +56 -2
  69. package/src/plugins/ai-chat/api/plugin.ts +13 -0
  70. package/src/plugins/ai-chat/client/analytics.ts +35 -0
  71. package/src/plugins/ai-chat/client/components/chat-interface.tsx +113 -10
  72. package/src/plugins/ai-chat/client/components/chat-layout.tsx +118 -7
  73. package/src/plugins/ai-chat/client/components/tool-call-display.tsx +29 -1
  74. package/src/plugins/ai-chat/client/context/chat-analytics.tsx +46 -0
  75. package/src/plugins/ai-chat/client/context/page-ai-context.tsx +9 -1
  76. package/src/plugins/ai-chat/client/index.ts +2 -0
  77. package/src/plugins/ai-chat/client/localization/index.ts +8 -0
  78. package/src/plugins/ai-chat/client/overrides.ts +5 -0
  79. package/src/plugins/ai-chat/client/plugin.tsx +3 -0
  80. package/dist/shared/{stack.Csi-4Mgz.d.cts → stack.CKc_Bais.d.cts} +7 -7
  81. package/dist/shared/{stack.Csi-4Mgz.d.mts → stack.CKc_Bais.d.mts} +7 -7
  82. package/dist/shared/{stack.Csi-4Mgz.d.ts → stack.CKc_Bais.d.ts} +7 -7
@@ -0,0 +1,46 @@
1
+ "use client";
2
+
3
+ import { createContext, useCallback, useContext, useRef } from "react";
4
+ import { usePluginOverrides, useStack } from "@btst/stack/context";
5
+ import { usePageAIContext } from "./page-ai-context";
6
+ import { resolveAiChatMode, type AiChatPluginOverrides } from "../overrides";
7
+ import type { AiChatAnalyticsEvent } from "../analytics";
8
+
9
+ export const ChatAnalyticsPageKey = createContext<string | undefined>(
10
+ undefined,
11
+ );
12
+ type EventDetails<T> = T extends unknown
13
+ ? Omit<T, "pageKey" | "routeName" | "mode">
14
+ : never;
15
+
16
+ export function useChatAnalytics() {
17
+ const { onAnalyticsEvent } = usePluginOverrides<
18
+ AiChatPluginOverrides,
19
+ Partial<AiChatPluginOverrides>
20
+ >("aiChat", {});
21
+ const { plugins } = useStack();
22
+ const pageKey = useContext(ChatAnalyticsPageKey);
23
+ const routeName = usePageAIContext()?.routeName;
24
+ const latest = useRef({
25
+ onAnalyticsEvent,
26
+ pageKey,
27
+ routeName,
28
+ mode: resolveAiChatMode(plugins?.aiChat?.config),
29
+ });
30
+ latest.current = {
31
+ onAnalyticsEvent,
32
+ pageKey,
33
+ routeName,
34
+ mode: resolveAiChatMode(plugins?.aiChat?.config),
35
+ };
36
+ return useCallback((event: EventDetails<AiChatAnalyticsEvent>) => {
37
+ const { onAnalyticsEvent: callback, ...context } = latest.current;
38
+ try {
39
+ void Promise.resolve(callback?.({ ...context, ...event })).catch(
40
+ () => {},
41
+ );
42
+ } catch {
43
+ // Optional instrumentation must never interrupt chat interactions.
44
+ }
45
+ }, []);
46
+ }
@@ -33,7 +33,8 @@ export interface PageAIContextConfig {
33
33
  /**
34
34
  * Human-readable description of the current page and its content.
35
35
  * Injected into the AI system prompt so it understands what the user is looking at.
36
- * Capped at 8,000 characters server-side.
36
+ * Limited to 16,000 characters server-side. For longer content, pass a page
37
+ * path and configure the backend pageContent resolver.
37
38
  */
38
39
  pageDescription: string;
39
40
 
@@ -74,6 +75,11 @@ const PageAIAPIContext = createContext<PageAIAPIContextValue | null>(null);
74
75
  */
75
76
  const PageAIVersionContext = createContext<number>(0);
76
77
 
78
+ /** Instance-local context used by ChatLayout; undefined inherits page registrations. */
79
+ export const PageAIContextScope = createContext<
80
+ PageAIContextConfig | null | undefined
81
+ >(undefined);
82
+
77
83
  /**
78
84
  * Provider that enables route-aware AI context across the app.
79
85
  *
@@ -235,6 +241,8 @@ export function usePageAIContext(): PageAIContextConfig | null {
235
241
  // registers or unregisters context, then read the latest active config.
236
242
  useContext(PageAIVersionContext);
237
243
  const ctx = useContext(PageAIAPIContext);
244
+ const scoped = useContext(PageAIContextScope);
245
+ if (scoped !== undefined) return scoped;
238
246
  if (!ctx) return null;
239
247
  return ctx.getActive();
240
248
  }
@@ -24,3 +24,5 @@ export { ToolCallDisplay } from "./components/tool-call-display";
24
24
 
25
25
  // Re-export UIMessage type from AI SDK for consumer convenience
26
26
  export type { UIMessage } from "ai";
27
+
28
+ export type { AiChatAnalyticsEvent } from "./analytics";
@@ -87,6 +87,10 @@ export interface AiChatLocalization {
87
87
  A11Y_CLEAR_CHAT: string;
88
88
  A11Y_CLOSE_CHAT: string;
89
89
  A11Y_OPEN_CHAT: string;
90
+ A11Y_DISMISS_CHAT_TIP: string;
91
+ PAGE_CONTENT_READING: string;
92
+ PAGE_CONTENT_READ: string;
93
+ PAGE_CONTENT_ERROR: string;
90
94
  A11Y_OPEN_MENU: string;
91
95
  A11Y_CLOSE_SIDEBAR: string;
92
96
  A11Y_OPEN_SIDEBAR: string;
@@ -191,6 +195,10 @@ export const AI_CHAT_LOCALIZATION: AiChatLocalization = {
191
195
  A11Y_CLEAR_CHAT: "Clear chat",
192
196
  A11Y_CLOSE_CHAT: "Close chat",
193
197
  A11Y_OPEN_CHAT: "Open chat",
198
+ A11Y_DISMISS_CHAT_TIP: "Dismiss chat tip",
199
+ PAGE_CONTENT_READING: "Reading page content…",
200
+ PAGE_CONTENT_READ: "Read page content",
201
+ PAGE_CONTENT_ERROR: "Unable to read this page.",
194
202
  A11Y_OPEN_MENU: "Open menu",
195
203
  A11Y_CLOSE_SIDEBAR: "Close sidebar",
196
204
  A11Y_OPEN_SIDEBAR: "Open sidebar",
@@ -1,3 +1,4 @@
1
+ import type { AiChatAnalyticsEvent } from "./analytics";
1
2
  import type { ComponentType } from "react";
2
3
  import { normalizePath } from "@btst/stack/client";
3
4
  import type { AiChatLocalization } from "./localization";
@@ -13,6 +14,8 @@ export type AiChatMode = "authenticated" | "public";
13
14
  export interface AiChatProviderConfig {
14
15
  /** Conversation behavior selected by `aiChatClientPlugin()`. */
15
16
  readonly mode: AiChatMode;
17
+ /** Whether built-in page reader permission checks and status UI are enabled. */
18
+ readonly pageContent?: boolean;
16
19
  }
17
20
 
18
21
  /** Resolve the registered AI Chat client factory mode. */
@@ -143,6 +146,8 @@ export interface RouteContext {
143
146
  * plugin-specific components and behavior.
144
147
  */
145
148
  export interface AiChatPluginOverrides {
149
+ /** Optional content-free usage telemetry. Exceptions and rejections are isolated from chat. */
150
+ onAnalyticsEvent?: (event: AiChatAnalyticsEvent) => void | Promise<void>;
146
151
  /**
147
152
  * Function used to upload a file and return its URL.
148
153
  * Called for images, PDFs, text files, and other supported file types.
@@ -67,6 +67,8 @@ export interface AiChatClientConfig {
67
67
  * @default 'authenticated'
68
68
  */
69
69
  mode?: AiChatMode;
70
+ /** Enable built-in readPage permission checks and compact status alongside backend pageContent. */
71
+ pageContent?: boolean;
70
72
 
71
73
  /** Optional SEO configuration for meta tags */
72
74
  seo?: {
@@ -560,6 +562,7 @@ export const aiChatClientPlugin = (config: AiChatClientConfig = {}) =>
560
562
  id: "aiChat",
561
563
  providerConfig: {
562
564
  mode: config.mode ?? "authenticated",
565
+ ...(config.pageContent ? { pageContent: true } : {}),
563
566
  } satisfies AiChatProviderConfig,
564
567
  resolve: (runtime) => resolveAiChatClientPlugin({ ...config, runtime }),
565
568
  });
@@ -1,13 +1,6 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  declare const createPostSchema: z.ZodObject<{
4
- tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
5
- name: z.ZodString;
6
- }, z.core.$strip>, z.ZodObject<{
7
- id: z.ZodString;
8
- name: z.ZodString;
9
- slug: z.ZodString;
10
- }, z.core.$strip>]>>>>;
11
4
  slug: z.ZodOptional<z.ZodString>;
12
5
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
13
6
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -17,6 +10,13 @@ declare const createPostSchema: z.ZodObject<{
17
10
  excerpt: z.ZodString;
18
11
  image: z.ZodOptional<z.ZodString>;
19
12
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
13
+ tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
14
+ name: z.ZodString;
15
+ }, z.core.$strip>, z.ZodObject<{
16
+ id: z.ZodString;
17
+ name: z.ZodString;
18
+ slug: z.ZodString;
19
+ }, z.core.$strip>]>>>>;
20
20
  }, z.core.$strip>;
21
21
  declare const updatePostSchema: z.ZodObject<{
22
22
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -1,13 +1,6 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  declare const createPostSchema: z.ZodObject<{
4
- tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
5
- name: z.ZodString;
6
- }, z.core.$strip>, z.ZodObject<{
7
- id: z.ZodString;
8
- name: z.ZodString;
9
- slug: z.ZodString;
10
- }, z.core.$strip>]>>>>;
11
4
  slug: z.ZodOptional<z.ZodString>;
12
5
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
13
6
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -17,6 +10,13 @@ declare const createPostSchema: z.ZodObject<{
17
10
  excerpt: z.ZodString;
18
11
  image: z.ZodOptional<z.ZodString>;
19
12
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
13
+ tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
14
+ name: z.ZodString;
15
+ }, z.core.$strip>, z.ZodObject<{
16
+ id: z.ZodString;
17
+ name: z.ZodString;
18
+ slug: z.ZodString;
19
+ }, z.core.$strip>]>>>>;
20
20
  }, z.core.$strip>;
21
21
  declare const updatePostSchema: z.ZodObject<{
22
22
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -1,13 +1,6 @@
1
1
  import { z } from 'zod';
2
2
 
3
3
  declare const createPostSchema: z.ZodObject<{
4
- tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
5
- name: z.ZodString;
6
- }, z.core.$strip>, z.ZodObject<{
7
- id: z.ZodString;
8
- name: z.ZodString;
9
- slug: z.ZodString;
10
- }, z.core.$strip>]>>>>;
11
4
  slug: z.ZodOptional<z.ZodString>;
12
5
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
13
6
  createdAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;
@@ -17,6 +10,13 @@ declare const createPostSchema: z.ZodObject<{
17
10
  excerpt: z.ZodString;
18
11
  image: z.ZodOptional<z.ZodString>;
19
12
  published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
13
+ tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
14
+ name: z.ZodString;
15
+ }, z.core.$strip>, z.ZodObject<{
16
+ id: z.ZodString;
17
+ name: z.ZodString;
18
+ slug: z.ZodString;
19
+ }, z.core.$strip>]>>>>;
20
20
  }, z.core.$strip>;
21
21
  declare const updatePostSchema: z.ZodObject<{
22
22
  publishedAt: z.ZodOptional<z.ZodCoercedDate<unknown>>;