open-memex
Version:
Local-first memory layer and protocol for AI coding agents. Markdown source of truth, SQLite FTS5 index, zero cloud.
77 lines (76 loc) • 3.25 kB
JavaScript
import { PERSONAL_SCOPE } from "../scope.js";
import { syncScope } from "../store/sync.js";
import { addMemory, searchMemories, listMemories, supersedeMemory, forgetMemory, memoryAddArgs, memorySearchArgs, memoryListArgs, memorySupersedeArgs, memoryForgetArgs, TOOL_DESCRIPTIONS, withOutboxNote, } from "./ops.js";
// Local stand-in for the SDK's tool() helper — same signature, and the SDK
// function is a runtime identity (`return input`). Do NOT import it from
// @opencode-ai/plugin at runtime: that package is a devDependency, and when
// opencode loads this plugin by file:// URL from a global npm install
// nothing resolves the module — the import fails and opencode silently
// skips the whole plugin: no error, no tools (D58).
function tool(input) {
return input;
}
export function makeTools(getScope, cfg) {
// D64: the plugin host syncs only at bootstrap, so without this the
// index goes stale for the whole session — memories added through the
// CLI or another client stay invisible to the agent, and same-content
// duplicates accumulate. Mirror the MCP server: reconcile from disk
// before every tool call. syncScope is mtime-based, so the steady-state
// cost is one directory scan.
const syncFirst = () => {
try {
syncScope(getScope().key, "request");
syncScope(PERSONAL_SCOPE.key, "request");
}
catch (err) {
// Deliberate asymmetry with the MCP adapter (which lets sync errors
// propagate as tool errors): a failed sync must not take the plugin
// tools down — the index may be stale for this call but the tool
// still answers. Loud at debug, silent otherwise.
if (cfg.logLevel === "debug") {
console.error("[open-memex] pre-tool sync failed:", err);
}
}
};
const memory_add = tool({
description: TOOL_DESCRIPTIONS.memory_add,
args: memoryAddArgs,
async execute(args) {
syncFirst();
return withOutboxNote(getScope().key, addMemory(getScope, cfg, args));
},
});
const memory_search = tool({
description: TOOL_DESCRIPTIONS.memory_search,
args: memorySearchArgs,
async execute(args) {
syncFirst();
return searchMemories(getScope, args);
},
});
const memory_list = tool({
description: TOOL_DESCRIPTIONS.memory_list,
args: memoryListArgs,
async execute(args) {
syncFirst();
return listMemories(getScope, args);
},
});
const memory_supersede = tool({
description: TOOL_DESCRIPTIONS.memory_supersede,
args: memorySupersedeArgs,
async execute(args) {
syncFirst();
return withOutboxNote(getScope().key, supersedeMemory(cfg, args));
},
});
const memory_forget = tool({
description: TOOL_DESCRIPTIONS.memory_forget,
args: memoryForgetArgs,
async execute(args) {
syncFirst();
return withOutboxNote(getScope().key, forgetMemory(args));
},
});
return { memory_add, memory_search, memory_list, memory_forget, memory_supersede };
}