@frockbot/configuration-core 0.1.3 → 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.json +3 -3
- package/src/bot-identity.test.ts +1 -2
- package/src/index.test.ts +381 -319
- package/src/index.ts +438 -439
- 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,12 @@ import {
|
|
|
20
18
|
decodeUserConfigurationReadRpcV1,
|
|
21
19
|
decodeUserSettingsViewV1,
|
|
22
20
|
initializeBotSettingsV1,
|
|
21
|
+
modelBindingFailureV1,
|
|
23
22
|
resolveBotExecutionPlanV1,
|
|
24
23
|
resolveBotModelBindingV1,
|
|
25
24
|
resolveEffectiveBotModelV1,
|
|
26
|
-
type
|
|
25
|
+
type ExecutionPackageDefinition,
|
|
26
|
+
type ModelBindingV1,
|
|
27
27
|
} from "./index.js";
|
|
28
28
|
import {
|
|
29
29
|
isApplicationDeploymentHash,
|
|
@@ -100,41 +100,74 @@ describe("configuration DTO seam", () => {
|
|
|
100
100
|
});
|
|
101
101
|
});
|
|
102
102
|
|
|
103
|
-
test("decodes exact
|
|
103
|
+
test("decodes exact Bot-scoped Package setting commands", () => {
|
|
104
104
|
const meta = {
|
|
105
105
|
schemaVersion: 1,
|
|
106
106
|
commandId: "operation-1",
|
|
107
107
|
expectedRevision: 2,
|
|
108
108
|
botId: "primary",
|
|
109
109
|
} as const;
|
|
110
|
-
const assignment = {
|
|
111
|
-
assignmentId: "mail",
|
|
112
|
-
packageId: "mail",
|
|
113
|
-
capabilityId: "send",
|
|
114
|
-
connectionId: "mail-1",
|
|
115
|
-
};
|
|
116
110
|
expect(
|
|
117
111
|
decodeConfigurationCommandV1({
|
|
118
112
|
...meta,
|
|
119
|
-
type: "bot/
|
|
120
|
-
|
|
113
|
+
type: "bot/set-package-settings",
|
|
114
|
+
packageId: "custom-models",
|
|
115
|
+
values: {
|
|
116
|
+
model: {
|
|
117
|
+
connectionId: "ollama-work",
|
|
118
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
121
|
}),
|
|
122
|
-
).toMatchObject({
|
|
123
|
-
|
|
122
|
+
).toMatchObject({
|
|
123
|
+
type: "bot/set-package-settings",
|
|
124
|
+
packageId: "custom-models",
|
|
125
|
+
values: { model: { connectionId: "ollama-work" } },
|
|
126
|
+
});
|
|
127
|
+
expect(() =>
|
|
124
128
|
decodeConfigurationCommandV1({
|
|
125
129
|
...meta,
|
|
126
|
-
type: "bot/
|
|
127
|
-
|
|
130
|
+
type: "bot/set-package-settings",
|
|
131
|
+
packageId: "custom-models",
|
|
132
|
+
values: {},
|
|
128
133
|
}),
|
|
129
|
-
).
|
|
130
|
-
expect(
|
|
134
|
+
).toThrow("values names no setting");
|
|
135
|
+
expect(
|
|
131
136
|
decodeConfigurationCommandV1({
|
|
132
137
|
...meta,
|
|
133
|
-
type: "bot/
|
|
134
|
-
|
|
135
|
-
|
|
138
|
+
type: "bot/set-package-settings",
|
|
139
|
+
packageId: "custom-models",
|
|
140
|
+
unset: ["model"],
|
|
136
141
|
}),
|
|
137
|
-
).
|
|
142
|
+
).toMatchObject({ unset: ["model"] });
|
|
143
|
+
for (const invalid of [
|
|
144
|
+
{ ...meta, type: "bot/set-package-settings", packageId: "custom-models" },
|
|
145
|
+
{
|
|
146
|
+
...meta,
|
|
147
|
+
type: "bot/set-package-settings",
|
|
148
|
+
packageId: "custom-models",
|
|
149
|
+
unset: [],
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
...meta,
|
|
153
|
+
type: "bot/set-package-settings",
|
|
154
|
+
packageId: "custom-models",
|
|
155
|
+
unset: ["model", "model"],
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
...meta,
|
|
159
|
+
type: "bot/set-package-settings",
|
|
160
|
+
packageId: "custom-models",
|
|
161
|
+
values: {
|
|
162
|
+
model: { connectionId: "ollama-work", providerModelId: "m" },
|
|
163
|
+
},
|
|
164
|
+
unset: ["model"],
|
|
165
|
+
},
|
|
166
|
+
]) {
|
|
167
|
+
expect(() => decodeConfigurationCommandV1(invalid)).toThrow(
|
|
168
|
+
ConfigurationDecodeError,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
138
171
|
});
|
|
139
172
|
|
|
140
173
|
test("accepts provider model IDs through the catalog limit", () => {
|
|
@@ -143,49 +176,43 @@ describe("configuration DTO seam", () => {
|
|
|
143
176
|
expect(
|
|
144
177
|
decodeConfigurationCommandV1({
|
|
145
178
|
schemaVersion: 1,
|
|
146
|
-
type: "
|
|
179
|
+
type: "user/set-platform-model",
|
|
147
180
|
commandId: "command-model",
|
|
148
|
-
botId: "primary",
|
|
149
181
|
expectedRevision: 3,
|
|
150
182
|
model: { connectionId: "connection-1", providerModelId },
|
|
151
183
|
}),
|
|
152
184
|
).toMatchObject({ model: { providerModelId } });
|
|
153
185
|
});
|
|
154
186
|
|
|
155
|
-
test("decodes
|
|
187
|
+
test("decodes the provider-bootstrap platform model command", () => {
|
|
156
188
|
expect(
|
|
157
189
|
decodeConfigurationCommandV1({
|
|
158
190
|
schemaVersion: 1,
|
|
159
|
-
type: "
|
|
160
|
-
commandId: "
|
|
161
|
-
botId: "primary",
|
|
191
|
+
type: "user/set-platform-model",
|
|
192
|
+
commandId: "bootstrap-model",
|
|
162
193
|
expectedRevision: 2,
|
|
163
|
-
assignment: {
|
|
164
|
-
assignmentId: "ollama-model",
|
|
165
|
-
packageId: "provider-ollama-cloud",
|
|
166
|
-
capabilityId: "ollama-cloud-models",
|
|
167
|
-
connectionId: "ollama-work",
|
|
168
|
-
},
|
|
169
194
|
model: {
|
|
170
195
|
connectionId: "ollama-work",
|
|
171
196
|
providerModelId: "glm-5.3-flash:cloud",
|
|
172
197
|
},
|
|
173
198
|
}),
|
|
174
199
|
).toMatchObject({
|
|
175
|
-
type: "
|
|
176
|
-
assignment: { connectionId: "ollama-work" },
|
|
200
|
+
type: "user/set-platform-model",
|
|
177
201
|
model: { providerModelId: "glm-5.3-flash:cloud" },
|
|
178
202
|
});
|
|
179
|
-
expect(
|
|
203
|
+
expect(() =>
|
|
180
204
|
decodeConfigurationCommandV1({
|
|
181
205
|
schemaVersion: 1,
|
|
182
|
-
type: "
|
|
183
|
-
commandId: "
|
|
184
|
-
botId: "primary",
|
|
206
|
+
type: "user/set-platform-model",
|
|
207
|
+
commandId: "bootstrap-model",
|
|
185
208
|
expectedRevision: 3,
|
|
186
|
-
|
|
209
|
+
model: {
|
|
210
|
+
connectionId: "ollama-work",
|
|
211
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
212
|
+
extra: true,
|
|
213
|
+
},
|
|
187
214
|
}),
|
|
188
|
-
).
|
|
215
|
+
).toThrow(ConfigurationDecodeError);
|
|
189
216
|
});
|
|
190
217
|
|
|
191
218
|
test("rejects unversioned, malformed, and unknown commands", () => {
|
|
@@ -207,30 +234,6 @@ describe("configuration DTO seam", () => {
|
|
|
207
234
|
}
|
|
208
235
|
});
|
|
209
236
|
|
|
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
237
|
test("rejects unknown fields throughout configuration commands", () => {
|
|
235
238
|
const meta = {
|
|
236
239
|
schemaVersion: 1,
|
|
@@ -245,8 +248,7 @@ describe("configuration DTO seam", () => {
|
|
|
245
248
|
},
|
|
246
249
|
{
|
|
247
250
|
...meta,
|
|
248
|
-
type: "user/set-
|
|
249
|
-
source: "user",
|
|
251
|
+
type: "user/set-platform-model",
|
|
250
252
|
model: {
|
|
251
253
|
connectionId: "provider-1",
|
|
252
254
|
providerModelId: "model-1",
|
|
@@ -261,14 +263,11 @@ describe("configuration DTO seam", () => {
|
|
|
261
263
|
},
|
|
262
264
|
{
|
|
263
265
|
...meta,
|
|
264
|
-
type: "bot/
|
|
266
|
+
type: "bot/set-package-settings",
|
|
265
267
|
botId: "primary",
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
capabilityId: "gmail-tools",
|
|
270
|
-
extra: true,
|
|
271
|
-
},
|
|
268
|
+
packageId: "custom-models",
|
|
269
|
+
values: { model: "fast" },
|
|
270
|
+
extra: true,
|
|
272
271
|
},
|
|
273
272
|
{
|
|
274
273
|
...meta,
|
|
@@ -303,45 +302,6 @@ describe("configuration DTO seam", () => {
|
|
|
303
302
|
}
|
|
304
303
|
});
|
|
305
304
|
|
|
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
305
|
test("strictly decodes versioned Connection commands", () => {
|
|
346
306
|
expect(
|
|
347
307
|
decodeStartConnectionCommandV1({
|
|
@@ -388,20 +348,20 @@ describe("configuration DTO seam", () => {
|
|
|
388
348
|
}
|
|
389
349
|
});
|
|
390
350
|
|
|
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
|
-
};
|
|
351
|
+
test("rejects hidden and symbol fields across Package-setting seams", () => {
|
|
398
352
|
const command = {
|
|
399
353
|
schemaVersion: 1,
|
|
400
|
-
type: "bot/
|
|
354
|
+
type: "bot/set-package-settings",
|
|
401
355
|
commandId: "command-1",
|
|
402
356
|
expectedRevision: 0,
|
|
403
357
|
botId: "primary",
|
|
404
|
-
|
|
358
|
+
packageId: "custom-models",
|
|
359
|
+
values: {
|
|
360
|
+
model: {
|
|
361
|
+
connectionId: "mail-1",
|
|
362
|
+
providerModelId: "model-1",
|
|
363
|
+
},
|
|
364
|
+
},
|
|
405
365
|
};
|
|
406
366
|
const receipt = {
|
|
407
367
|
schemaVersion: 1,
|
|
@@ -415,16 +375,14 @@ describe("configuration DTO seam", () => {
|
|
|
415
375
|
revision: 0,
|
|
416
376
|
profile: { name: "Primary" },
|
|
417
377
|
notifications: { enabled: false },
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
state: "retrying",
|
|
425
|
-
target: assignment,
|
|
378
|
+
packageValues: {
|
|
379
|
+
"custom-models": {
|
|
380
|
+
model: {
|
|
381
|
+
connectionId: "mail-1",
|
|
382
|
+
providerModelId: "model-1",
|
|
383
|
+
},
|
|
426
384
|
},
|
|
427
|
-
|
|
385
|
+
},
|
|
428
386
|
};
|
|
429
387
|
const rpc = {
|
|
430
388
|
schemaVersion: 1,
|
|
@@ -434,7 +392,6 @@ describe("configuration DTO seam", () => {
|
|
|
434
392
|
};
|
|
435
393
|
for (const [decode, candidate] of [
|
|
436
394
|
[decodeConfigurationCommandV1, command],
|
|
437
|
-
[decodeConfigurationCommandV1, { ...command, assignment }],
|
|
438
395
|
[decodeOperationReceiptV1, receipt],
|
|
439
396
|
[decodeBotSettingsViewV1, view],
|
|
440
397
|
[decodeBotConfigurationExecuteRpcV1, rpc],
|
|
@@ -445,20 +402,28 @@ describe("configuration DTO seam", () => {
|
|
|
445
402
|
const symbol = { ...candidate, [Symbol("extra")]: true };
|
|
446
403
|
expect(() => decode(symbol)).toThrow(ConfigurationDecodeError);
|
|
447
404
|
}
|
|
448
|
-
const
|
|
449
|
-
Object.defineProperty(
|
|
405
|
+
const hiddenValues = { ...command.values };
|
|
406
|
+
Object.defineProperty(hiddenValues, "hidden", { value: true });
|
|
450
407
|
expect(() =>
|
|
451
|
-
decodeConfigurationCommandV1({ ...command,
|
|
408
|
+
decodeConfigurationCommandV1({ ...command, values: hiddenValues }),
|
|
452
409
|
).toThrow(ConfigurationDecodeError);
|
|
453
|
-
const
|
|
410
|
+
const symbolModel = {
|
|
411
|
+
...command.values.model,
|
|
412
|
+
[Symbol("extra")]: true,
|
|
413
|
+
};
|
|
454
414
|
expect(() =>
|
|
455
415
|
decodeBotSettingsViewV1({
|
|
456
416
|
...view,
|
|
457
|
-
|
|
458
|
-
{
|
|
459
|
-
|
|
417
|
+
packageValues: {
|
|
418
|
+
"custom-models": { model: symbolModel },
|
|
419
|
+
},
|
|
460
420
|
}),
|
|
461
421
|
).toThrow(ConfigurationDecodeError);
|
|
422
|
+
const pollutedValues = Object.create({ inherited: true });
|
|
423
|
+
pollutedValues.model = command.values.model;
|
|
424
|
+
expect(() =>
|
|
425
|
+
decodeConfigurationCommandV1({ ...command, values: pollutedValues }),
|
|
426
|
+
).toThrow(ConfigurationDecodeError);
|
|
462
427
|
});
|
|
463
428
|
|
|
464
429
|
test("rejects unknown configuration RPC envelope fields", () => {
|
|
@@ -684,17 +649,9 @@ describe("configuration DTO seam", () => {
|
|
|
684
649
|
});
|
|
685
650
|
|
|
686
651
|
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
|
-
});
|
|
652
|
+
test("initializes Bot settings without seeding a model choice", () => {
|
|
653
|
+
const initialized = initializeBotSettingsV1("primary");
|
|
654
|
+
expect(initialized.packageValues).toEqual({});
|
|
698
655
|
expect(initialized.notifications).toEqual({ enabled: true });
|
|
699
656
|
});
|
|
700
657
|
|
|
@@ -704,28 +661,44 @@ describe("Bot execution-plan authority", () => {
|
|
|
704
661
|
revision: 4,
|
|
705
662
|
profile: { name: "Primary" },
|
|
706
663
|
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: [],
|
|
664
|
+
packageValues: {},
|
|
717
665
|
};
|
|
718
|
-
const packages = [
|
|
666
|
+
const packages: ExecutionPackageDefinition[] = [
|
|
719
667
|
{
|
|
720
668
|
packageId: "composio",
|
|
721
669
|
version: "0.0.1",
|
|
722
|
-
|
|
670
|
+
settings: [],
|
|
671
|
+
capabilities: [
|
|
672
|
+
{ id: "clock", kind: "tool", connectionTypes: [] },
|
|
673
|
+
{ id: "gmail-tools", kind: "tool", connectionTypes: ["gmail"] },
|
|
674
|
+
],
|
|
723
675
|
connectionTypes: [{ id: "gmail", capabilities: ["gmail-tools"] }],
|
|
724
676
|
},
|
|
725
677
|
];
|
|
726
678
|
|
|
727
|
-
|
|
728
|
-
|
|
679
|
+
const modelPackages: ExecutionPackageDefinition[] = [
|
|
680
|
+
{
|
|
681
|
+
packageId: "provider-ollama-cloud",
|
|
682
|
+
version: "0.0.1",
|
|
683
|
+
settings: [],
|
|
684
|
+
capabilities: [
|
|
685
|
+
{
|
|
686
|
+
id: "ollama-cloud-models",
|
|
687
|
+
kind: "model",
|
|
688
|
+
connectionTypes: ["ollama-cloud-account"],
|
|
689
|
+
},
|
|
690
|
+
],
|
|
691
|
+
connectionTypes: [
|
|
692
|
+
{
|
|
693
|
+
id: "ollama-cloud-account",
|
|
694
|
+
capabilities: ["ollama-cloud-models"],
|
|
695
|
+
},
|
|
696
|
+
],
|
|
697
|
+
},
|
|
698
|
+
];
|
|
699
|
+
|
|
700
|
+
function modelUser() {
|
|
701
|
+
return {
|
|
729
702
|
schemaVersion: 1 as const,
|
|
730
703
|
revision: 1,
|
|
731
704
|
profile: { name: "User" },
|
|
@@ -765,45 +738,16 @@ describe("Bot execution-plan authority", () => {
|
|
|
765
738
|
},
|
|
766
739
|
],
|
|
767
740
|
};
|
|
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
|
-
];
|
|
741
|
+
}
|
|
796
742
|
|
|
743
|
+
test("resolves a model through User enablement and its ready Connection", () => {
|
|
744
|
+
const user = modelUser();
|
|
745
|
+
const model = {
|
|
746
|
+
connectionId: "ollama-work",
|
|
747
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
748
|
+
};
|
|
797
749
|
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
|
-
}),
|
|
750
|
+
resolveBotModelBindingV1({ model, user, packages: modelPackages }),
|
|
807
751
|
).toMatchObject({
|
|
808
752
|
state: "ready",
|
|
809
753
|
providerType: "ollama-cloud",
|
|
@@ -811,43 +755,53 @@ describe("Bot execution-plan authority", () => {
|
|
|
811
755
|
});
|
|
812
756
|
expect(
|
|
813
757
|
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,
|
|
758
|
+
model: { ...model, providerModelId: "new-model:cloud" },
|
|
830
759
|
user,
|
|
831
760
|
packages: modelPackages,
|
|
832
761
|
}).state,
|
|
833
762
|
).toBe("requires-resolution");
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
test("disabling the Package or revoking the Connection fails every Bot closed", () => {
|
|
766
|
+
const model = {
|
|
767
|
+
connectionId: "ollama-work",
|
|
768
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
769
|
+
};
|
|
770
|
+
const user = {
|
|
771
|
+
...modelUser(),
|
|
772
|
+
packages: [{ ...modelUser().packages[0]!, state: "disabled" as const }],
|
|
773
|
+
};
|
|
834
774
|
expect(
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
},
|
|
840
|
-
assignments: [],
|
|
841
|
-
user,
|
|
842
|
-
packages: modelPackages,
|
|
843
|
-
}),
|
|
775
|
+
modelBindingFailureV1({ model, user, packages: modelPackages }),
|
|
776
|
+
).toContain('Package "provider-ollama-cloud" is not installed and enabled');
|
|
777
|
+
expect(
|
|
778
|
+
resolveBotModelBindingV1({ model, user, packages: modelPackages }),
|
|
844
779
|
).toMatchObject({
|
|
845
780
|
state: "unavailable",
|
|
846
|
-
failure: "
|
|
781
|
+
failure: expect.stringContaining("enable it"),
|
|
847
782
|
});
|
|
783
|
+
|
|
784
|
+
const revoked = {
|
|
785
|
+
...modelUser(),
|
|
786
|
+
connections: [
|
|
787
|
+
{ ...modelUser().connections[0]!, state: "revoked" as const },
|
|
788
|
+
],
|
|
789
|
+
};
|
|
790
|
+
for (const _botId of ["bot-one", "bot-two"]) {
|
|
791
|
+
expect(
|
|
792
|
+
resolveBotModelBindingV1({
|
|
793
|
+
model,
|
|
794
|
+
user: revoked,
|
|
795
|
+
packages: modelPackages,
|
|
796
|
+
}),
|
|
797
|
+
).toMatchObject({
|
|
798
|
+
state: "unavailable",
|
|
799
|
+
failure: expect.stringContaining("reconnect it"),
|
|
800
|
+
});
|
|
801
|
+
}
|
|
848
802
|
});
|
|
849
803
|
|
|
850
|
-
test("
|
|
804
|
+
test("projects the enabled User capability set in stable identity order", () => {
|
|
851
805
|
const user = {
|
|
852
806
|
schemaVersion: 1 as const,
|
|
853
807
|
revision: 2,
|
|
@@ -861,42 +815,44 @@ describe("Bot execution-plan authority", () => {
|
|
|
861
815
|
],
|
|
862
816
|
connections: [
|
|
863
817
|
{
|
|
864
|
-
connectionId: "gmail-
|
|
818
|
+
connectionId: "gmail-z",
|
|
865
819
|
packageId: "composio",
|
|
866
820
|
connectionTypeId: "gmail",
|
|
867
|
-
displayName: "Gmail",
|
|
821
|
+
displayName: "Gmail Z",
|
|
822
|
+
state: "ready" as const,
|
|
823
|
+
safeMetadata: {},
|
|
824
|
+
},
|
|
825
|
+
{
|
|
826
|
+
connectionId: "gmail-a",
|
|
827
|
+
packageId: "composio",
|
|
828
|
+
connectionTypeId: "gmail",
|
|
829
|
+
displayName: "Gmail A",
|
|
868
830
|
state: "ready" as const,
|
|
869
831
|
safeMetadata: {},
|
|
870
832
|
},
|
|
871
833
|
],
|
|
872
834
|
};
|
|
873
835
|
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");
|
|
836
|
+
resolveBotExecutionPlanV1({ bot, user, packages }).capabilities,
|
|
837
|
+
).toEqual([
|
|
838
|
+
{
|
|
839
|
+
packageId: "composio",
|
|
840
|
+
capabilityId: "clock",
|
|
841
|
+
kind: "tool",
|
|
842
|
+
},
|
|
843
|
+
{
|
|
844
|
+
packageId: "composio",
|
|
845
|
+
capabilityId: "gmail-tools",
|
|
846
|
+
kind: "tool",
|
|
847
|
+
connectionId: "gmail-a",
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
packageId: "composio",
|
|
851
|
+
capabilityId: "gmail-tools",
|
|
852
|
+
kind: "tool",
|
|
853
|
+
connectionId: "gmail-z",
|
|
854
|
+
},
|
|
855
|
+
]);
|
|
900
856
|
expect(
|
|
901
857
|
resolveBotExecutionPlanV1({
|
|
902
858
|
bot,
|
|
@@ -905,20 +861,27 @@ describe("Bot execution-plan authority", () => {
|
|
|
905
861
|
packages: [{ ...user.packages[0]!, state: "disabled" }],
|
|
906
862
|
},
|
|
907
863
|
packages,
|
|
908
|
-
}).
|
|
909
|
-
).
|
|
864
|
+
}).capabilities,
|
|
865
|
+
).toEqual([]);
|
|
910
866
|
expect(
|
|
911
867
|
resolveBotExecutionPlanV1({
|
|
912
868
|
bot,
|
|
913
869
|
user: {
|
|
914
870
|
...user,
|
|
915
|
-
connections:
|
|
916
|
-
|
|
917
|
-
|
|
871
|
+
connections: user.connections.map((connection) => ({
|
|
872
|
+
...connection,
|
|
873
|
+
state: "revoked" as const,
|
|
874
|
+
})),
|
|
918
875
|
},
|
|
919
876
|
packages,
|
|
920
|
-
}).
|
|
921
|
-
).
|
|
877
|
+
}).capabilities,
|
|
878
|
+
).toEqual([
|
|
879
|
+
{
|
|
880
|
+
packageId: "composio",
|
|
881
|
+
capabilityId: "clock",
|
|
882
|
+
kind: "tool",
|
|
883
|
+
},
|
|
884
|
+
]);
|
|
922
885
|
});
|
|
923
886
|
});
|
|
924
887
|
|
|
@@ -1087,10 +1050,28 @@ describe("Composition generation views", () => {
|
|
|
1087
1050
|
});
|
|
1088
1051
|
|
|
1089
1052
|
describe("effective Bot model resolution", () => {
|
|
1090
|
-
const
|
|
1053
|
+
const platformModel: ModelBindingV1 = {
|
|
1054
|
+
connectionId: "ollama-work",
|
|
1055
|
+
providerModelId: "glm-5.3-flash:cloud",
|
|
1056
|
+
};
|
|
1057
|
+
const accountModel: ModelBindingV1 = {
|
|
1058
|
+
connectionId: "ollama-work",
|
|
1059
|
+
providerModelId: "llama-3:cloud",
|
|
1060
|
+
};
|
|
1061
|
+
const modelSchema = {
|
|
1062
|
+
type: "object" as const,
|
|
1063
|
+
properties: {
|
|
1064
|
+
connectionId: { type: "string" as const },
|
|
1065
|
+
providerModelId: { type: "string" as const },
|
|
1066
|
+
},
|
|
1067
|
+
required: ["connectionId", "providerModelId"],
|
|
1068
|
+
additionalProperties: false,
|
|
1069
|
+
};
|
|
1070
|
+
const modelPackages: ExecutionPackageDefinition[] = [
|
|
1091
1071
|
{
|
|
1092
1072
|
packageId: "provider-ollama-cloud",
|
|
1093
1073
|
version: "0.0.1",
|
|
1074
|
+
settings: [],
|
|
1094
1075
|
capabilities: [
|
|
1095
1076
|
{
|
|
1096
1077
|
id: "ollama-cloud-models",
|
|
@@ -1102,10 +1083,23 @@ describe("effective Bot model resolution", () => {
|
|
|
1102
1083
|
{ id: "ollama-cloud-account", capabilities: ["ollama-cloud-models"] },
|
|
1103
1084
|
],
|
|
1104
1085
|
},
|
|
1086
|
+
{
|
|
1087
|
+
packageId: "custom-models",
|
|
1088
|
+
version: "0.0.1",
|
|
1089
|
+
settings: [
|
|
1090
|
+
{
|
|
1091
|
+
id: "model",
|
|
1092
|
+
schemaVersion: 1,
|
|
1093
|
+
scopes: ["user", "bot"],
|
|
1094
|
+
role: "model",
|
|
1095
|
+
schema: modelSchema,
|
|
1096
|
+
},
|
|
1097
|
+
],
|
|
1098
|
+
capabilities: [],
|
|
1099
|
+
connectionTypes: [],
|
|
1100
|
+
},
|
|
1105
1101
|
];
|
|
1106
|
-
function user(
|
|
1107
|
-
newBotModelTemplate?: ModelAssignment,
|
|
1108
|
-
): Parameters<typeof resolveEffectiveBotModelV1>[0]["user"] {
|
|
1102
|
+
function user(): Parameters<typeof resolveEffectiveBotModelV1>[0]["user"] {
|
|
1109
1103
|
return {
|
|
1110
1104
|
schemaVersion: 1,
|
|
1111
1105
|
revision: 1,
|
|
@@ -1147,82 +1141,129 @@ describe("effective Bot model resolution", () => {
|
|
|
1147
1141
|
safeMetadata: {},
|
|
1148
1142
|
},
|
|
1149
1143
|
],
|
|
1150
|
-
|
|
1151
|
-
? { newBotModelTemplate, newBotModelTemplateSource: "auto" as const }
|
|
1152
|
-
: {}),
|
|
1144
|
+
platformModel,
|
|
1153
1145
|
};
|
|
1154
1146
|
}
|
|
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
1147
|
|
|
1165
|
-
test("
|
|
1148
|
+
test("uses the platform model when the Bot and User configured nothing", () => {
|
|
1166
1149
|
const effective = resolveEffectiveBotModelV1({
|
|
1167
|
-
bot: {
|
|
1168
|
-
user: user(
|
|
1169
|
-
connectionId: "ollama-work",
|
|
1170
|
-
providerModelId: "llama-3:cloud",
|
|
1171
|
-
}),
|
|
1150
|
+
bot: { packageValues: {} },
|
|
1151
|
+
user: user(),
|
|
1172
1152
|
packages: modelPackages,
|
|
1173
1153
|
});
|
|
1174
|
-
expect(effective.source).toBe("
|
|
1175
|
-
expect(effective.model).toEqual(
|
|
1176
|
-
connectionId: "ollama-work",
|
|
1177
|
-
providerModelId: "llama-3:cloud",
|
|
1178
|
-
});
|
|
1154
|
+
expect(effective.source).toBe("platform");
|
|
1155
|
+
expect(effective.model).toEqual(platformModel);
|
|
1179
1156
|
expect(effective.binding?.state).toBe("ready");
|
|
1180
1157
|
});
|
|
1181
1158
|
|
|
1182
|
-
test("prefers
|
|
1183
|
-
const
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1159
|
+
test("prefers an enabled Package's User value, then its Bot value", () => {
|
|
1160
|
+
const configured = {
|
|
1161
|
+
...user(),
|
|
1162
|
+
packages: [
|
|
1163
|
+
...user().packages,
|
|
1164
|
+
{
|
|
1165
|
+
packageId: "custom-models",
|
|
1166
|
+
version: "0.0.1",
|
|
1167
|
+
state: "installed" as const,
|
|
1168
|
+
values: { model: accountModel },
|
|
1188
1169
|
},
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
}),
|
|
1170
|
+
],
|
|
1171
|
+
};
|
|
1172
|
+
const account = resolveEffectiveBotModelV1({
|
|
1173
|
+
bot: { packageValues: {} },
|
|
1174
|
+
user: configured,
|
|
1195
1175
|
packages: modelPackages,
|
|
1196
1176
|
});
|
|
1197
|
-
expect(
|
|
1198
|
-
expect(
|
|
1199
|
-
|
|
1177
|
+
expect(account.source).toBe("account");
|
|
1178
|
+
expect(account.model).toEqual(accountModel);
|
|
1179
|
+
|
|
1180
|
+
const bot = resolveEffectiveBotModelV1({
|
|
1181
|
+
bot: { packageValues: { "custom-models": { model: platformModel } } },
|
|
1182
|
+
user: configured,
|
|
1183
|
+
packages: modelPackages,
|
|
1184
|
+
});
|
|
1185
|
+
expect(bot.source).toBe("bot");
|
|
1186
|
+
expect(bot.model).toEqual(platformModel);
|
|
1187
|
+
expect(bot.binding?.state).toBe("ready");
|
|
1200
1188
|
});
|
|
1201
1189
|
|
|
1202
|
-
test("
|
|
1190
|
+
test("ignores disabled Package values without deleting them", () => {
|
|
1191
|
+
const disabled = decodeUserSettingsViewV1({
|
|
1192
|
+
...user(),
|
|
1193
|
+
packages: [
|
|
1194
|
+
...user().packages,
|
|
1195
|
+
{
|
|
1196
|
+
packageId: "custom-models",
|
|
1197
|
+
version: "0.0.1",
|
|
1198
|
+
state: "disabled" as const,
|
|
1199
|
+
values: { model: accountModel },
|
|
1200
|
+
},
|
|
1201
|
+
],
|
|
1202
|
+
});
|
|
1203
|
+
const bot = decodeBotSettingsViewV1({
|
|
1204
|
+
schemaVersion: 1,
|
|
1205
|
+
botId: "primary",
|
|
1206
|
+
revision: 1,
|
|
1207
|
+
profile: { name: "Primary" },
|
|
1208
|
+
notifications: { enabled: true },
|
|
1209
|
+
packageValues: { "custom-models": { model: accountModel } },
|
|
1210
|
+
});
|
|
1203
1211
|
expect(
|
|
1204
1212
|
resolveEffectiveBotModelV1({
|
|
1205
|
-
bot
|
|
1206
|
-
user:
|
|
1213
|
+
bot,
|
|
1214
|
+
user: disabled,
|
|
1207
1215
|
packages: modelPackages,
|
|
1208
1216
|
}),
|
|
1209
|
-
).
|
|
1217
|
+
).toMatchObject({ source: "platform", model: platformModel });
|
|
1218
|
+
expect(disabled.packages.at(-1)?.values).toEqual({ model: accountModel });
|
|
1219
|
+
expect(bot.packageValues).toEqual({
|
|
1220
|
+
"custom-models": { model: accountModel },
|
|
1221
|
+
});
|
|
1210
1222
|
});
|
|
1211
1223
|
|
|
1212
|
-
test("
|
|
1224
|
+
test("fails closed when two enabled Packages declare one scope's role", () => {
|
|
1225
|
+
const competing: ExecutionPackageDefinition = {
|
|
1226
|
+
...modelPackages[1]!,
|
|
1227
|
+
packageId: "other-models",
|
|
1228
|
+
};
|
|
1213
1229
|
const effective = resolveEffectiveBotModelV1({
|
|
1214
|
-
bot: {
|
|
1215
|
-
user:
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1230
|
+
bot: { packageValues: {} },
|
|
1231
|
+
user: {
|
|
1232
|
+
...user(),
|
|
1233
|
+
packages: [
|
|
1234
|
+
...user().packages,
|
|
1235
|
+
{
|
|
1236
|
+
packageId: "custom-models",
|
|
1237
|
+
version: "0.0.1",
|
|
1238
|
+
state: "installed",
|
|
1239
|
+
},
|
|
1240
|
+
{
|
|
1241
|
+
packageId: "other-models",
|
|
1242
|
+
version: "0.0.1",
|
|
1243
|
+
state: "installed",
|
|
1244
|
+
},
|
|
1245
|
+
],
|
|
1246
|
+
},
|
|
1247
|
+
packages: [...modelPackages, competing],
|
|
1220
1248
|
});
|
|
1221
|
-
expect(effective.source).toBe("
|
|
1249
|
+
expect(effective.source).toBe("bot");
|
|
1222
1250
|
expect(effective.binding).toMatchObject({
|
|
1223
1251
|
state: "unavailable",
|
|
1224
|
-
failure: "
|
|
1252
|
+
failure: expect.stringContaining('"custom-models" and "other-models"'),
|
|
1225
1253
|
});
|
|
1254
|
+
expect(effective.model).toBeUndefined();
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1257
|
+
test("reports none only when no Package or platform model supplies one", () => {
|
|
1258
|
+
const withoutPlatform = user();
|
|
1259
|
+
delete withoutPlatform.platformModel;
|
|
1260
|
+
expect(
|
|
1261
|
+
resolveEffectiveBotModelV1({
|
|
1262
|
+
bot: { packageValues: {} },
|
|
1263
|
+
user: withoutPlatform,
|
|
1264
|
+
packages: modelPackages,
|
|
1265
|
+
}),
|
|
1266
|
+
).toEqual({ source: "none" });
|
|
1226
1267
|
});
|
|
1227
1268
|
});
|
|
1228
1269
|
|
|
@@ -1271,6 +1312,27 @@ describe("Catalog installs and uninstall", () => {
|
|
|
1271
1312
|
});
|
|
1272
1313
|
});
|
|
1273
1314
|
|
|
1315
|
+
test("decodes optional install enablement and rejects a non-boolean", () => {
|
|
1316
|
+
expect(
|
|
1317
|
+
decodeConfigurationCommandV1({
|
|
1318
|
+
...meta,
|
|
1319
|
+
type: "user/install-package",
|
|
1320
|
+
packageId: "custom-models",
|
|
1321
|
+
version: "0.0.1",
|
|
1322
|
+
enabled: false,
|
|
1323
|
+
}),
|
|
1324
|
+
).toMatchObject({ enabled: false });
|
|
1325
|
+
expect(() =>
|
|
1326
|
+
decodeConfigurationCommandV1({
|
|
1327
|
+
...meta,
|
|
1328
|
+
type: "user/install-package",
|
|
1329
|
+
packageId: "custom-models",
|
|
1330
|
+
version: "0.0.1",
|
|
1331
|
+
enabled: "false",
|
|
1332
|
+
}),
|
|
1333
|
+
).toThrow("enabled is invalid");
|
|
1334
|
+
});
|
|
1335
|
+
|
|
1274
1336
|
test("refuses half a Catalog install", () => {
|
|
1275
1337
|
expect(() =>
|
|
1276
1338
|
decodeConfigurationCommandV1({
|