@jskit-ai/assistant-core 0.1.31 → 0.1.33

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.
@@ -1,11 +1,13 @@
1
1
  export default Object.freeze({
2
2
  packageVersion: 1,
3
3
  packageId: "@jskit-ai/assistant-core",
4
- version: "0.1.31",
4
+ version: "0.1.33",
5
5
  kind: "runtime",
6
6
  description: "Reusable assistant client/server/shared primitives without surface-specific routes or settings ownership.",
7
7
  dependsOn: [
8
8
  "@jskit-ai/http-runtime",
9
+ "@jskit-ai/resource-core",
10
+ "@jskit-ai/resource-crud-core",
9
11
  "@jskit-ai/users-core"
10
12
  ],
11
13
  capabilities: {
@@ -45,14 +47,16 @@ export default Object.freeze({
45
47
  mutations: {
46
48
  dependencies: {
47
49
  runtime: {
48
- "@jskit-ai/http-runtime": "0.1.54",
49
- "@jskit-ai/kernel": "0.1.55",
50
- "@jskit-ai/users-core": "0.1.65",
50
+ "@jskit-ai/http-runtime": "0.1.56",
51
+ "@jskit-ai/kernel": "0.1.57",
52
+ "@jskit-ai/resource-core": "0.1.2",
53
+ "@jskit-ai/resource-crud-core": "0.1.2",
54
+ "@jskit-ai/users-core": "0.1.67",
51
55
  "@tanstack/vue-query": "^5.90.5",
52
56
  "dompurify": "^3.3.3",
57
+ "json-rest-schema": "1.x.x",
53
58
  "marked": "^17.0.4",
54
59
  "openai": "^6.22.0",
55
- "typebox": "^1.0.81",
56
60
  "vuetify": "^4.0.0"
57
61
  },
58
62
  dev: {}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/assistant-core",
3
- "version": "0.1.31",
3
+ "version": "0.1.33",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "node --test"
@@ -11,15 +11,20 @@
11
11
  "./shared": "./src/shared/index.js"
12
12
  },
13
13
  "dependencies": {
14
- "@jskit-ai/database-runtime": "0.1.55",
15
- "@jskit-ai/http-runtime": "0.1.54",
16
- "@jskit-ai/kernel": "0.1.55",
17
- "@jskit-ai/users-core": "0.1.65",
14
+ "@jskit-ai/database-runtime": "0.1.57",
15
+ "@jskit-ai/http-runtime": "0.1.56",
16
+ "@jskit-ai/kernel": "0.1.57",
17
+ "@jskit-ai/resource-crud-core": "0.1.2",
18
+ "@jskit-ai/resource-core": "0.1.2",
19
+ "@jskit-ai/users-core": "0.1.67",
18
20
  "@tanstack/vue-query": "^5.90.5",
19
21
  "dompurify": "^3.3.3",
22
+ "json-rest-schema": "1.x.x",
20
23
  "marked": "^17.0.4",
21
24
  "openai": "^6.22.0",
22
- "typebox": "^1.0.81",
23
25
  "vuetify": "^4.0.0"
26
+ },
27
+ "peerDependencies": {
28
+ "vue": "^3.5.13"
24
29
  }
25
30
  }
@@ -3,6 +3,7 @@ import { resolveActionContributors } from "@jskit-ai/kernel/server/actions";
3
3
  import { normalizeActionDefinition } from "@jskit-ai/kernel/shared/actions";
4
4
  import { normalizeSurfaceId } from "@jskit-ai/kernel/shared/surface/registry";
5
5
  import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
6
+ import { resolveStructuredSchemaTransportSchema } from "@jskit-ai/kernel/shared/validators";
6
7
  import { resolveWorkspaceSlug } from "./resolveWorkspaceSlug.js";
7
8
 
8
9
  const AUTOMATION_CHANNEL = "automation";
@@ -275,8 +276,14 @@ function resolveActionBackedToolEntries(scope) {
275
276
  continue;
276
277
  }
277
278
 
278
- const inputSchema = normalizedAction.inputValidator?.schema || null;
279
- const outputSchema = normalizedAction.outputValidator?.schema || null;
279
+ const inputSchema = resolveStructuredSchemaTransportSchema(normalizedAction.input, {
280
+ context: `Action definition "${actionId}" input`,
281
+ defaultMode: "patch"
282
+ }) || null;
283
+ const outputSchema = resolveStructuredSchemaTransportSchema(normalizedAction.output, {
284
+ context: `Action definition "${actionId}" output`,
285
+ defaultMode: "replace"
286
+ }) || null;
280
287
  if (!inputSchema || !outputSchema) {
281
288
  continue;
282
289
  }
@@ -1,303 +1,301 @@
1
- import { Type } from "typebox";
1
+ import { createSchema } from "json-rest-schema";
2
+ import { createCursorListValidator, RECORD_ID_PATTERN } from "@jskit-ai/kernel/shared/validators";
2
3
  import {
3
- normalizeObjectInput,
4
- createCursorListValidator,
5
- recordIdSchema,
6
- recordIdInputSchema,
7
- nullableRecordIdSchema
8
- } from "@jskit-ai/kernel/shared/validators";
9
- import { normalizeRecordId, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
10
- import { normalizeConversationStatus } from "./support/conversationStatus.js";
11
- import { toPositiveInteger } from "./support/positiveInteger.js";
4
+ createSchemaDefinition,
5
+ defineResource
6
+ } from "@jskit-ai/resource-core/shared/resource";
12
7
 
13
8
  const MAX_INPUT_CHARS = 8000;
14
9
  const MAX_HISTORY_MESSAGES = 20;
15
10
  const MAX_PAGE_SIZE = 200;
16
11
  const MAX_MESSAGE_PAGE_SIZE = 500;
17
12
 
18
- function normalizePaginationValue(value, fallback, max) {
19
- const parsed = toPositiveInteger(value, fallback);
20
- return Math.max(1, Math.min(max, parsed));
21
- }
22
-
23
- function normalizeChatStreamBody(payload = {}) {
24
- const source = normalizeObjectInput(payload);
25
- const normalized = {
26
- messageId: normalizeText(source.messageId),
27
- input: normalizeText(source.input)
28
- };
29
-
30
- const conversationId = normalizeRecordId(source.conversationId, { fallback: null });
31
- if (conversationId) {
32
- normalized.conversationId = conversationId;
13
+ const historyMessageSchema = createSchema({
14
+ role: {
15
+ type: "string",
16
+ required: true,
17
+ enum: ["user", "assistant"]
18
+ },
19
+ content: {
20
+ type: "string",
21
+ required: true,
22
+ minLength: 1,
23
+ maxLength: MAX_INPUT_CHARS
33
24
  }
25
+ });
34
26
 
35
- const history = Array.isArray(source.history) ? source.history : [];
36
- normalized.history = history
37
- .slice(0, MAX_HISTORY_MESSAGES)
38
- .map((entry) => {
39
- const item = normalizeObjectInput(entry);
40
- const role = normalizeText(item.role).toLowerCase();
41
- if (role !== "user" && role !== "assistant") {
42
- return null;
43
- }
44
-
45
- const content = normalizeText(item.content);
46
- if (!content) {
47
- return null;
48
- }
49
-
50
- return {
51
- role,
52
- content: content.slice(0, MAX_INPUT_CHARS)
53
- };
54
- })
55
- .filter(Boolean);
56
-
57
- const clientContext = normalizeObjectInput(source.clientContext);
58
- if (Object.keys(clientContext).length > 0) {
59
- normalized.clientContext = {
60
- locale: normalizeText(clientContext.locale),
61
- timezone: normalizeText(clientContext.timezone)
62
- };
27
+ const clientContextSchema = createSchema({
28
+ locale: {
29
+ type: "string",
30
+ required: false,
31
+ maxLength: 64
32
+ },
33
+ timezone: {
34
+ type: "string",
35
+ required: false,
36
+ maxLength: 64
63
37
  }
38
+ });
64
39
 
65
- return normalized;
66
- }
67
-
68
- function normalizeConversationsListQuery(payload = {}) {
69
- const source = normalizeObjectInput(payload);
70
- const status = normalizeConversationStatus(source.status, {
71
- fallback: ""
72
- });
73
- const normalized = {};
74
-
75
- if (Object.hasOwn(source, "cursor")) {
76
- normalized.cursor = normalizeRecordId(source.cursor, { fallback: "" });
77
- }
78
- if (Object.hasOwn(source, "limit")) {
79
- normalized.limit = toPositiveInteger(source.limit, 0);
80
- }
81
- if (status) {
82
- normalized.status = status;
40
+ const chatStreamBodySchema = createSchema({
41
+ messageId: {
42
+ type: "string",
43
+ required: true,
44
+ minLength: 1,
45
+ maxLength: 128
46
+ },
47
+ conversationId: {
48
+ type: "string",
49
+ required: false,
50
+ minLength: 1,
51
+ pattern: RECORD_ID_PATTERN
52
+ },
53
+ input: {
54
+ type: "string",
55
+ required: true,
56
+ minLength: 1,
57
+ maxLength: MAX_INPUT_CHARS
58
+ },
59
+ history: {
60
+ type: "array",
61
+ required: false,
62
+ maxItems: MAX_HISTORY_MESSAGES,
63
+ items: historyMessageSchema
64
+ },
65
+ clientContext: {
66
+ type: "object",
67
+ required: false,
68
+ schema: clientContextSchema
83
69
  }
70
+ });
84
71
 
85
- return normalized;
86
- }
87
-
88
- function normalizeConversationMessagesQuery(payload = {}) {
89
- const source = normalizeObjectInput(payload);
90
-
91
- return {
92
- page: normalizePaginationValue(source.page, 1, MAX_MESSAGE_PAGE_SIZE),
93
- pageSize: normalizePaginationValue(source.pageSize, 200, MAX_MESSAGE_PAGE_SIZE)
94
- };
95
- }
96
-
97
- function normalizeConversationMessagesParams(payload = {}) {
98
- const source = normalizeObjectInput(payload);
99
- return {
100
- conversationId: normalizeRecordId(source.conversationId, { fallback: "" })
101
- };
102
- }
103
-
104
- function createOptionalPositiveIntegerQuerySchema(max = null) {
105
- const numericSchema = max == null
106
- ? Type.Integer({ minimum: 1 })
107
- : Type.Integer({ minimum: 1, maximum: max });
108
-
109
- return Type.Optional(
110
- Type.Union([
111
- numericSchema,
112
- Type.String({ pattern: "^[1-9][0-9]*$" })
113
- ])
114
- );
115
- }
116
-
117
- function normalizeConversationRecord(payload = {}) {
118
- const source = normalizeObjectInput(payload);
119
-
120
- return {
121
- id: normalizeRecordId(source.id, { fallback: "" }),
122
- workspaceId: normalizeRecordId(source.workspaceId, { fallback: null }),
123
- title: normalizeText(source.title),
124
- createdByUserId: normalizeRecordId(source.createdByUserId, { fallback: null }),
125
- status: normalizeText(source.status),
126
- provider: normalizeText(source.provider),
127
- model: normalizeText(source.model),
128
- surfaceId: normalizeText(source.surfaceId || source.surfaceSid).toLowerCase(),
129
- startedAt: normalizeText(source.startedAt),
130
- endedAt: normalizeText(source.endedAt) || null,
131
- messageCount: Math.max(0, Number(source.messageCount || 0)),
132
- metadata: normalizeObjectInput(source.metadata),
133
- createdAt: normalizeText(source.createdAt),
134
- updatedAt: normalizeText(source.updatedAt)
135
- };
136
- }
137
-
138
- function normalizeConversationMessageRecord(payload = {}) {
139
- const source = normalizeObjectInput(payload);
140
-
141
- return {
142
- id: normalizeRecordId(source.id, { fallback: "" }),
143
- conversationId: normalizeRecordId(source.conversationId, { fallback: "" }),
144
- workspaceId: normalizeRecordId(source.workspaceId, { fallback: null }),
145
- seq: toPositiveInteger(source.seq, 0),
146
- role: normalizeText(source.role),
147
- kind: normalizeText(source.kind),
148
- clientMessageSid: normalizeText(source.clientMessageSid),
149
- actorUserId: normalizeRecordId(source.actorUserId, { fallback: null }),
150
- contentText: source.contentText == null ? null : String(source.contentText),
151
- metadata: normalizeObjectInput(source.metadata),
152
- createdAt: normalizeText(source.createdAt)
153
- };
154
- }
155
-
156
- const historyMessageSchema = Type.Object(
157
- {
158
- role: Type.Union([Type.Literal("user"), Type.Literal("assistant")]),
159
- content: Type.String({ minLength: 1, maxLength: MAX_INPUT_CHARS })
72
+ const conversationsListQuerySchema = createSchema({
73
+ cursor: {
74
+ type: "id",
75
+ required: false
160
76
  },
161
- { additionalProperties: false }
162
- );
77
+ limit: {
78
+ type: "number",
79
+ required: false,
80
+ min: 1,
81
+ max: MAX_PAGE_SIZE
82
+ },
83
+ status: {
84
+ type: "string",
85
+ required: false,
86
+ minLength: 1,
87
+ maxLength: 32
88
+ }
89
+ });
163
90
 
164
- const chatStreamBodySchema = Type.Object(
165
- {
166
- messageId: Type.String({ minLength: 1, maxLength: 128 }),
167
- conversationId: Type.Optional(recordIdInputSchema),
168
- input: Type.String({ minLength: 1, maxLength: MAX_INPUT_CHARS }),
169
- history: Type.Optional(Type.Array(historyMessageSchema, { maxItems: MAX_HISTORY_MESSAGES })),
170
- clientContext: Type.Optional(
171
- Type.Object(
172
- {
173
- locale: Type.Optional(Type.String({ maxLength: 64 })),
174
- timezone: Type.Optional(Type.String({ maxLength: 64 }))
175
- },
176
- { additionalProperties: false }
177
- )
178
- )
179
- },
180
- {
181
- additionalProperties: false
91
+ const conversationRecordSchema = createSchema({
92
+ id: {
93
+ type: "string",
94
+ required: true,
95
+ minLength: 1,
96
+ pattern: RECORD_ID_PATTERN
97
+ },
98
+ workspaceId: {
99
+ type: "string",
100
+ required: true,
101
+ nullable: true,
102
+ minLength: 1,
103
+ pattern: RECORD_ID_PATTERN
104
+ },
105
+ title: {
106
+ type: "string",
107
+ required: true
108
+ },
109
+ createdByUserId: {
110
+ type: "string",
111
+ required: true,
112
+ nullable: true,
113
+ minLength: 1,
114
+ pattern: RECORD_ID_PATTERN
115
+ },
116
+ status: {
117
+ type: "string",
118
+ required: true
119
+ },
120
+ provider: {
121
+ type: "string",
122
+ required: true
123
+ },
124
+ model: {
125
+ type: "string",
126
+ required: true
127
+ },
128
+ surfaceId: {
129
+ type: "string",
130
+ required: true
131
+ },
132
+ startedAt: {
133
+ type: "string",
134
+ required: true,
135
+ minLength: 1
136
+ },
137
+ endedAt: {
138
+ type: "string",
139
+ required: true,
140
+ nullable: true,
141
+ minLength: 1
142
+ },
143
+ messageCount: {
144
+ type: "integer",
145
+ required: true,
146
+ min: 0
147
+ },
148
+ metadata: {
149
+ type: "object",
150
+ required: true,
151
+ additionalProperties: true
152
+ },
153
+ createdAt: {
154
+ type: "string",
155
+ required: true,
156
+ minLength: 1
157
+ },
158
+ updatedAt: {
159
+ type: "string",
160
+ required: true,
161
+ minLength: 1
182
162
  }
183
- );
163
+ });
184
164
 
185
- const conversationRecordSchema = Type.Object(
186
- {
187
- id: recordIdSchema,
188
- workspaceId: nullableRecordIdSchema,
189
- title: Type.String(),
190
- createdByUserId: nullableRecordIdSchema,
191
- status: Type.String(),
192
- provider: Type.String(),
193
- model: Type.String(),
194
- surfaceId: Type.String(),
195
- startedAt: Type.String({ minLength: 1 }),
196
- endedAt: Type.Union([Type.String({ minLength: 1 }), Type.Null()]),
197
- messageCount: Type.Integer({ minimum: 0 }),
198
- metadata: Type.Record(Type.String(), Type.Unknown()),
199
- createdAt: Type.String({ minLength: 1 }),
200
- updatedAt: Type.String({ minLength: 1 })
201
- },
202
- { additionalProperties: false }
203
- );
165
+ const messageRecordSchema = createSchema({
166
+ id: {
167
+ type: "string",
168
+ required: true,
169
+ minLength: 1,
170
+ pattern: RECORD_ID_PATTERN
171
+ },
172
+ conversationId: {
173
+ type: "string",
174
+ required: true,
175
+ minLength: 1,
176
+ pattern: RECORD_ID_PATTERN
177
+ },
178
+ workspaceId: {
179
+ type: "string",
180
+ required: true,
181
+ nullable: true,
182
+ minLength: 1,
183
+ pattern: RECORD_ID_PATTERN
184
+ },
185
+ seq: {
186
+ type: "integer",
187
+ required: true,
188
+ min: 1
189
+ },
190
+ role: {
191
+ type: "string",
192
+ required: true,
193
+ minLength: 1
194
+ },
195
+ kind: {
196
+ type: "string",
197
+ required: true,
198
+ minLength: 1
199
+ },
200
+ clientMessageSid: {
201
+ type: "string",
202
+ required: true
203
+ },
204
+ actorUserId: {
205
+ type: "string",
206
+ required: true,
207
+ nullable: true,
208
+ minLength: 1,
209
+ pattern: RECORD_ID_PATTERN
210
+ },
211
+ contentText: {
212
+ type: "string",
213
+ required: true,
214
+ nullable: true
215
+ },
216
+ createdAt: {
217
+ type: "string",
218
+ required: true,
219
+ minLength: 1
220
+ },
221
+ metadata: {
222
+ type: "object",
223
+ required: true,
224
+ additionalProperties: true
225
+ }
226
+ });
204
227
 
205
- const conversationRecordValidator = Object.freeze({
206
- schema: conversationRecordSchema,
207
- normalize: normalizeConversationRecord
228
+ const conversationMessagesParamsSchema = createSchema({
229
+ conversationId: {
230
+ type: "id",
231
+ required: true
232
+ }
208
233
  });
209
234
 
210
- const messageRecordSchema = Type.Object(
211
- {
212
- id: recordIdSchema,
213
- conversationId: recordIdSchema,
214
- workspaceId: nullableRecordIdSchema,
215
- seq: Type.Integer({ minimum: 1 }),
216
- role: Type.String({ minLength: 1 }),
217
- kind: Type.String({ minLength: 1 }),
218
- clientMessageSid: Type.String(),
219
- actorUserId: nullableRecordIdSchema,
220
- contentText: Type.Union([Type.String(), Type.Null()]),
221
- metadata: Type.Record(Type.String(), Type.Unknown()),
222
- createdAt: Type.String({ minLength: 1 })
223
- },
224
- { additionalProperties: false }
225
- );
235
+ const conversationMessagesQuerySchema = createSchema({
236
+ page: {
237
+ type: "number",
238
+ required: false,
239
+ min: 1
240
+ },
241
+ pageSize: {
242
+ type: "number",
243
+ required: false,
244
+ min: 1,
245
+ max: MAX_MESSAGE_PAGE_SIZE
246
+ }
247
+ });
226
248
 
227
- const paginationProperties = Object.freeze({
228
- page: Type.Integer({ minimum: 1 }),
229
- pageSize: Type.Integer({ minimum: 1 }),
230
- total: Type.Integer({ minimum: 0 }),
231
- totalPages: Type.Integer({ minimum: 1 })
249
+ const conversationMessagesListOutputSchema = createSchema({
250
+ page: {
251
+ type: "integer",
252
+ required: true,
253
+ min: 1
254
+ },
255
+ pageSize: {
256
+ type: "integer",
257
+ required: true,
258
+ min: 1
259
+ },
260
+ total: {
261
+ type: "integer",
262
+ required: true,
263
+ min: 0
264
+ },
265
+ totalPages: {
266
+ type: "integer",
267
+ required: true,
268
+ min: 1
269
+ },
270
+ conversation: {
271
+ type: "object",
272
+ required: true,
273
+ schema: conversationRecordSchema
274
+ },
275
+ entries: {
276
+ type: "array",
277
+ required: true,
278
+ items: messageRecordSchema
279
+ }
232
280
  });
233
281
 
234
- const assistantResource = Object.freeze({
282
+ const assistantResource = defineResource({
235
283
  namespace: "assistant",
236
284
  operations: {
237
285
  chatStream: {
238
286
  method: "POST",
239
- bodyValidator: Object.freeze({
240
- schema: chatStreamBodySchema,
241
- normalize: normalizeChatStreamBody
242
- })
287
+ body: chatStreamBodySchema
243
288
  },
244
289
  conversationsList: {
245
290
  method: "GET",
246
- queryValidator: Object.freeze({
247
- schema: Type.Object(
248
- {
249
- cursor: Type.Optional(recordIdInputSchema),
250
- limit: createOptionalPositiveIntegerQuerySchema(MAX_PAGE_SIZE),
251
- status: Type.Optional(Type.String({ minLength: 1, maxLength: 32 }))
252
- },
253
- { additionalProperties: false }
254
- ),
255
- normalize: normalizeConversationsListQuery
256
- }),
257
- outputValidator: createCursorListValidator(conversationRecordValidator)
291
+ query: conversationsListQuerySchema,
292
+ output: createCursorListValidator(createSchemaDefinition(conversationRecordSchema, "replace"))
258
293
  },
259
294
  conversationMessagesList: {
260
295
  method: "GET",
261
- paramsValidator: Object.freeze({
262
- schema: Type.Object(
263
- {
264
- conversationId: recordIdInputSchema
265
- },
266
- { additionalProperties: false }
267
- ),
268
- normalize: normalizeConversationMessagesParams
269
- }),
270
- queryValidator: Object.freeze({
271
- schema: Type.Object(
272
- {
273
- page: createOptionalPositiveIntegerQuerySchema(),
274
- pageSize: createOptionalPositiveIntegerQuerySchema(MAX_MESSAGE_PAGE_SIZE)
275
- },
276
- { additionalProperties: false }
277
- ),
278
- normalize: normalizeConversationMessagesQuery
279
- }),
280
- outputValidator: Object.freeze({
281
- schema: Type.Object(
282
- {
283
- ...paginationProperties,
284
- conversation: conversationRecordSchema,
285
- entries: Type.Array(messageRecordSchema)
286
- },
287
- { additionalProperties: false }
288
- ),
289
- normalize(payload = {}) {
290
- const source = normalizeObjectInput(payload);
291
- return {
292
- conversation: normalizeConversationRecord(source.conversation),
293
- entries: (Array.isArray(source.entries) ? source.entries : []).map(normalizeConversationMessageRecord),
294
- page: normalizePaginationValue(source.page, 1, MAX_MESSAGE_PAGE_SIZE),
295
- pageSize: normalizePaginationValue(source.pageSize, 200, MAX_MESSAGE_PAGE_SIZE),
296
- total: Math.max(0, Number(source.total || 0)),
297
- totalPages: Math.max(1, Number(source.totalPages || 1))
298
- };
299
- }
300
- })
296
+ params: conversationMessagesParamsSchema,
297
+ query: conversationMessagesQuerySchema,
298
+ output: conversationMessagesListOutputSchema
301
299
  }
302
300
  }
303
301
  });