From 0f91b4170c3c590604b7650ab7c67d0156970c99 Mon Sep 17 00:00:00 2001 From: Burak Yigit Kaya Date: Fri, 17 Apr 2026 10:20:31 +0000 Subject: [PATCH] fix: add root exports trampoline so file:// plugin loading works The monorepo refactor removed the root main/exports fields, breaking `"plugin": ["file:///path/to/opencode-lore"]` loading in OpenCode. Root now re-exports from packages/opencode/src/index.ts. --- AGENTS.md | 10 ++++++++-- package.json | 4 ++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 528abb5..98a3bdb 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -27,12 +27,18 @@ * **LTM injection pipeline: system transform → forSession → formatKnowledge → gradient deduction**: LTM injected via experimental.chat.system.transform hook. getLtmBudget() computes ceiling as (contextLimit - outputReserved - overhead) \* ltmFraction (default 10%, configurable 2-30%). forSession() loads project-specific entries unconditionally + cross-project entries scored by term overlap, greedy-packs into budget. formatKnowledge() renders as markdown. setLtmTokens() records consumption so gradient deducts it. Key: LTM goes into output.system (system prompt) — invisible to tryFit(), counts against overhead budget. + +* **Monorepo structure: @loreai/core + opencode-lore packages with Bun workspaces**: Lore uses a Bun workspace monorepo with two packages: \`packages/core/\` (\`@loreai/core\`) contains all runtime-agnostic logic (db, ltm, gradient, search, temporal, distillation, curator, etc.) with a barrel \`src/index.ts\`. \`packages/opencode/\` (\`opencode-lore\`) contains the OpenCode plugin entry (\`src/index.ts\`), recall tool (\`src/reflect.ts\`), scripts, eval harness, and tests that depend on \`@opencode-ai/plugin\`. Root \`package.json\` is private with \`workspaces: \["packages/\*"]\`. Tests run via \`bun test\` from root with preload at \`packages/core/test/setup.ts\` (configured in \`bunfig.toml\`). \`tsconfig.base.json\` at root, each package extends it. OpenCode package depends on \`@loreai/core\` as \`workspace:\*\`. + * **OpenCode plugin SDK has no embedding API — vector search blocked**: The OpenCode plugin SDK (\`@opencode-ai/plugin\`, \`@opencode-ai/sdk\`) exposes only session/chat/tool operations. There is no \`client.embed()\`, embeddings endpoint, or raw model inference API. The only LLM access is \`client.session.prompt()\` which creates full chat roundtrips through the agentic loop. This means Lore cannot do vector/embedding search without either: (1) OpenCode adding an embedding API, or (2) direct \`fetch()\` to provider APIs bypassing the SDK (fragile — requires key extraction from \`client.config.providers()\`). The FTS5 + RRF search infrastructure is designed to be additive — vector search would layer on top as another RRF input list, not replace BM25. * **OpenCode repo moved to anomalyco/opencode with Node conditional imports**: OpenCode repo moved from \`sst/opencode\` to \`anomalyco/opencode\`. Still uses Bun as default runtime (\`packageManager: bun@1.3.11\`) but added Node support via conditional imports pattern in \`packages/opencode/package.json\`: \`"#db"\`, \`"#pty"\`, \`"#hono"\` each have \`bun\`/\`node\`/\`default\` variants. Uses Drizzle ORM over both \`bun:sqlite\` and \`node:sqlite\` (via \`drizzle-orm/bun-sqlite\` and \`drizzle-orm/node-sqlite\`). Dependencies include \`@hono/node-server\`, \`@hono/node-ws\`, \`@agentclientprotocol/sdk@0.16.1\`. This dual-runtime pattern validates Lore's planned conditional imports approach. + +* **SQLite #db/driver subpath import for Bun/Node dual-runtime**: Core package uses Node subpath imports (\`#db/driver\` in \`package.json\`) to resolve \`bun:sqlite\` or \`node:sqlite\` at runtime. \`packages/core/src/db/driver.bun.ts\` re-exports \`Database\` from \`bun:sqlite\` + \`sha256\` via \`node:crypto\`. \`packages/core/src/db/driver.node.ts\` extends \`DatabaseSync\` from \`node:sqlite\` adding a \`.query(sql)\` method with WeakMap-based statement caching — providing API parity with \`bun:sqlite\`'s \`.query()\`. All 99+ \`.query()\` call sites in core work unchanged. \`db.ts\` imports \`{ Database, sha256 } from "#db/driver"\`. Tests run under Bun (which picks \`driver.bun.ts\`); esbuild bundles will use \`conditions: \["node"]\` or \`\["bun"]\` to select the right driver. + * **Worker session prompt helper with agent-not-found retry**: src/worker.ts owns workerSessionIDs Set, isWorkerSession(), and promptWorker(). promptWorker() calls session.prompt() and uses the return value directly (no redundant session.messages() call). On 'agent not found' errors (detected via regex on JSON.stringify(result.error)), it retries once without the agent parameter on a fresh session. All callers (distillation×2, curator×2, search×1) use this shared helper. Session rotation (deleting from the caller's Map) happens after every call. The retry creates a new child session via client.session.create() and registers its ID in workerSessionIDs. @@ -42,7 +48,7 @@ * **Curator prompt scoped to code-relevant knowledge only**: CURATOR\_SYSTEM in src/prompt.ts now explicitly excludes: general ecosystem knowledge available online, business strategy and marketing positioning, product pricing models, third-party tool details not needed for development, and personal contact information. This was added after the curator extracted entries about OpenWork integration strategy (including an email address), Lore Cloud pricing tiers, and AGENTS.md ecosystem facts — none of which help an agent write code. The curatorUser() function also appends guidance to prefer updating existing entries over creating new ones for the same concept, reducing duplicate creation. -* **Lore plugin cannot use native Node addons — pure bun:sqlite only**: OpenCode uses conditional imports (\`#db\` in package.json) to swap \`bun:sqlite\` ↔ \`node:sqlite\` at runtime — two 6-line files, zero build step, Drizzle ORM on top. Lore can use the same pattern for its core package: \`"#db": { "bun": "./src/db.bun.ts", "node": "./src/db.node.ts" }\`. The \`bun:sqlite\` and \`node:sqlite\` APIs are nearly identical (both inspired by better-sqlite3). Key differences: \`db.query()\` vs \`db.prepare()\`, \`Bun.CryptoHasher\` vs \`node:crypto\`. FTS5, transactions, pragmas all work identically. \`node:sqlite\` is stable without flags in Node 24+. Drizzle adoption is orthogonal — Lore's custom FTS5/BM25 queries wouldn't benefit from an ORM layer. No native addons needed in either path. +* **Lore plugin cannot use native Node addons — pure bun:sqlite only**: Lore uses Node conditional imports (\`#db/driver\`) to swap \`bun:sqlite\` ↔ \`node:sqlite\` at runtime — two driver files behind a subpath alias in \`packages/core/package.json\`. The Node driver extends \`DatabaseSync\` adding a \`.query()\` shim with statement caching. \`bun:sqlite\` and \`node:sqlite\` APIs differ: \`.query()\` vs \`.prepare()\`, \`{ create: true }\` option exists only in Bun. FTS5, transactions, pragmas work identically in both. \`node:sqlite\` is stable without flags in Node 22.5+. Drizzle adoption is orthogonal — Lore's FTS5/BM25 queries wouldn't benefit from ORM. No native addons needed. * **Lore standalone ACP server using Pi as agentic engine**: Lore is planned to become a standalone ACP (Agent Client Protocol) server, independent of OpenCode. Architecture: Lore speaks ACP to editors (Zed, JetBrains), uses Pi (\`@mariozechner/pi-coding-agent\`) internally as the agentic loop engine, and layers its memory system via Pi extensions. ACP proxy approach was rejected because proxies cannot modify the downstream agent's internal message array or system prompt — losing gradient context management and LTM injection, Lore's most valuable features. As a full ACP agent, Lore owns the LLM interaction with full control. Pi was chosen for its extension hooks (message injection, history filtering, custom compaction, custom tools) that map to Lore's existing OpenCode hooks. Requires a research spike first to verify Pi's extension API compatibility. @@ -59,7 +65,7 @@ * **Lore knowledge FTS search was sorted by updated\_at, not BM25 relevance**: Three FTS search bugs fixed in the search overhaul: (1) Knowledge FTS sorted by \`updated\_at DESC\` not BM25 — fix: JOIN knowledge\_fts, \`ORDER BY bm25(knowledge\_fts, 6, 2, 3)\`. (2) Distillations had no FTS table (LIKE-only search) — fix: \`distillation\_fts\` virtual table in schema migration v7 with backfill + sync triggers. (3) \`forSession()\` used coarse bag-of-words term-overlap (top 30 terms >3 chars, no stemming) — fix: replaced \`scoreEntries()\` with \`scoreEntriesFTS()\` using FTS5 BM25 with OR semantics. All search functions now use AND-then-OR fallback pattern. \`ftsQuery()\`/\`ftsQueryOr()\` centralized in \`src/search.ts\` with stopword filtering and single-char removal. -* **Test DB isolation via LORE\_DB\_PATH and Bun test preload**: Lore test suite uses isolated temp DB via test/setup.ts preload (bunfig.toml). Preload sets LORE\_DB\_PATH to mkdtempSync path before any imports of src/db.ts; afterAll cleans up. src/db.ts checks LORE\_DB\_PATH first. agents-file.test.ts needs beforeEach cleanup for intra-file isolation and TEST\_UUIDS cleanup in afterAll (shared with ltm.test.ts). Individual test files don't need close() calls — preload handles DB lifecycle. +* **Test DB isolation via LORE\_DB\_PATH and Bun test preload**: Lore test suite uses isolated temp DB via \`packages/core/test/setup.ts\` preload (\`bunfig.toml\` at repo root: \`preload = \["./packages/core/test/setup.ts"]\`). Preload sets \`LORE\_DB\_PATH\` to \`mkdtempSync\` path before any imports of \`src/db.ts\`; \`afterAll\` cleans up. \`src/db.ts\` checks \`LORE\_DB\_PATH\` first. \`agents-file.test.ts\` needs \`beforeEach\` cleanup for intra-file isolation and \`TEST\_UUIDS\` cleanup in \`afterAll\` (shared with \`ltm.test.ts\`). Tests covering OpenCode-specific code (plugin init, recovery functions) live in \`packages/opencode/test/\`. Driver-level tests in \`packages/core/test/db-driver.test.ts\`. ### Pattern diff --git a/package.json b/package.json index 56bd0f6..11184a2 100644 --- a/package.json +++ b/package.json @@ -4,6 +4,10 @@ "type": "module", "license": "MIT", "description": "Monorepo root for Lore — three-tier memory architecture", + "main": "./packages/opencode/src/index.ts", + "exports": { + ".": "./packages/opencode/src/index.ts" + }, "workspaces": [ "packages/*" ],