@jskit-ai/assistant-core 0.2.26 → 0.2.28
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/CHANGELOG.md +3 -0
- package/examples/conversation/package.json +5 -5
- package/fixtures/responsive-assistant/ConversationFixture.vue +4 -1
- package/package.json +4 -5
- package/src/shared/assistantResource.js +4 -4
- package/src/shared/assistantSettingsResource.js +20 -7
- package/src/shared/index.js +2 -2
- package/test/assistantResource.test.js +5 -5
- package/test/assistantSettingsResource.test.js +3 -3
- package/test/fixtureColours.test.js +21 -0
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
- Test fixture only: the responsive assistant fixture's avatar is drawn in the theme's `primary` and `on-primary` roles instead of a raw `#6750a4` and `white`, so it follows the dark scheme like the rest of the conversation. A package test runs `jskit check m3`'s raw-colour rule over the fixture, which the check itself skips with every `fixtures/` folder.
|
|
6
|
+
- The assistant settings resource (`assistantConfigResource`) has a required boolean `canUpdate` attribute: whether the signed-in person may change the settings, as the server decides (`@jskit-ai/assistant-runtime`'s settings pane shows Save only when it is true).
|
|
7
|
+
- **Breaking (data-access redesign).** The assistant operations are API contracts: `assistantContract` and `assistantConfigContract` (view and patch, the two its routes serve), which replace `assistantResource` and `assistantConfigResource`.
|
|
5
8
|
- The OpenAI SDK loads with an OpenAI-compatible client's first completion instead of with the server, so a boot of an app with the assistant loads 154 fewer modules (0.13 s on their own).
|
|
6
9
|
- A subscriber hears a streamed delta (an in-progress message) on an authorization under a second old instead of authorizing again for each one: every delta used to wait for one `authorize` call per subscriber before the stream continued (one SELECT each in `@jskit-ai/assistant-runtime`), so a 200-delta reply to one open page cost about 200 reads and now costs as many as a 10-delta one. Every other event (admission, phase, saved message, settlement) still authorizes again, and a revoked subscriber stops hearing the turn within a second.
|
|
7
10
|
- The conversation's remaining boxes are Material 3 tonal containers instead of outlines, visual only: queued and sent attachments and the questions are `surface-container-high`, code blocks and inline code `surface-container-highest`, tables and details `surface-container-low` (a table keeps `outline-variant` row dividers), question numbers `secondary-container` badges, and the model menu a `surface-container` surface at elevation level 2 with `outline-variant` dividers. The composer stays Material 3's outlined text field, the only outline left.
|
|
@@ -8,14 +8,14 @@
|
|
|
8
8
|
"build": "vite build"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@jskit-ai/assistant-core": "0.2.
|
|
12
|
-
"@jskit-ai/http-runtime": "0.2.
|
|
13
|
-
"@jskit-ai/kernel": "0.2.
|
|
11
|
+
"@jskit-ai/assistant-core": "0.2.28",
|
|
12
|
+
"@jskit-ai/http-runtime": "0.2.14",
|
|
13
|
+
"@jskit-ai/kernel": "0.2.14",
|
|
14
14
|
"@jskit-ai/material-web": "0.1.3",
|
|
15
15
|
"vue": "^3.5.13",
|
|
16
16
|
"vuetify": "^4.0.0",
|
|
17
|
-
"@jskit-ai/connectors-core": "0.2.
|
|
18
|
-
"@jskit-ai/connectors-catalog": "0.2.
|
|
17
|
+
"@jskit-ai/connectors-core": "0.2.15",
|
|
18
|
+
"@jskit-ai/connectors-catalog": "0.2.15"
|
|
19
19
|
},
|
|
20
20
|
"devDependencies": {
|
|
21
21
|
"@vitejs/plugin-vue": "^6.0.7",
|
|
@@ -201,7 +201,7 @@ const adapter = reactive({
|
|
|
201
201
|
</div>
|
|
202
202
|
<main class="fixture" :class="{ narrow, 'fixture--avatar': avatarFixture, 'fixture--short': short }">
|
|
203
203
|
<AssistantConversationElement v-model:avatar-size="avatarSize" :adapter="adapter">
|
|
204
|
-
<template v-if="showAvatar" #avatar><svg viewBox="0 0 100 100" role="img" aria-label="Support assistant avatar"><circle cx="50" cy="50" r="45"
|
|
204
|
+
<template v-if="showAvatar" #avatar><svg viewBox="0 0 100 100" role="img" aria-label="Support assistant avatar"><circle class="fixture-avatar__face" cx="50" cy="50" r="45" /><path class="fixture-avatar__features" d="M30 62Q50 80 70 62M35 40h1m28 0h1" fill="none" stroke-width="6" stroke-linecap="round" /></svg></template>
|
|
205
205
|
<template v-if="avatarToolsFixture" #avatar-tools="scope">
|
|
206
206
|
<button class="fixture-avatar-tool" aria-label="Avatar tool" :data-size="scope.size" :data-height="scope.height">Tool</button>
|
|
207
207
|
</template>
|
|
@@ -224,5 +224,8 @@ const adapter = reactive({
|
|
|
224
224
|
.fixture--avatar { width: min(320px, 100%); }
|
|
225
225
|
.fixture-avatar-tool { flex: 0 0 48px; min-width: 48px; min-height: 48px; }
|
|
226
226
|
.fixture--short { height: 420px; }
|
|
227
|
+
/* The avatar is drawn in the theme's primary roles, so it follows the light and dark schemes. */
|
|
228
|
+
.fixture-avatar__face { fill: rgb(var(--v-theme-primary)); }
|
|
229
|
+
.fixture-avatar__features { stroke: rgb(var(--v-theme-on-primary)); }
|
|
227
230
|
.feedback { flex: 1 1 24rem; min-width: 0; margin: 0; }
|
|
228
231
|
</style>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/assistant-core",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.28",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "jskit test"
|
|
@@ -52,8 +52,7 @@
|
|
|
52
52
|
"@ai-sdk/togetherai": "^3.0.49",
|
|
53
53
|
"@ai-sdk/xai": "^4.0.58",
|
|
54
54
|
"@jskit-ai/agent": "0.1.2",
|
|
55
|
-
"@jskit-ai/resource-core": "0.1.
|
|
56
|
-
"@jskit-ai/resource-crud-core": "0.1.203",
|
|
55
|
+
"@jskit-ai/resource-core": "0.1.205",
|
|
57
56
|
"@openrouter/ai-sdk-provider": "^3.0.0",
|
|
58
57
|
"ai": "^7.0.152",
|
|
59
58
|
"json-rest-schema": "^1.0.17",
|
|
@@ -61,8 +60,8 @@
|
|
|
61
60
|
"ws": "^8.20.1"
|
|
62
61
|
},
|
|
63
62
|
"peerDependencies": {
|
|
64
|
-
"@jskit-ai/http-runtime": "0.2.
|
|
65
|
-
"@jskit-ai/kernel": "0.2.
|
|
63
|
+
"@jskit-ai/http-runtime": "0.2.14",
|
|
64
|
+
"@jskit-ai/kernel": "0.2.14",
|
|
66
65
|
"vue": "^3.5.13",
|
|
67
66
|
"vuetify": "^4.3.0"
|
|
68
67
|
},
|
|
@@ -2,8 +2,8 @@ import { createSchema } from "json-rest-schema";
|
|
|
2
2
|
import { createCursorListValidator, RECORD_ID_PATTERN } from "@jskit-ai/kernel/shared/validators";
|
|
3
3
|
import {
|
|
4
4
|
createSchemaDefinition,
|
|
5
|
-
|
|
6
|
-
} from "@jskit-ai/resource-core/shared/
|
|
5
|
+
defineApiContract
|
|
6
|
+
} from "@jskit-ai/resource-core/shared/apiContract";
|
|
7
7
|
import { ASSISTANT_ATTACHMENT_ID_MAX_LENGTH, ASSISTANT_ATTACHMENT_MAX_ITEMS } from "./conversation/attachments.js";
|
|
8
8
|
|
|
9
9
|
const MAX_INPUT_CHARS = 8000;
|
|
@@ -267,7 +267,7 @@ const conversationMessagesListOutputSchema = createSchema({
|
|
|
267
267
|
|
|
268
268
|
const assistantConversationOutputValidator = createSchemaDefinition(conversationRecordSchema, "replace");
|
|
269
269
|
|
|
270
|
-
const
|
|
270
|
+
const assistantContract = defineApiContract({
|
|
271
271
|
namespace: "assistant",
|
|
272
272
|
operations: {
|
|
273
273
|
chatStream: {
|
|
@@ -292,6 +292,6 @@ export {
|
|
|
292
292
|
MAX_INPUT_CHARS,
|
|
293
293
|
MAX_MESSAGE_PAGE_SIZE,
|
|
294
294
|
attachmentIdsField,
|
|
295
|
-
|
|
295
|
+
assistantContract,
|
|
296
296
|
assistantConversationOutputValidator
|
|
297
297
|
};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createSchema } from "json-rest-schema";
|
|
2
2
|
import { RECORD_ID_PATTERN } from "@jskit-ai/kernel/shared/validators";
|
|
3
3
|
import { deepFreeze } from "@jskit-ai/kernel/shared/support/deepFreeze";
|
|
4
|
-
import {
|
|
4
|
+
import { defineApiContract } from "@jskit-ai/resource-core/shared/apiContract";
|
|
5
5
|
|
|
6
6
|
const MAX_SYSTEM_PROMPT_CHARS = 12_000;
|
|
7
7
|
|
|
@@ -46,6 +46,12 @@ const assistantConfigRecordSchema = createSchema({
|
|
|
46
46
|
maxLength: MAX_SYSTEM_PROMPT_CHARS
|
|
47
47
|
}
|
|
48
48
|
})
|
|
49
|
+
},
|
|
50
|
+
// Whether the signed-in person may change these settings, as the server decides; the settings screen shows Save
|
|
51
|
+
// only when it is true.
|
|
52
|
+
canUpdate: {
|
|
53
|
+
type: "boolean",
|
|
54
|
+
required: true
|
|
49
55
|
}
|
|
50
56
|
});
|
|
51
57
|
|
|
@@ -61,16 +67,23 @@ const assistantConfigPatchSchema = createSchema({
|
|
|
61
67
|
}
|
|
62
68
|
});
|
|
63
69
|
|
|
64
|
-
|
|
70
|
+
// The assistant's configuration over HTTP: read it, and patch it.
|
|
71
|
+
const assistantConfigContract = defineApiContract({
|
|
65
72
|
namespace: "assistantConfig",
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
73
|
+
operations: {
|
|
74
|
+
view: {
|
|
75
|
+
method: "GET",
|
|
76
|
+
output: assistantConfigRecordSchema
|
|
77
|
+
},
|
|
78
|
+
patch: {
|
|
79
|
+
method: "PATCH",
|
|
80
|
+
body: assistantConfigPatchSchema,
|
|
81
|
+
output: assistantConfigRecordSchema
|
|
82
|
+
}
|
|
70
83
|
}
|
|
71
84
|
});
|
|
72
85
|
|
|
73
86
|
export {
|
|
74
87
|
MAX_SYSTEM_PROMPT_CHARS,
|
|
75
|
-
|
|
88
|
+
assistantConfigContract
|
|
76
89
|
};
|
package/src/shared/index.js
CHANGED
|
@@ -29,13 +29,13 @@ export {
|
|
|
29
29
|
MAX_INPUT_CHARS,
|
|
30
30
|
MAX_MESSAGE_PAGE_SIZE,
|
|
31
31
|
attachmentIdsField,
|
|
32
|
-
|
|
32
|
+
assistantContract,
|
|
33
33
|
assistantConversationOutputValidator
|
|
34
34
|
} from "./assistantResource.js";
|
|
35
35
|
|
|
36
36
|
export {
|
|
37
37
|
MAX_SYSTEM_PROMPT_CHARS,
|
|
38
|
-
|
|
38
|
+
assistantConfigContract
|
|
39
39
|
} from "./assistantSettingsResource.js";
|
|
40
40
|
|
|
41
41
|
export {
|
|
@@ -2,18 +2,18 @@ import test from "node:test";
|
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { validateOperationSection } from "@jskit-ai/http-runtime/shared/validators/operationValidation";
|
|
4
4
|
import { resolveSchemaTransportSchemaDefinition } from "@jskit-ai/kernel/shared/validators";
|
|
5
|
-
import {
|
|
5
|
+
import { assistantContract } from "../src/shared/assistantResource.js";
|
|
6
6
|
|
|
7
7
|
test("assistant output schemas accept normalized paginated payloads", () => {
|
|
8
8
|
const conversationsListSchema = resolveSchemaTransportSchemaDefinition(
|
|
9
|
-
|
|
9
|
+
assistantContract.operations.conversationsList.output,
|
|
10
10
|
{
|
|
11
11
|
context: "assistant conversations list output",
|
|
12
12
|
defaultMode: "replace"
|
|
13
13
|
}
|
|
14
14
|
);
|
|
15
15
|
const conversationMessagesSchema = resolveSchemaTransportSchemaDefinition(
|
|
16
|
-
|
|
16
|
+
assistantContract.operations.conversationMessagesList.output,
|
|
17
17
|
{
|
|
18
18
|
context: "assistant conversation messages output",
|
|
19
19
|
defaultMode: "replace"
|
|
@@ -53,14 +53,14 @@ test("assistant output schemas accept normalized paginated payloads", () => {
|
|
|
53
53
|
|
|
54
54
|
test("assistant conversation message params accept numeric path strings", async () => {
|
|
55
55
|
const params = resolveSchemaTransportSchemaDefinition(
|
|
56
|
-
|
|
56
|
+
assistantContract.operations.conversationMessagesList.params,
|
|
57
57
|
{
|
|
58
58
|
context: "assistant conversation message params",
|
|
59
59
|
defaultMode: "patch"
|
|
60
60
|
}
|
|
61
61
|
);
|
|
62
62
|
const parsed = await validateOperationSection({
|
|
63
|
-
operation:
|
|
63
|
+
operation: assistantContract.operations.conversationMessagesList,
|
|
64
64
|
section: "params",
|
|
65
65
|
value: { conversationId: "2" }
|
|
66
66
|
});
|
|
@@ -2,11 +2,11 @@ import test from "node:test";
|
|
|
2
2
|
import assert from "node:assert/strict";
|
|
3
3
|
import { resolveSchemaTransportSchemaDefinition } from "@jskit-ai/kernel/shared/validators";
|
|
4
4
|
import { validateOperationSection } from "@jskit-ai/http-runtime/shared/validators/operationValidation";
|
|
5
|
-
import {
|
|
5
|
+
import { assistantConfigContract } from "../src/shared/assistantSettingsResource.js";
|
|
6
6
|
|
|
7
7
|
test("assistant config resource exposes valid output schema", () => {
|
|
8
8
|
const viewSchema = resolveSchemaTransportSchemaDefinition(
|
|
9
|
-
|
|
9
|
+
assistantConfigContract.operations.view.output,
|
|
10
10
|
{
|
|
11
11
|
context: "assistant config view output",
|
|
12
12
|
defaultMode: "replace"
|
|
@@ -23,7 +23,7 @@ test("assistant config resource exposes valid output schema", () => {
|
|
|
23
23
|
|
|
24
24
|
test("assistant settings patch validation preserves omitted fields", async () => {
|
|
25
25
|
const patch = await validateOperationSection({
|
|
26
|
-
operation:
|
|
26
|
+
operation: assistantConfigContract.operations.patch,
|
|
27
27
|
section: "body",
|
|
28
28
|
value: {}
|
|
29
29
|
});
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
|
|
5
|
+
import { checkMaterial3 } from "../../../tooling/jskit-catalog/scripts/material3.mjs";
|
|
6
|
+
|
|
7
|
+
// The fixtures the browser tests draw are app UI too: their colours are the theme's roles, so they follow the light
|
|
8
|
+
// and dark schemes the tests check. jskit check m3 skips every file under fixtures/, so this test checks the fixture
|
|
9
|
+
// as an app of its own.
|
|
10
|
+
test("the responsive assistant fixtures draw with theme colour roles, never raw colours", async () => {
|
|
11
|
+
const result = await checkMaterial3({
|
|
12
|
+
projectRoot: fileURLToPath(new URL("../fixtures/responsive-assistant", import.meta.url)),
|
|
13
|
+
paths: ["."]
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
assert.ok(result.files.includes("ConversationFixture.vue"), result.files.join(", "));
|
|
17
|
+
assert.deepEqual(
|
|
18
|
+
result.findings.filter((finding) => finding.rule === "m3/raw-hex").map(({ file, line, message }) => `${file}:${line} ${message}`),
|
|
19
|
+
[]
|
|
20
|
+
);
|
|
21
|
+
});
|