feat(authority): detect BME manifest capability

This commit is contained in:
opencode
2026-05-15 10:46:43 +00:00
parent 08fcfda6ac
commit 2174552e7c
6 changed files with 111 additions and 1 deletions

View File

@@ -1626,6 +1626,12 @@ function buildAuthorityPersistenceStatePatch(settings = getSettings()) {
authorityTriviumPrimaryReady: Boolean(capability.triviumPrimaryReady),
authorityJobsReady: Boolean(capability.jobsReady),
authorityBlobReady: Boolean(capability.blobReady),
authorityBmeProtocolVersion: Math.max(0, Number(capability.bmeProtocolVersion) || 0),
authorityBmeVectorManifestReady: Boolean(capability.bmeVectorManifestReady),
authorityBmeVectorApplyReady: Boolean(capability.bmeVectorApplyReady),
authorityBmeVectorApplyJobsReady: Boolean(capability.bmeVectorApplyJobsReady),
authorityBmeServerEmbeddingProbeReady: Boolean(capability.bmeServerEmbeddingProbeReady),
authorityBmeCandidateSearchReady: Boolean(capability.bmeCandidateSearchReady),
authorityBrowserCacheMode: String(browserState.mode || "minimal"),
authorityOfflineQueueBytes: Number(browserState.offlineQueueBytes || 0),
authorityOfflineQueueItems: Number(browserState.offlineQueueItems || 0),

View File

@@ -6,6 +6,11 @@ const SQL_MUTATION_FEATURES = ["sql", "sql.mutation", "sql.execute", "sql.exec",
const TRIVIUM_FEATURES = ["trivium", "trivium.search", "trivium.query", "trivium.filterwhere", "trivium.bulkupsert", "trivium.upsert", "trivium.bulkmutations"];
const JOB_FEATURES = ["jobs", "jobs.background", "jobs.list", "jobs.wait", "diagnostics.jobspage", "events", "sse"];
const BLOB_FEATURES = ["blob", "blob.write", "storage.blob", "transfers.blob", "transfers.fs", "fs.private", "privatefiles", "private.files", "files.private"];
const BME_VECTOR_MANIFEST_FEATURES = ["bme.vectormanifest", "bme.vector.manifest", "bme.vector-manifest"];
const BME_VECTOR_APPLY_FEATURES = ["bme.vectorapply", "bme.vector.apply"];
const BME_VECTOR_APPLY_JOB_FEATURES = ["bme.vectorapplyjobs", "bme.vector.applyjobs", "bme.vector.apply.jobs"];
const BME_SERVER_EMBEDDING_FEATURES = ["bme.serverembeddingprobe", "bme.server.embedding.probe"];
const BME_CANDIDATE_SEARCH_FEATURES = ["bme.candidatesearch", "bme.candidate.search"];
function toBoolean(value, fallback = false) {
if (typeof value === "boolean") return value;
@@ -65,9 +70,20 @@ function createFeatureReadiness(features) {
trivium: hasAnyFeature(features, TRIVIUM_FEATURES),
jobs: hasAnyFeature(features, JOB_FEATURES),
blob: hasAnyFeature(features, BLOB_FEATURES),
bmeVectorManifest: hasAnyFeature(features, BME_VECTOR_MANIFEST_FEATURES),
bmeVectorApply: hasAnyFeature(features, BME_VECTOR_APPLY_FEATURES),
bmeVectorApplyJobs: hasAnyFeature(features, BME_VECTOR_APPLY_JOB_FEATURES),
bmeServerEmbeddingProbe: hasAnyFeature(features, BME_SERVER_EMBEDDING_FEATURES),
bmeCandidateSearch: hasAnyFeature(features, BME_CANDIDATE_SEARCH_FEATURES),
};
}
function normalizeBmeProtocolVersion(features, source = {}) {
const direct = Number(source?.bme?.protocolVersion ?? source?.features?.bme?.protocolVersion ?? 0);
if (Number.isFinite(direct) && direct > 0) return Math.trunc(direct);
return features.has("bme.protocolversion") ? 1 : 0;
}
function collectMissingFeatures(readiness) {
const missing = [];
if (!readiness.sql) missing.push("sql.query");
@@ -430,6 +446,12 @@ export function createDefaultAuthorityCapabilityState(overrides = {}) {
supportedJobTypes: [],
supportedJobTypesKnown: false,
blobReady: false,
bmeVectorManifestReady: false,
bmeVectorApplyReady: false,
bmeVectorApplyJobsReady: false,
bmeServerEmbeddingProbeReady: false,
bmeCandidateSearchReady: false,
bmeProtocolVersion: 0,
features: [],
missingFeatures: ["sql.query", "sql.mutation", "trivium.search", "jobs", "blob-or-private-files"],
reason: "not-probed",
@@ -461,6 +483,12 @@ export function normalizeAuthorityCapabilityState(input = {}, settings = {}) {
const triviumPrimaryReady = healthy && sessionReady && permissionReady && readiness.trivium;
const jobsReady = healthy && readiness.jobs;
const blobReady = healthy && readiness.blob;
const bmeProtocolVersion = normalizeBmeProtocolVersion(features, source);
const bmeVectorManifestReady = healthy && sessionReady && permissionReady && readiness.bmeVectorManifest;
const bmeVectorApplyReady = healthy && sessionReady && permissionReady && readiness.bmeVectorApply;
const bmeVectorApplyJobsReady = healthy && sessionReady && permissionReady && readiness.bmeVectorApplyJobs;
const bmeServerEmbeddingProbeReady = healthy && sessionReady && permissionReady && readiness.bmeServerEmbeddingProbe;
const bmeCandidateSearchReady = healthy && sessionReady && permissionReady && readiness.bmeCandidateSearch;
const minimumFeatureSetReady = storagePrimaryReady && triviumPrimaryReady && jobsReady && blobReady;
const serverPrimaryRequested =
normalizedSettings.enabled &&
@@ -483,6 +511,12 @@ export function normalizeAuthorityCapabilityState(input = {}, settings = {}) {
supportedJobTypes: supportedJobs.supportedJobTypes,
supportedJobTypesKnown: supportedJobs.supportedJobTypesKnown,
blobReady,
bmeVectorManifestReady,
bmeVectorApplyReady,
bmeVectorApplyJobsReady,
bmeServerEmbeddingProbeReady,
bmeCandidateSearchReady,
bmeProtocolVersion,
features: Array.from(features).sort(),
missingFeatures,
reason: String(source.reason || (healthy ? "ok" : "not-ready")),

View File

@@ -25,6 +25,9 @@ export function createAuthorityUpgradeState(overrides = {}) {
storageReady: Boolean(overrides.storageReady),
vectorReady: Boolean(overrides.vectorReady),
jobsReady: Boolean(overrides.jobsReady),
bmeVectorManifestReady: Boolean(overrides.bmeVectorManifestReady),
bmeVectorApplyReady: Boolean(overrides.bmeVectorApplyReady),
bmeProtocolVersion: Math.max(0, Number(overrides.bmeProtocolVersion) || 0),
browserCacheMode: normalizeString(overrides.browserCacheMode, "minimal"),
updatedAt: normalizeString(overrides.updatedAt, new Date().toISOString()),
};
@@ -42,6 +45,9 @@ export function deriveAuthorityUpgradeState({
const vectorReady = Boolean(capability.triviumPrimaryReady);
const serverPrimaryReady = Boolean(capability.serverPrimaryReady || storageReady);
const jobsReady = Boolean(capability.jobsReady);
const bmeVectorManifestReady = Boolean(capability.bmeVectorManifestReady);
const bmeVectorApplyReady = Boolean(capability.bmeVectorApplyReady);
const bmeProtocolVersion = Math.max(0, Number(capability.bmeProtocolVersion) || 0);
const browserCacheMode = normalizeString(browserState.mode, "minimal");
const reason = normalizeString(capability.reason || capability.lastError, "standalone");
const updatedAt = new Date(Number.isFinite(Number(now)) ? Number(now) : Date.now()).toISOString();
@@ -93,6 +99,9 @@ export function deriveAuthorityUpgradeState({
storageReady,
vectorReady,
jobsReady,
bmeVectorManifestReady,
bmeVectorApplyReady,
bmeProtocolVersion,
browserCacheMode,
updatedAt,
});
@@ -103,7 +112,9 @@ export function deriveAuthorityUpgradeState({
mode: AUTHORITY_UPGRADE_MODES.ENHANCED,
text: "服务端增强已启用",
meta: jobsReady
? "图谱与向量存储已自动升级到 DOA/Authority 增强路径"
? bmeVectorManifestReady
? "图谱与向量存储已增强,服务端向量清单可用"
: "图谱与向量存储已增强,等待 BME 向量清单能力"
: "图谱与向量存储已增强,服务端后台任务能力暂不可用",
level: "success",
ready: true,
@@ -112,6 +123,9 @@ export function deriveAuthorityUpgradeState({
storageReady,
vectorReady,
jobsReady,
bmeVectorManifestReady,
bmeVectorApplyReady,
bmeProtocolVersion,
browserCacheMode,
updatedAt,
});
@@ -130,6 +144,9 @@ export function deriveAuthorityUpgradeState({
storageReady,
vectorReady,
jobsReady,
bmeVectorManifestReady,
bmeVectorApplyReady,
bmeProtocolVersion,
browserCacheMode,
updatedAt,
});

View File

@@ -0,0 +1,43 @@
import assert from "node:assert/strict";
import {
normalizeAuthorityProbeResponse,
normalizeAuthorityCapabilityState,
} from "../runtime/authority-capabilities.js";
const capability = normalizeAuthorityProbeResponse({
healthy: true,
features: {
sql: { queryPage: true, stat: true },
trivium: { bulkMutations: true, searchContext: true },
jobs: { background: true, builtinTypes: ["delay", "trivium.flush"] },
transfers: { blob: true, fs: true },
bme: {
protocolVersion: 1,
vectorManifest: true,
vectorApply: false,
vectorApplyJobs: false,
serverEmbeddingProbe: false,
candidateSearch: false,
},
},
});
assert.equal(capability.bmeProtocolVersion, 1);
assert.equal(capability.bmeVectorManifestReady, true);
assert.equal(capability.bmeVectorApplyReady, false);
assert.equal(capability.bmeServerEmbeddingProbeReady, false);
assert.ok(capability.features.includes("bme.vectormanifest"));
assert.ok(capability.features.includes("bme.protocolversion"));
const legacy = normalizeAuthorityCapabilityState({
installed: true,
healthy: true,
sessionReady: true,
permissionReady: true,
features: ["sql.query", "sql.mutation", "trivium.search"],
});
assert.equal(legacy.bmeVectorManifestReady, false);
assert.equal(legacy.bmeProtocolVersion, 0);
console.log("authority-bme-capabilities tests passed");

View File

@@ -53,12 +53,16 @@ const enhanced = deriveAuthorityUpgradeState({
storagePrimaryReady: true,
triviumPrimaryReady: true,
jobsReady: true,
bmeVectorManifestReady: true,
bmeProtocolVersion: 1,
},
browserState: { mode: "off" },
});
assert.equal(enhanced.mode, "authority-enhanced");
assert.equal(enhanced.ready, true);
assert.equal(enhanced.text, "服务端增强已启用");
assert.equal(enhanced.bmeVectorManifestReady, true);
assert.equal(enhanced.bmeProtocolVersion, 1);
assert.equal(
formatAuthorityUpgradeMeta("准备就绪", enhanced),

View File

@@ -156,6 +156,12 @@ export function createGraphPersistenceState() {
authorityMigrationLastError: "",
lastAuthorityMigrationResult: null,
authorityJobsReady: false,
authorityBmeProtocolVersion: 0,
authorityBmeVectorManifestReady: false,
authorityBmeVectorApplyReady: false,
authorityBmeVectorApplyJobsReady: false,
authorityBmeServerEmbeddingProbeReady: false,
authorityBmeCandidateSearchReady: false,
authorityJobQueueState: "idle",
authorityLastJob: null,
authorityLastJobId: "",