@frockbot/configuration-core 0.1.3 → 0.2.0
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.json +3 -3
- package/src/bot-identity.test.ts +3 -4
- package/src/index.test.ts +567 -319
- package/src/index.ts +659 -446
- package/src/package-settings.test.ts +100 -0
- package/src/package-settings.ts +209 -28
package/src/index.test.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import {
|
|
3
|
-
capabilityAssignmentFailureV1,
|
|
4
3
|
ConfigurationDecodeError,
|
|
5
4
|
decodeBotConfigurationExecuteRpcV1,
|
|
6
5
|
decodeBotConfigurationReadRpcV1,
|
|
@@ -10,7 +9,6 @@ import {
|
|
|
10
9
|
decodeCompositionGenerationListViewV1,
|
|
11
10
|
decodeCompositionGenerationViewV1,
|
|
12
11
|
decodeRevertCompositionCommandV1,
|
|
13
|
-
decodeConnectionDependencyRequirementV1,
|
|
14
12
|
decodeConfigurationCommandV1,
|
|
15
13
|
decodeConfigurationQueryV1,
|
|
16
14
|
decodeOperationReceiptV1,
|
|
@@ -20,10 +18,14 @@ import {
|
|
|
20
18
|
decodeUserConfigurationReadRpcV1,
|
|
21
19
|
decodeUserSettingsViewV1,
|
|
22
20
|
initializeBotSettingsV1,
|
|
21
|
+
migrateStoredBotSettingsV1,
|
|
22
|
+
migrateStoredUserSettingsV1,
|
|
23
|
+
modelBindingFailureV1,
|
|
23
24
|
resolveBotExecutionPlanV1,
|
|
24
25
|
resolveBotModelBindingV1,
|
|
25
26
|
resolveEffectiveBotModelV1,
|
|
26
|
-
type
|
|
27
|
+
type ExecutionPackageDefinition,
|
|
28
|
+
type ModelBindingV1,
|
|
27
29
|
} from "./index.js";
|
|
28
30
|
import {
|
|
29
31
|
isApplicationDeploymentHash,
|
|
@@ -32,6 +34,190 @@ import {
|
|
|
32
34
|
isRpcIdentifier,
|
|
33
35
|
} from "./identifiers.js";
|
|
34
36
|
|
|
37
|
+
describe("stored configuration migrations", () => {
|
|
38
|
+
test("migrates the pre-account-wide User record without honouring removed features", () => {
|
|
39
|
+
// Literal durable shape from eb0283edcce5daea976a21a9f6a6414bedc6e2bc,
|
|
40
|
+
// the first parent of PR #134's merge commit.
|
|
41
|
+
const stored = {
|
|
42
|
+
schemaVersion: 1,
|
|
43
|
+
revision: 7,
|
|
44
|
+
profile: { name: "Existing User" },
|
|
45
|
+
packages: [
|
|
46
|
+
{
|
|
47
|
+
packageId: "provider-ollama-cloud",
|
|
48
|
+
version: "0.0.1",
|
|
49
|
+
state: "installed" as const,
|
|
50
|
+
values: { "web-search-max-results": 5 },
|
|
51
|
+
},
|
|
52
|
+
],
|
|
53
|
+
connections: [
|
|
54
|
+
{
|
|
55
|
+
connectionId: "ollama-1",
|
|
56
|
+
packageId: "provider-ollama-cloud",
|
|
57
|
+
connectionTypeId: "ollama-cloud-account",
|
|
58
|
+
displayName: "Work",
|
|
59
|
+
state: "ready" as const,
|
|
60
|
+
safeMetadata: {
|
|
61
|
+
region: "au",
|
|
62
|
+
dependentAssignments: [
|
|
63
|
+
{
|
|
64
|
+
botId: "bot-1",
|
|
65
|
+
generation: "assignment-1",
|
|
66
|
+
packageId: "provider-ollama-cloud",
|
|
67
|
+
capabilityId: "ollama-cloud-models",
|
|
68
|
+
claimOrder: 7,
|
|
69
|
+
status: "acknowledged",
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
newBotModelTemplate: {
|
|
76
|
+
connectionId: "ollama-1",
|
|
77
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
78
|
+
},
|
|
79
|
+
newBotModelTemplateSource: "user",
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
expect(
|
|
83
|
+
decodeUserSettingsViewV1(migrateStoredUserSettingsV1(stored)),
|
|
84
|
+
).toEqual({
|
|
85
|
+
schemaVersion: 1,
|
|
86
|
+
revision: 7,
|
|
87
|
+
profile: { name: "Existing User" },
|
|
88
|
+
packages: stored.packages,
|
|
89
|
+
connections: [
|
|
90
|
+
{
|
|
91
|
+
...stored.connections[0],
|
|
92
|
+
safeMetadata: { region: "au" },
|
|
93
|
+
},
|
|
94
|
+
],
|
|
95
|
+
});
|
|
96
|
+
expect(stored.connections[0]?.safeMetadata).toHaveProperty(
|
|
97
|
+
"dependentAssignments",
|
|
98
|
+
);
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test("keeps unknown User, Package, and Connection fields strict", () => {
|
|
102
|
+
const current = {
|
|
103
|
+
schemaVersion: 1,
|
|
104
|
+
revision: 0,
|
|
105
|
+
profile: { name: "Existing User" },
|
|
106
|
+
packages: [],
|
|
107
|
+
connections: [],
|
|
108
|
+
};
|
|
109
|
+
for (const stored of [
|
|
110
|
+
{ ...current, unknown: true },
|
|
111
|
+
{
|
|
112
|
+
...current,
|
|
113
|
+
packages: [
|
|
114
|
+
{
|
|
115
|
+
packageId: "settings",
|
|
116
|
+
version: "0.0.1",
|
|
117
|
+
state: "installed",
|
|
118
|
+
unknown: true,
|
|
119
|
+
},
|
|
120
|
+
],
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
...current,
|
|
124
|
+
connections: [
|
|
125
|
+
{
|
|
126
|
+
connectionId: "connection-1",
|
|
127
|
+
packageId: "provider-ollama-cloud",
|
|
128
|
+
connectionTypeId: "ollama-cloud-account",
|
|
129
|
+
displayName: "Work",
|
|
130
|
+
state: "ready",
|
|
131
|
+
safeMetadata: {},
|
|
132
|
+
unknown: true,
|
|
133
|
+
},
|
|
134
|
+
],
|
|
135
|
+
},
|
|
136
|
+
]) {
|
|
137
|
+
expect(() =>
|
|
138
|
+
decodeUserSettingsViewV1(migrateStoredUserSettingsV1(stored)),
|
|
139
|
+
).toThrow(ConfigurationDecodeError);
|
|
140
|
+
}
|
|
141
|
+
expect(migrateStoredUserSettingsV1(current)).toBe(current);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("migrates the pre-account-wide Bot record to an empty Package value bag", () => {
|
|
145
|
+
// Literal durable shape from eb0283edcce5daea976a21a9f6a6414bedc6e2bc,
|
|
146
|
+
// the first parent of PR #134's merge commit.
|
|
147
|
+
const stored = {
|
|
148
|
+
schemaVersion: 1,
|
|
149
|
+
botId: "primary",
|
|
150
|
+
revision: 4,
|
|
151
|
+
profile: { name: "Primary" },
|
|
152
|
+
notifications: { enabled: true },
|
|
153
|
+
assignments: [
|
|
154
|
+
{
|
|
155
|
+
assignmentId: "ollama-model",
|
|
156
|
+
packageId: "provider-ollama-cloud",
|
|
157
|
+
capabilityId: "ollama-cloud-models",
|
|
158
|
+
connectionId: "ollama-1",
|
|
159
|
+
state: "enabled",
|
|
160
|
+
},
|
|
161
|
+
],
|
|
162
|
+
assignmentOperations: [
|
|
163
|
+
{
|
|
164
|
+
commandId: "replace-model",
|
|
165
|
+
kind: "replacing",
|
|
166
|
+
assignmentId: "ollama-model",
|
|
167
|
+
state: "retrying",
|
|
168
|
+
target: {
|
|
169
|
+
assignmentId: "ollama-model",
|
|
170
|
+
packageId: "provider-ollama-cloud",
|
|
171
|
+
capabilityId: "ollama-cloud-models",
|
|
172
|
+
connectionId: "ollama-1",
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
model: {
|
|
177
|
+
connectionId: "ollama-1",
|
|
178
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
179
|
+
},
|
|
180
|
+
};
|
|
181
|
+
|
|
182
|
+
expect(decodeBotSettingsViewV1(migrateStoredBotSettingsV1(stored))).toEqual(
|
|
183
|
+
{
|
|
184
|
+
schemaVersion: 1,
|
|
185
|
+
botId: "primary",
|
|
186
|
+
revision: 4,
|
|
187
|
+
profile: { name: "Primary" },
|
|
188
|
+
notifications: { enabled: true },
|
|
189
|
+
packageValues: {},
|
|
190
|
+
},
|
|
191
|
+
);
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
test("keeps unknown Bot fields strict and current records unchanged", () => {
|
|
195
|
+
const current = initializeBotSettingsV1("primary");
|
|
196
|
+
expect(migrateStoredBotSettingsV1(current)).toBe(current);
|
|
197
|
+
const { packageValues: _packageValues, ...withoutPackageValues } = current;
|
|
198
|
+
expect(
|
|
199
|
+
decodeBotSettingsViewV1(
|
|
200
|
+
migrateStoredBotSettingsV1({
|
|
201
|
+
...withoutPackageValues,
|
|
202
|
+
modelAssignment: {
|
|
203
|
+
connectionId: "ollama-1",
|
|
204
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
205
|
+
},
|
|
206
|
+
}),
|
|
207
|
+
),
|
|
208
|
+
).toEqual(current);
|
|
209
|
+
expect(() =>
|
|
210
|
+
decodeBotSettingsViewV1(
|
|
211
|
+
migrateStoredBotSettingsV1({
|
|
212
|
+
...current,
|
|
213
|
+
assignments: [],
|
|
214
|
+
unknown: true,
|
|
215
|
+
}),
|
|
216
|
+
),
|
|
217
|
+
).toThrow(ConfigurationDecodeError);
|
|
218
|
+
});
|
|
219
|
+
});
|
|
220
|
+
|
|
35
221
|
describe("shared public identifier policy", () => {
|
|
36
222
|
test("accepts the bounded cross-runtime identifier grammar", () => {
|
|
37
223
|
expect(isPublicIdentifier("Bot_1.release-candidate")).toBe(true);
|
|
@@ -100,41 +286,74 @@ describe("configuration DTO seam", () => {
|
|
|
100
286
|
});
|
|
101
287
|
});
|
|
102
288
|
|
|
103
|
-
test("decodes exact
|
|
289
|
+
test("decodes exact Bot-scoped Package setting commands", () => {
|
|
104
290
|
const meta = {
|
|
105
291
|
schemaVersion: 1,
|
|
106
292
|
commandId: "operation-1",
|
|
107
293
|
expectedRevision: 2,
|
|
108
294
|
botId: "primary",
|
|
109
295
|
} as const;
|
|
110
|
-
const assignment = {
|
|
111
|
-
assignmentId: "mail",
|
|
112
|
-
packageId: "mail",
|
|
113
|
-
capabilityId: "send",
|
|
114
|
-
connectionId: "mail-1",
|
|
115
|
-
};
|
|
116
296
|
expect(
|
|
117
297
|
decodeConfigurationCommandV1({
|
|
118
298
|
...meta,
|
|
119
|
-
type: "bot/
|
|
120
|
-
|
|
299
|
+
type: "bot/set-package-settings",
|
|
300
|
+
packageId: "custom-models",
|
|
301
|
+
values: {
|
|
302
|
+
model: {
|
|
303
|
+
connectionId: "ollama-work",
|
|
304
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
305
|
+
},
|
|
306
|
+
},
|
|
121
307
|
}),
|
|
122
|
-
).toMatchObject({
|
|
123
|
-
|
|
308
|
+
).toMatchObject({
|
|
309
|
+
type: "bot/set-package-settings",
|
|
310
|
+
packageId: "custom-models",
|
|
311
|
+
values: { model: { connectionId: "ollama-work" } },
|
|
312
|
+
});
|
|
313
|
+
expect(() =>
|
|
124
314
|
decodeConfigurationCommandV1({
|
|
125
315
|
...meta,
|
|
126
|
-
type: "bot/
|
|
127
|
-
|
|
316
|
+
type: "bot/set-package-settings",
|
|
317
|
+
packageId: "custom-models",
|
|
318
|
+
values: {},
|
|
128
319
|
}),
|
|
129
|
-
).
|
|
130
|
-
expect(
|
|
320
|
+
).toThrow("values names no setting");
|
|
321
|
+
expect(
|
|
131
322
|
decodeConfigurationCommandV1({
|
|
132
323
|
...meta,
|
|
133
|
-
type: "bot/
|
|
134
|
-
|
|
135
|
-
|
|
324
|
+
type: "bot/set-package-settings",
|
|
325
|
+
packageId: "custom-models",
|
|
326
|
+
unset: ["model"],
|
|
136
327
|
}),
|
|
137
|
-
).
|
|
328
|
+
).toMatchObject({ unset: ["model"] });
|
|
329
|
+
for (const invalid of [
|
|
330
|
+
{ ...meta, type: "bot/set-package-settings", packageId: "custom-models" },
|
|
331
|
+
{
|
|
332
|
+
...meta,
|
|
333
|
+
type: "bot/set-package-settings",
|
|
334
|
+
packageId: "custom-models",
|
|
335
|
+
unset: [],
|
|
336
|
+
},
|
|
337
|
+
{
|
|
338
|
+
...meta,
|
|
339
|
+
type: "bot/set-package-settings",
|
|
340
|
+
packageId: "custom-models",
|
|
341
|
+
unset: ["model", "model"],
|
|
342
|
+
},
|
|
343
|
+
{
|
|
344
|
+
...meta,
|
|
345
|
+
type: "bot/set-package-settings",
|
|
346
|
+
packageId: "custom-models",
|
|
347
|
+
values: {
|
|
348
|
+
model: { connectionId: "ollama-work", providerModelId: "m" },
|
|
349
|
+
},
|
|
350
|
+
unset: ["model"],
|
|
351
|
+
},
|
|
352
|
+
]) {
|
|
353
|
+
expect(() => decodeConfigurationCommandV1(invalid)).toThrow(
|
|
354
|
+
ConfigurationDecodeError,
|
|
355
|
+
);
|
|
356
|
+
}
|
|
138
357
|
});
|
|
139
358
|
|
|
140
359
|
test("accepts provider model IDs through the catalog limit", () => {
|
|
@@ -143,49 +362,43 @@ describe("configuration DTO seam", () => {
|
|
|
143
362
|
expect(
|
|
144
363
|
decodeConfigurationCommandV1({
|
|
145
364
|
schemaVersion: 1,
|
|
146
|
-
type: "
|
|
365
|
+
type: "user/set-platform-model",
|
|
147
366
|
commandId: "command-model",
|
|
148
|
-
botId: "primary",
|
|
149
367
|
expectedRevision: 3,
|
|
150
368
|
model: { connectionId: "connection-1", providerModelId },
|
|
151
369
|
}),
|
|
152
370
|
).toMatchObject({ model: { providerModelId } });
|
|
153
371
|
});
|
|
154
372
|
|
|
155
|
-
test("decodes
|
|
373
|
+
test("decodes the provider-bootstrap platform model command", () => {
|
|
156
374
|
expect(
|
|
157
375
|
decodeConfigurationCommandV1({
|
|
158
376
|
schemaVersion: 1,
|
|
159
|
-
type: "
|
|
160
|
-
commandId: "
|
|
161
|
-
botId: "primary",
|
|
377
|
+
type: "user/set-platform-model",
|
|
378
|
+
commandId: "bootstrap-model",
|
|
162
379
|
expectedRevision: 2,
|
|
163
|
-
assignment: {
|
|
164
|
-
assignmentId: "ollama-model",
|
|
165
|
-
packageId: "provider-ollama-cloud",
|
|
166
|
-
capabilityId: "ollama-cloud-models",
|
|
167
|
-
connectionId: "ollama-work",
|
|
168
|
-
},
|
|
169
380
|
model: {
|
|
170
381
|
connectionId: "ollama-work",
|
|
171
382
|
providerModelId: "glm-5.3-flash:cloud",
|
|
172
383
|
},
|
|
173
384
|
}),
|
|
174
385
|
).toMatchObject({
|
|
175
|
-
type: "
|
|
176
|
-
assignment: { connectionId: "ollama-work" },
|
|
386
|
+
type: "user/set-platform-model",
|
|
177
387
|
model: { providerModelId: "glm-5.3-flash:cloud" },
|
|
178
388
|
});
|
|
179
|
-
expect(
|
|
389
|
+
expect(() =>
|
|
180
390
|
decodeConfigurationCommandV1({
|
|
181
391
|
schemaVersion: 1,
|
|
182
|
-
type: "
|
|
183
|
-
commandId: "
|
|
184
|
-
botId: "primary",
|
|
392
|
+
type: "user/set-platform-model",
|
|
393
|
+
commandId: "bootstrap-model",
|
|
185
394
|
expectedRevision: 3,
|
|
186
|
-
|
|
395
|
+
model: {
|
|
396
|
+
connectionId: "ollama-work",
|
|
397
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
398
|
+
extra: true,
|
|
399
|
+
},
|
|
187
400
|
}),
|
|
188
|
-
).
|
|
401
|
+
).toThrow(ConfigurationDecodeError);
|
|
189
402
|
});
|
|
190
403
|
|
|
191
404
|
test("rejects unversioned, malformed, and unknown commands", () => {
|
|
@@ -207,30 +420,6 @@ describe("configuration DTO seam", () => {
|
|
|
207
420
|
}
|
|
208
421
|
});
|
|
209
422
|
|
|
210
|
-
test("requires a source for new-Bot defaults and forbids an empty automatic choice", () => {
|
|
211
|
-
const command = {
|
|
212
|
-
schemaVersion: 1,
|
|
213
|
-
type: "user/set-new-bot-model",
|
|
214
|
-
commandId: "choose-default",
|
|
215
|
-
expectedRevision: 0,
|
|
216
|
-
} as const;
|
|
217
|
-
expect(() =>
|
|
218
|
-
decodeConfigurationCommandV1({
|
|
219
|
-
...command,
|
|
220
|
-
model: { connectionId: "connection-1", providerModelId: "model-1" },
|
|
221
|
-
}),
|
|
222
|
-
).toThrow("invalid fields");
|
|
223
|
-
expect(() =>
|
|
224
|
-
decodeConfigurationCommandV1({ ...command, source: "provider" }),
|
|
225
|
-
).toThrow("source must be user or auto");
|
|
226
|
-
expect(() =>
|
|
227
|
-
decodeConfigurationCommandV1({ ...command, source: "auto" }),
|
|
228
|
-
).toThrow("must name a model");
|
|
229
|
-
expect(
|
|
230
|
-
decodeConfigurationCommandV1({ ...command, source: "user" }),
|
|
231
|
-
).toMatchObject({ source: "user", model: undefined });
|
|
232
|
-
});
|
|
233
|
-
|
|
234
423
|
test("rejects unknown fields throughout configuration commands", () => {
|
|
235
424
|
const meta = {
|
|
236
425
|
schemaVersion: 1,
|
|
@@ -245,8 +434,7 @@ describe("configuration DTO seam", () => {
|
|
|
245
434
|
},
|
|
246
435
|
{
|
|
247
436
|
...meta,
|
|
248
|
-
type: "user/set-
|
|
249
|
-
source: "user",
|
|
437
|
+
type: "user/set-platform-model",
|
|
250
438
|
model: {
|
|
251
439
|
connectionId: "provider-1",
|
|
252
440
|
providerModelId: "model-1",
|
|
@@ -261,14 +449,11 @@ describe("configuration DTO seam", () => {
|
|
|
261
449
|
},
|
|
262
450
|
{
|
|
263
451
|
...meta,
|
|
264
|
-
type: "bot/
|
|
452
|
+
type: "bot/set-package-settings",
|
|
265
453
|
botId: "primary",
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
capabilityId: "gmail-tools",
|
|
270
|
-
extra: true,
|
|
271
|
-
},
|
|
454
|
+
packageId: "custom-models",
|
|
455
|
+
values: { model: "fast" },
|
|
456
|
+
extra: true,
|
|
272
457
|
},
|
|
273
458
|
{
|
|
274
459
|
...meta,
|
|
@@ -303,45 +488,6 @@ describe("configuration DTO seam", () => {
|
|
|
303
488
|
}
|
|
304
489
|
});
|
|
305
490
|
|
|
306
|
-
test("decodes versioned Connection dependency requirements", () => {
|
|
307
|
-
expect(
|
|
308
|
-
decodeConnectionDependencyRequirementV1({
|
|
309
|
-
schemaVersion: 1,
|
|
310
|
-
packageId: "composio",
|
|
311
|
-
packageVersion: "0.0.1",
|
|
312
|
-
capabilityId: "gmail-tools",
|
|
313
|
-
connectionTypeIds: ["gmail"],
|
|
314
|
-
}),
|
|
315
|
-
).toEqual({
|
|
316
|
-
schemaVersion: 1,
|
|
317
|
-
packageId: "composio",
|
|
318
|
-
packageVersion: "0.0.1",
|
|
319
|
-
capabilityId: "gmail-tools",
|
|
320
|
-
connectionTypeIds: ["gmail"],
|
|
321
|
-
});
|
|
322
|
-
for (const value of [
|
|
323
|
-
{
|
|
324
|
-
schemaVersion: 1,
|
|
325
|
-
packageId: "composio",
|
|
326
|
-
packageVersion: "0.0.1",
|
|
327
|
-
capabilityId: "gmail-tools",
|
|
328
|
-
connectionTypeIds: [],
|
|
329
|
-
},
|
|
330
|
-
{
|
|
331
|
-
schemaVersion: 1,
|
|
332
|
-
packageId: "composio",
|
|
333
|
-
packageVersion: "0.0.1",
|
|
334
|
-
capabilityId: "gmail-tools",
|
|
335
|
-
connectionTypeIds: ["gmail"],
|
|
336
|
-
extra: true,
|
|
337
|
-
},
|
|
338
|
-
]) {
|
|
339
|
-
expect(() => decodeConnectionDependencyRequirementV1(value)).toThrow(
|
|
340
|
-
ConfigurationDecodeError,
|
|
341
|
-
);
|
|
342
|
-
}
|
|
343
|
-
});
|
|
344
|
-
|
|
345
491
|
test("strictly decodes versioned Connection commands", () => {
|
|
346
492
|
expect(
|
|
347
493
|
decodeStartConnectionCommandV1({
|
|
@@ -388,20 +534,20 @@ describe("configuration DTO seam", () => {
|
|
|
388
534
|
}
|
|
389
535
|
});
|
|
390
536
|
|
|
391
|
-
test("rejects hidden and symbol fields across
|
|
392
|
-
const assignment = {
|
|
393
|
-
assignmentId: "mail",
|
|
394
|
-
packageId: "mail",
|
|
395
|
-
capabilityId: "send",
|
|
396
|
-
connectionId: "mail-1",
|
|
397
|
-
};
|
|
537
|
+
test("rejects hidden and symbol fields across Package-setting seams", () => {
|
|
398
538
|
const command = {
|
|
399
539
|
schemaVersion: 1,
|
|
400
|
-
type: "bot/
|
|
540
|
+
type: "bot/set-package-settings",
|
|
401
541
|
commandId: "command-1",
|
|
402
542
|
expectedRevision: 0,
|
|
403
543
|
botId: "primary",
|
|
404
|
-
|
|
544
|
+
packageId: "custom-models",
|
|
545
|
+
values: {
|
|
546
|
+
model: {
|
|
547
|
+
connectionId: "mail-1",
|
|
548
|
+
providerModelId: "model-1",
|
|
549
|
+
},
|
|
550
|
+
},
|
|
405
551
|
};
|
|
406
552
|
const receipt = {
|
|
407
553
|
schemaVersion: 1,
|
|
@@ -415,16 +561,14 @@ describe("configuration DTO seam", () => {
|
|
|
415
561
|
revision: 0,
|
|
416
562
|
profile: { name: "Primary" },
|
|
417
563
|
notifications: { enabled: false },
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
state: "retrying",
|
|
425
|
-
target: assignment,
|
|
564
|
+
packageValues: {
|
|
565
|
+
"custom-models": {
|
|
566
|
+
model: {
|
|
567
|
+
connectionId: "mail-1",
|
|
568
|
+
providerModelId: "model-1",
|
|
569
|
+
},
|
|
426
570
|
},
|
|
427
|
-
|
|
571
|
+
},
|
|
428
572
|
};
|
|
429
573
|
const rpc = {
|
|
430
574
|
schemaVersion: 1,
|
|
@@ -434,7 +578,6 @@ describe("configuration DTO seam", () => {
|
|
|
434
578
|
};
|
|
435
579
|
for (const [decode, candidate] of [
|
|
436
580
|
[decodeConfigurationCommandV1, command],
|
|
437
|
-
[decodeConfigurationCommandV1, { ...command, assignment }],
|
|
438
581
|
[decodeOperationReceiptV1, receipt],
|
|
439
582
|
[decodeBotSettingsViewV1, view],
|
|
440
583
|
[decodeBotConfigurationExecuteRpcV1, rpc],
|
|
@@ -445,20 +588,28 @@ describe("configuration DTO seam", () => {
|
|
|
445
588
|
const symbol = { ...candidate, [Symbol("extra")]: true };
|
|
446
589
|
expect(() => decode(symbol)).toThrow(ConfigurationDecodeError);
|
|
447
590
|
}
|
|
448
|
-
const
|
|
449
|
-
Object.defineProperty(
|
|
591
|
+
const hiddenValues = { ...command.values };
|
|
592
|
+
Object.defineProperty(hiddenValues, "hidden", { value: true });
|
|
450
593
|
expect(() =>
|
|
451
|
-
decodeConfigurationCommandV1({ ...command,
|
|
594
|
+
decodeConfigurationCommandV1({ ...command, values: hiddenValues }),
|
|
452
595
|
).toThrow(ConfigurationDecodeError);
|
|
453
|
-
const
|
|
596
|
+
const symbolModel = {
|
|
597
|
+
...command.values.model,
|
|
598
|
+
[Symbol("extra")]: true,
|
|
599
|
+
};
|
|
454
600
|
expect(() =>
|
|
455
601
|
decodeBotSettingsViewV1({
|
|
456
602
|
...view,
|
|
457
|
-
|
|
458
|
-
{
|
|
459
|
-
|
|
603
|
+
packageValues: {
|
|
604
|
+
"custom-models": { model: symbolModel },
|
|
605
|
+
},
|
|
460
606
|
}),
|
|
461
607
|
).toThrow(ConfigurationDecodeError);
|
|
608
|
+
const pollutedValues = Object.create({ inherited: true });
|
|
609
|
+
pollutedValues.model = command.values.model;
|
|
610
|
+
expect(() =>
|
|
611
|
+
decodeConfigurationCommandV1({ ...command, values: pollutedValues }),
|
|
612
|
+
).toThrow(ConfigurationDecodeError);
|
|
462
613
|
});
|
|
463
614
|
|
|
464
615
|
test("rejects unknown configuration RPC envelope fields", () => {
|
|
@@ -684,17 +835,9 @@ describe("configuration DTO seam", () => {
|
|
|
684
835
|
});
|
|
685
836
|
|
|
686
837
|
describe("Bot execution-plan authority", () => {
|
|
687
|
-
test("
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
providerModelId: "model-1",
|
|
691
|
-
};
|
|
692
|
-
const initialized = initializeBotSettingsV1("primary", template);
|
|
693
|
-
template.providerModelId = "model-2";
|
|
694
|
-
expect(initialized.model).toEqual({
|
|
695
|
-
connectionId: "provider-1",
|
|
696
|
-
providerModelId: "model-1",
|
|
697
|
-
});
|
|
838
|
+
test("initializes Bot settings without seeding a model choice", () => {
|
|
839
|
+
const initialized = initializeBotSettingsV1("primary");
|
|
840
|
+
expect(initialized.packageValues).toEqual({});
|
|
698
841
|
expect(initialized.notifications).toEqual({ enabled: true });
|
|
699
842
|
});
|
|
700
843
|
|
|
@@ -704,28 +847,44 @@ describe("Bot execution-plan authority", () => {
|
|
|
704
847
|
revision: 4,
|
|
705
848
|
profile: { name: "Primary" },
|
|
706
849
|
notifications: { enabled: false },
|
|
707
|
-
|
|
708
|
-
{
|
|
709
|
-
assignmentId: "gmail-assignment",
|
|
710
|
-
packageId: "composio",
|
|
711
|
-
capabilityId: "gmail-tools",
|
|
712
|
-
connectionId: "gmail-connection",
|
|
713
|
-
state: "enabled" as const,
|
|
714
|
-
},
|
|
715
|
-
],
|
|
716
|
-
assignmentOperations: [],
|
|
850
|
+
packageValues: {},
|
|
717
851
|
};
|
|
718
|
-
const packages = [
|
|
852
|
+
const packages: ExecutionPackageDefinition[] = [
|
|
719
853
|
{
|
|
720
854
|
packageId: "composio",
|
|
721
855
|
version: "0.0.1",
|
|
722
|
-
|
|
856
|
+
settings: [],
|
|
857
|
+
capabilities: [
|
|
858
|
+
{ id: "clock", kind: "tool", connectionTypes: [] },
|
|
859
|
+
{ id: "gmail-tools", kind: "tool", connectionTypes: ["gmail"] },
|
|
860
|
+
],
|
|
723
861
|
connectionTypes: [{ id: "gmail", capabilities: ["gmail-tools"] }],
|
|
724
862
|
},
|
|
725
863
|
];
|
|
726
864
|
|
|
727
|
-
|
|
728
|
-
|
|
865
|
+
const modelPackages: ExecutionPackageDefinition[] = [
|
|
866
|
+
{
|
|
867
|
+
packageId: "provider-ollama-cloud",
|
|
868
|
+
version: "0.0.1",
|
|
869
|
+
settings: [],
|
|
870
|
+
capabilities: [
|
|
871
|
+
{
|
|
872
|
+
id: "ollama-cloud-models",
|
|
873
|
+
kind: "model",
|
|
874
|
+
connectionTypes: ["ollama-cloud-account"],
|
|
875
|
+
},
|
|
876
|
+
],
|
|
877
|
+
connectionTypes: [
|
|
878
|
+
{
|
|
879
|
+
id: "ollama-cloud-account",
|
|
880
|
+
capabilities: ["ollama-cloud-models"],
|
|
881
|
+
},
|
|
882
|
+
],
|
|
883
|
+
},
|
|
884
|
+
];
|
|
885
|
+
|
|
886
|
+
function modelUser() {
|
|
887
|
+
return {
|
|
729
888
|
schemaVersion: 1 as const,
|
|
730
889
|
revision: 1,
|
|
731
890
|
profile: { name: "User" },
|
|
@@ -765,45 +924,16 @@ describe("Bot execution-plan authority", () => {
|
|
|
765
924
|
},
|
|
766
925
|
],
|
|
767
926
|
};
|
|
768
|
-
|
|
769
|
-
{
|
|
770
|
-
packageId: "provider-ollama-cloud",
|
|
771
|
-
version: "0.0.1",
|
|
772
|
-
capabilities: [
|
|
773
|
-
{
|
|
774
|
-
id: "ollama-cloud-models",
|
|
775
|
-
kind: "model" as const,
|
|
776
|
-
connectionTypes: ["ollama-cloud-account"],
|
|
777
|
-
},
|
|
778
|
-
],
|
|
779
|
-
connectionTypes: [
|
|
780
|
-
{
|
|
781
|
-
id: "ollama-cloud-account",
|
|
782
|
-
capabilities: ["ollama-cloud-models"],
|
|
783
|
-
},
|
|
784
|
-
],
|
|
785
|
-
},
|
|
786
|
-
];
|
|
787
|
-
const assignments = [
|
|
788
|
-
{
|
|
789
|
-
assignmentId: "ollama-model",
|
|
790
|
-
packageId: "provider-ollama-cloud",
|
|
791
|
-
capabilityId: "ollama-cloud-models",
|
|
792
|
-
connectionId: "ollama-work",
|
|
793
|
-
state: "enabled" as const,
|
|
794
|
-
},
|
|
795
|
-
];
|
|
927
|
+
}
|
|
796
928
|
|
|
929
|
+
test("resolves a model through User enablement and its ready Connection", () => {
|
|
930
|
+
const user = modelUser();
|
|
931
|
+
const model = {
|
|
932
|
+
connectionId: "ollama-work",
|
|
933
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
934
|
+
};
|
|
797
935
|
expect(
|
|
798
|
-
resolveBotModelBindingV1({
|
|
799
|
-
model: {
|
|
800
|
-
connectionId: "ollama-work",
|
|
801
|
-
providerModelId: "glm-5.3-flash:cloud",
|
|
802
|
-
},
|
|
803
|
-
assignments,
|
|
804
|
-
user,
|
|
805
|
-
packages: modelPackages,
|
|
806
|
-
}),
|
|
936
|
+
resolveBotModelBindingV1({ model, user, packages: modelPackages }),
|
|
807
937
|
).toMatchObject({
|
|
808
938
|
state: "ready",
|
|
809
939
|
providerType: "ollama-cloud",
|
|
@@ -811,43 +941,53 @@ describe("Bot execution-plan authority", () => {
|
|
|
811
941
|
});
|
|
812
942
|
expect(
|
|
813
943
|
resolveBotModelBindingV1({
|
|
814
|
-
model: {
|
|
815
|
-
connectionId: "ollama-work",
|
|
816
|
-
providerModelId: "glm-5.3-flash:cloud",
|
|
817
|
-
},
|
|
818
|
-
assignments,
|
|
819
|
-
user,
|
|
820
|
-
packages: modelPackages.map((pkg) => ({ ...pkg, version: "0.0.2" })),
|
|
821
|
-
}).state,
|
|
822
|
-
).toBe("unavailable");
|
|
823
|
-
expect(
|
|
824
|
-
resolveBotModelBindingV1({
|
|
825
|
-
model: {
|
|
826
|
-
connectionId: "ollama-work",
|
|
827
|
-
providerModelId: "new-model:cloud",
|
|
828
|
-
},
|
|
829
|
-
assignments,
|
|
944
|
+
model: { ...model, providerModelId: "new-model:cloud" },
|
|
830
945
|
user,
|
|
831
946
|
packages: modelPackages,
|
|
832
947
|
}).state,
|
|
833
948
|
).toBe("requires-resolution");
|
|
949
|
+
});
|
|
950
|
+
|
|
951
|
+
test("disabling the Package or revoking the Connection fails every Bot closed", () => {
|
|
952
|
+
const model = {
|
|
953
|
+
connectionId: "ollama-work",
|
|
954
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
955
|
+
};
|
|
956
|
+
const user = {
|
|
957
|
+
...modelUser(),
|
|
958
|
+
packages: [{ ...modelUser().packages[0]!, state: "disabled" as const }],
|
|
959
|
+
};
|
|
834
960
|
expect(
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
},
|
|
840
|
-
assignments: [],
|
|
841
|
-
user,
|
|
842
|
-
packages: modelPackages,
|
|
843
|
-
}),
|
|
961
|
+
modelBindingFailureV1({ model, user, packages: modelPackages }),
|
|
962
|
+
).toContain('Package "provider-ollama-cloud" is not installed and enabled');
|
|
963
|
+
expect(
|
|
964
|
+
resolveBotModelBindingV1({ model, user, packages: modelPackages }),
|
|
844
965
|
).toMatchObject({
|
|
845
966
|
state: "unavailable",
|
|
846
|
-
failure: "
|
|
967
|
+
failure: expect.stringContaining("enable it"),
|
|
847
968
|
});
|
|
969
|
+
|
|
970
|
+
const revoked = {
|
|
971
|
+
...modelUser(),
|
|
972
|
+
connections: [
|
|
973
|
+
{ ...modelUser().connections[0]!, state: "revoked" as const },
|
|
974
|
+
],
|
|
975
|
+
};
|
|
976
|
+
for (const _botId of ["bot-one", "bot-two"]) {
|
|
977
|
+
expect(
|
|
978
|
+
resolveBotModelBindingV1({
|
|
979
|
+
model,
|
|
980
|
+
user: revoked,
|
|
981
|
+
packages: modelPackages,
|
|
982
|
+
}),
|
|
983
|
+
).toMatchObject({
|
|
984
|
+
state: "unavailable",
|
|
985
|
+
failure: expect.stringContaining("reconnect it"),
|
|
986
|
+
});
|
|
987
|
+
}
|
|
848
988
|
});
|
|
849
989
|
|
|
850
|
-
test("
|
|
990
|
+
test("projects the enabled User capability set in stable identity order", () => {
|
|
851
991
|
const user = {
|
|
852
992
|
schemaVersion: 1 as const,
|
|
853
993
|
revision: 2,
|
|
@@ -861,42 +1001,44 @@ describe("Bot execution-plan authority", () => {
|
|
|
861
1001
|
],
|
|
862
1002
|
connections: [
|
|
863
1003
|
{
|
|
864
|
-
connectionId: "gmail-
|
|
1004
|
+
connectionId: "gmail-z",
|
|
1005
|
+
packageId: "composio",
|
|
1006
|
+
connectionTypeId: "gmail",
|
|
1007
|
+
displayName: "Gmail Z",
|
|
1008
|
+
state: "ready" as const,
|
|
1009
|
+
safeMetadata: {},
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
connectionId: "gmail-a",
|
|
865
1013
|
packageId: "composio",
|
|
866
1014
|
connectionTypeId: "gmail",
|
|
867
|
-
displayName: "Gmail",
|
|
1015
|
+
displayName: "Gmail A",
|
|
868
1016
|
state: "ready" as const,
|
|
869
1017
|
safeMetadata: {},
|
|
870
1018
|
},
|
|
871
1019
|
],
|
|
872
1020
|
};
|
|
873
1021
|
expect(
|
|
874
|
-
resolveBotExecutionPlanV1({ bot, user, packages }).
|
|
875
|
-
).
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
capabilityAssignmentFailureV1({
|
|
895
|
-
assignment: { ...bot.assignments[0]!, capabilityId: "anything" },
|
|
896
|
-
user,
|
|
897
|
-
packages,
|
|
898
|
-
}),
|
|
899
|
-
).toContain("is not declared");
|
|
1022
|
+
resolveBotExecutionPlanV1({ bot, user, packages }).capabilities,
|
|
1023
|
+
).toEqual([
|
|
1024
|
+
{
|
|
1025
|
+
packageId: "composio",
|
|
1026
|
+
capabilityId: "clock",
|
|
1027
|
+
kind: "tool",
|
|
1028
|
+
},
|
|
1029
|
+
{
|
|
1030
|
+
packageId: "composio",
|
|
1031
|
+
capabilityId: "gmail-tools",
|
|
1032
|
+
kind: "tool",
|
|
1033
|
+
connectionId: "gmail-a",
|
|
1034
|
+
},
|
|
1035
|
+
{
|
|
1036
|
+
packageId: "composio",
|
|
1037
|
+
capabilityId: "gmail-tools",
|
|
1038
|
+
kind: "tool",
|
|
1039
|
+
connectionId: "gmail-z",
|
|
1040
|
+
},
|
|
1041
|
+
]);
|
|
900
1042
|
expect(
|
|
901
1043
|
resolveBotExecutionPlanV1({
|
|
902
1044
|
bot,
|
|
@@ -905,20 +1047,27 @@ describe("Bot execution-plan authority", () => {
|
|
|
905
1047
|
packages: [{ ...user.packages[0]!, state: "disabled" }],
|
|
906
1048
|
},
|
|
907
1049
|
packages,
|
|
908
|
-
}).
|
|
909
|
-
).
|
|
1050
|
+
}).capabilities,
|
|
1051
|
+
).toEqual([]);
|
|
910
1052
|
expect(
|
|
911
1053
|
resolveBotExecutionPlanV1({
|
|
912
1054
|
bot,
|
|
913
1055
|
user: {
|
|
914
1056
|
...user,
|
|
915
|
-
connections:
|
|
916
|
-
|
|
917
|
-
|
|
1057
|
+
connections: user.connections.map((connection) => ({
|
|
1058
|
+
...connection,
|
|
1059
|
+
state: "revoked" as const,
|
|
1060
|
+
})),
|
|
918
1061
|
},
|
|
919
1062
|
packages,
|
|
920
|
-
}).
|
|
921
|
-
).
|
|
1063
|
+
}).capabilities,
|
|
1064
|
+
).toEqual([
|
|
1065
|
+
{
|
|
1066
|
+
packageId: "composio",
|
|
1067
|
+
capabilityId: "clock",
|
|
1068
|
+
kind: "tool",
|
|
1069
|
+
},
|
|
1070
|
+
]);
|
|
922
1071
|
});
|
|
923
1072
|
});
|
|
924
1073
|
|
|
@@ -1087,10 +1236,28 @@ describe("Composition generation views", () => {
|
|
|
1087
1236
|
});
|
|
1088
1237
|
|
|
1089
1238
|
describe("effective Bot model resolution", () => {
|
|
1090
|
-
const
|
|
1239
|
+
const platformModel: ModelBindingV1 = {
|
|
1240
|
+
connectionId: "ollama-work",
|
|
1241
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
1242
|
+
};
|
|
1243
|
+
const accountModel: ModelBindingV1 = {
|
|
1244
|
+
connectionId: "ollama-work",
|
|
1245
|
+
providerModelId: "llama-3:cloud",
|
|
1246
|
+
};
|
|
1247
|
+
const modelSchema = {
|
|
1248
|
+
type: "object" as const,
|
|
1249
|
+
properties: {
|
|
1250
|
+
connectionId: { type: "string" as const },
|
|
1251
|
+
providerModelId: { type: "string" as const },
|
|
1252
|
+
},
|
|
1253
|
+
required: ["connectionId", "providerModelId"],
|
|
1254
|
+
additionalProperties: false,
|
|
1255
|
+
};
|
|
1256
|
+
const modelPackages: ExecutionPackageDefinition[] = [
|
|
1091
1257
|
{
|
|
1092
1258
|
packageId: "provider-ollama-cloud",
|
|
1093
1259
|
version: "0.0.1",
|
|
1260
|
+
settings: [],
|
|
1094
1261
|
capabilities: [
|
|
1095
1262
|
{
|
|
1096
1263
|
id: "ollama-cloud-models",
|
|
@@ -1102,10 +1269,23 @@ describe("effective Bot model resolution", () => {
|
|
|
1102
1269
|
{ id: "ollama-cloud-account", capabilities: ["ollama-cloud-models"] },
|
|
1103
1270
|
],
|
|
1104
1271
|
},
|
|
1272
|
+
{
|
|
1273
|
+
packageId: "custom-models",
|
|
1274
|
+
version: "0.0.1",
|
|
1275
|
+
settings: [
|
|
1276
|
+
{
|
|
1277
|
+
id: "model",
|
|
1278
|
+
schemaVersion: 1,
|
|
1279
|
+
scopes: ["user", "bot"],
|
|
1280
|
+
role: "model",
|
|
1281
|
+
schema: modelSchema,
|
|
1282
|
+
},
|
|
1283
|
+
],
|
|
1284
|
+
capabilities: [],
|
|
1285
|
+
connectionTypes: [],
|
|
1286
|
+
},
|
|
1105
1287
|
];
|
|
1106
|
-
function user(
|
|
1107
|
-
newBotModelTemplate?: ModelAssignment,
|
|
1108
|
-
): Parameters<typeof resolveEffectiveBotModelV1>[0]["user"] {
|
|
1288
|
+
function user(): Parameters<typeof resolveEffectiveBotModelV1>[0]["user"] {
|
|
1109
1289
|
return {
|
|
1110
1290
|
schemaVersion: 1,
|
|
1111
1291
|
revision: 1,
|
|
@@ -1147,82 +1327,129 @@ describe("effective Bot model resolution", () => {
|
|
|
1147
1327
|
safeMetadata: {},
|
|
1148
1328
|
},
|
|
1149
1329
|
],
|
|
1150
|
-
|
|
1151
|
-
? { newBotModelTemplate, newBotModelTemplateSource: "auto" as const }
|
|
1152
|
-
: {}),
|
|
1330
|
+
platformModel,
|
|
1153
1331
|
};
|
|
1154
1332
|
}
|
|
1155
|
-
const assignments = [
|
|
1156
|
-
{
|
|
1157
|
-
assignmentId: "ollama-model",
|
|
1158
|
-
packageId: "provider-ollama-cloud",
|
|
1159
|
-
capabilityId: "ollama-cloud-models",
|
|
1160
|
-
connectionId: "ollama-work",
|
|
1161
|
-
state: "enabled" as const,
|
|
1162
|
-
},
|
|
1163
|
-
];
|
|
1164
1333
|
|
|
1165
|
-
test("
|
|
1334
|
+
test("uses the platform model when the Bot and User configured nothing", () => {
|
|
1166
1335
|
const effective = resolveEffectiveBotModelV1({
|
|
1167
|
-
bot: {
|
|
1168
|
-
user: user(
|
|
1169
|
-
connectionId: "ollama-work",
|
|
1170
|
-
providerModelId: "llama-3:cloud",
|
|
1171
|
-
}),
|
|
1336
|
+
bot: { packageValues: {} },
|
|
1337
|
+
user: user(),
|
|
1172
1338
|
packages: modelPackages,
|
|
1173
1339
|
});
|
|
1174
|
-
expect(effective.source).toBe("
|
|
1175
|
-
expect(effective.model).toEqual(
|
|
1176
|
-
connectionId: "ollama-work",
|
|
1177
|
-
providerModelId: "llama-3:cloud",
|
|
1178
|
-
});
|
|
1340
|
+
expect(effective.source).toBe("platform");
|
|
1341
|
+
expect(effective.model).toEqual(platformModel);
|
|
1179
1342
|
expect(effective.binding?.state).toBe("ready");
|
|
1180
1343
|
});
|
|
1181
1344
|
|
|
1182
|
-
test("prefers
|
|
1183
|
-
const
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1345
|
+
test("prefers an enabled Package's User value, then its Bot value", () => {
|
|
1346
|
+
const configured = {
|
|
1347
|
+
...user(),
|
|
1348
|
+
packages: [
|
|
1349
|
+
...user().packages,
|
|
1350
|
+
{
|
|
1351
|
+
packageId: "custom-models",
|
|
1352
|
+
version: "0.0.1",
|
|
1353
|
+
state: "installed" as const,
|
|
1354
|
+
values: { model: accountModel },
|
|
1188
1355
|
},
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
}),
|
|
1356
|
+
],
|
|
1357
|
+
};
|
|
1358
|
+
const account = resolveEffectiveBotModelV1({
|
|
1359
|
+
bot: { packageValues: {} },
|
|
1360
|
+
user: configured,
|
|
1195
1361
|
packages: modelPackages,
|
|
1196
1362
|
});
|
|
1197
|
-
expect(
|
|
1198
|
-
expect(
|
|
1199
|
-
|
|
1363
|
+
expect(account.source).toBe("account");
|
|
1364
|
+
expect(account.model).toEqual(accountModel);
|
|
1365
|
+
|
|
1366
|
+
const bot = resolveEffectiveBotModelV1({
|
|
1367
|
+
bot: { packageValues: { "custom-models": { model: platformModel } } },
|
|
1368
|
+
user: configured,
|
|
1369
|
+
packages: modelPackages,
|
|
1370
|
+
});
|
|
1371
|
+
expect(bot.source).toBe("bot");
|
|
1372
|
+
expect(bot.model).toEqual(platformModel);
|
|
1373
|
+
expect(bot.binding?.state).toBe("ready");
|
|
1200
1374
|
});
|
|
1201
1375
|
|
|
1202
|
-
test("
|
|
1376
|
+
test("ignores disabled Package values without deleting them", () => {
|
|
1377
|
+
const disabled = decodeUserSettingsViewV1({
|
|
1378
|
+
...user(),
|
|
1379
|
+
packages: [
|
|
1380
|
+
...user().packages,
|
|
1381
|
+
{
|
|
1382
|
+
packageId: "custom-models",
|
|
1383
|
+
version: "0.0.1",
|
|
1384
|
+
state: "disabled" as const,
|
|
1385
|
+
values: { model: accountModel },
|
|
1386
|
+
},
|
|
1387
|
+
],
|
|
1388
|
+
});
|
|
1389
|
+
const bot = decodeBotSettingsViewV1({
|
|
1390
|
+
schemaVersion: 1,
|
|
1391
|
+
botId: "primary",
|
|
1392
|
+
revision: 1,
|
|
1393
|
+
profile: { name: "Primary" },
|
|
1394
|
+
notifications: { enabled: true },
|
|
1395
|
+
packageValues: { "custom-models": { model: accountModel } },
|
|
1396
|
+
});
|
|
1203
1397
|
expect(
|
|
1204
1398
|
resolveEffectiveBotModelV1({
|
|
1205
|
-
bot
|
|
1206
|
-
user:
|
|
1399
|
+
bot,
|
|
1400
|
+
user: disabled,
|
|
1207
1401
|
packages: modelPackages,
|
|
1208
1402
|
}),
|
|
1209
|
-
).
|
|
1403
|
+
).toMatchObject({ source: "platform", model: platformModel });
|
|
1404
|
+
expect(disabled.packages.at(-1)?.values).toEqual({ model: accountModel });
|
|
1405
|
+
expect(bot.packageValues).toEqual({
|
|
1406
|
+
"custom-models": { model: accountModel },
|
|
1407
|
+
});
|
|
1210
1408
|
});
|
|
1211
1409
|
|
|
1212
|
-
test("
|
|
1410
|
+
test("fails closed when two enabled Packages declare one scope's role", () => {
|
|
1411
|
+
const competing: ExecutionPackageDefinition = {
|
|
1412
|
+
...modelPackages[1]!,
|
|
1413
|
+
packageId: "other-models",
|
|
1414
|
+
};
|
|
1213
1415
|
const effective = resolveEffectiveBotModelV1({
|
|
1214
|
-
bot: {
|
|
1215
|
-
user:
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1416
|
+
bot: { packageValues: {} },
|
|
1417
|
+
user: {
|
|
1418
|
+
...user(),
|
|
1419
|
+
packages: [
|
|
1420
|
+
...user().packages,
|
|
1421
|
+
{
|
|
1422
|
+
packageId: "custom-models",
|
|
1423
|
+
version: "0.0.1",
|
|
1424
|
+
state: "installed",
|
|
1425
|
+
},
|
|
1426
|
+
{
|
|
1427
|
+
packageId: "other-models",
|
|
1428
|
+
version: "0.0.1",
|
|
1429
|
+
state: "installed",
|
|
1430
|
+
},
|
|
1431
|
+
],
|
|
1432
|
+
},
|
|
1433
|
+
packages: [...modelPackages, competing],
|
|
1220
1434
|
});
|
|
1221
|
-
expect(effective.source).toBe("
|
|
1435
|
+
expect(effective.source).toBe("bot");
|
|
1222
1436
|
expect(effective.binding).toMatchObject({
|
|
1223
1437
|
state: "unavailable",
|
|
1224
|
-
failure: "
|
|
1438
|
+
failure: expect.stringContaining('"custom-models" and "other-models"'),
|
|
1225
1439
|
});
|
|
1440
|
+
expect(effective.model).toBeUndefined();
|
|
1441
|
+
});
|
|
1442
|
+
|
|
1443
|
+
test("reports none only when no Package or platform model supplies one", () => {
|
|
1444
|
+
const withoutPlatform = user();
|
|
1445
|
+
delete withoutPlatform.platformModel;
|
|
1446
|
+
expect(
|
|
1447
|
+
resolveEffectiveBotModelV1({
|
|
1448
|
+
bot: { packageValues: {} },
|
|
1449
|
+
user: withoutPlatform,
|
|
1450
|
+
packages: modelPackages,
|
|
1451
|
+
}),
|
|
1452
|
+
).toEqual({ source: "none" });
|
|
1226
1453
|
});
|
|
1227
1454
|
});
|
|
1228
1455
|
|
|
@@ -1271,6 +1498,27 @@ describe("Catalog installs and uninstall", () => {
|
|
|
1271
1498
|
});
|
|
1272
1499
|
});
|
|
1273
1500
|
|
|
1501
|
+
test("decodes optional install enablement and rejects a non-boolean", () => {
|
|
1502
|
+
expect(
|
|
1503
|
+
decodeConfigurationCommandV1({
|
|
1504
|
+
...meta,
|
|
1505
|
+
type: "user/install-package",
|
|
1506
|
+
packageId: "custom-models",
|
|
1507
|
+
version: "0.0.1",
|
|
1508
|
+
enabled: false,
|
|
1509
|
+
}),
|
|
1510
|
+
).toMatchObject({ enabled: false });
|
|
1511
|
+
expect(() =>
|
|
1512
|
+
decodeConfigurationCommandV1({
|
|
1513
|
+
...meta,
|
|
1514
|
+
type: "user/install-package",
|
|
1515
|
+
packageId: "custom-models",
|
|
1516
|
+
version: "0.0.1",
|
|
1517
|
+
enabled: "false",
|
|
1518
|
+
}),
|
|
1519
|
+
).toThrow("enabled is invalid");
|
|
1520
|
+
});
|
|
1521
|
+
|
|
1274
1522
|
test("refuses half a Catalog install", () => {
|
|
1275
1523
|
expect(() =>
|
|
1276
1524
|
decodeConfigurationCommandV1({
|