@frockbot/plugin-settings 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/src/user.test.ts CHANGED
@@ -1,9 +1,6 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import type { CatalogEntryV1, CatalogIndexV1 } from "@frockbot/catalog-core";
3
3
  import {
4
- capabilityAssignmentFailureV1,
5
- initializeBotSettingsV1,
6
- resolveBotExecutionPlanV1,
7
4
  USER_PROFILE_PLACEHOLDER_NAME_V1,
8
5
  type UserSettingsViewV1,
9
6
  } from "@frockbot/configuration-core";
@@ -85,6 +82,75 @@ async function installOllama(
85
82
  }
86
83
 
87
84
  describe("User settings backend Contribution", () => {
85
+ test("reads old settings purely and writes the migrated shape on the next command", async () => {
86
+ const storage = new MemoryStorage();
87
+ // Literal durable shape from eb0283edcce5daea976a21a9f6a6414bedc6e2bc,
88
+ // the first parent of PR #134's merge commit.
89
+ const historical = {
90
+ schemaVersion: 1,
91
+ revision: 7,
92
+ profile: { name: "Existing User" },
93
+ packages: [],
94
+ connections: [
95
+ {
96
+ connectionId: "ollama-1",
97
+ packageId: "provider-ollama-cloud",
98
+ connectionTypeId: "ollama-cloud-account",
99
+ displayName: "Work",
100
+ state: "ready",
101
+ safeMetadata: {
102
+ dependentAssignments: [
103
+ {
104
+ botId: "primary",
105
+ generation: "assignment-1",
106
+ packageId: "provider-ollama-cloud",
107
+ capabilityId: "ollama-cloud-models",
108
+ claimOrder: 0,
109
+ status: "acknowledged",
110
+ },
111
+ ],
112
+ },
113
+ },
114
+ ],
115
+ newBotModelTemplate: {
116
+ connectionId: "ollama-1",
117
+ providerModelId: "glm-5.3-flash:cloud",
118
+ },
119
+ newBotModelTemplateSource: "user",
120
+ };
121
+ storage.values.set("user-configuration", structuredClone(historical));
122
+ const settings = contribution(storage);
123
+
124
+ await expect(settings.readSnapshot()).resolves.toMatchObject({
125
+ revision: 7,
126
+ connections: [{ safeMetadata: {} }],
127
+ });
128
+ expect(await storage.get<unknown>("user-configuration")).toEqual(
129
+ historical,
130
+ );
131
+
132
+ await settings.executeConfiguration({
133
+ schemaVersion: 1,
134
+ userId: "user-1",
135
+ command: {
136
+ schemaVersion: 1,
137
+ type: "user/update-profile",
138
+ commandId: "migrate-user-settings",
139
+ expectedRevision: 7,
140
+ profile: { name: "Migrated User" },
141
+ },
142
+ });
143
+ const written =
144
+ await storage.get<Record<string, unknown>>("user-configuration");
145
+ expect(written).toMatchObject({
146
+ revision: 8,
147
+ profile: { name: "Migrated User" },
148
+ connections: [{ safeMetadata: {} }],
149
+ });
150
+ expect(written).not.toHaveProperty("newBotModelTemplate");
151
+ expect(written).not.toHaveProperty("newBotModelTemplateSource");
152
+ });
153
+
88
154
  test("bootstraps first-party Packages once and preserves a later uninstall", async () => {
89
155
  const storage = new MemoryStorage();
90
156
  const settings = createUserSettingsBackendContribution({
@@ -150,6 +216,229 @@ describe("User settings backend Contribution", () => {
150
216
  ]);
151
217
  });
152
218
 
219
+ test("bootstraps a declared default-disabled Package without re-enabling it", async () => {
220
+ const settings = createUserSettingsBackendContribution({
221
+ storage: new MemoryStorage(),
222
+ availablePackages: [
223
+ {
224
+ packageId: "custom-models",
225
+ version: "0.0.1",
226
+ installByDefault: true,
227
+ defaultEnablement: "disabled",
228
+ },
229
+ ],
230
+ });
231
+
232
+ const first = await settings.readConfiguration({
233
+ schemaVersion: 1,
234
+ userId: "user-1",
235
+ });
236
+ expect(first.packages).toEqual([
237
+ {
238
+ packageId: "custom-models",
239
+ version: "0.0.1",
240
+ state: "disabled",
241
+ provenance: "first-party",
242
+ },
243
+ ]);
244
+ expect(
245
+ await settings.readConfiguration({ schemaVersion: 1, userId: "user-1" }),
246
+ ).toEqual(first);
247
+ });
248
+
249
+ test("installs a Package disabled when explicitly requested", async () => {
250
+ const settings = contribution();
251
+ const receipt = await settings.executeConfiguration({
252
+ schemaVersion: 1,
253
+ userId: "user-1",
254
+ command: {
255
+ schemaVersion: 1,
256
+ type: "user/install-package",
257
+ commandId: "install-disabled",
258
+ expectedRevision: 0,
259
+ packageId: "provider-ollama-cloud",
260
+ version: "0.0.1",
261
+ enabled: false,
262
+ },
263
+ });
264
+
265
+ expect(receipt).toMatchObject({ status: "applied", revision: 1 });
266
+ expect((await settings.read("user-1")).packages).toMatchObject([
267
+ { packageId: "provider-ollama-cloud", state: "disabled" },
268
+ ]);
269
+ });
270
+
271
+ test("does not clear an existing failed installation", async () => {
272
+ const storage = new MemoryStorage();
273
+ const settings = contribution(storage);
274
+ await storage.put("user-id", "user-1");
275
+ await storage.put("user-configuration", {
276
+ schemaVersion: 1,
277
+ revision: 0,
278
+ profile: { name: "User" },
279
+ packages: [
280
+ {
281
+ packageId: "provider-ollama-cloud",
282
+ version: "0.0.1",
283
+ state: "failed",
284
+ failure: "activation failed",
285
+ },
286
+ ],
287
+ connections: [],
288
+ } satisfies UserSettingsViewV1);
289
+
290
+ await settings.executeConfiguration({
291
+ schemaVersion: 1,
292
+ userId: "user-1",
293
+ command: {
294
+ schemaVersion: 1,
295
+ type: "user/install-package",
296
+ commandId: "retry-failed-disabled",
297
+ expectedRevision: 0,
298
+ packageId: "provider-ollama-cloud",
299
+ version: "0.0.1",
300
+ enabled: false,
301
+ },
302
+ });
303
+
304
+ expect((await settings.read("user-1")).packages).toMatchObject([
305
+ {
306
+ packageId: "provider-ollama-cloud",
307
+ state: "failed",
308
+ failure: "activation failed",
309
+ },
310
+ ]);
311
+ });
312
+
313
+ test("refuses enabling until every declared dependency is enabled", async () => {
314
+ const settings = createUserSettingsBackendContribution({
315
+ storage: new MemoryStorage(),
316
+ availablePackages: [
317
+ { packageId: "custom-models", version: "0.0.1" },
318
+ {
319
+ packageId: "provider-ollama-cloud",
320
+ version: "0.0.1",
321
+ dependencies: { "custom-models": ">=0.0.1" },
322
+ },
323
+ ],
324
+ });
325
+ await settings.executeConfiguration({
326
+ schemaVersion: 1,
327
+ userId: "user-1",
328
+ command: {
329
+ schemaVersion: 1,
330
+ type: "user/install-package",
331
+ commandId: "install-custom-models-disabled",
332
+ expectedRevision: 0,
333
+ packageId: "custom-models",
334
+ version: "0.0.1",
335
+ enabled: false,
336
+ },
337
+ });
338
+ const refusedInstall = await settings.executeConfiguration({
339
+ schemaVersion: 1,
340
+ userId: "user-1",
341
+ command: {
342
+ schemaVersion: 1,
343
+ type: "user/install-package",
344
+ commandId: "install-ollama-enabled-too-soon",
345
+ expectedRevision: 1,
346
+ packageId: "provider-ollama-cloud",
347
+ version: "0.0.1",
348
+ },
349
+ });
350
+ expect(refusedInstall).toMatchObject({
351
+ status: "rejected",
352
+ revision: 1,
353
+ failure: expect.stringContaining("custom-models"),
354
+ });
355
+ await settings.executeConfiguration({
356
+ schemaVersion: 1,
357
+ userId: "user-1",
358
+ command: {
359
+ schemaVersion: 1,
360
+ type: "user/install-package",
361
+ commandId: "install-ollama-disabled",
362
+ expectedRevision: 1,
363
+ packageId: "provider-ollama-cloud",
364
+ version: "0.0.1",
365
+ enabled: false,
366
+ },
367
+ });
368
+
369
+ const refused = await settings.executeConfiguration({
370
+ schemaVersion: 1,
371
+ userId: "user-1",
372
+ command: {
373
+ schemaVersion: 1,
374
+ type: "user/set-package-enabled",
375
+ commandId: "enable-ollama-too-soon",
376
+ expectedRevision: 2,
377
+ packageId: "provider-ollama-cloud",
378
+ enabled: true,
379
+ },
380
+ });
381
+ expect(refused).toMatchObject({
382
+ status: "rejected",
383
+ revision: 2,
384
+ failure: expect.stringContaining("custom-models"),
385
+ });
386
+ expect((await settings.read("user-1")).packages[1]?.state).toBe("disabled");
387
+
388
+ await settings.executeConfiguration({
389
+ schemaVersion: 1,
390
+ userId: "user-1",
391
+ command: {
392
+ schemaVersion: 1,
393
+ type: "user/set-package-enabled",
394
+ commandId: "enable-custom-models",
395
+ expectedRevision: 2,
396
+ packageId: "custom-models",
397
+ enabled: true,
398
+ },
399
+ });
400
+ await expect(
401
+ settings.executeConfiguration({
402
+ schemaVersion: 1,
403
+ userId: "user-1",
404
+ command: {
405
+ schemaVersion: 1,
406
+ type: "user/set-package-enabled",
407
+ commandId: "enable-ollama",
408
+ expectedRevision: 3,
409
+ packageId: "provider-ollama-cloud",
410
+ enabled: true,
411
+ },
412
+ }),
413
+ ).resolves.toMatchObject({ status: "applied", revision: 4 });
414
+ });
415
+
416
+ test("applies the platform model through the backend Contribution", async () => {
417
+ const storage = new MemoryStorage();
418
+ const settings = contribution(storage);
419
+
420
+ await expect(
421
+ settings.executeConfigurationCommand(
422
+ "user-1",
423
+ {
424
+ schemaVersion: 1,
425
+ type: "user/set-platform-model",
426
+ commandId: "bootstrap-platform-model",
427
+ expectedRevision: 0,
428
+ model: {
429
+ connectionId: "flock-default",
430
+ providerModelId: "@flock/auto",
431
+ },
432
+ },
433
+ storage,
434
+ ),
435
+ ).resolves.toMatchObject({ status: "applied", revision: 1 });
436
+ expect((await settings.read("user-1")).platformModel).toEqual({
437
+ connectionId: "flock-default",
438
+ providerModelId: "@flock/auto",
439
+ });
440
+ });
441
+
153
442
  test("owns durable User configuration independently of providers", async () => {
154
443
  const storage = new MemoryStorage();
155
444
  const settings = contribution(storage);
@@ -223,233 +512,6 @@ describe("User settings backend Contribution", () => {
223
512
  ).rejects.toThrow("Package is not available in this application");
224
513
  });
225
514
 
226
- test("coordinates Connection dependencies in provider-neutral User state", async () => {
227
- const settings = contribution();
228
- await settings.executeConfiguration({
229
- schemaVersion: 1,
230
- userId: "user-1",
231
- command: {
232
- schemaVersion: 1,
233
- type: "user/install-package",
234
- commandId: "install-ollama",
235
- expectedRevision: 0,
236
- packageId: "provider-ollama-cloud",
237
- version: "0.0.1",
238
- },
239
- });
240
- await settings.createConnection("user-1", {
241
- connectionId: "ollama-1",
242
- packageId: "provider-ollama-cloud",
243
- connectionTypeId: "ollama-cloud-account",
244
- displayName: "Work",
245
- state: "ready",
246
- providerType: "ollama-cloud",
247
- generation: "connection-generation",
248
- safeMetadata: {},
249
- });
250
- const requirement = {
251
- schemaVersion: 1 as const,
252
- packageId: "provider-ollama-cloud",
253
- packageVersion: "0.0.1",
254
- capabilityId: "ollama-cloud-models",
255
- connectionTypeIds: ["ollama-cloud-account"],
256
- };
257
-
258
- await expect(
259
- settings.claimConnectionDependency(
260
- "user-1",
261
- "ollama-1",
262
- "bot-1",
263
- "assignment-1",
264
- requirement,
265
- ),
266
- ).resolves.toBe(true);
267
- await expect(
268
- settings.acknowledgeConnectionDependency(
269
- "user-1",
270
- "ollama-1",
271
- "bot-1",
272
- "assignment-1",
273
- ),
274
- ).resolves.toBe(true);
275
- expect(
276
- (await settings.getConnection("user-1", "ollama-1"))?.safeMetadata,
277
- ).toMatchObject({
278
- dependentAssignments: [
279
- {
280
- botId: "bot-1",
281
- generation: "assignment-1",
282
- packageId: "provider-ollama-cloud",
283
- capabilityId: "ollama-cloud-models",
284
- status: "acknowledged",
285
- },
286
- ],
287
- });
288
- await expect(
289
- settings.compensateConnectionDependency(
290
- "user-1",
291
- "ollama-1",
292
- "bot-1",
293
- "assignment-1",
294
- ),
295
- ).resolves.toBe(false);
296
-
297
- await settings.createConnection("user-1", {
298
- connectionId: "ollama-2",
299
- packageId: "provider-ollama-cloud",
300
- connectionTypeId: "ollama-cloud-account",
301
- displayName: "Personal",
302
- state: "ready",
303
- providerType: "ollama-cloud",
304
- generation: "connection-generation-2",
305
- safeMetadata: {},
306
- });
307
- await settings.claimConnectionDependency(
308
- "user-1",
309
- "ollama-2",
310
- "bot-1",
311
- "assignment-2",
312
- requirement,
313
- );
314
- await settings.acknowledgeConnectionDependency(
315
- "user-1",
316
- "ollama-2",
317
- "bot-1",
318
- "assignment-2",
319
- );
320
-
321
- expect(
322
- (await settings.getConnection("user-1", "ollama-1"))?.safeMetadata,
323
- ).toMatchObject({ dependentAssignments: [] });
324
- expect(
325
- (await settings.getConnection("user-1", "ollama-2"))?.safeMetadata,
326
- ).toMatchObject({
327
- dependentAssignments: [
328
- {
329
- botId: "bot-1",
330
- generation: "assignment-2",
331
- packageId: "provider-ollama-cloud",
332
- capabilityId: "ollama-cloud-models",
333
- status: "acknowledged",
334
- },
335
- ],
336
- });
337
- await expect(
338
- settings.releaseConnectionDependency(
339
- "user-1",
340
- "ollama-2",
341
- "bot-1",
342
- "assignment-2",
343
- ),
344
- ).resolves.toBe(true);
345
- await expect(
346
- settings.releaseConnectionDependency(
347
- "user-1",
348
- "ollama-2",
349
- "bot-1",
350
- "assignment-2",
351
- ),
352
- ).resolves.toBe(true);
353
- expect(
354
- (await settings.getConnection("user-1", "ollama-2"))?.safeMetadata,
355
- ).toMatchObject({ dependentAssignments: [] });
356
- });
357
-
358
- test("rejects acknowledgement from a superseded dependency claim", async () => {
359
- const settings = contribution();
360
- await settings.executeConfiguration({
361
- schemaVersion: 1,
362
- userId: "user-1",
363
- command: {
364
- schemaVersion: 1,
365
- type: "user/install-package",
366
- commandId: "install-fenced-package",
367
- expectedRevision: 0,
368
- packageId: "provider-ollama-cloud",
369
- version: "0.0.1",
370
- },
371
- });
372
- for (const connectionId of ["ollama-old", "ollama-new"]) {
373
- await settings.createConnection("user-1", {
374
- connectionId,
375
- packageId: "provider-ollama-cloud",
376
- connectionTypeId: "ollama-cloud-account",
377
- displayName: connectionId,
378
- state: "ready",
379
- providerType: "ollama-cloud",
380
- generation: `${connectionId}-generation`,
381
- safeMetadata: {},
382
- });
383
- }
384
- const requirement = {
385
- schemaVersion: 1 as const,
386
- packageId: "provider-ollama-cloud",
387
- packageVersion: "0.0.1",
388
- capabilityId: "ollama-cloud-models",
389
- connectionTypeIds: ["ollama-cloud-account"],
390
- };
391
- await settings.claimConnectionDependency(
392
- "user-1",
393
- "ollama-old",
394
- "bot-1",
395
- "assignment-old",
396
- requirement,
397
- );
398
- await settings.claimConnectionDependency(
399
- "user-1",
400
- "ollama-new",
401
- "bot-1",
402
- "assignment-new",
403
- requirement,
404
- );
405
- await settings.claimConnectionDependency(
406
- "user-1",
407
- "ollama-old",
408
- "bot-1",
409
- "assignment-old",
410
- requirement,
411
- );
412
-
413
- await expect(
414
- settings.acknowledgeConnectionDependency(
415
- "user-1",
416
- "ollama-old",
417
- "bot-1",
418
- "assignment-old",
419
- ),
420
- ).resolves.toBe(false);
421
- expect(
422
- (await settings.getConnection("user-1", "ollama-new"))?.safeMetadata,
423
- ).toMatchObject({
424
- dependentAssignments: [
425
- { generation: "assignment-new", status: "pending" },
426
- ],
427
- });
428
- await expect(
429
- settings.compensateConnectionDependency(
430
- "user-1",
431
- "ollama-old",
432
- "bot-1",
433
- "assignment-old",
434
- ),
435
- ).resolves.toBe(true);
436
- expect(
437
- (await settings.getConnection("user-1", "ollama-old"))?.safeMetadata,
438
- ).toMatchObject({ dependentAssignments: [] });
439
-
440
- await expect(
441
- settings.acknowledgeConnectionDependency(
442
- "user-1",
443
- "ollama-new",
444
- "bot-1",
445
- "assignment-new",
446
- ),
447
- ).resolves.toBe(true);
448
- expect(
449
- (await settings.getConnection("user-1", "ollama-old"))?.safeMetadata,
450
- ).toMatchObject({ dependentAssignments: [] });
451
- });
452
-
453
515
  test("compacts revoked Connections and bounds active Connections", async () => {
454
516
  const settings = contribution();
455
517
  await settings.read("user-1");
@@ -664,6 +726,9 @@ class FakeCatalog implements UserPackageCatalogHost {
664
726
  version: entry.version,
665
727
  manifestHash: entry.manifestHash,
666
728
  kind: entry.kind,
729
+ ...(entry.bundle === undefined
730
+ ? {}
731
+ : { contentHash: entry.bundle.contentHash }),
667
732
  })),
668
733
  },
669
734
  });
@@ -759,6 +824,89 @@ describe("remote Package Catalog", () => {
759
824
  });
760
825
  });
761
826
 
827
+ test("admits only the exact hash of a code-carrying Catalog entry", async () => {
828
+ const contentHash = "b".repeat(64);
829
+ const catalog = new FakeCatalog();
830
+ catalog.entries = [
831
+ catalogEntry({
832
+ packageId: "parcel-tracking",
833
+ catalogId: "parcel-tracking",
834
+ displayName: "Parcel tracking",
835
+ manifestHash: "e".repeat(64),
836
+ bundle: {
837
+ contentHash,
838
+ size: 512,
839
+ mediaType: "application/javascript",
840
+ bundlerVersion: "catalog-test@1",
841
+ manifest: {
842
+ schemaVersion: 3,
843
+ id: "parcel-tracking",
844
+ displayName: "Parcel tracking",
845
+ version: "0.0.1",
846
+ compatibility: { frockbot: "*" },
847
+ dependencies: {},
848
+ contributions: {
849
+ runtime: { entry: "./package.js", host: "bot-isolate" },
850
+ },
851
+ tools: [],
852
+ permissions: [],
853
+ },
854
+ },
855
+ }),
856
+ ];
857
+ const { settings, storage } = catalogContribution(
858
+ new MemoryStorage(),
859
+ catalog,
860
+ );
861
+ await settings.readConfiguration({ schemaVersion: 1, userId: "user-1" });
862
+
863
+ await expect(
864
+ settings.executeConfiguration({
865
+ schemaVersion: 1,
866
+ userId: "user-1",
867
+ command: {
868
+ schemaVersion: 1,
869
+ type: "user/install-package",
870
+ commandId: "install-parcel",
871
+ expectedRevision: 0,
872
+ packageId: "parcel-tracking",
873
+ version: "0.0.1",
874
+ catalogId: "parcel-tracking",
875
+ catalogGeneration: "gen-one",
876
+ contentHash,
877
+ },
878
+ }),
879
+ ).resolves.toMatchObject({ status: "applied" });
880
+ expect(
881
+ (await storage.get<UserSettingsViewV1>("user-configuration"))?.packages,
882
+ ).toEqual([
883
+ expect.objectContaining({ packageId: "parcel-tracking", contentHash }),
884
+ ]);
885
+
886
+ const mismatch = catalogContribution(new MemoryStorage(), catalog);
887
+ await mismatch.settings.readConfiguration({
888
+ schemaVersion: 1,
889
+ userId: "user-1",
890
+ });
891
+ await expect(
892
+ mismatch.settings.executeConfiguration({
893
+ schemaVersion: 1,
894
+ userId: "user-1",
895
+ command: {
896
+ schemaVersion: 1,
897
+ type: "user/install-package",
898
+ commandId: "install-wrong-hash",
899
+ expectedRevision: 0,
900
+ packageId: "parcel-tracking",
901
+ version: "0.0.1",
902
+ catalogId: "parcel-tracking",
903
+ catalogGeneration: "gen-one",
904
+ contentHash: "c".repeat(64),
905
+ },
906
+ }),
907
+ ).rejects.toThrow('requires bundle hash "bbbb');
908
+ });
909
+
762
910
  test("refuses an install off the pinned generation", async () => {
763
911
  const { catalog, settings } = catalogContribution();
764
912
  await settings.readConfiguration({ schemaVersion: 1, userId: "user-1" });
@@ -935,92 +1083,6 @@ describe("uninstall", () => {
935
1083
  ]);
936
1084
  });
937
1085
 
938
- test("dependent Assignments become unavailable tombstones, not deletions", async () => {
939
- const storage = new MemoryStorage();
940
- const settings = contribution(storage);
941
- await settings.executeConfiguration({
942
- schemaVersion: 1,
943
- userId: "user-1",
944
- command: {
945
- schemaVersion: 1,
946
- type: "user/install-package",
947
- commandId: "install-provider",
948
- expectedRevision: 0,
949
- packageId: "provider-ollama-cloud",
950
- version: "0.0.1",
951
- },
952
- });
953
- await settings.createConnection("user-1", {
954
- connectionId: "connection-1",
955
- packageId: "provider-ollama-cloud",
956
- connectionTypeId: "ollama-cloud-account",
957
- displayName: "Work",
958
- state: "ready",
959
- safeMetadata: {},
960
- });
961
- const assignment = {
962
- assignmentId: "assignment-1",
963
- packageId: "provider-ollama-cloud",
964
- capabilityId: "ollama-cloud-models",
965
- connectionId: "connection-1",
966
- };
967
- const packages = [
968
- {
969
- packageId: "provider-ollama-cloud",
970
- version: "0.0.1",
971
- capabilities: [
972
- {
973
- id: "ollama-cloud-models",
974
- kind: "model" as const,
975
- connectionTypes: ["ollama-cloud-account"],
976
- },
977
- ],
978
- connectionTypes: [
979
- {
980
- id: "ollama-cloud-account",
981
- capabilities: ["ollama-cloud-models"],
982
- },
983
- ],
984
- },
985
- ];
986
-
987
- const installed = await settings.readSnapshot();
988
- expect(
989
- capabilityAssignmentFailureV1({ assignment, user: installed, packages }),
990
- ).toBeUndefined();
991
-
992
- await settings.executeConfiguration({
993
- schemaVersion: 1,
994
- userId: "user-1",
995
- command: {
996
- schemaVersion: 1,
997
- type: "user/uninstall-package",
998
- commandId: "uninstall-provider",
999
- expectedRevision: installed.revision,
1000
- packageId: "provider-ollama-cloud",
1001
- },
1002
- });
1003
-
1004
- const uninstalled = await settings.readSnapshot();
1005
- expect(
1006
- capabilityAssignmentFailureV1({
1007
- assignment,
1008
- user: uninstalled,
1009
- packages,
1010
- }),
1011
- ).toContain("is not installed and enabled");
1012
- expect(
1013
- resolveBotExecutionPlanV1({
1014
- bot: {
1015
- ...initializeBotSettingsV1("bot-1"),
1016
- assignments: [{ ...assignment, state: "enabled" }],
1017
- },
1018
- user: uninstalled,
1019
- packages,
1020
- }).assignments,
1021
- ).toEqual([{ ...assignment, state: "unavailable" }]);
1022
- });
1023
-
1024
1086
  test("refuses to uninstall a Package that is not installed", async () => {
1025
1087
  const settings = contribution();
1026
1088
 
@@ -1079,6 +1141,53 @@ describe("Package-level setting values", () => {
1079
1141
  });
1080
1142
  });
1081
1143
 
1144
+ test("removes only declared setting ids and drops an emptied value bag", async () => {
1145
+ const settings = contribution();
1146
+ await installOllama(settings, "user-1");
1147
+ await settings.executeConfiguration({
1148
+ schemaVersion: 1,
1149
+ userId: "user-1",
1150
+ command: {
1151
+ schemaVersion: 1,
1152
+ type: "user/set-package-settings",
1153
+ commandId: "set-before-unset",
1154
+ expectedRevision: 1,
1155
+ packageId: "provider-ollama-cloud",
1156
+ values: { "web-search-max-results": 4 },
1157
+ },
1158
+ });
1159
+
1160
+ await settings.executeConfiguration({
1161
+ schemaVersion: 1,
1162
+ userId: "user-1",
1163
+ command: {
1164
+ schemaVersion: 1,
1165
+ type: "user/set-package-settings",
1166
+ commandId: "unset-value",
1167
+ expectedRevision: 2,
1168
+ packageId: "provider-ollama-cloud",
1169
+ unset: ["web-search-max-results"],
1170
+ },
1171
+ });
1172
+ expect((await settings.read("user-1")).packages[0]?.values).toBeUndefined();
1173
+
1174
+ await expect(
1175
+ settings.executeConfiguration({
1176
+ schemaVersion: 1,
1177
+ userId: "user-1",
1178
+ command: {
1179
+ schemaVersion: 1,
1180
+ type: "user/set-package-settings",
1181
+ commandId: "unset-unknown",
1182
+ expectedRevision: 3,
1183
+ packageId: "provider-ollama-cloud",
1184
+ unset: ["unknown-setting"],
1185
+ },
1186
+ }),
1187
+ ).rejects.toThrow(/not declared by this Package/);
1188
+ expect((await settings.read("user-1")).revision).toBe(3);
1189
+ });
1190
+
1082
1191
  test("a replayed command id returns its receipt without applying twice", async () => {
1083
1192
  const settings = contribution();
1084
1193
  await installOllama(settings, "user-1");