@jskit-ai/assistant 0.1.4
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 +284 -0
- package/package.json +31 -0
- package/src/client/components/AssistantClientElement.vue +1316 -0
- package/src/client/components/AssistantConsoleSettingsClientElement.vue +71 -0
- package/src/client/components/AssistantSettingsFormCard.vue +76 -0
- package/src/client/components/AssistantWorkspaceClientElement.vue +15 -0
- package/src/client/components/AssistantWorkspaceSettingsClientElement.vue +73 -0
- package/src/client/composables/useAssistantWorkspaceRuntime.js +789 -0
- package/src/client/index.js +12 -0
- package/src/client/lib/assistantApi.js +137 -0
- package/src/client/lib/assistantHttpClient.js +10 -0
- package/src/client/lib/markdownRenderer.js +31 -0
- package/src/client/providers/AssistantWebClientProvider.js +25 -0
- package/src/server/AssistantServiceProvider.js +179 -0
- package/src/server/actionIds.js +11 -0
- package/src/server/actions.js +191 -0
- package/src/server/diTokens.js +19 -0
- package/src/server/lib/aiClient.js +43 -0
- package/src/server/lib/ndjson.js +47 -0
- package/src/server/lib/providers/anthropicClient.js +375 -0
- package/src/server/lib/providers/common.js +158 -0
- package/src/server/lib/providers/deepSeekClient.js +22 -0
- package/src/server/lib/providers/openAiClient.js +13 -0
- package/src/server/lib/providers/openAiCompatibleClient.js +69 -0
- package/src/server/lib/resolveWorkspaceSlug.js +24 -0
- package/src/server/lib/serviceToolCatalog.js +459 -0
- package/src/server/registerRoutes.js +384 -0
- package/src/server/repositories/assistantSettingsRepository.js +100 -0
- package/src/server/repositories/conversationsRepository.js +244 -0
- package/src/server/repositories/messagesRepository.js +154 -0
- package/src/server/repositories/repositoryPersistenceUtils.js +63 -0
- package/src/server/services/assistantSettingsService.js +153 -0
- package/src/server/services/chatService.js +987 -0
- package/src/server/services/transcriptService.js +334 -0
- package/src/shared/assistantPaths.js +50 -0
- package/src/shared/assistantResource.js +323 -0
- package/src/shared/assistantSettingsResource.js +214 -0
- package/src/shared/index.js +39 -0
- package/src/shared/queryKeys.js +69 -0
- package/src/shared/settingsEvents.js +7 -0
- package/src/shared/streamEvents.js +31 -0
- package/src/shared/support/positiveInteger.js +9 -0
- package/templates/migrations/assistant_settings_initial.cjs +39 -0
- package/templates/migrations/assistant_transcripts_initial.cjs +51 -0
- package/templates/src/pages/admin/workspace/assistant/index.vue +7 -0
- package/test/aiConfigValidation.test.js +15 -0
- package/test/assistantApiSurfaceHeader.test.js +64 -0
- package/test/assistantResource.test.js +53 -0
- package/test/assistantSettingsResource.test.js +48 -0
- package/test/assistantSettingsService.test.js +133 -0
- package/test/chatService.test.js +841 -0
- package/test/descriptorSurfaceOption.test.js +35 -0
- package/test/queryKeys.test.js +41 -0
- package/test/resolveWorkspaceSlug.test.js +83 -0
- package/test/routeInputContracts.test.js +287 -0
- package/test/serviceToolCatalog.test.js +1235 -0
- package/test/transcriptService.test.js +175 -0
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import descriptor from "../package.descriptor.mjs";
|
|
4
|
+
|
|
5
|
+
function findFileMutation(id) {
|
|
6
|
+
const mutations = Array.isArray(descriptor?.mutations?.files) ? descriptor.mutations.files : [];
|
|
7
|
+
return mutations.find((entry) => String(entry?.id || "") === id) || null;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function findTextMutation(id) {
|
|
11
|
+
const mutations = Array.isArray(descriptor?.mutations?.text) ? descriptor.mutations.text : [];
|
|
12
|
+
return mutations.find((entry) => String(entry?.id || "") === id) || null;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
test("assistant descriptor exposes configurable workspace surface option", () => {
|
|
16
|
+
const option = descriptor?.options?.surfaces;
|
|
17
|
+
assert.equal(option?.required, true);
|
|
18
|
+
assert.equal(option?.inputType, "text");
|
|
19
|
+
assert.equal(option?.defaultValue, "admin");
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
test("assistant descriptor routes workspace page + placements through surface option", () => {
|
|
23
|
+
const workspacePage = findFileMutation("assistant-page-admin-workspace-assistant-index");
|
|
24
|
+
const menuPlacement = findTextMutation("assistant-placement-menu");
|
|
25
|
+
const workspaceSettingsPlacement = findTextMutation("assistant-workspace-settings-form-placement");
|
|
26
|
+
const consoleSettingsPlacement = findTextMutation("assistant-console-settings-form-placement");
|
|
27
|
+
|
|
28
|
+
assert.equal(workspacePage?.toSurface, "${option:surfaces|lower}");
|
|
29
|
+
assert.match(String(menuPlacement?.value || ""), /"\$\{option:surfaces\|lower\}"/);
|
|
30
|
+
assert.match(String(menuPlacement?.value || ""), /\.split\(","\)/);
|
|
31
|
+
assert.match(String(menuPlacement?.value || ""), /surfaces: assistantWorkspaceSurfaceIds\.length > 0 \? assistantWorkspaceSurfaceIds : \["\*"\]/);
|
|
32
|
+
assert.doesNotMatch(String(menuPlacement?.value || ""), /surface:\s*"/);
|
|
33
|
+
assert.match(String(workspaceSettingsPlacement?.value || ""), /surfaces: \["\*"\]/);
|
|
34
|
+
assert.match(String(consoleSettingsPlacement?.value || ""), /surfaces: \["\*"\]/);
|
|
35
|
+
});
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import {
|
|
4
|
+
ASSISTANT_QUERY_KEY_PREFIX,
|
|
5
|
+
assistantRootQueryKey,
|
|
6
|
+
assistantWorkspaceScopeQueryKey,
|
|
7
|
+
assistantConversationsListQueryKey,
|
|
8
|
+
assistantConversationMessagesQueryKey
|
|
9
|
+
} from "../src/shared/queryKeys.js";
|
|
10
|
+
|
|
11
|
+
test("assistant query keys normalize workspace scope and paging", () => {
|
|
12
|
+
assert.deepEqual(ASSISTANT_QUERY_KEY_PREFIX, ["assistant"]);
|
|
13
|
+
assert.deepEqual(assistantRootQueryKey(), ["assistant"]);
|
|
14
|
+
|
|
15
|
+
assert.deepEqual(assistantWorkspaceScopeQueryKey({ workspaceSlug: "acme" }), ["assistant", "slug:acme"]);
|
|
16
|
+
assert.deepEqual(assistantWorkspaceScopeQueryKey({ workspaceId: "9" }), ["assistant", "id:9"]);
|
|
17
|
+
|
|
18
|
+
assert.deepEqual(assistantConversationsListQueryKey({ workspaceSlug: "acme" }), [
|
|
19
|
+
"assistant",
|
|
20
|
+
"slug:acme",
|
|
21
|
+
"conversations",
|
|
22
|
+
"list",
|
|
23
|
+
20,
|
|
24
|
+
"all"
|
|
25
|
+
]);
|
|
26
|
+
|
|
27
|
+
assert.deepEqual(
|
|
28
|
+
assistantConversationsListQueryKey({ workspaceId: 9 }, { limit: 10, status: "ACTIVE" }),
|
|
29
|
+
["assistant", "id:9", "conversations", "list", 10, "active"]
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
assert.deepEqual(assistantConversationMessagesQueryKey({ workspaceSlug: "acme" }, "22"), [
|
|
33
|
+
"assistant",
|
|
34
|
+
"slug:acme",
|
|
35
|
+
"conversations",
|
|
36
|
+
"22",
|
|
37
|
+
"messages",
|
|
38
|
+
1,
|
|
39
|
+
200
|
|
40
|
+
]);
|
|
41
|
+
});
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import test from "node:test";
|
|
2
|
+
import assert from "node:assert/strict";
|
|
3
|
+
import { resolveWorkspaceSlug } from "../src/server/lib/resolveWorkspaceSlug.js";
|
|
4
|
+
|
|
5
|
+
test("resolveWorkspaceSlug follows fallback priority", () => {
|
|
6
|
+
const slug = resolveWorkspaceSlug(
|
|
7
|
+
{
|
|
8
|
+
workspace: {
|
|
9
|
+
slug: "workspace-primary"
|
|
10
|
+
},
|
|
11
|
+
requestMeta: {
|
|
12
|
+
resolvedWorkspaceContext: {
|
|
13
|
+
workspace: {
|
|
14
|
+
slug: "workspace-resolved"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
request: {
|
|
18
|
+
input: {
|
|
19
|
+
params: {
|
|
20
|
+
workspaceSlug: "workspace-request"
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
workspaceSlug: "workspace-input"
|
|
28
|
+
}
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
assert.equal(slug, "workspace-primary");
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test("resolveWorkspaceSlug resolves from requestMeta resolved workspace", () => {
|
|
35
|
+
const slug = resolveWorkspaceSlug({
|
|
36
|
+
requestMeta: {
|
|
37
|
+
resolvedWorkspaceContext: {
|
|
38
|
+
workspace: {
|
|
39
|
+
slug: "workspace-resolved"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
assert.equal(slug, "workspace-resolved");
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("resolveWorkspaceSlug resolves from action input before request params", () => {
|
|
49
|
+
const slug = resolveWorkspaceSlug(
|
|
50
|
+
{
|
|
51
|
+
requestMeta: {
|
|
52
|
+
request: {
|
|
53
|
+
input: {
|
|
54
|
+
params: {
|
|
55
|
+
workspaceSlug: "workspace-request"
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
workspaceSlug: "workspace-input"
|
|
63
|
+
}
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
assert.equal(slug, "workspace-input");
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test("resolveWorkspaceSlug resolves from request params when needed", () => {
|
|
70
|
+
const slug = resolveWorkspaceSlug({
|
|
71
|
+
requestMeta: {
|
|
72
|
+
request: {
|
|
73
|
+
input: {
|
|
74
|
+
params: {
|
|
75
|
+
workspaceSlug: "workspace-request"
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
assert.equal(slug, "workspace-request");
|
|
83
|
+
});
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { KERNEL_TOKENS } from "@jskit-ai/kernel/shared/support/tokens";
|
|
4
|
+
import { registerRoutes } from "../src/server/registerRoutes.js";
|
|
5
|
+
|
|
6
|
+
function createReplyDouble() {
|
|
7
|
+
return {
|
|
8
|
+
statusCode: 200,
|
|
9
|
+
payload: null,
|
|
10
|
+
code(statusCode) {
|
|
11
|
+
this.statusCode = statusCode;
|
|
12
|
+
return this;
|
|
13
|
+
},
|
|
14
|
+
send(payload) {
|
|
15
|
+
this.payload = payload;
|
|
16
|
+
return this;
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function findRoute(routes, method, path) {
|
|
22
|
+
return routes.find((route) => route.method === method && route.path === path) || null;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
test("assistant routes build list inputs with explicit query object", async () => {
|
|
26
|
+
const registeredRoutes = [];
|
|
27
|
+
const router = {
|
|
28
|
+
register(method, path, route, handler) {
|
|
29
|
+
registeredRoutes.push({
|
|
30
|
+
method,
|
|
31
|
+
path,
|
|
32
|
+
route,
|
|
33
|
+
handler
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
const app = {
|
|
38
|
+
make(token) {
|
|
39
|
+
if (token !== KERNEL_TOKENS.HttpRouter) {
|
|
40
|
+
throw new Error(`Unexpected token: ${String(token)}`);
|
|
41
|
+
}
|
|
42
|
+
return router;
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
registerRoutes(app);
|
|
47
|
+
|
|
48
|
+
const conversationsRoute = findRoute(registeredRoutes, "GET", "/api/w/:workspaceSlug/assistant/conversations");
|
|
49
|
+
const messagesRoute = findRoute(
|
|
50
|
+
registeredRoutes,
|
|
51
|
+
"GET",
|
|
52
|
+
"/api/w/:workspaceSlug/assistant/conversations/:conversationId/messages"
|
|
53
|
+
);
|
|
54
|
+
const consoleSettingsReadRoute = findRoute(registeredRoutes, "GET", "/api/console/settings/assistant");
|
|
55
|
+
const consoleSettingsPatchRoute = findRoute(registeredRoutes, "PATCH", "/api/console/settings/assistant");
|
|
56
|
+
const workspaceSettingsReadRoute = findRoute(
|
|
57
|
+
registeredRoutes,
|
|
58
|
+
"GET",
|
|
59
|
+
"/api/w/:workspaceSlug/workspace/settings/assistant"
|
|
60
|
+
);
|
|
61
|
+
const workspaceSettingsPatchRoute = findRoute(
|
|
62
|
+
registeredRoutes,
|
|
63
|
+
"PATCH",
|
|
64
|
+
"/api/w/:workspaceSlug/workspace/settings/assistant"
|
|
65
|
+
);
|
|
66
|
+
assert.ok(conversationsRoute);
|
|
67
|
+
assert.ok(messagesRoute);
|
|
68
|
+
assert.ok(consoleSettingsReadRoute);
|
|
69
|
+
assert.ok(consoleSettingsPatchRoute);
|
|
70
|
+
assert.ok(workspaceSettingsReadRoute);
|
|
71
|
+
assert.ok(workspaceSettingsPatchRoute);
|
|
72
|
+
|
|
73
|
+
const calls = [];
|
|
74
|
+
const executeAction = async (payload) => {
|
|
75
|
+
calls.push(payload);
|
|
76
|
+
return {};
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
await conversationsRoute.handler(
|
|
80
|
+
{
|
|
81
|
+
input: {
|
|
82
|
+
params: { workspaceSlug: "acme" },
|
|
83
|
+
query: { cursor: 30, limit: 50, status: "active" }
|
|
84
|
+
},
|
|
85
|
+
executeAction
|
|
86
|
+
},
|
|
87
|
+
createReplyDouble()
|
|
88
|
+
);
|
|
89
|
+
await messagesRoute.handler(
|
|
90
|
+
{
|
|
91
|
+
input: {
|
|
92
|
+
params: { workspaceSlug: "acme", conversationId: 10 },
|
|
93
|
+
query: { page: 3, pageSize: 100 }
|
|
94
|
+
},
|
|
95
|
+
executeAction
|
|
96
|
+
},
|
|
97
|
+
createReplyDouble()
|
|
98
|
+
);
|
|
99
|
+
await consoleSettingsReadRoute.handler(
|
|
100
|
+
{
|
|
101
|
+
input: {},
|
|
102
|
+
executeAction
|
|
103
|
+
},
|
|
104
|
+
createReplyDouble()
|
|
105
|
+
);
|
|
106
|
+
await consoleSettingsPatchRoute.handler(
|
|
107
|
+
{
|
|
108
|
+
input: {
|
|
109
|
+
body: { workspaceSurfacePrompt: "Console prompt" }
|
|
110
|
+
},
|
|
111
|
+
executeAction
|
|
112
|
+
},
|
|
113
|
+
createReplyDouble()
|
|
114
|
+
);
|
|
115
|
+
await workspaceSettingsReadRoute.handler(
|
|
116
|
+
{
|
|
117
|
+
input: {
|
|
118
|
+
params: { workspaceSlug: "acme" }
|
|
119
|
+
},
|
|
120
|
+
executeAction
|
|
121
|
+
},
|
|
122
|
+
createReplyDouble()
|
|
123
|
+
);
|
|
124
|
+
await workspaceSettingsPatchRoute.handler(
|
|
125
|
+
{
|
|
126
|
+
input: {
|
|
127
|
+
params: { workspaceSlug: "acme" },
|
|
128
|
+
body: { appSurfacePrompt: "Workspace prompt" }
|
|
129
|
+
},
|
|
130
|
+
executeAction
|
|
131
|
+
},
|
|
132
|
+
createReplyDouble()
|
|
133
|
+
);
|
|
134
|
+
|
|
135
|
+
assert.deepEqual(calls[0].input, {
|
|
136
|
+
workspaceSlug: "acme",
|
|
137
|
+
query: { cursor: 30, limit: 50, status: "active" }
|
|
138
|
+
});
|
|
139
|
+
assert.deepEqual(calls[0].context, {
|
|
140
|
+
surface: "app"
|
|
141
|
+
});
|
|
142
|
+
assert.deepEqual(calls[1].input, {
|
|
143
|
+
workspaceSlug: "acme",
|
|
144
|
+
conversationId: 10,
|
|
145
|
+
query: { page: 3, pageSize: 100 }
|
|
146
|
+
});
|
|
147
|
+
assert.deepEqual(calls[1].context, {
|
|
148
|
+
surface: "app"
|
|
149
|
+
});
|
|
150
|
+
assert.deepEqual(calls[2], {
|
|
151
|
+
actionId: "assistant.console.settings.read"
|
|
152
|
+
});
|
|
153
|
+
assert.deepEqual(calls[3], {
|
|
154
|
+
actionId: "assistant.console.settings.update",
|
|
155
|
+
input: {
|
|
156
|
+
payload: {
|
|
157
|
+
workspaceSurfacePrompt: "Console prompt"
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
});
|
|
161
|
+
assert.deepEqual(calls[4], {
|
|
162
|
+
actionId: "assistant.workspace.settings.read",
|
|
163
|
+
context: {
|
|
164
|
+
surface: "app"
|
|
165
|
+
},
|
|
166
|
+
input: {
|
|
167
|
+
workspaceSlug: "acme"
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
assert.deepEqual(calls[5], {
|
|
171
|
+
actionId: "assistant.workspace.settings.update",
|
|
172
|
+
context: {
|
|
173
|
+
surface: "app"
|
|
174
|
+
},
|
|
175
|
+
input: {
|
|
176
|
+
workspaceSlug: "acme",
|
|
177
|
+
patch: {
|
|
178
|
+
appSurfacePrompt: "Workspace prompt"
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
test("assistant workspace routes use workspace default surface and honor x-jskit-surface header", async () => {
|
|
185
|
+
const registeredRoutes = [];
|
|
186
|
+
const router = {
|
|
187
|
+
register(method, path, route, handler) {
|
|
188
|
+
registeredRoutes.push({
|
|
189
|
+
method,
|
|
190
|
+
path,
|
|
191
|
+
route,
|
|
192
|
+
handler
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
const app = {
|
|
197
|
+
has(token) {
|
|
198
|
+
return token === "appConfig";
|
|
199
|
+
},
|
|
200
|
+
make(token) {
|
|
201
|
+
if (token === KERNEL_TOKENS.HttpRouter) {
|
|
202
|
+
return router;
|
|
203
|
+
}
|
|
204
|
+
if (token === "appConfig") {
|
|
205
|
+
return {
|
|
206
|
+
surfaceDefaultId: "admin",
|
|
207
|
+
surfaceDefinitions: {
|
|
208
|
+
app: {
|
|
209
|
+
id: "app",
|
|
210
|
+
enabled: true,
|
|
211
|
+
requiresWorkspace: true
|
|
212
|
+
},
|
|
213
|
+
admin: {
|
|
214
|
+
id: "admin",
|
|
215
|
+
enabled: true,
|
|
216
|
+
requiresWorkspace: true
|
|
217
|
+
},
|
|
218
|
+
console: {
|
|
219
|
+
id: "console",
|
|
220
|
+
enabled: true,
|
|
221
|
+
requiresWorkspace: false
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
throw new Error(`Unexpected token: ${String(token)}`);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
registerRoutes(app);
|
|
231
|
+
|
|
232
|
+
const expectedWorkspaceRoutes = [
|
|
233
|
+
["GET", "/api/w/:workspaceSlug/workspace/settings/assistant"],
|
|
234
|
+
["PATCH", "/api/w/:workspaceSlug/workspace/settings/assistant"],
|
|
235
|
+
["POST", "/api/w/:workspaceSlug/assistant/chat/stream"],
|
|
236
|
+
["GET", "/api/w/:workspaceSlug/assistant/conversations"],
|
|
237
|
+
["GET", "/api/w/:workspaceSlug/assistant/conversations/:conversationId/messages"]
|
|
238
|
+
];
|
|
239
|
+
|
|
240
|
+
for (const [method, path] of expectedWorkspaceRoutes) {
|
|
241
|
+
const route = findRoute(registeredRoutes, method, path);
|
|
242
|
+
assert.ok(route);
|
|
243
|
+
assert.equal(route.route.surface, "admin");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const conversationsRoute = findRoute(registeredRoutes, "GET", "/api/w/:workspaceSlug/assistant/conversations");
|
|
247
|
+
const actionCalls = [];
|
|
248
|
+
const executeAction = async (payload) => {
|
|
249
|
+
actionCalls.push(payload);
|
|
250
|
+
return {};
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
await conversationsRoute.handler(
|
|
254
|
+
{
|
|
255
|
+
headers: {
|
|
256
|
+
"x-jskit-surface": "app"
|
|
257
|
+
},
|
|
258
|
+
input: {
|
|
259
|
+
params: { workspaceSlug: "acme" },
|
|
260
|
+
query: {}
|
|
261
|
+
},
|
|
262
|
+
executeAction
|
|
263
|
+
},
|
|
264
|
+
createReplyDouble()
|
|
265
|
+
);
|
|
266
|
+
await conversationsRoute.handler(
|
|
267
|
+
{
|
|
268
|
+
headers: {
|
|
269
|
+
"x-jskit-surface": "missing"
|
|
270
|
+
},
|
|
271
|
+
input: {
|
|
272
|
+
params: { workspaceSlug: "acme" },
|
|
273
|
+
query: {}
|
|
274
|
+
},
|
|
275
|
+
executeAction
|
|
276
|
+
},
|
|
277
|
+
createReplyDouble()
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
assert.equal(actionCalls.length, 2);
|
|
281
|
+
assert.deepEqual(actionCalls[0].context, {
|
|
282
|
+
surface: "app"
|
|
283
|
+
});
|
|
284
|
+
assert.deepEqual(actionCalls[1].context, {
|
|
285
|
+
surface: "admin"
|
|
286
|
+
});
|
|
287
|
+
});
|