@jskit-ai/assistant-runtime 0.1.15 → 0.1.17
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/package.descriptor.mjs +10 -16
- package/package.json +8 -10
- package/src/client/components/AssistantSettingsClientElement.vue +11 -3
- package/src/client/composables/useAssistantRuntime.js +7 -3
- package/src/client/support/workspaceScopeSupport.js +38 -0
- package/src/server/AssistantProvider.js +5 -2
- package/src/server/registerRoutes.js +60 -26
- package/src/server/services/assistantConfigService.js +12 -3
- package/src/server/services/chatService.js +17 -11
- package/src/server/support/workspaceScopeSupport.js +35 -0
- package/test/lazyAppConfig.test.js +109 -3
- package/test/packageDescriptor.test.js +4 -0
package/package.descriptor.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export default Object.freeze({
|
|
2
2
|
packageVersion: 1,
|
|
3
3
|
packageId: "@jskit-ai/assistant-runtime",
|
|
4
|
-
version: "0.1.
|
|
4
|
+
version: "0.1.17",
|
|
5
5
|
kind: "runtime",
|
|
6
6
|
description: "Shared assistant runtime with per-surface assistant registration.",
|
|
7
7
|
dependsOn: [
|
|
@@ -10,9 +10,7 @@ export default Object.freeze({
|
|
|
10
10
|
"@jskit-ai/http-runtime",
|
|
11
11
|
"@jskit-ai/shell-web",
|
|
12
12
|
"@jskit-ai/users-core",
|
|
13
|
-
"@jskit-ai/users-web"
|
|
14
|
-
"@jskit-ai/workspaces-core",
|
|
15
|
-
"@jskit-ai/workspaces-web"
|
|
13
|
+
"@jskit-ai/users-web"
|
|
16
14
|
],
|
|
17
15
|
capabilities: {
|
|
18
16
|
provides: ["assistant.runtime"],
|
|
@@ -22,9 +20,7 @@ export default Object.freeze({
|
|
|
22
20
|
"auth.policy",
|
|
23
21
|
"runtime.http-client",
|
|
24
22
|
"users.core",
|
|
25
|
-
"users.web"
|
|
26
|
-
"workspaces.core",
|
|
27
|
-
"workspaces.web"
|
|
23
|
+
"users.web"
|
|
28
24
|
]
|
|
29
25
|
},
|
|
30
26
|
runtime: {
|
|
@@ -78,15 +74,13 @@ export default Object.freeze({
|
|
|
78
74
|
mutations: {
|
|
79
75
|
dependencies: {
|
|
80
76
|
runtime: {
|
|
81
|
-
"@jskit-ai/assistant-core": "0.1.
|
|
82
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
83
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
84
|
-
"@jskit-ai/kernel": "0.1.
|
|
85
|
-
"@jskit-ai/shell-web": "0.1.
|
|
86
|
-
"@jskit-ai/users-core": "0.1.
|
|
87
|
-
"@jskit-ai/users-web": "0.1.
|
|
88
|
-
"@jskit-ai/workspaces-core": "0.1.20",
|
|
89
|
-
"@jskit-ai/workspaces-web": "0.1.20",
|
|
77
|
+
"@jskit-ai/assistant-core": "0.1.22",
|
|
78
|
+
"@jskit-ai/database-runtime": "0.1.46",
|
|
79
|
+
"@jskit-ai/http-runtime": "0.1.45",
|
|
80
|
+
"@jskit-ai/kernel": "0.1.46",
|
|
81
|
+
"@jskit-ai/shell-web": "0.1.45",
|
|
82
|
+
"@jskit-ai/users-core": "0.1.56",
|
|
83
|
+
"@jskit-ai/users-web": "0.1.61",
|
|
90
84
|
"@tanstack/vue-query": "^5.90.5",
|
|
91
85
|
"vuetify": "^4.0.0"
|
|
92
86
|
},
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jskit-ai/assistant-runtime",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.17",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"exports": {
|
|
6
6
|
"./client": "./src/client/index.js",
|
|
@@ -8,15 +8,13 @@
|
|
|
8
8
|
"./server/actionIds": "./src/server/actionIds.js"
|
|
9
9
|
},
|
|
10
10
|
"dependencies": {
|
|
11
|
-
"@jskit-ai/assistant-core": "0.1.
|
|
12
|
-
"@jskit-ai/database-runtime": "0.1.
|
|
13
|
-
"@jskit-ai/http-runtime": "0.1.
|
|
14
|
-
"@jskit-ai/kernel": "0.1.
|
|
15
|
-
"@jskit-ai/shell-web": "0.1.
|
|
16
|
-
"@jskit-ai/users-core": "0.1.
|
|
17
|
-
"@jskit-ai/users-web": "0.1.
|
|
18
|
-
"@jskit-ai/workspaces-core": "0.1.20",
|
|
19
|
-
"@jskit-ai/workspaces-web": "0.1.20",
|
|
11
|
+
"@jskit-ai/assistant-core": "0.1.22",
|
|
12
|
+
"@jskit-ai/database-runtime": "0.1.46",
|
|
13
|
+
"@jskit-ai/http-runtime": "0.1.45",
|
|
14
|
+
"@jskit-ai/kernel": "0.1.46",
|
|
15
|
+
"@jskit-ai/shell-web": "0.1.45",
|
|
16
|
+
"@jskit-ai/users-core": "0.1.56",
|
|
17
|
+
"@jskit-ai/users-web": "0.1.61",
|
|
20
18
|
"@tanstack/vue-query": "^5.90.5",
|
|
21
19
|
"vuetify": "^4.0.0"
|
|
22
20
|
}
|
|
@@ -30,8 +30,9 @@ import { validateOperationSection } from "@jskit-ai/http-runtime/shared/validato
|
|
|
30
30
|
import { assistantHttpClient, createAssistantApi, AssistantSettingsFormCard } from "@jskit-ai/assistant-core/client";
|
|
31
31
|
import { assistantConfigResource, assistantSettingsQueryKey, buildAssistantApiPath } from "@jskit-ai/assistant-core/shared";
|
|
32
32
|
import { useShellWebErrorRuntime } from "@jskit-ai/shell-web/client/error";
|
|
33
|
-
import {
|
|
33
|
+
import { useSurfaceRouteContext } from "@jskit-ai/users-web/client/composables/useSurfaceRouteContext";
|
|
34
34
|
import { resolveAssistantSurfaceConfig } from "../../shared/assistantSurfaces.js";
|
|
35
|
+
import { useWorkspaceWebScopeSupport } from "../support/workspaceScopeSupport.js";
|
|
35
36
|
|
|
36
37
|
const props = defineProps({
|
|
37
38
|
targetSurfaceId: {
|
|
@@ -51,14 +52,17 @@ const fieldErrors = reactive({
|
|
|
51
52
|
const errorRuntime = useShellWebErrorRuntime();
|
|
52
53
|
const queryClient = useQueryClient();
|
|
53
54
|
const appConfig = getClientAppConfig();
|
|
54
|
-
const
|
|
55
|
+
const routeContext = useSurfaceRouteContext();
|
|
56
|
+
const workspaceScopeSupport = useWorkspaceWebScopeSupport();
|
|
57
|
+
const { placementContext, currentSurfaceId } = routeContext;
|
|
55
58
|
|
|
56
59
|
const assistantSurface = computed(() => resolveAssistantSurfaceConfig(appConfig, props.targetSurfaceId));
|
|
57
60
|
const placementSnapshot = computed(() => normalizeObject(placementContext.value));
|
|
61
|
+
const routeScope = computed(() => workspaceScopeSupport.readRouteScope(routeContext));
|
|
58
62
|
const scope = computed(() => {
|
|
59
63
|
const settingsRequiresWorkspace = assistantSurface.value?.settingsSurfaceRequiresWorkspace === true;
|
|
60
64
|
const workspaceSlug = settingsRequiresWorkspace
|
|
61
|
-
? normalizeText(
|
|
65
|
+
? normalizeText(routeScope.value.workspaceSlug).toLowerCase()
|
|
62
66
|
: "";
|
|
63
67
|
|
|
64
68
|
return {
|
|
@@ -154,6 +158,10 @@ const loadError = computed(() => {
|
|
|
154
158
|
}
|
|
155
159
|
|
|
156
160
|
if (assistantSurface.value?.settingsSurfaceRequiresWorkspace) {
|
|
161
|
+
if (workspaceScopeSupport.available !== true) {
|
|
162
|
+
return "Workspace support is not available for this assistant surface.";
|
|
163
|
+
}
|
|
164
|
+
|
|
157
165
|
return "Select a workspace to configure assistant settings.";
|
|
158
166
|
}
|
|
159
167
|
|
|
@@ -21,8 +21,9 @@ import {
|
|
|
21
21
|
} from "@jskit-ai/assistant-core/client";
|
|
22
22
|
import { useShellWebErrorRuntime } from "@jskit-ai/shell-web/client/error";
|
|
23
23
|
import { usePagedCollection } from "@jskit-ai/users-web/client/composables/usePagedCollection";
|
|
24
|
-
import {
|
|
24
|
+
import { useSurfaceRouteContext } from "@jskit-ai/users-web/client/composables/useSurfaceRouteContext";
|
|
25
25
|
import { resolveAssistantSurfaceConfig } from "../../shared/assistantSurfaces.js";
|
|
26
|
+
import { useWorkspaceWebScopeSupport } from "../support/workspaceScopeSupport.js";
|
|
26
27
|
|
|
27
28
|
const DEFAULT_STREAM_TIMEOUT_MS = 120_000;
|
|
28
29
|
const DEFAULT_HISTORY_PAGE_SIZE = 20;
|
|
@@ -234,7 +235,9 @@ function useAssistantRuntime({ api = null, surfaceId = "" } = {}) {
|
|
|
234
235
|
const runtimePolicy = resolveRuntimePolicy();
|
|
235
236
|
const queryClient = useQueryClient();
|
|
236
237
|
const errorRuntime = useShellWebErrorRuntime();
|
|
237
|
-
const
|
|
238
|
+
const routeContext = useSurfaceRouteContext();
|
|
239
|
+
const workspaceScopeSupport = useWorkspaceWebScopeSupport();
|
|
240
|
+
const { placementContext, currentSurfaceId } = routeContext;
|
|
238
241
|
const appConfig = getClientAppConfig();
|
|
239
242
|
|
|
240
243
|
const messages = ref([]);
|
|
@@ -250,9 +253,10 @@ function useAssistantRuntime({ api = null, surfaceId = "" } = {}) {
|
|
|
250
253
|
const assistantSurface = computed(() =>
|
|
251
254
|
resolveAssistantSurfaceConfig(appConfig, surfaceId)
|
|
252
255
|
);
|
|
256
|
+
const routeScope = computed(() => workspaceScopeSupport.readRouteScope(routeContext));
|
|
253
257
|
const runtimeScope = computed(() => {
|
|
254
258
|
const workspaceSlug = assistantSurface.value?.runtimeSurfaceRequiresWorkspace
|
|
255
|
-
? normalizeText(
|
|
259
|
+
? normalizeText(routeScope.value.workspaceSlug).toLowerCase()
|
|
256
260
|
: "";
|
|
257
261
|
|
|
258
262
|
return {
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { inject } from "vue";
|
|
2
|
+
|
|
3
|
+
const WORKSPACES_WEB_SCOPE_SUPPORT_INJECTION_KEY = "jskit.workspaces.web.scope-support";
|
|
4
|
+
|
|
5
|
+
const EMPTY_ROUTE_SCOPE = Object.freeze({
|
|
6
|
+
workspaceSlug: ""
|
|
7
|
+
});
|
|
8
|
+
|
|
9
|
+
const EMPTY_WORKSPACE_WEB_SCOPE_SUPPORT = Object.freeze({
|
|
10
|
+
available: false,
|
|
11
|
+
readRouteScope() {
|
|
12
|
+
return EMPTY_ROUTE_SCOPE;
|
|
13
|
+
}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
function isWorkspaceWebScopeSupport(value) {
|
|
17
|
+
return Boolean(value && typeof value.readRouteScope === "function");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function useWorkspaceWebScopeSupport({ required = false } = {}) {
|
|
21
|
+
const support = inject(WORKSPACES_WEB_SCOPE_SUPPORT_INJECTION_KEY, null);
|
|
22
|
+
if (isWorkspaceWebScopeSupport(support)) {
|
|
23
|
+
return support;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (required) {
|
|
27
|
+
throw new Error("Workspace web scope support is not available in Vue injection context.");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return EMPTY_WORKSPACE_WEB_SCOPE_SUPPORT;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export {
|
|
34
|
+
EMPTY_WORKSPACE_WEB_SCOPE_SUPPORT,
|
|
35
|
+
WORKSPACES_WEB_SCOPE_SUPPORT_INJECTION_KEY,
|
|
36
|
+
isWorkspaceWebScopeSupport,
|
|
37
|
+
useWorkspaceWebScopeSupport
|
|
38
|
+
};
|
|
@@ -12,6 +12,7 @@ import { createChatService } from "./services/chatService.js";
|
|
|
12
12
|
import { createTranscriptService } from "./services/transcriptService.js";
|
|
13
13
|
import { resolveAssistantAiConfig } from "./support/assistantServerConfig.js";
|
|
14
14
|
import { createSurfaceAwareToolCatalog } from "./support/createSurfaceAwareToolCatalog.js";
|
|
15
|
+
import { resolveWorkspaceServerScopeSupport } from "./support/workspaceScopeSupport.js";
|
|
15
16
|
|
|
16
17
|
function resolveGlobalAssistantConfig(scope) {
|
|
17
18
|
const appConfig = resolveAppConfig(scope);
|
|
@@ -108,7 +109,8 @@ class AssistantProvider {
|
|
|
108
109
|
createAssistantConfigService({
|
|
109
110
|
assistantConfigRepository: scope.make("assistant.config.repository"),
|
|
110
111
|
consoleService: scope.has("consoleService") ? scope.make("consoleService") : null,
|
|
111
|
-
resolveAppConfig: resolveCurrentAppConfig
|
|
112
|
+
resolveAppConfig: resolveCurrentAppConfig,
|
|
113
|
+
workspaceScopeSupport: resolveWorkspaceServerScopeSupport(scope)
|
|
112
114
|
})
|
|
113
115
|
);
|
|
114
116
|
|
|
@@ -129,7 +131,8 @@ class AssistantProvider {
|
|
|
129
131
|
transcriptService: scope.make("assistant.transcript.service"),
|
|
130
132
|
serviceToolCatalog: scope.make("assistant.service.tool-catalog"),
|
|
131
133
|
assistantConfigService: scope.make("assistant.config.service"),
|
|
132
|
-
resolveAppConfig: resolveCurrentAppConfig
|
|
134
|
+
resolveAppConfig: resolveCurrentAppConfig,
|
|
135
|
+
workspaceScopeSupport: resolveWorkspaceServerScopeSupport(scope)
|
|
133
136
|
})
|
|
134
137
|
);
|
|
135
138
|
|
|
@@ -2,8 +2,6 @@ import { AppError } from "@jskit-ai/kernel/server/runtime";
|
|
|
2
2
|
import { resolveAppConfig } from "@jskit-ai/kernel/server/support";
|
|
3
3
|
import { normalizeSurfaceId } from "@jskit-ai/kernel/shared/surface/registry";
|
|
4
4
|
import { withStandardErrorResponses } from "@jskit-ai/http-runtime/shared/validators/errorResponses";
|
|
5
|
-
import { buildWorkspaceInputFromRouteParams } from "@jskit-ai/workspaces-core/server/support/workspaceRouteInput";
|
|
6
|
-
import { workspaceSlugParamsValidator } from "@jskit-ai/workspaces-core/server/validators/routeParamsValidator";
|
|
7
5
|
import {
|
|
8
6
|
assistantConfigResource,
|
|
9
7
|
assistantResource,
|
|
@@ -18,17 +16,22 @@ import {
|
|
|
18
16
|
import { resolveAssistantSurfaceConfig } from "../shared/assistantSurfaces.js";
|
|
19
17
|
import { actionIds } from "./actionIds.js";
|
|
20
18
|
import { assistantSurfaceRouteParamsValidator } from "./inputValidators.js";
|
|
19
|
+
import { resolveWorkspaceServerScopeSupport } from "./support/workspaceScopeSupport.js";
|
|
21
20
|
|
|
22
|
-
function buildRouteParamsValidator(requiresWorkspace) {
|
|
21
|
+
function buildRouteParamsValidator(requiresWorkspace, workspaceScopeSupport = null) {
|
|
23
22
|
if (requiresWorkspace === true) {
|
|
24
|
-
|
|
23
|
+
if (!workspaceScopeSupport) {
|
|
24
|
+
throw new Error("Assistant workspace routes require workspace server scope support.");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return [workspaceScopeSupport.paramsValidator, assistantSurfaceRouteParamsValidator];
|
|
25
28
|
}
|
|
26
29
|
|
|
27
30
|
return assistantSurfaceRouteParamsValidator;
|
|
28
31
|
}
|
|
29
32
|
|
|
30
|
-
function buildConversationMessagesRouteParamsValidator(requiresWorkspace) {
|
|
31
|
-
const validators = buildRouteParamsValidator(requiresWorkspace);
|
|
33
|
+
function buildConversationMessagesRouteParamsValidator(requiresWorkspace, workspaceScopeSupport = null) {
|
|
34
|
+
const validators = buildRouteParamsValidator(requiresWorkspace, workspaceScopeSupport);
|
|
32
35
|
if (Array.isArray(validators)) {
|
|
33
36
|
return validators.concat(assistantResource.operations.conversationMessagesList.paramsValidator);
|
|
34
37
|
}
|
|
@@ -36,12 +39,16 @@ function buildConversationMessagesRouteParamsValidator(requiresWorkspace) {
|
|
|
36
39
|
return [validators, assistantResource.operations.conversationMessagesList.paramsValidator];
|
|
37
40
|
}
|
|
38
41
|
|
|
39
|
-
function readWorkspaceInput(request, requiresWorkspace) {
|
|
42
|
+
function readWorkspaceInput(request, requiresWorkspace, workspaceScopeSupport = null) {
|
|
40
43
|
if (requiresWorkspace !== true) {
|
|
41
44
|
return {};
|
|
42
45
|
}
|
|
43
46
|
|
|
44
|
-
|
|
47
|
+
if (!workspaceScopeSupport) {
|
|
48
|
+
throw new Error("Assistant workspace routes require workspace server scope support.");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return workspaceScopeSupport.buildInputFromRouteParams(request?.input?.params);
|
|
45
52
|
}
|
|
46
53
|
|
|
47
54
|
function requireAssistantSurface(appConfig = {}, targetSurfaceId = "") {
|
|
@@ -102,7 +109,15 @@ function sendPreStreamErrorResponse(reply, error) {
|
|
|
102
109
|
});
|
|
103
110
|
}
|
|
104
111
|
|
|
105
|
-
function resolveRouteRequestState(
|
|
112
|
+
function resolveRouteRequestState(
|
|
113
|
+
request,
|
|
114
|
+
{
|
|
115
|
+
resolveCurrentAppConfig = () => ({}),
|
|
116
|
+
kind = "runtime",
|
|
117
|
+
requiresWorkspace = false,
|
|
118
|
+
workspaceScopeSupport = null
|
|
119
|
+
} = {}
|
|
120
|
+
) {
|
|
106
121
|
const appConfig = resolveCurrentAppConfig();
|
|
107
122
|
const targetSurfaceId = normalizeSurfaceId(request?.input?.params?.surfaceId);
|
|
108
123
|
const assistantSurface = requireAssistantSurface(appConfig, targetSurfaceId);
|
|
@@ -126,18 +141,22 @@ function resolveRouteRequestState(request, { resolveCurrentAppConfig = () => ({}
|
|
|
126
141
|
hostSurfaceId,
|
|
127
142
|
actionInput: Object.freeze({
|
|
128
143
|
targetSurfaceId: assistantSurface.targetSurfaceId,
|
|
129
|
-
...readWorkspaceInput(request, requiresWorkspace)
|
|
144
|
+
...readWorkspaceInput(request, requiresWorkspace, workspaceScopeSupport)
|
|
130
145
|
})
|
|
131
146
|
});
|
|
132
147
|
}
|
|
133
148
|
|
|
134
|
-
function registerSettingsRoutes(
|
|
149
|
+
function registerSettingsRoutes(
|
|
150
|
+
router,
|
|
151
|
+
resolveCurrentAppConfig,
|
|
152
|
+
{ requiresWorkspace = false, workspaceScopeSupport = null } = {}
|
|
153
|
+
) {
|
|
135
154
|
const routeBase = resolveAssistantApiBasePath({
|
|
136
155
|
requiresWorkspace
|
|
137
156
|
});
|
|
138
157
|
const visibility = requiresWorkspace ? "workspace" : "public";
|
|
139
158
|
const routePath = `${routeBase}/:surfaceId/settings`;
|
|
140
|
-
const paramsValidator = buildRouteParamsValidator(requiresWorkspace);
|
|
159
|
+
const paramsValidator = buildRouteParamsValidator(requiresWorkspace, workspaceScopeSupport);
|
|
141
160
|
|
|
142
161
|
router.register(
|
|
143
162
|
"GET",
|
|
@@ -158,7 +177,8 @@ function registerSettingsRoutes(router, resolveCurrentAppConfig, { requiresWorks
|
|
|
158
177
|
const routeState = resolveRouteRequestState(request, {
|
|
159
178
|
resolveCurrentAppConfig,
|
|
160
179
|
kind: "settings",
|
|
161
|
-
requiresWorkspace
|
|
180
|
+
requiresWorkspace,
|
|
181
|
+
workspaceScopeSupport
|
|
162
182
|
});
|
|
163
183
|
|
|
164
184
|
const response = await request.executeAction({
|
|
@@ -198,7 +218,8 @@ function registerSettingsRoutes(router, resolveCurrentAppConfig, { requiresWorks
|
|
|
198
218
|
const routeState = resolveRouteRequestState(request, {
|
|
199
219
|
resolveCurrentAppConfig,
|
|
200
220
|
kind: "settings",
|
|
201
|
-
requiresWorkspace
|
|
221
|
+
requiresWorkspace,
|
|
222
|
+
workspaceScopeSupport
|
|
202
223
|
});
|
|
203
224
|
|
|
204
225
|
const response = await request.executeAction({
|
|
@@ -217,13 +238,17 @@ function registerSettingsRoutes(router, resolveCurrentAppConfig, { requiresWorks
|
|
|
217
238
|
);
|
|
218
239
|
}
|
|
219
240
|
|
|
220
|
-
function registerRuntimeRoutes(
|
|
241
|
+
function registerRuntimeRoutes(
|
|
242
|
+
router,
|
|
243
|
+
resolveCurrentAppConfig,
|
|
244
|
+
{ requiresWorkspace = false, workspaceScopeSupport = null } = {}
|
|
245
|
+
) {
|
|
221
246
|
const routeBase = resolveAssistantApiBasePath({
|
|
222
247
|
requiresWorkspace
|
|
223
248
|
});
|
|
224
249
|
const visibility = requiresWorkspace ? "workspace" : "public";
|
|
225
250
|
const surfaceRouteBase = `${routeBase}/:surfaceId`;
|
|
226
|
-
const paramsValidator = buildRouteParamsValidator(requiresWorkspace);
|
|
251
|
+
const paramsValidator = buildRouteParamsValidator(requiresWorkspace, workspaceScopeSupport);
|
|
227
252
|
|
|
228
253
|
router.register(
|
|
229
254
|
"POST",
|
|
@@ -242,7 +267,8 @@ function registerRuntimeRoutes(router, resolveCurrentAppConfig, { requiresWorksp
|
|
|
242
267
|
const routeState = resolveRouteRequestState(request, {
|
|
243
268
|
resolveCurrentAppConfig,
|
|
244
269
|
kind: "runtime",
|
|
245
|
-
requiresWorkspace
|
|
270
|
+
requiresWorkspace,
|
|
271
|
+
workspaceScopeSupport
|
|
246
272
|
});
|
|
247
273
|
const abortController = new AbortController();
|
|
248
274
|
const requestBody = request?.input?.body && typeof request.input.body === "object" ? request.input.body : {};
|
|
@@ -367,7 +393,8 @@ function registerRuntimeRoutes(router, resolveCurrentAppConfig, { requiresWorksp
|
|
|
367
393
|
const routeState = resolveRouteRequestState(request, {
|
|
368
394
|
resolveCurrentAppConfig,
|
|
369
395
|
kind: "runtime",
|
|
370
|
-
requiresWorkspace
|
|
396
|
+
requiresWorkspace,
|
|
397
|
+
workspaceScopeSupport
|
|
371
398
|
});
|
|
372
399
|
|
|
373
400
|
const response = await request.executeAction({
|
|
@@ -391,7 +418,7 @@ function registerRuntimeRoutes(router, resolveCurrentAppConfig, { requiresWorksp
|
|
|
391
418
|
{
|
|
392
419
|
auth: "required",
|
|
393
420
|
visibility,
|
|
394
|
-
paramsValidator: buildConversationMessagesRouteParamsValidator(requiresWorkspace),
|
|
421
|
+
paramsValidator: buildConversationMessagesRouteParamsValidator(requiresWorkspace, workspaceScopeSupport),
|
|
395
422
|
meta: {
|
|
396
423
|
tags: ["assistant"],
|
|
397
424
|
summary: "List assistant conversation messages."
|
|
@@ -405,7 +432,8 @@ function registerRuntimeRoutes(router, resolveCurrentAppConfig, { requiresWorksp
|
|
|
405
432
|
const routeState = resolveRouteRequestState(request, {
|
|
406
433
|
resolveCurrentAppConfig,
|
|
407
434
|
kind: "runtime",
|
|
408
|
-
requiresWorkspace
|
|
435
|
+
requiresWorkspace,
|
|
436
|
+
workspaceScopeSupport
|
|
409
437
|
});
|
|
410
438
|
|
|
411
439
|
const response = await request.executeAction({
|
|
@@ -432,19 +460,25 @@ function registerRoutes(app) {
|
|
|
432
460
|
|
|
433
461
|
const router = app.make("jskit.http.router");
|
|
434
462
|
const resolveCurrentAppConfig = () => resolveAppConfig(app);
|
|
463
|
+
const workspaceScopeSupport = resolveWorkspaceServerScopeSupport(app);
|
|
435
464
|
|
|
436
465
|
registerSettingsRoutes(router, resolveCurrentAppConfig, {
|
|
437
466
|
requiresWorkspace: false
|
|
438
467
|
});
|
|
439
|
-
registerSettingsRoutes(router, resolveCurrentAppConfig, {
|
|
440
|
-
requiresWorkspace: true
|
|
441
|
-
});
|
|
442
468
|
registerRuntimeRoutes(router, resolveCurrentAppConfig, {
|
|
443
469
|
requiresWorkspace: false
|
|
444
470
|
});
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
471
|
+
|
|
472
|
+
if (workspaceScopeSupport) {
|
|
473
|
+
registerSettingsRoutes(router, resolveCurrentAppConfig, {
|
|
474
|
+
requiresWorkspace: true,
|
|
475
|
+
workspaceScopeSupport
|
|
476
|
+
});
|
|
477
|
+
registerRuntimeRoutes(router, resolveCurrentAppConfig, {
|
|
478
|
+
requiresWorkspace: true,
|
|
479
|
+
workspaceScopeSupport
|
|
480
|
+
});
|
|
481
|
+
}
|
|
448
482
|
}
|
|
449
483
|
|
|
450
484
|
export { registerRoutes };
|
|
@@ -1,9 +1,14 @@
|
|
|
1
1
|
import { AppError, requireAuth } from "@jskit-ai/kernel/server/runtime";
|
|
2
2
|
import { normalizeObject, normalizeRecordId } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
-
import { resolveWorkspace } from "@jskit-ai/workspaces-core/server/support/resolveWorkspace";
|
|
4
3
|
import { resolveAssistantSurfaceConfig } from "../../shared/assistantSurfaces.js";
|
|
5
4
|
|
|
6
|
-
function createService({
|
|
5
|
+
function createService({
|
|
6
|
+
assistantConfigRepository,
|
|
7
|
+
consoleService = null,
|
|
8
|
+
appConfig = {},
|
|
9
|
+
resolveAppConfig = null,
|
|
10
|
+
workspaceScopeSupport = null
|
|
11
|
+
} = {}) {
|
|
7
12
|
if (!assistantConfigRepository || typeof assistantConfigRepository.findByScope !== "function") {
|
|
8
13
|
throw new Error("assistantConfigService requires assistantConfigRepository.findByScope().");
|
|
9
14
|
}
|
|
@@ -63,7 +68,11 @@ function createService({ assistantConfigRepository, consoleService = null, appCo
|
|
|
63
68
|
return null;
|
|
64
69
|
}
|
|
65
70
|
|
|
66
|
-
|
|
71
|
+
if (!workspaceScopeSupport || typeof workspaceScopeSupport.resolveWorkspace !== "function") {
|
|
72
|
+
throw new Error("assistant.config.service requires workspace server scope support for workspace-scoped settings.");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const resolvedWorkspace = workspace || workspaceScopeSupport.resolveWorkspace(context, input);
|
|
67
76
|
const workspaceId = normalizeRecordId(resolvedWorkspace?.id, { fallback: null });
|
|
68
77
|
if (!workspaceId) {
|
|
69
78
|
throw new AppError(409, "Workspace selection required.");
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { AppError } from "@jskit-ai/kernel/server/runtime";
|
|
2
2
|
import { normalizeObject, normalizeRecordId, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
|
-
import { resolveWorkspace } from "@jskit-ai/workspaces-core/server/support/resolveWorkspace";
|
|
4
3
|
import { resolveWorkspaceSlug } from "@jskit-ai/assistant-core/server";
|
|
5
4
|
import {
|
|
6
5
|
ASSISTANT_STREAM_EVENT_TYPES
|
|
@@ -553,7 +552,8 @@ function createChatService({
|
|
|
553
552
|
serviceToolCatalog,
|
|
554
553
|
assistantConfigService,
|
|
555
554
|
appConfig = {},
|
|
556
|
-
resolveAppConfig = null
|
|
555
|
+
resolveAppConfig = null,
|
|
556
|
+
workspaceScopeSupport = null
|
|
557
557
|
} = {}) {
|
|
558
558
|
if (!aiClientFactory || typeof aiClientFactory.resolveClient !== "function" || !transcriptService || !serviceToolCatalog || !assistantConfigService) {
|
|
559
559
|
throw new Error(
|
|
@@ -564,6 +564,18 @@ function createChatService({
|
|
|
564
564
|
const resolveCurrentAppConfig =
|
|
565
565
|
typeof resolveAppConfig === "function" ? resolveAppConfig : () => appConfig;
|
|
566
566
|
|
|
567
|
+
function resolveRuntimeWorkspace(assistantSurface, context = {}, input = {}) {
|
|
568
|
+
if (assistantSurface?.runtimeSurfaceRequiresWorkspace !== true) {
|
|
569
|
+
return null;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
if (!workspaceScopeSupport || typeof workspaceScopeSupport.resolveWorkspace !== "function") {
|
|
573
|
+
throw new Error("assistant.chat.service requires workspace server scope support for workspace-scoped assistant surfaces.");
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
return workspaceScopeSupport.resolveWorkspace(context, input);
|
|
577
|
+
}
|
|
578
|
+
|
|
567
579
|
async function streamChat(payload = {}, options = {}) {
|
|
568
580
|
const assistantSurface = requireAssistantSurface(resolveCurrentAppConfig(), payload?.targetSurfaceId);
|
|
569
581
|
const aiClient = aiClientFactory.resolveClient(assistantSurface.targetSurfaceId);
|
|
@@ -573,9 +585,7 @@ function createChatService({
|
|
|
573
585
|
|
|
574
586
|
const context = normalizeObject(options.context);
|
|
575
587
|
const assistantContext = buildAssistantActionContext(context, assistantSurface);
|
|
576
|
-
const workspace = assistantSurface
|
|
577
|
-
? resolveWorkspace(assistantContext, payload)
|
|
578
|
-
: null;
|
|
588
|
+
const workspace = resolveRuntimeWorkspace(assistantSurface, assistantContext, payload);
|
|
579
589
|
const source = normalizeStreamInput(payload);
|
|
580
590
|
const streamWriter = options.streamWriter;
|
|
581
591
|
if (!hasStreamWriter(streamWriter)) {
|
|
@@ -1010,9 +1020,7 @@ function createChatService({
|
|
|
1010
1020
|
const assistantSurface = requireAssistantSurface(resolveCurrentAppConfig(), options?.input?.targetSurfaceId);
|
|
1011
1021
|
const context = normalizeObject(options.context);
|
|
1012
1022
|
const assistantContext = buildAssistantActionContext(context, assistantSurface);
|
|
1013
|
-
const workspace = assistantSurface.
|
|
1014
|
-
? resolveWorkspace(assistantContext, options.input || {})
|
|
1015
|
-
: null;
|
|
1023
|
+
const workspace = resolveRuntimeWorkspace(assistantSurface, assistantContext, options.input || {});
|
|
1016
1024
|
return transcriptService.listConversationsForUser(assistantSurface, workspace, assistantContext.actor, query, {
|
|
1017
1025
|
context: assistantContext
|
|
1018
1026
|
});
|
|
@@ -1022,9 +1030,7 @@ function createChatService({
|
|
|
1022
1030
|
const assistantSurface = requireAssistantSurface(resolveCurrentAppConfig(), options?.input?.targetSurfaceId);
|
|
1023
1031
|
const context = normalizeObject(options.context);
|
|
1024
1032
|
const assistantContext = buildAssistantActionContext(context, assistantSurface);
|
|
1025
|
-
const workspace = assistantSurface.
|
|
1026
|
-
? resolveWorkspace(assistantContext, options.input || {})
|
|
1027
|
-
: null;
|
|
1033
|
+
const workspace = resolveRuntimeWorkspace(assistantSurface, assistantContext, options.input || {});
|
|
1028
1034
|
return transcriptService.getConversationMessagesForUser(
|
|
1029
1035
|
assistantSurface,
|
|
1030
1036
|
workspace,
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
const WORKSPACES_SERVER_SCOPE_SUPPORT_TOKEN = "workspaces.server.scope-support";
|
|
2
|
+
|
|
3
|
+
function isWorkspaceServerScopeSupport(value) {
|
|
4
|
+
return Boolean(
|
|
5
|
+
value &&
|
|
6
|
+
value.available === true &&
|
|
7
|
+
value.paramsValidator &&
|
|
8
|
+
typeof value.paramsValidator.normalize === "function" &&
|
|
9
|
+
typeof value.buildInputFromRouteParams === "function" &&
|
|
10
|
+
typeof value.resolveWorkspace === "function"
|
|
11
|
+
);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function resolveWorkspaceServerScopeSupport(scope = null, { required = false, caller = "assistant-runtime" } = {}) {
|
|
15
|
+
const support =
|
|
16
|
+
scope && typeof scope.has === "function" && typeof scope.make === "function" && scope.has(WORKSPACES_SERVER_SCOPE_SUPPORT_TOKEN)
|
|
17
|
+
? scope.make(WORKSPACES_SERVER_SCOPE_SUPPORT_TOKEN)
|
|
18
|
+
: null;
|
|
19
|
+
|
|
20
|
+
if (isWorkspaceServerScopeSupport(support)) {
|
|
21
|
+
return support;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (required) {
|
|
25
|
+
throw new Error(`${caller} requires ${WORKSPACES_SERVER_SCOPE_SUPPORT_TOKEN} for workspace-scoped assistant surfaces.`);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
WORKSPACES_SERVER_SCOPE_SUPPORT_TOKEN,
|
|
33
|
+
isWorkspaceServerScopeSupport,
|
|
34
|
+
resolveWorkspaceServerScopeSupport
|
|
35
|
+
};
|
|
@@ -23,6 +23,26 @@ function createAssistantAppConfig() {
|
|
|
23
23
|
};
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
function createWorkspaceServerScopeSupport() {
|
|
27
|
+
return Object.freeze({
|
|
28
|
+
available: true,
|
|
29
|
+
paramsValidator: Object.freeze({
|
|
30
|
+
normalize(value = {}) {
|
|
31
|
+
return {
|
|
32
|
+
workspaceSlug: String(value?.workspaceSlug || "").trim().toLowerCase()
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
}),
|
|
36
|
+
buildInputFromRouteParams(params = {}) {
|
|
37
|
+
const workspaceSlug = String(params?.workspaceSlug || "").trim().toLowerCase();
|
|
38
|
+
return workspaceSlug ? { workspaceSlug } : {};
|
|
39
|
+
},
|
|
40
|
+
resolveWorkspace(context = {}, input = {}) {
|
|
41
|
+
return input.workspace || context.workspace || null;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
26
46
|
test("registerRoutes resolves appConfig lazily when handlers run", async () => {
|
|
27
47
|
const routes = [];
|
|
28
48
|
let currentAppConfig = null;
|
|
@@ -41,10 +61,16 @@ test("registerRoutes resolves appConfig lazily when handlers run", async () => {
|
|
|
41
61
|
if (token === "appConfig") {
|
|
42
62
|
return currentAppConfig;
|
|
43
63
|
}
|
|
64
|
+
if (token === "workspaces.server.scope-support") {
|
|
65
|
+
return createWorkspaceServerScopeSupport();
|
|
66
|
+
}
|
|
44
67
|
throw new Error(`Unexpected token: ${token}`);
|
|
45
68
|
},
|
|
46
69
|
has(token) {
|
|
47
|
-
return
|
|
70
|
+
return (
|
|
71
|
+
(token === "appConfig" ? Boolean(currentAppConfig) : token === "jskit.http.router") ||
|
|
72
|
+
token === "workspaces.server.scope-support"
|
|
73
|
+
);
|
|
48
74
|
}
|
|
49
75
|
};
|
|
50
76
|
|
|
@@ -126,10 +152,16 @@ test("registerRoutes returns clear AppError payload for pre-stream assistant fai
|
|
|
126
152
|
if (token === "appConfig") {
|
|
127
153
|
return currentAppConfig;
|
|
128
154
|
}
|
|
155
|
+
if (token === "workspaces.server.scope-support") {
|
|
156
|
+
return createWorkspaceServerScopeSupport();
|
|
157
|
+
}
|
|
129
158
|
throw new Error(`Unexpected token: ${token}`);
|
|
130
159
|
},
|
|
131
160
|
has(token) {
|
|
132
|
-
return
|
|
161
|
+
return (
|
|
162
|
+
(token === "appConfig" ? Boolean(currentAppConfig) : token === "jskit.http.router") ||
|
|
163
|
+
token === "workspaces.server.scope-support"
|
|
164
|
+
);
|
|
133
165
|
}
|
|
134
166
|
};
|
|
135
167
|
|
|
@@ -216,7 +248,8 @@ test("chat service resolves appConfig lazily when conversations are listed", asy
|
|
|
216
248
|
},
|
|
217
249
|
serviceToolCatalog: {},
|
|
218
250
|
assistantConfigService: {},
|
|
219
|
-
resolveAppConfig: () => currentAppConfig
|
|
251
|
+
resolveAppConfig: () => currentAppConfig,
|
|
252
|
+
workspaceScopeSupport: createWorkspaceServerScopeSupport()
|
|
220
253
|
});
|
|
221
254
|
|
|
222
255
|
currentAppConfig = createAssistantAppConfig();
|
|
@@ -246,3 +279,76 @@ test("chat service resolves appConfig lazily when conversations are listed", asy
|
|
|
246
279
|
assert.equal(response.assistantSurface.targetSurfaceId, "admin");
|
|
247
280
|
assert.equal(response.workspace.slug, "dogandgroom");
|
|
248
281
|
});
|
|
282
|
+
|
|
283
|
+
test("chat service rejects workspace-scoped assistant surfaces when workspace support is unavailable", async () => {
|
|
284
|
+
const chatService = createChatService({
|
|
285
|
+
aiClientFactory: {
|
|
286
|
+
resolveClient() {
|
|
287
|
+
throw new Error("resolveClient should not be called when listing conversations.");
|
|
288
|
+
}
|
|
289
|
+
},
|
|
290
|
+
transcriptService: {
|
|
291
|
+
async listConversationsForUser() {
|
|
292
|
+
throw new Error("listConversationsForUser should not be called without workspace support.");
|
|
293
|
+
}
|
|
294
|
+
},
|
|
295
|
+
serviceToolCatalog: {},
|
|
296
|
+
assistantConfigService: {},
|
|
297
|
+
resolveAppConfig: () => createAssistantAppConfig()
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
await assert.rejects(
|
|
301
|
+
() =>
|
|
302
|
+
chatService.listConversations(
|
|
303
|
+
{
|
|
304
|
+
limit: 20
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
input: {
|
|
308
|
+
targetSurfaceId: "admin",
|
|
309
|
+
workspaceSlug: "dogandgroom"
|
|
310
|
+
},
|
|
311
|
+
context: {
|
|
312
|
+
actor: {
|
|
313
|
+
authenticated: true,
|
|
314
|
+
userId: 42
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
),
|
|
319
|
+
/workspace server scope support/
|
|
320
|
+
);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test("registerRoutes omits workspace assistant routes when workspace scope support is unavailable", () => {
|
|
324
|
+
const routes = [];
|
|
325
|
+
const app = {
|
|
326
|
+
make(token) {
|
|
327
|
+
if (token === "jskit.http.router") {
|
|
328
|
+
return {
|
|
329
|
+
register(method, path, options, handler) {
|
|
330
|
+
routes.push({ method, path, options, handler });
|
|
331
|
+
}
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
if (token === "appConfig") {
|
|
335
|
+
return createAssistantAppConfig();
|
|
336
|
+
}
|
|
337
|
+
throw new Error(`Unexpected token: ${token}`);
|
|
338
|
+
},
|
|
339
|
+
has(token) {
|
|
340
|
+
return token === "jskit.http.router" || token === "appConfig";
|
|
341
|
+
}
|
|
342
|
+
};
|
|
343
|
+
|
|
344
|
+
registerRoutes(app);
|
|
345
|
+
|
|
346
|
+
assert.equal(
|
|
347
|
+
routes.some((entry) => entry.path.startsWith("/api/w/:workspaceSlug/assistant/")),
|
|
348
|
+
false
|
|
349
|
+
);
|
|
350
|
+
assert.equal(
|
|
351
|
+
routes.some((entry) => entry.path.startsWith("/api/assistant/")),
|
|
352
|
+
true
|
|
353
|
+
);
|
|
354
|
+
});
|
|
@@ -15,6 +15,10 @@ function findFileMutation(id) {
|
|
|
15
15
|
test("assistant-runtime descriptor registers runtime providers and initializes assistant config roots", () => {
|
|
16
16
|
assert.equal(descriptor.kind, "runtime");
|
|
17
17
|
assert.equal(descriptor.packageId, "@jskit-ai/assistant-runtime");
|
|
18
|
+
assert.equal(descriptor.dependsOn.includes("@jskit-ai/workspaces-core"), false);
|
|
19
|
+
assert.equal(descriptor.dependsOn.includes("@jskit-ai/workspaces-web"), false);
|
|
20
|
+
assert.equal(descriptor.capabilities?.requires?.includes("workspaces.core"), false);
|
|
21
|
+
assert.equal(descriptor.capabilities?.requires?.includes("workspaces.web"), false);
|
|
18
22
|
assert.equal(descriptor.runtime?.server?.providers?.[0]?.entrypoint, "src/server/AssistantProvider.js");
|
|
19
23
|
assert.equal(descriptor.runtime?.client?.providers?.[0]?.entrypoint, "src/client/providers/AssistantClientProvider.js");
|
|
20
24
|
|