@jskit-ai/assistant-runtime 0.2.41 → 0.2.42

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 CHANGED
@@ -2,6 +2,7 @@
2
2
 
3
3
  ## Unreleased
4
4
 
5
+ - The saved-conversations list (`GET /api/assistant/:surfaceId/conversations`, and its workspace form) answers 200 again instead of 500 ("metadata is required!") once a person has a conversation: the route still serialized each item with the conversation's read schema, which requires `metadata`, after list items stopped carrying it. It serializes them with assistant-core's `assistantConversationListItemOutputValidator`, the item of `assistantContract`'s `conversationsList`. `conversationsListRoute.test.js` lists a saved conversation over HTTP through the real route, actions and SQL repositories.
5
6
  - Each conversation request authenticates once: the route's access check and the action's request context share one sign-in lookup instead of each asking again.
6
7
  - **Breaking.** Conversation list items no longer carry `metadata`: the conversations repository's `listForActorScope` reads only the fields a list shows, as `assistantContract`'s `conversationsList` now answers them. Upgrading: code that read `metadata` from a list item reads the conversation instead, with `findByIdForActorScope` or the conversation's messages read, which still answer it.
7
8
  - A chat turn ends by reading the turn it waited on (`handle.wait({ turnId })`) instead of reading and cloning every turn of the conversation to keep one.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jskit-ai/assistant-runtime",
3
- "version": "0.2.41",
3
+ "version": "0.2.42",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "test": "jskit test"
@@ -15,21 +15,21 @@
15
15
  "./locales/en.json": "./locales/en.json"
16
16
  },
17
17
  "dependencies": {
18
- "@jskit-ai/assistant-core": "0.2.39",
18
+ "@jskit-ai/assistant-core": "0.2.40",
19
19
  "@jskit-ai/resource-core": "0.1.209",
20
20
  "json-rest-schema": "^1.0.20"
21
21
  },
22
22
  "peerDependencies": {
23
23
  "@jskit-ai/http-runtime": "0.2.18",
24
- "@jskit-ai/http-web": "0.2.28",
24
+ "@jskit-ai/http-web": "0.2.29",
25
25
  "@jskit-ai/kernel": "0.2.18",
26
- "@jskit-ai/realtime": "0.2.26",
27
- "@jskit-ai/shell-web": "0.2.23",
26
+ "@jskit-ai/realtime": "0.2.27",
27
+ "@jskit-ai/shell-web": "0.2.24",
28
28
  "@tanstack/vue-query": "^5.90.5",
29
29
  "vue": "^3.5.13",
30
30
  "vuetify": "^4.0.0",
31
31
  "@jskit-ai/database-runtime": "0.1.267",
32
- "@jskit-ai/resource-runtime": "0.2.22"
32
+ "@jskit-ai/resource-runtime": "0.2.23"
33
33
  },
34
34
  "description": "Capability-based assistant runtime with per-surface chat, transcripts, settings, and action tools.",
35
35
  "jskit": {
@@ -139,6 +139,6 @@
139
139
  },
140
140
  "devDependencies": {
141
141
  "@jskit-ai/material-web": "0.1.5",
142
- "@jskit-ai/resource-runtime": "0.2.22"
142
+ "@jskit-ai/resource-runtime": "0.2.23"
143
143
  }
144
144
  }
@@ -4,7 +4,7 @@ import { createJsonApiResourceRouteContract } from "@jskit-ai/http-runtime/share
4
4
  import {
5
5
  ASSISTANT_CONVERSATIONS_TRANSPORT,
6
6
  ASSISTANT_CONVERSATION_MESSAGES_TRANSPORT,
7
- assistantConversationOutputValidator,
7
+ assistantConversationListItemOutputValidator,
8
8
  assistantContract,
9
9
  resolveAssistantApiBasePath
10
10
  } from "@jskit-ai/assistant-core/shared";
@@ -227,7 +227,7 @@ function registerRuntimeRoutes(
227
227
  ...createJsonApiResourceRouteContract({
228
228
  ...ASSISTANT_CONVERSATIONS_TRANSPORT,
229
229
  query: assistantContract.operations.conversationsList.query,
230
- output: assistantConversationOutputValidator,
230
+ output: assistantConversationListItemOutputValidator,
231
231
  outputKind: "collection"
232
232
  })
233
233
  },
@@ -0,0 +1,68 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+ import Fastify from "fastify";
4
+ import knex from "knex";
5
+ import { createActionCatalogue } from "@jskit-ai/kernel/server/actions";
6
+ import { createRouter } from "../../kernel/server/http/lib/router.js";
7
+ import { registerRoutes as registerFastifyRoutes } from "../../kernel/server/http/lib/routeRegistration.js";
8
+ import { registerApiErrorHandler } from "../../kernel/server/http/errorHandling.js";
9
+ import transcriptsMigration from "../migrations/assistant_transcripts_initial.cjs";
10
+ import summaryMigration from "../migrations/assistant_transcripts_summary.cjs";
11
+ import claimsMigration from "../migrations/assistant_turn_requests.cjs";
12
+ import { createAssistantActions } from "../src/server/actions.js";
13
+ import { registerRoutes } from "../src/server/registerRoutes.js";
14
+ import { createChatService } from "../src/server/services/chatService.js";
15
+ import { createTranscriptService } from "../src/server/services/transcriptService.js";
16
+ import { createTestAssistantStore } from "./support/assistantStore.js";
17
+
18
+ const config = Object.freeze({
19
+ surfaceDefinitions: { assistant: { enabled: true, requiresWorkspace: false } },
20
+ assistantSurfaces: { assistant: { settingsSurfaceId: "assistant", configScope: "global" } }
21
+ });
22
+
23
+ // The saved-conversations list as an application serves it: the assistant's SQL repositories on SQLite, its chat
24
+ // service and actions, and its routes on Fastify, with the signed-in user "1".
25
+ async function listApp(t) {
26
+ const db = knex({ client: "better-sqlite3", connection: { filename: ":memory:" }, useNullAsDefault: true, pool: { min: 1, max: 1 } });
27
+ const store = await createTestAssistantStore(db);
28
+ const app = Fastify();
29
+ t.after(async () => {
30
+ await app.close();
31
+ await store.close();
32
+ await db.destroy();
33
+ });
34
+ await db.schema.createTable("users", (table) => table.bigInteger("id").primary());
35
+ await db("users").insert([{ id: 1 }]);
36
+ await transcriptsMigration.up(db);
37
+ await summaryMigration.up(db);
38
+ await claimsMigration.up(db);
39
+
40
+ const chatService = createChatService({
41
+ transcriptService: createTranscriptService({ conversationsRepository: store.conversations, messagesRepository: store.messages }),
42
+ turnRequests: store.turnRequests,
43
+ appConfig: config
44
+ });
45
+ const actions = createActionCatalogue();
46
+ actions.register({ contributorId: "test.assistant", domain: "assistant", actions: createAssistantActions({ config, chatService, assistantConfigService: {} }) });
47
+ actions.registerContextContributor({ id: "test.actor", contribute: () => ({ actor: { id: "1" } }) });
48
+ const router = createRouter();
49
+ registerRoutes(router, { config });
50
+ registerApiErrorHandler(app);
51
+ registerFastifyRoutes(app, { routes: router.list(), actions });
52
+ return { app, conversations: store.conversations };
53
+ }
54
+
55
+ test("the saved-conversations list answers its conversations over HTTP, without their runtime metadata", async (t) => {
56
+ const { app, conversations } = await listApp(t);
57
+ const saved = await conversations.create({ createdByUserId: "1", surfaceId: "assistant", title: "Order follow-up",
58
+ metadata: { runtime: { systemPrompt: "Answer briefly." } } });
59
+
60
+ const response = await app.inject({ method: "GET", url: "/api/assistant/assistant/conversations", headers: { "x-jskit-surface": "assistant" } });
61
+
62
+ // Before, the route serialized each item with the conversation's read schema, which requires metadata: a 500.
63
+ assert.equal(response.statusCode, 200, response.body);
64
+ const { data } = response.json();
65
+ assert.deepEqual(data.map((item) => [item.type, item.id, item.attributes.title]), [["assistant-conversations", saved.id, "Order follow-up"]]);
66
+ assert.equal(Object.hasOwn(data[0].attributes, "metadata"), false);
67
+ assert.equal(data[0].attributes.createdByUserId, "1");
68
+ });