diff --git a/README.md b/README.md index ef60b67..a040dc7 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,8 @@ A pluggable benchmarking framework for evaluating memory and context systems. ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ Benchmarks │ │ Providers │ │ Judges │ │ (LoCoMo, │ │ (Supermem, │ │ (GPT-4o, │ -│ LongMem..) │ │ Mem0, Zep) │ │ Claude..) │ +│ LongMem..) │ │ Mem0, Zep, │ │ Claude..) │ +│ │ │ Memento) │ │ │ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ └──────────────────┼──────────────────┘ ▼ @@ -48,6 +49,14 @@ bun run src/index.ts run -p supermemory -b locomo SUPERMEMORY_API_KEY= MEM0_API_KEY= ZEP_API_KEY= +# Memento: no API key — runs as a local stdio subprocess. The provider +# distils each session through an LLM (the configured answering model +# by default) before writing to Memento. Optional knobs: +# MEMENTO_BIN= (default: "npx -y @psraghuveer/memento") +# MEMENTO_BENCH_DB= (default: /tmp/memento-bench-.db) +# MEMENTO_DISTILL_MODEL= (default: memorybench answering model) +# MEMENTO_BENCH_SEARCH_LIMIT= (default: 30) +# MEMENTO_AWAIT_INDEXING_MS= (default: 180000) # Judges (at least one) OPENAI_API_KEY= @@ -73,7 +82,7 @@ GOOGLE_API_KEY= ## Options ``` --p, --provider Memory provider (supermemory, mem0, zep) +-p, --provider Memory provider (supermemory, mem0, zep, memento) -b, --benchmark Benchmark (locomo, longmemeval, convomem) -j, --judge Judge model (gpt-4o, sonnet-4, gemini-2.5-flash, etc.) -r, --run-id Run identifier (auto-generated if omitted) diff --git a/bun.lock b/bun.lock index 637c144..c16ef91 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,7 @@ "@ai-sdk/openai": "^2.0.88", "@anthropic-ai/tokenizer": "^0.0.4", "@getzep/zep-cloud": "^3.13.0", + "@modelcontextprotocol/sdk": "^1.29.0", "ai": "^5.0.115", "drizzle-orm": "^0.45.1", "js-tiktoken": "^1.0.21", @@ -58,6 +59,8 @@ "@google/genai": ["@google/genai@1.34.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.24.0" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-vu53UMPvjmb7PGzlYu6Tzxso8Dfhn+a7eQFaS2uNemVtDZKwzSpJ5+ikqBbXplF7RGB1STcVDqCkPvquiwb2sw=="], + "@hono/node-server": ["@hono/node-server@1.19.14", "", { "peerDependencies": { "hono": "^4" } }, "sha512-GwtvgtXxnWsucXvbQXkRgqksiH2Qed37H9xHZocE5sA3N8O8O8/8FA3uclQXxXVzc9XBZuEOMK7+r02FmSpHtw=="], + "@isaacs/cliui": ["@isaacs/cliui@8.0.2", "", { "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" } }, "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA=="], "@jest/expect-utils": ["@jest/expect-utils@29.7.0", "", { "dependencies": { "jest-get-type": "^29.6.3" } }, "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA=="], @@ -70,6 +73,8 @@ "@mistralai/mistralai": ["@mistralai/mistralai@1.11.0", "", { "dependencies": { "zod": "^3.20.0", "zod-to-json-schema": "^3.24.1" } }, "sha512-6/BVj2mcaggYbpMzNSxtqtM2Tv/Jb5845XFd2CMYFO+O5VBkX70iLjtkBBTI4JFhh1l9vTCIMYXBVOjLoBVHGQ=="], + "@modelcontextprotocol/sdk": ["@modelcontextprotocol/sdk@1.29.0", "", { "dependencies": { "@hono/node-server": "^1.19.9", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "content-type": "^1.0.5", "cors": "^2.8.5", "cross-spawn": "^7.0.5", "eventsource": "^3.0.2", "eventsource-parser": "^3.0.0", "express": "^5.2.1", "express-rate-limit": "^8.2.1", "hono": "^4.11.4", "jose": "^6.1.3", "json-schema-typed": "^8.0.2", "pkce-challenge": "^5.0.0", "raw-body": "^3.0.0", "zod": "^3.25 || ^4.0", "zod-to-json-schema": "^3.25.1" }, "peerDependencies": { "@cfworker/json-schema": "^4.1.1" }, "optionalPeers": ["@cfworker/json-schema"] }, "sha512-zo37mZA9hJWpULgkRpowewez1y6ML5GsXJPY8FI0tBBCd77HEvza4jDqRKOXgHNn867PVGCyTdzqpz0izu5ZjQ=="], + "@npmcli/fs": ["@npmcli/fs@1.1.1", "", { "dependencies": { "@gar/promisify": "^1.0.1", "semver": "^7.3.5" } }, "sha512-8KG5RD0GVP4ydEzRn/I4BNDuxDtqVbOdm8675T49OIG/NGhaK0pjPX7ZcDlvKYbA+ulvVK3ztfcF4uBdOxuJbQ=="], "@npmcli/move-file": ["@npmcli/move-file@1.1.2", "", { "dependencies": { "mkdirp": "^1.0.4", "rimraf": "^3.0.2" } }, "sha512-1SUf/Cg2GzGDyaf15aR9St9TWlb+XvbZXWpDx8YKs7MLzMH/BCeopv+y9vzrzgkfykCGuWOlSu3mZhj2+FQcrg=="], @@ -154,6 +159,8 @@ "abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="], + "accepts": ["accepts@2.0.0", "", { "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" } }, "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng=="], + "agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="], "agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="], @@ -162,6 +169,10 @@ "ai": ["ai@5.0.115", "", { "dependencies": { "@ai-sdk/gateway": "2.0.22", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.19", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-aVuHx0orGxXvhyL7oXUyW8TnWQE6Al8f3Bl6VZjz0WHMV+WaACHPkSyvQ3wje2QCUGzdl5DBF5d+OaXyghPQyg=="], + "ajv": ["ajv@8.20.0", "", { "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" } }, "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA=="], + + "ajv-formats": ["ajv-formats@3.0.1", "", { "dependencies": { "ajv": "^8.0.0" } }, "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ=="], + "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], "ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="], @@ -188,6 +199,8 @@ "bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="], + "body-parser": ["body-parser@2.2.2", "", { "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" } }, "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA=="], + "brace-expansion": ["brace-expansion@1.1.12", "", { "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], @@ -200,10 +213,14 @@ "bun-types": ["bun-types@1.3.4", "", { "dependencies": { "@types/node": "*" } }, "sha512-5ua817+BZPZOlNaRgGBpZJOSAQ9RQ17pkwPD0yR7CfJg+r8DgIILByFifDTa+IPDDxzf5VNhtNlcKqFzDgJvlQ=="], + "bytes": ["bytes@3.1.2", "", {}, "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg=="], + "cacache": ["cacache@15.3.0", "", { "dependencies": { "@npmcli/fs": "^1.0.0", "@npmcli/move-file": "^1.0.1", "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "glob": "^7.1.4", "infer-owner": "^1.0.4", "lru-cache": "^6.0.0", "minipass": "^3.1.1", "minipass-collect": "^1.0.2", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.2", "mkdirp": "^1.0.3", "p-map": "^4.0.0", "promise-inflight": "^1.0.1", "rimraf": "^3.0.2", "ssri": "^8.0.1", "tar": "^6.0.2", "unique-filename": "^1.1.1" } }, "sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ=="], "call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="], + "call-bound": ["call-bound@1.0.4", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" } }, "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg=="], + "camelcase": ["camelcase@6.3.0", "", {}, "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA=="], "chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="], @@ -234,6 +251,16 @@ "console-table-printer": ["console-table-printer@2.15.0", "", { "dependencies": { "simple-wcswidth": "^1.1.2" } }, "sha512-SrhBq4hYVjLCkBVOWaTzceJalvn5K1Zq5aQA6wXC/cYjI3frKWNPEMK3sZsJfNNQApvCQmgBcc13ZKmFj8qExw=="], + "content-disposition": ["content-disposition@1.1.0", "", {}, "sha512-5jRCH9Z/+DRP7rkvY83B+yGIGX96OYdJmzngqnw2SBSxqCFPd0w2km3s5iawpGX8krnwSGmF0FW5Nhr0Hfai3g=="], + + "content-type": ["content-type@1.0.5", "", {}, "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA=="], + + "cookie": ["cookie@0.7.2", "", {}, "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w=="], + + "cookie-signature": ["cookie-signature@1.2.2", "", {}, "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg=="], + + "cors": ["cors@2.8.6", "", { "dependencies": { "object-assign": "^4", "vary": "^1" } }, "sha512-tJtZBBHA6vjIAaF6EnIaq6laBBP9aq/Y3ouVJjEfoHbRBcHBAHYcMh/w8LDrk2PvIMMq8gmopa5D4V8RmbrxGw=="], + "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], "crypt": ["crypt@0.0.2", "", {}, "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow=="], @@ -252,6 +279,8 @@ "delegates": ["delegates@1.0.0", "", {}, "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ=="], + "depd": ["depd@2.0.0", "", {}, "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="], + "detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="], @@ -266,8 +295,12 @@ "ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="], + "ee-first": ["ee-first@1.1.1", "", {}, "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="], + "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], + "encodeurl": ["encodeurl@2.0.0", "", {}, "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg=="], + "encoding": ["encoding@0.1.13", "", { "dependencies": { "iconv-lite": "^0.6.2" } }, "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A=="], "end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="], @@ -284,26 +317,42 @@ "es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="], + "escape-html": ["escape-html@1.0.3", "", {}, "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="], + "escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="], + "etag": ["etag@1.8.1", "", {}, "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg=="], + "event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="], "eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="], + "eventsource": ["eventsource@3.0.7", "", { "dependencies": { "eventsource-parser": "^3.0.1" } }, "sha512-CRT1WTyuQoD771GW56XEZFQ/ZoSfWid1alKGDYMmkt2yl8UXrVR4pspqWNEcqKvVIzg6PAltWjxcSSPrboA4iA=="], + "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], "expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="], "expect": ["expect@29.7.0", "", { "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw=="], + "express": ["express@5.2.1", "", { "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" } }, "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw=="], + + "express-rate-limit": ["express-rate-limit@8.5.2", "", { "dependencies": { "ip-address": "^10.2.0" }, "peerDependencies": { "express": ">= 4.11" } }, "sha512-5Kb34ipNX694DH48vN9irak1Qx30nb0PLYHXfJgw4YEjiC3ZEmZJhwOp+VfiCYwFzvFTdB9QkArYS5kXa2cx2A=="], + "extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="], + "fast-deep-equal": ["fast-deep-equal@3.1.3", "", {}, "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="], + + "fast-uri": ["fast-uri@3.1.2", "", {}, "sha512-rVjf7ArG3LTk+FS6Yw81V1DLuZl1bRbNrev6Tmd/9RaroeeRRJhAt7jg/6YFxbvAQXUCavSoZhPPj6oOx+5KjQ=="], + "fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="], "file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], "fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="], + "finalhandler": ["finalhandler@2.1.1", "", { "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" } }, "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA=="], + "follow-redirects": ["follow-redirects@1.15.11", "", {}, "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ=="], "foreground-child": ["foreground-child@3.3.1", "", { "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" } }, "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw=="], @@ -316,6 +365,10 @@ "formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="], + "forwarded": ["forwarded@0.2.0", "", {}, "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow=="], + + "fresh": ["fresh@2.0.0", "", {}, "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A=="], + "fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="], "fs-minipass": ["fs-minipass@2.1.0", "", { "dependencies": { "minipass": "^3.0.0" } }, "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg=="], @@ -362,8 +415,12 @@ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "hono": ["hono@4.12.18", "", {}, "sha512-RWzP96k/yv0PQfyXnWjs6zot20TqfpfsNXhOnev8d1InAxubW93L11/oNUc3tQqn2G0bSdAOBpX+2uDFHV7kdQ=="], + "http-cache-semantics": ["http-cache-semantics@4.2.0", "", {}, "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ=="], + "http-errors": ["http-errors@2.0.1", "", { "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" } }, "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ=="], + "http-proxy-agent": ["http-proxy-agent@4.0.1", "", { "dependencies": { "@tootallnate/once": "1", "agent-base": "6", "debug": "4" } }, "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg=="], "https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="], @@ -372,7 +429,7 @@ "iceberg-js": ["iceberg-js@0.8.1", "", {}, "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA=="], - "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], @@ -388,7 +445,9 @@ "ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="], - "ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + "ip-address": ["ip-address@10.2.0", "", {}, "sha512-/+S6j4E9AHvW9SWMSEY9Xfy66O5PWvVEJ08O0y5JGyEKQpojb0K0GKpz/v5HJ/G0vi3D2sjGK78119oXZeE0qA=="], + + "ipaddr.js": ["ipaddr.js@1.9.1", "", {}, "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g=="], "is-buffer": ["is-buffer@1.1.6", "", {}, "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="], @@ -398,6 +457,8 @@ "is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="], + "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], + "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], "jackspeak": ["jackspeak@3.4.3", "", { "dependencies": { "@isaacs/cliui": "^8.0.2" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw=="], @@ -412,6 +473,8 @@ "jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="], + "jose": ["jose@6.2.3", "", {}, "sha512-YYVDInQKFJfR/xa3ojUTl8c2KoTwiL1R5Wg9YCydwH0x0B9grbzlg5HC7mMjCtUJjbQ/YnGEZIhI5tCgfTb4Hw=="], + "js-tiktoken": ["js-tiktoken@1.0.21", "", { "dependencies": { "base64-js": "^1.5.1" } }, "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g=="], "js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="], @@ -420,6 +483,10 @@ "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + "json-schema-traverse": ["json-schema-traverse@1.0.0", "", {}, "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="], + + "json-schema-typed": ["json-schema-typed@8.0.2", "", {}, "sha512-fQhoXdcvc3V28x7C7BMs4P5+kNlgUURe2jmUT1T//oBRMDrqy1QPelJimwZGo7Hg9VPV3EQV5Bnq4hbFy2vetA=="], + "jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="], "jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="], @@ -434,13 +501,17 @@ "md5": ["md5@2.3.0", "", { "dependencies": { "charenc": "0.0.2", "crypt": "0.0.2", "is-buffer": "~1.1.6" } }, "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g=="], + "media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], + "mem0ai": ["mem0ai@2.1.38", "", { "dependencies": { "axios": "1.7.7", "openai": "^4.93.0", "uuid": "9.0.1", "zod": "^3.24.1" }, "peerDependencies": { "@anthropic-ai/sdk": "^0.40.1", "@cloudflare/workers-types": "^4.20250504.0", "@google/genai": "^1.2.0", "@langchain/core": "^0.3.44", "@mistralai/mistralai": "^1.5.2", "@qdrant/js-client-rest": "1.13.0", "@supabase/supabase-js": "^2.49.1", "@types/jest": "29.5.14", "@types/pg": "8.11.0", "@types/sqlite3": "3.1.11", "cloudflare": "^4.2.0", "groq-sdk": "0.3.0", "neo4j-driver": "^5.28.1", "ollama": "^0.5.14", "pg": "8.11.3", "redis": "^4.6.13", "sqlite3": "5.1.7" } }, "sha512-es8ffk0VbYJ1RDSblcoYzxaaafDMD8XgvyYTGb0HrKcDLj1rlvFqaV4K5IMBm4GGOAI+I0BwGh8d49z7vC/ajQ=="], + "merge-descriptors": ["merge-descriptors@2.0.0", "", {}, "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g=="], + "micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="], - "mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "mime-db": ["mime-db@1.54.0", "", {}, "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ=="], - "mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "mime-types": ["mime-types@3.0.2", "", { "dependencies": { "mime-db": "^1.54.0" } }, "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A=="], "mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="], @@ -472,7 +543,7 @@ "napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="], - "negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="], + "negotiator": ["negotiator@1.0.0", "", {}, "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg=="], "neo4j-driver": ["neo4j-driver@5.28.2", "", { "dependencies": { "neo4j-driver-bolt-connection": "5.28.2", "neo4j-driver-core": "5.28.2", "rxjs": "^7.8.2" } }, "sha512-nix4Canllf7Tl4FZL9sskhkKYoCp40fg7VsknSRTRgbm1JaE2F1Ej/c2nqlM06nqh3WrkI0ww3taVB+lem7w7w=="], @@ -494,10 +565,16 @@ "npmlog": ["npmlog@6.0.2", "", { "dependencies": { "are-we-there-yet": "^3.0.0", "console-control-strings": "^1.1.0", "gauge": "^4.0.3", "set-blocking": "^2.0.0" } }, "sha512-/vBvz5Jfr9dT/aFWd0FIRf+T/Q2WBsLENygUaFUqstqsycmZAP/t5BvFJTK0viFmSUxiUKTUplWy5vt+rvKIxg=="], + "object-assign": ["object-assign@4.1.1", "", {}, "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="], + + "object-inspect": ["object-inspect@1.13.4", "", {}, "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew=="], + "obuf": ["obuf@1.1.2", "", {}, "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="], "ollama": ["ollama@0.5.18", "", { "dependencies": { "whatwg-fetch": "^3.6.20" } }, "sha512-lTFqTf9bo7Cd3hpF6CviBe/DEhewjoZYd9N/uCe7O20qYTvGqrNOFOBDj3lbZgFWHUgDv5EeyusYxsZSLS8nvg=="], + "on-finished": ["on-finished@2.4.1", "", { "dependencies": { "ee-first": "1.1.1" } }, "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg=="], + "once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="], "openai": ["openai@4.104.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" }, "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA=="], @@ -516,12 +593,16 @@ "packet-reader": ["packet-reader@1.0.0", "", {}, "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="], + "parseurl": ["parseurl@1.3.3", "", {}, "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="], + "path-is-absolute": ["path-is-absolute@1.0.1", "", {}, "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg=="], "path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="], "path-scurry": ["path-scurry@1.11.1", "", { "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" } }, "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA=="], + "path-to-regexp": ["path-to-regexp@8.4.2", "", {}, "sha512-qRcuIdP69NPm4qbACK+aDogI5CBDMi1jKe0ry5rSQJz8JVLsC7jV8XpiJjGRLLol3N+R5ihGYcrPLTno6pAdBA=="], + "pg": ["pg@8.11.3", "", { "dependencies": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", "pg-connection-string": "^2.6.2", "pg-pool": "^3.6.1", "pg-protocol": "^1.6.0", "pg-types": "^2.1.0", "pgpass": "1.x" }, "optionalDependencies": { "pg-cloudflare": "^1.1.1" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g=="], "pg-cloudflare": ["pg-cloudflare@1.2.7", "", {}, "sha512-YgCtzMH0ptvZJslLM1ffsY4EuGaU0cx4XSdXLRFae8bPP4dS5xL1tNB3k2o/N64cHJpwU7dxKli/nZ2lUa5fLg=="], @@ -544,6 +625,8 @@ "picomatch": ["picomatch@2.3.1", "", {}, "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="], + "pkce-challenge": ["pkce-challenge@5.0.1", "", {}, "sha512-wQ0b/W4Fr01qtpHlqSqspcj3EhBvimsdh0KlHhH8HRZnMsEa0ea2fTULOXOS9ccQr3om+GcGRk4e+isrZWV8qQ=="], + "postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="], "postgres-bytea": ["postgres-bytea@3.0.0", "", { "dependencies": { "obuf": "~1.1.2" } }, "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw=="], @@ -564,10 +647,18 @@ "promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="], + "proxy-addr": ["proxy-addr@2.0.7", "", { "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" } }, "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg=="], + "proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="], "pump": ["pump@3.0.3", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA=="], + "qs": ["qs@6.15.1", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-6YHEFRL9mfgcAvql/XhwTvf5jKcOiiupt2FiJxHkiX1z4j7WL8J/jRHYLluORvc1XxB5rV20KoeK00gVJamspg=="], + + "range-parser": ["range-parser@1.2.1", "", {}, "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg=="], + + "raw-body": ["raw-body@3.0.2", "", { "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" } }, "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA=="], + "rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="], "react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="], @@ -576,10 +667,14 @@ "redis": ["redis@4.7.1", "", { "dependencies": { "@redis/bloom": "1.2.0", "@redis/client": "1.6.1", "@redis/graph": "1.1.1", "@redis/json": "1.0.7", "@redis/search": "1.2.0", "@redis/time-series": "1.1.0" } }, "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ=="], + "require-from-string": ["require-from-string@2.0.2", "", {}, "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="], + "retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="], "rimraf": ["rimraf@3.0.2", "", { "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" } }, "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA=="], + "router": ["router@2.2.0", "", { "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" } }, "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ=="], + "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], "safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="], @@ -588,12 +683,26 @@ "semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], + + "serve-static": ["serve-static@2.2.1", "", { "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" } }, "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw=="], + "set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="], + "setprototypeof": ["setprototypeof@1.2.0", "", {}, "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="], + "shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="], "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], + "side-channel": ["side-channel@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" } }, "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw=="], + + "side-channel-list": ["side-channel-list@1.0.1", "", { "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.4" } }, "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w=="], + + "side-channel-map": ["side-channel-map@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" } }, "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA=="], + + "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], + "signal-exit": ["signal-exit@3.0.7", "", {}, "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ=="], "simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="], @@ -618,6 +727,8 @@ "stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="], + "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], + "string-width": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], "string-width-cjs": ["string-width@4.2.3", "", { "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" } }, "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g=="], @@ -644,12 +755,16 @@ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "toidentifier": ["toidentifier@1.0.1", "", {}, "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA=="], + "tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="], + "type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], + "typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="], "undici": ["undici@5.28.5", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA=="], @@ -660,10 +775,14 @@ "unique-slug": ["unique-slug@2.0.2", "", { "dependencies": { "imurmurhash": "^0.1.4" } }, "sha512-zoWr9ObaxALD3DOPfjPSqxt4fnZiWblxHIgeWqW8x7UqDzEtHEQLzji2cuJYQFCU6KmoJikOYAZlrTHHebjx2w=="], + "unpipe": ["unpipe@1.0.0", "", {}, "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="], + "util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="], "uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], + "vary": ["vary@1.1.2", "", {}, "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg=="], + "web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="], "webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="], @@ -690,7 +809,7 @@ "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="], + "zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="], "@anthropic-ai/sdk/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], @@ -702,6 +821,10 @@ "@langchain/core/uuid": ["uuid@10.0.0", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ=="], + "@langchain/core/zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="], + + "@mistralai/mistralai/zod-to-json-schema": ["zod-to-json-schema@3.25.0", "", { "peerDependencies": { "zod": "^3.25 || ^4" } }, "sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ=="], + "bl/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="], "cacache/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -710,8 +833,12 @@ "cloudflare/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="], + "encoding/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], + "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "form-data/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], + "formdata-node/web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="], "fs-minipass/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -728,6 +855,8 @@ "make-fetch-happen/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "make-fetch-happen/negotiator": ["negotiator@0.6.4", "", {}, "sha512-myRT3DiWPHqho5PrJaIRyaMv2kgYf0mUVgBNOYMuCH5Ki1yEiQaf/ZJuQ62nvpc44wL5WDbTX7yGJi1Neevw8w=="], + "minipass-collect/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], "minipass-fetch/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -750,10 +879,14 @@ "promise-retry/retry": ["retry@0.12.0", "", {}, "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow=="], + "socks/ip-address": ["ip-address@10.1.0", "", {}, "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q=="], + "ssri/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], "tar-fs/chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="], + "type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], + "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "wrap-ansi/string-width": ["string-width@5.1.2", "", { "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" } }, "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA=="], @@ -772,6 +905,8 @@ "cloudflare/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="], + "form-data/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], + "gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="], "gaxios/rimraf/glob": ["glob@10.5.0", "", { "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" } }, "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg=="], diff --git a/package.json b/package.json index 8c1ab9f..1ae5206 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "@ai-sdk/openai": "^2.0.88", "@anthropic-ai/tokenizer": "^0.0.4", "@getzep/zep-cloud": "^3.13.0", + "@modelcontextprotocol/sdk": "^1.29.0", "ai": "^5.0.115", "drizzle-orm": "^0.45.1", "js-tiktoken": "^1.0.21", diff --git a/src/cli/index.ts b/src/cli/index.ts index b3c29d6..1df1a34 100644 --- a/src/cli/index.ts +++ b/src/cli/index.ts @@ -77,12 +77,24 @@ Available providers for storing and retrieving memories: Extracts memories via LLM, chunks + embeds extracted content, hybrid BM25 + vector search. Requires: OPENAI_API_KEY (for memory extraction via gpt-4o-mini + embeddings) + memento Memento - Local-first MCP memory layer (https://github.com/veerps57/memento) + Spawns \`memento serve\` over stdio; hybrid FTS + local embeddings (bge-base-en-v1.5). + The provider distils each session through an LLM before writing to Memento. + No Memento-specific API key required (the distillation reuses the answering-model key). + Configure via env: + MEMENTO_BIN shell-like command (default: "npx -y @psraghuveer/memento") + MEMENTO_BENCH_DB SQLite path (default: /tmp/memento-bench-.db) + MEMENTO_DISTILL_MODEL model alias for distillation (default: memorybench answering model) + MEMENTO_BENCH_SEARCH_LIMIT top-K returned by search_memory (default: 30) + MEMENTO_AWAIT_INDEXING_MS polling deadline per question (default: 180000) + Usage: -p supermemory Use Supermemory as the memory provider -p mem0 Use Mem0 as the memory provider -p zep Use Zep as the memory provider -p filesystem Use file-based memory (CLAUDE.md style) -p rag Use hybrid RAG memory (OpenClaw/QMD style) + -p memento Use Memento as the memory provider (local MCP server) `) } diff --git a/src/providers/index.ts b/src/providers/index.ts index 5f71566..7719fe4 100644 --- a/src/providers/index.ts +++ b/src/providers/index.ts @@ -5,6 +5,7 @@ import { Mem0Provider } from "./mem0" import { ZepProvider } from "./zep" import { FilesystemProvider } from "./filesystem" import { RAGProvider } from "./rag" +import { MementoProvider } from "./memento" const providers: Record Provider> = { supermemory: SupermemoryProvider, @@ -12,6 +13,7 @@ const providers: Record Provider> = { zep: ZepProvider, filesystem: FilesystemProvider, rag: RAGProvider, + memento: MementoProvider, } export function createProvider(name: ProviderName): Provider { @@ -39,4 +41,11 @@ export function getProviderInfo(name: ProviderName): { } } -export { SupermemoryProvider, Mem0Provider, ZepProvider, FilesystemProvider, RAGProvider } +export { + SupermemoryProvider, + Mem0Provider, + ZepProvider, + FilesystemProvider, + RAGProvider, + MementoProvider, +} diff --git a/src/providers/memento/distill.ts b/src/providers/memento/distill.ts new file mode 100644 index 0000000..6125fce --- /dev/null +++ b/src/providers/memento/distill.ts @@ -0,0 +1,235 @@ +// Per-session LLM distillation step for the Memento provider. +// +// `extract_memory` in Memento is the *storage* side — it embeds, dedups, +// scrubs, and persists candidates. It does NOT run an LLM itself +// (Memento is local-first and LLM-agnostic per the project's +// architectural commitments). The decision of *what's worth +// remembering* belongs to the calling AI assistant. +// +// In production, the assistant looks at a conversation and produces +// distilled `{kind, content}` candidates before calling `extract_memory`. +// This provider mirrors that flow inside the bench: it calls an LLM +// with each session transcript, parses the result into candidates, +// and hands them to Memento. +// +// The LLM is configurable via `MEMENTO_DISTILL_MODEL` (default matches +// memorybench's answering-model alias for the run). Pinning the same +// alias for distill and answer keeps the cross-benchmark story simple. + +import { createAnthropic } from "@ai-sdk/anthropic" +import { createGoogleGenerativeAI } from "@ai-sdk/google" +import { createOpenAI } from "@ai-sdk/openai" +import { generateText } from "ai" +import { config } from "../../utils/config" +import { getModelConfig, DEFAULT_ANSWERING_MODEL } from "../../utils/models" +import type { UnifiedSession } from "../../types/unified" + +// All five Memento memory kinds are eligible. `preference` and `decision` +// require a `topic: value\n\nprose` first line in `content` (the conflict +// detector parses that line); the LLM is instructed to follow that shape +// on a best-effort basis. This matches real-world usage: SKILL.md teaches +// the same format to assistants, and there is no server-side coercion +// rescuing a malformed candidate. Malformed candidates fail the whole +// extract_memory batch they belong to, which is the same cost a real +// assistant would face for the same mistake. +export type DistilledKind = "fact" | "preference" | "decision" | "todo" | "snippet" + +export interface DistilledCandidate { + kind: DistilledKind + content: string + summary?: string +} + +const DISTILL_KINDS: readonly DistilledKind[] = [ + "fact", + "preference", + "decision", + "todo", + "snippet", +] + +function getLLMClient(modelAlias: string) { + const modelConfig = getModelConfig(modelAlias) + switch (modelConfig.provider) { + case "openai": + return { client: createOpenAI({ apiKey: config.openaiApiKey }), modelConfig } + case "anthropic": + return { client: createAnthropic({ apiKey: config.anthropicApiKey }), modelConfig } + case "google": + return { + client: createGoogleGenerativeAI({ apiKey: config.googleApiKey }), + modelConfig, + } + } +} + +function buildDistillPrompt(session: UnifiedSession): string { + const sessionDate = session.metadata?.date as string | undefined + const formattedDate = session.metadata?.formattedDate as string | undefined + const dateLine = sessionDate ? `Session date: ${sessionDate}` : "" + const friendlyDate = formattedDate ? ` (${formattedDate})` : "" + + const transcript = session.messages + .map((m) => { + const ts = m.timestamp ? ` [${m.timestamp}]` : "" + return `${m.role}${ts}: ${m.content}` + }) + .join("\n") + + return `You are the memory-distillation step of an AI assistant. The assistant just finished a conversation; you must decide what is worth keeping in a durable, searchable memory layer (Memento). + +You are **not** summarising the conversation for a reader. You are producing **retrieval candidates for unknown future queries**. The future query may ask about any specific date, named entity, proper noun, action, or object that appeared in the conversation — including ones that feel incidental at write time. The right mental frame is "index every concrete reference," not "capture the gist." + +${dateLine}${friendlyDate} + +Conversation transcript: +${transcript} + +Produce a JSON array of memory candidates. Each candidate is a self-contained assertion the assistant should remember for future conversations. + +**Rules, in priority order:** + +1. **PRESERVE specific words.** Use the speakers' exact terms for proper nouns, named entities, identity qualifiers, places, organisations, and the specific object of any action. Do not paraphrase concrete details into broader categories — the future question will use the specific term, and a paraphrase makes the memory unfindable. Pattern (anti-pattern → correct): + - "researched " → preserve . Do not collapse to "researched options". + - "identifies as " → preserve the qualifier. Do not drop it to the bare noun. + - "went to the " → name it. Do not collapse to "a place". + +2. **Capture facts about every named participant, not only the user.** A conversation may involve more than one named person — a friend the user mentions, a colleague, a family member, or (in a multi-party transcript) another speaker. Facts each named person shares about themselves AND the user's specific observations about them are both worth indexing. Attribute each candidate to the right named person — never "the speakers" collectively, never just "the user" when the fact is about someone else. + - "My friend Alex is moving to Berlin next month for a SAP job" → emit "Alex is moving to Berlin in " AND "Alex has a new job at SAP" (attributed to Alex). + - In a two-party transcript: if Caroline says "I researched adoption agencies" and Melanie says "I made a plate in pottery class on August 24", BOTH facts get captured — one attributed to Caroline, the other to Melanie. Do not bias toward the first speaker, the more talkative one, or the apparent "user" persona; index both sides. + +3. **Emit a candidate for every dated event.** A "dated event" is any assertion that maps to a specific point in time: absolute ("May 7", "in 2022", "last December"), or relative to the session_date ("yesterday", "last Tuesday", "two weeks ago", "this morning"). Resolve relative dates against the session_date and emit the absolute date in the content. Do NOT generalise dated events into untimed habits ("the user participates in " loses the date and the answer to "when did it happen?" is now gone). When the same event has both a date-anchored framing and a thematic framing, emit BOTH as separate candidates. + +4. **Capture precursor actions alongside outcomes.** When the conversation describes a sequence ("researched X then chose Y", "tried A and settled on B", "considered and picked "), emit a candidate for the precursor (the research, the try, the consideration) AND a candidate for the outcome. Future questions can target either step. Outcomes never erase precursors — "What did the user research?" and "What did the user choose?" have different answers and need different candidates. + +5. **Don't squash enumerations.** If the speaker lists three things (hobbies, books, people, options), emit them as three candidates or as one candidate that names all three. Never collapse to a category label ("outdoor activities", "various books") — the future question will name one item from the list, and the category label won't match. + +6. **Each candidate is self-contained.** Include the actor's name and any context the assertion needs to be understood in isolation, with no surrounding messages. + +7. **Pick the right kind. For \`preference\` and \`decision\`, follow the topic-line rule.** + - \`fact\` — a stable assertion about the world or the speakers (what they did, where they live, how they identify, what is true of them right now). Default for most assertions. + - \`preference\` — a soft-but-durable preference that should bias future behaviour (a baking style they like, a tool they choose, a writing convention they hold, a likes/dislikes pattern). REQUIRED FORMAT: \`content\` MUST start with a single \`topic: value\` line followed by a blank line and prose. The conflict detector parses that first line; without it the entire extract_memory batch is rejected with INVALID_INPUT, dropping all candidates that travelled with the offender. Do this best-effort on every preference candidate: +\`\`\` +baking-style: lemon-themed +[blank line] +The speaker has had success with lemon bakes (e.g. the lemon cake at the last gathering) and reaches for lemon recipes when stuck for ideas. +\`\`\` + - \`decision\` — a chosen path among alternatives, often with reasoning. Same \`topic: value\\n\\nprose\` format requirement as preference. Example: \`storage-engine: SQLite\\n\\nChosen for the local-first story; FTS5 built in, no daemon.\` + - \`todo\` — an action the speaker explicitly intends to take in the future. + - \`snippet\` — a reusable code fragment or quote worth preserving verbatim. + When in doubt between \`fact\` and \`preference\`, prefer \`preference\` for forward-looking biases ("the speaker tends to / prefers / reaches for X") and \`fact\` for backward-looking events ("the speaker did Y on Z"). Both can co-exist for the same theme. + +8. **Bias toward inclusion.** Better 20 precise candidates than 5 broad ones. The server dedups via embedding similarity, so two near-equivalent candidates collapse to one row — the cost of over-including is low; the cost of under-including is permanent (the fact is gone). Skip only pleasantries (greetings, "thanks", "lol", small talk). + +9. **Output JSON only.** No markdown fences, no reasoning prose, no commentary. + +Schema: +\`\`\` +[ + { "kind": "fact" | "todo" | "snippet", + "content": "", + "summary": "" } +] +\`\`\` + +If nothing in the session is worth remembering, return \`[]\`. + +**Before you emit, do one pass over the transcript and check:** does every (a) date or time-relative word, (b) proper noun / named entity, (c) action verb with a specific object map to at least one candidate? If a reference is missing, add the candidate now. + +JSON:` +} + +function parseDistillResponse(text: string): DistilledCandidate[] { + // Strip markdown fences the model might emit despite instructions. + const trimmed = text + .trim() + .replace(/^```(?:json)?\s*/i, "") + .replace(/```\s*$/i, "") + .trim() + // Some models prefix with "JSON:" or similar. Take the slice starting at + // the first `[` and ending at the matching last `]`. + const start = trimmed.indexOf("[") + const end = trimmed.lastIndexOf("]") + if (start < 0 || end < start) return [] + const raw = trimmed.slice(start, end + 1) + let parsed: unknown + try { + parsed = JSON.parse(raw) + } catch { + return [] + } + if (!Array.isArray(parsed)) return [] + const candidates: DistilledCandidate[] = [] + for (const item of parsed) { + if (!item || typeof item !== "object") continue + const obj = item as Record + const rawKind = typeof obj.kind === "string" ? obj.kind.toLowerCase() : null + const content = typeof obj.content === "string" ? obj.content.trim() : null + if (!rawKind || !content) continue + if (content.length === 0) continue + // The five valid kinds are accepted as-is. An unrecognised kind + // (e.g. the LLM hallucinated "memory" or "thought") coerces to + // `fact` — the safe free-form catch-all. preference/decision + // candidates with a missing topic-line are NOT coerced here; they + // pass through and let Memento's INVALID_INPUT response surface + // the prompt-following failure as honestly as a real assistant + // would face it. + const kind: DistilledKind = DISTILL_KINDS.includes(rawKind as DistilledKind) + ? (rawKind as DistilledKind) + : "fact" + const summary = + typeof obj.summary === "string" && obj.summary.trim().length > 0 + ? obj.summary.trim().slice(0, 60) + : undefined + candidates.push({ kind, content, ...(summary ? { summary } : {}) }) + } + return candidates +} + +export interface DistillResult { + candidates: DistilledCandidate[] + rawResponseChars: number + modelAlias: string + promptTokens?: number + responseTokens?: number +} + +export async function distillSession(session: UnifiedSession): Promise { + const modelAlias = process.env.MEMENTO_DISTILL_MODEL ?? DEFAULT_ANSWERING_MODEL + const { client, modelConfig } = getLLMClient(modelAlias) + const prompt = buildDistillPrompt(session) + + // Build params in the same shape memorybench's answer phase uses. + const params: Record = { + model: (client as unknown as (id: string) => unknown)(modelConfig.id), + prompt, + } + // Force temperature=0 for distillation regardless of the model's + // default. memorybench pins Gemini-3 at temp=1 for answer-generation + // (because lower allegedly causes "issues" with reasoning) but + // distillation needs determinism — the same conversation should + // always produce the same candidate set, or the bench is measuring + // sampling variance instead of system capability. Override via + // MEMENTO_DISTILL_TEMPERATURE if the default-zero turns out to + // break a specific model in practice. + if (modelConfig.supportsTemperature) { + const envTemp = process.env.MEMENTO_DISTILL_TEMPERATURE + const tempOverride = envTemp !== undefined ? Number(envTemp) : 0 + if (!Number.isFinite(tempOverride) || tempOverride < 0 || tempOverride > 2) { + throw new Error(`MEMENTO_DISTILL_TEMPERATURE must be a number in [0, 2] (got: ${envTemp})`) + } + params.temperature = tempOverride + } + // Use the right maxTokens parameter name for the model family. + params[modelConfig.maxTokensParam] = 4000 + + const { text, usage } = await generateText(params as Parameters[0]) + const candidates = parseDistillResponse(text) + return { + candidates, + rawResponseChars: text.length, + modelAlias, + promptTokens: usage?.inputTokens, + responseTokens: usage?.outputTokens, + } +} diff --git a/src/providers/memento/index.ts b/src/providers/memento/index.ts new file mode 100644 index 0000000..89d83c6 --- /dev/null +++ b/src/providers/memento/index.ts @@ -0,0 +1,419 @@ +// Memento Memory Provider +// +// Memento (https://github.com/veerps57/memento) is a local-first, +// MCP-native memory layer for AI assistants. It runs as a stdio MCP +// server over a local SQLite database. This provider spawns one +// `memento serve` for the lifetime of a memorybench run and routes +// ingest / search / clear through MCP tool calls. +// +// Memento is designed to store **distilled assertions, not transcripts**: +// the calling AI assistant uses its own LLM to decide what's worth +// remembering, then hands those distilled candidates to Memento's +// `extract_memory` MCP tool. Memento embeds, scrubs, dedups, and +// persists. To faithfully represent that flow inside the bench (which +// only gives the provider raw `UnifiedSession` transcripts), this +// provider performs the same distillation step itself — calling the +// configured LLM per session and passing the resulting candidates to +// `extract_memory`. See `./distill.ts`. +// +// Isolation: every benchmark question gets its own Memento `workspace` +// scope keyed by `containerTag` (workspace, not session — Memento's +// `session.id` requires a 26-char ULID while memorybench's +// `containerTag` is an arbitrary string). Memento's architectural +// rule that scope is immutable per memory makes per-question isolation +// reliable. One DB, one server, many scopes. +// +// Env contract: +// +// - MEMENTO_BIN: shell-like command that already understands `serve`. +// Default `"npx -y @psraghuveer/memento"`. Override +// with `"node /abs/path/to/cli.js"` for local dev. +// - MEMENTO_BENCH_DB: SQLite path. Default `/tmp/memento-bench-.db`. +// - MEMENTO_DISTILL_MODEL: model alias for the distillation LLM +// (defaults to memorybench's answering model). +// - MEMENTO_BENCH_SEARCH_LIMIT: top-K returned by search_memory. Default 30. +// - MEMENTO_AWAIT_INDEXING_MS: per-question polling deadline. Default 180000. + +import { Client } from "@modelcontextprotocol/sdk/client/index.js" +import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js" +import type { + IndexingProgressCallback, + IngestOptions, + IngestResult, + Provider, + ProviderConfig, + SearchOptions, +} from "../../types/provider" +import type { UnifiedSession } from "../../types/unified" +import { logger } from "../../utils/logger" +import { distillSession } from "./distill" +import { parseSearchPage, parseToolResultJson } from "./mcp-helpers" +import { MEMENTO_PROMPTS } from "./prompts" + +// Per-question isolation primitive. Workspace scope's `path` accepts any +// POSIX-absolute string; we synthesize `/memorybench/`. +interface MementoScope { + type: "workspace" + path: string +} + +function scopeForContainer(containerTag: string): MementoScope { + return { type: "workspace", path: `/memorybench/${containerTag}` } +} + +// Memento's TagSchema enforces /^[a-z0-9][a-z0-9._:/-]*$/ (lowercase +// alphanumerics plus `._:/-`, 1-64 chars). Benchmark metadata (session +// ids, ISO dates) generally fits already; this normaliser is a safety +// net for unusual inputs. +function sanitizeTagValue(value: string): string { + const lowered = value.toLowerCase().trim() + const cleaned = lowered.replace(/[^a-z0-9._:/-]+/g, "-").replace(/^-+|-+$/g, "") + return cleaned.length > 64 ? cleaned.slice(0, 64) : cleaned +} + +function tag(key: string, value: string): string | null { + const v = sanitizeTagValue(value) + if (!v) return null + const candidate = `${key}:${v}` + return candidate.length > 64 ? candidate.slice(0, 64) : candidate +} + +function parseBinSpec(spec: string): { command: string; baseArgs: string[] } { + const trimmed = spec.trim() + if (trimmed.length === 0) { + throw new Error("MEMENTO_BIN is empty") + } + const parts = trimmed.split(/\s+/) + return { command: parts[0]!, baseArgs: parts.slice(1) } +} + +export class MementoProvider implements Provider { + name = "memento" + prompts = MEMENTO_PROMPTS + concurrency = { + // Per-phase tuned for one local stdio server. Ingest is heavier + // than search because each ingest call makes an LLM round-trip + // plus a write batch; we keep it modest to avoid back-pressure + // stalls on the JSON-RPC stream. + default: 10, + ingest: 5, + search: 10, + } + + private client: Client | null = null + private transport: StdioClientTransport | null = null + private dbPath = "" + private binSpec = "" + // 180s gives the local bge-base-en-v1.5 embedder enough headroom to + // finish a per-question haystack before search runs. Override via + // MEMENTO_AWAIT_INDEXING_MS for unusually heavy haystacks. + private awaitIndexingDeadlineMs = 180_000 + // 30 aligns with Supermemory (30), Mem0 (30), and Zep (20+10=30). + private searchLimit = 30 + // Per-conversation distillation cache. memorybench typically issues + // the same haystack sessions across many questions of the same + // conversation (LoCoMo's `conv-26` underlies 5+ questions). Distilling + // the same session every time is wasteful (5× LLM cost) AND amplifies + // per-call LLM variance into per-scope drift. Cache the distill result + // by `session.sessionId` for the lifetime of the provider; subsequent + // ingests of the same session reuse the cached candidates and only + // pay the extract_memory write cost. + private distillCache = new Map>>() + + async initialize(_config: ProviderConfig): Promise { + this.dbPath = process.env.MEMENTO_BENCH_DB ?? `/tmp/memento-bench-${Date.now()}.db` + this.binSpec = process.env.MEMENTO_BIN ?? "npx -y @psraghuveer/memento" + + const deadlineEnv = process.env.MEMENTO_AWAIT_INDEXING_MS + if (deadlineEnv !== undefined) { + const parsed = Number(deadlineEnv) + if (!Number.isFinite(parsed) || parsed <= 0) { + throw new Error(`MEMENTO_AWAIT_INDEXING_MS must be a positive number (got: ${deadlineEnv})`) + } + this.awaitIndexingDeadlineMs = parsed + } + const searchLimitEnv = process.env.MEMENTO_BENCH_SEARCH_LIMIT + if (searchLimitEnv !== undefined) { + const parsed = Number(searchLimitEnv) + if (!Number.isFinite(parsed) || parsed <= 0 || !Number.isInteger(parsed)) { + throw new Error( + `MEMENTO_BENCH_SEARCH_LIMIT must be a positive integer (got: ${searchLimitEnv})` + ) + } + this.searchLimit = parsed + } + + const { command, baseArgs } = parseBinSpec(this.binSpec) + const args = [...baseArgs, "serve", "--db", this.dbPath] + + this.transport = new StdioClientTransport({ + command, + args, + env: process.env as Record, + stderr: "pipe", + }) + + // Drain stderr lines to our logger so a misbehaving server is + // visible in the run output. Lines containing an error-like token + // are bumped to warn; the rest go to info. + this.transport.stderr?.on("data", (chunk: Buffer) => { + for (const line of chunk.toString("utf8").split(/\r?\n/)) { + const trimmed = line.trim() + if (trimmed.length === 0) continue + if (/error|fatal|panic|unhandled|throw/i.test(trimmed)) { + logger.warn(`[memento-server] ${trimmed}`) + } else { + logger.info(`[memento-server] ${trimmed}`) + } + } + }) + + this.client = new Client( + { name: "memorybench-memento-provider", version: "1.0.0" }, + { capabilities: {} } + ) + await this.client.connect(this.transport) + + // memorybench's orchestrator doesn't expose a teardown hook for + // providers, so when the run finishes there's no callback to close + // the MCP client. The transport keeps the cli.js child process + // refed in Node's event loop, which keeps Bun alive forever. Unref + // the child + its pipes so the host can exit normally once its + // main work is done; the orphaned `memento serve` child sees EOF + // on stdin and exits cleanly via its own transport-closed handling. + const childHandle = ( + this.transport as unknown as { + _process?: { + unref?: () => void + stdin?: { unref?: () => void } + stdout?: { unref?: () => void } + stderr?: { unref?: () => void } + } + } + )._process + childHandle?.unref?.() + childHandle?.stdin?.unref?.() + childHandle?.stdout?.unref?.() + childHandle?.stderr?.unref?.() + + const tools = await this.client.listTools() + for (const t of ["extract_memory", "search_memory", "forget_many_memories"]) { + if (!tools.tools.find((x) => x.name === t)) { + throw new Error(`Memento server missing required tool: ${t}`) + } + } + + // Warmup: trigger embedder model load before the first benchmark + // question, so the first real write doesn't pay the cold download. + const warmupScope = scopeForContainer("__warmup__") + try { + await this.client.callTool({ + name: "extract_memory", + arguments: { + candidates: [ + { + kind: "fact", + content: "Provider warmup write to trigger embedder load.", + tags: ["benchmark:memorybench", "warmup"], + }, + ], + scope: warmupScope, + }, + }) + await this.client.callTool({ + name: "forget_many_memories", + arguments: { + filter: { scope: warmupScope }, + reason: "warmup teardown", + confirm: true, + dryRun: false, + }, + }) + } catch (e) { + logger.warn(`Memento warmup failed (continuing): ${(e as Error).message}`) + } + + logger.info(`Initialized Memento provider (db=${this.dbPath}, bin=${this.binSpec})`) + + process.once("beforeExit", () => { + void this.client?.close() + }) + } + + async ingest(sessions: UnifiedSession[], options: IngestOptions): Promise { + if (!this.client) throw new Error("Memento provider not initialized") + const documentIds: string[] = [] + for (const session of sessions) { + documentIds.push(...(await this.ingestSession(session, options))) + } + return { documentIds } + } + + private async ingestSession(session: UnifiedSession, options: IngestOptions): Promise { + const scope = scopeForContainer(options.containerTag) + const sessionDate = session.metadata?.date as string | undefined + const baseTags = [ + "benchmark:memorybench", + tag("session", session.sessionId), + sessionDate ? tag("session-date", sessionDate) : null, + ].filter((t): t is string => t !== null) + + const cached = this.distillCache.get(session.sessionId) + const result = cached ?? (await distillSession(session)) + if (!cached) { + this.distillCache.set(session.sessionId, result) + } + if (result.candidates.length === 0) { + logger.debug(`distill produced 0 candidates for ${session.sessionId} (skipping)`) + return [] + } + + // `extract_memory`'s ExtractionCandidate schema is FLAT — `kind` is a + // string enum, `language` is a top-level optional (NOT nested inside + // kind the way write_memory's discriminated union expects). The + // schema is strict(), so a nested kind would fail validation. + const candidates = result.candidates.map((c) => { + const candidate: Record = { + kind: c.kind, + content: c.content, + tags: baseTags, + } + if (c.summary) candidate.summary = c.summary + if (c.kind === "snippet") candidate.language = "text" + return candidate + }) + + // Chunk into batches of <= 20 to respect Memento's + // `extraction.maxCandidatesPerCall` cap (default 20). The cap is + // operator-tunable but we'd rather not depend on it; a strong + // distillation prompt regularly emits more than 20 candidates from + // a substantive session. + const CHUNK = 20 + for (let i = 0; i < candidates.length; i += CHUNK) { + const slice = candidates.slice(i, i + CHUNK) + const callResult = await this.client!.callTool({ + name: "extract_memory", + arguments: { candidates: slice, scope }, + }) + // Surface validation / scrubber rejections that would otherwise be silent. + parseToolResultJson(callResult) + } + logger.info( + `distill: session=${session.sessionId} model=${result.modelAlias} ` + + `candidates=${result.candidates.length} ` + + `tokens=${result.promptTokens ?? "?"}p/${result.responseTokens ?? "?"}r` + + `${cached ? " (cached)" : ""}` + ) + return result.candidates.map((_, i) => `distill:${session.sessionId}:${i}`) + } + + async awaitIndexing( + result: IngestResult, + containerTag: string, + onProgress?: IndexingProgressCallback + ): Promise { + if (!this.client) throw new Error("Memento provider not initialized") + const total = result.documentIds.length + if (total === 0) { + onProgress?.({ completedIds: [], failedIds: [], total: 0 }) + return + } + + const deadline = Date.now() + this.awaitIndexingDeadlineMs + const completed = new Set() + const failed = new Set() + const scope = scopeForContainer(containerTag) + + onProgress?.({ completedIds: [], failedIds: [], total }) + + // Poll the per-question scope until either every row's embedding is + // ready (present or disabled) or the deadline expires. `total` is the + // synthetic candidate count from ingest; extract_memory may dedup to + // fewer rows. We're ready when at least one row exists and none are + // still pending. + while (Date.now() < deadline) { + const probe = await this.client.callTool({ + name: "search_memory", + arguments: { + text: "user assistant", + scopes: [scope], + limit: 1000, + includeStatuses: ["active"], + projection: "summary", + }, + }) + const page = parseSearchPage(probe) + + for (const r of page.results) { + const status = r.memory.embeddingStatus + if (status === "present" || status === "disabled") { + completed.add(r.memory.id) + } + } + + onProgress?.({ + completedIds: [...completed], + failedIds: [...failed], + total, + }) + + if ( + page.results.length > 0 && + page.results.every((r) => r.memory.embeddingStatus !== "pending") + ) { + return + } + + await new Promise((r) => setTimeout(r, 250)) + } + + logger.warn( + `Memento awaitIndexing hit deadline (${this.awaitIndexingDeadlineMs}ms) for ${containerTag}: ` + + `${completed.size}/${total} ready` + ) + } + + async search(query: string, options: SearchOptions): Promise { + if (!this.client) throw new Error("Memento provider not initialized") + const scope = scopeForContainer(options.containerTag) + // We deliberately ignore `options.limit` (memorybench passes 10 in + // every call) and use the provider-configured `this.searchLimit` + // (default 30). The 10 default is calibrated for slow cloud + // providers; native local retrieval comfortably handles 30+ and + // matching peers (Supermemory, Mem0, Zep) all override it the + // same way. + const result = await this.client.callTool({ + name: "search_memory", + arguments: { + text: query, + scopes: [scope], + limit: this.searchLimit, + projection: "full", + }, + }) + const page = parseSearchPage(result) + return page.results + } + + async clear(containerTag: string): Promise { + if (!this.client) { + logger.warn(`Memento clear called before initialize for ${containerTag}`) + return + } + const result = await this.client.callTool({ + name: "forget_many_memories", + arguments: { + filter: { scope: scopeForContainer(containerTag) }, + reason: "benchmark container teardown", + confirm: true, + dryRun: false, + }, + }) + const payload = parseToolResultJson<{ matched: number; applied: number }>(result) + logger.info( + `Memento clear ${containerTag}: matched=${payload.matched} applied=${payload.applied}` + ) + } +} + +export default MementoProvider diff --git a/src/providers/memento/mcp-helpers.ts b/src/providers/memento/mcp-helpers.ts new file mode 100644 index 0000000..31e5ed2 --- /dev/null +++ b/src/providers/memento/mcp-helpers.ts @@ -0,0 +1,86 @@ +// Small helpers for working with `@modelcontextprotocol/sdk` CallToolResult +// payloads. Memento returns its command results as a JSON object encoded in +// a single `text` content part — we drill in here so the main provider file +// stays focused on lifecycle and orchestration. + +// `callTool` returns a union for backwards compat: either the modern +// `CallToolResult` (with `content`) or the legacy `{toolResult: unknown}`. +// Memento always returns the modern shape, but we accept the wider union +// here so the helpers compose with `client.callTool(...)` directly without +// callers having to pass the result schema. +type CallToolResultLike = { + content?: Array<{ type: string; text?: string }> + isError?: boolean + toolResult?: unknown + [key: string]: unknown +} + +/** + * Parse a single tool result whose payload is `{...}` encoded as one + * `text` content part. Throws when the result is missing, errored, or + * malformed — the caller is expected to surface that as a phase failure. + */ +export function parseToolResultJson(result: CallToolResultLike): T { + if (result.isError) { + const message = textOf(result) ?? "" + throw new Error(`Memento tool call returned isError: ${message}`) + } + const text = textOf(result) + if (!text) { + throw new Error("Memento tool call returned no text content") + } + try { + return JSON.parse(text) as T + } catch (e) { + throw new Error(`Memento tool call returned non-JSON text: ${(e as Error).message}`) + } +} + +interface MementoSearchPage { + results: Array<{ + memory: { + id: string + scope: { type: string; id?: string; path?: string; remote?: string; branch?: string } + kind: { type: string } + tags: string[] + content: string | null + createdAt: string + lastConfirmedAt: string + status: string + embedding: number[] | null + embeddingStatus: "present" | "pending" | "disabled" + pinned: boolean + summary: string | null + supersedes: string[] | null + supersededBy: string | null + sensitive: boolean + redacted: boolean + storedConfidence: number + } + score: number + breakdown?: { + fts: number + vector: number + confidence: number + recency: number + scope: number + pinned: number + } + conflicts?: Array<{ conflictId: string; otherMemoryId: string; kind: string }> + }> + nextCursor: string | null +} + +export function parseSearchPage(result: CallToolResultLike): MementoSearchPage { + return parseToolResultJson(result) +} + +export type { MementoSearchPage } + +function textOf(result: CallToolResultLike): string | null { + if (!Array.isArray(result.content)) return null + for (const part of result.content) { + if (part.type === "text" && typeof part.text === "string") return part.text + } + return null +} diff --git a/src/providers/memento/prompts.ts b/src/providers/memento/prompts.ts new file mode 100644 index 0000000..ef90cdb --- /dev/null +++ b/src/providers/memento/prompts.ts @@ -0,0 +1,67 @@ +import type { ProviderPrompts } from "../../types/prompts" +import type { MementoSearchPage } from "./mcp-helpers" + +type MementoResult = MementoSearchPage["results"][number] + +function buildMementoContext(context: unknown[]): string { + const results = context as MementoResult[] + if (results.length === 0) { + return "No memories matched this query in the per-question workspace scope." + } + return results + .map((r, i) => { + const content = r.memory.content ?? "[redacted]" + const sessionDate = r.memory.tags + .find((t) => t.startsWith("session-date:")) + ?.slice("session-date:".length) + const header = + `=== Memory ${i + 1} (score=${r.score.toFixed(3)}` + + (sessionDate ? `, session_date=${sessionDate}` : "") + + ", kind=" + + r.memory.kind.type + + ") ===" + return `${header}\n${content}` + }) + .join("\n\n") +} + +export function buildMementoAnswerPrompt( + question: string, + context: unknown[], + questionDate?: string +): string { + const retrievedContext = buildMementoContext(context) + + return `You are answering a question over a long, multi-session conversation. Memento — a local-first MCP memory layer — has retrieved the distilled memories most relevant to the question. Each memory is a self-contained assertion an AI assistant chose to remember from a single session of the original conversation. + +Question: ${question} +Question Date: ${questionDate || "Not specified"} + +Retrieved memories (highest-ranked first): +${retrievedContext} + +**Understanding the context:** +- Each "Memory" block is one distilled assertion (a fact, preference, decision, todo, or snippet) extracted from one session of the conversation. +- \`score\` is the combined FTS + vector retrieval score (higher = more relevant). +- \`session_date\`, when present, is the date of the session the memory was distilled from. Relative time expressions inside a memory are typically already resolved against the session_date during distillation, but if you see one that isn't, resolve it now. +- \`kind\` describes the memory type the assistant chose at distillation time. + +**How to answer:** +1. Scan the retrieved memories for assertions that directly answer the question. +2. Cross-reference memories when the question requires combining information from multiple sessions. +3. For time-based questions, prefer absolute dates already present in memories; only fall back to relative resolution if no absolute is given. +4. When the ground truth expects a specific format (e.g. "7 May 2023"), emit the absolute value. +5. If the retrieved memories do not contain enough information to answer, respond exactly "I don't know". + +Reasoning: +[Your step-by-step reasoning here] + +Answer: +[Your final answer here]` +} + +export const MEMENTO_PROMPTS: ProviderPrompts = { + answerPrompt: buildMementoAnswerPrompt, +} + +export default MEMENTO_PROMPTS diff --git a/src/types/provider.ts b/src/types/provider.ts index cdc0228..b3e77c4 100644 --- a/src/types/provider.ts +++ b/src/types/provider.ts @@ -47,4 +47,4 @@ export interface Provider { clear(containerTag: string): Promise } -export type ProviderName = "supermemory" | "mem0" | "zep" | "filesystem" | "rag" +export type ProviderName = "supermemory" | "mem0" | "zep" | "filesystem" | "rag" | "memento" diff --git a/src/utils/config.ts b/src/utils/config.ts index 8ac1268..c3ba028 100644 --- a/src/utils/config.ts +++ b/src/utils/config.ts @@ -30,6 +30,8 @@ export function getProviderConfig(provider: string): { apiKey: string; baseUrl?: return { apiKey: config.openaiApiKey } // Filesystem uses OpenAI for memory extraction case "rag": return { apiKey: config.openaiApiKey } // RAG provider uses OpenAI for embeddings + case "memento": + return { apiKey: "none" } // Memento runs as a local stdio subprocess; no API key needed default: throw new Error(`Unknown provider: ${provider}`) } diff --git a/src/utils/models.ts b/src/utils/models.ts index b29ac80..70ae48c 100644 --- a/src/utils/models.ts +++ b/src/utils/models.ts @@ -131,6 +131,15 @@ export const MODEL_CONFIGS: Record = { }, // Anthropic - All Claude models (support temperature) + "opus-4.6": { + id: "claude-opus-4-6", + provider: "anthropic", + displayName: "Claude Opus 4.6", + supportsTemperature: true, + defaultTemperature: 0, + maxTokensParam: "maxTokens", + defaultMaxTokens: 1000, + }, "opus-4.5": { id: "claude-opus-4-5-20251101", provider: "anthropic", @@ -140,6 +149,15 @@ export const MODEL_CONFIGS: Record = { maxTokensParam: "maxTokens", defaultMaxTokens: 1000, }, + "sonnet-4.6": { + id: "claude-sonnet-4-6", + provider: "anthropic", + displayName: "Claude Sonnet 4.6", + supportsTemperature: true, + defaultTemperature: 0, + maxTokensParam: "maxTokens", + defaultMaxTokens: 1000, + }, "sonnet-4.5": { id: "claude-sonnet-4-5-20250929", provider: "anthropic",