diff --git a/bun.lock b/bun.lock index 26aa766..a7b94da 100644 --- a/bun.lock +++ b/bun.lock @@ -4,17 +4,35 @@ "workspaces": { "": { "name": "opencode-nuum", + "devDependencies": { + "@types/bun": "^1.2.0", + "esbuild": "^0.25.12", + "fast-check": "^4.5.3", + "typescript": "^5.8.0", + }, + }, + "packages/core": { + "name": "@loreai/core", + "version": "0.9.1", "dependencies": { "remark": "^15.0.1", "uuidv7": "^1.1.0", - "zod": "^3.25.0", + "zod": "^4.3.6", + }, + "devDependencies": { + "@opencode-ai/sdk": "^1.1.39", + "@types/mdast": "^4.0.4", + }, + }, + "packages/opencode": { + "name": "opencode-lore", + "version": "0.9.1", + "dependencies": { + "@loreai/core": "workspace:*", }, "devDependencies": { "@opencode-ai/plugin": "^1.1.39", "@opencode-ai/sdk": "^1.1.39", - "@types/bun": "^1.2.0", - "fast-check": "^4.5.3", - "typescript": "^5.8.0", }, "peerDependencies": { "@opencode-ai/plugin": ">=1.1.0", @@ -22,6 +40,60 @@ }, }, "packages": { + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.25.12", "", { "os": "android", "cpu": "arm64" }, "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.25.12", "", { "os": "android", "cpu": "x64" }, "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.25.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.25.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.25.12", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.25.12", "", { "os": "freebsd", "cpu": "x64" }, "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.25.12", "", { "os": "linux", "cpu": "arm" }, "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.25.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.25.12", "", { "os": "linux", "cpu": "ia32" }, "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.25.12", "", { "os": "linux", "cpu": "ppc64" }, "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.25.12", "", { "os": "linux", "cpu": "none" }, "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.25.12", "", { "os": "linux", "cpu": "s390x" }, "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.25.12", "", { "os": "linux", "cpu": "x64" }, "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.25.12", "", { "os": "none", "cpu": "x64" }, "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.25.12", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.25.12", "", { "os": "openbsd", "cpu": "x64" }, "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.25.12", "", { "os": "none", "cpu": "arm64" }, "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.25.12", "", { "os": "sunos", "cpu": "x64" }, "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.25.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.25.12", "", { "os": "win32", "cpu": "ia32" }, "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.25.12", "", { "os": "win32", "cpu": "x64" }, "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA=="], + + "@loreai/core": ["@loreai/core@workspace:packages/core"], + "@opencode-ai/plugin": ["@opencode-ai/plugin@1.2.6", "", { "dependencies": { "@opencode-ai/sdk": "1.2.6", "zod": "4.1.8" } }, "sha512-CJEp3k17yWsjyfivm3zQof8L42pdze3a7iTqMOyesHgJplSuLiBYAMndbBYMDuJkyAh0dHYjw8v10vVw7Kfl4Q=="], "@opencode-ai/sdk": ["@opencode-ai/sdk@1.2.6", "", {}, "sha512-dWMF8Aku4h7fh8sw5tQ2FtbqRLbIFT8FcsukpxTird49ax7oUXP+gzqxM/VdxHjfksQvzLBjLZyMdDStc5g7xA=="], @@ -52,6 +124,8 @@ "devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="], + "esbuild": ["esbuild@0.25.12", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.25.12", "@esbuild/android-arm": "0.25.12", "@esbuild/android-arm64": "0.25.12", "@esbuild/android-x64": "0.25.12", "@esbuild/darwin-arm64": "0.25.12", "@esbuild/darwin-x64": "0.25.12", "@esbuild/freebsd-arm64": "0.25.12", "@esbuild/freebsd-x64": "0.25.12", "@esbuild/linux-arm": "0.25.12", "@esbuild/linux-arm64": "0.25.12", "@esbuild/linux-ia32": "0.25.12", "@esbuild/linux-loong64": "0.25.12", "@esbuild/linux-mips64el": "0.25.12", "@esbuild/linux-ppc64": "0.25.12", "@esbuild/linux-riscv64": "0.25.12", "@esbuild/linux-s390x": "0.25.12", "@esbuild/linux-x64": "0.25.12", "@esbuild/netbsd-arm64": "0.25.12", "@esbuild/netbsd-x64": "0.25.12", "@esbuild/openbsd-arm64": "0.25.12", "@esbuild/openbsd-x64": "0.25.12", "@esbuild/openharmony-arm64": "0.25.12", "@esbuild/sunos-x64": "0.25.12", "@esbuild/win32-arm64": "0.25.12", "@esbuild/win32-ia32": "0.25.12", "@esbuild/win32-x64": "0.25.12" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg=="], + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], "fast-check": ["fast-check@4.5.3", "", { "dependencies": { "pure-rand": "^7.0.0" } }, "sha512-IE9csY7lnhxBnA8g/WI5eg/hygA6MGWJMSNfFRrBlXUciADEhS1EDB0SIsMSvzubzIlOBbVITSsypCsW717poA=="], @@ -112,6 +186,8 @@ "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + "opencode-lore": ["opencode-lore@workspace:packages/opencode"], + "pure-rand": ["pure-rand@7.0.1", "", {}, "sha512-oTUZM/NAZS8p7ANR3SHh30kXB+zK2r2BPcEn/awJIbOvq82WoMN4p62AWWp3Hhw50G0xMsw1mhIBLqHw64EcNQ=="], "remark": ["remark@15.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-Eht5w30ruCXgFmxVUSlNWQ9iiimq07URKeFS3hNc8cUWy1llX4KDWfyEDZRycMc+znsN9Ux5/tJ/BFdgdOwA3A=="], @@ -142,7 +218,7 @@ "vfile-message": ["vfile-message@4.0.3", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-stringify-position": "^4.0.0" } }, "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw=="], - "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "zod": ["zod@4.3.6", "", {}, "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg=="], "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], diff --git a/bunfig.toml b/bunfig.toml index 8755352..1d216be 100644 --- a/bunfig.toml +++ b/bunfig.toml @@ -1,2 +1,2 @@ [test] -preload = ["./test/setup.ts"] +preload = ["./packages/core/test/setup.ts"] diff --git a/package.json b/package.json index 389f49d..56bd0f6 100644 --- a/package.json +++ b/package.json @@ -1,48 +1,26 @@ { - "name": "opencode-lore", - "version": "0.9.1", + "name": "lore-monorepo", + "private": true, "type": "module", "license": "MIT", - "description": "Three-tier memory architecture for OpenCode — distillation, not summarization", - "main": "src/index.ts", - "exports": { - ".": "./src/index.ts" - }, + "description": "Monorepo root for Lore — three-tier memory architecture", + "workspaces": [ + "packages/*" + ], "scripts": { - "typecheck": "bun run tsc --noEmit", - "test": "bun test" - }, - "peerDependencies": { - "@opencode-ai/plugin": ">=1.1.0" - }, - "dependencies": { - "remark": "^15.0.1", - "uuidv7": "^1.1.0", - "zod": "^4.3.6" + "typecheck": "bun --filter '*' typecheck", + "test": "bun test", + "build": "bun --filter '*' build" }, "devDependencies": { - "@opencode-ai/plugin": "^1.1.39", - "@opencode-ai/sdk": "^1.1.39", "@types/bun": "^1.2.0", + "esbuild": "^0.25.12", "fast-check": "^4.5.3", "typescript": "^5.8.0" }, - "files": [ - "src/", - "README.md", - "LICENSE" - ], "repository": { "type": "git", "url": "git+https://github.com/BYK/opencode-lore.git" }, - "keywords": [ - "opencode", - "plugin", - "memory", - "agent", - "distillation", - "llm" - ], "author": "BYK" } diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 0000000..6e78e0b --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,58 @@ +{ + "name": "@loreai/core", + "version": "0.9.1", + "type": "module", + "license": "MIT", + "description": "Shared memory engine for Lore — three-tier storage, distillation, gradient context management", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "bun": "./src/index.ts", + "default": "./dist/node/index.js" + } + }, + "imports": { + "#db/driver": { + "bun": "./src/db/driver.bun.ts", + "default": "./src/db/driver.node.ts" + } + }, + "scripts": { + "typecheck": "tsc --noEmit", + "build": "bun run script/build.ts" + }, + "dependencies": { + "remark": "^15.0.1", + "uuidv7": "^1.1.0", + "zod": "^4.3.6" + }, + "devDependencies": { + "@opencode-ai/sdk": "^1.1.39", + "@types/mdast": "^4.0.4" + }, + "files": [ + "src/", + "dist/", + "README.md", + "LICENSE" + ], + "engines": { + "node": ">=22.5" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/BYK/opencode-lore.git", + "directory": "packages/core" + }, + "keywords": [ + "lore", + "memory", + "llm", + "sqlite", + "fts5", + "distillation" + ], + "author": "BYK" +} diff --git a/src/agents-file.ts b/packages/core/src/agents-file.ts similarity index 100% rename from src/agents-file.ts rename to packages/core/src/agents-file.ts diff --git a/src/config.ts b/packages/core/src/config.ts similarity index 100% rename from src/config.ts rename to packages/core/src/config.ts diff --git a/src/curator.ts b/packages/core/src/curator.ts similarity index 100% rename from src/curator.ts rename to packages/core/src/curator.ts diff --git a/src/db.ts b/packages/core/src/db.ts similarity index 100% rename from src/db.ts rename to packages/core/src/db.ts diff --git a/src/distillation.ts b/packages/core/src/distillation.ts similarity index 100% rename from src/distillation.ts rename to packages/core/src/distillation.ts diff --git a/src/embedding.ts b/packages/core/src/embedding.ts similarity index 100% rename from src/embedding.ts rename to packages/core/src/embedding.ts diff --git a/src/gradient.ts b/packages/core/src/gradient.ts similarity index 100% rename from src/gradient.ts rename to packages/core/src/gradient.ts diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 0000000..23f6122 --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1,80 @@ +// @loreai/core — shared memory engine for Lore. +// +// This barrel re-exports every core module so hosts (the OpenCode plugin, the +// Pi extension, or any future adapter) can import from a single entry: +// +// import { ltm, temporal, gradient, ... } from "@loreai/core" +// +// Modules that are intentionally not re-exported: +// - `db.ts` internals are exposed via specific functions (db(), ensureProject(), etc.) +// - No Plugin/Hooks surface — those live in host-specific packages. + +export * as temporal from "./temporal"; +export * as ltm from "./ltm"; +export * as distillation from "./distillation"; +export * as curator from "./curator"; +export * as embedding from "./embedding"; +export * as latReader from "./lat-reader"; +export * as log from "./log"; + +export { load, config, type LoreConfig } from "./config"; +export { + db, + ensureProject, + isFirstRun, + projectId, + projectName, + loadForceMinLayer, + saveForceMinLayer, + close, +} from "./db"; +export { + transform, + setModelLimits, + needsUrgentDistillation, + calibrate, + setLtmTokens, + getLtmTokens, + getLtmBudget, + setForceMinLayer, + getLastTransformedCount, + getLastTransformEstimate, +} from "./gradient"; +export { + formatKnowledge, + formatDistillations, + DISTILLATION_SYSTEM, + distillationUser, + RECURSIVE_SYSTEM, + recursiveUser, + CURATOR_SYSTEM, + curatorUser, + CONSOLIDATION_SYSTEM, + consolidationUser, + QUERY_EXPANSION_SYSTEM, +} from "./prompt"; +export { shouldImport, importFromFile, exportToFile } from "./agents-file"; +export { workerSessionIDs, promptWorker, isWorkerSession } from "./worker"; +export { + ftsQuery, + ftsQueryOr, + EMPTY_QUERY, + reciprocalRankFusion, + expandQuery, + extractTopTerms, +} from "./search"; +export { + serialize, + inline, + h, + p, + ul, + lip, + liph, + t, + root, + strong, + normalize, + sanitizeSurrogates, + unescapeMarkdown, +} from "./markdown"; diff --git a/src/lat-reader.ts b/packages/core/src/lat-reader.ts similarity index 100% rename from src/lat-reader.ts rename to packages/core/src/lat-reader.ts diff --git a/src/log.ts b/packages/core/src/log.ts similarity index 100% rename from src/log.ts rename to packages/core/src/log.ts diff --git a/src/ltm.ts b/packages/core/src/ltm.ts similarity index 100% rename from src/ltm.ts rename to packages/core/src/ltm.ts diff --git a/src/markdown.ts b/packages/core/src/markdown.ts similarity index 100% rename from src/markdown.ts rename to packages/core/src/markdown.ts diff --git a/src/prompt.ts b/packages/core/src/prompt.ts similarity index 100% rename from src/prompt.ts rename to packages/core/src/prompt.ts diff --git a/src/search.ts b/packages/core/src/search.ts similarity index 100% rename from src/search.ts rename to packages/core/src/search.ts diff --git a/src/temporal.ts b/packages/core/src/temporal.ts similarity index 100% rename from src/temporal.ts rename to packages/core/src/temporal.ts diff --git a/src/worker.ts b/packages/core/src/worker.ts similarity index 100% rename from src/worker.ts rename to packages/core/src/worker.ts diff --git a/test/agents-file.test.ts b/packages/core/test/agents-file.test.ts similarity index 100% rename from test/agents-file.test.ts rename to packages/core/test/agents-file.test.ts diff --git a/test/config.test.ts b/packages/core/test/config.test.ts similarity index 100% rename from test/config.test.ts rename to packages/core/test/config.test.ts diff --git a/test/db.test.ts b/packages/core/test/db.test.ts similarity index 100% rename from test/db.test.ts rename to packages/core/test/db.test.ts diff --git a/test/embedding.test.ts b/packages/core/test/embedding.test.ts similarity index 100% rename from test/embedding.test.ts rename to packages/core/test/embedding.test.ts diff --git a/test/fixtures/lat.md/architecture.md b/packages/core/test/fixtures/lat.md/architecture.md similarity index 100% rename from test/fixtures/lat.md/architecture.md rename to packages/core/test/fixtures/lat.md/architecture.md diff --git a/test/fixtures/lat.md/auth.md b/packages/core/test/fixtures/lat.md/auth.md similarity index 100% rename from test/fixtures/lat.md/auth.md rename to packages/core/test/fixtures/lat.md/auth.md diff --git a/test/gradient.test.ts b/packages/core/test/gradient.test.ts similarity index 100% rename from test/gradient.test.ts rename to packages/core/test/gradient.test.ts diff --git a/test/integrity.test.ts b/packages/core/test/integrity.test.ts similarity index 100% rename from test/integrity.test.ts rename to packages/core/test/integrity.test.ts diff --git a/test/lat-reader.test.ts b/packages/core/test/lat-reader.test.ts similarity index 100% rename from test/lat-reader.test.ts rename to packages/core/test/lat-reader.test.ts diff --git a/test/ltm.test.ts b/packages/core/test/ltm.test.ts similarity index 100% rename from test/ltm.test.ts rename to packages/core/test/ltm.test.ts diff --git a/test/markdown.test.ts b/packages/core/test/markdown.test.ts similarity index 79% rename from test/markdown.test.ts rename to packages/core/test/markdown.test.ts index f5fca9f..14472bf 100644 --- a/test/markdown.test.ts +++ b/packages/core/test/markdown.test.ts @@ -3,7 +3,6 @@ import fc from "fast-check"; import { remark } from "remark"; import { normalize, unescapeMarkdown, sanitizeSurrogates, inline } from "../src/markdown"; import { formatDistillations, formatKnowledge } from "../src/prompt"; -import { isContextOverflow, buildRecoveryMessage } from "../src/index"; const proc = remark(); @@ -275,83 +274,6 @@ describe("unescapeMarkdown", () => { }); }); -describe("isContextOverflow", () => { - test("detects Anthropic 'prompt is too long' error", () => { - expect(isContextOverflow({ - message: "prompt is too long: 214636 tokens > 200000 maximum", - })).toBe(true); - }); - - test("detects wrapped APIError shape (error.data.message)", () => { - expect(isContextOverflow({ - name: "APIError", - data: { message: "prompt is too long: 214636 tokens > 200000 maximum" }, - })).toBe(true); - }); - - test("detects OpenAI 'context length exceeded'", () => { - expect(isContextOverflow({ - message: "This model's maximum context length is 128000 tokens. However, your messages resulted in 150000 tokens. context length exceeded", - })).toBe(true); - }); - - test("detects 'maximum context length'", () => { - expect(isContextOverflow({ - message: "maximum context length exceeded", - })).toBe(true); - }); - - test("detects 'too many tokens'", () => { - expect(isContextOverflow({ message: "too many tokens" })).toBe(true); - }); - - test("detects 'ContextWindowExceededError'", () => { - expect(isContextOverflow({ - message: "ContextWindowExceededError: request too large", - })).toBe(true); - }); - - test("returns false for unrelated errors", () => { - expect(isContextOverflow({ message: "rate limit exceeded" })).toBe(false); - expect(isContextOverflow({ message: "internal server error" })).toBe(false); - expect(isContextOverflow({ name: "TimeoutError" })).toBe(false); - }); - - test("returns false for null/undefined", () => { - expect(isContextOverflow(null)).toBe(false); - expect(isContextOverflow(undefined)).toBe(false); - }); -}); - -describe("buildRecoveryMessage", () => { - test("includes distilled history when summaries exist", () => { - const msg = buildRecoveryMessage([ - { observations: "Fixed the bug in auth.ts", generation: 0 }, - ]); - expect(msg).toContain(""); - expect(msg).toContain(""); - expect(msg).toContain("context overflow error"); - expect(msg).toContain("Fixed the bug in auth.ts"); - }); - - test("includes meta and recent sections from formatDistillations", () => { - const msg = buildRecoveryMessage([ - { observations: "Earlier consolidated work", generation: 1 }, - { observations: "Recent detailed work", generation: 0 }, - ]); - expect(msg).toContain("Earlier Work (summarized)"); - expect(msg).toContain("Recent Work (distilled)"); - expect(msg).toContain("Earlier consolidated work"); - expect(msg).toContain("Recent detailed work"); - }); - - test("shows fallback message when no summaries available", () => { - const msg = buildRecoveryMessage([]); - expect(msg).toContain(""); - expect(msg).toContain("No distilled history available"); - }); -}); - describe("sanitizeSurrogates", () => { test("passes through normal text unchanged", () => { expect(sanitizeSurrogates("hello world")).toBe("hello world"); diff --git a/test/refs.test.ts b/packages/core/test/refs.test.ts similarity index 100% rename from test/refs.test.ts rename to packages/core/test/refs.test.ts diff --git a/test/search.test.ts b/packages/core/test/search.test.ts similarity index 100% rename from test/search.test.ts rename to packages/core/test/search.test.ts diff --git a/test/setup.ts b/packages/core/test/setup.ts similarity index 100% rename from test/setup.ts rename to packages/core/test/setup.ts diff --git a/test/temporal.test.ts b/packages/core/test/temporal.test.ts similarity index 100% rename from test/temporal.test.ts rename to packages/core/test/temporal.test.ts diff --git a/test/worker.test.ts b/packages/core/test/worker.test.ts similarity index 100% rename from test/worker.test.ts rename to packages/core/test/worker.test.ts diff --git a/packages/core/tsconfig.json b/packages/core/tsconfig.json new file mode 100644 index 0000000..cc5979b --- /dev/null +++ b/packages/core/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src", "test", "script"] +} diff --git a/eval/backfill.ts b/packages/opencode/eval/backfill.ts similarity index 99% rename from eval/backfill.ts rename to packages/opencode/eval/backfill.ts index 5d3d4d7..1f22647 100644 --- a/eval/backfill.ts +++ b/packages/opencode/eval/backfill.ts @@ -1,5 +1,5 @@ import { Database } from "bun:sqlite"; -import { DISTILLATION_SYSTEM, distillationUser } from "../src/prompt"; +import { DISTILLATION_SYSTEM, distillationUser } from "@loreai/core"; const BASE_URL = "http://localhost:4096"; const MODEL = { providerID: "anthropic", modelID: "claude-sonnet-4-6" }; diff --git a/eval/coding_eval.ts b/packages/opencode/eval/coding_eval.ts similarity index 99% rename from eval/coding_eval.ts rename to packages/opencode/eval/coding_eval.ts index 770b51e..6038b66 100644 --- a/eval/coding_eval.ts +++ b/packages/opencode/eval/coding_eval.ts @@ -1,6 +1,6 @@ import { parseArgs } from "util"; import { Database } from "bun:sqlite"; -import { DISTILLATION_SYSTEM, distillationUser } from "../src/prompt"; +import { DISTILLATION_SYSTEM, distillationUser } from "@loreai/core"; const BASE_URL = "http://localhost:4096"; const MODEL = { providerID: "anthropic", modelID: "claude-sonnet-4-6" }; diff --git a/eval/data/coding_memory_eval.json b/packages/opencode/eval/data/coding_memory_eval.json similarity index 100% rename from eval/data/coding_memory_eval.json rename to packages/opencode/eval/data/coding_memory_eval.json diff --git a/eval/data/coding_session_eval.json b/packages/opencode/eval/data/coding_session_eval.json similarity index 100% rename from eval/data/coding_session_eval.json rename to packages/opencode/eval/data/coding_session_eval.json diff --git a/eval/data/sessions/cli-nightly.json b/packages/opencode/eval/data/sessions/cli-nightly.json similarity index 100% rename from eval/data/sessions/cli-nightly.json rename to packages/opencode/eval/data/sessions/cli-nightly.json diff --git a/eval/data/sessions/cli-sentry-issue.json b/packages/opencode/eval/data/sessions/cli-sentry-issue.json similarity index 100% rename from eval/data/sessions/cli-sentry-issue.json rename to packages/opencode/eval/data/sessions/cli-sentry-issue.json diff --git a/eval/extract_session.ts b/packages/opencode/eval/extract_session.ts similarity index 100% rename from eval/extract_session.ts rename to packages/opencode/eval/extract_session.ts diff --git a/eval/results/.keep b/packages/opencode/eval/results/.keep similarity index 100% rename from eval/results/.keep rename to packages/opencode/eval/results/.keep diff --git a/eval/results/coding_eval_v3.jsonl b/packages/opencode/eval/results/coding_eval_v3.jsonl similarity index 100% rename from eval/results/coding_eval_v3.jsonl rename to packages/opencode/eval/results/coding_eval_v3.jsonl diff --git a/eval/results/session_eval_v1.jsonl b/packages/opencode/eval/results/session_eval_v1.jsonl similarity index 100% rename from eval/results/session_eval_v1.jsonl rename to packages/opencode/eval/results/session_eval_v1.jsonl diff --git a/eval/results/session_eval_v2.jsonl b/packages/opencode/eval/results/session_eval_v2.jsonl similarity index 100% rename from eval/results/session_eval_v2.jsonl rename to packages/opencode/eval/results/session_eval_v2.jsonl diff --git a/eval/results/session_eval_v3.jsonl b/packages/opencode/eval/results/session_eval_v3.jsonl similarity index 100% rename from eval/results/session_eval_v3.jsonl rename to packages/opencode/eval/results/session_eval_v3.jsonl diff --git a/eval/session_eval.ts b/packages/opencode/eval/session_eval.ts similarity index 99% rename from eval/session_eval.ts rename to packages/opencode/eval/session_eval.ts index 02a1c94..e343279 100644 --- a/eval/session_eval.ts +++ b/packages/opencode/eval/session_eval.ts @@ -10,7 +10,7 @@ */ import { parseArgs } from "util"; import { Database } from "bun:sqlite"; -import { DISTILLATION_SYSTEM, distillationUser } from "../src/prompt"; +import { DISTILLATION_SYSTEM, distillationUser } from "@loreai/core"; // --- Config --- const BASE_URL = "http://localhost:4096"; diff --git a/packages/opencode/package.json b/packages/opencode/package.json new file mode 100644 index 0000000..e18d9d1 --- /dev/null +++ b/packages/opencode/package.json @@ -0,0 +1,50 @@ +{ + "name": "opencode-lore", + "version": "0.9.1", + "type": "module", + "license": "MIT", + "description": "Three-tier memory architecture for OpenCode — distillation, not summarization", + "main": "./src/index.ts", + "types": "./src/index.ts", + "exports": { + ".": { + "types": "./src/index.ts", + "bun": "./src/index.ts", + "default": "./dist/index.js" + } + }, + "scripts": { + "typecheck": "tsc --noEmit", + "build": "bun run script/build.ts" + }, + "peerDependencies": { + "@opencode-ai/plugin": ">=1.1.0" + }, + "dependencies": { + "@loreai/core": "workspace:*" + }, + "devDependencies": { + "@opencode-ai/plugin": "^1.1.39", + "@opencode-ai/sdk": "^1.1.39" + }, + "files": [ + "src/", + "dist/", + "README.md", + "LICENSE" + ], + "repository": { + "type": "git", + "url": "git+https://github.com/BYK/opencode-lore.git", + "directory": "packages/opencode" + }, + "keywords": [ + "opencode", + "plugin", + "memory", + "agent", + "distillation", + "llm" + ], + "author": "BYK" +} diff --git a/scripts/distill-session.ts b/packages/opencode/scripts/distill-session.ts similarity index 93% rename from scripts/distill-session.ts rename to packages/opencode/scripts/distill-session.ts index 99ad8a2..9a3636e 100644 --- a/scripts/distill-session.ts +++ b/packages/opencode/scripts/distill-session.ts @@ -16,10 +16,7 @@ import { parseArgs } from "util"; import { createOpencodeClient } from "@opencode-ai/sdk"; -import { load, config } from "../src/config"; -import { ensureProject } from "../src/db"; -import * as temporal from "../src/temporal"; -import * as distillation from "../src/distillation"; +import { load, config, ensureProject, temporal, distillation } from "@loreai/core"; const { values, positionals } = parseArgs({ args: Bun.argv.slice(2), diff --git a/scripts/list-sessions.ts b/packages/opencode/scripts/list-sessions.ts similarity index 96% rename from scripts/list-sessions.ts rename to packages/opencode/scripts/list-sessions.ts index d270ac1..af9a83c 100644 --- a/scripts/list-sessions.ts +++ b/packages/opencode/scripts/list-sessions.ts @@ -10,8 +10,7 @@ */ import { parseArgs } from "util"; -import { load } from "../src/config"; -import { db } from "../src/db"; +import { load, db } from "@loreai/core"; const { values } = parseArgs({ args: Bun.argv.slice(2), diff --git a/src/index.ts b/packages/opencode/src/index.ts similarity index 98% rename from src/index.ts rename to packages/opencode/src/index.ts index 1ad484f..efd221b 100644 --- a/src/index.ts +++ b/packages/opencode/src/index.ts @@ -1,12 +1,14 @@ import type { Plugin, Hooks } from "@opencode-ai/plugin"; import { join } from "path"; -import { load, config } from "./config"; -import { ensureProject, isFirstRun } from "./db"; -import * as temporal from "./temporal"; -import * as ltm from "./ltm"; -import * as distillation from "./distillation"; -import * as curator from "./curator"; import { + load, + config, + ensureProject, + isFirstRun, + temporal, + ltm, + distillation, + curator, transform, setModelLimits, needsUrgentDistillation, @@ -16,14 +18,17 @@ import { setForceMinLayer, getLastTransformedCount, getLastTransformEstimate, -} from "./gradient"; -import { formatKnowledge, formatDistillations } from "./prompt"; + formatKnowledge, + formatDistillations, + shouldImport, + importFromFile, + exportToFile, + latReader, + embedding, + log, + isWorkerSession, +} from "@loreai/core"; import { createRecallTool } from "./reflect"; -import { shouldImport, importFromFile, exportToFile } from "./agents-file"; -import * as latReader from "./lat-reader"; -import * as embedding from "./embedding"; -import * as log from "./log"; -import { isWorkerSession } from "./worker"; /** * Detect whether an error from session.error is a context overflow ("prompt too long"). diff --git a/src/reflect.ts b/packages/opencode/src/reflect.ts similarity index 97% rename from src/reflect.ts rename to packages/opencode/src/reflect.ts index 20c87b3..c47a053 100644 --- a/src/reflect.ts +++ b/packages/opencode/src/reflect.ts @@ -1,14 +1,30 @@ import { tool } from "@opencode-ai/plugin/tool"; import type { createOpencodeClient } from "@opencode-ai/sdk"; -import * as temporal from "./temporal"; -import * as ltm from "./ltm"; -import * as latReader from "./lat-reader"; -import * as log from "./log"; -import * as embedding from "./embedding"; -import { db, ensureProject, projectName } from "./db"; -import { ftsQuery, ftsQueryOr, EMPTY_QUERY, reciprocalRankFusion, expandQuery } from "./search"; -import { serialize, inline, h, p, ul, lip, liph, t, root } from "./markdown"; -import type { LoreConfig } from "./config"; +import { + temporal, + ltm, + latReader, + log, + embedding, + db, + ensureProject, + projectName, + ftsQuery, + ftsQueryOr, + EMPTY_QUERY, + reciprocalRankFusion, + expandQuery, + serialize, + inline, + h, + p, + ul, + lip, + liph, + t, + root, + type LoreConfig, +} from "@loreai/core"; type Client = ReturnType; diff --git a/test/index.test.ts b/packages/opencode/test/index.test.ts similarity index 99% rename from test/index.test.ts rename to packages/opencode/test/index.test.ts index 4878783..ed97b7a 100644 --- a/test/index.test.ts +++ b/packages/opencode/test/index.test.ts @@ -1,8 +1,6 @@ import { describe, test, expect, beforeEach } from "bun:test"; import { isContextOverflow, buildRecoveryMessage, LorePlugin, isValidProjectPath } from "../src/index"; -import * as ltm from "../src/ltm"; -import { db } from "../src/db"; -import { getLtmTokens, setModelLimits, calibrate, setLtmTokens } from "../src/gradient"; +import { ltm, db, getLtmTokens, setModelLimits, calibrate, setLtmTokens } from "@loreai/core"; import type { Plugin } from "@opencode-ai/plugin"; import type { Message, Part } from "@opencode-ai/sdk"; diff --git a/packages/opencode/test/recovery.test.ts b/packages/opencode/test/recovery.test.ts new file mode 100644 index 0000000..c5aa313 --- /dev/null +++ b/packages/opencode/test/recovery.test.ts @@ -0,0 +1,79 @@ +import { describe, test, expect } from "bun:test"; +import { isContextOverflow, buildRecoveryMessage } from "../src/index"; + +describe("isContextOverflow", () => { + test("detects Anthropic 'prompt is too long' error", () => { + expect(isContextOverflow({ + message: "prompt is too long: 214636 tokens > 200000 maximum", + })).toBe(true); + }); + + test("detects wrapped APIError shape (error.data.message)", () => { + expect(isContextOverflow({ + name: "APIError", + data: { message: "prompt is too long: 214636 tokens > 200000 maximum" }, + })).toBe(true); + }); + + test("detects OpenAI 'context length exceeded'", () => { + expect(isContextOverflow({ + message: "This model's maximum context length is 128000 tokens. However, your messages resulted in 150000 tokens. context length exceeded", + })).toBe(true); + }); + + test("detects 'maximum context length'", () => { + expect(isContextOverflow({ + message: "maximum context length exceeded", + })).toBe(true); + }); + + test("detects 'too many tokens'", () => { + expect(isContextOverflow({ message: "too many tokens" })).toBe(true); + }); + + test("detects 'ContextWindowExceededError'", () => { + expect(isContextOverflow({ + message: "ContextWindowExceededError: request too large", + })).toBe(true); + }); + + test("returns false for unrelated errors", () => { + expect(isContextOverflow({ message: "rate limit exceeded" })).toBe(false); + expect(isContextOverflow({ message: "internal server error" })).toBe(false); + expect(isContextOverflow({ name: "TimeoutError" })).toBe(false); + }); + + test("returns false for null/undefined", () => { + expect(isContextOverflow(null)).toBe(false); + expect(isContextOverflow(undefined)).toBe(false); + }); +}); + +describe("buildRecoveryMessage", () => { + test("includes distilled history when summaries exist", () => { + const msg = buildRecoveryMessage([ + { observations: "Fixed the bug in auth.ts", generation: 0 }, + ]); + expect(msg).toContain(""); + expect(msg).toContain(""); + expect(msg).toContain("context overflow error"); + expect(msg).toContain("Fixed the bug in auth.ts"); + }); + + test("includes meta and recent sections from formatDistillations", () => { + const msg = buildRecoveryMessage([ + { observations: "Earlier consolidated work", generation: 1 }, + { observations: "Recent detailed work", generation: 0 }, + ]); + expect(msg).toContain("Earlier Work (summarized)"); + expect(msg).toContain("Recent Work (distilled)"); + expect(msg).toContain("Earlier consolidated work"); + expect(msg).toContain("Recent detailed work"); + }); + + test("shows fallback message when no summaries available", () => { + const msg = buildRecoveryMessage([]); + expect(msg).toContain(""); + expect(msg).toContain("No distilled history available"); + }); +}); diff --git a/packages/opencode/tsconfig.json b/packages/opencode/tsconfig.json new file mode 100644 index 0000000..e943d76 --- /dev/null +++ b/packages/opencode/tsconfig.json @@ -0,0 +1,7 @@ +{ + "extends": "../../tsconfig.base.json", + "compilerOptions": { + "noEmit": true + }, + "include": ["src", "test", "script", "scripts", "eval"] +} diff --git a/tsconfig.base.json b/tsconfig.base.json new file mode 100644 index 0000000..3df0b2b --- /dev/null +++ b/tsconfig.base.json @@ -0,0 +1,12 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "declaration": true, + "types": ["bun"] + } +} diff --git a/tsconfig.json b/tsconfig.json index fc0043c..f405837 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,14 @@ { + "extends": "./tsconfig.base.json", "compilerOptions": { - "target": "ESNext", - "module": "ESNext", - "moduleResolution": "bundler", - "strict": true, - "esModuleInterop": true, - "skipLibCheck": true, - "outDir": "dist", - "rootDir": "src", - "declaration": true, - "types": ["bun"] + "noEmit": true }, - "include": ["src"], - "exclude": ["node_modules", "dist", "test"] + "include": [ + "packages/*/src", + "packages/*/test", + "packages/*/script", + "packages/*/scripts", + "packages/*/eval" + ], + "exclude": ["node_modules", "**/dist"] }