| 19 | import type { PluginContext } from "./types"; |
| 20 | |
| 21 | export function createToolRegistry(args: { |
| 22 | ctx: PluginContext; |
| 23 | pluginConfig: MagicContextPluginConfig; |
| 24 | }): Record<string, ToolDefinition> { |
| 25 | const { ctx, pluginConfig } = args; |
| 26 | |
| 27 | if (pluginConfig.enabled !== true) { |
| 28 | return {}; |
| 29 | } |
| 30 | |
| 31 | // Storage failure (binary ABI mismatch, unwritable path, etc.) must |
| 32 | // disable Magic Context cleanly instead of silently degrading. We never |
| 33 | // expose ctx_* tools when storage isn't healthy — see openDatabase() |
| 34 | // for the reasoning. |
| 35 | let db: Database; |
| 36 | try { |
| 37 | const opened = openDatabase(); |
| 38 | // openDatabase returns null on the schema-fence path (DB newer than this |
| 39 | // binary) and throws on a fatal open error — handle both as "storage |
| 40 | // unavailable, disable tools cleanly". |
| 41 | if (!opened || !isDatabasePersisted(opened)) { |
| 42 | const reason = getDatabasePersistenceError(opened); |
| 43 | console.warn( |
| 44 | `[magic-context] persistent storage unavailable; disabling magic-context tools${reason ? `: ${reason}` : ""}`, |
| 45 | ); |
| 46 | return {}; |
| 47 | } |
| 48 | db = opened; |
| 49 | } catch (error) { |
| 50 | const reason = error instanceof Error ? error.message : String(error); |
| 51 | // console.warn intentional: this runs during plugin init before the file logger is |
| 52 | // guaranteed to be ready, and storage failure is user-visible enough to warrant stderr. |
| 53 | console.warn( |
| 54 | `[magic-context] persistent storage unavailable; disabling magic-context tools: ${reason}`, |
| 55 | ); |
| 56 | return {}; |
| 57 | } |
| 58 | |
| 59 | void ensureProjectRegisteredFromOpenCodeDirectory(ctx.directory, db); |
| 60 | |
| 61 | // Tools resolve project per-call from `toolContext.directory` because |
| 62 | // OpenCode's top-level `ctx.directory` reflects the launch dir, not the |
| 63 | // session's actual working directory (e.g. when launched via |
| 64 | // `opencode -s <id>` from outside the project). |
| 65 | const resolveProjectPath = (directory: string) => resolveProjectIdentity(directory); |
| 66 | |
| 67 | const ctxReduceEnabled = pluginConfig.ctx_reduce_enabled !== false; |
| 68 | // When memory is off the <project-memory> block is never injected, so an |
| 69 | // agent's memory writes would never resurface. Omit ctx_memory entirely |
| 70 | // (the matching guidance is gated in buildMagicContextSection). ctx_search |
| 71 | // stays: it still recalls conversation + git commits, just not memories. |
| 72 | const memoryEnabled = pluginConfig.memory?.enabled !== false; |
| 73 | const allTools: Record<string, ToolDefinition> = { |
| 74 | ...(ctxReduceEnabled |
| 75 | ? createCtxReduceTools({ |
| 76 | db, |
| 77 | protectedTags: pluginConfig.protected_tags ?? DEFAULT_PROTECTED_TAGS, |
| 78 | }) |