diff --git a/packages/deja-local/README.md b/packages/deja-local/README.md new file mode 100644 index 0000000..590cc18 --- /dev/null +++ b/packages/deja-local/README.md @@ -0,0 +1,148 @@ +# deja-local + +Cross-session memory for AI agents. One function to remember, one function to recall. + +```ts +import { createMemory } from "deja-local"; + +const mem = await createMemory({ path: "./agent-memory.db" }); + +// Store a learning +await mem.remember("Always run migrations before deploying to staging"); + +// Recall relevant memories later, in any session +const results = await mem.recall("deploying to staging"); +// [{ text: "Always run migrations before deploying to staging", score: 0.82, confidence: 0.5 }] +``` + +## Why + +AI agents are amnesiacs. Every session starts from zero. Your agent figures out that the test suite needs `NODE_ENV=test` on Monday, then wastes 10 minutes rediscovering it on Tuesday. + +deja-local gives agents a durable memory that gets smarter over time. + +- **SQLite-backed** -- no external services, no API keys, no network +- **Real embeddings** -- all-MiniLM-L6-v2 via ONNX, runs on CPU +- **ACID durable** -- memory is persisted before `remember()` returns +- **Gets smarter** -- confirm/reject feedback makes good memories rise and bad ones fade + +## Install + +```bash +npm install deja-local +``` + +## API + +### `remember(text)` + +Store a memory. Deja handles dedup and conflict resolution automatically. + +```ts +await mem.remember("The Stripe webhook secret is in 1Password, not .env"); +await mem.remember("Use pnpm, not npm -- the lockfile breaks otherwise"); +await mem.remember("Redis must be running before starting the API server"); +``` + +If a new memory contradicts an existing one, Deja detects the conflict and supersedes it: + +```ts +await mem.remember("Deploy target is us-east-1"); +// ... weeks later ... +await mem.remember("Deploy target moved to eu-west-1"); +// Old memory is superseded -- its confidence drops, new one takes priority +``` + +Identical or near-identical memories are deduplicated at write time. + +### `recall(query, opts?)` + +Retrieve relevant memories, ranked by relevance and confidence. + +```ts +const results = await mem.recall("setting up the dev environment"); +// [ +// { text: "Redis must be running before starting the API server", score: 0.81, confidence: 0.7 }, +// { text: "Use pnpm, not npm -- the lockfile breaks otherwise", score: 0.74, confidence: 0.5 }, +// ] +``` + +Complex queries are automatically decomposed into sub-queries for broader recall: + +```ts +const results = await mem.recall("full deploy checklist for production"); +// Internally searches: deploy, checklist, production, deploy checklist, checklist production +// Returns merged, deduplicated results +``` + +Options: + +```ts +await mem.recall("deploy steps", { limit: 3 }); // max results +await mem.recall("deploy steps", { threshold: 0.5 }); // min relevance +await mem.recall("deploy steps", { minConfidence: 0.4 }); // skip low-confidence memories +``` + +### `confirm(id)` / `reject(id)` + +Give feedback on recalled memories. This is the ratchet -- memories that help get promoted, memories that mislead get demoted. + +```ts +const results = await mem.recall("database connection string format"); + +// This one was useful +await mem.confirm(results[0].id); + +// This one was outdated +await mem.reject(results[1].id); +``` + +Over time, high-signal memories surface first. Low-signal memories fade. + +### `forget(id)` + +Permanently remove a memory. + +```ts +await mem.forget(memory.id); +``` + +### `list(opts?)` / `recallLog(opts?)` + +Inspect stored memories and the audit trail. + +```ts +const all = mem.list(); // all memories, newest first +const recent = mem.list({ limit: 10 }); // paginated + +const log = mem.recallLog(); // what was recalled and when +``` + +## Configuration + +```ts +const mem = createMemory({ + path: "./memory.db", // required -- SQLite file path + model: "Xenova/all-MiniLM-L6-v2", // HuggingFace model (default) + embed: customEmbedFn, // or bring your own embed function + threshold: 0.3, // min similarity for recall (default 0.3) + dedupeThreshold: 0.95, // similarity to consider duplicate (default 0.95) + conflictThreshold: 0.6, // similarity to detect conflict (default 0.6) +}); +``` + +## How it works + +**Dedup:** At write time, if a new memory's embedding is >= 0.95 similar to an existing one, it's a duplicate and skipped. + +**Conflict resolution:** If similarity is between 0.6 and 0.95, the memories are about the same topic but say different things. The new memory supersedes the old one -- the old memory's confidence drops to 30% of its current value, so it naturally sinks in recall rankings. + +**The ratchet:** `confirm()` boosts confidence by 0.1, `reject()` drops it by 0.15. Recall ranks by `relevance * 0.7 + confidence * 0.3`. Over thousands of memories, this is the difference between useful recall and noise. + +**Recall decomposition:** Complex queries are split into keyword pairs and individual terms. Each sub-query is embedded and scored independently. The best score per memory wins. This catches memories that match part of your intent even if they don't match the full query. + +**Audit trail:** Every `recall()` is logged with the query, matched memory IDs, scores, and timestamp. Inspect with `recallLog()`. + +## License + +MIT diff --git a/packages/deja-local/bun.lock b/packages/deja-local/bun.lock new file mode 100644 index 0000000..dfbcc13 --- /dev/null +++ b/packages/deja-local/bun.lock @@ -0,0 +1,376 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "deja-local", + "dependencies": { + "@huggingface/transformers": "^3.8.1", + }, + "devDependencies": { + "@types/bun": "latest", + "tsup": "^8.0.0", + "typescript": "^5.4.0", + }, + }, + }, + "packages": { + "@emnapi/runtime": ["@emnapi/runtime@1.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw=="], + + "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.27.4", "", { "os": "aix", "cpu": "ppc64" }, "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q=="], + + "@esbuild/android-arm": ["@esbuild/android-arm@0.27.4", "", { "os": "android", "cpu": "arm" }, "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ=="], + + "@esbuild/android-arm64": ["@esbuild/android-arm64@0.27.4", "", { "os": "android", "cpu": "arm64" }, "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw=="], + + "@esbuild/android-x64": ["@esbuild/android-x64@0.27.4", "", { "os": "android", "cpu": "x64" }, "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw=="], + + "@esbuild/darwin-arm64": ["@esbuild/darwin-arm64@0.27.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ=="], + + "@esbuild/darwin-x64": ["@esbuild/darwin-x64@0.27.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw=="], + + "@esbuild/freebsd-arm64": ["@esbuild/freebsd-arm64@0.27.4", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw=="], + + "@esbuild/freebsd-x64": ["@esbuild/freebsd-x64@0.27.4", "", { "os": "freebsd", "cpu": "x64" }, "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ=="], + + "@esbuild/linux-arm": ["@esbuild/linux-arm@0.27.4", "", { "os": "linux", "cpu": "arm" }, "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg=="], + + "@esbuild/linux-arm64": ["@esbuild/linux-arm64@0.27.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA=="], + + "@esbuild/linux-ia32": ["@esbuild/linux-ia32@0.27.4", "", { "os": "linux", "cpu": "ia32" }, "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA=="], + + "@esbuild/linux-loong64": ["@esbuild/linux-loong64@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA=="], + + "@esbuild/linux-mips64el": ["@esbuild/linux-mips64el@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw=="], + + "@esbuild/linux-ppc64": ["@esbuild/linux-ppc64@0.27.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA=="], + + "@esbuild/linux-riscv64": ["@esbuild/linux-riscv64@0.27.4", "", { "os": "linux", "cpu": "none" }, "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw=="], + + "@esbuild/linux-s390x": ["@esbuild/linux-s390x@0.27.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA=="], + + "@esbuild/linux-x64": ["@esbuild/linux-x64@0.27.4", "", { "os": "linux", "cpu": "x64" }, "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA=="], + + "@esbuild/netbsd-arm64": ["@esbuild/netbsd-arm64@0.27.4", "", { "os": "none", "cpu": "arm64" }, "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q=="], + + "@esbuild/netbsd-x64": ["@esbuild/netbsd-x64@0.27.4", "", { "os": "none", "cpu": "x64" }, "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg=="], + + "@esbuild/openbsd-arm64": ["@esbuild/openbsd-arm64@0.27.4", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow=="], + + "@esbuild/openbsd-x64": ["@esbuild/openbsd-x64@0.27.4", "", { "os": "openbsd", "cpu": "x64" }, "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ=="], + + "@esbuild/openharmony-arm64": ["@esbuild/openharmony-arm64@0.27.4", "", { "os": "none", "cpu": "arm64" }, "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg=="], + + "@esbuild/sunos-x64": ["@esbuild/sunos-x64@0.27.4", "", { "os": "sunos", "cpu": "x64" }, "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g=="], + + "@esbuild/win32-arm64": ["@esbuild/win32-arm64@0.27.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg=="], + + "@esbuild/win32-ia32": ["@esbuild/win32-ia32@0.27.4", "", { "os": "win32", "cpu": "ia32" }, "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw=="], + + "@esbuild/win32-x64": ["@esbuild/win32-x64@0.27.4", "", { "os": "win32", "cpu": "x64" }, "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg=="], + + "@huggingface/jinja": ["@huggingface/jinja@0.5.6", "", {}, "sha512-MyMWyLnjqo+KRJYSH7oWNbsOn5onuIvfXYPcc0WOGxU0eHUV7oAYUoQTl2BMdu7ml+ea/bu11UM+EshbeHwtIA=="], + + "@huggingface/transformers": ["@huggingface/transformers@3.8.1", "", { "dependencies": { "@huggingface/jinja": "^0.5.3", "onnxruntime-node": "1.21.0", "onnxruntime-web": "1.22.0-dev.20250409-89f8206ba4", "sharp": "^0.34.1" } }, "sha512-tsTk4zVjImqdqjS8/AOZg2yNLd1z9S5v+7oUPpXaasDRwEDhB+xnglK1k5cad26lL5/ZIaeREgWWy0bs9y9pPA=="], + + "@img/colour": ["@img/colour@1.1.0", "", {}, "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ=="], + + "@img/sharp-darwin-arm64": ["@img/sharp-darwin-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" }, "os": "darwin", "cpu": "arm64" }, "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w=="], + + "@img/sharp-darwin-x64": ["@img/sharp-darwin-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" }, "os": "darwin", "cpu": "x64" }, "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw=="], + + "@img/sharp-libvips-darwin-arm64": ["@img/sharp-libvips-darwin-arm64@1.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g=="], + + "@img/sharp-libvips-darwin-x64": ["@img/sharp-libvips-darwin-x64@1.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg=="], + + "@img/sharp-libvips-linux-arm": ["@img/sharp-libvips-linux-arm@1.2.4", "", { "os": "linux", "cpu": "arm" }, "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A=="], + + "@img/sharp-libvips-linux-arm64": ["@img/sharp-libvips-linux-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw=="], + + "@img/sharp-libvips-linux-ppc64": ["@img/sharp-libvips-linux-ppc64@1.2.4", "", { "os": "linux", "cpu": "ppc64" }, "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA=="], + + "@img/sharp-libvips-linux-riscv64": ["@img/sharp-libvips-linux-riscv64@1.2.4", "", { "os": "linux", "cpu": "none" }, "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA=="], + + "@img/sharp-libvips-linux-s390x": ["@img/sharp-libvips-linux-s390x@1.2.4", "", { "os": "linux", "cpu": "s390x" }, "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ=="], + + "@img/sharp-libvips-linux-x64": ["@img/sharp-libvips-linux-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw=="], + + "@img/sharp-libvips-linuxmusl-arm64": ["@img/sharp-libvips-linuxmusl-arm64@1.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw=="], + + "@img/sharp-libvips-linuxmusl-x64": ["@img/sharp-libvips-linuxmusl-x64@1.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg=="], + + "@img/sharp-linux-arm": ["@img/sharp-linux-arm@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" }, "os": "linux", "cpu": "arm" }, "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw=="], + + "@img/sharp-linux-arm64": ["@img/sharp-linux-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg=="], + + "@img/sharp-linux-ppc64": ["@img/sharp-linux-ppc64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" }, "os": "linux", "cpu": "ppc64" }, "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA=="], + + "@img/sharp-linux-riscv64": ["@img/sharp-linux-riscv64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" }, "os": "linux", "cpu": "none" }, "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw=="], + + "@img/sharp-linux-s390x": ["@img/sharp-linux-s390x@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" }, "os": "linux", "cpu": "s390x" }, "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg=="], + + "@img/sharp-linux-x64": ["@img/sharp-linux-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ=="], + + "@img/sharp-linuxmusl-arm64": ["@img/sharp-linuxmusl-arm64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" }, "os": "linux", "cpu": "arm64" }, "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg=="], + + "@img/sharp-linuxmusl-x64": ["@img/sharp-linuxmusl-x64@0.34.5", "", { "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" }, "os": "linux", "cpu": "x64" }, "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q=="], + + "@img/sharp-wasm32": ["@img/sharp-wasm32@0.34.5", "", { "dependencies": { "@emnapi/runtime": "^1.7.0" }, "cpu": "none" }, "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw=="], + + "@img/sharp-win32-arm64": ["@img/sharp-win32-arm64@0.34.5", "", { "os": "win32", "cpu": "arm64" }, "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g=="], + + "@img/sharp-win32-ia32": ["@img/sharp-win32-ia32@0.34.5", "", { "os": "win32", "cpu": "ia32" }, "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg=="], + + "@img/sharp-win32-x64": ["@img/sharp-win32-x64@0.34.5", "", { "os": "win32", "cpu": "x64" }, "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw=="], + + "@isaacs/fs-minipass": ["@isaacs/fs-minipass@4.0.1", "", { "dependencies": { "minipass": "^7.0.4" } }, "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w=="], + + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], + + "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], + + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], + + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], + + "@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="], + + "@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="], + + "@protobufjs/codegen": ["@protobufjs/codegen@2.0.4", "", {}, "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="], + + "@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.0", "", {}, "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="], + + "@protobufjs/fetch": ["@protobufjs/fetch@1.1.0", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ=="], + + "@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="], + + "@protobufjs/inquire": ["@protobufjs/inquire@1.1.0", "", {}, "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="], + + "@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="], + + "@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="], + + "@protobufjs/utf8": ["@protobufjs/utf8@1.1.0", "", {}, "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="], + + "@rollup/rollup-android-arm-eabi": ["@rollup/rollup-android-arm-eabi@4.59.0", "", { "os": "android", "cpu": "arm" }, "sha512-upnNBkA6ZH2VKGcBj9Fyl9IGNPULcjXRlg0LLeaioQWueH30p6IXtJEbKAgvyv+mJaMxSm1l6xwDXYjpEMiLMg=="], + + "@rollup/rollup-android-arm64": ["@rollup/rollup-android-arm64@4.59.0", "", { "os": "android", "cpu": "arm64" }, "sha512-hZ+Zxj3SySm4A/DylsDKZAeVg0mvi++0PYVceVyX7hemkw7OreKdCvW2oQ3T1FMZvCaQXqOTHb8qmBShoqk69Q=="], + + "@rollup/rollup-darwin-arm64": ["@rollup/rollup-darwin-arm64@4.59.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-W2Psnbh1J8ZJw0xKAd8zdNgF9HRLkdWwwdWqubSVk0pUuQkoHnv7rx4GiF9rT4t5DIZGAsConRE3AxCdJ4m8rg=="], + + "@rollup/rollup-darwin-x64": ["@rollup/rollup-darwin-x64@4.59.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-ZW2KkwlS4lwTv7ZVsYDiARfFCnSGhzYPdiOU4IM2fDbL+QGlyAbjgSFuqNRbSthybLbIJ915UtZBtmuLrQAT/w=="], + + "@rollup/rollup-freebsd-arm64": ["@rollup/rollup-freebsd-arm64@4.59.0", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-EsKaJ5ytAu9jI3lonzn3BgG8iRBjV4LxZexygcQbpiU0wU0ATxhNVEpXKfUa0pS05gTcSDMKpn3Sx+QB9RlTTA=="], + + "@rollup/rollup-freebsd-x64": ["@rollup/rollup-freebsd-x64@4.59.0", "", { "os": "freebsd", "cpu": "x64" }, "sha512-d3DuZi2KzTMjImrxoHIAODUZYoUUMsuUiY4SRRcJy6NJoZ6iIqWnJu9IScV9jXysyGMVuW+KNzZvBLOcpdl3Vg=="], + + "@rollup/rollup-linux-arm-gnueabihf": ["@rollup/rollup-linux-arm-gnueabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-t4ONHboXi/3E0rT6OZl1pKbl2Vgxf9vJfWgmUoCEVQVxhW6Cw/c8I6hbbu7DAvgp82RKiH7TpLwxnJeKv2pbsw=="], + + "@rollup/rollup-linux-arm-musleabihf": ["@rollup/rollup-linux-arm-musleabihf@4.59.0", "", { "os": "linux", "cpu": "arm" }, "sha512-CikFT7aYPA2ufMD086cVORBYGHffBo4K8MQ4uPS/ZnY54GKj36i196u8U+aDVT2LX4eSMbyHtyOh7D7Zvk2VvA=="], + + "@rollup/rollup-linux-arm64-gnu": ["@rollup/rollup-linux-arm64-gnu@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-jYgUGk5aLd1nUb1CtQ8E+t5JhLc9x5WdBKew9ZgAXg7DBk0ZHErLHdXM24rfX+bKrFe+Xp5YuJo54I5HFjGDAA=="], + + "@rollup/rollup-linux-arm64-musl": ["@rollup/rollup-linux-arm64-musl@4.59.0", "", { "os": "linux", "cpu": "arm64" }, "sha512-peZRVEdnFWZ5Bh2KeumKG9ty7aCXzzEsHShOZEFiCQlDEepP1dpUl/SrUNXNg13UmZl+gzVDPsiCwnV1uI0RUA=="], + + "@rollup/rollup-linux-loong64-gnu": ["@rollup/rollup-linux-loong64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-gbUSW/97f7+r4gHy3Jlup8zDG190AuodsWnNiXErp9mT90iCy9NKKU0Xwx5k8VlRAIV2uU9CsMnEFg/xXaOfXg=="], + + "@rollup/rollup-linux-loong64-musl": ["@rollup/rollup-linux-loong64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-yTRONe79E+o0FWFijasoTjtzG9EBedFXJMl888NBEDCDV9I2wGbFFfJQQe63OijbFCUZqxpHz1GzpbtSFikJ4Q=="], + + "@rollup/rollup-linux-ppc64-gnu": ["@rollup/rollup-linux-ppc64-gnu@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-sw1o3tfyk12k3OEpRddF68a1unZ5VCN7zoTNtSn2KndUE+ea3m3ROOKRCZxEpmT9nsGnogpFP9x6mnLTCaoLkA=="], + + "@rollup/rollup-linux-ppc64-musl": ["@rollup/rollup-linux-ppc64-musl@4.59.0", "", { "os": "linux", "cpu": "ppc64" }, "sha512-+2kLtQ4xT3AiIxkzFVFXfsmlZiG5FXYW7ZyIIvGA7Bdeuh9Z0aN4hVyXS/G1E9bTP/vqszNIN/pUKCk/BTHsKA=="], + + "@rollup/rollup-linux-riscv64-gnu": ["@rollup/rollup-linux-riscv64-gnu@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-NDYMpsXYJJaj+I7UdwIuHHNxXZ/b/N2hR15NyH3m2qAtb/hHPA4g4SuuvrdxetTdndfj9b1WOmy73kcPRoERUg=="], + + "@rollup/rollup-linux-riscv64-musl": ["@rollup/rollup-linux-riscv64-musl@4.59.0", "", { "os": "linux", "cpu": "none" }, "sha512-nLckB8WOqHIf1bhymk+oHxvM9D3tyPndZH8i8+35p/1YiVoVswPid2yLzgX7ZJP0KQvnkhM4H6QZ5m0LzbyIAg=="], + + "@rollup/rollup-linux-s390x-gnu": ["@rollup/rollup-linux-s390x-gnu@4.59.0", "", { "os": "linux", "cpu": "s390x" }, "sha512-oF87Ie3uAIvORFBpwnCvUzdeYUqi2wY6jRFWJAy1qus/udHFYIkplYRW+wo+GRUP4sKzYdmE1Y3+rY5Gc4ZO+w=="], + + "@rollup/rollup-linux-x64-gnu": ["@rollup/rollup-linux-x64-gnu@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-3AHmtQq/ppNuUspKAlvA8HtLybkDflkMuLK4DPo77DfthRb71V84/c4MlWJXixZz4uruIH4uaa07IqoAkG64fg=="], + + "@rollup/rollup-linux-x64-musl": ["@rollup/rollup-linux-x64-musl@4.59.0", "", { "os": "linux", "cpu": "x64" }, "sha512-2UdiwS/9cTAx7qIUZB/fWtToJwvt0Vbo0zmnYt7ED35KPg13Q0ym1g442THLC7VyI6JfYTP4PiSOWyoMdV2/xg=="], + + "@rollup/rollup-openbsd-x64": ["@rollup/rollup-openbsd-x64@4.59.0", "", { "os": "openbsd", "cpu": "x64" }, "sha512-M3bLRAVk6GOwFlPTIxVBSYKUaqfLrn8l0psKinkCFxl4lQvOSz8ZrKDz2gxcBwHFpci0B6rttydI4IpS4IS/jQ=="], + + "@rollup/rollup-openharmony-arm64": ["@rollup/rollup-openharmony-arm64@4.59.0", "", { "os": "none", "cpu": "arm64" }, "sha512-tt9KBJqaqp5i5HUZzoafHZX8b5Q2Fe7UjYERADll83O4fGqJ49O1FsL6LpdzVFQcpwvnyd0i+K/VSwu/o/nWlA=="], + + "@rollup/rollup-win32-arm64-msvc": ["@rollup/rollup-win32-arm64-msvc@4.59.0", "", { "os": "win32", "cpu": "arm64" }, "sha512-V5B6mG7OrGTwnxaNUzZTDTjDS7F75PO1ae6MJYdiMu60sq0CqN5CVeVsbhPxalupvTX8gXVSU9gq+Rx1/hvu6A=="], + + "@rollup/rollup-win32-ia32-msvc": ["@rollup/rollup-win32-ia32-msvc@4.59.0", "", { "os": "win32", "cpu": "ia32" }, "sha512-UKFMHPuM9R0iBegwzKF4y0C4J9u8C6MEJgFuXTBerMk7EJ92GFVFYBfOZaSGLu6COf7FxpQNqhNS4c4icUPqxA=="], + + "@rollup/rollup-win32-x64-gnu": ["@rollup/rollup-win32-x64-gnu@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-laBkYlSS1n2L8fSo1thDNGrCTQMmxjYY5G0WFWjFFYZkKPjsMBsgJfGf4TLxXrF6RyhI60L8TMOjBMvXiTcxeA=="], + + "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.59.0", "", { "os": "win32", "cpu": "x64" }, "sha512-2HRCml6OztYXyJXAvdDXPKcawukWY2GpR5/nxKp4iBgiO3wcoEGkAaqctIbZcNB6KlUQBIqt8VYkNSj2397EfA=="], + + "@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="], + + "@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + + "@types/node": ["@types/node@25.5.0", "", { "dependencies": { "undici-types": "~7.18.0" } }, "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw=="], + + "acorn": ["acorn@8.16.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw=="], + + "any-promise": ["any-promise@1.3.0", "", {}, "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A=="], + + "boolean": ["boolean@3.2.0", "", {}, "sha512-d0II/GO9uf9lfUHH2BQsjxzRJZBdsjgsBiW4BvhWk/3qoKwQFjIDVN19PfX8F2D/r9PCMTtLWjYVCFrpeYUzsw=="], + + "bun-types": ["bun-types@1.3.11", "", { "dependencies": { "@types/node": "*" } }, "sha512-1KGPpoxQWl9f6wcZh57LvrPIInQMn2TQ7jsgxqpRzg+l0QPOFvJVH7HmvHo/AiPgwXy+/Thf6Ov3EdVn1vOabg=="], + + "bundle-require": ["bundle-require@5.1.0", "", { "dependencies": { "load-tsconfig": "^0.2.3" }, "peerDependencies": { "esbuild": ">=0.18" } }, "sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA=="], + + "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], + + "chokidar": ["chokidar@4.0.3", "", { "dependencies": { "readdirp": "^4.0.1" } }, "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA=="], + + "chownr": ["chownr@3.0.0", "", {}, "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g=="], + + "commander": ["commander@4.1.1", "", {}, "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA=="], + + "confbox": ["confbox@0.1.8", "", {}, "sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w=="], + + "consola": ["consola@3.4.2", "", {}, "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA=="], + + "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], + + "define-data-property": ["define-data-property@1.1.4", "", { "dependencies": { "es-define-property": "^1.0.0", "es-errors": "^1.3.0", "gopd": "^1.0.1" } }, "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A=="], + + "define-properties": ["define-properties@1.2.1", "", { "dependencies": { "define-data-property": "^1.0.1", "has-property-descriptors": "^1.0.0", "object-keys": "^1.1.1" } }, "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg=="], + + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], + + "detect-node": ["detect-node@2.1.0", "", {}, "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="], + + "es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="], + + "es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="], + + "es6-error": ["es6-error@4.1.1", "", {}, "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="], + + "esbuild": ["esbuild@0.27.4", "", { "optionalDependencies": { "@esbuild/aix-ppc64": "0.27.4", "@esbuild/android-arm": "0.27.4", "@esbuild/android-arm64": "0.27.4", "@esbuild/android-x64": "0.27.4", "@esbuild/darwin-arm64": "0.27.4", "@esbuild/darwin-x64": "0.27.4", "@esbuild/freebsd-arm64": "0.27.4", "@esbuild/freebsd-x64": "0.27.4", "@esbuild/linux-arm": "0.27.4", "@esbuild/linux-arm64": "0.27.4", "@esbuild/linux-ia32": "0.27.4", "@esbuild/linux-loong64": "0.27.4", "@esbuild/linux-mips64el": "0.27.4", "@esbuild/linux-ppc64": "0.27.4", "@esbuild/linux-riscv64": "0.27.4", "@esbuild/linux-s390x": "0.27.4", "@esbuild/linux-x64": "0.27.4", "@esbuild/netbsd-arm64": "0.27.4", "@esbuild/netbsd-x64": "0.27.4", "@esbuild/openbsd-arm64": "0.27.4", "@esbuild/openbsd-x64": "0.27.4", "@esbuild/openharmony-arm64": "0.27.4", "@esbuild/sunos-x64": "0.27.4", "@esbuild/win32-arm64": "0.27.4", "@esbuild/win32-ia32": "0.27.4", "@esbuild/win32-x64": "0.27.4" }, "bin": { "esbuild": "bin/esbuild" } }, "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ=="], + + "escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], + + "fdir": ["fdir@6.5.0", "", { "peerDependencies": { "picomatch": "^3 || ^4" }, "optionalPeers": ["picomatch"] }, "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg=="], + + "fix-dts-default-cjs-exports": ["fix-dts-default-cjs-exports@1.0.1", "", { "dependencies": { "magic-string": "^0.30.17", "mlly": "^1.7.4", "rollup": "^4.34.8" } }, "sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg=="], + + "flatbuffers": ["flatbuffers@25.9.23", "", {}, "sha512-MI1qs7Lo4Syw0EOzUl0xjs2lsoeqFku44KpngfIduHBYvzm8h2+7K8YMQh1JtVVVrUvhLpNwqVi4DERegUJhPQ=="], + + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], + + "global-agent": ["global-agent@3.0.0", "", { "dependencies": { "boolean": "^3.0.1", "es6-error": "^4.1.1", "matcher": "^3.0.0", "roarr": "^2.15.3", "semver": "^7.3.2", "serialize-error": "^7.0.1" } }, "sha512-PT6XReJ+D07JvGoxQMkT6qji/jVNfX/h364XHZOWeRzy64sSFr+xJ5OX7LI3b4MPQzdL4H8Y8M0xzPpsVMwA8Q=="], + + "globalthis": ["globalthis@1.0.4", "", { "dependencies": { "define-properties": "^1.2.1", "gopd": "^1.0.1" } }, "sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ=="], + + "gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="], + + "guid-typescript": ["guid-typescript@1.0.9", "", {}, "sha512-Y8T4vYhEfwJOTbouREvG+3XDsjr8E3kIr7uf+JZ0BYloFsttiHU0WfvANVsR7TxNUJa/WpCnw/Ino/p+DeBhBQ=="], + + "has-property-descriptors": ["has-property-descriptors@1.0.2", "", { "dependencies": { "es-define-property": "^1.0.0" } }, "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg=="], + + "joycon": ["joycon@3.1.1", "", {}, "sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw=="], + + "json-stringify-safe": ["json-stringify-safe@5.0.1", "", {}, "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA=="], + + "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], + + "lines-and-columns": ["lines-and-columns@1.2.4", "", {}, "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg=="], + + "load-tsconfig": ["load-tsconfig@0.2.5", "", {}, "sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg=="], + + "long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="], + + "magic-string": ["magic-string@0.30.21", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ=="], + + "matcher": ["matcher@3.0.0", "", { "dependencies": { "escape-string-regexp": "^4.0.0" } }, "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng=="], + + "minipass": ["minipass@7.1.3", "", {}, "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A=="], + + "minizlib": ["minizlib@3.1.0", "", { "dependencies": { "minipass": "^7.1.2" } }, "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw=="], + + "mlly": ["mlly@1.8.1", "", { "dependencies": { "acorn": "^8.16.0", "pathe": "^2.0.3", "pkg-types": "^1.3.1", "ufo": "^1.6.3" } }, "sha512-SnL6sNutTwRWWR/vcmCYHSADjiEesp5TGQQ0pXyLhW5IoeibRlF/CbSLailbB3CNqJUk9cVJ9dUDnbD7GrcHBQ=="], + + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], + + "mz": ["mz@2.7.0", "", { "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q=="], + + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-keys": ["object-keys@1.1.1", "", {}, "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="], + + "onnxruntime-common": ["onnxruntime-common@1.21.0", "", {}, "sha512-Q632iLLrtCAVOTO65dh2+mNbQir/QNTVBG3h/QdZBpns7mZ0RYbLRBgGABPbpU9351AgYy7SJf1WaeVwMrBFPQ=="], + + "onnxruntime-node": ["onnxruntime-node@1.21.0", "", { "dependencies": { "global-agent": "^3.0.0", "onnxruntime-common": "1.21.0", "tar": "^7.0.1" }, "os": [ "linux", "win32", "darwin", ] }, "sha512-NeaCX6WW2L8cRCSqy3bInlo5ojjQqu2fD3D+9W5qb5irwxhEyWKXeH2vZ8W9r6VxaMPUan+4/7NDwZMtouZxEw=="], + + "onnxruntime-web": ["onnxruntime-web@1.22.0-dev.20250409-89f8206ba4", "", { "dependencies": { "flatbuffers": "^25.1.24", "guid-typescript": "^1.0.9", "long": "^5.2.3", "onnxruntime-common": "1.22.0-dev.20250409-89f8206ba4", "platform": "^1.3.6", "protobufjs": "^7.2.4" } }, "sha512-0uS76OPgH0hWCPrFKlL8kYVV7ckM7t/36HfbgoFw6Nd0CZVVbQC4PkrR8mBX8LtNUFZO25IQBqV2Hx2ho3FlbQ=="], + + "pathe": ["pathe@2.0.3", "", {}, "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w=="], + + "picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="], + + "picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], + + "pirates": ["pirates@4.0.7", "", {}, "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA=="], + + "pkg-types": ["pkg-types@1.3.1", "", { "dependencies": { "confbox": "^0.1.8", "mlly": "^1.7.4", "pathe": "^2.0.1" } }, "sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ=="], + + "platform": ["platform@1.3.6", "", {}, "sha512-fnWVljUchTro6RiCFvCXBbNhJc2NijN7oIQxbwsyL0buWJPG85v81ehlHI9fXrJsMNgTofEoWIQeClKpgxFLrg=="], + + "postcss-load-config": ["postcss-load-config@6.0.1", "", { "dependencies": { "lilconfig": "^3.1.1" }, "peerDependencies": { "jiti": ">=1.21.0", "postcss": ">=8.0.9", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["jiti", "postcss", "tsx", "yaml"] }, "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g=="], + + "protobufjs": ["protobufjs@7.5.4", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" } }, "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg=="], + + "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], + + "resolve-from": ["resolve-from@5.0.0", "", {}, "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw=="], + + "roarr": ["roarr@2.15.4", "", { "dependencies": { "boolean": "^3.0.1", "detect-node": "^2.0.4", "globalthis": "^1.0.1", "json-stringify-safe": "^5.0.1", "semver-compare": "^1.0.0", "sprintf-js": "^1.1.2" } }, "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="], + + "rollup": ["rollup@4.59.0", "", { "dependencies": { "@types/estree": "1.0.8" }, "optionalDependencies": { "@rollup/rollup-android-arm-eabi": "4.59.0", "@rollup/rollup-android-arm64": "4.59.0", "@rollup/rollup-darwin-arm64": "4.59.0", "@rollup/rollup-darwin-x64": "4.59.0", "@rollup/rollup-freebsd-arm64": "4.59.0", "@rollup/rollup-freebsd-x64": "4.59.0", "@rollup/rollup-linux-arm-gnueabihf": "4.59.0", "@rollup/rollup-linux-arm-musleabihf": "4.59.0", "@rollup/rollup-linux-arm64-gnu": "4.59.0", "@rollup/rollup-linux-arm64-musl": "4.59.0", "@rollup/rollup-linux-loong64-gnu": "4.59.0", "@rollup/rollup-linux-loong64-musl": "4.59.0", "@rollup/rollup-linux-ppc64-gnu": "4.59.0", "@rollup/rollup-linux-ppc64-musl": "4.59.0", "@rollup/rollup-linux-riscv64-gnu": "4.59.0", "@rollup/rollup-linux-riscv64-musl": "4.59.0", "@rollup/rollup-linux-s390x-gnu": "4.59.0", "@rollup/rollup-linux-x64-gnu": "4.59.0", "@rollup/rollup-linux-x64-musl": "4.59.0", "@rollup/rollup-openbsd-x64": "4.59.0", "@rollup/rollup-openharmony-arm64": "4.59.0", "@rollup/rollup-win32-arm64-msvc": "4.59.0", "@rollup/rollup-win32-ia32-msvc": "4.59.0", "@rollup/rollup-win32-x64-gnu": "4.59.0", "@rollup/rollup-win32-x64-msvc": "4.59.0", "fsevents": "~2.3.2" }, "bin": { "rollup": "dist/bin/rollup" } }, "sha512-2oMpl67a3zCH9H79LeMcbDhXW/UmWG/y2zuqnF2jQq5uq9TbM9TVyXvA4+t+ne2IIkBdrLpAaRQAvo7YI/Yyeg=="], + + "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="], + + "serialize-error": ["serialize-error@7.0.1", "", { "dependencies": { "type-fest": "^0.13.1" } }, "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw=="], + + "sharp": ["sharp@0.34.5", "", { "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg=="], + + "source-map": ["source-map@0.7.6", "", {}, "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ=="], + + "sprintf-js": ["sprintf-js@1.1.3", "", {}, "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA=="], + + "sucrase": ["sucrase@3.35.1", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.2", "commander": "^4.0.0", "lines-and-columns": "^1.1.6", "mz": "^2.7.0", "pirates": "^4.0.1", "tinyglobby": "^0.2.11", "ts-interface-checker": "^0.1.9" }, "bin": { "sucrase": "bin/sucrase", "sucrase-node": "bin/sucrase-node" } }, "sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw=="], + + "tar": ["tar@7.5.11", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ=="], + + "thenify": ["thenify@3.3.1", "", { "dependencies": { "any-promise": "^1.0.0" } }, "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw=="], + + "thenify-all": ["thenify-all@1.6.0", "", { "dependencies": { "thenify": ">= 3.1.0 < 4" } }, "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA=="], + + "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], + + "tinyglobby": ["tinyglobby@0.2.15", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" } }, "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ=="], + + "tree-kill": ["tree-kill@1.2.2", "", { "bin": { "tree-kill": "cli.js" } }, "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A=="], + + "ts-interface-checker": ["ts-interface-checker@0.1.13", "", {}, "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA=="], + + "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], + + "tsup": ["tsup@8.5.1", "", { "dependencies": { "bundle-require": "^5.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "consola": "^3.4.0", "debug": "^4.4.0", "esbuild": "^0.27.0", "fix-dts-default-cjs-exports": "^1.0.0", "joycon": "^3.1.1", "picocolors": "^1.1.1", "postcss-load-config": "^6.0.1", "resolve-from": "^5.0.0", "rollup": "^4.34.8", "source-map": "^0.7.6", "sucrase": "^3.35.0", "tinyexec": "^0.3.2", "tinyglobby": "^0.2.11", "tree-kill": "^1.2.2" }, "peerDependencies": { "@microsoft/api-extractor": "^7.36.0", "@swc/core": "^1", "postcss": "^8.4.12", "typescript": ">=4.5.0" }, "optionalPeers": ["@microsoft/api-extractor", "@swc/core", "postcss", "typescript"], "bin": { "tsup": "dist/cli-default.js", "tsup-node": "dist/cli-node.js" } }, "sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing=="], + + "type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="], + + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], + + "ufo": ["ufo@1.6.3", "", {}, "sha512-yDJTmhydvl5lJzBmy/hyOAA0d+aqCBuwl818haVdYCRrWV84o7YyeVm4QlVHStqNrrJSTb6jKuFAVqAFsr+K3Q=="], + + "undici-types": ["undici-types@7.18.2", "", {}, "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w=="], + + "yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], + + "onnxruntime-web/onnxruntime-common": ["onnxruntime-common@1.22.0-dev.20250409-89f8206ba4", "", {}, "sha512-vDJMkfCfb0b1A836rgHj+ORuZf4B4+cc2bASQtpeoJLueuFc5DuYwjIZUBrSvx/fO5IrLjLz+oTrB3pcGlhovQ=="], + } +} diff --git a/packages/deja-local/package.json b/packages/deja-local/package.json new file mode 100644 index 0000000..b09e7de --- /dev/null +++ b/packages/deja-local/package.json @@ -0,0 +1,47 @@ +{ + "name": "deja-local", + "version": "0.1.0", + "description": "Local in-process vector memory for agents. Zero latency, zero eventual consistency.", + "main": "dist/index.js", + "module": "dist/index.mjs", + "types": "dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.mjs", + "require": "./dist/index.js" + } + }, + "files": [ + "dist" + ], + "scripts": { + "build": "tsup src/index.ts --format cjs,esm --dts --clean", + "test": "bun test", + "lint": "tsc --noEmit", + "prepublishOnly": "bun run lint && bun run test && bun run build" + }, + "keywords": [ + "ai", + "agents", + "memory", + "vector", + "local", + "embedding", + "deja" + ], + "author": "", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/acoyfellow/deja" + }, + "devDependencies": { + "tsup": "^8.0.0", + "typescript": "^5.4.0", + "@types/bun": "latest" + }, + "dependencies": { + "@huggingface/transformers": "^3.8.1" + } +} diff --git a/packages/deja-local/src/index.ts b/packages/deja-local/src/index.ts new file mode 100644 index 0000000..5a35e76 --- /dev/null +++ b/packages/deja-local/src/index.ts @@ -0,0 +1,424 @@ +/** + * deja-local — Trusted vector memory for agents. + * + * SQLite-backed. Real embeddings. Audit trail. ACID durable. + * + * ```ts + * const mem = await createMemory({ path: './agent-memory.db' }) + * await mem.remember("check wrangler.toml before deploying") + * const results = await mem.recall("deploying to production") + * // results[0] = { id, text, score, confidence, createdAt } + * ``` + */ + +import { Database } from 'bun:sqlite' +import { pipeline, type FeatureExtractionPipeline } from '@huggingface/transformers' + +// ============================================================================ +// Types +// ============================================================================ + +export type EmbedFn = (text: string) => number[] | Promise + +export interface Memory { + id: string + text: string + confidence: number + createdAt: string + supersedes?: string +} + +export interface RecallResult { + id: string + text: string + score: number + confidence: number + createdAt: string +} + +export interface RecallLogEntry { + id: number + context: string + results: Array<{ memoryId: string; score: number }> + timestamp: string +} + +export interface MemoryStore { + /** Store a memory. Deduplicates and resolves conflicts automatically. */ + remember(text: string): Promise + + /** Find relevant memories. Decomposes complex queries for better recall. */ + recall(context: string, options?: { limit?: number; threshold?: number; minConfidence?: number }): Promise + + /** Signal that a recalled memory was useful. Boosts its confidence. */ + confirm(id: string): Promise + + /** Signal that a recalled memory was wrong or outdated. Drops its confidence. */ + reject(id: string): Promise + + /** Remove a memory by id. */ + forget(id: string): Promise + + /** All memories, newest first. */ + list(options?: { limit?: number; offset?: number }): Memory[] + + /** View the recall audit log. See what the agent recalled and when. */ + recallLog(options?: { limit?: number }): RecallLogEntry[] + + /** How many memories are stored. */ + readonly size: number + + /** Close the database connection. */ + close(): void + + // Backward compat + /** @deprecated Use remember() */ + learn(text: string): Promise +} + +export interface CreateMemoryOptions { + /** Path to SQLite database file. Required — memory is always durable. */ + path: string + /** Embedding function. Default: all-MiniLM-L6-v2 via ONNX (~23MB, cached locally). */ + embed?: EmbedFn + /** HuggingFace model ID. Default: 'Xenova/all-MiniLM-L6-v2' */ + model?: string + /** Minimum similarity score for recall. Default: 0.3 */ + threshold?: number + /** Similarity threshold for deduplication. Default: 0.95 */ + dedupeThreshold?: number + /** Similarity range for conflict detection. Default: [0.6, 0.95) */ + conflictThreshold?: number +} + +// ============================================================================ +// Embeddings +// ============================================================================ + +function createModelEmbed(modelId: string): EmbedFn { + let extractor: FeatureExtractionPipeline | null = null + return async (text: string): Promise => { + if (!extractor) { + // @ts-expect-error - pipeline() union type too complex for TS, runtime works fine + extractor = await pipeline('feature-extraction', modelId, { dtype: 'fp32' }) + } + const output = await extractor!(text, { pooling: 'mean', normalize: true }) + return Array.from(output.data as Float32Array) + } +} + +function cosine(a: number[], b: number[]): number { + let dot = 0, na = 0, nb = 0 + for (let i = 0; i < a.length; i++) { dot += a[i] * b[i]; na += a[i] * a[i]; nb += b[i] * b[i] } + const d = Math.sqrt(na) * Math.sqrt(nb) + return d === 0 ? 0 : dot / d +} + +// ============================================================================ +// Vector serialization — Float32Array ↔ Buffer +// ============================================================================ + +function vecToBuffer(vec: number[]): Buffer { + const f32 = new Float32Array(vec) + return Buffer.from(f32.buffer) +} + +function bufferToVec(buf: Buffer): number[] { + const f32 = new Float32Array(buf.buffer, buf.byteOffset, buf.byteLength / 4) + return Array.from(f32) +} + +// ============================================================================ +// Recall decomposition — extract meaningful sub-queries from a complex query +// ============================================================================ + +/** Stop words to filter out when extracting keywords */ +const STOP_WORDS = new Set([ + 'a', 'an', 'the', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', + 'of', 'with', 'by', 'from', 'is', 'it', 'as', 'be', 'was', 'are', + 'been', 'being', 'have', 'has', 'had', 'do', 'does', 'did', 'will', + 'would', 'could', 'should', 'may', 'might', 'can', 'this', 'that', + 'these', 'those', 'i', 'you', 'he', 'she', 'we', 'they', 'my', 'me', + 'what', 'how', 'when', 'where', 'which', 'who', 'all', 'about', + 'up', 'out', 'if', 'not', 'no', 'so', 'just', 'get', 'make', + 'full', 'before', 'after', 'every', 'any', 'some', +]) + +/** + * Extract keyword sub-queries from a complex query. + * Returns the original query plus sub-queries if the query is complex enough. + */ +function decomposeQuery(context: string): string[] { + const queries = [context] + + // Split into words, filter stop words, keep meaningful terms + const words = context.toLowerCase().replace(/[^a-z0-9\s-]/g, '').split(/\s+/) + const keywords = words.filter(w => w.length > 2 && !STOP_WORDS.has(w)) + + // Only decompose if there are enough distinct keywords + if (keywords.length >= 3) { + // Build 2-word phrases from adjacent keywords + for (let i = 0; i < keywords.length - 1; i++) { + queries.push(`${keywords[i]} ${keywords[i + 1]}`) + } + // Also add individual keywords as sub-queries + for (const kw of keywords) { + queries.push(kw) + } + } + + return queries +} + +// ============================================================================ +// Confidence scoring +// ============================================================================ + +const CONFIDENCE_DEFAULT = 0.5 +const CONFIDENCE_BOOST = 0.1 +const CONFIDENCE_DECAY = 0.15 +const CONFIDENCE_MIN = 0.01 +const CONFIDENCE_MAX = 1.0 + +function clampConfidence(c: number): number { + return Math.min(CONFIDENCE_MAX, Math.max(CONFIDENCE_MIN, Math.round(c * 1000) / 1000)) +} + +// ============================================================================ +// Schema +// ============================================================================ + +const SCHEMA = ` + CREATE TABLE IF NOT EXISTS memories ( + id TEXT PRIMARY KEY, + text TEXT NOT NULL, + embedding BLOB NOT NULL, + confidence REAL NOT NULL DEFAULT 0.5, + supersedes TEXT, + created_at TEXT NOT NULL + ); + + CREATE TABLE IF NOT EXISTS recall_log ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + context TEXT NOT NULL, + results TEXT NOT NULL, + timestamp TEXT NOT NULL + ); + + CREATE INDEX IF NOT EXISTS idx_memories_created ON memories(created_at); + CREATE INDEX IF NOT EXISTS idx_recall_log_ts ON recall_log(timestamp); +` + +// Migration: add confidence column if missing (for DBs created before this version) +function migrateSchema(db: Database) { + const cols = db.prepare("PRAGMA table_info(memories)").all() as Array<{ name: string }> + const colNames = new Set(cols.map(c => c.name)) + if (!colNames.has('confidence')) { + db.exec(`ALTER TABLE memories ADD COLUMN confidence REAL NOT NULL DEFAULT ${CONFIDENCE_DEFAULT}`) + } + if (!colNames.has('supersedes')) { + db.exec('ALTER TABLE memories ADD COLUMN supersedes TEXT') + } +} + +// ============================================================================ +// createMemory +// ============================================================================ + +export function createMemory(opts: CreateMemoryOptions): MemoryStore { + const embed = opts.embed ?? createModelEmbed(opts.model ?? 'Xenova/all-MiniLM-L6-v2') + const threshold = opts.threshold ?? 0.3 + const dedupeThreshold = opts.dedupeThreshold ?? 0.95 + const conflictThreshold = opts.conflictThreshold ?? 0.6 + + // Open database, enable WAL mode, create schema + const db = new Database(opts.path) + db.exec('PRAGMA journal_mode = WAL') + db.exec('PRAGMA synchronous = NORMAL') + db.exec(SCHEMA) + migrateSchema(db) + + // Prepared statements + const insertMemory = db.prepare( + 'INSERT INTO memories (id, text, embedding, confidence, supersedes, created_at) VALUES (?, ?, ?, ?, ?, ?)' + ) + const deleteMemory = db.prepare('DELETE FROM memories WHERE id = ?') + const updateConfidence = db.prepare('UPDATE memories SET confidence = ? WHERE id = ?') + const selectAll = db.prepare('SELECT id, text, embedding, confidence, supersedes, created_at FROM memories') + const countMemories = db.prepare('SELECT COUNT(*) as count FROM memories') + const insertRecall = db.prepare( + 'INSERT INTO recall_log (context, results, timestamp) VALUES (?, ?, ?)' + ) + + // In-memory vector index — loaded from DB on startup + interface IndexEntry { id: string; text: string; vec: number[]; confidence: number; supersedes?: string; createdAt: string } + const index: IndexEntry[] = [] + + // Load existing memories into index + for (const row of selectAll.all() as Array<{ id: string; text: string; embedding: Buffer; confidence: number; supersedes: string | null; created_at: string }>) { + index.push({ + id: row.id, + text: row.text, + vec: bufferToVec(row.embedding), + confidence: row.confidence, + supersedes: row.supersedes ?? undefined, + createdAt: row.created_at, + }) + } + + async function remember(text: string): Promise { + const vec = await embed(text) + + // Scan for dedup or conflict + let bestSimilarity = 0 + let bestEntry: IndexEntry | null = null + + for (const entry of index) { + const sim = cosine(vec, entry.vec) + if (sim > bestSimilarity) { + bestSimilarity = sim + bestEntry = entry + } + } + + // Dedup: near-identical memory exists, skip + if (bestEntry && bestSimilarity >= dedupeThreshold) { + return { id: bestEntry.id, text: bestEntry.text, confidence: bestEntry.confidence, createdAt: bestEntry.createdAt } + } + + // Conflict: same topic, different content — supersede the old memory + let supersedes: string | undefined + if (bestEntry && bestSimilarity >= conflictThreshold) { + supersedes = bestEntry.id + // Drop the old memory's confidence — it's been superseded + const newConf = clampConfidence(bestEntry.confidence * 0.3) + updateConfidence.run(newConf, bestEntry.id) + bestEntry.confidence = newConf + } + + const id = crypto.randomUUID() + const createdAt = new Date().toISOString() + const confidence = CONFIDENCE_DEFAULT + const buf = vecToBuffer(vec) + + insertMemory.run(id, text, buf, confidence, supersedes ?? null, createdAt) + index.push({ id, text, vec, confidence, supersedes, createdAt }) + + return { id, text, confidence, createdAt, supersedes } + } + + async function recall(context: string, options: { limit?: number; threshold?: number; minConfidence?: number } = {}): Promise { + const limit = options.limit ?? 5 + const min = options.threshold ?? threshold + const minConf = options.minConfidence ?? 0 + + // Decompose complex queries into sub-queries + const subQueries = decomposeQuery(context) + const subVecs = await Promise.all(subQueries.map(q => embed(q))) + + // Score each memory against all sub-queries, take best match + const scoreMap = new Map() + + for (const entry of index) { + if (entry.confidence < minConf) continue + + let bestScore = 0 + for (const qv of subVecs) { + const sim = cosine(qv, entry.vec) + if (sim > bestScore) bestScore = sim + } + + if (bestScore >= min) { + // Blend relevance with confidence: 70% relevance, 30% confidence + const blended = bestScore * 0.7 + entry.confidence * 0.3 + const existing = scoreMap.get(entry.id) + if (!existing || existing.score < blended) { + scoreMap.set(entry.id, { + id: entry.id, + text: entry.text, + score: Math.round(blended * 1000) / 1000, + confidence: entry.confidence, + createdAt: entry.createdAt, + }) + } + } + } + + const results = Array.from(scoreMap.values()) + results.sort((a, b) => b.score - a.score) + const topResults = results.slice(0, limit) + + // Audit: log every recall + const now = new Date().toISOString() + const logData = topResults.map(r => ({ memoryId: r.id, score: r.score })) + insertRecall.run(context, JSON.stringify(logData), now) + + return topResults + } + + const store: MemoryStore = { + get size() { return (countMemories.get() as { count: number }).count }, + + remember, + + recall, + + async confirm(id) { + const entry = index.find(e => e.id === id) + if (!entry) return false + entry.confidence = clampConfidence(entry.confidence + CONFIDENCE_BOOST) + updateConfidence.run(entry.confidence, id) + return true + }, + + async reject(id) { + const entry = index.find(e => e.id === id) + if (!entry) return false + entry.confidence = clampConfidence(entry.confidence - CONFIDENCE_DECAY) + updateConfidence.run(entry.confidence, id) + return true + }, + + async forget(id) { + const changes = deleteMemory.run(id).changes + if (changes > 0) { + const idx = index.findIndex(e => e.id === id) + if (idx >= 0) index.splice(idx, 1) + return true + } + return false + }, + + list(options = {}) { + const limit = options.limit ?? 1000 + const offset = options.offset ?? 0 + const rows = db.prepare( + 'SELECT id, text, confidence, supersedes, created_at FROM memories ORDER BY created_at DESC LIMIT ? OFFSET ?' + ).all(limit, offset) as Array<{ id: string; text: string; confidence: number; supersedes: string | null; created_at: string }> + return rows.map(r => ({ id: r.id, text: r.text, confidence: r.confidence, supersedes: r.supersedes ?? undefined, createdAt: r.created_at })) + }, + + recallLog(options = {}) { + const limit = options.limit ?? 50 + const rows = db.prepare( + 'SELECT id, context, results, timestamp FROM recall_log ORDER BY timestamp DESC LIMIT ?' + ).all(limit) as Array<{ id: number; context: string; results: string; timestamp: string }> + return rows.map(r => ({ + id: r.id, + context: r.context, + results: JSON.parse(r.results), + timestamp: r.timestamp, + })) + }, + + close() { db.close() }, + + // Backward compat + learn: remember, + } + + return store +} + +export { createModelEmbed } +export default createMemory diff --git a/packages/deja-local/test/index.test.ts b/packages/deja-local/test/index.test.ts new file mode 100644 index 0000000..c4513ae --- /dev/null +++ b/packages/deja-local/test/index.test.ts @@ -0,0 +1,544 @@ +import { describe, test, expect, afterEach } from 'bun:test' +import { createMemory, type MemoryStore } from '../src/index' +import { unlinkSync, existsSync } from 'fs' + +// All tests use a trivial embed function — fast, deterministic. +// The real model is tested separately. These tests verify TRUST, not embedding quality. +function testEmbed(text: string): number[] { + // Deterministic: hash each char into a 16-dim vector + const vec = new Float64Array(16) + const lower = text.toLowerCase() + for (let i = 0; i < lower.length; i++) { + const code = lower.charCodeAt(i) + vec[code % 16] += (code & 1) ? 1 : -1 + } + let norm = 0 + for (let i = 0; i < 16; i++) norm += vec[i] * vec[i] + norm = Math.sqrt(norm) + if (norm > 0) for (let i = 0; i < 16; i++) vec[i] /= norm + return Array.from(vec) +} + +function tmpDb() { + return `/tmp/deja-test-${crypto.randomUUID()}.db` +} + +let cleanup: string[] = [] + +afterEach(() => { + for (const f of cleanup) { + try { unlinkSync(f) } catch {} + try { unlinkSync(f + '-wal') } catch {} + try { unlinkSync(f + '-shm') } catch {} + } + cleanup = [] +}) + +function mem(path?: string): MemoryStore { + const p = path ?? tmpDb() + cleanup.push(p) + return createMemory({ path: p, embed: testEmbed, threshold: 0.1 }) +} + +// ============================================================================ +// Trust guarantee: DURABILITY +// ============================================================================ + +describe('durability', () => { + test('memories survive process restart (new instance, same file)', async () => { + const p = tmpDb() + cleanup.push(p) + + const mem1 = createMemory({ path: p, embed: testEmbed }) + await mem1.remember('survive restart') + mem1.close() + + const mem2 = createMemory({ path: p, embed: testEmbed }) + expect(mem2.size).toBe(1) + expect(mem2.list()[0].text).toBe('survive restart') + mem2.close() + }) + + test('database file is created on disk', async () => { + const p = tmpDb() + cleanup.push(p) + const m = createMemory({ path: p, embed: testEmbed }) + await m.remember('test') + expect(existsSync(p)).toBe(true) + m.close() + }) + + test('remember is durable before returning', async () => { + const p = tmpDb() + cleanup.push(p) + + const m = createMemory({ path: p, embed: testEmbed }) + await m.remember('durable write') + // Open fresh connection without closing — simulate crash + const m2 = createMemory({ path: p, embed: testEmbed }) + expect(m2.size).toBe(1) + m.close() + m2.close() + }) +}) + +// ============================================================================ +// Trust guarantee: CONSISTENCY +// ============================================================================ + +describe('consistency', () => { + test('remember on step N is recallable on step N+1 (zero lag)', async () => { + const m = mem() + await m.remember('check wrangler.toml before deploying') + const results = await m.recall('deploying to production') + expect(results.length).toBeGreaterThan(0) + expect(results[0].text).toBe('check wrangler.toml before deploying') + m.close() + }) + + test('forget immediately removes from recall', async () => { + const m = mem() + const memory = await m.remember('remove me') + const before = await m.recall('remove me') + expect(before.length).toBe(1) + + await m.forget(memory.id) + const after = await m.recall('remove me') + expect(after.length).toBe(0) + m.close() + }) +}) + +// ============================================================================ +// Trust guarantee: DEDUPLICATION +// ============================================================================ + +describe('deduplication', () => { + test('identical text is not stored twice', async () => { + const m = mem() + await m.remember('deploy tip: check wrangler.toml') + await m.remember('deploy tip: check wrangler.toml') + expect(m.size).toBe(1) + m.close() + }) + + test('near-identical text is deduplicated', async () => { + const m = mem() + await m.remember('always check wrangler.toml before deploying') + await m.remember('always check wrangler.toml before deploying!') + // Near-identical — should be deduplicated (depends on embed similarity) + expect(m.size).toBeLessThanOrEqual(2) // may or may not dedup with test embedder + m.close() + }) + + test('genuinely different memories are both stored', async () => { + const m = mem() + await m.remember('check wrangler.toml before deploying') + await m.remember('always backup database before migrations') + expect(m.size).toBe(2) + m.close() + }) +}) + +// ============================================================================ +// Trust guarantee: AUDITABILITY +// ============================================================================ + +describe('auditability', () => { + test('every recall is logged', async () => { + const m = mem() + await m.remember('tip about deploying') + await m.recall('deploying to production') + await m.recall('running database migration') + + const log = m.recallLog() + expect(log.length).toBe(2) + m.close() + }) + + test('recall log contains context and matched memories', async () => { + const m = mem() + const memory = await m.remember('tip about deploying') + await m.recall('deploying stuff') + + const log = m.recallLog() + expect(log[0].context).toBe('deploying stuff') + expect(log[0].results.length).toBeGreaterThan(0) + expect(log[0].results[0].memoryId).toBe(memory.id) + expect(log[0].results[0].score).toBeGreaterThan(0) + expect(log[0].timestamp).toBeTruthy() + m.close() + }) + + test('recall log persists across restarts', async () => { + const p = tmpDb() + cleanup.push(p) + + const m1 = createMemory({ path: p, embed: testEmbed }) + await m1.remember('test memory') + await m1.recall('test query') + m1.close() + + const m2 = createMemory({ path: p, embed: testEmbed }) + const log = m2.recallLog() + expect(log.length).toBe(1) + expect(log[0].context).toBe('test query') + m2.close() + }) + + test('list returns memories newest first', async () => { + const m = mem() + await m.remember('first') + await m.remember('second') + await m.remember('third') + + const all = m.list() + expect(all[0].text).toBe('third') + expect(all[2].text).toBe('first') + m.close() + }) +}) + +// ============================================================================ +// Trust guarantee: CORRECTNESS +// ============================================================================ + +describe('correctness', () => { + test('recall ranks by relevance', async () => { + const m = mem() + await m.remember('backup database before migrations') + await m.remember('check wrangler.toml before deploying') + await m.remember('clear build cache when css breaks') + + const results = await m.recall('deploying to production') + expect(results.length).toBeGreaterThan(0) + expect(results[0].text).toContain('deploy') + m.close() + }) + + test('recall respects threshold', async () => { + const m = mem() + await m.remember('javascript closures capture by reference') + const results = await m.recall('kubernetes yaml', { threshold: 0.99 }) + expect(results.length).toBe(0) + m.close() + }) + + test('recall respects limit', async () => { + const m = mem() + for (let i = 0; i < 10; i++) await m.remember(`memory about topic ${i}`) + const results = await m.recall('topic', { limit: 3 }) + expect(results.length).toBeLessThanOrEqual(3) + m.close() + }) + + test('forget returns false for unknown id', async () => { + const m = mem() + expect(await m.forget('nonexistent')).toBe(false) + m.close() + }) + + test('forget returns true and actually removes', async () => { + const m = mem() + const memory = await m.remember('to be forgotten') + expect(m.size).toBe(1) + expect(await m.forget(memory.id)).toBe(true) + expect(m.size).toBe(0) + m.close() + }) + + test('list supports pagination', async () => { + const m = mem() + for (let i = 0; i < 5; i++) await m.remember(`memory ${i}`) + + const page1 = m.list({ limit: 2 }) + const page2 = m.list({ limit: 2, offset: 2 }) + + expect(page1.length).toBe(2) + expect(page2.length).toBe(2) + expect(page1[0].id).not.toBe(page2[0].id) + m.close() + }) + + test('size is accurate after remember and forget', async () => { + const m = mem() + expect(m.size).toBe(0) + + const a = await m.remember('a') + const b = await m.remember('b') + expect(m.size).toBe(2) + + await m.forget(a.id) + expect(m.size).toBe(1) + + await m.forget(b.id) + expect(m.size).toBe(0) + m.close() + }) +}) + +// ============================================================================ +// THE RATCHET: confirm / reject / confidence scoring +// ============================================================================ + +describe('ratchet', () => { + test('memories start with default confidence of 0.5', async () => { + const m = mem() + const memory = await m.remember('default confidence') + expect(memory.confidence).toBe(0.5) + m.close() + }) + + test('confirm boosts confidence', async () => { + const m = mem() + const memory = await m.remember('useful memory') + await m.confirm(memory.id) + const listed = m.list() + expect(listed[0].confidence).toBeGreaterThan(0.5) + m.close() + }) + + test('reject drops confidence', async () => { + const m = mem() + const memory = await m.remember('bad memory') + await m.reject(memory.id) + const listed = m.list() + expect(listed[0].confidence).toBeLessThan(0.5) + m.close() + }) + + test('confidence is clamped between 0.01 and 1.0', async () => { + const m = mem() + const memory = await m.remember('test clamping') + + // Boost many times + for (let i = 0; i < 20; i++) await m.confirm(memory.id) + let listed = m.list() + expect(listed[0].confidence).toBeLessThanOrEqual(1.0) + + // Reject many times + for (let i = 0; i < 40; i++) await m.reject(memory.id) + listed = m.list() + expect(listed[0].confidence).toBeGreaterThanOrEqual(0.01) + m.close() + }) + + test('confirm/reject return false for unknown ids', async () => { + const m = mem() + expect(await m.confirm('nonexistent')).toBe(false) + expect(await m.reject('nonexistent')).toBe(false) + m.close() + }) + + test('confidence persists across restarts', async () => { + const p = tmpDb() + cleanup.push(p) + + const m1 = createMemory({ path: p, embed: testEmbed }) + const memory = await m1.remember('persistent confidence') + await m1.confirm(memory.id) + await m1.confirm(memory.id) + const conf = m1.list()[0].confidence + m1.close() + + const m2 = createMemory({ path: p, embed: testEmbed }) + expect(m2.list()[0].confidence).toBe(conf) + m2.close() + }) + + test('high-confidence memories rank higher in recall', async () => { + const m = mem() + const low = await m.remember('deploy tip alpha') + const high = await m.remember('deploy tip beta') + + // Boost one, reject the other + for (let i = 0; i < 5; i++) await m.confirm(high.id) + for (let i = 0; i < 3; i++) await m.reject(low.id) + + const results = await m.recall('deploy tip') + expect(results.length).toBe(2) + // The confirmed one should rank first (confidence affects score) + expect(results[0].id).toBe(high.id) + m.close() + }) + + test('minConfidence filters low-confidence memories', async () => { + const m = mem() + const good = await m.remember('reliable tip about deploy') + const bad = await m.remember('unreliable tip about deploy process') + + for (let i = 0; i < 5; i++) await m.confirm(good.id) + for (let i = 0; i < 3; i++) await m.reject(bad.id) + + const all = await m.recall('deploy', { minConfidence: 0 }) + const filtered = await m.recall('deploy', { minConfidence: 0.5 }) + + expect(all.length).toBeGreaterThanOrEqual(filtered.length) + m.close() + }) +}) + +// ============================================================================ +// CONFLICT RESOLUTION +// ============================================================================ + +describe('conflict resolution', () => { + test('conflicting memory supersedes the old one', async () => { + // Custom embed: "deploy target" memories share a base vector, but the + // specific region name pushes them apart enough to land in the conflict + // zone (similarity ~0.8) rather than the dedup zone (>= 0.95). + let callCount = 0 + const conflictEmbed = (text: string): number[] => { + const vec = new Float64Array(16).fill(0) + // Shared topic signal + vec[0] = 5; vec[1] = 5; vec[2] = 5; vec[3] = 5 + // Per-call variation: use call order to shift a different dimension + callCount++ + vec[4 + (callCount % 12)] = 3 + let norm = 0 + for (let i = 0; i < 16; i++) norm += vec[i] * vec[i] + norm = Math.sqrt(norm) + for (let i = 0; i < 16; i++) vec[i] /= norm + return Array.from(vec) + } + + const p = tmpDb() + cleanup.push(p) + const m = createMemory({ + path: p, + embed: conflictEmbed, + threshold: 0.1, + dedupeThreshold: 0.98, // Very high dedup threshold + conflictThreshold: 0.7, // Moderate conflict threshold + }) + + const old = await m.remember('deploy target is us-east-1') + const updated = await m.remember('deploy target is eu-west-1') + + // Both should exist (not deduped) + expect(m.size).toBe(2) + // New memory should reference the old one + expect(updated.supersedes).toBe(old.id) + // Old memory's confidence should be reduced + const listed = m.list() + const oldMem = listed.find(l => l.id === old.id)! + expect(oldMem.confidence).toBeLessThan(0.5) + m.close() + }) + + test('superseded memory ranks lower in recall', async () => { + let callCount = 0 + const conflictEmbed = (text: string): number[] => { + const vec = new Float64Array(16).fill(0) + vec[0] = 5; vec[1] = 5; vec[2] = 5 + callCount++ + vec[3 + (callCount % 13)] = 3 + let norm = 0 + for (let i = 0; i < 16; i++) norm += vec[i] * vec[i] + norm = Math.sqrt(norm) + for (let i = 0; i < 16; i++) vec[i] /= norm + return Array.from(vec) + } + + const p = tmpDb() + cleanup.push(p) + const m = createMemory({ + path: p, + embed: conflictEmbed, + threshold: 0.1, + dedupeThreshold: 0.98, + conflictThreshold: 0.7, + }) + + await m.remember('api url is http://old.example.com') + await m.remember('api url is http://new.example.com') + + const results = await m.recall('api url') + expect(results.length).toBe(2) + // The newer (non-superseded) memory should rank first + expect(results[0].text).toContain('new.example.com') + m.close() + }) +}) + +// ============================================================================ +// RECALL DECOMPOSITION +// ============================================================================ + +describe('recall decomposition', () => { + test('complex queries find memories that match sub-parts', async () => { + const m = mem() + await m.remember('always run database migrations first') + await m.remember('check environment variables before deploy') + await m.remember('use pnpm not npm for this project') + + // A complex query that touches multiple memories + const results = await m.recall('full production deploy checklist database migrations environment') + // Should find memories matching sub-parts of the query + expect(results.length).toBeGreaterThan(0) + m.close() + }) + + test('short queries are not decomposed unnecessarily', async () => { + const m = mem() + await m.remember('deploy tip') + // A short query — should work fine without decomposition + const results = await m.recall('deploy') + expect(results.length).toBeGreaterThan(0) + m.close() + }) +}) + +// ============================================================================ +// BACKWARD COMPATIBILITY +// ============================================================================ + +describe('backward compatibility', () => { + test('learn() still works as alias for remember()', async () => { + const m = mem() + const memory = await m.learn('via learn') + expect(memory.text).toBe('via learn') + expect(m.size).toBe(1) + + const results = await m.recall('via learn') + expect(results.length).toBe(1) + m.close() + }) + + test('old databases without confidence column are migrated', async () => { + const p = tmpDb() + cleanup.push(p) + + // Simulate old schema by creating DB without confidence column + const { Database } = await import('bun:sqlite') + const db = new Database(p) + db.exec(` + CREATE TABLE memories ( + id TEXT PRIMARY KEY, + text TEXT NOT NULL, + embedding BLOB NOT NULL, + created_at TEXT NOT NULL + ); + CREATE TABLE recall_log ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + context TEXT NOT NULL, + results TEXT NOT NULL, + timestamp TEXT NOT NULL + ); + `) + // Insert a memory in old format + const vec = new Float32Array(testEmbed('old memory')) + const buf = Buffer.from(vec.buffer) + db.prepare('INSERT INTO memories (id, text, embedding, created_at) VALUES (?, ?, ?, ?)').run( + 'old-id', 'old memory', buf, new Date().toISOString() + ) + db.close() + + // Open with new createMemory — should migrate + const m = createMemory({ path: p, embed: testEmbed }) + expect(m.size).toBe(1) + const listed = m.list() + expect(listed[0].confidence).toBe(0.5) // default from migration + m.close() + }) +}) diff --git a/packages/deja-local/tsconfig.json b/packages/deja-local/tsconfig.json new file mode 100644 index 0000000..3239d16 --- /dev/null +++ b/packages/deja-local/tsconfig.json @@ -0,0 +1,16 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "outDir": "dist", + "declaration": true, + "resolveJsonModule": true, + "types": ["bun"] + }, + "include": ["src"], + "exclude": ["node_modules", "dist", "test"] +}