From 9ae81fa38504a80620ed55c9f0fd699791c1c02d Mon Sep 17 00:00:00 2001 From: Edison-A-N Date: Sat, 21 Mar 2026 14:55:17 +0800 Subject: [PATCH 1/3] feat: add memory.disable_global config to skip global scope --- .npmrc | 1 + package.json | 9 ++++++--- src/journal.ts | 5 +++++ src/memory.ts | 36 ++++++++++++++++++++++++++++++++++-- src/plugin.ts | 12 +++++++----- src/tools.ts | 34 +++++++++++++++++++++++++--------- 6 files changed, 78 insertions(+), 19 deletions(-) create mode 100644 .npmrc diff --git a/.npmrc b/.npmrc new file mode 100644 index 0000000..418be29 --- /dev/null +++ b/.npmrc @@ -0,0 +1 @@ +@Edison-A-N:registry=https://npm.pkg.github.com diff --git a/package.json b/package.json index e33818e..65645a1 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,15 @@ { - "name": "opencode-agent-memory", - "version": "0.2.0", + "name": "@Edison-A-N/opencode-agent-memory", + "version": "0.2.1", "description": "Letta-style editable memory blocks for OpenCode.", "author": "Josh Thomas ", "license": "MIT", "repository": { "type": "git", - "url": "https://github.com/joshuadavidthomas/opencode-agent-memory" + "url": "https://github.com/Edison-A-N/opencode-agent-memory" + }, + "publishConfig": { + "registry": "https://npm.pkg.github.com" }, "main": "src/plugin.ts", "type": "module", diff --git a/src/journal.ts b/src/journal.ts index b46a3e6..8f09788 100644 --- a/src/journal.ts +++ b/src/journal.ts @@ -20,6 +20,11 @@ const ConfigSchema = z.looseObject({ tags: z.array(TagSchema).optional(), }) .optional(), + memory: z + .looseObject({ + disable_global: z.boolean().optional(), + }) + .optional(), }); export type AgentMemoryConfig = z.infer; diff --git a/src/memory.ts b/src/memory.ts index a47718a..5e5b2a8 100644 --- a/src/memory.ts +++ b/src/memory.ts @@ -173,12 +173,33 @@ function stableSortBlocks(blocks: MemoryBlock[]): MemoryBlock[] { return blocks; } -export function createMemoryStore(projectDirectory: string): MemoryStore { +export type MemoryStoreOptions = { + disableGlobal?: boolean; +}; + +export function createMemoryStore( + projectDirectory: string, + storeOpts?: MemoryStoreOptions, +): MemoryStore { + const disableGlobal = storeOpts?.disableGlobal === true; + + function assertGlobalAllowed(scope: MemoryScope): void { + if (disableGlobal && scope === "global") { + throw new Error( + "Global memory scope is disabled. Only project-scoped memory blocks are available.", + ); + } + } + return { async ensureSeed() { await ensureGitignore(projectDirectory); for (const seed of SEED_BLOCKS) { + if (disableGlobal && seed.scope === "global") { + continue; + } + const dir = scopeDir(projectDirectory, seed.scope); await fs.mkdir(dir, { recursive: true }); @@ -198,7 +219,15 @@ export function createMemoryStore(projectDirectory: string): MemoryStore { }, async listBlocks(scope) { - const scopes: MemoryScope[] = scope === "all" ? ["global", "project"] : [scope]; + let scopes: MemoryScope[]; + if (scope === "all") { + scopes = disableGlobal ? ["project"] : ["global", "project"]; + } else { + if (disableGlobal && scope === "global") { + return []; + } + scopes = [scope]; + } const blocks: MemoryBlock[] = []; for (const s of scopes) { @@ -225,6 +254,7 @@ export function createMemoryStore(projectDirectory: string): MemoryStore { }, async getBlock(scope, label) { + assertGlobalAllowed(scope); const safeLabel = validateLabel(label); const dir = scopeDir(projectDirectory, scope); const filePath = path.join(dir, `${safeLabel}.md`); @@ -237,6 +267,7 @@ export function createMemoryStore(projectDirectory: string): MemoryStore { }, async setBlock(scope, label, value, opts) { + assertGlobalAllowed(scope); const safeLabel = validateLabel(label); const dir = scopeDir(projectDirectory, scope); await fs.mkdir(dir, { recursive: true }); @@ -267,6 +298,7 @@ export function createMemoryStore(projectDirectory: string): MemoryStore { }, async replaceInBlock(scope, label, oldText, newText) { + assertGlobalAllowed(scope); const block = await this.getBlock(scope, label); if (block.readOnly) { throw new Error(`Memory block is read-only: ${scope}:${block.label}`); diff --git a/src/plugin.ts b/src/plugin.ts index ac4a747..ffedc1d 100644 --- a/src/plugin.ts +++ b/src/plugin.ts @@ -18,11 +18,13 @@ import { import type { JournalContext } from "./tools"; export const MemoryPlugin: Plugin = async ({ directory }) => { - const store = createMemoryStore(directory); + const config = await loadConfig(); + const disableGlobal = config.memory?.disable_global === true; + + const store = createMemoryStore(directory, { disableGlobal }); await store.ensureSeed(); // Journal: opt-in via ~/.config/opencode/agent-memory.json - const config = await loadConfig(); const journalEnabled = config.journal?.enabled === true; // Mutable state updated by chat.message hook @@ -70,9 +72,9 @@ export const MemoryPlugin: Plugin = async ({ directory }) => { }, tool: { - memory_list: MemoryList(store), - memory_set: MemorySet(store), - memory_replace: MemoryReplace(store), + memory_list: MemoryList(store, { disableGlobal }), + memory_set: MemorySet(store, { disableGlobal }), + memory_replace: MemoryReplace(store, { disableGlobal }), ...journalTools, }, }; diff --git a/src/tools.ts b/src/tools.ts index 80ba044..615abe1 100644 --- a/src/tools.ts +++ b/src/tools.ts @@ -3,14 +3,22 @@ import { tool } from "@opencode-ai/plugin"; import type { JournalStore } from "./journal"; import type { MemoryScope, MemoryStore } from "./memory"; -export function MemoryList(store: MemoryStore) { +export type MemoryToolOptions = { + disableGlobal?: boolean; +}; + +export function MemoryList(store: MemoryStore, opts?: MemoryToolOptions) { + const disableGlobal = opts?.disableGlobal === true; + const scopeValues = disableGlobal + ? (["all", "project"] as const) + : (["all", "global", "project"] as const); + return tool({ description: "List available memory blocks (labels, descriptions, sizes).", args: { - scope: tool.schema.enum(["all", "global", "project"]).optional(), + scope: tool.schema.enum(scopeValues).optional(), }, async execute(args) { - // Default to "all" for list (show everything) const scope = (args.scope ?? "all") as MemoryScope | "all"; const blocks = await store.listBlocks(scope); if (blocks.length === 0) { @@ -27,18 +35,22 @@ export function MemoryList(store: MemoryStore) { }); } -export function MemorySet(store: MemoryStore) { +export function MemorySet(store: MemoryStore, opts?: MemoryToolOptions) { + const disableGlobal = opts?.disableGlobal === true; + const scopeValues = disableGlobal + ? (["project"] as const) + : (["global", "project"] as const); + return tool({ description: "Create or update a memory block (full overwrite).", args: { label: tool.schema.string(), - scope: tool.schema.enum(["global", "project"]).optional(), + scope: tool.schema.enum(scopeValues).optional(), value: tool.schema.string(), description: tool.schema.string().optional(), limit: tool.schema.number().int().positive().optional(), }, async execute(args) { - // Default to "project" for mutations (safer default) const scope = (args.scope ?? "project") as MemoryScope; await store.setBlock(scope, args.label, args.value, { description: args.description, @@ -49,17 +61,21 @@ export function MemorySet(store: MemoryStore) { }); } -export function MemoryReplace(store: MemoryStore) { +export function MemoryReplace(store: MemoryStore, opts?: MemoryToolOptions) { + const disableGlobal = opts?.disableGlobal === true; + const scopeValues = disableGlobal + ? (["project"] as const) + : (["global", "project"] as const); + return tool({ description: "Replace a substring within a memory block.", args: { label: tool.schema.string(), - scope: tool.schema.enum(["global", "project"]).optional(), + scope: tool.schema.enum(scopeValues).optional(), oldText: tool.schema.string(), newText: tool.schema.string(), }, async execute(args) { - // Default to "project" for mutations (safer default) const scope = (args.scope ?? "project") as MemoryScope; await store.replaceInBlock(scope, args.label, args.oldText, args.newText); return `Updated memory block ${scope}:${args.label}.`; From 1bba422b64b8c718815fc4074f02dbd6afaab8cc Mon Sep 17 00:00:00 2001 From: Edison-A-N Date: Sat, 21 Mar 2026 15:12:33 +0800 Subject: [PATCH 2/3] bump version to 0.3.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 65645a1..b47d1e0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@Edison-A-N/opencode-agent-memory", - "version": "0.2.1", + "version": "0.3.0", "description": "Letta-style editable memory blocks for OpenCode.", "author": "Josh Thomas ", "license": "MIT", From 23e1f8c0e1741cc52df75436d2309cca70297b1a Mon Sep 17 00:00:00 2001 From: Edison-A-N Date: Sat, 21 Mar 2026 15:13:32 +0800 Subject: [PATCH 3/3] version 0.2.0-a1: based on upstream 0.2.0 + disable_global --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index b47d1e0..0545350 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@Edison-A-N/opencode-agent-memory", - "version": "0.3.0", + "version": "0.2.0-a1", "description": "Letta-style editable memory blocks for OpenCode.", "author": "Josh Thomas ", "license": "MIT",