@jskit-ai/assistant-runtime 0.2.8 → 0.2.10
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 +1 -1
- package/fixtures/canonical-conversation/main.js +2 -1
- package/fixtures/canonical-conversation/vite.config.mjs +11 -0
- package/fixtures/conversation/main.js +2 -1
- package/fixtures/conversation/vite.config.mjs +4 -3
- package/locales/en.json +1 -0
- package/migrations/assistant_config_initial.cjs +1 -5
- package/migrations/assistant_transcripts_initial.cjs +3 -9
- package/migrations/assistant_transcripts_summary.cjs +28 -0
- package/migrations/assistant_turn_requests.cjs +2 -1
- package/migrations/constraints/assistant_users_foreign_keys.cjs +22 -0
- package/migrations/constraints/assistant_workspaces_foreign_keys.cjs +26 -0
- package/package.json +17 -10
- package/patterns/assistant-surface/example/src/ApplicationAiFeature.js +4 -2
- package/src/client/components/AssistantConversationClientElement.vue +10 -4
- package/src/client/components/AssistantSurfaceClientElement.vue +8 -4
- package/src/client/composables/conversationGoal.js +266 -0
- package/src/client/composables/conversationHistory.js +215 -0
- package/src/client/composables/conversationRuntime.js +624 -0
- package/src/client/composables/useAssistantConversation.js +208 -840
- package/src/client/composables/useAssistantRuntime.js +60 -45
- package/src/client/providers/AssistantClientProvider.js +1 -0
- package/src/client/support/assistantRuntimeState.js +6 -34
- package/src/client/support/conversationFailures.js +30 -0
- package/src/client/support/conversationRestoreSupport.js +13 -5
- package/src/client/support/subscribeAssistantConversation.js +32 -9
- package/src/client/support/workspaceScopeSupport.js +13 -22
- package/src/client/types.js +46 -0
- package/src/server/AssistantProvider.js +45 -17
- package/src/server/actions.js +162 -201
- package/src/server/conversationOperations.js +141 -0
- package/src/server/createAssistantRuntime.js +83 -44
- package/src/server/createAssistantSettingsActions.js +69 -47
- package/src/server/hostedAssistant.js +21 -0
- package/src/server/index.js +1 -0
- package/src/server/registerConversationSubscriptions.js +34 -11
- package/src/server/registerFastifyConversations.js +18 -6
- package/src/server/registerRoutes.js +5 -2
- package/src/server/registerSettingsRoutes.js +1 -1
- package/src/server/repositories/assistantConfigRepository.js +10 -20
- package/src/server/repositories/conversationsRepository.js +35 -10
- package/src/server/repositories/messagesRepository.js +14 -10
- package/src/server/repositories/repositoryPersistenceUtils.js +1 -6
- package/src/server/repositories/turnRequestsRepository.js +6 -1
- package/src/server/services/assistantConfigService.js +25 -23
- package/src/server/services/chatService.js +57 -61
- package/src/server/services/transcriptService.js +15 -7
- package/src/server/sqlConversationStorage.js +90 -18
- package/src/server/support/assistantServerConfig.js +1 -1
- package/src/server/support/assistantStreamWriter.js +7 -1
- package/src/server/support/createSurfaceAwareToolCatalog.js +35 -17
- package/src/server/types.js +38 -0
- package/src/shared/assistantSurfaces.js +2 -2
- package/test/assistantConversationBinding.browser.test.js +115 -1
- package/test/assistantConversationOperations.test.js +5 -5
- package/test/assistantRuntimeState.test.js +1 -16
- package/test/chatServiceLifecycle.test.js +62 -154
- package/test/createSurfaceAwareToolCatalog.test.js +5 -0
- package/test/featureRuntime.test.js +12 -6
- package/test/lazyAppConfig.test.js +3 -5
- package/test/packageMetadata.test.js +7 -1
- package/test/sqlConversationStorage.mysql.test.js +89 -0
- package/test/sqlConversationStorage.test.js +161 -16
- package/test/support/memoryAssistantRows.js +6 -1
- package/test/support/scriptedModel.js +32 -0
- package/test/turnRequests.test.js +4 -2
- package/test/usersForeignKeysMigration.test.js +46 -0
- package/test/workspacesForeignKeysMigration.test.js +66 -0
- package/src/client/support/accessDenied.js +0 -6
package/README.md
CHANGED
|
@@ -19,7 +19,7 @@ Main exports by subpath:
|
|
|
19
19
|
|
|
20
20
|
| Subpath | Provides |
|
|
21
21
|
| --- | --- |
|
|
22
|
-
| `@jskit-ai/assistant-runtime/server` | `AssistantFeature`, `createAssistantActions`, `registerAssistantRoutes`, `registerConversationSubscriptions`, `registerFastifyConversations` |
|
|
22
|
+
| `@jskit-ai/assistant-runtime/server` | `AssistantFeature`, `createAssistantActions`, `registerAssistantRoutes`, `registerConversationSubscriptions`, `registerFastifyConversations`, `createSurfaceAwareToolCatalog` |
|
|
23
23
|
| `@jskit-ai/assistant-runtime/server/actionIds` | Assistant action identifiers |
|
|
24
24
|
| `@jskit-ai/assistant-runtime/client` | `AssistantConversationClientElement`, `AssistantSurfaceClientElement`, `AssistantSettingsClientElement`, `useAssistantConversation`, `configureAssistantConversations` |
|
|
25
25
|
| `@jskit-ai/assistant-runtime/shared` | Assistant surface configuration helpers |
|
|
@@ -10,12 +10,13 @@ import { createWebPlacementRuntime } from "../../../shell-web/src/client/placeme
|
|
|
10
10
|
import { createFixtureSocket } from "./socket.js";
|
|
11
11
|
import { assistantHttpClient, createAssistantApi } from "@jskit-ai/assistant-core/client";
|
|
12
12
|
import { configureAssistantConversations, useAssistantConversation } from "../../src/client/composables/useAssistantConversation.js";
|
|
13
|
+
import { silentLogger } from "@jskit-ai/kernel/shared/support/silentLogger";
|
|
13
14
|
|
|
14
15
|
globalThis.__JSKIT_CLIENT_APP_CONFIG__ = {
|
|
15
16
|
surfaceDefinitions: { home: { requiresWorkspace: false } },
|
|
16
17
|
assistantSurfaces: { home: { settingsSurfaceId: "home", configScope: "global" } }
|
|
17
18
|
};
|
|
18
|
-
const placement = createWebPlacementRuntime({ components: new Map() });
|
|
19
|
+
const placement = createWebPlacementRuntime({ components: new Map(), logger: silentLogger });
|
|
19
20
|
placement.setContext({ user: { id: "42" }, surfaceConfig: {
|
|
20
21
|
defaultSurfaceId: "home", surfacesById: { home: { id: "home", routeBase: "/", requiresWorkspace: false } }
|
|
21
22
|
} });
|
|
@@ -110,6 +110,12 @@ export default defineConfig({
|
|
|
110
110
|
for (const pending of held.splice(0)) pending.respond(pending.receipt);
|
|
111
111
|
return json({ ok: true });
|
|
112
112
|
}
|
|
113
|
+
if (url.pathname === "/fixture/start-queued-goal") {
|
|
114
|
+
const state = states.get(input.id);
|
|
115
|
+
changeGoal(state, state.queuedGoal);
|
|
116
|
+
state.queuedGoal = null;
|
|
117
|
+
return json({ ok: true });
|
|
118
|
+
}
|
|
113
119
|
if (url.pathname === "/fixture/finish") {
|
|
114
120
|
const state = states.get(input.id);
|
|
115
121
|
state.status = "ready";
|
|
@@ -145,6 +151,11 @@ export default defineConfig({
|
|
|
145
151
|
}
|
|
146
152
|
const receipt = command.delivery === "message" ? accept(state, { messageId: input.messageId,
|
|
147
153
|
text: input.objective || (input.action === "resume" ? "Resume the goal." : "Cancel the goal.") }) : null;
|
|
154
|
+
// The command is accepted behind the running turn; its goal changes later.
|
|
155
|
+
if (receipt && mode === "queue-goal") {
|
|
156
|
+
state.queuedGoal = input;
|
|
157
|
+
return json(receipt);
|
|
158
|
+
}
|
|
148
159
|
changeGoal(state, input);
|
|
149
160
|
const result = receipt || state.goal || null;
|
|
150
161
|
return json(result, result === null ? 204 : 200);
|
|
@@ -8,6 +8,7 @@ import { aliases, mdi } from "vuetify/iconsets/mdi-svg";
|
|
|
8
8
|
import "vuetify/styles";
|
|
9
9
|
import App from "./App.vue";
|
|
10
10
|
import { createWebPlacementRuntime } from "../../../shell-web/src/client/placement/runtime.js";
|
|
11
|
+
import { silentLogger } from "@jskit-ai/kernel/shared/support/silentLogger";
|
|
11
12
|
|
|
12
13
|
globalThis.__JSKIT_CLIENT_APP_CONFIG__ = {
|
|
13
14
|
surfaceDefinitions: { admin: { requiresWorkspace: true }, console: { requiresWorkspace: false } },
|
|
@@ -18,7 +19,7 @@ globalThis.__JSKIT_CLIENT_APP_CONFIG__ = {
|
|
|
18
19
|
};
|
|
19
20
|
const router = createRouter({ history: createWebHistory(), routes: [{ path: "/:pathMatch(.*)*", component: App }] });
|
|
20
21
|
const app = createApp(App);
|
|
21
|
-
const placement = createWebPlacementRuntime({ components: new Map() });
|
|
22
|
+
const placement = createWebPlacementRuntime({ components: new Map(), logger: silentLogger });
|
|
22
23
|
placement.setContext({ user: { id: "1" }, surfaceConfig: {
|
|
23
24
|
defaultSurfaceId: "admin",
|
|
24
25
|
surfacesById: { admin: { id: "admin", routeBase: "/", requiresWorkspace: false } }
|
|
@@ -3,6 +3,7 @@ import { createChatService } from "../../src/server/services/chatService.js";
|
|
|
3
3
|
import { ASSISTANT_STREAM_WRITER_METHODS } from "../../src/server/support/assistantStreamWriter.js";
|
|
4
4
|
import { createAssistantConversationRuntime } from "../../src/server/createAssistantRuntime.js";
|
|
5
5
|
import { createMemoryAssistantRows } from "../../test/support/memoryAssistantRows.js";
|
|
6
|
+
import { createScriptedModel } from "../../test/support/scriptedModel.js";
|
|
6
7
|
import { defineConfig } from "vite";
|
|
7
8
|
import vue from "@vitejs/plugin-vue";
|
|
8
9
|
import { fileURLToPath } from "node:url";
|
|
@@ -66,14 +67,14 @@ export default defineConfig({
|
|
|
66
67
|
turnRequests,
|
|
67
68
|
aiClientFactory: { async resolveClient(_surface, { integrationId }) {
|
|
68
69
|
return { enabled: true, provider: "fixture", defaultModel: integrationId, supportsAttachments: true,
|
|
69
|
-
async *
|
|
70
|
+
model: createScriptedModel(async function* ({ messages }) {
|
|
70
71
|
stream.receivedFiles = messages.at(-1).content;
|
|
71
72
|
await nextChunk();
|
|
72
73
|
yield { choices: [{ delta: { content: "Partial answer" } }] };
|
|
73
74
|
await nextChunk();
|
|
74
75
|
yield { choices: [{ delta: { content: " completed." } }] };
|
|
75
76
|
yield { choices: [{ delta: {}, finish_reason: "stop" }] };
|
|
76
|
-
}
|
|
77
|
+
}, { model: integrationId })
|
|
77
78
|
};
|
|
78
79
|
} },
|
|
79
80
|
attachments: { async resolve({ attachmentIds }) {
|
|
@@ -118,7 +119,7 @@ export default defineConfig({
|
|
|
118
119
|
const older = url.searchParams.has("page[cursor]");
|
|
119
120
|
return json({ data: [{ type: "assistant-conversations", id: older ? "2" : "1", attributes: {
|
|
120
121
|
title: older ? "Older conversation" : "Saved conversation", status: "completed", startedAt: "2026-09-01T00:00:00Z"
|
|
121
|
-
} }], meta: {
|
|
122
|
+
} }], meta: { pagination: { cursor: { next: older ? null : "older" } } } });
|
|
122
123
|
}
|
|
123
124
|
res.statusCode = 404;
|
|
124
125
|
json({ error: "Unknown fixture request" });
|
package/locales/en.json
CHANGED
|
@@ -74,6 +74,7 @@
|
|
|
74
74
|
"goal": {
|
|
75
75
|
"budgetsUnsupported": "This conversation does not support goal token budgets.",
|
|
76
76
|
"changeFailed": "Could not change the goal.",
|
|
77
|
+
"notStarted": "The goal did not start. Check the chat before starting it again.",
|
|
77
78
|
"unavailable": "Goal status is unavailable.",
|
|
78
79
|
"resumeMessage": "Resume the goal.",
|
|
79
80
|
"cancelMessage": "Cancel the goal."
|
|
@@ -1,16 +1,12 @@
|
|
|
1
1
|
exports.up = async function up(knex) {
|
|
2
2
|
const hasTable = await knex.schema.hasTable("assistant_config");
|
|
3
3
|
if (!hasTable) {
|
|
4
|
-
const hasWorkspacesTable = await knex.schema.hasTable("workspaces");
|
|
5
|
-
|
|
6
4
|
await knex.schema.createTable("assistant_config", (table) => {
|
|
7
5
|
table.bigIncrements("id").primary();
|
|
8
6
|
table.string("target_surface_id", 64).notNullable();
|
|
9
7
|
table.string("scope_key", 160).notNullable();
|
|
8
|
+
// constraints/assistant_workspaces_foreign_keys.cjs adds the workspaces key when that table exists.
|
|
10
9
|
table.bigInteger("workspace_id").unsigned().nullable();
|
|
11
|
-
if (hasWorkspacesTable) {
|
|
12
|
-
table.foreign("workspace_id").references("id").inTable("workspaces").onDelete("CASCADE");
|
|
13
|
-
}
|
|
14
10
|
table.text("system_prompt").notNullable().defaultTo("");
|
|
15
11
|
table.timestamp("created_at").notNullable().defaultTo(knex.fn.now());
|
|
16
12
|
table.timestamp("updated_at").notNullable().defaultTo(knex.fn.now());
|
|
@@ -1,14 +1,11 @@
|
|
|
1
|
+
// constraints/ adds the users keys, and the workspaces keys when that table exists.
|
|
1
2
|
exports.up = async function up(knex) {
|
|
2
|
-
const hasWorkspacesTable = await knex.schema.hasTable("workspaces");
|
|
3
3
|
const hasConversationsTable = await knex.schema.hasTable("assistant_conversations");
|
|
4
4
|
if (!hasConversationsTable) {
|
|
5
5
|
await knex.schema.createTable("assistant_conversations", (table) => {
|
|
6
6
|
table.bigIncrements("id").primary();
|
|
7
7
|
table.bigInteger("workspace_id").unsigned().nullable();
|
|
8
|
-
|
|
9
|
-
table.foreign("workspace_id").references("id").inTable("workspaces").onDelete("CASCADE");
|
|
10
|
-
}
|
|
11
|
-
table.bigInteger("created_by_user_id").unsigned().nullable().references("id").inTable("users").onDelete("SET NULL").index();
|
|
8
|
+
table.bigInteger("created_by_user_id").unsigned().nullable().index();
|
|
12
9
|
table.string("title", 160).notNullable().defaultTo("New conversation");
|
|
13
10
|
table.string("status", 32).notNullable().defaultTo("active");
|
|
14
11
|
table.string("provider", 64).notNullable().defaultTo("");
|
|
@@ -33,14 +30,11 @@ exports.up = async function up(knex) {
|
|
|
33
30
|
table.bigIncrements("id").primary();
|
|
34
31
|
table.bigInteger("conversation_id").unsigned().notNullable().references("id").inTable("assistant_conversations").onDelete("CASCADE");
|
|
35
32
|
table.bigInteger("workspace_id").unsigned().nullable();
|
|
36
|
-
if (hasWorkspacesTable) {
|
|
37
|
-
table.foreign("workspace_id").references("id").inTable("workspaces").onDelete("CASCADE");
|
|
38
|
-
}
|
|
39
33
|
table.integer("seq").unsigned().notNullable();
|
|
40
34
|
table.string("role", 32).notNullable();
|
|
41
35
|
table.string("kind", 32).notNullable().defaultTo("chat");
|
|
42
36
|
table.string("client_message_sid", 128).notNullable().defaultTo("");
|
|
43
|
-
table.bigInteger("actor_user_id").unsigned().nullable().
|
|
37
|
+
table.bigInteger("actor_user_id").unsigned().nullable().index();
|
|
44
38
|
table.text("content_text").nullable();
|
|
45
39
|
table.text("metadata_json").nullable();
|
|
46
40
|
table.timestamp("created_at").notNullable().defaultTo(knex.fn.now());
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// A long conversation keeps one compaction summary next to its messages, which are never
|
|
2
|
+
// deleted: the summary text and the last turn it covers. transcript_revision changes on
|
|
3
|
+
// every saved transcript change, so a server can keep reusing the rows it already read.
|
|
4
|
+
const COLUMNS = ["summary_text", "summary_through_turn_id", "transcript_revision"];
|
|
5
|
+
|
|
6
|
+
exports.up = async function up(knex) {
|
|
7
|
+
const missing = [];
|
|
8
|
+
for (const column of COLUMNS) {
|
|
9
|
+
if (!await knex.schema.hasColumn("assistant_conversations", column)) missing.push(column);
|
|
10
|
+
}
|
|
11
|
+
if (!missing.length) return;
|
|
12
|
+
await knex.schema.alterTable("assistant_conversations", table => {
|
|
13
|
+
if (missing.includes("summary_text")) table.text("summary_text", "longtext").nullable();
|
|
14
|
+
if (missing.includes("summary_through_turn_id")) table.string("summary_through_turn_id", 64).nullable();
|
|
15
|
+
if (missing.includes("transcript_revision")) table.string("transcript_revision", 36).nullable();
|
|
16
|
+
});
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
exports.down = async function down(knex) {
|
|
20
|
+
const present = [];
|
|
21
|
+
for (const column of COLUMNS) {
|
|
22
|
+
if (await knex.schema.hasColumn("assistant_conversations", column)) present.push(column);
|
|
23
|
+
}
|
|
24
|
+
if (!present.length) return;
|
|
25
|
+
await knex.schema.alterTable("assistant_conversations", table => {
|
|
26
|
+
table.dropColumns(...present);
|
|
27
|
+
});
|
|
28
|
+
};
|
|
@@ -2,7 +2,8 @@ exports.up = async function up(knex) {
|
|
|
2
2
|
if (await knex.schema.hasTable("assistant_turn_requests")) return;
|
|
3
3
|
await knex.schema.createTable("assistant_turn_requests", table => {
|
|
4
4
|
table.bigIncrements("id").primary();
|
|
5
|
-
|
|
5
|
+
// constraints/assistant_users_foreign_keys.cjs adds the users key once the users table exists.
|
|
6
|
+
table.bigInteger("actor_user_id").unsigned().notNullable();
|
|
6
7
|
table.string("scope_key", 128).notNullable();
|
|
7
8
|
table.string("message_sid", 128).notNullable();
|
|
8
9
|
table.string("claim_token", 36).notNullable();
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
// The users table can come from the application's own migrations, which run after every
|
|
2
|
+
// package's table migrations, so the assistant tables get their users keys in this later phase.
|
|
3
|
+
// A database migrated before this file already has them from the table migrations.
|
|
4
|
+
const USER_KEYS = [
|
|
5
|
+
{ tableName: "assistant_conversations", columnName: "created_by_user_id", parentTable: "users", onDelete: "SET NULL" },
|
|
6
|
+
{ tableName: "assistant_messages", columnName: "actor_user_id", parentTable: "users", onDelete: "SET NULL" },
|
|
7
|
+
{ tableName: "assistant_turn_requests", columnName: "actor_user_id", parentTable: "users", onDelete: "CASCADE" }
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
exports.up = async function up(knex) {
|
|
11
|
+
const { addMissingForeignKey } = await import("@jskit-ai/database-runtime/server/foreignKeyMigrations");
|
|
12
|
+
for (const key of USER_KEYS) {
|
|
13
|
+
await addMissingForeignKey(knex, key);
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
exports.down = async function down(knex) {
|
|
18
|
+
const { dropForeignKeyIfPresent } = await import("@jskit-ai/database-runtime/server/foreignKeyMigrations");
|
|
19
|
+
for (const key of USER_KEYS) {
|
|
20
|
+
await dropForeignKeyIfPresent(knex, key);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// workspaces-core's migrations run after assistant-runtime's, and an application may create
|
|
2
|
+
// workspaces itself, so the assistant tables get their workspaces keys in this later phase, when
|
|
3
|
+
// that table exists; an application without workspaces gets none.
|
|
4
|
+
// A database migrated before this file may already have them from the table migrations.
|
|
5
|
+
const WORKSPACE_KEYS = [
|
|
6
|
+
{ tableName: "assistant_config", columnName: "workspace_id", parentTable: "workspaces", onDelete: "CASCADE" },
|
|
7
|
+
{ tableName: "assistant_conversations", columnName: "workspace_id", parentTable: "workspaces", onDelete: "CASCADE" },
|
|
8
|
+
{ tableName: "assistant_messages", columnName: "workspace_id", parentTable: "workspaces", onDelete: "CASCADE" }
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
exports.up = async function up(knex) {
|
|
12
|
+
if (!(await knex.schema.hasTable("workspaces"))) {
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
const { addMissingForeignKey } = await import("@jskit-ai/database-runtime/server/foreignKeyMigrations");
|
|
16
|
+
for (const key of WORKSPACE_KEYS) {
|
|
17
|
+
await addMissingForeignKey(knex, key);
|
|
18
|
+
}
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
exports.down = async function down(knex) {
|
|
22
|
+
const { dropForeignKeyIfPresent } = await import("@jskit-ai/database-runtime/server/foreignKeyMigrations");
|
|
23
|
+
for (const key of WORKSPACE_KEYS) {
|
|
24
|
+
await dropForeignKeyIfPresent(knex, key);
|
|
25
|
+
}
|
|
26
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/assistant-runtime",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.10",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"test": "node --test --test-concurrency=1"
|
|
@@ -10,22 +10,23 @@
|
|
|
10
10
|
"./shared": "./src/shared/index.js",
|
|
11
11
|
"./server/actionIds": "./src/server/actionIds.js",
|
|
12
12
|
"./server": "./src/server/index.js",
|
|
13
|
+
"./server/types": "./src/server/types.js",
|
|
13
14
|
"./locales/en.json": "./locales/en.json"
|
|
14
15
|
},
|
|
15
16
|
"dependencies": {
|
|
16
|
-
"@jskit-ai/assistant-core": "0.2.
|
|
17
|
+
"@jskit-ai/assistant-core": "0.2.8",
|
|
17
18
|
"json-rest-schema": "^1.0.17"
|
|
18
19
|
},
|
|
19
20
|
"peerDependencies": {
|
|
20
|
-
"@jskit-ai/http-runtime": "0.2.
|
|
21
|
-
"@jskit-ai/http-web": "0.2.
|
|
22
|
-
"@jskit-ai/kernel": "0.2.
|
|
23
|
-
"@jskit-ai/realtime": "0.2.
|
|
24
|
-
"@jskit-ai/shell-web": "0.2.
|
|
21
|
+
"@jskit-ai/http-runtime": "0.2.4",
|
|
22
|
+
"@jskit-ai/http-web": "0.2.8",
|
|
23
|
+
"@jskit-ai/kernel": "0.2.4",
|
|
24
|
+
"@jskit-ai/realtime": "0.2.7",
|
|
25
|
+
"@jskit-ai/shell-web": "0.2.4",
|
|
25
26
|
"@tanstack/vue-query": "^5.90.5",
|
|
26
27
|
"vue": "^3.5.13",
|
|
27
28
|
"vuetify": "^4.0.0",
|
|
28
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
29
|
+
"@jskit-ai/database-runtime": "0.1.253"
|
|
29
30
|
},
|
|
30
31
|
"description": "Capability-based assistant runtime with per-surface chat, transcripts, settings, and action tools.",
|
|
31
32
|
"jskit": {
|
|
@@ -38,8 +39,10 @@
|
|
|
38
39
|
"runtime.actions",
|
|
39
40
|
"runtime.config",
|
|
40
41
|
"runtime.env",
|
|
42
|
+
"runtime.events",
|
|
41
43
|
"runtime.http",
|
|
42
|
-
"runtime.http-web"
|
|
44
|
+
"runtime.http-web",
|
|
45
|
+
"runtime.logger"
|
|
43
46
|
]
|
|
44
47
|
},
|
|
45
48
|
"runtime": {
|
|
@@ -95,7 +98,11 @@
|
|
|
95
98
|
},
|
|
96
99
|
{
|
|
97
100
|
"subpath": "./server",
|
|
98
|
-
"summary": "Provides conversation routes, authorized shared realtime subscriptions, explicit Fastify hosting shorthand
|
|
101
|
+
"summary": "Provides conversation routes, authorized shared realtime subscriptions, explicit Fastify hosting shorthand, optional database integration and the per-surface assistant tool catalogue."
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"subpath": "./server/types",
|
|
105
|
+
"summary": "Type of the assistant.runtime capability, and the contracts of the optional capabilities it reads."
|
|
99
106
|
}
|
|
100
107
|
],
|
|
101
108
|
"capabilities": [
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { resolveActorId } from "@jskit-ai/kernel/server/actions/actionContributorHelpers";
|
|
1
2
|
import { defineFeature } from "@jskit-ai/kernel/server/features";
|
|
2
3
|
import { createAiConnectionResolver } from "@jskit-ai/connectors-catalog/server/ai";
|
|
3
4
|
import { createEnvironmentReferenceResolver } from "@jskit-ai/connectors-core/server";
|
|
@@ -10,9 +11,10 @@ export const ApplicationAiFeature = defineFeature({
|
|
|
10
11
|
return { ai: createAiConnectionResolver({
|
|
11
12
|
configuration,
|
|
12
13
|
authorize(context) {
|
|
13
|
-
|
|
14
|
+
const actorId = resolveActorId(context);
|
|
15
|
+
if (!actorId) return null;
|
|
14
16
|
// Apply the app's AI-use policy here; the actor comes from authentication.
|
|
15
|
-
return { applicationId: "my-app", subjectId:
|
|
17
|
+
return { applicationId: "my-app", subjectId: actorId };
|
|
16
18
|
},
|
|
17
19
|
resolveReference: createEnvironmentReferenceResolver(env)
|
|
18
20
|
}) };
|
|
@@ -4,6 +4,11 @@ import { AssistantConversationElement } from "@jskit-ai/assistant-core/client/co
|
|
|
4
4
|
import { useAssistantConversation } from "../composables/useAssistantConversation.js";
|
|
5
5
|
import { useJskitText } from "../i18n.js";
|
|
6
6
|
|
|
7
|
+
/** @typedef {import("@jskit-ai/assistant-core/client/types").AssistantApi} AssistantApi */
|
|
8
|
+
/** @typedef {import("@jskit-ai/assistant-core/client/types").AssistantAttachments} AssistantAttachments */
|
|
9
|
+
/** @typedef {import("@jskit-ai/assistant-core/client/types").AssistantSuggestions} AssistantSuggestions */
|
|
10
|
+
/** @typedef {import("@jskit-ai/assistant-core/client/types").AssistantModelChoices} AssistantModelChoices */
|
|
11
|
+
|
|
7
12
|
const props = defineProps({
|
|
8
13
|
conversationId: { type: String, required: true },
|
|
9
14
|
endpoint: { type: String, default: "" },
|
|
@@ -11,7 +16,8 @@ const props = defineProps({
|
|
|
11
16
|
hostSurfaceId: { type: String, default: "" },
|
|
12
17
|
workspaceSlug: { type: String, default: undefined },
|
|
13
18
|
actorKey: { type: String, default: undefined },
|
|
14
|
-
|
|
19
|
+
// An application's own assistant API; without one the binding uses the surface's standard API.
|
|
20
|
+
api: { type: /** @type {import("vue").PropType<AssistantApi>} */ (Object), default: null },
|
|
15
21
|
active: { type: Boolean, default: true },
|
|
16
22
|
clearDraftOn: { type: String, default: undefined, validator: value => ["dispatch", "accepted"].includes(value) },
|
|
17
23
|
// Unset texts follow the active language; an application's own text is shown as given.
|
|
@@ -20,9 +26,9 @@ const props = defineProps({
|
|
|
20
26
|
placeholder: { type: String, default: undefined },
|
|
21
27
|
layout: { type: String, default: "compact", validator: value => ["page", "compact"].includes(value) },
|
|
22
28
|
data: { type: Object, default: undefined },
|
|
23
|
-
attachments: { type: Object, default: null },
|
|
24
|
-
suggestions: { type: Object, default: null },
|
|
25
|
-
models: { type: Object, default: null },
|
|
29
|
+
attachments: { type: /** @type {import("vue").PropType<AssistantAttachments>} */ (Object), default: null },
|
|
30
|
+
suggestions: { type: /** @type {import("vue").PropType<AssistantSuggestions>} */ (Object), default: null },
|
|
31
|
+
models: { type: /** @type {import("vue").PropType<AssistantModelChoices>} */ (Object), default: null },
|
|
26
32
|
questions: { type: [Boolean, Object], default: null },
|
|
27
33
|
goal: { type: [Boolean, Object], default: null },
|
|
28
34
|
avatarSize: { type: String, default: "compact", validator: (value) => ["hidden", "compact", "standard", "large"].includes(value) }
|
|
@@ -6,6 +6,10 @@ import { mapAssistantConversationTurns, toolStatusText } from "../support/assist
|
|
|
6
6
|
import { DEFAULT_CONVERSATION_TITLE } from "../../shared/assistantRuntimeConfig.js";
|
|
7
7
|
import { useJskitText } from "../i18n.js";
|
|
8
8
|
|
|
9
|
+
/** @typedef {import("@jskit-ai/assistant-core/client/types").AssistantConversationAdapter} AssistantConversationAdapter */
|
|
10
|
+
/** @typedef {import("@jskit-ai/assistant-core/client/types").AssistantAttachments} AssistantAttachments */
|
|
11
|
+
/** @typedef {import("@jskit-ai/assistant-core/client/types").AssistantSuggestions} AssistantSuggestions */
|
|
12
|
+
|
|
9
13
|
const props = defineProps({
|
|
10
14
|
surfaceId: { type: String, required: true },
|
|
11
15
|
layout: { type: String, default: "page", validator: (value) => ["page", "compact"].includes(value) },
|
|
@@ -15,10 +19,10 @@ const props = defineProps({
|
|
|
15
19
|
placeholder: { type: String, default: undefined },
|
|
16
20
|
showToolActivity: { type: Boolean, default: true },
|
|
17
21
|
connections: { type: Array, default: () => [] },
|
|
18
|
-
attachments: { type: Object, default: null },
|
|
19
|
-
suggestions: { type: Object, default: null },
|
|
20
|
-
goal: { type: Object, default: null },
|
|
21
|
-
activity: { type: Object, default: undefined },
|
|
22
|
+
attachments: { type: /** @type {import("vue").PropType<AssistantAttachments>} */ (Object), default: null },
|
|
23
|
+
suggestions: { type: /** @type {import("vue").PropType<AssistantSuggestions>} */ (Object), default: null },
|
|
24
|
+
goal: { type: /** @type {import("vue").PropType<AssistantConversationAdapter["goal"]>} */ (Object), default: null },
|
|
25
|
+
activity: { type: /** @type {import("vue").PropType<AssistantConversationAdapter["activity"]>} */ (Object), default: undefined },
|
|
22
26
|
configurationMode: { type: String, default: "editable", validator: value => ["hidden", "readonly", "editable"].includes(value) }
|
|
23
27
|
});
|
|
24
28
|
const integrationId = defineModel("integrationId", { type: String, default: "" });
|