mirror of
https://github.com/Youzini-afk/ST-Bionic-Memory-Ecology.git
synced 2026-05-15 22:30:38 +08:00
fix(authority): gate vector rebuild jobs
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
@@ -634,4 +634,55 @@ assert.deepEqual(JSON.parse(String(httpRequests[3].options.body || "{}")), {
|
||||
},
|
||||
});
|
||||
|
||||
// Regression: onRebuildVectorIndexController skips submitAuthorityVectorRebuildJob when
|
||||
// shouldUseAuthorityJobs() returns false and still runs syncVectorState with purge: true
|
||||
// for Authority vector config.
|
||||
const noJobsRuntime = createVectorControllerRuntime({
|
||||
shouldUseAuthorityJobs() {
|
||||
this.calls.push(["shouldUseAuthorityJobs"]);
|
||||
return false;
|
||||
},
|
||||
});
|
||||
await onRebuildVectorIndexController(noJobsRuntime);
|
||||
assert.equal(
|
||||
noJobsRuntime.calls.some(([name]) => name === "submitAuthorityVectorRebuildJob"),
|
||||
false,
|
||||
"submitAuthorityVectorRebuildJob should NOT be called when shouldUseAuthorityJobs returns false",
|
||||
);
|
||||
const noJobsSync = noJobsRuntime.calls.find(([name]) => name === "syncVectorState");
|
||||
assert.equal(noJobsSync?.[1]?.purge, true, "syncVectorState should use purge:true for Authority config fallback");
|
||||
assert.equal(
|
||||
noJobsRuntime.calls.some(([name]) => name === "saveGraphToChat"),
|
||||
true,
|
||||
"saveGraphToChat should still be called in fallback path",
|
||||
);
|
||||
|
||||
// Regression: Fallback warning wording no longer says 本地重建; instead it uses
|
||||
// direct/synchronous rebuild wording.
|
||||
const fallbackWarningRuntime = createVectorControllerRuntime({
|
||||
async submitAuthorityVectorRebuildJob(payload) {
|
||||
this.calls.push(["submitAuthorityVectorRebuildJob", payload]);
|
||||
return { submitted: false, error: "job-type-unsupported" };
|
||||
},
|
||||
});
|
||||
await onRebuildVectorIndexController(fallbackWarningRuntime);
|
||||
const fallbackWarningMessage = fallbackWarningRuntime.calls.find(
|
||||
([name]) => name === "toastr.warning",
|
||||
)?.[1];
|
||||
assert.equal(
|
||||
typeof fallbackWarningMessage === "string",
|
||||
true,
|
||||
"a warning toast should be emitted when job submission fails",
|
||||
);
|
||||
assert.equal(
|
||||
fallbackWarningMessage.includes("本地重建"),
|
||||
false,
|
||||
"fallback warning should NOT contain 本地重建",
|
||||
);
|
||||
assert.equal(
|
||||
/直接|同步|direct|synchronous/i.test(fallbackWarningMessage),
|
||||
true,
|
||||
"fallback warning should contain direct/synchronous rebuild wording",
|
||||
);
|
||||
|
||||
console.log("authority-jobs tests passed");
|
||||
|
||||
@@ -107,6 +107,7 @@ import {
|
||||
normalizeAuthorityCapabilityState,
|
||||
probeAuthorityCapabilities,
|
||||
} from "../runtime/authority-capabilities.js";
|
||||
import { normalizeAuthorityBlobConfig } from "../maintenance/authority-blob-adapter.js";
|
||||
import {
|
||||
createAuthorityBrowserState,
|
||||
getAuthorityBrowserStateSnapshot,
|
||||
@@ -138,6 +139,27 @@ const moduleDir = path.dirname(fileURLToPath(import.meta.url));
|
||||
const indexPath = path.resolve(moduleDir, "../index.js");
|
||||
const indexSource = await fs.readFile(indexPath, "utf8");
|
||||
|
||||
function isAuthorityVectorConfig(config = null) {
|
||||
return config?.mode === "authority" || config?.source === "authority-trivium";
|
||||
}
|
||||
|
||||
function normalizeAuthorityVectorConfig(settings = {}, overrides = {}) {
|
||||
return {
|
||||
...overrides,
|
||||
mode: "authority",
|
||||
source: "authority-trivium",
|
||||
baseUrl: String(settings?.authorityBaseUrl || overrides?.baseUrl || "/api/plugins/authority"),
|
||||
};
|
||||
}
|
||||
|
||||
function createAuthorityBlobAdapter() {
|
||||
return {
|
||||
async writeJson(path = "", payload = null) {
|
||||
return { path, payload, written: true };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function extractSnippet(startMarker, endMarker) {
|
||||
const start = indexSource.indexOf(startMarker);
|
||||
const end = indexSource.indexOf(endMarker);
|
||||
@@ -629,6 +651,10 @@ async function createGraphPersistenceHarness({
|
||||
normalizeAuthoritySettings,
|
||||
normalizeAuthorityCapabilityState,
|
||||
probeAuthorityCapabilities,
|
||||
isAuthorityVectorConfig,
|
||||
normalizeAuthorityVectorConfig,
|
||||
normalizeAuthorityBlobConfig,
|
||||
createAuthorityBlobAdapter,
|
||||
createAuthorityBrowserState,
|
||||
getAuthorityBrowserStateSnapshot,
|
||||
normalizeAuthorityBrowserState,
|
||||
@@ -1581,6 +1607,7 @@ result = {
|
||||
maybeFlushQueuedGraphPersist,
|
||||
retryPendingGraphPersist,
|
||||
persistExtractionBatchResult,
|
||||
shouldUseAuthorityJobs,
|
||||
onRebuildLocalCacheFromLukerSidecar,
|
||||
saveGraphToIndexedDb,
|
||||
cloneGraphForPersistence,
|
||||
@@ -3993,6 +4020,12 @@ result = {
|
||||
sessionReady: true,
|
||||
permissionReady: true,
|
||||
features: ["sql.query", "sql.mutation", "trivium.search", "jobs", "blob"],
|
||||
jobs: {
|
||||
builtinTypes: ["delay", "sql.backup", "trivium.flush", "fs.import-jsonl"],
|
||||
registry: {
|
||||
jobTypes: ["delay", "sql.backup", "trivium.flush", "fs.import-jsonl"],
|
||||
},
|
||||
},
|
||||
reason: "ok",
|
||||
lastProbeAt: Date.now(),
|
||||
});
|
||||
@@ -4037,6 +4070,29 @@ result = {
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const harness = await createGraphPersistenceHarness({
|
||||
chatId: "chat-authority-empty-jobs",
|
||||
globalChatId: "chat-authority-empty-jobs",
|
||||
});
|
||||
harness.api.setAuthorityCapabilityState({
|
||||
installed: true,
|
||||
healthy: true,
|
||||
sessionReady: true,
|
||||
permissionReady: true,
|
||||
features: ["sql.query", "sql.mutation", "trivium.search", "jobs", "blob"],
|
||||
supportedJobTypes: [],
|
||||
supportedJobTypesKnown: true,
|
||||
reason: "ok",
|
||||
});
|
||||
|
||||
assert.equal(
|
||||
harness.api.shouldUseAuthorityJobs({ mode: "authority", source: "authority-trivium" }),
|
||||
false,
|
||||
"显式空 Authority job 白名单应阻止 vector rebuild job 提交",
|
||||
);
|
||||
}
|
||||
|
||||
{
|
||||
const harness = await createGraphPersistenceHarness({
|
||||
chatId: "chat-b",
|
||||
|
||||
Reference in New Issue
Block a user