@btst/stack 4.4.0 → 4.5.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.
- package/README.md +2 -2
- package/dist/packages/stack/src/plugins/ai-chat/api/compaction.cjs +64 -24
- package/dist/packages/stack/src/plugins/ai-chat/api/compaction.mjs +65 -25
- package/dist/packages/stack/src/plugins/ai-chat/api/context-checkpoint.cjs +54 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/context-checkpoint.mjs +51 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/getters.cjs +2 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/getters.mjs +2 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/operations.cjs +269 -93
- package/dist/packages/stack/src/plugins/ai-chat/api/operations.mjs +270 -94
- package/dist/packages/stack/src/plugins/ai-chat/api/plugin.cjs +3 -5
- package/dist/packages/stack/src/plugins/ai-chat/api/plugin.mjs +3 -5
- package/dist/packages/stack/src/plugins/ai-chat/api/serializers.cjs +1 -0
- package/dist/packages/stack/src/plugins/ai-chat/api/serializers.mjs +1 -0
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.cjs +26 -16
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-interface.mjs +26 -16
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-message.cjs +22 -2
- package/dist/packages/stack/src/plugins/ai-chat/client/components/chat-message.mjs +22 -2
- package/dist/packages/stack/src/plugins/ai-chat/db.cjs +8 -0
- package/dist/packages/stack/src/plugins/ai-chat/db.mjs +8 -0
- package/dist/packages/stack/src/plugins/ai-chat/history.cjs +47 -0
- package/dist/packages/stack/src/plugins/ai-chat/history.mjs +45 -0
- package/dist/plugins/ai-chat/api/index.d.cts +2 -2
- package/dist/plugins/ai-chat/api/index.d.mts +2 -2
- package/dist/plugins/ai-chat/api/index.d.ts +2 -2
- package/dist/plugins/ai-chat/client/components/index.d.cts +1 -1
- package/dist/plugins/ai-chat/client/components/index.d.mts +1 -1
- package/dist/plugins/ai-chat/client/components/index.d.ts +1 -1
- package/dist/plugins/ai-chat/client/hooks/index.d.cts +2 -2
- package/dist/plugins/ai-chat/client/hooks/index.d.mts +2 -2
- package/dist/plugins/ai-chat/client/hooks/index.d.ts +2 -2
- package/dist/plugins/ai-chat/client/index.d.cts +3 -3
- package/dist/plugins/ai-chat/client/index.d.mts +3 -3
- package/dist/plugins/ai-chat/client/index.d.ts +3 -3
- package/dist/plugins/ai-chat/query-keys.d.cts +1 -1
- package/dist/plugins/ai-chat/query-keys.d.mts +1 -1
- package/dist/plugins/ai-chat/query-keys.d.ts +1 -1
- package/dist/plugins/blog/api/index.d.cts +2 -2
- package/dist/plugins/blog/api/index.d.mts +2 -2
- package/dist/plugins/blog/api/index.d.ts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.cts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.mts +2 -2
- package/dist/plugins/blog/client/hooks/index.d.ts +2 -2
- package/dist/plugins/blog/client/index.d.cts +2 -2
- package/dist/plugins/blog/client/index.d.mts +2 -2
- package/dist/plugins/blog/client/index.d.ts +2 -2
- package/dist/plugins/blog/query-keys.d.cts +2 -2
- package/dist/plugins/blog/query-keys.d.mts +2 -2
- package/dist/plugins/blog/query-keys.d.ts +2 -2
- package/dist/plugins/form-builder/api/index.d.cts +1 -1
- package/dist/plugins/form-builder/api/index.d.mts +1 -1
- package/dist/plugins/form-builder/api/index.d.ts +1 -1
- package/dist/plugins/form-builder/client/hooks/index.d.cts +1 -1
- package/dist/plugins/form-builder/client/hooks/index.d.mts +1 -1
- package/dist/plugins/form-builder/client/hooks/index.d.ts +1 -1
- package/dist/plugins/form-builder/query-keys.d.cts +1 -1
- package/dist/plugins/form-builder/query-keys.d.mts +1 -1
- package/dist/plugins/form-builder/query-keys.d.ts +1 -1
- package/dist/shared/{stack.CjCSDfWy.d.mts → stack.94kN3pEK.d.mts} +5 -5
- package/dist/shared/{stack.C4zspX0T.d.ts → stack.B2EYZyh_.d.ts} +5 -2
- package/dist/shared/{stack.BGzgFx3R.d.cts → stack.BTStHVTy.d.cts} +5 -5
- package/dist/shared/{stack.DhZtP2JZ.d.mts → stack.BggTHKlo.d.mts} +37 -37
- package/dist/shared/{stack.DDQlE2Pm.d.cts → stack.Cei2T2gc.d.cts} +1 -0
- package/dist/shared/{stack.DNw6Hv5o.d.cts → stack.CtrnoQgH.d.cts} +37 -37
- package/dist/shared/{stack.tAUeRAtd.d.mts → stack.DZRZAzcx.d.mts} +1 -0
- package/dist/shared/{stack.Dpg_JjIo.d.ts → stack.DfrRYOIl.d.ts} +5 -5
- package/dist/shared/{stack.Ps3MDhTC.d.mts → stack.DivuAnNI.d.mts} +5 -2
- package/dist/shared/{stack.DlehgNZZ.d.ts → stack.byjorQi5.d.ts} +1 -0
- package/dist/shared/{stack.ymWsP4HZ.d.cts → stack.fF273jws.d.cts} +5 -2
- package/dist/shared/{stack.Clil75lW.d.ts → stack.lPKwd_v0.d.ts} +37 -37
- package/package.json +1 -1
- package/src/plugins/ai-chat/__tests__/authorization.test.ts +56 -30
- package/src/plugins/ai-chat/__tests__/classnames-overrides.test.tsx +25 -0
- package/src/plugins/ai-chat/__tests__/client-sweep.test.tsx +101 -0
- package/src/plugins/ai-chat/__tests__/persisted-compaction.test.ts +819 -0
- package/src/plugins/ai-chat/api/compaction.ts +107 -40
- package/src/plugins/ai-chat/api/context-checkpoint.ts +66 -0
- package/src/plugins/ai-chat/api/getters.ts +2 -0
- package/src/plugins/ai-chat/api/operations.ts +361 -99
- package/src/plugins/ai-chat/api/plugin.ts +5 -8
- package/src/plugins/ai-chat/api/serializers.ts +1 -0
- package/src/plugins/ai-chat/client/components/chat-interface.tsx +43 -22
- package/src/plugins/ai-chat/client/components/chat-message.tsx +32 -2
- package/src/plugins/ai-chat/client/localization/index.ts +1 -0
- package/src/plugins/ai-chat/db.ts +8 -0
- package/src/plugins/ai-chat/history.ts +68 -0
- package/src/plugins/ai-chat/types.ts +2 -0
- package/dist/shared/{stack.CGmh-Lpe.d.ts → stack.BuxUQqL8.d.ts} +12 -12
- package/dist/shared/{stack.Csi-4Mgz.d.cts → stack.CKc_Bais.d.cts} +7 -7
- package/dist/shared/{stack.Csi-4Mgz.d.mts → stack.CKc_Bais.d.mts} +7 -7
- package/dist/shared/{stack.Csi-4Mgz.d.ts → stack.CKc_Bais.d.ts} +7 -7
- package/dist/shared/{stack.zch70Loj.d.cts → stack.CX83YNuS.d.cts} +12 -12
- package/dist/shared/{stack.Doy2_wgl.d.mts → stack.Egr3RWhA.d.mts} +12 -12
|
@@ -2,7 +2,7 @@ import * as _tanstack_react_query from '@tanstack/react-query';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { ResourceFormConfig, ResourceFormResult } from '@btst/stack/plugins/client/hooks';
|
|
4
4
|
import { S as SerializedPost, a as SerializedTag } from './stack.Dsatjh0B.js';
|
|
5
|
-
import { c as createPostSchema, u as updatePostSchema } from './stack.
|
|
5
|
+
import { c as createPostSchema, u as updatePostSchema } from './stack.CKc_Bais.js';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Options for the usePosts hook
|
|
@@ -136,6 +136,10 @@ declare function useSuspenseTags(): {
|
|
|
136
136
|
declare function usePostForm<TValues>(config: ResourceFormConfig<TValues, SerializedPost | null, SerializedPost | null>): ResourceFormResult<TValues, SerializedPost | null, SerializedPost | null>;
|
|
137
137
|
/** Create a new post */
|
|
138
138
|
declare function useCreatePost(): _tanstack_react_query.UseMutationResult<SerializedPost | null, Error, {
|
|
139
|
+
title: string;
|
|
140
|
+
content: string;
|
|
141
|
+
excerpt: string;
|
|
142
|
+
published: boolean;
|
|
139
143
|
tags: ({
|
|
140
144
|
name: string;
|
|
141
145
|
} | {
|
|
@@ -143,10 +147,6 @@ declare function useCreatePost(): _tanstack_react_query.UseMutationResult<Serial
|
|
|
143
147
|
name: string;
|
|
144
148
|
slug: string;
|
|
145
149
|
})[];
|
|
146
|
-
title: string;
|
|
147
|
-
content: string;
|
|
148
|
-
excerpt: string;
|
|
149
|
-
published: boolean;
|
|
150
150
|
slug?: string | undefined;
|
|
151
151
|
publishedAt?: Date | undefined;
|
|
152
152
|
createdAt?: Date | undefined;
|
|
@@ -31,6 +31,8 @@ type Message = {
|
|
|
31
31
|
conversationId: string;
|
|
32
32
|
role: "system" | "user" | "assistant" | "data";
|
|
33
33
|
content: string;
|
|
34
|
+
/** True when generation stopped before a successful completion. */
|
|
35
|
+
interrupted?: boolean | null;
|
|
34
36
|
createdAt: Date;
|
|
35
37
|
};
|
|
36
38
|
type SerializedConversation = Omit<Conversation, "createdAt" | "updatedAt"> & {
|
|
@@ -55,7 +57,7 @@ interface AiChatPageContentConfig {
|
|
|
55
57
|
description?: string;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
/** Opt-in context compaction for
|
|
60
|
+
/** Opt-in context compaction for text-only conversations. */
|
|
59
61
|
interface AiChatCompactionConfig {
|
|
60
62
|
/** Actual context window. Configure the model's output limit at or below 20% of this. */
|
|
61
63
|
contextWindowTokens: number;
|
|
@@ -228,7 +230,7 @@ interface AiChatBackendConfig {
|
|
|
228
230
|
*/
|
|
229
231
|
access?: AiChatAccess;
|
|
230
232
|
systemPrompt?: string;
|
|
231
|
-
/** Automatically summarize older context in
|
|
233
|
+
/** Automatically summarize older context in text chats; off by default. */
|
|
232
234
|
compaction?: AiChatCompactionConfig;
|
|
233
235
|
tools?: Record<string, Tool>;
|
|
234
236
|
/** Enable the standard readPage server tool for full page content. Reserves the readPage tool name. */
|
|
@@ -319,6 +321,7 @@ declare const aiChatBackendPlugin: <TTools extends Record<string, Tool> = Record
|
|
|
319
321
|
readonly conversationId: string;
|
|
320
322
|
readonly role: "system" | "user" | "assistant" | "data";
|
|
321
323
|
readonly content: string;
|
|
324
|
+
readonly interrupted?: boolean | null | undefined;
|
|
322
325
|
readonly createdAt: string;
|
|
323
326
|
}[];
|
|
324
327
|
}>;
|
|
@@ -31,6 +31,8 @@ type Message = {
|
|
|
31
31
|
conversationId: string;
|
|
32
32
|
role: "system" | "user" | "assistant" | "data";
|
|
33
33
|
content: string;
|
|
34
|
+
/** True when generation stopped before a successful completion. */
|
|
35
|
+
interrupted?: boolean | null;
|
|
34
36
|
createdAt: Date;
|
|
35
37
|
};
|
|
36
38
|
type SerializedConversation = Omit<Conversation, "createdAt" | "updatedAt"> & {
|
|
@@ -55,7 +57,7 @@ interface AiChatPageContentConfig {
|
|
|
55
57
|
description?: string;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
|
-
/** Opt-in context compaction for
|
|
60
|
+
/** Opt-in context compaction for text-only conversations. */
|
|
59
61
|
interface AiChatCompactionConfig {
|
|
60
62
|
/** Actual context window. Configure the model's output limit at or below 20% of this. */
|
|
61
63
|
contextWindowTokens: number;
|
|
@@ -228,7 +230,7 @@ interface AiChatBackendConfig {
|
|
|
228
230
|
*/
|
|
229
231
|
access?: AiChatAccess;
|
|
230
232
|
systemPrompt?: string;
|
|
231
|
-
/** Automatically summarize older context in
|
|
233
|
+
/** Automatically summarize older context in text chats; off by default. */
|
|
232
234
|
compaction?: AiChatCompactionConfig;
|
|
233
235
|
tools?: Record<string, Tool>;
|
|
234
236
|
/** Enable the standard readPage server tool for full page content. Reserves the readPage tool name. */
|
|
@@ -319,6 +321,7 @@ declare const aiChatBackendPlugin: <TTools extends Record<string, Tool> = Record
|
|
|
319
321
|
readonly conversationId: string;
|
|
320
322
|
readonly role: "system" | "user" | "assistant" | "data";
|
|
321
323
|
readonly content: string;
|
|
324
|
+
readonly interrupted?: boolean | null | undefined;
|
|
322
325
|
readonly createdAt: string;
|
|
323
326
|
}[];
|
|
324
327
|
}>;
|
|
@@ -2,7 +2,7 @@ import * as _btst_stack_plugins_client from '@btst/stack/plugins/client';
|
|
|
2
2
|
import { createApiClient } from '@btst/stack/plugins/client';
|
|
3
3
|
import * as zod from 'zod';
|
|
4
4
|
import { z } from 'zod';
|
|
5
|
-
import { c as createPostSchema, u as updatePostSchema } from './stack.
|
|
5
|
+
import { c as createPostSchema, u as updatePostSchema } from './stack.CKc_Bais.js';
|
|
6
6
|
import { P as Post, T as Tag, S as SerializedPost, a as SerializedTag } from './stack.Dsatjh0B.js';
|
|
7
7
|
import * as _btst_stack_plugins_api from '@btst/stack/plugins/api';
|
|
8
8
|
import { DeepReadonly, OperationContext, OperationErrorContext } from '@btst/stack/plugins/api';
|
|
@@ -89,6 +89,12 @@ declare const NextPreviousPostsQuerySchema: z.ZodObject<{
|
|
|
89
89
|
date: z.ZodPipe<z.ZodCoercedDate<unknown>, z.ZodTransform<string, Date>>;
|
|
90
90
|
}, z.core.$strip>;
|
|
91
91
|
declare const CreatePostOperationInputSchema: z.ZodObject<{
|
|
92
|
+
slug: z.ZodOptional<z.ZodString>;
|
|
93
|
+
title: z.ZodString;
|
|
94
|
+
content: z.ZodString;
|
|
95
|
+
excerpt: z.ZodString;
|
|
96
|
+
image: z.ZodOptional<z.ZodString>;
|
|
97
|
+
published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
92
98
|
tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
93
99
|
name: z.ZodString;
|
|
94
100
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -96,12 +102,6 @@ declare const CreatePostOperationInputSchema: z.ZodObject<{
|
|
|
96
102
|
name: z.ZodString;
|
|
97
103
|
slug: z.ZodString;
|
|
98
104
|
}, z.core.$strip>]>>>>;
|
|
99
|
-
slug: z.ZodOptional<z.ZodString>;
|
|
100
|
-
title: z.ZodString;
|
|
101
|
-
content: z.ZodString;
|
|
102
|
-
excerpt: z.ZodString;
|
|
103
|
-
image: z.ZodOptional<z.ZodString>;
|
|
104
|
-
published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
105
105
|
publishedAt: z.ZodOptional<z.ZodPipe<z.ZodCoercedDate<unknown>, z.ZodTransform<string, Date>>>;
|
|
106
106
|
createdAt: z.ZodOptional<z.ZodPipe<z.ZodCoercedDate<unknown>, z.ZodTransform<string, Date>>>;
|
|
107
107
|
updatedAt: z.ZodOptional<z.ZodPipe<z.ZodCoercedDate<unknown>, z.ZodTransform<string, Date>>>;
|
|
@@ -109,6 +109,12 @@ declare const CreatePostOperationInputSchema: z.ZodObject<{
|
|
|
109
109
|
declare const UpdatePostOperationInputSchema: z.ZodObject<{
|
|
110
110
|
id: z.ZodString;
|
|
111
111
|
data: z.ZodObject<{
|
|
112
|
+
slug: z.ZodString;
|
|
113
|
+
title: z.ZodString;
|
|
114
|
+
content: z.ZodString;
|
|
115
|
+
excerpt: z.ZodString;
|
|
116
|
+
image: z.ZodOptional<z.ZodString>;
|
|
117
|
+
published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
112
118
|
tags: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodUnion<readonly [z.ZodObject<{
|
|
113
119
|
name: z.ZodString;
|
|
114
120
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -116,12 +122,6 @@ declare const UpdatePostOperationInputSchema: z.ZodObject<{
|
|
|
116
122
|
name: z.ZodString;
|
|
117
123
|
slug: z.ZodString;
|
|
118
124
|
}, z.core.$strip>]>>>>;
|
|
119
|
-
slug: z.ZodString;
|
|
120
|
-
title: z.ZodString;
|
|
121
|
-
content: z.ZodString;
|
|
122
|
-
excerpt: z.ZodString;
|
|
123
|
-
image: z.ZodOptional<z.ZodString>;
|
|
124
|
-
published: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
125
125
|
publishedAt: z.ZodOptional<z.ZodPipe<z.ZodCoercedDate<unknown>, z.ZodTransform<string, Date>>>;
|
|
126
126
|
createdAt: z.ZodOptional<z.ZodPipe<z.ZodCoercedDate<unknown>, z.ZodTransform<string, Date>>>;
|
|
127
127
|
updatedAt: z.ZodOptional<z.ZodPipe<z.ZodCoercedDate<unknown>, z.ZodTransform<string, Date>>>;
|
|
@@ -386,6 +386,9 @@ declare const blogBackendPlugin: (options?: BlogBackendOptions) => _btst_stack_p
|
|
|
386
386
|
title: string;
|
|
387
387
|
content: string;
|
|
388
388
|
excerpt: string;
|
|
389
|
+
slug?: string | undefined;
|
|
390
|
+
image?: string | undefined;
|
|
391
|
+
published?: boolean | undefined;
|
|
389
392
|
tags?: ({
|
|
390
393
|
name: string;
|
|
391
394
|
} | {
|
|
@@ -393,9 +396,6 @@ declare const blogBackendPlugin: (options?: BlogBackendOptions) => _btst_stack_p
|
|
|
393
396
|
name: string;
|
|
394
397
|
slug: string;
|
|
395
398
|
})[] | undefined;
|
|
396
|
-
slug?: string | undefined;
|
|
397
|
-
image?: string | undefined;
|
|
398
|
-
published?: boolean | undefined;
|
|
399
399
|
publishedAt?: unknown;
|
|
400
400
|
createdAt?: unknown;
|
|
401
401
|
updatedAt?: unknown;
|
|
@@ -403,6 +403,9 @@ declare const blogBackendPlugin: (options?: BlogBackendOptions) => _btst_stack_p
|
|
|
403
403
|
title: string;
|
|
404
404
|
content: string;
|
|
405
405
|
excerpt: string;
|
|
406
|
+
slug?: string | undefined;
|
|
407
|
+
image?: string | undefined;
|
|
408
|
+
published?: boolean | undefined;
|
|
406
409
|
tags?: ({
|
|
407
410
|
name: string;
|
|
408
411
|
} | {
|
|
@@ -410,9 +413,6 @@ declare const blogBackendPlugin: (options?: BlogBackendOptions) => _btst_stack_p
|
|
|
410
413
|
name: string;
|
|
411
414
|
slug: string;
|
|
412
415
|
})[] | undefined;
|
|
413
|
-
slug?: string | undefined;
|
|
414
|
-
image?: string | undefined;
|
|
415
|
-
published?: boolean | undefined;
|
|
416
416
|
publishedAt?: unknown;
|
|
417
417
|
createdAt?: unknown;
|
|
418
418
|
updatedAt?: unknown;
|
|
@@ -448,6 +448,8 @@ declare const blogBackendPlugin: (options?: BlogBackendOptions) => _btst_stack_p
|
|
|
448
448
|
title: string;
|
|
449
449
|
content: string;
|
|
450
450
|
excerpt: string;
|
|
451
|
+
image?: string | undefined;
|
|
452
|
+
published?: boolean | undefined;
|
|
451
453
|
tags?: ({
|
|
452
454
|
name: string;
|
|
453
455
|
} | {
|
|
@@ -455,8 +457,6 @@ declare const blogBackendPlugin: (options?: BlogBackendOptions) => _btst_stack_p
|
|
|
455
457
|
name: string;
|
|
456
458
|
slug: string;
|
|
457
459
|
})[] | undefined;
|
|
458
|
-
image?: string | undefined;
|
|
459
|
-
published?: boolean | undefined;
|
|
460
460
|
publishedAt?: unknown;
|
|
461
461
|
createdAt?: unknown;
|
|
462
462
|
updatedAt?: unknown;
|
|
@@ -465,6 +465,8 @@ declare const blogBackendPlugin: (options?: BlogBackendOptions) => _btst_stack_p
|
|
|
465
465
|
title: string;
|
|
466
466
|
content: string;
|
|
467
467
|
excerpt: string;
|
|
468
|
+
image?: string | undefined;
|
|
469
|
+
published?: boolean | undefined;
|
|
468
470
|
tags?: ({
|
|
469
471
|
name: string;
|
|
470
472
|
} | {
|
|
@@ -472,8 +474,6 @@ declare const blogBackendPlugin: (options?: BlogBackendOptions) => _btst_stack_p
|
|
|
472
474
|
name: string;
|
|
473
475
|
slug: string;
|
|
474
476
|
})[] | undefined;
|
|
475
|
-
image?: string | undefined;
|
|
476
|
-
published?: boolean | undefined;
|
|
477
477
|
publishedAt?: unknown;
|
|
478
478
|
createdAt?: unknown;
|
|
479
479
|
updatedAt?: unknown;
|
|
@@ -627,6 +627,12 @@ declare const blogBackendPlugin: (options?: BlogBackendOptions) => _btst_stack_p
|
|
|
627
627
|
total: number;
|
|
628
628
|
}, false>;
|
|
629
629
|
readonly createPost: _btst_stack_plugins_api.Operation<zod.ZodObject<{
|
|
630
|
+
slug: zod.ZodOptional<zod.ZodString>;
|
|
631
|
+
title: zod.ZodString;
|
|
632
|
+
content: zod.ZodString;
|
|
633
|
+
excerpt: zod.ZodString;
|
|
634
|
+
image: zod.ZodOptional<zod.ZodString>;
|
|
635
|
+
published: zod.ZodDefault<zod.ZodOptional<zod.ZodBoolean>>;
|
|
630
636
|
tags: zod.ZodDefault<zod.ZodOptional<zod.ZodArray<zod.ZodUnion<readonly [zod.ZodObject<{
|
|
631
637
|
name: zod.ZodString;
|
|
632
638
|
}, zod_v4_core.$strip>, zod.ZodObject<{
|
|
@@ -634,12 +640,6 @@ declare const blogBackendPlugin: (options?: BlogBackendOptions) => _btst_stack_p
|
|
|
634
640
|
name: zod.ZodString;
|
|
635
641
|
slug: zod.ZodString;
|
|
636
642
|
}, zod_v4_core.$strip>]>>>>;
|
|
637
|
-
slug: zod.ZodOptional<zod.ZodString>;
|
|
638
|
-
title: zod.ZodString;
|
|
639
|
-
content: zod.ZodString;
|
|
640
|
-
excerpt: zod.ZodString;
|
|
641
|
-
image: zod.ZodOptional<zod.ZodString>;
|
|
642
|
-
published: zod.ZodDefault<zod.ZodOptional<zod.ZodBoolean>>;
|
|
643
643
|
publishedAt: zod.ZodOptional<zod.ZodPipe<zod.ZodCoercedDate<unknown>, zod.ZodTransform<string, Date>>>;
|
|
644
644
|
createdAt: zod.ZodOptional<zod.ZodPipe<zod.ZodCoercedDate<unknown>, zod.ZodTransform<string, Date>>>;
|
|
645
645
|
updatedAt: zod.ZodOptional<zod.ZodPipe<zod.ZodCoercedDate<unknown>, zod.ZodTransform<string, Date>>>;
|
|
@@ -673,6 +673,12 @@ declare const blogBackendPlugin: (options?: BlogBackendOptions) => _btst_stack_p
|
|
|
673
673
|
readonly updatePost: _btst_stack_plugins_api.Operation<zod.ZodObject<{
|
|
674
674
|
id: zod.ZodString;
|
|
675
675
|
data: zod.ZodObject<{
|
|
676
|
+
slug: zod.ZodString;
|
|
677
|
+
title: zod.ZodString;
|
|
678
|
+
content: zod.ZodString;
|
|
679
|
+
excerpt: zod.ZodString;
|
|
680
|
+
image: zod.ZodOptional<zod.ZodString>;
|
|
681
|
+
published: zod.ZodDefault<zod.ZodOptional<zod.ZodBoolean>>;
|
|
676
682
|
tags: zod.ZodDefault<zod.ZodOptional<zod.ZodArray<zod.ZodUnion<readonly [zod.ZodObject<{
|
|
677
683
|
name: zod.ZodString;
|
|
678
684
|
}, zod_v4_core.$strip>, zod.ZodObject<{
|
|
@@ -680,12 +686,6 @@ declare const blogBackendPlugin: (options?: BlogBackendOptions) => _btst_stack_p
|
|
|
680
686
|
name: zod.ZodString;
|
|
681
687
|
slug: zod.ZodString;
|
|
682
688
|
}, zod_v4_core.$strip>]>>>>;
|
|
683
|
-
slug: zod.ZodString;
|
|
684
|
-
title: zod.ZodString;
|
|
685
|
-
content: zod.ZodString;
|
|
686
|
-
excerpt: zod.ZodString;
|
|
687
|
-
image: zod.ZodOptional<zod.ZodString>;
|
|
688
|
-
published: zod.ZodDefault<zod.ZodOptional<zod.ZodBoolean>>;
|
|
689
689
|
publishedAt: zod.ZodOptional<zod.ZodPipe<zod.ZodCoercedDate<unknown>, zod.ZodTransform<string, Date>>>;
|
|
690
690
|
createdAt: zod.ZodOptional<zod.ZodPipe<zod.ZodCoercedDate<unknown>, zod.ZodTransform<string, Date>>>;
|
|
691
691
|
updatedAt: zod.ZodOptional<zod.ZodPipe<zod.ZodCoercedDate<unknown>, zod.ZodTransform<string, Date>>>;
|
|
@@ -900,8 +900,8 @@ declare const blogResources: {
|
|
|
900
900
|
method: "POST";
|
|
901
901
|
input: (vars: PostCreateInput) => {
|
|
902
902
|
body: {
|
|
903
|
-
title: string;
|
|
904
903
|
published: boolean;
|
|
904
|
+
title: string;
|
|
905
905
|
content: string;
|
|
906
906
|
excerpt: string;
|
|
907
907
|
tags: ({
|
|
@@ -1072,8 +1072,8 @@ declare function createBlogQueryKeys(client: ReturnType<typeof createApiClient<B
|
|
|
1072
1072
|
method: "POST";
|
|
1073
1073
|
input: (vars: PostCreateInput) => {
|
|
1074
1074
|
body: {
|
|
1075
|
-
title: string;
|
|
1076
1075
|
published: boolean;
|
|
1076
|
+
title: string;
|
|
1077
1077
|
content: string;
|
|
1078
1078
|
excerpt: string;
|
|
1079
1079
|
tags: ({
|
package/package.json
CHANGED
|
@@ -14,18 +14,53 @@ import {
|
|
|
14
14
|
import { aiChatPermissions } from "../permissions";
|
|
15
15
|
import type { Conversation, Message } from "../types";
|
|
16
16
|
|
|
17
|
-
const { streamText } = vi.hoisted(() => ({
|
|
17
|
+
const { streamText, completions } = vi.hoisted(() => ({
|
|
18
|
+
completions: [] as Array<
|
|
19
|
+
NonNullable<
|
|
20
|
+
Parameters<typeof import("ai").createUIMessageStream>[0]["onFinish"]
|
|
21
|
+
>
|
|
22
|
+
>,
|
|
18
23
|
streamText: vi.fn(() => ({
|
|
24
|
+
toUIMessageStream: () =>
|
|
25
|
+
new ReadableStream({
|
|
26
|
+
start(controller) {
|
|
27
|
+
controller.close();
|
|
28
|
+
},
|
|
29
|
+
}),
|
|
19
30
|
toUIMessageStreamResponse: () => new Response("stream", { status: 200 }),
|
|
20
31
|
})),
|
|
21
32
|
}));
|
|
22
33
|
|
|
23
|
-
vi.mock("ai", async (importOriginal) =>
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
34
|
+
vi.mock("ai", async (importOriginal) => {
|
|
35
|
+
const original = await importOriginal<typeof import("ai")>();
|
|
36
|
+
return {
|
|
37
|
+
...original,
|
|
38
|
+
createUIMessageStream: (
|
|
39
|
+
options: Parameters<typeof original.createUIMessageStream>[0],
|
|
40
|
+
) => {
|
|
41
|
+
if (options.onFinish) completions.push(options.onFinish);
|
|
42
|
+
return original.createUIMessageStream(options);
|
|
43
|
+
},
|
|
44
|
+
convertToModelMessages: (messages: unknown) => messages,
|
|
45
|
+
stepCountIs: () => () => false,
|
|
46
|
+
streamText,
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
function completion(text: string) {
|
|
51
|
+
const responseMessage = {
|
|
52
|
+
id: "server-response",
|
|
53
|
+
role: "assistant" as const,
|
|
54
|
+
parts: [{ type: "text" as const, text }],
|
|
55
|
+
};
|
|
56
|
+
return {
|
|
57
|
+
responseMessage,
|
|
58
|
+
messages: [responseMessage],
|
|
59
|
+
isAborted: false,
|
|
60
|
+
isContinuation: false,
|
|
61
|
+
finishReason: "stop" as const,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
29
64
|
|
|
30
65
|
type Identity = { id: string; role: "user" | "admin" };
|
|
31
66
|
|
|
@@ -173,7 +208,10 @@ async function seedMessage(
|
|
|
173
208
|
}
|
|
174
209
|
|
|
175
210
|
describe("AI Chat operation authorization", () => {
|
|
176
|
-
beforeEach(() =>
|
|
211
|
+
beforeEach(() => {
|
|
212
|
+
streamText.mockClear();
|
|
213
|
+
completions.length = 0;
|
|
214
|
+
});
|
|
177
215
|
|
|
178
216
|
it("publishes one executable inventory for every maintained transport", () => {
|
|
179
217
|
const plugin = aiChatBackendPlugin({ model });
|
|
@@ -815,15 +853,7 @@ describe("AI Chat operation authorization", () => {
|
|
|
815
853
|
await app
|
|
816
854
|
.forRequest(request("/chat", { identity: owner }))
|
|
817
855
|
.operations.aiChat.startStream(messageBody);
|
|
818
|
-
const finish =
|
|
819
|
-
streamText.mock.calls as unknown as Array<
|
|
820
|
-
[
|
|
821
|
-
{
|
|
822
|
-
onFinish: (completion: { text: string }) => Promise<void>;
|
|
823
|
-
},
|
|
824
|
-
]
|
|
825
|
-
>
|
|
826
|
-
)[0]?.[0].onFinish;
|
|
856
|
+
const finish = completions[0];
|
|
827
857
|
expect(finish).toBeDefined();
|
|
828
858
|
if (!finish) throw new Error("Missing stream completion callback");
|
|
829
859
|
const adapterConfig = app.adapter.options?.adapterConfig;
|
|
@@ -831,7 +861,7 @@ describe("AI Chat operation authorization", () => {
|
|
|
831
861
|
if (!adapterConfig) throw new Error("Missing adapter config");
|
|
832
862
|
adapterConfig.transaction = false;
|
|
833
863
|
|
|
834
|
-
await finish(
|
|
864
|
+
await finish(completion("unsafe answer"));
|
|
835
865
|
|
|
836
866
|
expect(
|
|
837
867
|
await app.adapter.count({
|
|
@@ -876,6 +906,7 @@ describe("AI Chat operation authorization", () => {
|
|
|
876
906
|
identity: z.object({ id: z.string(), role: z.enum(["user", "admin"]) }),
|
|
877
907
|
permissions: [aiChatPermissions] as const,
|
|
878
908
|
rules: ({ aiChat }) => [
|
|
909
|
+
aiChat.conversation.read.allow(),
|
|
879
910
|
aiChat.stream.start.allow(),
|
|
880
911
|
aiChat.message.retry.when(({ facts }) => {
|
|
881
912
|
observed.push({ intent: "retry", messageId: facts.messageId });
|
|
@@ -959,6 +990,7 @@ describe("AI Chat operation authorization", () => {
|
|
|
959
990
|
identity: z.object({ id: z.string(), role: z.enum(["user", "admin"]) }),
|
|
960
991
|
permissions: [aiChatPermissions] as const,
|
|
961
992
|
rules: ({ aiChat }) => [
|
|
993
|
+
aiChat.conversation.read.allow(),
|
|
962
994
|
aiChat.stream.start.allow(),
|
|
963
995
|
aiChat.message.retry.allow(),
|
|
964
996
|
aiChat.message.edit.when(() => false),
|
|
@@ -1030,6 +1062,7 @@ describe("AI Chat operation authorization", () => {
|
|
|
1030
1062
|
identity: z.object({ id: z.string(), role: z.enum(["user", "admin"]) }),
|
|
1031
1063
|
permissions: [aiChatPermissions] as const,
|
|
1032
1064
|
rules: ({ aiChat }) => [
|
|
1065
|
+
aiChat.conversation.read.allow(),
|
|
1033
1066
|
aiChat.stream.start.allow(),
|
|
1034
1067
|
aiChat.message.send.allow(),
|
|
1035
1068
|
aiChat.message.retry.when(({ facts }) => observedRetry(facts)),
|
|
@@ -1490,7 +1523,7 @@ describe("AI Chat operation authorization", () => {
|
|
|
1490
1523
|
expect(before).not.toHaveBeenCalled();
|
|
1491
1524
|
expect(streamText).not.toHaveBeenCalled();
|
|
1492
1525
|
expect(await app.adapter.count({ model: "message" })).toBe(0);
|
|
1493
|
-
expect(onError).
|
|
1526
|
+
expect(onError).not.toHaveBeenCalled();
|
|
1494
1527
|
});
|
|
1495
1528
|
|
|
1496
1529
|
it("rechecks a missing requested conversation before entering hooks", async () => {
|
|
@@ -1962,20 +1995,13 @@ describe("AI Chat operation authorization", () => {
|
|
|
1962
1995
|
await app
|
|
1963
1996
|
.forRequest(request("/first", { identity: owner }))
|
|
1964
1997
|
.operations.aiChat.startStream(input);
|
|
1965
|
-
const
|
|
1966
|
-
[
|
|
1967
|
-
{
|
|
1968
|
-
onFinish: (completion: { text: string }) => Promise<void>;
|
|
1969
|
-
},
|
|
1970
|
-
]
|
|
1971
|
-
>;
|
|
1972
|
-
const firstFinish = streamCalls[0]?.[0].onFinish;
|
|
1998
|
+
const firstFinish = completions[0];
|
|
1973
1999
|
await app
|
|
1974
2000
|
.forRequest(request("/second", { identity: owner }))
|
|
1975
2001
|
.operations.aiChat.startStream(input);
|
|
1976
|
-
const secondFinish =
|
|
2002
|
+
const secondFinish = completions[1];
|
|
1977
2003
|
|
|
1978
|
-
await firstFinish?.(
|
|
2004
|
+
await firstFinish?.(completion("stale answer"));
|
|
1979
2005
|
expect(
|
|
1980
2006
|
await app.adapter.count({
|
|
1981
2007
|
model: "message",
|
|
@@ -1984,7 +2010,7 @@ describe("AI Chat operation authorization", () => {
|
|
|
1984
2010
|
).toBe(0);
|
|
1985
2011
|
expect(onErrorChat).toHaveBeenCalledOnce();
|
|
1986
2012
|
|
|
1987
|
-
await secondFinish?.(
|
|
2013
|
+
await secondFinish?.(completion("current answer"));
|
|
1988
2014
|
const assistants = await app.adapter.findMany<Message>({
|
|
1989
2015
|
model: "message",
|
|
1990
2016
|
where: [{ field: "role", value: "assistant", operator: "eq" }],
|
|
@@ -161,6 +161,31 @@ function query(selector: string): HTMLElement {
|
|
|
161
161
|
}
|
|
162
162
|
|
|
163
163
|
describe("ai-chat classNames overrides (issue #36)", () => {
|
|
164
|
+
it("labels interrupted responses and stops unfinished tool indicators", async () => {
|
|
165
|
+
await render(
|
|
166
|
+
<ChatMessage
|
|
167
|
+
message={{
|
|
168
|
+
...assistantMessage,
|
|
169
|
+
metadata: { interrupted: true },
|
|
170
|
+
parts: [
|
|
171
|
+
...assistantMessage.parts,
|
|
172
|
+
{
|
|
173
|
+
type: "tool-inspect",
|
|
174
|
+
toolCallId: "pending",
|
|
175
|
+
state: "input-available",
|
|
176
|
+
input: {},
|
|
177
|
+
},
|
|
178
|
+
],
|
|
179
|
+
}}
|
|
180
|
+
/>,
|
|
181
|
+
);
|
|
182
|
+
expect(query('[data-testid="chat-interrupted"]').textContent).toBe(
|
|
183
|
+
"Response interrupted",
|
|
184
|
+
);
|
|
185
|
+
expect(container.textContent).not.toContain("Executing...");
|
|
186
|
+
expect(container.querySelector(".animate-spin")).toBeNull();
|
|
187
|
+
});
|
|
188
|
+
|
|
164
189
|
it("applies message, userMessage and assistantMessage to a user message", async () => {
|
|
165
190
|
await render(<ChatMessage message={userMessage} />);
|
|
166
191
|
|
|
@@ -301,6 +301,107 @@ function menuItem(text: string) {
|
|
|
301
301
|
}
|
|
302
302
|
|
|
303
303
|
describe("AI Chat permissions", () => {
|
|
304
|
+
it("shows authorized compaction status and ignores browser checkpoints", async () => {
|
|
305
|
+
let chatOptions: any;
|
|
306
|
+
const messages = [
|
|
307
|
+
{ id: "u1", role: "user", parts: [{ type: "text", text: "Hello" }] },
|
|
308
|
+
];
|
|
309
|
+
mocks.useAiChatIdentityPartition.mockReturnValue({
|
|
310
|
+
id: "owner-1",
|
|
311
|
+
role: "user",
|
|
312
|
+
});
|
|
313
|
+
mocks.useChat.mockImplementation((options) => {
|
|
314
|
+
chatOptions = options;
|
|
315
|
+
return {
|
|
316
|
+
messages,
|
|
317
|
+
status: "streaming",
|
|
318
|
+
error: null,
|
|
319
|
+
sendMessage: vi.fn(),
|
|
320
|
+
setMessages: vi.fn(),
|
|
321
|
+
regenerate: vi.fn(),
|
|
322
|
+
addToolOutput: vi.fn(),
|
|
323
|
+
stop: vi.fn(),
|
|
324
|
+
};
|
|
325
|
+
});
|
|
326
|
+
await render(<ChatInterface id={conversation.id} />);
|
|
327
|
+
await act(async () => {
|
|
328
|
+
chatOptions.transport.prepareSendMessagesRequest({ messages });
|
|
329
|
+
chatOptions.onData({
|
|
330
|
+
type: "data-context-status",
|
|
331
|
+
data: { compacting: true },
|
|
332
|
+
});
|
|
333
|
+
chatOptions.onData({
|
|
334
|
+
type: "data-context-checkpoint",
|
|
335
|
+
data: { summary: "Do not trust this", throughMessageId: "u1" },
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
expect(container.textContent).toContain("Summarizing");
|
|
339
|
+
let next: any;
|
|
340
|
+
await act(async () => {
|
|
341
|
+
next = chatOptions.transport.prepareSendMessagesRequest({ messages });
|
|
342
|
+
});
|
|
343
|
+
expect(next.body.messages).toEqual(messages);
|
|
344
|
+
expect(next.body).not.toHaveProperty("contextSummary");
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
it.each([
|
|
348
|
+
{ interrupted: true, state: "output-available" },
|
|
349
|
+
{ interrupted: false, state: "input-available" },
|
|
350
|
+
{ interrupted: false, state: "input-streaming" },
|
|
351
|
+
])(
|
|
352
|
+
"restores interrupted or abandoned $state tools without a live callback",
|
|
353
|
+
async ({ interrupted, state }) => {
|
|
354
|
+
const setMessages = vi.fn();
|
|
355
|
+
mocks.useChat.mockReturnValue({
|
|
356
|
+
messages: [],
|
|
357
|
+
status: "ready",
|
|
358
|
+
error: null,
|
|
359
|
+
sendMessage: vi.fn(),
|
|
360
|
+
setMessages,
|
|
361
|
+
regenerate: vi.fn(),
|
|
362
|
+
addToolOutput: vi.fn(),
|
|
363
|
+
stop: vi.fn(),
|
|
364
|
+
});
|
|
365
|
+
const parts = [
|
|
366
|
+
{ type: "text", text: "Partial response" },
|
|
367
|
+
{
|
|
368
|
+
type: "tool-inspect",
|
|
369
|
+
toolCallId: "call",
|
|
370
|
+
state,
|
|
371
|
+
input: {},
|
|
372
|
+
...(state === "output-available" ? { output: "saved result" } : {}),
|
|
373
|
+
},
|
|
374
|
+
];
|
|
375
|
+
mocks.useConversation.mockReturnValue({
|
|
376
|
+
conversation: {
|
|
377
|
+
...conversation,
|
|
378
|
+
messages: [
|
|
379
|
+
{
|
|
380
|
+
id: "answer",
|
|
381
|
+
conversationId: conversation.id,
|
|
382
|
+
role: "assistant",
|
|
383
|
+
content: JSON.stringify(parts),
|
|
384
|
+
interrupted,
|
|
385
|
+
createdAt: conversation.createdAt,
|
|
386
|
+
},
|
|
387
|
+
],
|
|
388
|
+
},
|
|
389
|
+
isLoading: false,
|
|
390
|
+
error: null,
|
|
391
|
+
refetch: vi.fn(),
|
|
392
|
+
});
|
|
393
|
+
await render(<ChatInterface id={conversation.id} />);
|
|
394
|
+
expect(setMessages).toHaveBeenCalledWith([
|
|
395
|
+
{
|
|
396
|
+
id: "answer",
|
|
397
|
+
role: "assistant",
|
|
398
|
+
parts,
|
|
399
|
+
metadata: { interrupted: true },
|
|
400
|
+
},
|
|
401
|
+
]);
|
|
402
|
+
},
|
|
403
|
+
);
|
|
404
|
+
|
|
304
405
|
it("uses the resolved AI Chat endpoint for the browser stream transport", async () => {
|
|
305
406
|
const observeRuntime = vi.fn();
|
|
306
407
|
const stack = createClientStack({
|