@frockbot/plugin-flock 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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frockbot/plugin-flock",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -27,16 +27,16 @@
27
27
  "typecheck": "vue-tsc --noEmit -p tsconfig.json"
28
28
  },
29
29
  "dependencies": {
30
- "@frockbot/client-core": "0.1.3",
31
- "@frockbot/client-ui": "0.1.3",
32
- "@frockbot/configuration-core": "0.1.3",
33
- "@frockbot/kernel-contracts": "0.1.3",
34
- "@frockbot/plugin-shell": "0.1.3",
30
+ "@frockbot/client-core": "0.1.4",
31
+ "@frockbot/client-ui": "0.1.4",
32
+ "@frockbot/configuration-core": "0.1.4",
33
+ "@frockbot/kernel-contracts": "0.1.4",
34
+ "@frockbot/plugin-shell": "0.1.4",
35
35
  "cordis": "4.0.0-rc.8",
36
36
  "vue": "3.5.41"
37
37
  },
38
38
  "devDependencies": {
39
- "@frockbot/plugin-testkit": "0.1.3",
39
+ "@frockbot/plugin-testkit": "0.1.4",
40
40
  "@types/bun": "1.4.0",
41
41
  "@vitejs/plugin-vue": "6.0.8",
42
42
  "css-tree": "2.3.1",
package/src/agent.test.ts CHANGED
@@ -94,14 +94,6 @@ function harness(initial?: Partial<BotSettingsViewV1>): Harness {
94
94
  const storage = memoryStorage();
95
95
  const flock = createFlockUserBackendContribution({
96
96
  storage,
97
- readUserSettings: () =>
98
- Promise.resolve({
99
- schemaVersion: 1,
100
- revision: 0,
101
- profile: { name: "User" },
102
- packages: [],
103
- connections: [],
104
- }),
105
97
  now: () => new Date("2026-08-31T10:00:00.000Z"),
106
98
  random: () => 0,
107
99
  commandBotLifecycle: () => {
@@ -339,10 +331,9 @@ describe("bot_create", () => {
339
331
  expect(created.botId).toBe(
340
332
  await createdBotIdV1(OWNER, CONTEXT.effectId, "Budget"),
341
333
  );
342
- // The new Bot receives no authority beyond what `bot/create` grants: no
343
- // model of its own and no Assignment.
344
- expect(created.initialModel).toBeUndefined();
345
- expect(created.initialModelBinding).toBeUndefined();
334
+ // Model and Capability authority resolve account-wide on the new Bot's
335
+ // next admitted Turn; neither is copied into its registration.
336
+ expect(Object.hasOwn(created, "initialModel")).toBe(false);
346
337
  expect(result.content).toContain(created.botId);
347
338
  });
348
339
 
@@ -73,7 +73,7 @@ onBeforeUnmount(() => restoreFocus?.focus());
73
73
  <h1 id="flock-title">Archive this Bot?</h1>
74
74
  <p>
75
75
  Archiving stops new work and hides the Bot from your active flock.
76
- History, settings, and Assignments are preserved for restoration.
76
+ History and settings are preserved for restoration.
77
77
  </p>
78
78
  <p
79
79
  v-if="flock.error"
@@ -185,8 +185,8 @@ onBeforeUnmount(() => restoreFocus?.focus());
185
185
  </div>
186
186
  </fieldset>
187
187
  <div class="flock-note">
188
- This identity is durable. Refreshing or switching devices won’t
189
- re-roll it.
188
+ This identity stays with your Bot. Refreshing or switching devices
189
+ won’t re-roll it.
190
190
  </div>
191
191
  <p
192
192
  v-if="flock.error"
@@ -69,10 +69,9 @@ describe("Flock v1 contracts", () => {
69
69
  ).toThrow("unknown or missing field");
70
70
  });
71
71
 
72
- test("round-trips configuration-valid provider model text", () => {
72
+ test("rejects retired model seed fields", () => {
73
73
  const sheep = randomSheepRecipeV1(() => 0);
74
- const providerModelId = "m".repeat(256);
75
- expect(
74
+ expect(() =>
76
75
  decodeDirectoryViewV1({
77
76
  schemaVersion: 1,
78
77
  revision: 1,
@@ -84,23 +83,13 @@ describe("Flock v1 contracts", () => {
84
83
  initialName: "Alpha",
85
84
  initialModel: {
86
85
  connectionId: "connection-1",
87
- providerModelId,
88
- },
89
- initialModelBinding: {
90
- assignment: {
91
- assignmentId: "create-alpha",
92
- packageId: "provider-ollama-cloud",
93
- capabilityId: "ollama-cloud-models",
94
- connectionId: "connection-1",
95
- state: "enabled",
96
- },
97
- generation: "create-alpha",
86
+ providerModelId: "model-1",
98
87
  },
99
88
  sheep,
100
89
  },
101
90
  ],
102
- }).bots[0]?.initialModel?.providerModelId,
103
- ).toBe(providerModelId);
91
+ }),
92
+ ).toThrow("unknown or missing field");
104
93
  expect(() =>
105
94
  decodeCreateBotCommandV1({
106
95
  schemaVersion: 1,
package/src/shared.ts CHANGED
@@ -1,13 +1,10 @@
1
1
  // Shared configuration types remain provider-neutral at this package seam.
2
2
  import {
3
- decodeCapabilityAssignmentV1,
4
3
  decodeBotIdV1,
5
4
  decodeBotSelfWriterV1,
6
5
  isPublicIdentifier,
7
6
  type BotNameProvenanceV1,
8
7
  type BotSelfWriterV1,
9
- type CapabilityAssignmentView,
10
- type ModelAssignment,
11
8
  } from "@frockbot/configuration-core";
12
9
  export type { BotSelfWriterV1 } from "@frockbot/configuration-core";
13
10
  import assetManifest from "../assets/manifest.json" with { type: "json" };
@@ -26,10 +23,6 @@ export interface SheepRecipeV1 {
26
23
  middle: string;
27
24
  lower: string;
28
25
  }
29
- export interface InitialModelBindingV1 {
30
- assignment: CapabilityAssignmentView;
31
- generation: string;
32
- }
33
26
  export interface BotRegistrationV1 {
34
27
  schemaVersion: 1;
35
28
  botId: string;
@@ -42,13 +35,6 @@ export interface BotRegistrationV1 {
42
35
  * Durable Object, which no other Bot may write.
43
36
  */
44
37
  initialDescription?: string;
45
- initialModel?: ModelAssignment;
46
- initialModelBinding?: InitialModelBindingV1;
47
- /**
48
- * Connection-less Capability authority snapshotted from the User's installed
49
- * Packages when this Bot was admitted.
50
- */
51
- initialAssignments?: CapabilityAssignmentView[];
52
38
  /**
53
39
  * The Bot and Turn that created this Bot, when a Bot created it. Absent for
54
40
  * a User-created Bot and for every registration written before this existed.
@@ -264,63 +250,6 @@ function revision(value: unknown): number {
264
250
  throw new FlockDecodeError("revision is invalid");
265
251
  return value as number;
266
252
  }
267
- function modelAssignment(value: unknown): ModelAssignment {
268
- const model = record(value, "initialModel");
269
- exact(model, ["connectionId", "providerModelId"]);
270
- return {
271
- connectionId: identifier(model.connectionId, "initialModel.connectionId"),
272
- providerModelId: boundedText(
273
- model.providerModelId,
274
- "initialModel.providerModelId",
275
- 256,
276
- ),
277
- };
278
- }
279
-
280
- function initialModelBinding(value: unknown): InitialModelBindingV1 {
281
- const binding = record(value, "initialModelBinding");
282
- exact(binding, ["assignment", "generation"]);
283
- const assignment = record(
284
- binding.assignment,
285
- "initialModelBinding.assignment",
286
- );
287
- exact(assignment, [
288
- "assignmentId",
289
- "packageId",
290
- "capabilityId",
291
- "connectionId",
292
- "state",
293
- ]);
294
- if (assignment.state !== "enabled") {
295
- throw new FlockDecodeError("initial model assignment is invalid");
296
- }
297
- return {
298
- assignment: {
299
- assignmentId: identifier(
300
- assignment.assignmentId,
301
- "initialModelBinding.assignment.assignmentId",
302
- ),
303
- packageId: identifier(
304
- assignment.packageId,
305
- "initialModelBinding.assignment.packageId",
306
- ),
307
- capabilityId: identifier(
308
- assignment.capabilityId,
309
- "initialModelBinding.assignment.capabilityId",
310
- ),
311
- connectionId: identifier(
312
- assignment.connectionId,
313
- "initialModelBinding.assignment.connectionId",
314
- ),
315
- state: "enabled",
316
- },
317
- generation: identifier(
318
- binding.generation,
319
- "initialModelBinding.generation",
320
- ),
321
- };
322
- }
323
-
324
253
  export function decodeSheepRecipeV1(input: unknown): SheepRecipeV1 {
325
254
  const value = record(input, "sheep recipe");
326
255
  exact(value, ["schemaVersion", "background", "upper", "middle", "lower"]);
@@ -405,51 +334,10 @@ export function decodeBotRegistrationV1(input: unknown): BotRegistrationV1 {
405
334
  exact(
406
335
  bot,
407
336
  ["schemaVersion", "botId", "registeredAt", "initialName", "sheep"],
408
- [
409
- "initialDescription",
410
- "initialModel",
411
- "initialModelBinding",
412
- "initialAssignments",
413
- "createdBy",
414
- ],
337
+ ["initialDescription", "createdBy"],
415
338
  );
416
339
  if (bot.schemaVersion !== 1)
417
340
  throw new FlockDecodeError("unsupported Bot registration");
418
- const initialModel =
419
- bot.initialModel === undefined
420
- ? undefined
421
- : modelAssignment(bot.initialModel);
422
- const binding =
423
- bot.initialModelBinding === undefined
424
- ? undefined
425
- : initialModelBinding(bot.initialModelBinding);
426
- if (
427
- bot.initialAssignments !== undefined &&
428
- !Array.isArray(bot.initialAssignments)
429
- )
430
- throw new FlockDecodeError("initial Assignments are invalid");
431
- const initialAssignments =
432
- bot.initialAssignments === undefined
433
- ? undefined
434
- : bot.initialAssignments.map(decodeCapabilityAssignmentV1);
435
- if (
436
- new Set(
437
- (initialAssignments ?? []).map((assignment) => assignment.assignmentId),
438
- ).size !== (initialAssignments ?? []).length ||
439
- (initialAssignments ?? []).some(
440
- (assignment) =>
441
- assignment.connectionId !== undefined || assignment.state !== "enabled",
442
- )
443
- ) {
444
- throw new FlockDecodeError("initial Assignments are invalid");
445
- }
446
- if (
447
- binding &&
448
- (!initialModel ||
449
- binding.assignment.connectionId !== initialModel.connectionId)
450
- ) {
451
- throw new FlockDecodeError("initial model binding is invalid");
452
- }
453
341
  const botId = botIdentifier(bot.botId);
454
342
  const createdBy = botWriter(bot.createdBy, "createdBy");
455
343
  if (createdBy && createdBy.botId === botId)
@@ -468,9 +356,6 @@ export function decodeBotRegistrationV1(input: unknown): BotRegistrationV1 {
468
356
  10_000,
469
357
  ),
470
358
  }),
471
- initialModel,
472
- initialModelBinding: binding,
473
- ...(initialAssignments ? { initialAssignments } : {}),
474
359
  ...(createdBy ? { createdBy } : {}),
475
360
  sheep: decodeSheepRecipeV1(bot.sheep),
476
361
  };
package/src/user.test.ts CHANGED
@@ -41,22 +41,6 @@ class MemoryStorage {
41
41
  return Promise.resolve();
42
42
  }
43
43
  }
44
- const settings = {
45
- schemaVersion: 1 as const,
46
- revision: 4,
47
- profile: { name: "User" },
48
- packages: [
49
- { packageId: "web", version: "0.0.1", state: "installed" as const },
50
- {
51
- packageId: "provider-ollama-cloud",
52
- version: "0.0.1",
53
- state: "installed" as const,
54
- },
55
- ],
56
- connections: [],
57
- newBotModelTemplate: { connectionId: "provider", providerModelId: "model" },
58
- newBotModelTemplateSource: "auto" as const,
59
- };
60
44
  function command(commandId = "create-1", expectedRevision = 0) {
61
45
  return {
62
46
  schemaVersion: 1 as const,
@@ -70,46 +54,10 @@ function command(commandId = "create-1", expectedRevision = 0) {
70
54
  }
71
55
 
72
56
  describe("Flock User contribution", () => {
73
- test("atomically admits, snapshots defaults, and replays duplicate delivery", async () => {
57
+ test("atomically admits a model-free Bot and replays duplicate delivery", async () => {
74
58
  const storage = new MemoryStorage();
75
59
  const contribution = createFlockUserBackendContribution({
76
60
  storage,
77
- availablePackages: [
78
- {
79
- packageId: "web",
80
- version: "0.0.1",
81
- capabilities: [
82
- { id: "web-fetch", kind: "tool", connectionTypes: [] },
83
- ],
84
- connectionTypes: [],
85
- },
86
- {
87
- packageId: "provider-ollama-cloud",
88
- version: "0.0.1",
89
- capabilities: [
90
- {
91
- id: "ollama-cloud-models",
92
- kind: "model",
93
- connectionTypes: ["ollama-cloud-account"],
94
- },
95
- ],
96
- connectionTypes: [
97
- {
98
- id: "ollama-cloud-account",
99
- capabilities: ["ollama-cloud-models"],
100
- },
101
- ],
102
- },
103
- ],
104
- readUserSettings: () => Promise.resolve(settings),
105
- claimInitialModelBinding: (_storage, input) =>
106
- Promise.resolve({
107
- assignmentId: input.generation,
108
- packageId: "provider-ollama-cloud",
109
- capabilityId: "ollama-cloud-models",
110
- connectionId: input.model.connectionId,
111
- state: "enabled",
112
- }),
113
61
  now: () => new Date("2026-08-29T00:00:00.000Z"),
114
62
  commandBotLifecycle: () => Promise.reject(new Error("not used")),
115
63
  readBotLifecycle: () => Promise.reject(new Error("not used")),
@@ -122,20 +70,9 @@ describe("Flock User contribution", () => {
122
70
  initialName: "Alpha",
123
71
  registeredAt: "2026-08-29T00:00:00.000Z",
124
72
  });
125
- // A new Bot follows the User's default model instead of copying it, so
126
- // the registration seed carries no model and no claimed binding.
127
- expect(await contribution.registration("alpha")).toMatchObject({
128
- initialModel: undefined,
129
- initialModelBinding: undefined,
130
- initialAssignments: [
131
- {
132
- assignmentId: "default-0-0",
133
- packageId: "web",
134
- capabilityId: "web-fetch",
135
- state: "enabled",
136
- },
137
- ],
138
- });
73
+ const registration = await contribution.registration("alpha");
74
+ expect(Object.hasOwn(registration, "initialModel")).toBe(false);
75
+ expect(Object.hasOwn(registration, "initialCapabilities")).toBe(false);
139
76
  await expect(
140
77
  contribution.createBot("user-1", { ...command(), name: "Different" }),
141
78
  ).rejects.toThrow("command ID collision");
@@ -147,16 +84,10 @@ describe("Flock User contribution", () => {
147
84
  ).rejects.toBeInstanceOf(FlockConflictError);
148
85
  });
149
86
 
150
- test("admits a Bot while the User default model is unclaimable", async () => {
87
+ test("admits a Bot without consulting User model state", async () => {
151
88
  const storage = new MemoryStorage();
152
- let claims = 0;
153
89
  const contribution = createFlockUserBackendContribution({
154
90
  storage,
155
- readUserSettings: () => Promise.resolve(settings),
156
- claimInitialModelBinding: () => {
157
- claims += 1;
158
- return Promise.resolve(undefined);
159
- },
160
91
  commandBotLifecycle: () => Promise.reject(new Error("not used")),
161
92
  readBotLifecycle: () => Promise.reject(new Error("not used")),
162
93
  });
@@ -164,9 +95,8 @@ describe("Flock User contribution", () => {
164
95
  await expect(
165
96
  contribution.createBot("user-1", command()),
166
97
  ).resolves.toMatchObject({ status: "applied" });
167
- expect(claims).toBe(0);
168
98
  expect((await contribution.listBots()).bots).toMatchObject([
169
- { botId: "alpha", initialModel: undefined },
99
+ { botId: "alpha" },
170
100
  ]);
171
101
  });
172
102
 
@@ -174,10 +104,8 @@ describe("Flock User contribution", () => {
174
104
  const storage = new MemoryStorage();
175
105
  const contribution = createFlockUserBackendContribution({
176
106
  storage,
177
- readUserSettings: () => Promise.resolve(settings),
178
107
  commandBotLifecycle: () => Promise.reject(new Error("not used")),
179
108
  readBotLifecycle: () => Promise.reject(new Error("not used")),
180
- claimInitialModelBinding: () => Promise.resolve(undefined),
181
109
  });
182
110
  await storage.put("flock:directory:v1", {
183
111
  schemaVersion: 1,
@@ -204,15 +132,6 @@ describe("Flock User contribution", () => {
204
132
  let calls = 0;
205
133
  const contribution = createFlockUserBackendContribution({
206
134
  storage,
207
- readUserSettings: () => Promise.resolve(settings),
208
- claimInitialModelBinding: (_storage, input) =>
209
- Promise.resolve({
210
- assignmentId: input.generation,
211
- packageId: "provider-ollama-cloud",
212
- capabilityId: "ollama-cloud-models",
213
- connectionId: input.model.connectionId,
214
- state: "enabled" as const,
215
- }),
216
135
  commandBotLifecycle: (_userId, lifecycleCommand) => {
217
136
  calls += 1;
218
137
  botStatus =
@@ -273,15 +192,6 @@ describe("Flock User contribution", () => {
273
192
  let recovered = false;
274
193
  const contribution = createFlockUserBackendContribution({
275
194
  storage,
276
- readUserSettings: () => Promise.resolve(settings),
277
- claimInitialModelBinding: (_storage, input) =>
278
- Promise.resolve({
279
- assignmentId: input.generation,
280
- packageId: "provider-ollama-cloud",
281
- capabilityId: "ollama-cloud-models",
282
- connectionId: input.model.connectionId,
283
- state: "enabled" as const,
284
- }),
285
195
  commandBotLifecycle: () => Promise.reject(new Error("response lost")),
286
196
  readBotLifecycle: (_userId, botId) =>
287
197
  recovered
@@ -315,15 +225,6 @@ describe("Flock User contribution", () => {
315
225
  recovered = true;
316
226
  const reconstructed = createFlockUserBackendContribution({
317
227
  storage,
318
- readUserSettings: () => Promise.resolve(settings),
319
- claimInitialModelBinding: (_storage, input) =>
320
- Promise.resolve({
321
- assignmentId: input.generation,
322
- packageId: "provider-ollama-cloud",
323
- capabilityId: "ollama-cloud-models",
324
- connectionId: input.model.connectionId,
325
- state: "enabled" as const,
326
- }),
327
228
  commandBotLifecycle: () => Promise.reject(new Error("response lost")),
328
229
  readBotLifecycle: (_userId, botId) =>
329
230
  Promise.resolve({
@@ -356,15 +257,6 @@ describe("Flock User contribution", () => {
356
257
  const create = () =>
357
258
  createFlockUserBackendContribution({
358
259
  storage,
359
- readUserSettings: () => Promise.resolve(settings),
360
- claimInitialModelBinding: (_storage, input) =>
361
- Promise.resolve({
362
- assignmentId: input.generation,
363
- packageId: "provider-ollama-cloud",
364
- capabilityId: "ollama-cloud-models",
365
- connectionId: input.model.connectionId,
366
- state: "enabled" as const,
367
- }),
368
260
  commandBotLifecycle: () =>
369
261
  Promise.resolve({
370
262
  schemaVersion: 1,
@@ -411,17 +303,8 @@ describe("Flock User contribution", () => {
411
303
  const storage = new MemoryStorage();
412
304
  const contribution = createFlockUserBackendContribution({
413
305
  storage,
414
- readUserSettings: () => Promise.resolve(settings),
415
306
  commandBotLifecycle: () => Promise.reject(new Error("not used")),
416
307
  readBotLifecycle: () => Promise.reject(new Error("not used")),
417
- claimInitialModelBinding: (_storage, input) =>
418
- Promise.resolve({
419
- assignmentId: input.generation,
420
- packageId: "provider-ollama-cloud",
421
- capabilityId: "ollama-cloud-models",
422
- connectionId: input.model.connectionId,
423
- state: "enabled",
424
- }),
425
308
  });
426
309
  await contribution.createBot("user-1", command());
427
310
  expect(
package/src/user.ts CHANGED
@@ -1,9 +1,3 @@
1
- import type {
2
- CapabilityAssignmentView,
3
- ExecutionPackageDefinition,
4
- ModelAssignment,
5
- UserSettingsViewV1,
6
- } from "@frockbot/configuration-core";
7
1
  import type { Plugin } from "cordis";
8
2
  import {
9
3
  BotNotFoundError,
@@ -51,28 +45,6 @@ interface Storage extends FlockUserTransaction {
51
45
  }
52
46
  export interface FlockUserBackendHost {
53
47
  storage: Storage;
54
- /**
55
- * Retained for hosts that materialize a Bot from User state. A new Bot no
56
- * longer copies the User default model, so neither seam is called here.
57
- */
58
- readUserSettings(
59
- storage: FlockUserTransaction,
60
- userId: string,
61
- ): Promise<UserSettingsViewV1>;
62
- /**
63
- * Immutable Package definitions from the application manifest. Omitted only
64
- * by narrow test adapters that intentionally expose no Package authority.
65
- */
66
- availablePackages?: readonly ExecutionPackageDefinition[];
67
- claimInitialModelBinding?(
68
- storage: FlockUserTransaction,
69
- input: {
70
- userId: string;
71
- botId: string;
72
- generation: string;
73
- model: ModelAssignment;
74
- },
75
- ): Promise<CapabilityAssignmentView | undefined>;
76
48
  now?: () => Date;
77
49
  random?: () => number;
78
50
  commandBotLifecycle(
@@ -187,34 +159,9 @@ export class FlockUserBackendContribution {
187
159
  failure: "Bot directory limit reached",
188
160
  };
189
161
  } else {
190
- const user = await this.host.readUserSettings(storage, userId);
191
- const installedVersions = new Map(
192
- user.packages.flatMap((pkg) =>
193
- pkg.state === "installed" ? [[pkg.packageId, pkg.version]] : [],
194
- ),
195
- );
196
- const initialAssignments = (this.host.availablePackages ?? []).flatMap(
197
- (pkg, packageIndex) =>
198
- installedVersions.get(pkg.packageId) === pkg.version
199
- ? pkg.capabilities.flatMap((capability, capabilityIndex) =>
200
- capability.connectionTypes.length === 0 &&
201
- capability.kind !== "model"
202
- ? [
203
- {
204
- assignmentId: `default-${packageIndex}-${capabilityIndex}`,
205
- packageId: pkg.packageId,
206
- capabilityId: capability.id,
207
- state: "enabled" as const,
208
- },
209
- ]
210
- : [],
211
- )
212
- : [],
213
- );
214
- // A new Bot carries no model of its own: it follows the User's
215
- // default model dynamically, and claims the Connection's model
216
- // Capability the first time it resolves its execution context. Only a
217
- // Bot that overrides the default owns a durable `model`.
162
+ // A new Bot carries neither a model nor a grant. Both resolve from the
163
+ // User's enabled Packages and Connections at its next admitted Turn
164
+ // (AGENTS.md Configuration shape; ADR 0019).
218
165
  const registration: BotRegistrationV1 = {
219
166
  schemaVersion: 1,
220
167
  botId: command.botId,
@@ -223,9 +170,6 @@ export class FlockUserBackendContribution {
223
170
  ...(command.description === undefined
224
171
  ? {}
225
172
  : { initialDescription: command.description }),
226
- initialModel: undefined,
227
- initialModelBinding: undefined,
228
- initialAssignments,
229
173
  // The creator is durable history: a Bot the Flock made on another
230
174
  // Bot's behalf says so in the registration seed itself.
231
175
  ...(command.createdBy