@frockbot/plugin-provider-ollama-cloud 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/README.md CHANGED
@@ -38,10 +38,10 @@ kernel.
38
38
  | Effect class | read-only, `idempotent: true` |
39
39
  | Turn types | all four (manifest v4 `admission`) |
40
40
 
41
- **Authority.** The tool mounts only for an enabled Assignment of
42
- `ollama-cloud-web-search` bound to a ready Connection. There is no
43
- unauthenticated fallback: a Bot without the Assignment is never offered the
44
- tool. The key is leased per durable `effectId`, opened inside this Package, used
41
+ **Authority.** The tool mounts only while `ollama-cloud-web-search` is enabled
42
+ account-wide and resolves a ready Connection. There is no unauthenticated
43
+ fallback: without that capability the Bot is never offered the tool. The key
44
+ is leased per durable `effectId`, opened inside this Package, used
45
45
  and settled, so it never reaches a tool argument, a tool result, or the event
46
46
  log.
47
47
 
package/frockbot.json CHANGED
@@ -5,10 +5,12 @@
5
5
  "version": "0.0.1",
6
6
  "compatibility": { "frockbot": ">=0.0.1" },
7
7
  "dependencies": {
8
+ "custom-models": ">=0.0.1",
8
9
  "credentials": ">=0.0.1",
9
10
  "settings": ">=0.0.1",
10
11
  "web": ">=0.0.1"
11
12
  },
13
+ "defaultEnablement": "disabled",
12
14
  "contributions": {
13
15
  "backend": [{ "entry": "./user", "host": "user" }],
14
16
  "runtime": { "entry": "./runtime" }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@frockbot/plugin-provider-ollama-cloud",
3
- "version": "0.1.3",
3
+ "version": "0.2.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "exports": {
@@ -20,19 +20,19 @@
20
20
  "typecheck": "tsc --noEmit -p tsconfig.json"
21
21
  },
22
22
  "dependencies": {
23
- "@frockbot/configuration-core": "0.1.3",
24
- "@frockbot/connection-core": "0.1.3",
25
- "@frockbot/kernel-agent-loop": "0.1.3",
26
- "@frockbot/kernel-contracts": "0.1.3",
27
- "@frockbot/plugin-credentials": "0.1.3",
28
- "@frockbot/plugin-models": "0.1.3",
29
- "@frockbot/plugin-settings": "0.1.3",
30
- "@frockbot/plugin-web": "0.1.3",
31
- "@frockbot/provider-openai-compatible": "0.1.3",
23
+ "@frockbot/configuration-core": "0.2.0",
24
+ "@frockbot/connection-core": "0.2.0",
25
+ "@frockbot/kernel-agent-loop": "0.2.0",
26
+ "@frockbot/kernel-contracts": "0.2.0",
27
+ "@frockbot/plugin-credentials": "0.2.0",
28
+ "@frockbot/plugin-models": "0.2.0",
29
+ "@frockbot/plugin-settings": "0.2.0",
30
+ "@frockbot/plugin-web": "0.2.0",
31
+ "@frockbot/provider-openai-compatible": "0.2.0",
32
32
  "cordis": "4.0.0-rc.8"
33
33
  },
34
34
  "devDependencies": {
35
- "@frockbot/plugin-tools": "0.1.3",
35
+ "@frockbot/plugin-tools": "0.2.0",
36
36
  "@types/bun": "1.3.6",
37
37
  "typescript": "^7.0.2"
38
38
  },
package/src/user.test.ts CHANGED
@@ -12,7 +12,6 @@ import {
12
12
  import { OllamaCloudClient, type OllamaFetch } from "./client.js";
13
13
  import {
14
14
  createOllamaCloudUserBackendContribution,
15
- selectAutomaticOllamaModelV1,
16
15
  type OllamaUserBackendHost,
17
16
  } from "./user.js";
18
17
 
@@ -254,84 +253,14 @@ describe("Ollama Cloud User Contribution", () => {
254
253
  models: [{ providerModelId: "glm-5.3-flash:cloud" }],
255
254
  },
256
255
  });
257
- expect(await settings.read("account-1")).toMatchObject({
258
- newBotModelTemplate: {
259
- connectionId: result.connectionId,
260
- providerModelId: "glm-5.3-flash:cloud",
261
- },
262
- newBotModelTemplateSource: "auto",
263
- });
256
+ expect(
257
+ Object.hasOwn(await settings.read("account-1"), "platformModel"),
258
+ ).toBe(false);
264
259
  expect(JSON.stringify([...storage.values.values()])).not.toContain(
265
260
  "ollama-secret",
266
261
  );
267
262
  });
268
263
 
269
- test("never replaces a User-chosen default when a Connection succeeds", async () => {
270
- const { settings, ollama } = await fixture();
271
- const before = await settings.read("account-1");
272
- await settings.executeConfiguration({
273
- schemaVersion: 1,
274
- userId: "account-1",
275
- command: {
276
- schemaVersion: 1,
277
- type: "user/set-new-bot-model",
278
- commandId: "choose-model",
279
- expectedRevision: before.revision,
280
- model: {
281
- connectionId: "chosen-connection",
282
- providerModelId: "chosen-model",
283
- },
284
- source: "user",
285
- },
286
- });
287
-
288
- await ollama.executeConnection("account-1", {
289
- schemaVersion: 1,
290
- type: "connection/create-api-key",
291
- commandId: "connect-after-choice",
292
- packageId: "provider-ollama-cloud",
293
- connectionTypeId: "ollama-cloud-account",
294
- label: "Work",
295
- apiKey: "ollama-secret",
296
- });
297
-
298
- expect(await settings.read("account-1")).toMatchObject({
299
- newBotModelTemplate: {
300
- connectionId: "chosen-connection",
301
- providerModelId: "chosen-model",
302
- },
303
- newBotModelTemplateSource: "user",
304
- });
305
- });
306
-
307
- test("prefers gpt-oss, then GLM, then the first catalog model", () => {
308
- const model = (providerModelId: string) => ({
309
- providerModelId,
310
- displayName: providerModelId,
311
- capabilities: { tools: true, vision: false, reasoning: true },
312
- source: "discovered" as const,
313
- });
314
- const catalog = (ids: string[]) => ({
315
- schemaVersion: 1 as const,
316
- generation: "catalog-1",
317
- state: "fresh" as const,
318
- models: ids.map(model),
319
- });
320
- expect(
321
- selectAutomaticOllamaModelV1(
322
- catalog(["other", "glm-5.3-flash:cloud", "gpt-oss:20b"]),
323
- )?.providerModelId,
324
- ).toBe("gpt-oss:20b");
325
- expect(
326
- selectAutomaticOllamaModelV1(catalog(["other", "glm-5.3-flash:cloud"]))
327
- ?.providerModelId,
328
- ).toBe("glm-5.3-flash:cloud");
329
- expect(
330
- selectAutomaticOllamaModelV1(catalog(["other", "second"]))
331
- ?.providerModelId,
332
- ).toBe("other");
333
- });
334
-
335
264
  test("atomically admits the command, Connection, and encrypted credential", async () => {
336
265
  const { storage, settings, ollama } = await fixture();
337
266
  storage.failNextEntriesContaining =
@@ -891,7 +820,7 @@ describe("Ollama Cloud User Contribution", () => {
891
820
  );
892
821
  });
893
822
 
894
- test("rejects disconnect while a Bot assignment depends on the Connection", async () => {
823
+ test("disconnects without a retired per-Bot dependency lock", async () => {
895
824
  const { settings, ollama } = await fixture();
896
825
  const created = await ollama.executeConnection("account-1", {
897
826
  schemaVersion: 1,
@@ -902,20 +831,6 @@ describe("Ollama Cloud User Contribution", () => {
902
831
  label: "Work",
903
832
  apiKey: "key",
904
833
  });
905
- await settings.claimConnectionDependency(
906
- "account-1",
907
- created.connectionId,
908
- "bot-1",
909
- "assignment-1",
910
- {
911
- schemaVersion: 1,
912
- packageId: "provider-ollama-cloud",
913
- packageVersion: "0.0.1",
914
- capabilityId: "ollama-cloud-models",
915
- connectionTypeIds: ["ollama-cloud-account"],
916
- },
917
- );
918
-
919
834
  await expect(
920
835
  ollama.executeConnection("account-1", {
921
836
  schemaVersion: 1,
@@ -924,10 +839,10 @@ describe("Ollama Cloud User Contribution", () => {
924
839
  connectionId: created.connectionId,
925
840
  revokeUpstream: false,
926
841
  }),
927
- ).resolves.toMatchObject({ status: "failed" });
842
+ ).resolves.toMatchObject({ status: "applied" });
928
843
  expect(
929
844
  await settings.getConnection("account-1", created.connectionId),
930
- ).toMatchObject({ state: "ready" });
845
+ ).toMatchObject({ state: "revoked" });
931
846
  });
932
847
 
933
848
  test("does not downgrade concurrent upstream revocation reconciliation", async () => {
package/src/user.ts CHANGED
@@ -44,23 +44,6 @@ const MAX_CATALOG_REFRESHES_PER_ALARM = 1;
44
44
  const ACCOUNT_KEY = "ollama-connection-account";
45
45
  const AUTOMATIC_REFRESH_RECEIPT_PREFIX = "ollama-refresh-receipt:";
46
46
 
47
- const AUTOMATIC_DEFAULT_MODEL_PREFERENCES = [
48
- "gpt-oss:20b",
49
- "glm-5.3-flash:cloud",
50
- ] as const;
51
-
52
- /** Choose the safest useful default from the catalog a new Connection proved. */
53
- export function selectAutomaticOllamaModelV1(
54
- catalog: ConnectionModelCatalogV1,
55
- ): ConnectionModelV1 | undefined {
56
- for (const providerModelId of AUTOMATIC_DEFAULT_MODEL_PREFERENCES) {
57
- const preferred = catalog.models.find(
58
- (model) => model.providerModelId === providerModelId,
59
- );
60
- if (preferred) return preferred;
61
- }
62
- return catalog.models[0];
63
- }
64
47
  const MUTATION_SEQUENCE_PREFIX = "ollama-mutation-sequence:";
65
48
  const MODEL_RESOLUTION_PREFIX = "ollama-model-resolution:";
66
49
  const REFRESH_INTERVAL_MS = 60 * 60 * 1_000;
@@ -1402,35 +1385,6 @@ export class OllamaCloudUserBackendContribution {
1402
1385
  },
1403
1386
  storage,
1404
1387
  );
1405
- if (
1406
- record.operation === "connection/create-api-key" &&
1407
- outcome.validationCatalog
1408
- ) {
1409
- const user = await this.host.settings.read(
1410
- record.accountId,
1411
- storage,
1412
- );
1413
- const selected = selectAutomaticOllamaModelV1(
1414
- outcome.validationCatalog,
1415
- );
1416
- if (selected && user.newBotModelTemplateSource !== "user") {
1417
- await this.host.settings.executeConfigurationCommand(
1418
- record.accountId,
1419
- {
1420
- schemaVersion: 1,
1421
- type: "user/set-new-bot-model",
1422
- commandId: record.commandId,
1423
- expectedRevision: user.revision,
1424
- model: {
1425
- connectionId: record.connectionId,
1426
- providerModelId: selected.providerModelId,
1427
- },
1428
- source: "auto",
1429
- },
1430
- storage,
1431
- );
1432
- }
1433
- }
1434
1388
  },
1435
1389
  );
1436
1390
  } catch (error) {
@@ -1858,12 +1812,6 @@ export class OllamaCloudUserBackendContribution {
1858
1812
  if (current.state === "reconciliation-required") {
1859
1813
  return "reconciliation-required";
1860
1814
  }
1861
- if (
1862
- Array.isArray(current.safeMetadata.dependentAssignments) &&
1863
- current.safeMetadata.dependentAssignments.length > 0
1864
- ) {
1865
- return "dependent";
1866
- }
1867
1815
  await this.cancelPendingCredentialMutations(record, storage);
1868
1816
  if (current.state !== "revoking") {
1869
1817
  await this.host.settings.replaceConnection(
@@ -1877,7 +1825,7 @@ export class OllamaCloudUserBackendContribution {
1877
1825
  return "revoking";
1878
1826
  },
1879
1827
  );
1880
- if (transition === "stale" || transition === "dependent") {
1828
+ if (transition === "stale") {
1881
1829
  return this.finishRecord(record, "failed");
1882
1830
  }
1883
1831
  if (transition === "revoked") return this.finishRecord(record, "applied");
@@ -13,11 +13,10 @@ const CONNECTION_ID = "connection-1";
13
13
  const GENERATION = "generation-1";
14
14
  const API_KEY = "ollama-test-key";
15
15
 
16
- const ASSIGNMENT = {
16
+ const CAPABILITY = {
17
17
  packageId: "provider-ollama-cloud",
18
18
  capabilityId: "ollama-cloud-web-search",
19
19
  connectionId: CONNECTION_ID,
20
- state: "enabled",
21
20
  } as const;
22
21
 
23
22
  interface Recorded {
@@ -86,7 +85,7 @@ async function mount(options: {
86
85
  await root.plugin(ToolRegistry);
87
86
  await root.plugin(FakeCredentialLease);
88
87
  const plugin = createConfiguredOllamaWebSearchRuntimeContribution({
89
- assignment: ASSIGNMENT,
88
+ capability: CAPABILITY,
90
89
  accountId: "user-1",
91
90
  connectionId: CONNECTION_ID,
92
91
  connectionGeneration: GENERATION,
@@ -336,7 +335,7 @@ describe("the Ollama Cloud web_search Capability", () => {
336
335
  await root.fiber.dispose();
337
336
  });
338
337
 
339
- test("mounts nothing without an enabled Assignment bound to the Connection", () => {
338
+ test("mounts only for the enabled Capability bound to the Connection", () => {
340
339
  const base = {
341
340
  accountId: "user-1",
342
341
  connectionId: CONNECTION_ID,
@@ -347,19 +346,13 @@ describe("the Ollama Cloud web_search Capability", () => {
347
346
  expect(
348
347
  createConfiguredOllamaWebSearchRuntimeContribution({
349
348
  ...base,
350
- assignment: { ...ASSIGNMENT, state: "disabled" },
349
+ capability: { ...CAPABILITY, capabilityId: "ollama-cloud-models" },
351
350
  }),
352
351
  ).toBeUndefined();
353
352
  expect(
354
353
  createConfiguredOllamaWebSearchRuntimeContribution({
355
354
  ...base,
356
- assignment: { ...ASSIGNMENT, capabilityId: "ollama-cloud-models" },
357
- }),
358
- ).toBeUndefined();
359
- expect(
360
- createConfiguredOllamaWebSearchRuntimeContribution({
361
- ...base,
362
- assignment: { ...ASSIGNMENT, connectionId: "other" },
355
+ capability: { ...CAPABILITY, connectionId: "other" },
363
356
  }),
364
357
  ).toBeUndefined();
365
358
  });
package/src/web-search.ts CHANGED
@@ -12,7 +12,7 @@
12
12
  // `@frockbot/plugin-web/contract`, and a second provider satisfies it with no
13
13
  // change here and none in the kernel.
14
14
  //
15
- // AUTHORITY. `web_search` needs an enabled Assignment of the
15
+ // AUTHORITY. `web_search` needs the enabled
16
16
  // `ollama-cloud-web-search` Capability bound to a ready `ollama-cloud-account`
17
17
  // Connection. Without one this module mounts nothing and the tool is simply
18
18
  // absent from the catalog, which is what "fail visibly" means for a tool: the
@@ -249,16 +249,15 @@ export function createOllamaWebSearchRuntimePlugin(
249
249
  }
250
250
 
251
251
  /**
252
- * The Assignment fence (D3): `web_search` exists for a Bot only through an
253
- * enabled Assignment of `ollama-cloud-web-search` bound to a Connection. There
252
+ * The enablement fence (D3): `web_search` exists for a Bot only through an
253
+ * enabled `ollama-cloud-web-search` Capability bound to a Connection. There
254
254
  * is no unauthenticated fallback provider.
255
255
  */
256
256
  export function createConfiguredOllamaWebSearchRuntimeContribution(config: {
257
- assignment: {
257
+ capability: {
258
258
  packageId: string;
259
259
  capabilityId: string;
260
260
  connectionId?: string;
261
- state: string;
262
261
  };
263
262
  accountId: string;
264
263
  connectionId: string;
@@ -274,10 +273,9 @@ export function createConfiguredOllamaWebSearchRuntimeContribution(config: {
274
273
  fetch?: OllamaFetch;
275
274
  }): Plugin.Function | undefined {
276
275
  if (
277
- config.assignment.packageId !== "provider-ollama-cloud" ||
278
- config.assignment.capabilityId !== "ollama-cloud-web-search" ||
279
- config.assignment.state !== "enabled" ||
280
- config.assignment.connectionId !== config.connectionId
276
+ config.capability.packageId !== "provider-ollama-cloud" ||
277
+ config.capability.capabilityId !== "ollama-cloud-web-search" ||
278
+ config.capability.connectionId !== config.connectionId
281
279
  ) {
282
280
  return undefined;
283
281
  }