mirror of
https://github.com/moltbot/moltbot.git
synced 2026-04-21 13:44:03 +00:00
feat(memory-wiki): extend gateway wiki controls
This commit is contained in:
@@ -32,8 +32,13 @@ describe("memory-wiki plugin", () => {
|
||||
"wiki.compile",
|
||||
"wiki.lint",
|
||||
"wiki.search",
|
||||
"wiki.apply",
|
||||
"wiki.get",
|
||||
"wiki.obsidian.status",
|
||||
"wiki.obsidian.search",
|
||||
"wiki.obsidian.open",
|
||||
"wiki.obsidian.command",
|
||||
"wiki.obsidian.daily",
|
||||
]);
|
||||
expect(registerTool).toHaveBeenCalledTimes(5);
|
||||
expect(registerTool.mock.calls.map((call) => call[1]?.name)).toEqual([
|
||||
|
||||
@@ -57,6 +57,53 @@ export type ApplyMemoryWikiMutationResult = {
|
||||
compile: CompileMemoryWikiResult;
|
||||
};
|
||||
|
||||
export function normalizeMemoryWikiMutationInput(rawParams: unknown): ApplyMemoryWikiMutation {
|
||||
const params = rawParams as {
|
||||
op: ApplyMemoryWikiMutation["op"];
|
||||
title?: string;
|
||||
body?: string;
|
||||
lookup?: string;
|
||||
sourceIds?: string[];
|
||||
contradictions?: string[];
|
||||
questions?: string[];
|
||||
confidence?: number | null;
|
||||
status?: string;
|
||||
};
|
||||
if (params.op === "create_synthesis") {
|
||||
if (!params.title?.trim()) {
|
||||
throw new Error("wiki mutation requires title for create_synthesis.");
|
||||
}
|
||||
if (!params.body?.trim()) {
|
||||
throw new Error("wiki mutation requires body for create_synthesis.");
|
||||
}
|
||||
if (!params.sourceIds || params.sourceIds.length === 0) {
|
||||
throw new Error("wiki mutation requires at least one sourceId for create_synthesis.");
|
||||
}
|
||||
return {
|
||||
op: "create_synthesis",
|
||||
title: params.title,
|
||||
body: params.body,
|
||||
sourceIds: params.sourceIds,
|
||||
...(params.contradictions ? { contradictions: params.contradictions } : {}),
|
||||
...(params.questions ? { questions: params.questions } : {}),
|
||||
...(typeof params.confidence === "number" ? { confidence: params.confidence } : {}),
|
||||
...(params.status ? { status: params.status } : {}),
|
||||
};
|
||||
}
|
||||
if (!params.lookup?.trim()) {
|
||||
throw new Error("wiki mutation requires lookup for update_metadata.");
|
||||
}
|
||||
return {
|
||||
op: "update_metadata",
|
||||
lookup: params.lookup,
|
||||
...(params.sourceIds ? { sourceIds: params.sourceIds } : {}),
|
||||
...(params.contradictions ? { contradictions: params.contradictions } : {}),
|
||||
...(params.questions ? { questions: params.questions } : {}),
|
||||
...(params.confidence !== undefined ? { confidence: params.confidence } : {}),
|
||||
...(params.status ? { status: params.status } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeUniqueStrings(values: string[] | undefined): string[] | undefined {
|
||||
if (!values) {
|
||||
return undefined;
|
||||
|
||||
@@ -108,4 +108,39 @@ describe("memory-wiki gateway methods", () => {
|
||||
expect.objectContaining({ message: "query is required." }),
|
||||
);
|
||||
});
|
||||
|
||||
it("applies wiki mutations over the gateway", async () => {
|
||||
const rootDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-wiki-gateway-"));
|
||||
tempDirs.push(rootDir);
|
||||
const { api, registerGatewayMethod } = createGatewayApi();
|
||||
const config = resolveMemoryWikiConfig(
|
||||
{ vault: { path: rootDir } },
|
||||
{ homedir: "/Users/tester" },
|
||||
);
|
||||
|
||||
registerMemoryWikiGatewayMethods({ api, config });
|
||||
const handler = findGatewayHandler(registerGatewayMethod, "wiki.apply");
|
||||
if (!handler) {
|
||||
throw new Error("wiki.apply handler missing");
|
||||
}
|
||||
const respond = vi.fn();
|
||||
|
||||
await handler({
|
||||
params: {
|
||||
op: "create_synthesis",
|
||||
title: "Gateway Alpha",
|
||||
body: "Gateway summary.",
|
||||
sourceIds: ["source.alpha"],
|
||||
},
|
||||
respond,
|
||||
});
|
||||
|
||||
expect(respond).toHaveBeenCalledWith(
|
||||
true,
|
||||
expect.objectContaining({
|
||||
operation: "create_synthesis",
|
||||
pagePath: "syntheses/gateway-alpha.md",
|
||||
}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,8 +1,15 @@
|
||||
import type { OpenClawConfig, OpenClawPluginApi } from "../api.js";
|
||||
import { applyMemoryWikiMutation, normalizeMemoryWikiMutationInput } from "./apply.js";
|
||||
import { compileMemoryWikiVault } from "./compile.js";
|
||||
import type { ResolvedMemoryWikiConfig } from "./config.js";
|
||||
import { lintMemoryWikiVault } from "./lint.js";
|
||||
import { probeObsidianCli } from "./obsidian.js";
|
||||
import {
|
||||
probeObsidianCli,
|
||||
runObsidianCommand,
|
||||
runObsidianDaily,
|
||||
runObsidianOpen,
|
||||
runObsidianSearch,
|
||||
} from "./obsidian.js";
|
||||
import { getMemoryWikiPage, searchMemoryWiki } from "./query.js";
|
||||
import { syncMemoryWikiImportedSources } from "./source-sync.js";
|
||||
import { buildMemoryWikiDoctorReport, resolveMemoryWikiStatus } from "./status.js";
|
||||
@@ -140,6 +147,25 @@ export function registerMemoryWikiGatewayMethods(params: {
|
||||
{ scope: READ_SCOPE },
|
||||
);
|
||||
|
||||
api.registerGatewayMethod(
|
||||
"wiki.apply",
|
||||
async ({ params: requestParams, respond }) => {
|
||||
try {
|
||||
await syncImportedSourcesIfNeeded(config, appConfig);
|
||||
respond(
|
||||
true,
|
||||
await applyMemoryWikiMutation({
|
||||
config,
|
||||
mutation: normalizeMemoryWikiMutationInput(requestParams),
|
||||
}),
|
||||
);
|
||||
} catch (error) {
|
||||
respondError(respond, error);
|
||||
}
|
||||
},
|
||||
{ scope: WRITE_SCOPE },
|
||||
);
|
||||
|
||||
api.registerGatewayMethod(
|
||||
"wiki.get",
|
||||
async ({ params: requestParams, respond }) => {
|
||||
@@ -175,4 +201,55 @@ export function registerMemoryWikiGatewayMethods(params: {
|
||||
},
|
||||
{ scope: READ_SCOPE },
|
||||
);
|
||||
|
||||
api.registerGatewayMethod(
|
||||
"wiki.obsidian.search",
|
||||
async ({ params: requestParams, respond }) => {
|
||||
try {
|
||||
const query = readStringParam(requestParams, "query", { required: true });
|
||||
respond(true, await runObsidianSearch({ config, query }));
|
||||
} catch (error) {
|
||||
respondError(respond, error);
|
||||
}
|
||||
},
|
||||
{ scope: READ_SCOPE },
|
||||
);
|
||||
|
||||
api.registerGatewayMethod(
|
||||
"wiki.obsidian.open",
|
||||
async ({ params: requestParams, respond }) => {
|
||||
try {
|
||||
const vaultPath = readStringParam(requestParams, "path", { required: true });
|
||||
respond(true, await runObsidianOpen({ config, vaultPath }));
|
||||
} catch (error) {
|
||||
respondError(respond, error);
|
||||
}
|
||||
},
|
||||
{ scope: WRITE_SCOPE },
|
||||
);
|
||||
|
||||
api.registerGatewayMethod(
|
||||
"wiki.obsidian.command",
|
||||
async ({ params: requestParams, respond }) => {
|
||||
try {
|
||||
const id = readStringParam(requestParams, "id", { required: true });
|
||||
respond(true, await runObsidianCommand({ config, id }));
|
||||
} catch (error) {
|
||||
respondError(respond, error);
|
||||
}
|
||||
},
|
||||
{ scope: WRITE_SCOPE },
|
||||
);
|
||||
|
||||
api.registerGatewayMethod(
|
||||
"wiki.obsidian.daily",
|
||||
async ({ respond }) => {
|
||||
try {
|
||||
respond(true, await runObsidianDaily({ config }));
|
||||
} catch (error) {
|
||||
respondError(respond, error);
|
||||
}
|
||||
},
|
||||
{ scope: WRITE_SCOPE },
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Type } from "@sinclair/typebox";
|
||||
import type { AnyAgentTool, OpenClawConfig } from "../api.js";
|
||||
import { applyMemoryWikiMutation, type ApplyMemoryWikiMutation } from "./apply.js";
|
||||
import { applyMemoryWikiMutation, normalizeMemoryWikiMutationInput } from "./apply.js";
|
||||
import type { ResolvedMemoryWikiConfig } from "./config.js";
|
||||
import { lintMemoryWikiVault } from "./lint.js";
|
||||
import { getMemoryWikiPage, searchMemoryWiki } from "./query.js";
|
||||
@@ -46,53 +46,6 @@ async function syncImportedSourcesIfNeeded(
|
||||
await syncMemoryWikiImportedSources({ config, appConfig });
|
||||
}
|
||||
|
||||
function normalizeWikiApplyMutation(rawParams: unknown): ApplyMemoryWikiMutation {
|
||||
const params = rawParams as {
|
||||
op: ApplyMemoryWikiMutation["op"];
|
||||
title?: string;
|
||||
body?: string;
|
||||
lookup?: string;
|
||||
sourceIds?: string[];
|
||||
contradictions?: string[];
|
||||
questions?: string[];
|
||||
confidence?: number | null;
|
||||
status?: string;
|
||||
};
|
||||
if (params.op === "create_synthesis") {
|
||||
if (!params.title?.trim()) {
|
||||
throw new Error("wiki_apply requires title for create_synthesis.");
|
||||
}
|
||||
if (!params.body?.trim()) {
|
||||
throw new Error("wiki_apply requires body for create_synthesis.");
|
||||
}
|
||||
if (!params.sourceIds || params.sourceIds.length === 0) {
|
||||
throw new Error("wiki_apply requires at least one sourceId for create_synthesis.");
|
||||
}
|
||||
return {
|
||||
op: "create_synthesis",
|
||||
title: params.title,
|
||||
body: params.body,
|
||||
sourceIds: params.sourceIds,
|
||||
...(params.contradictions ? { contradictions: params.contradictions } : {}),
|
||||
...(params.questions ? { questions: params.questions } : {}),
|
||||
...(typeof params.confidence === "number" ? { confidence: params.confidence } : {}),
|
||||
...(params.status ? { status: params.status } : {}),
|
||||
};
|
||||
}
|
||||
if (!params.lookup?.trim()) {
|
||||
throw new Error("wiki_apply requires lookup for update_metadata.");
|
||||
}
|
||||
return {
|
||||
op: "update_metadata",
|
||||
lookup: params.lookup,
|
||||
...(params.sourceIds ? { sourceIds: params.sourceIds } : {}),
|
||||
...(params.contradictions ? { contradictions: params.contradictions } : {}),
|
||||
...(params.questions ? { questions: params.questions } : {}),
|
||||
...(params.confidence !== undefined ? { confidence: params.confidence } : {}),
|
||||
...(params.status ? { status: params.status } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
export function createWikiStatusTool(
|
||||
config: ResolvedMemoryWikiConfig,
|
||||
appConfig?: OpenClawConfig,
|
||||
@@ -195,7 +148,7 @@ export function createWikiApplyTool(
|
||||
"Apply narrow wiki mutations for syntheses and page metadata without freeform markdown surgery.",
|
||||
parameters: WikiApplySchema,
|
||||
execute: async (_toolCallId, rawParams) => {
|
||||
const mutation = normalizeWikiApplyMutation(rawParams);
|
||||
const mutation = normalizeMemoryWikiMutationInput(rawParams);
|
||||
await syncImportedSourcesIfNeeded(config, appConfig);
|
||||
const result = await applyMemoryWikiMutation({ config, mutation });
|
||||
const action = result.changed ? "Updated" : "No changes for";
|
||||
|
||||
Reference in New Issue
Block a user