@get-bb/plugin-sdk 0.4.24 → 0.4.27
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/bundled-types/bb-plugin-sdk-internal-host-policy.d.ts +0 -25
- package/bundled-types/bb-plugin-sdk-provider-bridge-acp.d.ts +0 -200
- package/bundled-types/bb-plugin-sdk-provider-bridge-testing.d.ts +0 -440
- package/bundled-types/bb-plugin-sdk-provider-bridge.d.ts +0 -665
- package/bundled-types/bb-plugin-sdk-testing.d.ts +0 -16
- package/bundled-types/bb-plugin-sdk.d.ts +25 -277
- package/dist/ai-services.js +5 -1
- package/dist/internal/host-policy.js +95 -108
- package/dist/provider-bridge-acp.js +72 -615
- package/dist/provider-bridge-testing.js +175 -579
- package/dist/provider-bridge-worker-entry.mjs +15 -9
- package/dist/provider-bridge.js +62 -557
- package/dist/testing/index.js +133 -114
- package/package.json +1 -1
package/dist/testing/index.js
CHANGED
|
@@ -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(
|
|
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(
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
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
|
-
|
|
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
|
-
|
|
909
|
-
|
|
910
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
|
2080
|
-
|
|
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
|
}
|