@get-bb/plugin-sdk 0.4.24 → 0.4.28

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.
@@ -1,4 +1,5 @@
1
1
  // src/testing/fake-plugin-host.ts
2
+ import { createHash } from "node:crypto";
2
3
  import { mkdtempSync, rmSync } from "node:fs";
3
4
  import { tmpdir } from "node:os";
4
5
  import { join } from "node:path";
@@ -524,9 +525,7 @@ function normalizeProviderBridgeOptions(providerId, value, label = "experimental
524
525
  return current;
525
526
  }
526
527
  if (typeof current !== "object") {
527
- throw new Error(
528
- `provider "${providerId}" ${label}${path} must be JSON`
529
- );
528
+ throw new Error(`provider "${providerId}" ${label}${path} must be JSON`);
530
529
  }
531
530
  if (active.has(current)) {
532
531
  throw new Error(
@@ -562,9 +561,7 @@ function normalizeProviderBridgeOptions(providerId, value, label = "experimental
562
561
  }
563
562
  const normalized = visit(value, "");
564
563
  if (normalized === null || Array.isArray(normalized) || typeof normalized !== "object") {
565
- throw new Error(
566
- `provider "${providerId}" ${label} must be an object`
567
- );
564
+ throw new Error(`provider "${providerId}" ${label} must be an object`);
568
565
  }
569
566
  if (Buffer.byteLength(JSON.stringify(normalized), "utf8") > PLUGIN_PROVIDER_BRIDGE_OPTIONS_MAX_BYTES) {
570
567
  throw new Error(
@@ -587,9 +584,7 @@ function requireNonBlankString(args) {
587
584
  }
588
585
  function validateProviderStrings(providerId, value) {
589
586
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
590
- throw new Error(
591
- `provider "${providerId}" strings must be an object`
592
- );
587
+ throw new Error(`provider "${providerId}" strings must be an object`);
593
588
  }
594
589
  const record = Object.fromEntries(
595
590
  Object.entries(value)
@@ -791,7 +786,9 @@ function validateProviderNativeRoots(providerId, field, value) {
791
786
  }
792
787
  return Object.freeze({
793
788
  user: Object.freeze(wire.data.user.map((root) => Object.freeze(root))),
794
- project: Object.freeze(wire.data.project.map((root) => Object.freeze(root)))
789
+ project: Object.freeze(
790
+ wire.data.project.map((root) => Object.freeze(root))
791
+ )
795
792
  });
796
793
  }
797
794
  var PROVIDER_MODEL_CATALOG_SCOPES = [
@@ -811,9 +808,7 @@ function validateProviderModelCatalogScope(providerId, value) {
811
808
  }
812
809
  function validateProviderFallbackModels(providerId, value) {
813
810
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
814
- throw new Error(
815
- `provider "${providerId}" models must be an object`
816
- );
811
+ throw new Error(`provider "${providerId}" models must be an object`);
817
812
  }
818
813
  const fallback = Reflect.get(value, "fallback");
819
814
  if (fallback === void 0) {
@@ -831,95 +826,95 @@ function validateProviderFallbackModels(providerId, value) {
831
826
  }
832
827
  const seen = /* @__PURE__ */ new Set();
833
828
  let defaults = 0;
834
- const normalized = fallback.map((entry, index) => {
835
- const field = `models.fallback[${index}]`;
836
- if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
837
- throw new Error(`provider "${providerId}" ${field} must be an object`);
838
- }
839
- const record = Object.fromEntries(
840
- Object.entries(entry)
841
- );
842
- const id = requireNonBlankString({
843
- providerId,
844
- field: `${field}.id`,
845
- value: record.id
846
- });
847
- if (seen.has(id)) {
848
- throw new Error(
849
- `provider "${providerId}" models.fallback id ${JSON.stringify(id)} is duplicated`
850
- );
851
- }
852
- seen.add(id);
853
- const displayName = requireNonBlankString({
854
- providerId,
855
- field: `${field}.displayName`,
856
- value: record.displayName
857
- });
858
- const description = requireNonBlankString({
859
- providerId,
860
- field: `${field}.description`,
861
- value: record.description
862
- });
863
- const efforts = record.supportedReasoningEfforts;
864
- if (!Array.isArray(efforts) || efforts.length === 0) {
865
- throw new Error(
866
- `provider "${providerId}" ${field}.supportedReasoningEfforts must be a non-empty array`
867
- );
868
- }
869
- const levels = /* @__PURE__ */ new Set();
870
- const supportedReasoningEfforts = efforts.map(
871
- (effort, effortIndex) => {
872
- if (typeof effort !== "object" || effort === null || Array.isArray(effort)) {
873
- throw new Error(
874
- `provider "${providerId}" ${field}.supportedReasoningEfforts[${effortIndex}] must be { reasoningEffort, description }`
875
- );
876
- }
877
- const reasoningEffort = Reflect.get(effort, "reasoningEffort");
878
- if (typeof reasoningEffort !== "string" || !PLUGIN_PROVIDER_REASONING_LEVEL_VALUES.includes(
879
- reasoningEffort
880
- )) {
881
- throw new Error(
882
- `provider "${providerId}" ${field}.supportedReasoningEfforts[${effortIndex}].reasoningEffort must be one of ${PLUGIN_PROVIDER_REASONING_LEVEL_VALUES.join(", ")}`
883
- );
884
- }
885
- const level = reasoningEffort;
886
- if (levels.has(level)) {
887
- throw new Error(
888
- `provider "${providerId}" ${field}.supportedReasoningEfforts repeats ${JSON.stringify(level)}`
889
- );
890
- }
891
- levels.add(level);
892
- return Object.freeze({
893
- reasoningEffort: level,
894
- description: requireNonBlankString({
895
- providerId,
896
- field: `${field}.supportedReasoningEfforts[${effortIndex}].description`,
897
- value: Reflect.get(effort, "description")
898
- })
899
- });
829
+ const normalized = fallback.map(
830
+ (entry, index) => {
831
+ const field = `models.fallback[${index}]`;
832
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
833
+ throw new Error(`provider "${providerId}" ${field} must be an object`);
900
834
  }
901
- );
902
- const defaultReasoningEffort = record.defaultReasoningEffort;
903
- if (typeof defaultReasoningEffort !== "string" || !levels.has(defaultReasoningEffort)) {
904
- throw new Error(
905
- `provider "${providerId}" ${field}.defaultReasoningEffort must be one of its supportedReasoningEfforts`
835
+ const record = Object.fromEntries(
836
+ Object.entries(entry)
906
837
  );
907
- }
908
- if (typeof record.isDefault !== "boolean") {
909
- throw new Error(
910
- `provider "${providerId}" ${field}.isDefault must be a boolean`
838
+ const id = requireNonBlankString({
839
+ providerId,
840
+ field: `${field}.id`,
841
+ value: record.id
842
+ });
843
+ if (seen.has(id)) {
844
+ throw new Error(
845
+ `provider "${providerId}" models.fallback id ${JSON.stringify(id)} is duplicated`
846
+ );
847
+ }
848
+ seen.add(id);
849
+ const displayName = requireNonBlankString({
850
+ providerId,
851
+ field: `${field}.displayName`,
852
+ value: record.displayName
853
+ });
854
+ const description = requireNonBlankString({
855
+ providerId,
856
+ field: `${field}.description`,
857
+ value: record.description
858
+ });
859
+ const efforts = record.supportedReasoningEfforts;
860
+ if (!Array.isArray(efforts) || efforts.length === 0) {
861
+ throw new Error(
862
+ `provider "${providerId}" ${field}.supportedReasoningEfforts must be a non-empty array`
863
+ );
864
+ }
865
+ const levels = /* @__PURE__ */ new Set();
866
+ const supportedReasoningEfforts = efforts.map(
867
+ (effort, effortIndex) => {
868
+ if (typeof effort !== "object" || effort === null || Array.isArray(effort)) {
869
+ throw new Error(
870
+ `provider "${providerId}" ${field}.supportedReasoningEfforts[${effortIndex}] must be { reasoningEffort, description }`
871
+ );
872
+ }
873
+ const reasoningEffort = Reflect.get(effort, "reasoningEffort");
874
+ if (typeof reasoningEffort !== "string" || !PLUGIN_PROVIDER_REASONING_LEVEL_VALUES.includes(reasoningEffort)) {
875
+ throw new Error(
876
+ `provider "${providerId}" ${field}.supportedReasoningEfforts[${effortIndex}].reasoningEffort must be one of ${PLUGIN_PROVIDER_REASONING_LEVEL_VALUES.join(", ")}`
877
+ );
878
+ }
879
+ const level = reasoningEffort;
880
+ if (levels.has(level)) {
881
+ throw new Error(
882
+ `provider "${providerId}" ${field}.supportedReasoningEfforts repeats ${JSON.stringify(level)}`
883
+ );
884
+ }
885
+ levels.add(level);
886
+ return Object.freeze({
887
+ reasoningEffort: level,
888
+ description: requireNonBlankString({
889
+ providerId,
890
+ field: `${field}.supportedReasoningEfforts[${effortIndex}].description`,
891
+ value: Reflect.get(effort, "description")
892
+ })
893
+ });
894
+ }
911
895
  );
896
+ const defaultReasoningEffort = record.defaultReasoningEffort;
897
+ if (typeof defaultReasoningEffort !== "string" || !levels.has(defaultReasoningEffort)) {
898
+ throw new Error(
899
+ `provider "${providerId}" ${field}.defaultReasoningEffort must be one of its supportedReasoningEfforts`
900
+ );
901
+ }
902
+ if (typeof record.isDefault !== "boolean") {
903
+ throw new Error(
904
+ `provider "${providerId}" ${field}.isDefault must be a boolean`
905
+ );
906
+ }
907
+ if (record.isDefault) defaults += 1;
908
+ return Object.freeze({
909
+ id,
910
+ displayName,
911
+ description,
912
+ supportedReasoningEfforts: Object.freeze(supportedReasoningEfforts),
913
+ defaultReasoningEffort,
914
+ isDefault: record.isDefault
915
+ });
912
916
  }
913
- if (record.isDefault) defaults += 1;
914
- return Object.freeze({
915
- id,
916
- displayName,
917
- description,
918
- supportedReasoningEfforts: Object.freeze(supportedReasoningEfforts),
919
- defaultReasoningEffort,
920
- isDefault: record.isDefault
921
- });
922
- });
917
+ );
923
918
  if (normalized.length > 0 && defaults !== 1) {
924
919
  throw new Error(
925
920
  `provider "${providerId}" models.fallback must mark exactly one model isDefault (found ${defaults})`
@@ -981,9 +976,7 @@ function validatePluginAiServiceDeclaration(declaration) {
981
976
  }
982
977
  const displayName = typeof declaration.displayName === "string" ? declaration.displayName.trim() : "";
983
978
  if (displayName.length === 0 || displayName.length > 64) {
984
- throw new Error(
985
- `AI service "${id}" displayName must be 1-64 characters`
986
- );
979
+ throw new Error(`AI service "${id}" displayName must be 1-64 characters`);
987
980
  }
988
981
  const kinds = declaration.kinds;
989
982
  if (!Array.isArray(kinds) || kinds.length === 0) {
@@ -1219,10 +1212,7 @@ function validatePluginProviderDeclaration(declaration) {
1219
1212
  field: "reasoningLevels",
1220
1213
  value: declaration.reasoningLevels
1221
1214
  });
1222
- const extensionKinds = declaration.extensionKinds === void 0 ? void 0 : validateProviderExtensionKinds(
1223
- id,
1224
- declaration.extensionKinds
1225
- );
1215
+ const extensionKinds = declaration.extensionKinds === void 0 ? void 0 : validateProviderExtensionKinds(id, declaration.extensionKinds);
1226
1216
  const fallbackModels = declaration.models === void 0 ? void 0 : validateProviderFallbackModels(id, declaration.models);
1227
1217
  const modelCatalogScope = validateProviderModelCatalogScope(
1228
1218
  id,
@@ -1473,9 +1463,7 @@ function parsePluginAgentToolPresentation(toolName, value) {
1473
1463
  return null;
1474
1464
  }
1475
1465
  if (typeof value !== "object" || value === null || Array.isArray(value)) {
1476
- throw new Error(
1477
- `tool "${toolName}" presentation must be an object`
1478
- );
1466
+ throw new Error(`tool "${toolName}" presentation must be an object`);
1479
1467
  }
1480
1468
  const declared = value;
1481
1469
  const presentation = {};
@@ -1695,6 +1683,10 @@ function createFakeSdk(options) {
1695
1683
  }
1696
1684
 
1697
1685
  // src/testing/fake-plugin-host.ts
1686
+ var LEGACY_UNKNOWN_MIGRATION_HASH = "legacy-unknown";
1687
+ function migrationStatementHash(statement) {
1688
+ return createHash("sha256").update(statement).digest("hex");
1689
+ }
1698
1690
  var PluginContextStaleError = class extends Error {
1699
1691
  constructor(pluginId) {
1700
1692
  super(
@@ -2008,7 +2000,9 @@ function createFakePluginHostInternal(options, sharedState) {
2008
2000
  )
2009
2001
  };
2010
2002
  const pluginId = options.pluginId ?? "test-plugin";
2011
- const declaredIconNames = new Set(options.experimental_declaredIconNames ?? []);
2003
+ const declaredIconNames = new Set(
2004
+ options.experimental_declaredIconNames ?? []
2005
+ );
2012
2006
  const agentSkillIds = [...options.agentSkillIds ?? []];
2013
2007
  if (new Set(agentSkillIds).size !== agentSkillIds.length) {
2014
2008
  throw new Error("agentSkillIds must not contain duplicates");
@@ -2074,19 +2068,44 @@ function createFakePluginHostInternal(options, sharedState) {
2074
2068
  migrate(database, statements) {
2075
2069
  assertLive();
2076
2070
  database.exec(
2077
- "CREATE TABLE IF NOT EXISTS _bb_migrations (id INTEGER PRIMARY KEY, applied_at INTEGER NOT NULL)"
2071
+ "CREATE TABLE IF NOT EXISTS _bb_migrations (id INTEGER PRIMARY KEY, applied_at INTEGER NOT NULL, statement_hash TEXT)"
2078
2072
  );
2079
- const applied = new Set(
2080
- database.prepare("SELECT id FROM _bb_migrations").all().map((row) => row.id)
2073
+ const migrationColumns = database.prepare("PRAGMA table_info(_bb_migrations)").all();
2074
+ if (!migrationColumns.some((column) => column.name === "statement_hash")) {
2075
+ database.exec(
2076
+ "ALTER TABLE _bb_migrations ADD COLUMN statement_hash TEXT"
2077
+ );
2078
+ }
2079
+ const rows = database.prepare("SELECT id, statement_hash FROM _bb_migrations ORDER BY id").all();
2080
+ const applied = /* @__PURE__ */ new Map();
2081
+ for (const row of rows) applied.set(row.id, row.statement_hash);
2082
+ const statementHashes = statements.map(migrationStatementHash);
2083
+ statementHashes.forEach((statementHash, index) => {
2084
+ const recordedHash = applied.get(index);
2085
+ if (recordedHash !== void 0 && recordedHash !== null && recordedHash !== statementHash) {
2086
+ throw new Error(
2087
+ `migration ${index} does not match the recorded statement; append a new migration instead of changing or reusing an index`
2088
+ );
2089
+ }
2090
+ });
2091
+ const adopt = database.prepare(
2092
+ "UPDATE _bb_migrations SET statement_hash = ? WHERE id = ? AND statement_hash IS NULL"
2081
2093
  );
2082
2094
  const record = database.prepare(
2083
- "INSERT INTO _bb_migrations (id, applied_at) VALUES (?, ?)"
2095
+ "INSERT INTO _bb_migrations (id, applied_at, statement_hash) VALUES (?, ?, ?)"
2084
2096
  );
2085
2097
  database.transaction(() => {
2098
+ for (const row of rows) {
2099
+ if (row.statement_hash !== null) continue;
2100
+ adopt.run(
2101
+ statementHashes[row.id] ?? LEGACY_UNKNOWN_MIGRATION_HASH,
2102
+ row.id
2103
+ );
2104
+ }
2086
2105
  statements.forEach((statement, index) => {
2087
2106
  if (applied.has(index)) return;
2088
2107
  database.exec(statement);
2089
- record.run(index, Date.now());
2108
+ record.run(index, Date.now(), statementHashes[index]);
2090
2109
  });
2091
2110
  })();
2092
2111
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@get-bb/plugin-sdk",
3
- "version": "0.4.24",
3
+ "version": "0.4.28",
4
4
  "homepage": "https://github.com/get-bb/bb#readme",
5
5
  "bugs": {
6
6
  "url": "https://github.com/get-bb/bb/issues"