@jskit-ai/assistant 0.1.38 → 0.1.40

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 (29) hide show
  1. package/package.descriptor.mjs +84 -319
  2. package/package.json +2 -2
  3. package/src/server/buildTemplateContext.js +39 -14
  4. package/templates/src/pages/assistant/index.vue +2 -2
  5. package/templates/src/pages/settings/assistant/index.vue +7 -0
  6. package/test/buildTemplateContext.test.js +42 -27
  7. package/test/packageDescriptor.test.js +26 -55
  8. package/test/templateContracts.test.js +8 -6
  9. package/templates/migrations/assistant_config_initial.cjs +0 -25
  10. package/templates/migrations/assistant_transcripts_initial.cjs +0 -56
  11. package/templates/src/local-package/client/components/AssistantSettingsClientElement.vue +0 -88
  12. package/templates/src/local-package/client/components/AssistantSurfaceClientElement.vue +0 -10
  13. package/templates/src/local-package/client/composables/useAssistantRuntime.js +0 -754
  14. package/templates/src/local-package/client/index.js +0 -4
  15. package/templates/src/local-package/client/providers/AssistantClientProvider.js +0 -16
  16. package/templates/src/local-package/package.descriptor.mjs +0 -85
  17. package/templates/src/local-package/package.json +0 -11
  18. package/templates/src/local-package/server/AssistantProvider.js +0 -143
  19. package/templates/src/local-package/server/actionIds.js +0 -9
  20. package/templates/src/local-package/server/actions.js +0 -183
  21. package/templates/src/local-package/server/registerRoutes.js +0 -296
  22. package/templates/src/local-package/server/repositories/assistantConfigRepository.js +0 -141
  23. package/templates/src/local-package/server/repositories/conversationsRepository.js +0 -240
  24. package/templates/src/local-package/server/repositories/messagesRepository.js +0 -166
  25. package/templates/src/local-package/server/services/assistantConfigService.js +0 -90
  26. package/templates/src/local-package/server/services/chatService.js +0 -995
  27. package/templates/src/local-package/server/services/transcriptService.js +0 -308
  28. package/templates/src/local-package/shared/assistantRuntimeConfig.js +0 -13
  29. package/templates/src/local-package/shared/index.js +0 -1
@@ -1,308 +0,0 @@
1
- import { AppError, parsePositiveInteger } from "@jskit-ai/kernel/server/runtime";
2
- import { normalizeObject, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
3
- import { normalizeConversationStatus } from "@jskit-ai/assistant-core/shared";
4
- import { assistantRuntimeConfig } from "../../shared/assistantRuntimeConfig.js";
5
-
6
- const DEFAULT_PAGE_SIZE = 20;
7
- const MAX_PAGE_SIZE = 200;
8
- const DEFAULT_MESSAGES_PAGE_SIZE = 200;
9
- const MAX_MESSAGES_PAGE_SIZE = 500;
10
- const DEFAULT_CONVERSATION_TITLE = "New conversation";
11
- const MAX_CONVERSATION_TITLE_LENGTH = 80;
12
-
13
- function resolveWorkspaceId(workspace, { required = false } = {}) {
14
- const workspaceId = parsePositiveInteger(workspace?.id || workspace);
15
- if (!workspaceId && required) {
16
- throw new AppError(409, "Workspace selection required.");
17
- }
18
-
19
- return workspaceId || null;
20
- }
21
-
22
- function resolveActorUserId(user, { required = false } = {}) {
23
- const actorUserId = parsePositiveInteger(user?.id);
24
- if (!actorUserId && required) {
25
- throw new AppError(401, "Authentication required.");
26
- }
27
-
28
- return actorUserId || null;
29
- }
30
-
31
- function normalizePagination(pagination = {}, { defaultPageSize = DEFAULT_PAGE_SIZE, maxPageSize = MAX_PAGE_SIZE } = {}) {
32
- const page = Math.max(1, parsePositiveInteger(pagination.page) || 1);
33
- const pageSize = Math.max(1, Math.min(maxPageSize, parsePositiveInteger(pagination.pageSize) || defaultPageSize));
34
-
35
- return {
36
- page,
37
- pageSize
38
- };
39
- }
40
-
41
- function normalizeCursorPagination(query = {}, { defaultLimit = DEFAULT_PAGE_SIZE, maxLimit = MAX_PAGE_SIZE } = {}) {
42
- const cursor = parsePositiveInteger(query.cursor) || 0;
43
- const limit = Math.max(1, Math.min(maxLimit, parsePositiveInteger(query.limit) || defaultLimit));
44
-
45
- return {
46
- cursor,
47
- limit
48
- };
49
- }
50
-
51
- function deriveConversationTitleFromMessage(contentText) {
52
- const normalized = String(contentText == null ? "" : contentText).replace(/\s+/g, " ").trim();
53
- if (!normalized) {
54
- return "";
55
- }
56
-
57
- return normalized.slice(0, MAX_CONVERSATION_TITLE_LENGTH).trim();
58
- }
59
-
60
- function isDefaultConversationTitle(value) {
61
- return normalizeText(value).toLowerCase() === DEFAULT_CONVERSATION_TITLE.toLowerCase();
62
- }
63
-
64
- function createTranscriptService({ conversationsRepository, messagesRepository } = {}) {
65
- if (!conversationsRepository || !messagesRepository) {
66
- throw new Error("createTranscriptService requires conversationsRepository and messagesRepository.");
67
- }
68
-
69
- function resolveExpectedWorkspaceId(workspace) {
70
- return resolveWorkspaceId(workspace, {
71
- required: assistantRuntimeConfig.runtimeSurfaceRequiresWorkspace === true
72
- });
73
- }
74
-
75
- async function createConversationForTurn(workspace, user, options = {}) {
76
- const workspaceId = resolveExpectedWorkspaceId(workspace);
77
- const actorUserId = resolveActorUserId(user, {
78
- required: true
79
- });
80
- const source = normalizeObject(options);
81
- const conversationId = parsePositiveInteger(source.conversationId);
82
-
83
- if (conversationId) {
84
- const existing = await conversationsRepository.findByIdForActorScope(conversationId, {
85
- workspaceId,
86
- actorUserId
87
- });
88
- if (!existing) {
89
- throw new AppError(404, "Conversation not found.");
90
- }
91
-
92
- if (existing.status !== "active") {
93
- const reopened = await conversationsRepository.updateById(existing.id, {
94
- status: "active",
95
- endedAt: null
96
- });
97
-
98
- return {
99
- conversation: reopened,
100
- created: false
101
- };
102
- }
103
-
104
- return {
105
- conversation: existing,
106
- created: false
107
- };
108
- }
109
-
110
- const createdConversation = await conversationsRepository.create({
111
- workspaceId,
112
- createdByUserId: actorUserId,
113
- title: normalizeText(source.title) || DEFAULT_CONVERSATION_TITLE,
114
- status: "active",
115
- provider: normalizeText(source.provider),
116
- model: normalizeText(source.model),
117
- surfaceId: normalizeText(source.surfaceId).toLowerCase() || assistantRuntimeConfig.runtimeSurfaceId,
118
- metadata: {
119
- firstMessageId: normalizeText(source.messageId)
120
- }
121
- });
122
-
123
- return {
124
- conversation: createdConversation,
125
- created: true
126
- };
127
- }
128
-
129
- async function appendMessage(conversationId, payload = {}, options = {}) {
130
- const numericConversationId = parsePositiveInteger(conversationId);
131
- if (!numericConversationId) {
132
- throw new TypeError("appendMessage requires conversationId.");
133
- }
134
-
135
- const source = normalizeObject(payload);
136
- const context = normalizeObject(options.context);
137
- const actorUserId = parsePositiveInteger(source.actorUserId) || resolveActorUserId(context.actor);
138
- const workspaceId = resolveExpectedWorkspaceId(options.workspace || context.workspace);
139
- const conversation = await conversationsRepository.findByIdForActorScope(numericConversationId, {
140
- workspaceId,
141
- actorUserId
142
- });
143
- if (!conversation) {
144
- throw new AppError(404, "Conversation not found.");
145
- }
146
-
147
- const createdMessage = await messagesRepository.create({
148
- conversationId: numericConversationId,
149
- workspaceId: conversation.workspaceId,
150
- role: normalizeText(source.role).toLowerCase(),
151
- kind: normalizeText(source.kind).toLowerCase() || "chat",
152
- clientMessageSid: normalizeText(source.clientMessageSid),
153
- actorUserId,
154
- contentText: source.contentText == null ? null : String(source.contentText),
155
- metadata: normalizeObject(source.metadata)
156
- });
157
-
158
- await conversationsRepository.incrementMessageCount(numericConversationId, 1);
159
-
160
- const messageRole = normalizeText(source.role).toLowerCase();
161
- const messageKind = normalizeText(source.kind).toLowerCase() || "chat";
162
- if (messageRole === "user" && messageKind === "chat" && isDefaultConversationTitle(conversation.title)) {
163
- const derivedTitle = deriveConversationTitleFromMessage(source.contentText);
164
- if (derivedTitle) {
165
- await conversationsRepository.updateById(numericConversationId, {
166
- title: derivedTitle
167
- });
168
- }
169
- }
170
-
171
- return {
172
- conversationId: numericConversationId,
173
- message: createdMessage
174
- };
175
- }
176
-
177
- async function completeConversation(conversationId, payload = {}, options = {}) {
178
- const numericConversationId = parsePositiveInteger(conversationId);
179
- if (!numericConversationId) {
180
- throw new TypeError("completeConversation requires conversationId.");
181
- }
182
-
183
- const source = normalizeObject(payload);
184
- const context = normalizeObject(options.context);
185
- const actorUserId = resolveActorUserId(context.actor, {
186
- required: true
187
- });
188
- const workspaceId = resolveExpectedWorkspaceId(options.workspace || context.workspace);
189
- const existing = await conversationsRepository.findByIdForActorScope(numericConversationId, {
190
- workspaceId,
191
- actorUserId
192
- });
193
- if (!existing) {
194
- throw new AppError(404, "Conversation not found.");
195
- }
196
-
197
- return conversationsRepository.updateById(numericConversationId, {
198
- status: normalizeConversationStatus(source.status, {
199
- fallback: "completed"
200
- }),
201
- endedAt: source.endedAt || new Date(),
202
- metadata: {
203
- ...normalizeObject(existing.metadata),
204
- ...normalizeObject(source.metadata)
205
- }
206
- });
207
- }
208
-
209
- async function listConversationsForUser(workspace, user, query = {}) {
210
- const workspaceId = resolveExpectedWorkspaceId(workspace);
211
- const actorUserId = resolveActorUserId(user, {
212
- required: true
213
- });
214
- const pagination = normalizeCursorPagination(query, {
215
- defaultLimit: DEFAULT_PAGE_SIZE,
216
- maxLimit: MAX_PAGE_SIZE
217
- });
218
-
219
- const status = normalizeConversationStatus(query.status, {
220
- fallback: ""
221
- });
222
- const filters = {
223
- ...(status ? { status } : {})
224
- };
225
-
226
- return conversationsRepository.listForActorScope(
227
- {
228
- workspaceId,
229
- actorUserId,
230
- pagination: {
231
- cursor: pagination.cursor,
232
- limit: pagination.limit
233
- },
234
- filters
235
- }
236
- );
237
- }
238
-
239
- async function getConversationMessagesForUser(workspace, user, conversationId, query = {}) {
240
- const workspaceId = resolveExpectedWorkspaceId(workspace);
241
- const actorUserId = resolveActorUserId(user, {
242
- required: true
243
- });
244
- const numericConversationId = parsePositiveInteger(conversationId);
245
- if (!numericConversationId) {
246
- throw new AppError(400, "Validation failed.", {
247
- details: {
248
- fieldErrors: {
249
- conversationId: "conversationId must be a positive integer."
250
- }
251
- }
252
- });
253
- }
254
-
255
- const conversation = await conversationsRepository.findByIdForActorScope(
256
- numericConversationId,
257
- {
258
- workspaceId,
259
- actorUserId
260
- }
261
- );
262
- if (!conversation) {
263
- throw new AppError(404, "Conversation not found.");
264
- }
265
-
266
- const pagination = normalizePagination(query, {
267
- defaultPageSize: DEFAULT_MESSAGES_PAGE_SIZE,
268
- maxPageSize: MAX_MESSAGES_PAGE_SIZE
269
- });
270
- const total = await messagesRepository.countByConversationScope(
271
- numericConversationId,
272
- {
273
- workspaceId
274
- }
275
- );
276
- const totalPages = Math.max(1, Math.ceil(total / pagination.pageSize));
277
- const page = Math.min(pagination.page, totalPages);
278
- const entries = await messagesRepository.listByConversationScope(
279
- numericConversationId,
280
- {
281
- workspaceId
282
- },
283
- {
284
- page,
285
- pageSize: pagination.pageSize
286
- }
287
- );
288
-
289
- return {
290
- conversation,
291
- entries,
292
- page,
293
- pageSize: pagination.pageSize,
294
- total,
295
- totalPages
296
- };
297
- }
298
-
299
- return Object.freeze({
300
- createConversationForTurn,
301
- appendMessage,
302
- completeConversation,
303
- listConversationsForUser,
304
- getConversationMessagesForUser
305
- });
306
- }
307
-
308
- export { createTranscriptService };
@@ -1,13 +0,0 @@
1
- const assistantRuntimeConfig = Object.freeze({
2
- runtimeSurfaceId: "__ASSISTANT_RUNTIME_SURFACE_ID__",
3
- settingsSurfaceId: "__ASSISTANT_SETTINGS_SURFACE_ID__",
4
- runtimeSurfaceRequiresWorkspace: __ASSISTANT_RUNTIME_SURFACE_REQUIRES_WORKSPACE__,
5
- settingsSurfaceRequiresWorkspace: __ASSISTANT_SETTINGS_SURFACE_REQUIRES_WORKSPACE__,
6
- settingsSurfaceRequiresConsoleOwner: __ASSISTANT_SETTINGS_SURFACE_REQUIRES_CONSOLE_OWNER__,
7
- configScope: "__ASSISTANT_CONFIG_SCOPE__",
8
- configTable: "__ASSISTANT_CONFIG_TABLE__",
9
- conversationsTable: "__ASSISTANT_CONVERSATIONS_TABLE__",
10
- messagesTable: "__ASSISTANT_MESSAGES_TABLE__"
11
- });
12
-
13
- export { assistantRuntimeConfig };
@@ -1 +0,0 @@
1
- export { assistantRuntimeConfig } from "./assistantRuntimeConfig.js";