Compare commits
91 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6ddf1669f4 | |||
| d3d2e89fd5 | |||
| 43175d85f2 | |||
| 661ecb9f0f | |||
| 09f181c577 | |||
| 3497f26a00 | |||
| e9c0547423 | |||
| 25bc1b7426 | |||
| fee344db85 | |||
| b611f69381 | |||
| c2862831db | |||
| 1678e682ee | |||
| ced4af681f | |||
| 565db27121 | |||
| c0ac9f81fa | |||
| 879c68555c | |||
| c2e723352e | |||
| 716f021df8 | |||
| 7fa996261d | |||
| 87bd2d91e0 | |||
| 15a930dac2 | |||
| fa9abc77a6 | |||
| 4e448269bc | |||
| 29d131f7aa | |||
| 42fe129330 | |||
| bd5996f41e | |||
| 299c423213 | |||
| ce0531a13e | |||
| 513b56159f | |||
| 1676b3168d | |||
| 8a786bf72d | |||
| a48f34cf77 | |||
| 650b734b1b | |||
| 871a1de7d2 | |||
| e615cc66de | |||
| ca86a164bd | |||
| 2ac3f3956a | |||
| 7a9f03af3f | |||
| 48f1d6f010 | |||
| f0ccd99924 | |||
| c5971193a2 | |||
| ff53fd60b7 | |||
| ffa334537a | |||
| bd7ce2c13c | |||
| 5d767219ff | |||
| 6b744845c3 | |||
| 5b4478458b | |||
| 1751e7bff9 | |||
| 7ae6a8c36a | |||
| 1dcee153b9 | |||
| 4065e846f6 | |||
| 466249113c | |||
| 3e2ae734e7 | |||
| 96b31c4bc0 | |||
| 0117d5838b | |||
| 42a3b4043c | |||
| 158e9111cb | |||
| 9ed1983b85 | |||
| 703e8a035d | |||
| 7ed2faab84 | |||
| 0d66d3d127 | |||
| 137b7519f7 | |||
| e34f5835bd | |||
| f122eb7c65 | |||
| a5123b8a5e | |||
| 6aa9bffa55 | |||
| d772f9a961 | |||
| 7c841a2bce | |||
| 6a6dfb4935 | |||
| 8b370def80 | |||
| d46464282c | |||
| bb4a239cb1 | |||
| e30f0d91fe | |||
| 9f34e858c7 | |||
| 94bbc13de0 | |||
| a2f01a8fcc | |||
| 30d172e826 | |||
| bb69b036b5 | |||
| b55c51e004 | |||
| 4492e75d04 | |||
| 4d949022f2 | |||
| 3ef034a9e4 | |||
| b90e3c0b76 | |||
| a8eeddde64 | |||
| 09a9e34382 | |||
| 66c4394b40 | |||
| 32575a65fc | |||
| 66901d7393 | |||
| a1eefc31bc | |||
| de471799d1 | |||
| 3951ad4705 |
@@ -189,3 +189,5 @@ eval/
|
||||
qdrant_storage/
|
||||
.crossnote
|
||||
testing.ipynb
|
||||
.weave/
|
||||
|
||||
|
||||
@@ -5,6 +5,15 @@ All notable changes to `@mem0/cli` are documented here.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.2.9] — 2026-06-19
|
||||
|
||||
### Security
|
||||
|
||||
- Telemetry no longer passes the Mem0 API key to its child process via
|
||||
command-line arguments. The context is now sent over stdin, so the key is no
|
||||
longer visible in the process list (`ps`, `/proc/<pid>/cmdline`, Activity
|
||||
Monitor). Fixes #4862.
|
||||
|
||||
## [0.2.8] — 2026-06-01
|
||||
|
||||
### Security
|
||||
|
||||
+11
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mem0/cli",
|
||||
"version": "0.2.8",
|
||||
"version": "0.2.9",
|
||||
"description": "The official CLI for mem0 — the memory layer for AI agents",
|
||||
"type": "module",
|
||||
"bin": {
|
||||
@@ -44,5 +44,15 @@
|
||||
"vitest": "^4.1.0",
|
||||
"@biomejs/biome": "^1.7.0",
|
||||
"@types/node": "^20.0.0"
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"jws@4.0.0": "4.0.1",
|
||||
"langsmith@<0.6.0": "^0.6.0",
|
||||
"tar-fs@>=2.0.0 <2.1.4": "^2.1.4",
|
||||
"picomatch@<2.3.2": "^2.3.2",
|
||||
"postcss@<8.5.10": ">=8.5.10",
|
||||
"esbuild": ">=0.28.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+115
-399
@@ -10,6 +10,7 @@ overrides:
|
||||
tar-fs@>=2.0.0 <2.1.4: ^2.1.4
|
||||
picomatch@<2.3.2: ^2.3.2
|
||||
postcss@<8.5.10: '>=8.5.10'
|
||||
esbuild: '>=0.28.1'
|
||||
|
||||
importers:
|
||||
|
||||
@@ -77,28 +78,24 @@ packages:
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@biomejs/cli-linux-arm64@1.9.4':
|
||||
resolution: {integrity: sha512-fJIW0+LYujdjUgJJuwesP4EjIBl/N/TcOX3IvIHJQNsAqvV2CHIogsmA94BPG6jZATS4Hi+xv4SkBBQSt1N4/g==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@biomejs/cli-linux-x64-musl@1.9.4':
|
||||
resolution: {integrity: sha512-gEhi/jSBhZ2m6wjV530Yy8+fNqG8PAinM3oV7CyO+6c3CEh16Eizm21uHVsyVBEB6RIM8JHIl6AGYCv6Q6Q9Tg==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@biomejs/cli-linux-x64@1.9.4':
|
||||
resolution: {integrity: sha512-lRCJv/Vi3Vlwmbd6K+oQ0KhLHMAysN8lXoCI7XeHlxaajk06u7G+UsFSO01NAs5iYuWKmVZjmiOzJ0OJmGsMwg==}
|
||||
engines: {node: '>=14.21.3'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@biomejs/cli-win32-arm64@1.9.4':
|
||||
resolution: {integrity: sha512-tlbhLk+WXZmgwoIKwHIHEBZUwxml7bRJgk0X2sPyNR3S93cdRq6XulAZRQJ17FYGGzWne0fgrXBKpl7l4M87Hg==}
|
||||
@@ -116,314 +113,158 @@ packages:
|
||||
resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==}
|
||||
engines: {node: '>=0.1.90'}
|
||||
|
||||
'@esbuild/aix-ppc64@0.25.12':
|
||||
resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==}
|
||||
'@esbuild/aix-ppc64@0.28.1':
|
||||
resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.4':
|
||||
resolution: {integrity: sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/android-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==}
|
||||
'@esbuild/android-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm64@0.27.4':
|
||||
resolution: {integrity: sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.25.12':
|
||||
resolution: {integrity: sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==}
|
||||
'@esbuild/android-arm@0.28.1':
|
||||
resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.27.4':
|
||||
resolution: {integrity: sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.25.12':
|
||||
resolution: {integrity: sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==}
|
||||
'@esbuild/android-x64@0.28.1':
|
||||
resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.27.4':
|
||||
resolution: {integrity: sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==}
|
||||
'@esbuild/darwin-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.4':
|
||||
resolution: {integrity: sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.25.12':
|
||||
resolution: {integrity: sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==}
|
||||
'@esbuild/darwin-x64@0.28.1':
|
||||
resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.27.4':
|
||||
resolution: {integrity: sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==}
|
||||
'@esbuild/freebsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.4':
|
||||
resolution: {integrity: sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.25.12':
|
||||
resolution: {integrity: sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==}
|
||||
'@esbuild/freebsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.4':
|
||||
resolution: {integrity: sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==}
|
||||
'@esbuild/linux-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm64@0.27.4':
|
||||
resolution: {integrity: sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.25.12':
|
||||
resolution: {integrity: sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==}
|
||||
'@esbuild/linux-arm@0.28.1':
|
||||
resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.27.4':
|
||||
resolution: {integrity: sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.25.12':
|
||||
resolution: {integrity: sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==}
|
||||
'@esbuild/linux-ia32@0.28.1':
|
||||
resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.27.4':
|
||||
resolution: {integrity: sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.25.12':
|
||||
resolution: {integrity: sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==}
|
||||
'@esbuild/linux-loong64@0.28.1':
|
||||
resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.27.4':
|
||||
resolution: {integrity: sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.25.12':
|
||||
resolution: {integrity: sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==}
|
||||
'@esbuild/linux-mips64el@0.28.1':
|
||||
resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.4':
|
||||
resolution: {integrity: sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.25.12':
|
||||
resolution: {integrity: sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==}
|
||||
'@esbuild/linux-ppc64@0.28.1':
|
||||
resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.4':
|
||||
resolution: {integrity: sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.25.12':
|
||||
resolution: {integrity: sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==}
|
||||
'@esbuild/linux-riscv64@0.28.1':
|
||||
resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.4':
|
||||
resolution: {integrity: sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.25.12':
|
||||
resolution: {integrity: sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==}
|
||||
'@esbuild/linux-s390x@0.28.1':
|
||||
resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.27.4':
|
||||
resolution: {integrity: sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.25.12':
|
||||
resolution: {integrity: sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==}
|
||||
'@esbuild/linux-x64@0.28.1':
|
||||
resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.27.4':
|
||||
resolution: {integrity: sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==}
|
||||
'@esbuild/netbsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.4':
|
||||
resolution: {integrity: sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.25.12':
|
||||
resolution: {integrity: sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==}
|
||||
'@esbuild/netbsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.4':
|
||||
resolution: {integrity: sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==}
|
||||
'@esbuild/openbsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.4':
|
||||
resolution: {integrity: sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.25.12':
|
||||
resolution: {integrity: sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==}
|
||||
'@esbuild/openbsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.4':
|
||||
resolution: {integrity: sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openharmony-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==}
|
||||
'@esbuild/openharmony-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.4':
|
||||
resolution: {integrity: sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@esbuild/sunos-x64@0.25.12':
|
||||
resolution: {integrity: sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==}
|
||||
'@esbuild/sunos-x64@0.28.1':
|
||||
resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/sunos-x64@0.27.4':
|
||||
resolution: {integrity: sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.25.12':
|
||||
resolution: {integrity: sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==}
|
||||
'@esbuild/win32-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-arm64@0.27.4':
|
||||
resolution: {integrity: sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.25.12':
|
||||
resolution: {integrity: sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==}
|
||||
'@esbuild/win32-ia32@0.28.1':
|
||||
resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.27.4':
|
||||
resolution: {integrity: sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.25.12':
|
||||
resolution: {integrity: sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.27.4':
|
||||
resolution: {integrity: sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==}
|
||||
'@esbuild/win32-x64@0.28.1':
|
||||
resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@@ -475,79 +316,66 @@ packages:
|
||||
resolution: {integrity: sha512-RzeBwv0B3qtVBWtcuABtSuCzToo2IEAIQrcyB/b2zMvBWVbjo8bZDjACUpnaafaxhTw2W+imQbP2BD1usasK4g==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.60.0':
|
||||
resolution: {integrity: sha512-Sf7zusNI2CIU1HLzuu9Tc5YGAHEZs5Lu7N1ssJG4Tkw6e0MEsN7NdjUDDfGNHy2IU+ENyWT+L2obgWiguWibWQ==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.60.0':
|
||||
resolution: {integrity: sha512-DX2x7CMcrJzsE91q7/O02IJQ5/aLkVtYFryqCjduJhUfGKG6yJV8hxaw8pZa93lLEpPTP/ohdN4wFz7yp/ry9A==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.60.0':
|
||||
resolution: {integrity: sha512-09EL+yFVbJZlhcQfShpswwRZ0Rg+z/CsSELFCnPt3iK+iqwGsI4zht3secj5vLEs957QvFFXnzAT0FFPIxSrkQ==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.60.0':
|
||||
resolution: {integrity: sha512-i9IcCMPr3EXm8EQg5jnja0Zyc1iFxJjZWlb4wr7U2Wx/GrddOuEafxRdMPRYVaXjgbhvqalp6np07hN1w9kAKw==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.60.0':
|
||||
resolution: {integrity: sha512-DGzdJK9kyJ+B78MCkWeGnpXJ91tK/iKA6HwHxF4TAlPIY7GXEvMe8hBFRgdrR9Ly4qebR/7gfUs9y2IoaVEyog==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.60.0':
|
||||
resolution: {integrity: sha512-RwpnLsqC8qbS8z1H1AxBA1H6qknR4YpPR9w2XX0vo2Sz10miu57PkNcnHVaZkbqyw/kUWfKMI73jhmfi9BRMUQ==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.60.0':
|
||||
resolution: {integrity: sha512-Z8pPf54Ly3aqtdWC3G4rFigZgNvd+qJlOE52fmko3KST9SoGfAdSRCwyoyG05q1HrrAblLbk1/PSIV+80/pxLg==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.60.0':
|
||||
resolution: {integrity: sha512-3a3qQustp3COCGvnP4SvrMHnPQ9d1vzCakQVRTliaz8cIp/wULGjiGpbcqrkv0WrHTEp8bQD/B3HBjzujVWLOA==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.60.0':
|
||||
resolution: {integrity: sha512-pjZDsVH/1VsghMJ2/kAaxt6dL0psT6ZexQVrijczOf+PeP2BUqTHYejk3l6TlPRydggINOeNRhvpLa0AYpCWSQ==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.60.0':
|
||||
resolution: {integrity: sha512-3ObQs0BhvPgiUVZrN7gqCSvmFuMWvWvsjG5ayJ3Lraqv+2KhOsp+pUbigqbeWqueGIsnn+09HBw27rJ+gYK4VQ==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.60.0':
|
||||
resolution: {integrity: sha512-EtylprDtQPdS5rXvAayrNDYoJhIz1/vzN2fEubo3yLE7tfAw+948dO0g4M0vkTVFhKojnF+n6C8bDNe+gDRdTg==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.60.0':
|
||||
resolution: {integrity: sha512-k09oiRCi/bHU9UVFqD17r3eJR9bn03TyKraCrlz5ULFJGdJGi7VOmm9jl44vOJvRJ6P7WuBi/s2A97LxxHGIdw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-openbsd-x64@4.60.0':
|
||||
resolution: {integrity: sha512-1o/0/pIhozoSaDJoDcec+IVLbnRtQmHwPV730+AOD29lHEEo4F5BEUB24H0OBdhbBBDwIOSuf7vgg0Ywxdfiiw==}
|
||||
@@ -658,7 +486,7 @@ packages:
|
||||
resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
peerDependencies:
|
||||
esbuild: '>=0.18'
|
||||
esbuild: '>=0.28.1'
|
||||
|
||||
cac@6.7.14:
|
||||
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||
@@ -738,13 +566,8 @@ packages:
|
||||
es-module-lexer@2.1.0:
|
||||
resolution: {integrity: sha512-n27zTYMjYu1aj4MjCWzSP7G9r75utsaoc8m61weK+W8JMBGGQybd43GstCXZ3WNmSFtGT9wi59qQTW6mhTR5LQ==}
|
||||
|
||||
esbuild@0.25.12:
|
||||
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
esbuild@0.27.4:
|
||||
resolution: {integrity: sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==}
|
||||
esbuild@0.28.1:
|
||||
resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
@@ -1164,160 +987,82 @@ snapshots:
|
||||
'@colors/colors@1.5.0':
|
||||
optional: true
|
||||
|
||||
'@esbuild/aix-ppc64@0.25.12':
|
||||
'@esbuild/aix-ppc64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.4':
|
||||
'@esbuild/android-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.25.12':
|
||||
'@esbuild/android-arm@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.27.4':
|
||||
'@esbuild/android-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.25.12':
|
||||
'@esbuild/darwin-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.27.4':
|
||||
'@esbuild/darwin-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.25.12':
|
||||
'@esbuild/freebsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.27.4':
|
||||
'@esbuild/freebsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.25.12':
|
||||
'@esbuild/linux-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.4':
|
||||
'@esbuild/linux-arm@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.25.12':
|
||||
'@esbuild/linux-ia32@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.27.4':
|
||||
'@esbuild/linux-loong64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.25.12':
|
||||
'@esbuild/linux-mips64el@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.4':
|
||||
'@esbuild/linux-ppc64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.25.12':
|
||||
'@esbuild/linux-riscv64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.4':
|
||||
'@esbuild/linux-s390x@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.25.12':
|
||||
'@esbuild/linux-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.27.4':
|
||||
'@esbuild/netbsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.25.12':
|
||||
'@esbuild/netbsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.27.4':
|
||||
'@esbuild/openbsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.25.12':
|
||||
'@esbuild/openbsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.27.4':
|
||||
'@esbuild/openharmony-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.25.12':
|
||||
'@esbuild/sunos-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.27.4':
|
||||
'@esbuild/win32-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.25.12':
|
||||
'@esbuild/win32-ia32@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-arm64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openharmony-arm64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.27.4':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.25.12':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.27.4':
|
||||
'@esbuild/win32-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@jridgewell/gen-mapping@0.3.13':
|
||||
@@ -1492,9 +1237,9 @@ snapshots:
|
||||
widest-line: 4.0.1
|
||||
wrap-ansi: 8.1.0
|
||||
|
||||
bundle-require@5.1.0(esbuild@0.27.4):
|
||||
bundle-require@5.1.0(esbuild@0.28.1):
|
||||
dependencies:
|
||||
esbuild: 0.27.4
|
||||
esbuild: 0.28.1
|
||||
load-tsconfig: 0.2.5
|
||||
|
||||
cac@6.7.14: {}
|
||||
@@ -1547,63 +1292,34 @@ snapshots:
|
||||
|
||||
es-module-lexer@2.1.0: {}
|
||||
|
||||
esbuild@0.25.12:
|
||||
esbuild@0.28.1:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.25.12
|
||||
'@esbuild/android-arm': 0.25.12
|
||||
'@esbuild/android-arm64': 0.25.12
|
||||
'@esbuild/android-x64': 0.25.12
|
||||
'@esbuild/darwin-arm64': 0.25.12
|
||||
'@esbuild/darwin-x64': 0.25.12
|
||||
'@esbuild/freebsd-arm64': 0.25.12
|
||||
'@esbuild/freebsd-x64': 0.25.12
|
||||
'@esbuild/linux-arm': 0.25.12
|
||||
'@esbuild/linux-arm64': 0.25.12
|
||||
'@esbuild/linux-ia32': 0.25.12
|
||||
'@esbuild/linux-loong64': 0.25.12
|
||||
'@esbuild/linux-mips64el': 0.25.12
|
||||
'@esbuild/linux-ppc64': 0.25.12
|
||||
'@esbuild/linux-riscv64': 0.25.12
|
||||
'@esbuild/linux-s390x': 0.25.12
|
||||
'@esbuild/linux-x64': 0.25.12
|
||||
'@esbuild/netbsd-arm64': 0.25.12
|
||||
'@esbuild/netbsd-x64': 0.25.12
|
||||
'@esbuild/openbsd-arm64': 0.25.12
|
||||
'@esbuild/openbsd-x64': 0.25.12
|
||||
'@esbuild/openharmony-arm64': 0.25.12
|
||||
'@esbuild/sunos-x64': 0.25.12
|
||||
'@esbuild/win32-arm64': 0.25.12
|
||||
'@esbuild/win32-ia32': 0.25.12
|
||||
'@esbuild/win32-x64': 0.25.12
|
||||
|
||||
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
|
||||
'@esbuild/aix-ppc64': 0.28.1
|
||||
'@esbuild/android-arm': 0.28.1
|
||||
'@esbuild/android-arm64': 0.28.1
|
||||
'@esbuild/android-x64': 0.28.1
|
||||
'@esbuild/darwin-arm64': 0.28.1
|
||||
'@esbuild/darwin-x64': 0.28.1
|
||||
'@esbuild/freebsd-arm64': 0.28.1
|
||||
'@esbuild/freebsd-x64': 0.28.1
|
||||
'@esbuild/linux-arm': 0.28.1
|
||||
'@esbuild/linux-arm64': 0.28.1
|
||||
'@esbuild/linux-ia32': 0.28.1
|
||||
'@esbuild/linux-loong64': 0.28.1
|
||||
'@esbuild/linux-mips64el': 0.28.1
|
||||
'@esbuild/linux-ppc64': 0.28.1
|
||||
'@esbuild/linux-riscv64': 0.28.1
|
||||
'@esbuild/linux-s390x': 0.28.1
|
||||
'@esbuild/linux-x64': 0.28.1
|
||||
'@esbuild/netbsd-arm64': 0.28.1
|
||||
'@esbuild/netbsd-x64': 0.28.1
|
||||
'@esbuild/openbsd-arm64': 0.28.1
|
||||
'@esbuild/openbsd-x64': 0.28.1
|
||||
'@esbuild/openharmony-arm64': 0.28.1
|
||||
'@esbuild/sunos-x64': 0.28.1
|
||||
'@esbuild/win32-arm64': 0.28.1
|
||||
'@esbuild/win32-ia32': 0.28.1
|
||||
'@esbuild/win32-x64': 0.28.1
|
||||
|
||||
estree-walker@3.0.3:
|
||||
dependencies:
|
||||
@@ -1840,12 +1556,12 @@ snapshots:
|
||||
|
||||
tsup@8.5.1(postcss@8.5.15)(tsx@4.21.0)(typescript@5.9.3):
|
||||
dependencies:
|
||||
bundle-require: 5.1.0(esbuild@0.27.4)
|
||||
bundle-require: 5.1.0(esbuild@0.28.1)
|
||||
cac: 6.7.14
|
||||
chokidar: 4.0.3
|
||||
consola: 3.4.2
|
||||
debug: 4.4.3
|
||||
esbuild: 0.27.4
|
||||
esbuild: 0.28.1
|
||||
fix-dts-default-cjs-exports: 1.0.1
|
||||
joycon: 3.1.1
|
||||
picocolors: 1.1.1
|
||||
@@ -1868,7 +1584,7 @@ snapshots:
|
||||
|
||||
tsx@4.21.0:
|
||||
dependencies:
|
||||
esbuild: 0.27.4
|
||||
esbuild: 0.28.1
|
||||
get-tsconfig: 4.13.7
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
@@ -1883,7 +1599,7 @@ snapshots:
|
||||
|
||||
vite@6.4.3(@types/node@20.19.37)(tsx@4.21.0):
|
||||
dependencies:
|
||||
esbuild: 0.25.12
|
||||
esbuild: 0.28.1
|
||||
fdir: 6.5.0(picomatch@4.0.4)
|
||||
picomatch: 4.0.4
|
||||
postcss: 8.5.15
|
||||
|
||||
@@ -11,3 +11,4 @@ overrides:
|
||||
tar-fs@>=2.0.0 <2.1.4: ^2.1.4
|
||||
picomatch@<2.3.2: ^2.3.2
|
||||
"postcss@<8.5.10": ">=8.5.10"
|
||||
"esbuild": ">=0.28.1"
|
||||
|
||||
@@ -145,11 +145,11 @@ export function captureEvent(
|
||||
anonDistinctIdToAlias: anonIdToAlias,
|
||||
};
|
||||
|
||||
const child = spawn(
|
||||
process.execPath,
|
||||
[SENDER_SCRIPT, JSON.stringify(context)],
|
||||
{ detached: true, stdio: "ignore" },
|
||||
);
|
||||
const child = spawn(process.execPath, [SENDER_SCRIPT], {
|
||||
detached: true,
|
||||
stdio: ["pipe", "ignore", "ignore"],
|
||||
});
|
||||
child.stdin?.end(JSON.stringify(context));
|
||||
child.unref();
|
||||
} catch {
|
||||
/* silently swallow */
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
/**
|
||||
* Standalone telemetry sender — runs as a detached child process.
|
||||
*
|
||||
* Usage: node telemetry-sender.cjs '<json context>'
|
||||
* Usage: node telemetry-sender.cjs (JSON context is read from stdin; a single
|
||||
* argv argument is still accepted as a legacy fallback)
|
||||
*
|
||||
* This script is spawned by telemetry.captureEvent() and runs independently
|
||||
* of the parent CLI process. It:
|
||||
@@ -19,6 +20,31 @@
|
||||
const https = require("https");
|
||||
const fs = require("fs");
|
||||
|
||||
function loadContext() {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (process.argv[2]) {
|
||||
try {
|
||||
resolve(JSON.parse(process.argv[2]));
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
let data = "";
|
||||
process.stdin.setEncoding("utf8");
|
||||
process.stdin.on("data", (chunk) => (data += chunk));
|
||||
process.stdin.on("end", () => {
|
||||
try {
|
||||
resolve(JSON.parse(data));
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
});
|
||||
process.stdin.on("error", reject);
|
||||
});
|
||||
}
|
||||
|
||||
function httpsRequest(url, method, headers, body) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const u = new URL(url);
|
||||
@@ -108,7 +134,7 @@ async function sendIdentifyEvent(ctx, payload, anonId) {
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const ctx = JSON.parse(process.argv[2]);
|
||||
const ctx = await loadContext();
|
||||
const payload = ctx.payload;
|
||||
|
||||
if (ctx.needsEmail && ctx.mem0ApiKey) {
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const mockLoadConfig = vi.fn();
|
||||
const mockSaveConfig = vi.fn();
|
||||
const mockSpawn = vi.fn();
|
||||
|
||||
vi.mock("../src/config.js", () => ({
|
||||
CONFIG_FILE: "/tmp/mem0-config.json",
|
||||
loadConfig: mockLoadConfig,
|
||||
saveConfig: mockSaveConfig,
|
||||
}));
|
||||
|
||||
vi.mock("node:child_process", () => ({
|
||||
spawn: mockSpawn,
|
||||
}));
|
||||
|
||||
describe("captureEvent", () => {
|
||||
beforeEach(() => {
|
||||
vi.resetModules();
|
||||
mockLoadConfig.mockReset();
|
||||
mockSaveConfig.mockReset();
|
||||
mockSpawn.mockReset();
|
||||
delete process.env.MEM0_TELEMETRY;
|
||||
});
|
||||
|
||||
it("pipes the telemetry context through stdin instead of argv", async () => {
|
||||
mockLoadConfig.mockReturnValue({
|
||||
platform: {
|
||||
apiKey: "m0-node-secret",
|
||||
baseUrl: "https://api.mem0.ai",
|
||||
userEmail: "",
|
||||
},
|
||||
telemetry: {
|
||||
anonymousId: "cli-anon-node",
|
||||
},
|
||||
});
|
||||
|
||||
const stdin = { end: vi.fn() };
|
||||
const child = { stdin, unref: vi.fn() };
|
||||
mockSpawn.mockReturnValue(child);
|
||||
|
||||
const { captureEvent } = await import("../src/telemetry.js");
|
||||
captureEvent("node_test_event", { case: "stdin-secret" });
|
||||
|
||||
expect(mockSpawn).toHaveBeenCalledTimes(1);
|
||||
const [execPath, args, options] = mockSpawn.mock.calls[0];
|
||||
expect(execPath).toBe(process.execPath);
|
||||
expect(args).toHaveLength(1);
|
||||
expect(String(args[0])).toContain("telemetry-sender.cjs");
|
||||
expect(JSON.stringify(args)).not.toContain("m0-node-secret");
|
||||
expect(options).toMatchObject({ detached: true, stdio: ["pipe", "ignore", "ignore"] });
|
||||
|
||||
expect(stdin.end).toHaveBeenCalledTimes(1);
|
||||
const payload = JSON.parse(stdin.end.mock.calls[0][0]);
|
||||
expect(payload.mem0ApiKey).toBe("m0-node-secret");
|
||||
expect(payload.payload.event).toBe("node_test_event");
|
||||
expect(child.unref).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -5,6 +5,19 @@ All notable changes to `mem0-cli` (Python) are documented here.
|
||||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [0.2.8] — 2026-06-19
|
||||
|
||||
### Security
|
||||
|
||||
- Telemetry no longer passes the Mem0 API key to its child process via
|
||||
command-line arguments. The context is now sent over stdin, so the key is no
|
||||
longer visible in the process list (`ps`, `/proc/<pid>/cmdline`, Activity
|
||||
Monitor). Fixes #4862.
|
||||
|
||||
### Fixed
|
||||
|
||||
- `__version__` now matches the packaged version (was stale at 0.2.4).
|
||||
|
||||
## [0.2.7] — 2026-05-20
|
||||
|
||||
### Added
|
||||
|
||||
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
||||
|
||||
[project]
|
||||
name = "mem0-cli"
|
||||
version = "0.2.7"
|
||||
version = "0.2.8"
|
||||
description = "The official CLI for mem0 — the memory layer for AI agents"
|
||||
readme = "README.md"
|
||||
license = "Apache-2.0"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
"""mem0 CLI — the command-line interface for the mem0 memory layer."""
|
||||
|
||||
__version__ = "0.2.4"
|
||||
__version__ = "0.2.8"
|
||||
|
||||
@@ -137,12 +137,19 @@ def capture_event(
|
||||
"anon_distinct_id_to_alias": anon_id_to_alias,
|
||||
}
|
||||
|
||||
subprocess.Popen(
|
||||
[sys.executable, "-m", "mem0_cli.telemetry_sender", json.dumps(context)],
|
||||
child = subprocess.Popen(
|
||||
[sys.executable, "-m", "mem0_cli.telemetry_sender"],
|
||||
stdin=subprocess.PIPE,
|
||||
stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL,
|
||||
start_new_session=True,
|
||||
close_fds=True,
|
||||
text=True,
|
||||
)
|
||||
if child.stdin:
|
||||
with contextlib.suppress(Exception):
|
||||
child.stdin.write(json.dumps(context))
|
||||
with contextlib.suppress(Exception):
|
||||
child.stdin.close()
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
"""Standalone telemetry sender — runs as a detached subprocess.
|
||||
|
||||
Usage: python -m mem0_cli.telemetry_sender '<json context>'
|
||||
Usage: python -m mem0_cli.telemetry_sender (JSON context is read from stdin;
|
||||
a single argv argument is still accepted as a legacy fallback)
|
||||
|
||||
This module is spawned by telemetry.capture_event() and runs independently
|
||||
of the parent CLI process. It:
|
||||
@@ -20,8 +21,18 @@ import sys
|
||||
import urllib.request
|
||||
|
||||
|
||||
def _load_context() -> dict:
|
||||
"""Load telemetry context from stdin, falling back to argv for compatibility."""
|
||||
raw = ""
|
||||
if not sys.stdin.isatty():
|
||||
raw = sys.stdin.read().strip()
|
||||
if not raw and len(sys.argv) > 1:
|
||||
raw = sys.argv[1]
|
||||
return json.loads(raw)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
ctx = json.loads(sys.argv[1])
|
||||
ctx = _load_context()
|
||||
payload = ctx["payload"]
|
||||
|
||||
if ctx.get("needs_email") and ctx.get("mem0_api_key"):
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
"""Tests for telemetry subprocess secret handling."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import io
|
||||
import json
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
from mem0_cli.config import Mem0Config, save_config
|
||||
from mem0_cli.telemetry import capture_event
|
||||
from mem0_cli.telemetry_sender import _load_context
|
||||
|
||||
|
||||
class _CaptureStdin:
|
||||
def __init__(self):
|
||||
self.buffer = ""
|
||||
self.closed = False
|
||||
|
||||
def write(self, value: str) -> None:
|
||||
self.buffer += value
|
||||
|
||||
def close(self) -> None:
|
||||
self.closed = True
|
||||
|
||||
|
||||
class _DummyProcess:
|
||||
def __init__(self):
|
||||
self.stdin = _CaptureStdin()
|
||||
|
||||
|
||||
def test_capture_event_writes_context_to_stdin_not_argv(isolate_config, monkeypatch):
|
||||
config = Mem0Config()
|
||||
config.platform.api_key = "m0-test-secret"
|
||||
config.telemetry.anonymous_id = "cli-anon-test"
|
||||
save_config(config)
|
||||
|
||||
captured: dict[str, object] = {}
|
||||
proc = _DummyProcess()
|
||||
|
||||
def fake_popen(args, **kwargs):
|
||||
captured["args"] = args
|
||||
captured["kwargs"] = kwargs
|
||||
return proc
|
||||
|
||||
monkeypatch.setattr("mem0_cli.telemetry.subprocess.Popen", fake_popen)
|
||||
|
||||
capture_event("unit_test_event", {"case": "stdin-secret"})
|
||||
|
||||
argv = captured["args"]
|
||||
assert argv == [sys.executable, "-m", "mem0_cli.telemetry_sender"]
|
||||
assert all("m0-test-secret" not in arg for arg in argv)
|
||||
|
||||
kwargs = captured["kwargs"]
|
||||
assert kwargs["stdin"] == subprocess.PIPE
|
||||
assert kwargs["text"] is True
|
||||
|
||||
ctx = json.loads(proc.stdin.buffer)
|
||||
assert ctx["mem0_api_key"] == "m0-test-secret"
|
||||
assert ctx["payload"]["event"] == "unit_test_event"
|
||||
|
||||
assert proc.stdin.closed
|
||||
|
||||
|
||||
def test_load_context_reads_from_stdin(monkeypatch):
|
||||
monkeypatch.setattr("sys.argv", ["telemetry_sender"])
|
||||
monkeypatch.setattr("sys.stdin", io.StringIO('{"payload": {"event": "stdin"}}'))
|
||||
|
||||
ctx = _load_context()
|
||||
|
||||
assert ctx["payload"]["event"] == "stdin"
|
||||
|
||||
|
||||
def test_load_context_falls_back_to_argv(monkeypatch):
|
||||
monkeypatch.setattr("sys.argv", ["telemetry_sender", '{"payload": {"event": "argv"}}'])
|
||||
monkeypatch.setattr("sys.stdin", io.StringIO(""))
|
||||
|
||||
ctx = _load_context()
|
||||
|
||||
assert ctx["payload"]["event"] == "argv"
|
||||
@@ -46,9 +46,9 @@ Ground-up rewrite of the memory pipeline with 20+ point benchmark improvements:
|
||||
- **~3-4x fewer tokens** — Under 7K tokens per retrieval vs 25K+ for full-context approaches
|
||||
- **ADD-only extraction** — Memories accumulate; nothing is overwritten or deleted
|
||||
- **Hybrid retrieval** — Semantic + BM25 keyword + entity boost, scored in parallel
|
||||
- **Entity linking** — Entities extracted, embedded, and linked across memories
|
||||
- **Graph memory (built-in)**: entities extracted, embedded, and linked across memories, with no external graph store required
|
||||
|
||||
Breaking changes: Graph memory removed from OSS, `search()` defaults changed, deprecated params removed. See [migration guide](/migration/oss-v2-to-v3).
|
||||
Breaking changes: external graph stores removed from OSS (replaced by built-in graph memory), `search()` defaults changed, deprecated params removed. See [migration guide](/migration/oss-v2-to-v3).
|
||||
|
||||
</Update>
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ mode: "wide"
|
||||
<Update label="2026-04-16" description="">
|
||||
|
||||
**Improvements:**
|
||||
- **UI:** Removed Graph Memory tab, page, and all references from dashboard, sidebar, project settings, playground, and billing
|
||||
- **UI:** Removed the legacy external-graph-store visualization tab, page, and its references from dashboard, sidebar, project settings, playground, and billing
|
||||
|
||||
</Update>
|
||||
|
||||
|
||||
+50
-3
@@ -7,6 +7,41 @@ mode: "wide"
|
||||
<Tabs>
|
||||
<Tab title="Python">
|
||||
|
||||
<Update label="2026-06-17" description="v2.0.7">
|
||||
|
||||
**New Features:**
|
||||
- **LLMs:** Add Gemini via Vertex AI as LLM provider ([#4030](https://github.com/mem0ai/mem0/pull/4030))
|
||||
- **Embeddings:** Add native `embed_batch` to `OllamaEmbedding` for batched embedding requests ([#5415](https://github.com/mem0ai/mem0/pull/5415))
|
||||
|
||||
**Bug Fixes:**
|
||||
- **Core:** Fix `api_error_handler` silently dropping return values from async methods ([#5540](https://github.com/mem0ai/mem0/pull/5540))
|
||||
- **Core:** Fix `AsyncMemory.reset()` not resetting the entity store ([#5535](https://github.com/mem0ai/mem0/pull/5535))
|
||||
- **Core:** Fix `async delete_all` aborting on first error, leaving partial deletion ([#5529](https://github.com/mem0ai/mem0/pull/5529))
|
||||
- **Core:** Skip messages without a `content` key in message parsers to prevent `KeyError` crashes ([#5575](https://github.com/mem0ai/mem0/pull/5575))
|
||||
- **Core:** Preserve custom metadata fields during memory update ([#5480](https://github.com/mem0ai/mem0/pull/5480))
|
||||
- **LLMs:** Fix Anthropic `tool_choice` format and tool response parsing ([#5537](https://github.com/mem0ai/mem0/pull/5537))
|
||||
- **LLMs:** Fix Ollama `json` format mutating the caller's messages list in-place ([#5539](https://github.com/mem0ai/mem0/pull/5539))
|
||||
- **LLMs:** Omit `None` config values from Gemini `GenerateContentConfig` to prevent validation errors ([#5528](https://github.com/mem0ai/mem0/pull/5528))
|
||||
- **LLMs:** Honor reasoning-model params in `AzureOpenAIStructuredLLM` ([#5548](https://github.com/mem0ai/mem0/pull/5548))
|
||||
- **LLMs:** Honor reasoning-model params in `OpenAIStructuredLLM` ([#5458](https://github.com/mem0ai/mem0/pull/5458))
|
||||
- **LLMs:** Send `max_completion_tokens` for the GPT-5 family across all providers ([#5547](https://github.com/mem0ai/mem0/pull/5547))
|
||||
- **LLMs:** Accept and forward `**kwargs` in Together, LangChain, and Sarvam providers ([#5556](https://github.com/mem0ai/mem0/pull/5556))
|
||||
- **LLMs:** Fix Bedrock AI21 response parse default using `dict` literal instead of `set` ([#5527](https://github.com/mem0ai/mem0/pull/5527))
|
||||
- **LLMs:** Fix LiteLLM function-calling check blocking all calls on non-tool models ([#5536](https://github.com/mem0ai/mem0/pull/5536))
|
||||
- **LLMs:** Fix HuggingFace provider using `self.config` instead of raw `config` parameter ([#5538](https://github.com/mem0ai/mem0/pull/5538))
|
||||
- **Embeddings:** Honor `aws_session_token` in AWS Bedrock embeddings ([#5566](https://github.com/mem0ai/mem0/pull/5566))
|
||||
- **Rerankers:** Respect `config.top_k` in Cohere and ZeroEntropy fallback paths ([#5560](https://github.com/mem0ai/mem0/pull/5560))
|
||||
- **Vector Stores:** Fix FAISS filtered search dropping over-fetched candidates before filtering ([#5453](https://github.com/mem0ai/mem0/pull/5453))
|
||||
- **Vector Stores:** Fix Weaviate `reset()` crashing with missing `vector_size` argument ([#5531](https://github.com/mem0ai/mem0/pull/5531))
|
||||
- **Vector Stores:** Pass embedding dims in Weaviate `reset()` to avoid re-init crash ([#5570](https://github.com/mem0ai/mem0/pull/5570))
|
||||
- **Vector Stores:** Fix MongoDB `reset()` passing wrong argument to `create_col()` ([#5532](https://github.com/mem0ai/mem0/pull/5532))
|
||||
- **Vector Stores:** Fix Pinecone hybrid search crashing when `filters` is `None` ([#5533](https://github.com/mem0ai/mem0/pull/5533))
|
||||
- **Vector Stores:** Fix Redis crashing on empty or `None` filters in `search()` and `list()` ([#5446](https://github.com/mem0ai/mem0/pull/5446))
|
||||
- **Vector Stores:** Return `None` from `get()` for missing IDs in Milvus, Weaviate, and Supabase ([#5562](https://github.com/mem0ai/mem0/pull/5562))
|
||||
- **Vector Stores:** Return `None` from ChromaDB `get()` for missing IDs ([#5561](https://github.com/mem0ai/mem0/pull/5561))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-06-13" description="v2.0.6">
|
||||
|
||||
**New Features:**
|
||||
@@ -114,8 +149,8 @@ mode: "wide"
|
||||
- **`messages` in `Memory.add()` rejects invalid types:** Passing `None` or non-`(str | dict | list)` values raises `Mem0ValidationError` (`error_code="VALIDATION_003"`) ([#4843](https://github.com/mem0ai/mem0/pull/4843))
|
||||
- **`qdrant-client>=1.12.0` required** — Upgrade from `>=1.9.1` ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **`org_id` and `project_id` removed** — Removed from `MemoryClient` constructor and all method signatures ([#4740](https://github.com/mem0ai/mem0/pull/4740))
|
||||
- **Graph Memory Removed (OSS):** `mem0/memory/graph_memory.py`, `memgraph_memory.py`, `kuzu_memory.py`, `apache_age_memory.py`, and `mem0/graphs/` (Neo4j / Memgraph / Kuzu / Apache AGE / Neptune drivers) deleted — ~4,000 lines. Graph memory is no longer supported in the OSS SDK; graph drivers (neo4j, memgraph, kuzu, etc.) can be uninstalled. Use the Platform API for graph features. Remove `enable_graph` and `graph_store` from your config ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **`enable_graph` removed from Client SDK** — Graph memory is now a project-level setting on the Platform. Remove `enable_graph` from `MemoryClient.add()` / `search()` / `get_all()` / `update_project()` calls ([#4776](https://github.com/mem0ai/mem0/pull/4776))
|
||||
- **External Graph Store Removed (OSS):** `mem0/memory/graph_memory.py`, `memgraph_memory.py`, `kuzu_memory.py`, `apache_age_memory.py`, and `mem0/graphs/` (Neo4j / Memgraph / Kuzu / Apache AGE / Neptune drivers) deleted, about 4,000 lines. The external graph store integration is no longer part of the OSS SDK; graph drivers (neo4j, memgraph, kuzu, etc.) can be uninstalled. Graph memory now runs natively as built-in entity linking. Remove `enable_graph` and `graph_store` from your config ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **`enable_graph` removed from Client SDK:** Graph memory now runs automatically and no longer needs a flag. Remove `enable_graph` from `MemoryClient.add()` / `search()` / `get_all()` / `update_project()` calls ([#4776](https://github.com/mem0ai/mem0/pull/4776))
|
||||
- **`custom_fact_extraction_prompt` renamed to `custom_instructions`** — Update config and memory module references ([#4740](https://github.com/mem0ai/mem0/pull/4740))
|
||||
- **Typed option classes** — Added Pydantic v2 typed classes: `AddMemoryOptions`, `SearchMemoryOptions`, `GetAllMemoryOptions`, `DeleteAllMemoryOptions`, `UpdateMemoryOptions`, `ProjectUpdateOptions` ([#4740](https://github.com/mem0ai/mem0/pull/4740))
|
||||
|
||||
@@ -976,6 +1011,18 @@ See the [OSS v1 to v2 migration guide](https://docs.mem0.ai/migration/oss-v1-to-
|
||||
|
||||
<Tab title="TypeScript">
|
||||
|
||||
<Update label="2026-06-17" description="v3.0.9">
|
||||
|
||||
**Bug Fixes:**
|
||||
- **LLMs:** Fix Anthropic `tool_choice` format — was incorrectly sent as a bare string `"auto"` (rejected by the API); now correctly sent as `{ type: "auto" }`. Also fixes tool response parsing: `tool_use` blocks are now parsed into `toolCalls` objects instead of throwing. Updated default model to `claude-sonnet-4-6` and default `max_tokens` to `2000` to match the Python provider. Added `temperature`, `topP`, and `maxTokens` to `LLMConfig` so Anthropic params can be configured ([#5537](https://github.com/mem0ai/mem0/pull/5537))
|
||||
- **Memory (OSS):** Preserve custom metadata fields during `update()` — fields such as `category`, `priority`, and other user-defined keys were previously dropped on update; the existing payload is now spread before applying the new data ([#5480](https://github.com/mem0ai/mem0/pull/5480))
|
||||
- **Client:** Preserve user-defined schema keys in `createMemoryExport` ([#5594](https://github.com/mem0ai/mem0/pull/5594))
|
||||
|
||||
**Security:**
|
||||
- **Dependencies:** Bump `esbuild` to `>=0.28.1` across all npm packages via pnpm overrides to remediate upstream vulnerability ([#5563](https://github.com/mem0ai/mem0/pull/5563))
|
||||
|
||||
</Update>
|
||||
|
||||
<Update label="2026-06-13" description="v3.0.8">
|
||||
|
||||
**New Features:**
|
||||
@@ -1066,7 +1113,7 @@ See the [OSS v1 to v2 migration guide](https://docs.mem0.ai/migration/oss-v1-to-
|
||||
- **Default model:** `gpt-5-mini` is now the default in `OpenAI`, `OpenAIStructured`, and `Azure` LLM providers ([#4829](https://github.com/mem0ai/mem0/pull/4829))
|
||||
|
||||
**Breaking Changes:**
|
||||
- **Graph Memory Removed (OSS):** `graph_memory.ts` (675 lines), `graphs/tools.ts` (267 lines), `graphs/utils.ts` (116 lines), `graphs/configs.ts` (30 lines) deleted. Graph memory is no longer supported in the OSS SDK — use Platform API for graph features ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **External Graph Store Removed (OSS):** `graph_memory.ts` (675 lines), `graphs/tools.ts` (267 lines), `graphs/utils.ts` (116 lines), `graphs/configs.ts` (30 lines) deleted. The external graph store integration is no longer part of the OSS SDK; graph memory now runs natively as built-in entity linking ([#4805](https://github.com/mem0ai/mem0/pull/4805))
|
||||
- **camelCase Parameters (Client SDK):** All user-facing parameters converted from snake_case to camelCase. Mapping is transparent at API boundary via `camelToSnakeKeys()` / `snakeToCamelKeys()` ([#4776](https://github.com/mem0ai/mem0/pull/4776))
|
||||
```typescript
|
||||
// Before
|
||||
|
||||
@@ -56,6 +56,30 @@ config = {
|
||||
}
|
||||
```
|
||||
|
||||
### Configuration Options
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `collection_name` | string | required | Name of the OpenSearch index |
|
||||
| `host` | string | required | OpenSearch endpoint URL |
|
||||
| `port` | int | 9200 | Port number |
|
||||
| `http_auth` | object | None | Authentication credentials (e.g., AWSV4SignerAuth) |
|
||||
| `embedding_model_dims` | int | 1536 | Dimension of embedding vectors |
|
||||
| `use_ssl` | bool | False | Enable SSL/TLS connection |
|
||||
| `verify_certs` | bool | False | Verify SSL certificates |
|
||||
| `auto_refresh` | bool | False | Automatically refresh index after insert. OpenSearch refreshes every ~1 second by default, so this is rarely needed. |
|
||||
|
||||
<Note>
|
||||
The defaults above match a local OpenSearch instance. The AWS OpenSearch Serverless
|
||||
example earlier on this page intentionally overrides them with `port=443`, `use_ssl=True`,
|
||||
and `verify_certs=True`, which are required when connecting to a Serverless collection.
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
For **AWS OpenSearch Serverless**, keep `auto_refresh=False` (the default).
|
||||
The `indices.refresh()` API is not supported on Serverless collections.
|
||||
</Note>
|
||||
|
||||
### Add Memories
|
||||
|
||||
```python
|
||||
|
||||
@@ -17,7 +17,7 @@ Some benchmarks today — particularly smaller ones like LoCoMo and LongMemEval
|
||||
|
||||
## Architecture Overview
|
||||
|
||||
Mem0's memory system operates across two phases — **extraction** (writing) and **retrieval** (reading) — with an entity linking layer connecting them.
|
||||
Mem0's memory system operates across two phases, **extraction** (writing) and **retrieval** (reading), with a graph memory layer (entity linking) connecting them.
|
||||
|
||||
### Memory Extraction (Distillation)
|
||||
|
||||
@@ -27,14 +27,14 @@ When new conversations arrive, the extraction pipeline processes them through fi
|
||||
2. **Context Lookup** — Find related existing memories to avoid duplicates
|
||||
3. **Distill Memories** — Single-pass LLM extraction produces ADD-only facts from input + context
|
||||
4. **Deduplicate + Embed** — Hash-based deduplication, then vectorize new memories
|
||||
5. **Entity Linking** — Identify entities (proper nouns, quoted text, compound noun phrases) and link them across memories
|
||||
5. **Graph Memory (Entity Linking)**: Identify entities (proper nouns, quoted text, compound noun phrases) and link them across memories into a graph
|
||||
|
||||
Memories are distributed across three storage layers, each tuned for a specific retrieval pattern:
|
||||
|
||||
| Store | Contents | Purpose |
|
||||
|---|---|---|
|
||||
| **Vector Database** | Memory text, embeddings, metadata (timestamps, hash, categories, attributed_to) | Primary fact storage + semantic retrieval |
|
||||
| **Entity Store** | Entities + embeddings + linked memory IDs | Entity-based retrieval boost |
|
||||
| **Graph / Entity Store** | Entities + embeddings + linked memory IDs | Graph connections across memories + entity-based retrieval boost |
|
||||
| **SQL Database** | History log (ADD events) + rolling message window | Audit trail + extraction dedup context |
|
||||
|
||||
<Info>
|
||||
@@ -76,7 +76,7 @@ The combined score outperformed every individual signal across every category te
|
||||
|
||||
*Mean tokens: 6,956*
|
||||
|
||||
The two largest gains are **temporal queries (+29.6)** and **multi-hop reasoning (+23.1)**. Both categories directly test the ADD-only architecture (preserving temporal context) and entity linking (connecting facts across memories).
|
||||
The two largest gains are **temporal queries (+29.6)** and **multi-hop reasoning (+23.1)**. Both categories directly test the ADD-only architecture (preserving temporal context) and graph memory / entity linking (connecting facts across memories).
|
||||
|
||||
### LongMemEval
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ Adding memory is how Mem0 captures useful details from a conversation so your ag
|
||||
- **Messages** – The ordered list of user/assistant turns you send to `add`.
|
||||
- **Infer** – Controls whether Mem0 extracts structured memories (`infer=True`, default) or stores raw messages.
|
||||
- **Metadata** – Optional filters (e.g., `{"category": "movie_recommendations"}`) that improve retrieval later.
|
||||
- **User / Session identifiers** – `user_id`, `agent_id`, or `run_id` that scope the memory for future searches.
|
||||
- **User / Session identifiers** – `user_id`, `agent_id`, `app_id`, or `run_id` that scope the memory for future searches.
|
||||
|
||||
## How does it work?
|
||||
|
||||
@@ -30,22 +30,22 @@ Mem0 offers two flows:
|
||||
- **Mem0 Platform** – Fully managed API with dashboard and scaling.
|
||||
- **Mem0 Open Source** – Local SDK that you run in your own environment.
|
||||
|
||||
Both flows take the same payload and pass it through the same pipeline.
|
||||
Both flows take the same payload and add memories through an additive pipeline.
|
||||
|
||||
<Steps>
|
||||
<Step title="Information extraction">
|
||||
Mem0 sends the messages through an LLM that pulls out key facts, decisions, or preferences to remember.
|
||||
</Step>
|
||||
<Step title="Conflict resolution">
|
||||
Existing memories are checked for duplicates or contradictions so the latest truth wins.
|
||||
<Step title="Additive storage">
|
||||
New memories are added without overwriting or deleting existing memories.
|
||||
</Step>
|
||||
<Step title="Storage">
|
||||
The resulting memories land in managed vector storage so future searches return them quickly.
|
||||
<Step title="Retrieval">
|
||||
Future searches rank the most relevant memories for the query.
|
||||
</Step>
|
||||
</Steps>
|
||||
|
||||
<Warning>
|
||||
Duplicate protection only runs during that conflict-resolution step when you let Mem0 infer memories (`infer=True`, the default). If you switch to `infer=False`, Mem0 stores your payload exactly as provided, so duplicates will land. Mixing both modes for the same fact will save it twice.
|
||||
When you switch to `infer=False`, Mem0 stores your payload exactly as provided, so duplicates can land. Mixing both modes for the same fact can save it twice.
|
||||
</Warning>
|
||||
|
||||
You trigger this pipeline with a single `add` call—no manual orchestration needed.
|
||||
@@ -80,13 +80,13 @@ const messages = [
|
||||
];
|
||||
|
||||
await client.add(messages, {
|
||||
user_id: "alice",
|
||||
userId: "alice",
|
||||
});
|
||||
```
|
||||
</CodeGroup>
|
||||
|
||||
<Info icon="check">
|
||||
Expect a `memory_id` (or list of IDs) in the response. Check the Mem0 dashboard to confirm the new entry under the correct user.
|
||||
Expect a `status: "PENDING"` response with an `event_id`. Poll `GET /v1/event/{event_id}/` to confirm completion.
|
||||
</Info>
|
||||
|
||||
## Add with Mem0 Open Source
|
||||
@@ -138,7 +138,7 @@ const result = memory.add(messages, {
|
||||
</Tip>
|
||||
|
||||
<Warning>
|
||||
If you do choose `infer=False`, keep it consistent. Raw inserts skip conflict resolution, so a later `infer=True` call with the same content will create a second memory instead of updating the first.
|
||||
If you do choose `infer=False`, keep it consistent. Raw inserts skip inference, so a later `infer=True` call with the same content can create a second memory.
|
||||
</Warning>
|
||||
|
||||
## When Should You Add Memory?
|
||||
@@ -167,7 +167,7 @@ For full list of supported fields, required formats, and advanced options, see t
|
||||
|
||||
| Capability | Mem0 Platform | Mem0 OSS |
|
||||
| --- | --- | --- |
|
||||
| Conflict resolution | Automatic with dashboard visibility | SDK handles merges locally; you control storage |
|
||||
| Add behavior | ADD-only; memories accumulate | ADD-only; you control storage |
|
||||
| Rate limits | Managed quotas per workspace | Limited by your hardware and provider APIs |
|
||||
| Dashboard visibility | Yes — inspect memories visually | Inspect via CLI, logs, or custom UI |
|
||||
|
||||
|
||||
+3
-6
@@ -71,6 +71,7 @@
|
||||
"pages": [
|
||||
"platform/features/v2-memory-filters",
|
||||
"platform/features/entity-scoped-memory",
|
||||
"platform/features/graph-memory",
|
||||
"platform/features/async-client",
|
||||
"platform/features/multimodal-support",
|
||||
"platform/features/custom-categories",
|
||||
@@ -440,7 +441,7 @@
|
||||
"integrations/flowise",
|
||||
"integrations/langchain-tools",
|
||||
"integrations/agentops",
|
||||
"integrations/keywords",
|
||||
"integrations/respan",
|
||||
"integrations/raycast"
|
||||
]
|
||||
}
|
||||
@@ -647,10 +648,6 @@
|
||||
"source": "/open-source/features/custom-fact-extraction-prompt",
|
||||
"destination": "/open-source/features/custom-instructions"
|
||||
},
|
||||
{
|
||||
"source": "/platform/features/graph-memory",
|
||||
"destination": "/migration/oss-v2-to-v3"
|
||||
},
|
||||
{
|
||||
"source": "/cookbooks/essentials/choosing-memory-architecture-vector-vs-graph",
|
||||
"destination": "/migration/oss-v2-to-v3"
|
||||
@@ -1025,7 +1022,7 @@
|
||||
},
|
||||
{
|
||||
"source": "/features/graph-memory",
|
||||
"destination": "/migration/oss-v2-to-v3"
|
||||
"destination": "/platform/features/graph-memory"
|
||||
},
|
||||
{
|
||||
"source": "/features/:slug",
|
||||
|
||||
@@ -309,19 +309,21 @@ Here are the available integrations for Mem0:
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
title="Keywords AI"
|
||||
title="Respan"
|
||||
icon={
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="24"
|
||||
height="24"
|
||||
viewBox="0 0 24 24"
|
||||
viewBox="0 0 200 200"
|
||||
fill="none"
|
||||
>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.07513 1.1863C9.21663 1.07722 9.39144 1.01009 9.56624 1.01009C9.83261 1.01009 10.0823 1.12756 10.2405 1.33734L15.0101 7.4964V12.4136L16.4335 13.8401C16.7582 14.1673 16.7582 14.7043 16.4335 15.0316C16.1089 15.3588 15.5762 15.3588 15.2515 15.0316L13.3453 13.1016V8.07538L8.92529 2.36944V2.36105C8.64228 2.00024 8.70887 1.4716 9.07513 1.1863ZM18.976 14.4133C18.8344 14.3778 18.7003 14.3042 18.5894 14.1925L16.9163 12.5059C16.7249 12.3129 16.6416 12.0528 16.6749 11.8094V6.88385H16.6499L11.8553 0.691225C11.7282 0.529117 11.6716 0.333133 11.6803 0.140562C11.134 0.0481292 10.5726 0 10 0C4.47715 0 0 4.47715 0 10C0 15.5228 4.47715 20 10 20C13.9387 20 17.3456 17.7229 18.976 14.4133Z" fill="currentColor"></path>
|
||||
<path d="M2.00635 190.234V9.76584H53.3558V29.5101H26.7223V170.562H53.3558V190.234H2.00635Z" fill="currentColor"></path>
|
||||
<path d="M120.692 160.902C116.383 160.902 112.691 159.387 109.612 156.357C106.535 153.327 105.02 149.633 105.067 145.277C105.02 141.016 106.535 137.37 109.612 134.34C112.691 131.309 116.383 129.794 120.692 129.794C124.859 129.794 128.481 131.309 131.559 134.34C134.684 137.37 136.27 141.016 136.317 145.277C136.27 148.166 135.512 150.793 134.045 153.161C132.624 155.528 130.73 157.422 128.362 158.842C126.042 160.216 123.486 160.902 120.692 160.902Z" fill="currentColor"></path>
|
||||
<path d="M197.993 9.76584V190.234H146.643V170.562H173.278V29.5101H146.643V9.76584H197.993Z" fill="currentColor"></path>
|
||||
</svg>
|
||||
}
|
||||
href="/integrations/keywords"
|
||||
href="/integrations/respan"
|
||||
>
|
||||
Build AI applications with persistent memory and comprehensive LLM observability.
|
||||
</Card>
|
||||
|
||||
+164
-37
@@ -1,35 +1,42 @@
|
||||
---
|
||||
title: Hermes Agent
|
||||
description: "Add long-term memory to Hermes agents using Mem0 as a pluggable memory provider with automatic background sync and zero-latency prefetch."
|
||||
description: "Add long-term memory to Hermes agents with Mem0, on managed Mem0 Cloud or fully self-hosted (OSS), with automatic background sync and zero-latency prefetch."
|
||||
---
|
||||
|
||||
Add long-term memory to [Hermes Agent](https://github.com/NousResearch/hermes-agent) — a self-improving AI agent CLI by Nous Research. Hermes has a pluggable memory system, and Mem0 is one of the supported providers. Once enabled, Mem0 automatically learns facts from your conversations and surfaces relevant ones before each turn — all without slowing down the chat.
|
||||
Add long-term memory to [Hermes Agent](https://github.com/NousResearch/hermes-agent), a self-improving AI agent CLI by Nous Research. Hermes has a pluggable memory system, and Mem0 is one of the supported providers. Once enabled, Mem0 learns facts from your conversations and surfaces relevant ones before each turn, without slowing down the chat.
|
||||
|
||||
## Overview
|
||||
You can run Mem0 in two ways:
|
||||
|
||||
Hermes runs a built-in memory system (file-based `MEMORY.md` and `USER.md`) alongside one external provider. When Mem0 is active, it works additively with the built-in system at three key moments in every conversation turn:
|
||||
- **Platform mode** (default): managed Mem0 Cloud. Add your API key and you are ready.
|
||||
- **OSS mode**: fully self-hosted with your own LLM, embedder, and vector store. No data leaves your machine.
|
||||
|
||||
### 1. Before the Agent Responds (Prefetch)
|
||||
## How It Works
|
||||
|
||||
When you send a message, Hermes checks if it already has cached Mem0 search results from the previous turn. If so, those memories are injected into the system prompt so the LLM can see them. This is **zero-latency** — no waiting for an API call.
|
||||
Hermes runs a built-in memory system (file-based `MEMORY.md` and `USER.md`) alongside one external provider. When Mem0 is active, it works additively with the built-in system at three points in every conversation turn.
|
||||
|
||||
### 2. After the Agent Responds (Sync)
|
||||
### 1. Before the agent responds (prefetch)
|
||||
|
||||
Once the LLM finishes responding, Hermes sends the `(user message, assistant response)` pair to Mem0's API in a **background thread**. Mem0's server-side LLM automatically extracts facts (e.g., "user prefers Python", "user works at Acme Corp") — you don't have to tell it what to remember.
|
||||
When you send a message, Hermes checks for cached Mem0 search results from the previous turn. If they exist, those memories are injected into the system prompt so the model can see them. This is zero-latency, with no waiting on an API call.
|
||||
|
||||
### 3. Background Prefetch for Next Turn
|
||||
### 2. After the agent responds (sync)
|
||||
|
||||
At the same time as sync, Hermes kicks off a background search on Mem0 to pre-load relevant memories for the next turn. By the time you type your next message, the memories are already cached.
|
||||
Once the model finishes, Hermes sends the `(user message, assistant response)` pair to Mem0 in a background thread. Mem0 extracts facts automatically (for example, "user prefers Python" or "user works at Acme Corp"), so you never have to tell it what to remember. Each write is tagged with the gateway channel it came from.
|
||||
|
||||
### 3. Background prefetch for the next turn
|
||||
|
||||
At the same time, Hermes runs a background search to pre-load relevant memories for your next message. By the time you type, the results are already cached.
|
||||
|
||||
## Agent Tools
|
||||
|
||||
When Mem0 is active, the LLM gets three extra tools it can call during conversations:
|
||||
When Mem0 is active, the model gets five tools it can call during a conversation:
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `mem0_profile` | Fetch all stored memories about the user |
|
||||
| `mem0_search` | Semantic search through memories (supports optional reranking via `rerank` and `top_k` parameters) |
|
||||
| `mem0_conclude` | Store a specific fact verbatim — uses `infer=False` so no server-side LLM extraction happens |
|
||||
| Tool | Description | Parameters |
|
||||
|------|-------------|------------|
|
||||
| `mem0_list` | List all stored memories, for a full overview | `page`, `page_size` (default 100, max 200) |
|
||||
| `mem0_search` | Semantic search by meaning, ranked by relevance | `query` (required), `top_k` (default 10, max 50), `rerank` (default `true`, Platform mode only) |
|
||||
| `mem0_add` | Store a fact verbatim, with no LLM extraction | `content` (required) |
|
||||
| `mem0_update` | Update a memory's text by ID | `memory_id`, `text` (both required) |
|
||||
| `mem0_delete` | Delete a memory by ID | `memory_id` (required) |
|
||||
|
||||
## Installation
|
||||
|
||||
@@ -40,17 +47,19 @@ curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scri
|
||||
source ~/.bashrc
|
||||
```
|
||||
|
||||
The `mem0ai` Python package is automatically installed when you enable the Mem0 provider — no manual pip install needed.
|
||||
The `mem0ai` package is installed automatically when you enable the Mem0 provider, so there is no manual pip step. OSS providers may need extra packages (for example `qdrant-client`, `psycopg2-binary`, or `ollama`), which the setup flow installs for you when you pick them.
|
||||
|
||||
## Setup
|
||||
## Platform Setup
|
||||
|
||||
### Option 1: Interactive Setup Wizard (Recommended)
|
||||
Platform mode uses managed Mem0 Cloud and is the fastest way to start.
|
||||
|
||||
### Option 1: Interactive wizard (recommended)
|
||||
|
||||
```bash
|
||||
hermes memory setup
|
||||
```
|
||||
|
||||
Select **mem0** as the provider and enter your Mem0 API key when prompted. The wizard writes your config to `~/.hermes/mem0.json`.
|
||||
Select **mem0**, choose **Platform**, and paste your API key when prompted. The wizard writes the non-secret settings to `~/.hermes/mem0.json` and keeps the key in `~/.hermes/.env`.
|
||||
|
||||
<Note>Get your API key from <a href="https://app.mem0.ai?utm_source=oss&utm_medium=integration-hermes" rel="nofollow">app.mem0.ai</a>.</Note>
|
||||
|
||||
@@ -68,33 +77,151 @@ memory:
|
||||
provider: mem0
|
||||
```
|
||||
|
||||
That's it — Mem0 runs automatically from this point.
|
||||
That's it. Mem0 runs automatically from here.
|
||||
|
||||
## Configuration Options
|
||||
## OSS (Self-Hosted) Setup
|
||||
|
||||
Configuration is stored in `~/.hermes/mem0.json`. Values can also be set via environment variables.
|
||||
OSS mode runs Mem0 entirely on your own infrastructure: your LLM, your embedder, and your vector store. No data is sent to Mem0 Cloud, and no Mem0 API key is required.
|
||||
|
||||
| Key | Env Variable | Default | Description |
|
||||
|-----|-------------|---------|-------------|
|
||||
| `api_key` | `MEM0_API_KEY` | — | **Required.** Mem0 Platform API key |
|
||||
| `user_id` | `MEM0_USER_ID` | `hermes-user` | User identifier for scoping memories |
|
||||
| `agent_id` | `MEM0_AGENT_ID` | `hermes` | Agent identifier |
|
||||
| `rerank` | — | `true` | Enable reranking for memory recall |
|
||||
### Interactive
|
||||
|
||||
```bash
|
||||
hermes memory setup
|
||||
# Select "mem0", then "Open Source (self-hosted)"
|
||||
# Follow the prompts for LLM, embedder, and vector store
|
||||
```
|
||||
|
||||
### With flags
|
||||
|
||||
```bash
|
||||
hermes memory setup mem0 --mode oss \
|
||||
--oss-llm openai --oss-llm-key sk-... \
|
||||
--oss-vector qdrant
|
||||
```
|
||||
|
||||
### Supported providers
|
||||
|
||||
| Component | Providers |
|
||||
|-----------|-----------|
|
||||
| LLM | `openai` (default model `gpt-5-mini`), `ollama` (local, default `llama3.1:8b`) |
|
||||
| Embedder | `openai` (default `text-embedding-3-small`), `ollama` (local, default `nomic-embed-text`) |
|
||||
| Vector store | `qdrant` (local path or server), `pgvector` |
|
||||
|
||||
### Flag reference
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--mode` | `platform` or `oss` |
|
||||
| `--oss-llm` | LLM provider (`openai` or `ollama`, default `openai`) |
|
||||
| `--oss-llm-key` | LLM API key (for `openai`) |
|
||||
| `--oss-llm-model` | Override the LLM model |
|
||||
| `--oss-llm-url` | LLM base URL (for `ollama` or a custom endpoint) |
|
||||
| `--oss-embedder` | Embedder provider (default `openai`) |
|
||||
| `--oss-embedder-key` | Embedder API key |
|
||||
| `--oss-vector` | Vector store (`qdrant` or `pgvector`, default `qdrant`) |
|
||||
| `--oss-vector-path` | Local Qdrant storage path |
|
||||
| `--oss-vector-host`, `--oss-vector-port` | PGVector or remote Qdrant host and port |
|
||||
| `--oss-vector-user`, `--oss-vector-password`, `--oss-vector-dbname` | PGVector connection details |
|
||||
| `--user-id` | Canonical user identifier |
|
||||
| `--dry-run` | Preview the resolved config without writing it |
|
||||
|
||||
## Switching Modes
|
||||
|
||||
You can move between Platform and OSS at any time. Run the setup command again, or edit `~/.hermes/mem0.json` directly.
|
||||
|
||||
```bash
|
||||
# Platform to OSS
|
||||
hermes memory setup mem0 --mode oss --oss-llm-key sk-...
|
||||
|
||||
# OSS to Platform
|
||||
hermes memory setup mem0 --mode platform --api-key sk-...
|
||||
|
||||
# Preview without writing anything
|
||||
hermes memory setup mem0 --mode oss --oss-llm-key sk-... --dry-run
|
||||
```
|
||||
|
||||
A self-hosted `~/.hermes/mem0.json` looks like this:
|
||||
|
||||
```json
|
||||
{
|
||||
"mode": "oss",
|
||||
"oss": {
|
||||
"llm": {"provider": "openai", "config": {"model": "gpt-5-mini"}},
|
||||
"embedder": {"provider": "openai", "config": {"model": "text-embedding-3-small"}},
|
||||
"vector_store": {"provider": "qdrant", "config": {"path": "~/.hermes/mem0_qdrant"}}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Configuration
|
||||
|
||||
Behavioral settings live in `~/.hermes/mem0.json` and are written for you by `hermes memory setup`. Only the secret `MEM0_API_KEY` belongs in `~/.hermes/.env`.
|
||||
|
||||
| Key | Default | Description |
|
||||
|-----|---------|-------------|
|
||||
| `mode` | `platform` | `platform` (Mem0 Cloud) or `oss` (self-hosted) |
|
||||
| `api_key` | none | Mem0 Platform API key, required in Platform mode. Stored in `.env` as `MEM0_API_KEY` |
|
||||
| `user_id` | `hermes-user` | Identifier that scopes memories. See cross-channel behavior below |
|
||||
| `agent_id` | `hermes` | Agent identifier attached to writes |
|
||||
| `rerank` | `true` | Rerank search results for relevance (Platform mode only) |
|
||||
|
||||
### Cross-channel memories
|
||||
|
||||
Hermes can run from the CLI and from gateways like Telegram, Slack, and Discord. The `user_id` setting controls how memories are scoped across them:
|
||||
|
||||
- **Set a `user_id`** and it applies to every gateway, so one person gets a single merged memory store no matter where they talk to the agent.
|
||||
- **Leave it unset** (or at the default `hermes-user`) and each gateway uses its own native id, keeping per-platform memories separate.
|
||||
|
||||
Either way, every write is tagged with `metadata.channel` (for example `telegram` or `cli`), so per-channel views are still possible at query time.
|
||||
|
||||
|
||||
## Reliability
|
||||
|
||||
- **Circuit Breaker** — If Mem0's API fails 5 times in a row, Hermes stops calling it for 2 minutes, then retries. The agent keeps working fine without memory during that time.
|
||||
- **Non-blocking** — All Mem0 API calls happen in background daemon threads. A slow or failed API call never blocks your conversation.
|
||||
- **Thread-safe** — The Mem0 client uses lazy initialization with locking, safe for concurrent access.
|
||||
- **Circuit breaker**: if Mem0 fails five times in a row, Hermes pauses calls for two minutes, then retries. The agent keeps working without memory during that window. Expected client errors, like a 404 on a missing memory id, do not count toward tripping the breaker.
|
||||
- **Non-blocking**: every Mem0 call runs in a background daemon thread, so a slow or failed call never blocks your conversation.
|
||||
- **Thread-safe**: the client uses lazy initialization with locking, and the background sync and prefetch threads are guarded so concurrent gateway messages cannot produce duplicate memories.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### "Mem0 temporarily unavailable"
|
||||
|
||||
The circuit breaker tripped after five consecutive failures and resets after two minutes.
|
||||
|
||||
- **Platform mode**: check your API key and internet connection.
|
||||
- **OSS mode**: make sure your vector store (Qdrant or PGVector) is running and reachable.
|
||||
|
||||
### OSS: vector store connection refused
|
||||
|
||||
```bash
|
||||
# Local Qdrant: confirm the storage path is writable
|
||||
ls -la ~/.hermes/mem0_qdrant
|
||||
|
||||
# Qdrant server: confirm it is reachable
|
||||
curl http://localhost:6333/healthz
|
||||
|
||||
# PGVector: confirm PostgreSQL is accepting connections
|
||||
pg_isready -h localhost -p 5432
|
||||
```
|
||||
|
||||
### OSS: Ollama not reachable
|
||||
|
||||
```bash
|
||||
curl http://localhost:11434/api/tags
|
||||
```
|
||||
|
||||
### Memories not appearing
|
||||
|
||||
- `mem0_add` stores text verbatim with no extraction. Ordinary conversation turns are extracted automatically by the background sync.
|
||||
- Search is semantic, so try a broader query.
|
||||
- Confirm `user_id` is the same across sessions (check `~/.hermes/mem0.json`).
|
||||
|
||||
## Key Features
|
||||
|
||||
1. **Zero-Latency Recall** — Memories are prefetched in the background and cached, ready before you type
|
||||
2. **Server-side Extraction** — Mem0's API automatically extracts and deduplicates facts from each exchange
|
||||
3. **Non-blocking** — All API calls run in background daemon threads
|
||||
4. **Fault Tolerant** — Circuit breaker ensures the agent works even if Mem0 is temporarily unreachable
|
||||
5. **Additive Memory** — Works alongside Hermes' built-in file-based memory system (MEMORY.md, USER.md)
|
||||
1. **Two ways to run**: managed Platform or fully self-hosted OSS, switchable at any time.
|
||||
2. **Zero-latency recall**: memories are prefetched in the background and cached before you type.
|
||||
3. **Automatic extraction**: Mem0 extracts and deduplicates facts from each exchange for you.
|
||||
4. **Non-blocking and fault tolerant**: background threads plus a circuit breaker keep the agent responsive even when Mem0 is unreachable.
|
||||
5. **Additive memory**: works alongside Hermes' built-in file memory (`MEMORY.md`, `USER.md`).
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="OpenClaw Integration" icon={<svg width="24" height="24" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg"><path fill-rule="evenodd" d="m153.5 173.5q24.62 1.46 46 13.5 12.11 8.1 17.5 21.5 0.74 2.45 0.5 5 0.09 0.81 1 1 1.48-4.9 1-10 5.04 10.48 1.5 22-9.81 27.86-35.5 42.5-26.17 14.97-56 19.5-2.77-0.4-2 1 2.86 1.27 6 1 25.64 1.53 48.5-10 0.34 10.08 2 20 1.08 5.76 5 10 1 1.5 0 3-31.11 20.84-68.5 17.5-23.7-5.7-32.5-28.5-4.39-9.18-3.5-19 15.41 6.23 32 4.5-20.68-6.39-39-18-34.81-27.22-12.5-65.5 11.84-14.83 29-23 4.21 7.66 11.5 12.5 3 1 6 0-26.04-34.62-29-78-0.13-8.46 2-16.5 1 6.5 2 13 3.43 39.53 24.5 73 2.03 2.28 4.5 4 0.5-1.25 1-2.5-1.27-6.54-5-12 0.5-0.75 1-1.5 9.72-3.43 20-4 0.55 10.34 8 17.5 1.94 0.74 4 0.5-17.8-64.6 16.5-122 0.98-1.79 1.5 0-28.21 56.64-13.5 118 1.08 1.43 2.5 0.5 2.21-4.98 2-10.5z" fill="currentColor"/><path fill-rule="evenodd" d="m454.5 97.5q-1.33 11.18-8.5 20-21.81 26.28-55.5 32-1.11-0.2-2 0.5 2.31 2.82 5.5 4.5 1 2 0 4-9.56 11.3-19.5 20 19.71-8.72 31-27 2.68-0.43 5 1-14.24 30.97-48 36.5-9.93 1.71-20 1.5-6.8-0.48-13 1 5.81 6.92 14 11-10.78 16.03-27 26.5 27.16-7.4 38-33.5 4.34 1.35 9 1-9.08 23.84-33 33.5-18.45 6.41-38 7 22.59 8.92 45-1 12.05-5.52 24-11 9.01-1.79 17 2.5 5.28-4.38 11-8 12.8-6.07 27-5 0 0.5 0 1-19.34 2.69-34 15.5 0.5 0.25 1 0.5 17.79-8.09 36-15 2.71-0.79 5-2 2.5-1 5-2 5.53-4.04 11-8 11.7-4.18 24-6.5 7.78-1.36 15 1.5-2.97 18.45-13.5 34-34.92 49.37-94.5 62.5-59.27 12.45-108-23-15.53-12.52-21.5-31.5-2.47-14.26 4-27-3.15 24.41 14 42-4.92-10.28-7-22-1.97-17.63 7-33 47.28-69.5 125.5-100 15.86-3.42 32-5.5 18.63-1.47 37 1.5z" fill="currentColor"/><path fill-rule="evenodd" d="m231.5 238.5q1.31-0.2 2 1-3.13 28.62 15 51-16.25 6.75-27-7.5-1-1-2 0 14.73 29.34 46 18.5 1.79 0.52 0 1.5-37.63 16.82-50.5-22.5-5.1-26.48 16.5-42z" fill="currentColor"/><path fill-rule="evenodd" d="m203.5 266.5q1.31-0.2 2 1-2.48 22.08 12 39-6.99 1.35-14 0.5 4.59 4.08 10 7-8.71 0.28-14.5-6.5-16.98-22.76 4.5-41z" fill="currentColor"/><path fill-rule="evenodd" d="m58.5 284.5q9.6-2.17 14.5 6 5.15 14.18-1 28-11.05-13.14-27.5-17.5 5.15-9.9 14-16.5z" fill="currentColor"/><path fill-rule="evenodd" d="m56.5 313.5q3.43 5.43 8 10-4.88 0.44-8 4-1.11-0.2-2 0.5 28.91 1.65 38 28.5 0.45 3.16-1 6-11.02-7.01-23-12.5-4.75-3.75-9.5-7.5 1.47 7.42 7 13 8.34 27.18 32 43 0.99 2.41-1.5 3.5-40.25 5.58-66.5-25.5-15.67-22.01-8-48 10.46-23.87 34.5-15z" fill="currentColor"/><path fill-rule="evenodd" d="m198.5 319.5q1.44 0.68 2.5 2 2.41 8.23 6 16 1.2 2.64-0.5 5-30.65 21.41-68 18.5-25.16-6.17-32.5-30.5 6.96 4.99 15.5 6.5 8.99 0.75 18 0.5 16.25 2.38 32-2.5 15.9-3.94 27-15.5z" fill="currentColor"/><path fill-rule="evenodd" d="m239.5 342.5q7.02-0.25 14 0.5 4.46 1.06 8 3.5-5.2 2.35-10 5.5-3.88 4.65-9 7.5-9.89-3.09-9.5-13 2.36-3.63 6.5-4z" fill="currentColor"/><path fill-rule="evenodd" d="m214.5 349.5q5.96 7.2 13.5 13 1 1 0 2-28.58 23.34-65.5 20.5-18.15-4.24-27.5-19.5 1.13 0.94 2.5 1.5 14.7 1.42 29-1.5 26.57-0.52 48-16z" fill="currentColor"/><path fill-rule="evenodd" d="m302.5 373.5q0.21 2.44-2 3.5-28.69 7.6-50.5-12.5-0.06-6.71 6.5-9 4.45-0.75 9-1 22.26 2.27 37 19z" fill="currentColor"/><path fill-rule="evenodd" d="m232.5 365.5q17.6 6.19 10.5 23-10.6 10.42-25.5 11.5-25.94 3.21-49-9 36.75-1.65 64-25.5z" fill="currentColor"/><path fill-rule="evenodd" d="m113.5 367.5q7.7-0.01 9.5 7-9.69 7.19-18.5 15.5-7.23 5.76-5.5-3.5 3.12-12.84 14.5-19z" fill="currentColor"/><path fill-rule="evenodd" d="m126.5 380.5q7.88-0.4 12 6.5-8.5 7.25-17 14.5-5.62-12.55 5-21z" fill="currentColor"/><path fill-rule="evenodd" d="m283.5 385.5q3.22 2.95 7 5.5 2.8 4.03 6 7.5 0.42 2.77-2 4-15.5-9.75-31-19.5-1.79-0.98 0-1.5 9.96 2.49 20 4z" fill="currentColor"/></svg>} href="/integrations/openclaw">
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: OpenCode
|
||||
description: "Add persistent memory to OpenCode with the Mem0 plugin — MCP server, lifecycle hooks, and slash commands."
|
||||
description: "Add persistent memory to OpenCode with the Mem0 plugin — native SDK-backed memory tools, lifecycle hooks, and skills."
|
||||
---
|
||||
|
||||
Add persistent memory to [**OpenCode**](https://opencode.ai) with the Mem0 plugin. Your agent forgets everything between sessions — Mem0 fixes that by storing decisions, preferences, and learnings so they carry over automatically.
|
||||
@@ -30,27 +30,17 @@ echo 'export MEM0_API_KEY="m0-your-api-key"' >> ~/.bashrc && source ~/.bashrc
|
||||
opencode plugin @mem0/opencode-plugin
|
||||
```
|
||||
|
||||
|
||||
Or using this command which does the same thing:
|
||||
|
||||
|
||||
```bash
|
||||
bunx @mem0/opencode-plugin@latest install
|
||||
```
|
||||
|
||||
|
||||
|
||||
**Or let your agent do it** — paste this into OpenCode:
|
||||
|
||||
```
|
||||
Install @mem0/opencode-plugin by following https://raw.githubusercontent.com/mem0ai/mem0/main/integrations/mem0-plugin/.opencode-plugin/README.md
|
||||
```
|
||||
|
||||
All commands auto-add the plugin and MCP server to your `~/.config/opencode/opencode.json`. Restart OpenCode — you get the MCP server, lifecycle hooks, and all `/mem0:` slash commands.
|
||||
This adds the plugin to your `~/.config/opencode/opencode.json`. Restart OpenCode — you get the native memory tools, lifecycle hooks, and all `/mem0-*` slash commands. The memory tools are registered by the plugin itself via the `mem0ai` SDK — no MCP server to configure.
|
||||
|
||||
### Option B — MCP Only
|
||||
### Option B — Standalone MCP Server
|
||||
|
||||
If you only need the memory tools without hooks or skills, add this to your `opencode.json` (project-level or global at `~/.config/opencode/opencode.json`):
|
||||
If you only need the memory tools without the plugin's hooks or skills, point OpenCode at Mem0's hosted MCP server directly. Add this to your `opencode.json` (project-level or global at `~/.config/opencode/opencode.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
@@ -69,13 +59,13 @@ If you only need the memory tools without hooks or skills, add this to your `ope
|
||||
|
||||
## What's Included
|
||||
|
||||
| Component | Plugin (A) | MCP Only (B) |
|
||||
|-----------|:----------:|:------------:|
|
||||
| MCP Server (9 memory tools) | Yes | Yes |
|
||||
| Component | Plugin (A) | Standalone MCP (B) |
|
||||
|-----------|:----------:|:------------------:|
|
||||
| 9 memory tools | Native (SDK) | Remote MCP server |
|
||||
| Lifecycle Hooks | Yes | No |
|
||||
| 16 Slash Commands | Yes | No |
|
||||
| 9 Skills | Yes | No |
|
||||
|
||||
## Available MCP Tools
|
||||
## Available Memory Tools
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
@@ -89,25 +79,70 @@ If you only need the memory tools without hooks or skills, add this to your `ope
|
||||
| `delete_entities` | Delete a user/agent/app/run entity and its memories |
|
||||
| `list_entities` | List users/agents/apps/runs stored in Mem0 |
|
||||
|
||||
## Memory scope
|
||||
|
||||
`search_memories`, `get_memories`, `add_memory`, and `delete_all_memories` accept an optional **`scope`** that controls how widely they read or write:
|
||||
|
||||
| Scope | Reads | Writes |
|
||||
|-------|-------|--------|
|
||||
| `project` *(default)* | this repo (`user_id` + `app_id`) | this repo |
|
||||
| `session` | this run only (`+ run_id`) | this run |
|
||||
| `global` | **all your projects in the workspace** (`app_id: "*"`) | user-wide |
|
||||
|
||||
Just ask naturally — e.g. *"search my memories across all my projects"* — and the agent passes `scope: "global"`. For normal questions it stays scoped to the current project automatically.
|
||||
|
||||
To change the **default** scope (used when no scope is passed), run the `/mem0-scope` skill:
|
||||
|
||||
```
|
||||
/mem0-scope # show the current default scope + identity
|
||||
/mem0-scope global # save & search across all your projects by default
|
||||
/mem0-scope project # back to repo-only (the default)
|
||||
```
|
||||
|
||||
The default persists in `~/.mem0/settings.json` (`default_scope`) and is read fresh on each memory operation, so a change applies immediately — no restart. `delete_all_memories` always requires an explicit `scope: "global"` to delete user-wide, so changing the default can't trigger a cross-project wipe.
|
||||
|
||||
The project id (`app_id`) is derived from your git remote (`owner-repo`), falling back to the git repo's root directory name, then the current directory. Launch OpenCode from inside your repo so memories scope to the project rather than your home directory.
|
||||
|
||||
## Lifecycle Hooks
|
||||
|
||||
The plugin uses the [mem0ai](https://www.npmjs.com/package/mem0ai) TypeScript SDK directly — pure TypeScript, no Python, no shell scripts.
|
||||
|
||||
| OpenCode Event | Hook | What happens |
|
||||
|----------------|------|-------------|
|
||||
| `config` | **Config** | Registers the `/mem0-*` slash commands (`config.command`) and adds the plugin's own `opencode-skills/` dir to OpenCode's `skills.paths` for in-place skill discovery (no copying) |
|
||||
| `chat.message` | **Chat message** | Searches prior memories on session start, searches relevant memories before each prompt, auto-captures learnings periodically |
|
||||
| `tool.execute.before` | **Pre-tool** | Blocks MEMORY.md writes, injects `user_id`/`app_id` on mem0 tool calls |
|
||||
| `tool.execute.after` | **Post-tool** | Tracks stats, scans Bash errors and pre-fetches related error memories |
|
||||
| `experimental.chat.system.transform` | **System transform** | Injects memory context (session memories, search results, error lookups) into the system prompt |
|
||||
| `tool.execute.before` | **Pre-tool** | Blocks MEMORY.md writes, steering them to the `add_memory` tool |
|
||||
| `tool.execute.after` | **Post-tool** | Scans Bash errors and pre-fetches related error memories |
|
||||
| `experimental.chat.messages.transform` | **Messages transform** | Injects memory context (session memories, search results, error lookups) into the prompt |
|
||||
| `experimental.session.compacting` | **Compaction** | Stores session state memory, then injects prior memories into compaction context so nothing is lost |
|
||||
| `shell.env` | **Shell env** | Exports `MEM0_USER_ID`, `MEM0_APP_ID`, `MEM0_SESSION_ID`, and `MEM0_BRANCH` to all shell executions |
|
||||
|
||||
## Auto-dream (memory consolidation)
|
||||
|
||||
The plugin can automatically consolidate stored memories — merging duplicates, dropping stale/sensitive entries, and rewriting vague ones — so your memory set stays clean over time. It runs at most once per session, and only when **all** gates pass:
|
||||
|
||||
- **Time** — at least `minHours` (default 24) since the last consolidation
|
||||
- **Sessions** — at least `minSessions` (default 5) sessions since then
|
||||
- **Memories** — at least `minMemories` (default 20) stored for the project
|
||||
|
||||
A filesystem lock (`~/.mem0/mem0-dream.lock`) keeps two sessions from consolidating at once. Tune the thresholds with a `dream` block in `~/.mem0/settings.json`, or disable entirely with `MEM0_DREAM=false`:
|
||||
|
||||
```json
|
||||
{
|
||||
"dream": { "enabled": true, "auto": true, "minHours": 24, "minSessions": 5, "minMemories": 20 }
|
||||
}
|
||||
```
|
||||
|
||||
If auto-dream hasn't run yet, it's almost always because a gate hasn't been met (most often too few memories). Run `/mem0-status` to see the exact gate progress (e.g. `sessions 2/5, memories 3/20`), `/mem0-dream` to consolidate **now** regardless of the gates, or lower the thresholds above.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- **No tools appearing** — Restart OpenCode after installing
|
||||
- **"Connection failed"** — Verify your key is set: `echo $MEM0_API_KEY`
|
||||
- **Plugin not loading** — Run `opencode plugin @mem0/opencode-plugin` again, then restart
|
||||
- **Hooks not firing** — Hooks require the plugin install (Option A). MCP-only installs don't include hooks.
|
||||
- **Auto-dream never runs** — It's gated (time + sessions + memories). Run `/mem0-status` to see which gate is blocking, or `/mem0-dream` to consolidate now.
|
||||
- **Wrong project name / memories not found** — The project id comes from your git remote; launch OpenCode from inside the repo (not your home directory). Check the resolved id with `/mem0-status`.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="Mem0 MCP Setup" icon="puzzle-piece" href="/platform/mem0-mcp">
|
||||
|
||||
@@ -1,22 +1,22 @@
|
||||
---
|
||||
title: Keywords AI
|
||||
description: "Combine Mem0 persistent memory with Keywords AI observability for tracked, cost-optimized AI applications."
|
||||
title: Respan
|
||||
description: "Combine Mem0 persistent memory with Respan observability for tracked, cost-optimized AI applications."
|
||||
---
|
||||
|
||||
Build AI applications with persistent memory and comprehensive LLM observability by integrating Mem0 with Keywords AI.
|
||||
Build AI applications with persistent memory and comprehensive LLM observability by integrating Mem0 with Respan.
|
||||
|
||||
## Overview
|
||||
|
||||
Mem0 is a self-improving memory layer for LLM applications, enabling personalized AI experiences that save costs and delight users. Keywords AI provides complete LLM observability.
|
||||
Mem0 is a self-improving memory layer for LLM applications, enabling personalized AI experiences that save costs and delight users. Respan (formerly Keywords AI) provides complete LLM observability.
|
||||
|
||||
Combining Mem0 with Keywords AI allows you to:
|
||||
Combining Mem0 with Respan allows you to:
|
||||
1. Add persistent memory to your AI applications
|
||||
2. Track interactions across sessions
|
||||
3. Monitor memory usage and retrieval with Keywords AI observability
|
||||
3. Monitor memory usage and retrieval with Respan observability
|
||||
4. Optimize token usage and reduce costs
|
||||
|
||||
<Note>
|
||||
You can get your Mem0 API key from the <a href="https://app.mem0.ai/?utm_source=oss&utm_medium=integration-keywords" rel="nofollow">Mem0 dashboard</a>.
|
||||
You can get your Mem0 API key from the <a href="https://app.mem0.ai/?utm_source=oss&utm_medium=integration-respan" rel="nofollow">Mem0 dashboard</a>.
|
||||
</Note>
|
||||
|
||||
## Setup and Configuration
|
||||
@@ -24,7 +24,7 @@ You can get your Mem0 API key from the <a href="https://app.mem0.ai/?utm_source=
|
||||
Install the necessary libraries:
|
||||
|
||||
```bash
|
||||
pip install mem0ai keywordsai-sdk
|
||||
pip install mem0ai openai
|
||||
```
|
||||
|
||||
Set up your environment variables:
|
||||
@@ -34,13 +34,13 @@ import os
|
||||
|
||||
# Set your API keys
|
||||
os.environ["MEM0_API_KEY"] = "your-mem0-api-key"
|
||||
os.environ["KEYWORDSAI_API_KEY"] = "your-keywords-api-key"
|
||||
os.environ["KEYWORDSAI_BASE_URL"] = "https://api.keywordsai.co/api/"
|
||||
os.environ["RESPAN_API_KEY"] = "your-respan-api-key"
|
||||
os.environ["RESPAN_BASE_URL"] = "https://api.respan.ai/api/"
|
||||
```
|
||||
|
||||
## Basic Integration Example
|
||||
|
||||
Here's a simple example of using Mem0 with Keywords AI:
|
||||
Here's a simple example of using Mem0 with Respan:
|
||||
|
||||
```python
|
||||
from mem0 import Memory
|
||||
@@ -48,17 +48,17 @@ import os
|
||||
|
||||
# Configuration
|
||||
api_key = os.getenv("MEM0_API_KEY")
|
||||
keywordsai_api_key = os.getenv("KEYWORDSAI_API_KEY")
|
||||
base_url = os.getenv("KEYWORDSAI_BASE_URL") # "https://api.keywordsai.co/api/"
|
||||
respan_api_key = os.getenv("RESPAN_API_KEY")
|
||||
base_url = os.getenv("RESPAN_BASE_URL") # "https://api.respan.ai/api/"
|
||||
|
||||
# Set up Mem0 with Keywords AI as the LLM provider
|
||||
# Set up Mem0 with Respan as the LLM provider
|
||||
config = {
|
||||
"llm": {
|
||||
"provider": "openai",
|
||||
"config": {
|
||||
"model": "gpt-5-mini",
|
||||
"temperature": 0.0,
|
||||
"api_key": keywordsai_api_key,
|
||||
"api_key": respan_api_key,
|
||||
"openai_base_url": base_url,
|
||||
},
|
||||
}
|
||||
@@ -79,7 +79,7 @@ print(result)
|
||||
|
||||
## Advanced Integration with OpenAI SDK
|
||||
|
||||
For more advanced use cases, you can integrate Keywords AI with Mem0 through the OpenAI SDK:
|
||||
For more advanced use cases, you can integrate Respan with Mem0 through the OpenAI SDK:
|
||||
|
||||
```python
|
||||
from openai import OpenAI
|
||||
@@ -88,8 +88,8 @@ import json
|
||||
|
||||
# Initialize client
|
||||
client = OpenAI(
|
||||
api_key=os.environ.get("KEYWORDSAI_API_KEY"),
|
||||
base_url=os.environ.get("KEYWORDSAI_BASE_URL"),
|
||||
api_key=os.environ.get("RESPAN_API_KEY"),
|
||||
base_url=os.environ.get("RESPAN_BASE_URL"),
|
||||
)
|
||||
|
||||
# Sample conversation messages
|
||||
@@ -118,18 +118,18 @@ response = client.chat.completions.create(
|
||||
print(json.dumps(response.model_dump(), indent=4))
|
||||
```
|
||||
|
||||
For detailed information on this integration, refer to the official [Keywords AI Mem0 integration documentation](https://docs.keywordsai.co/integration/development-frameworks/mem0).
|
||||
For detailed information on this integration, refer to the official [Respan Mem0 integration documentation](https://www.respan.ai/docs/integrations/mem0).
|
||||
|
||||
## Key Features
|
||||
|
||||
1. **Memory Integration**: Store and retrieve relevant information from past interactions
|
||||
2. **LLM Observability**: Track memory usage and retrieval patterns with Keywords AI
|
||||
2. **LLM Observability**: Track memory usage and retrieval patterns with Respan
|
||||
3. **Session Persistence**: Maintain context across multiple user sessions
|
||||
4. **Cost Optimization**: Reduce token usage through efficient memory retrieval
|
||||
|
||||
## Conclusion
|
||||
|
||||
Integrating Mem0 with Keywords AI provides a powerful combination for building AI applications with persistent memory and comprehensive observability. This integration enables more personalized user experiences while providing insights into your application's memory usage.
|
||||
Integrating Mem0 with Respan provides a powerful combination for building AI applications with persistent memory and comprehensive observability. This integration enables more personalized user experiences while providing insights into your application's memory usage.
|
||||
|
||||
<CardGroup cols={2}>
|
||||
<Card title="OpenAI Agents SDK" icon="cube" href="/integrations/openai-agents-sdk">
|
||||
@@ -139,4 +139,3 @@ Integrating Mem0 with Keywords AI provides a powerful combination for building A
|
||||
Monitor agent performance with AgentOps
|
||||
</Card>
|
||||
</CardGroup>
|
||||
|
||||
+2
-1
@@ -197,6 +197,7 @@ If the user is on a pre-current major (Python < 2, TS < 3, or Platform `output_f
|
||||
- [Platform Features Overview](https://docs.mem0.ai/platform/features/platform-overview) [Platform]: Use when surveying what managed offers beyond CRUD.
|
||||
- [V2 Memory Filters](https://docs.mem0.ai/platform/features/v2-memory-filters) [Platform]: Use when compound filters (AND/OR on metadata, entity, time) are needed at search.
|
||||
- [Entity-Scoped Memory](https://docs.mem0.ai/platform/features/entity-scoped-memory) [Platform]: Use when partitioning memories by user, agent, app, or run.
|
||||
- [Graph Memory](https://docs.mem0.ai/platform/features/graph-memory) [Platform]: Use when connecting facts across memories through shared entities for entity-centric or multi-hop questions.
|
||||
- [Async Client](https://docs.mem0.ai/platform/features/async-client) [Platform]: Use when the app issues many concurrent Mem0 calls and needs non-blocking I/O.
|
||||
- [Multimodal Support](https://docs.mem0.ai/platform/features/multimodal-support) [Platform]: Use when storing images or PDFs as memory input.
|
||||
- [Custom Categories](https://docs.mem0.ai/platform/features/custom-categories) [Platform]: Use when the default categories do not match the domain.
|
||||
@@ -285,7 +286,7 @@ If the user is on a pre-current major (Python < 2, TS < 3, or Platform `output_f
|
||||
- [Dify](https://docs.mem0.ai/integrations/dify) [Both]: Use when the user is on Dify LLMOps.
|
||||
- [Flowise](https://docs.mem0.ai/integrations/flowise) [Both]: Use when the user is on Flowise no-code.
|
||||
- [AgentOps](https://docs.mem0.ai/integrations/agentops) [Both]: Use when tracking agent observability with memory metadata.
|
||||
- [Keywords AI](https://docs.mem0.ai/integrations/keywords) [Both]: Use when monitoring with Keywords AI.
|
||||
- [Respan](https://docs.mem0.ai/integrations/respan) [Both]: Use when monitoring Mem0 with Respan (formerly Keywords AI) LLM observability.
|
||||
- [Raycast](https://docs.mem0.ai/integrations/raycast) [Both]: Use when the user wants quick memory access via Raycast.
|
||||
|
||||
## Cookbooks
|
||||
|
||||
@@ -328,27 +328,27 @@ The new algorithm automatically creates a parallel entity store collection named
|
||||
Make sure your vector store user/credentials have permission to create new collections. If you're using a managed vector database with restricted permissions, pre-create the `{collection_name}_entities` collection with the same embedding dimensions as your main collection.
|
||||
</Warning>
|
||||
|
||||
## Graph Memory → Entity Linking
|
||||
## Graph Memory: Now Built-In
|
||||
|
||||
Graph store support has been removed from the open-source SDK. It is replaced by **built-in entity linking**, which runs natively with no external dependencies.
|
||||
External graph **store** support has been removed from the open-source SDK and replaced by **built-in graph memory** (entity linking), which runs natively with no external dependencies.
|
||||
|
||||
**What was removed:**
|
||||
- `enable_graph` / `enableGraph` config flag
|
||||
- `graph_store` / `graphStore` configuration block (Neo4j, Memgraph, Kuzu, Apache AGE, Neptune)
|
||||
- All graph memory code paths (~4000 lines)
|
||||
- All external graph store code paths (~4000 lines)
|
||||
|
||||
**What replaces it:**
|
||||
|
||||
Entity linking extracts entities (proper nouns, quoted text, compound noun phrases) from every memory during the add pipeline and stores them in a parallel collection (`{collection}_entities`) inside your existing vector store. At search time, entities from the query are matched against this collection and used to boost relevant memories. The boost is folded into the combined `score` on each result.
|
||||
Mem0 now builds the graph itself. It extracts entities (proper nouns, quoted text, compound noun phrases) from every memory during the add pipeline and stores them in a parallel collection (`{collection}_entities`) inside your existing vector store. Memories that share an entity are linked, and at search time entities from the query are matched against this collection to boost connected memories. The boost is folded into the combined `score` on each result.
|
||||
|
||||
**Migration:**
|
||||
- Remove `enable_graph` / `enableGraph` from your config
|
||||
- Remove the `graph_store` / `graphStore` block — it is no longer read
|
||||
- Uninstall graph drivers (neo4j, memgraph, etc.) if you were using them only for Mem0
|
||||
- No data migration is required. Entity linking activates automatically on the next `add()` call.
|
||||
- Uninstall external graph drivers (neo4j, memgraph, etc.) if you were using them only for Mem0
|
||||
- No data migration is required. Built-in graph memory activates automatically on the next `add()` call.
|
||||
|
||||
<Warning>
|
||||
Graph relationships exposed via the old `relations` field on search results are no longer populated. Entity relationships are consumed indirectly through retrieval ranking, not exposed as a queryable graph structure. If your application depended on traversing graph relationships directly, you will need to redesign that part against the new API.
|
||||
The old `relations` field on search results (populated by the external graph store) is no longer returned. Entity connections are now applied through retrieval ranking rather than exposed as a separate, directly traversable structure. If your application read or traversed the `relations` array, you will need to redesign that part against the new API.
|
||||
</Warning>
|
||||
|
||||
## How the New Algorithm Works
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
---
|
||||
title: "Platform: Migrating to the New Memory Algorithm"
|
||||
description: "Guide for Mem0 Platform users to adopt the new memory algorithm with single-pass extraction, entity linking, and multi-signal retrieval."
|
||||
description: "Guide for Mem0 Platform users to adopt the new memory algorithm with single-pass extraction, built-in graph memory, and multi-signal retrieval."
|
||||
icon: "arrow-right"
|
||||
iconType: "solid"
|
||||
---
|
||||
@@ -18,8 +18,7 @@ The new Mem0 memory algorithm is a ground-up redesign of how memories are extrac
|
||||
| **Extraction** | Two LLM passes (extract + merge) | Single-pass ADD-only (one LLM call) |
|
||||
| **Memory mutations** | ADD, UPDATE, DELETE | ADD only — nothing is overwritten or deleted |
|
||||
| **Agent-generated facts** | Often ignored | First-class, stored with equal weight |
|
||||
| **Entity linking** | Not available | Entities extracted and linked across memories |
|
||||
| **Graph memory** | Separate graph store + dashboard visualization | Replaced by built-in entity linking, no graph visuals on platform dashboard |
|
||||
| **Graph memory** | External graph store (Neo4j, etc.) + manual setup | Built-in and automatic; entities extracted and linked across memories natively, no external store |
|
||||
| **Retrieval** | Semantic (vector) only | Hybrid retrieval combining multiple signals |
|
||||
|
||||
## What This Means for Your Application
|
||||
@@ -249,19 +248,18 @@ await client.search("query", {
|
||||
For the full list of parameter changes across all SDKs, see the [OSS migration guide](/migration/oss-v2-to-v3#removed-parameters-reference).
|
||||
</Info>
|
||||
|
||||
## Graph Memory → Entity Linking
|
||||
## Graph Memory Is Now Built-In
|
||||
|
||||
Graph memory has been replaced by **built-in entity linking**. The changes:
|
||||
Graph memory no longer requires an external graph database. It is now **native to the platform** and automatic. The changes:
|
||||
|
||||
- **Graph visualizations removed from the platform dashboard.** The graph view in your project dashboard is no longer available.
|
||||
- **`enable_graph` project setting removed.** The toggle is gone from the dashboard; the API parameter is ignored.
|
||||
- **No external graph store to configure.** Previously graph memory required a separate Neo4j (or similar) deployment. Entity linking runs natively inside the platform — nothing to provision, no connection strings to manage.
|
||||
- **Entity linking is the native replacement.** Entities (proper nouns, quoted text, compound noun phrases) are automatically extracted from every memory and linked across memories belonging to the same user. At search time, entities from the query are matched against this index and used to boost ranking. The boost is folded into the combined `score` returned on each result.
|
||||
- **No external graph store to configure.** Previously, graph memory required a separate Neo4j (or similar) deployment. Mem0 now builds the graph itself from your memories, so there is nothing to provision and no connection strings to manage.
|
||||
- **Always on, no flag.** The `enable_graph` project setting is no longer needed; graph memory activates automatically. (The API parameter is now ignored if sent.)
|
||||
- **Connections power retrieval directly.** Entities (proper nouns, quoted text, compound noun phrases) are automatically extracted from every memory and linked across memories belonging to the same user. At search time, entities from the query are matched against the graph and used to boost ranking. The boost is folded into the combined `score` returned on each result.
|
||||
|
||||
**No migration work is required.** Entity linking activates automatically for all projects on the new algorithm. Existing memories are not re-processed, but any new memories you add will be indexed for entity-based retrieval going forward.
|
||||
**No migration work is required.** Graph memory activates automatically for all projects on the new algorithm. Existing memories are not re-processed, but any new memories you add are added to the graph going forward. See [Graph Memory](/platform/features/graph-memory) for how the built-in graph works.
|
||||
|
||||
<Note>
|
||||
If your application previously read graph relations from the API response (`relations` field on search results), note that this field is no longer populated. Entity relationships are now consumed indirectly through retrieval ranking, not exposed as a separate graph structure.
|
||||
If your application previously read graph relations from the API response (`relations` field on search results), note that this field is no longer populated. Entity connections are now applied through retrieval ranking rather than returned as a separate `relations` array.
|
||||
</Note>
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
@@ -5,6 +5,10 @@ description: Scope conversations by user, agent, app, and session so memories la
|
||||
|
||||
Mem0's Platform API lets you separate memories for different users, agents, and apps. By tagging each write and query with the right identifiers, you can prevent data from mixing between them, maintain clear audit trails, and control data retention.
|
||||
|
||||
<Note>
|
||||
**Entity IDs vs. graph entities.** This page covers the `user_id` / `agent_id` / `app_id` / `run_id` identifiers used to *scope* memories. These are different from the **graph entities** (the people, places, and concepts surfaced in [Graph Memory](/platform/features/graph-memory)).
|
||||
</Note>
|
||||
|
||||
<Tip icon="layers">
|
||||
Want the long-form tutorial? The <Link href="/cookbooks/essentials/entity-partitioning-playbook">Partition Memories by Entity</Link> cookbook walks through multi-agent storage, debugging, and cleanup step by step.
|
||||
</Tip>
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
---
|
||||
title: "Graph Memory"
|
||||
description: "Mem0 Platform builds a native graph linking people, places, and concepts across your memories, with no external graph database to provision."
|
||||
icon: "circle-nodes"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
Mem0 Platform automatically organizes your memories into a **graph**: the **graph entities** mentioned across your memories (the people, places, organizations, and concepts they refer to) become nodes, and memories that share an entity are connected. This is how Mem0 reasons across separate facts, for example linking everything it knows about a person, a company, or a project, without you defining any schema.
|
||||
|
||||
Graph Memory is **built in**. There is no Neo4j, Memgraph, or other graph store to deploy, no connection strings to manage, and nothing to enable. It runs natively inside the platform and is always on.
|
||||
|
||||
<Info>
|
||||
**Graph Memory matters when…**
|
||||
- You ask entity-centric questions like "what do we know about Alice?" and expect facts pulled from many different conversations
|
||||
- Your app needs multi-hop recall, connecting a fact in one memory to a related fact in another
|
||||
- You previously used an external graph store and want the same cross-memory connections with zero infrastructure
|
||||
</Info>
|
||||
|
||||
<Note>
|
||||
**Graph entities vs. entity IDs.** The entities in your graph (people, places, and concepts extracted from memory text) are different from the *entity IDs* (`user_id`, `agent_id`, `app_id`, `run_id`) used to scope memories. Those are covered in [Entity-Scoped Memory](/platform/features/entity-scoped-memory).
|
||||
</Note>
|
||||
|
||||
<Note>
|
||||
Graph Memory is the native successor to Mem0's earlier graph store integration. Earlier versions connected an external graph database (Neo4j and others) and exposed a `relations` field. Mem0 now builds the graph itself from your memories. See [What changed from the external graph store](#what-changed-from-the-external-graph-store) below.
|
||||
</Note>
|
||||
|
||||
## How it works
|
||||
|
||||
Graph Memory is built and used across the two phases of the memory pipeline: **extraction** (when you add memories) and **retrieval** (when you search).
|
||||
|
||||
### 1. Entities become nodes
|
||||
|
||||
Every time you add a memory, Mem0 extracts the **entities** it contains: the proper nouns, names, and key phrases that identify a specific person, place, organization, product, or concept (for example *Alice*, *San Francisco*, *Acme Corp*, *the Q1 roadmap*). Each distinct entity is stored once and embedded, so entities that refer to the same thing can be matched even when they are phrased differently.
|
||||
|
||||
### 2. Shared entities become connections
|
||||
|
||||
When the same entity appears in more than one memory, those memories are **linked** through that entity. Over time this forms a graph: a web of entities, each connecting all the memories that mention it. The connections are derived directly from your data. There is no relationship schema to define and nothing to label by hand.
|
||||
|
||||
### 3. The graph powers retrieval
|
||||
|
||||
At search time, Mem0 extracts the entities from your query and matches them against the graph. Memories connected to those entities receive a ranking boost, which is combined with semantic (vector) and keyword (BM25) scores into the single `score` returned on each result.
|
||||
|
||||
This is what lets Mem0 answer entity-centric and multi-hop questions: a query about *Alice* surfaces facts about Alice that live in completely different memories, because the graph connects them. The connecting-facts-across-memories behavior contributes to Mem0's gains on multi-hop and temporal benchmarks. See [Memory Evaluation](/core-concepts/memory-evaluation).
|
||||
|
||||
<Info>
|
||||
Graph Memory affects **ranking**, not the response shape. Search results come back in the normal format with a combined `score`; there is no separate graph payload to parse.
|
||||
</Info>
|
||||
|
||||
## What's in the graph
|
||||
|
||||
| Element | What it is |
|
||||
| --- | --- |
|
||||
| **Graph entity** (node) | A distinct person, place, organization, product, or concept extracted from your memories (e.g. *Alice*, *Acme Corp*). Distinct from the user/agent/app/run *entity IDs* used to scope memories. |
|
||||
| **Memory node** | An individual memory (fact) stored for a user, agent, or session. |
|
||||
| **Connection** | A link between an entity and every memory that mentions it. Two entities are related when they co-occur in one or more memories. |
|
||||
|
||||
Graph Memory captures **which entities your memories are about and how they connect through shared context**. It does not assign typed, labeled relationships between entities (it won't, for example, record a "manages" edge from one person to another); connections are inferred from co-occurrence rather than declared. This is what makes it schema-free and zero-configuration.
|
||||
|
||||
## Availability
|
||||
|
||||
Graph Memory is **automatic and included on all plans**. It activates on the new memory algorithm with no flag, no configuration, and no external dependencies. You don't need to do anything to benefit from it.
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-api-key")
|
||||
|
||||
# Entities are extracted and linked into the graph automatically on add
|
||||
client.add(
|
||||
messages=[
|
||||
{"role": "user", "content": "I work at Acme Corp with Alice on the Q1 roadmap"}
|
||||
],
|
||||
user_id="jordan",
|
||||
)
|
||||
|
||||
# Entity matches from the query are used to connect and boost related memories
|
||||
results = client.search(
|
||||
query="who does jordan work with?",
|
||||
filters={"user_id": "jordan"},
|
||||
)
|
||||
```
|
||||
|
||||
## What changed from the external graph store
|
||||
|
||||
Earlier versions of Mem0 offered graph memory by connecting an **external graph database** (Neo4j, Memgraph, Kuzu, Apache AGE, or Neptune) through an `enable_graph` flag and a `graph_store` configuration block. That integration has been replaced by **native, built-in Graph Memory**:
|
||||
|
||||
- **No external graph store.** The graph is built inside Mem0 from your memories. There is nothing to provision or connect.
|
||||
- **Always on, all plans.** The `enable_graph` flag is no longer needed; Graph Memory is automatic. (If you still send the parameter, it is ignored.)
|
||||
- **Connections power retrieval directly.** Entity connections are folded into the combined `score` on each result. The standalone `relations` field that the external graph store returned is no longer populated. If your application read that field, see the migration guide below.
|
||||
|
||||
<Card title="Platform Migration Guide" icon="arrow-right" href="/migration/platform-v2-to-v3">
|
||||
Full details on the move to the new algorithm, including the `relations` field change.
|
||||
</Card>
|
||||
@@ -23,7 +23,7 @@
|
||||
"@types/js-cookie": "^3.0.6",
|
||||
"@types/react-syntax-highlighter": "^15.5.13",
|
||||
"@types/uuid": "^10.0.0",
|
||||
"ai": "^4.1.46",
|
||||
"ai": "^5.0.52",
|
||||
"class-variance-authority": "^0.7.1",
|
||||
"clsx": "^2.1.1",
|
||||
"js-cookie": "^3.0.6",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"@radix-ui/react-scroll-area": "^1.2.0",
|
||||
"@radix-ui/react-select": "^2.1.2",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"ai": "4.1.42",
|
||||
"ai": "^5.0.52",
|
||||
"buffer": "^6.0.3",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
|
||||
@@ -18,7 +18,7 @@
|
||||
"@radix-ui/react-scroll-area": "^1.2.0",
|
||||
"@radix-ui/react-select": "^2.1.2",
|
||||
"@radix-ui/react-slot": "^1.1.0",
|
||||
"ai": "4.1.42",
|
||||
"ai": "^5.0.52",
|
||||
"buffer": "^6.0.3",
|
||||
"class-variance-authority": "^0.7.0",
|
||||
"clsx": "^2.1.1",
|
||||
|
||||
@@ -2,6 +2,36 @@
|
||||
|
||||
All notable changes to the `@mem0/opencode-plugin` will be documented in this file.
|
||||
|
||||
## 0.2.0 — Native SDK tools, MCP-free, leaner skill set
|
||||
|
||||
### Changed (breaking)
|
||||
|
||||
- **Memory tools are now native OpenCode tools** registered via the `@opencode-ai/plugin` `tool()` helper and backed by the `mem0ai` SDK directly. The plugin no longer registers or depends on the remote MCP server (`mcp.mem0.ai`); the bundled `opencode.json` and the regex-based MCP call interception have been removed. Tools: `add_memory`, `search_memories`, `get_memories`, `get_memory`, `update_memory`, `delete_memory`, `delete_all_memories`, `delete_entities`, `list_entities`, plus a `get_event_status` helper for async-write status.
|
||||
- **Skills load via the `config` hook (`skills.paths`)** instead of being copied into the project's `.opencode/` directory on startup. The `installSkills()` filesystem copy and the `cli.ts` installer (`mem0-opencode` bin) have been removed — install with `opencode plugin @mem0/opencode-plugin`.
|
||||
- **Trimmed to 9 focused skills** (`context-loader`, `dream`, `forget`, `status`, `search`, `scope`, `pin`, `remember`, `tour`). Removed `import`, `export`, `memory-reviewer`, `mem0` (SDK reference), `list-projects`, `stats`, and `onboard`. The old stateful `switch-project` skill is superseded by the project/session/global scope model and the new `/mem0-scope` skill.
|
||||
|
||||
### Added
|
||||
|
||||
- **Expanded telemetry to the full shared `plugin.*` schema.** In addition to `plugin.session_start` and `plugin.tool_use`, the plugin now emits `plugin.user_prompt`, `plugin.bash_error`, `plugin.pre_compact`, and `plugin.session_stop`. `tool_use` now fires from inside each native tool. Every event also carries `project_hash` (anonymized `sha256(app_id)`) and `os_version`, matching the editor plugin's `telemetry.py`.
|
||||
- **Auto-dream — gated automatic memory consolidation** (ported from the pi-agent plugin). When the time (`minHours`, default 24), session-count (`minSessions`, default 5), and memory-count (`minMemories`, default 20) gates all pass, the plugin injects a consolidation protocol so the agent merges duplicates, drops stale/sensitive entries, and rewrites vague ones before answering. A filesystem lock (`~/.mem0/mem0-dream.lock`) prevents concurrent sessions from dreaming at once, and completion resets the gates. Tune via the `dream` block in `~/.mem0/settings.json`; disable with `MEM0_DREAM=false`. Emits `plugin.dream_triggered` / `plugin.dream_completed`.
|
||||
- **Memory `scope` — per-call parameter and a persistent default.** `search_memories`, `get_memories`, `add_memory`, and `delete_all_memories` accept an optional `scope`: `"project"` (this repo, default), `"session"` (this run, adds `run_id`), or `"global"` (across all the user's projects — `app_id: "*"` for reads, user-wide for writes). The new **`/mem0-scope` skill** views and changes the *default* scope (used when no scope is passed), persisted to `~/.mem0/settings.json` (`default_scope`) and read **fresh on each memory operation** so changes apply immediately — no restart. `add_memory` / `search_memories` / `get_memories` honor the default (an explicit `scope`, `filters`, or `agent_id` still wins; a `project` default preserves prior behavior, including `global_search`).
|
||||
|
||||
### Changed
|
||||
|
||||
- **`/mem0-status` now reports the active default scope and auto-dream readiness.** It reads `default_scope` from `~/.mem0/settings.json` (falling back to `project`) and shows the auto-dream gate progress (sessions / memories / time vs. thresholds) so it's clear *why* a consolidation hasn't run yet.
|
||||
|
||||
### Fixed
|
||||
|
||||
- **Skills load in place via `skills.paths` — no copying.** The `config` hook adds the plugin's own `opencode-skills/` directory to OpenCode's `skills.paths`, so OpenCode discovers the skills directly from the linked/installed plugin package (recursive `**/SKILL.md` scan). The `installSkills()` step that copied skills into `~/.config/opencode/skills/` (and the legacy `~/.opencode/skills/`) and its version-marker gating are removed — the plugin no longer writes into those directories or creates `~/.opencode`. The `config` hook still registers the `/mem0-*` slash commands via `config.command`: OpenCode's TUI slash menu is built from `config.command`, and skills on `skills.paths` are available to the agent's skill tool but do not appear as slash commands on their own. Skill dir names are `mem0-<skill>` (matching `^[a-z0-9]+(-[a-z0-9]+)*$`); commands are `/mem0-<skill>`.
|
||||
- **Robust project-id (`app_id`) detection.** Parsed from the git remote's `owner/repo` — handling https, scp-style ssh, and **custom ssh host aliases** like `git@github.com-work:owner/repo.git` — falling back to the git repo's **root directory name** (not the cwd, which may be a sub-directory or your home dir), then the cwd. Fixes the project showing as your username/home when OpenCode was launched outside the repo root.
|
||||
- **Auto-dream visibility + robustness.** When auto-dream doesn't fire, the plugin logs the blocking gate (e.g. `auto-dream waiting — memories: 3 < 20`), and `/mem0-status` surfaces the same gate progress. The session-start memory count is parsed defensively (handles both paginated `{count}` and bare-array SDK responses) so the memory gate evaluates correctly.
|
||||
- **Error-pattern lookup** in `tool.execute.after` no longer issues two identical `mem0.search()` calls; it now performs a single `topK: 6` search.
|
||||
- Corrected the documented system-prompt hook name from `experimental.chat.system.transform` to the actual `experimental.chat.messages.transform`.
|
||||
|
||||
### Safety
|
||||
|
||||
- **`delete_all_memories` deliberately ignores the default scope.** Deleting user-wide always requires an explicit `scope="global"`, so raising the default to `global` can never turn a routine cleanup into a cross-project wipe.
|
||||
|
||||
## 0.1.3 — File-context injection, session summaries & activity timeline, anonymous telemetry
|
||||
|
||||
### Added
|
||||
|
||||
@@ -4,24 +4,18 @@ Persistent memory for [OpenCode](https://opencode.ai). Your agent remembers deci
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
bunx @mem0/opencode-plugin@latest install
|
||||
```
|
||||
|
||||
Or using OpenCode's built-in CLI:
|
||||
|
||||
```bash
|
||||
opencode plugin @mem0/opencode-plugin
|
||||
```
|
||||
|
||||
This adds the plugin to your `~/.config/opencode/opencode.json`. The plugin registers its memory tools and skills itself — there is no MCP server to configure.
|
||||
|
||||
**Or let your agent do it** — paste this into OpenCode:
|
||||
|
||||
```
|
||||
Install @mem0/opencode-plugin by following https://raw.githubusercontent.com/mem0ai/mem0/main/integrations/mem0-plugin/.opencode-plugin/README.md
|
||||
```
|
||||
|
||||
All commands auto-add the plugin and MCP server to your `~/.config/opencode/opencode.json`. No manual config needed.
|
||||
|
||||
Get your API key (free): [app.mem0.ai/dashboard/api-keys](https://app.mem0.ai/dashboard/api-keys)
|
||||
|
||||
```bash
|
||||
@@ -34,24 +28,25 @@ Restart OpenCode.
|
||||
|
||||
| Component | Description |
|
||||
|-----------|-------------|
|
||||
| **MCP Server** | 9 memory tools — add, search, get, update, delete memories |
|
||||
| **Lifecycle Hooks** | Auto-search on session start and every prompt, metadata enforcement, error memory lookup, compaction context |
|
||||
| **16 Slash Commands** | `/mem0:remember`, `/mem0:tour`, `/mem0:stats`, `/mem0:health`, `/mem0:dream`, and more |
|
||||
| **9 Native Memory Tools** | `add_memory`, `search_memories`, `get_memories`, `update_memory`, `delete_memory`, and more — registered as OpenCode tools, backed by the `mem0ai` SDK (no MCP server required) |
|
||||
| **Lifecycle Hooks** | Auto-search on session start and every prompt, error memory lookup, compaction context, secret redaction |
|
||||
| **9 Skills** | `/mem0-remember`, `/mem0-tour`, `/mem0-search`, `/mem0-status`, `/mem0-scope`, `/mem0-dream`, `/mem0-forget`, `/mem0-pin`, `/mem0-context-loader` — discovered in place from the plugin via OpenCode's `skills.paths` |
|
||||
|
||||
## Hooks
|
||||
|
||||
Pure TypeScript — no Python, no shell scripts. Uses the [mem0ai](https://www.npmjs.com/package/mem0ai) SDK directly.
|
||||
Pure TypeScript — no Python, no shell scripts. Memory operations are native OpenCode tools backed by the [mem0ai](https://www.npmjs.com/package/mem0ai) SDK directly.
|
||||
|
||||
| Hook | Event | What it does |
|
||||
|------|-------|-------------|
|
||||
| **Config** | `config` | Registers the `/mem0-*` slash commands (via `config.command`) and adds the plugin's own `opencode-skills/` dir to OpenCode's `skills.paths` for in-place skill discovery — no copying into `~/.config/opencode/skills` |
|
||||
| **Chat message** | `chat.message` | Loads prior memories on session start, searches relevant memories before each prompt, auto-captures learnings periodically |
|
||||
| **Pre-tool** | `tool.execute.before` | Blocks MEMORY.md writes, enforces `user_id`/`app_id` on mem0 tools |
|
||||
| **Post-tool** | `tool.execute.after` | Tracks stats, scans bash errors for related memories |
|
||||
| **System transform** | `experimental.chat.system.transform` | Injects memory context (session memories, search results, error lookups) into system prompt |
|
||||
| **Pre-tool** | `tool.execute.before` | Blocks MEMORY.md writes, steering them to the `add_memory` tool |
|
||||
| **Post-tool** | `tool.execute.after` | Scans bash errors and pre-fetches related memories |
|
||||
| **Messages transform** | `experimental.chat.messages.transform` | Injects memory context (session memories, search results, error lookups) into the prompt |
|
||||
| **Compaction** | `experimental.session.compacting` | Stores session state memory, then injects prior memories into compaction context so nothing is lost |
|
||||
| **Shell env** | `shell.env` | Exports `MEM0_USER_ID`, `MEM0_APP_ID`, `MEM0_SESSION_ID`, and `MEM0_BRANCH` to shell |
|
||||
|
||||
## MCP Tools
|
||||
## Memory Tools
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
@@ -65,6 +60,28 @@ Pure TypeScript — no Python, no shell scripts. Uses the [mem0ai](https://www.n
|
||||
| `delete_entities` | Delete an entity and its memories |
|
||||
| `list_entities` | List users/agents/apps stored in Mem0 |
|
||||
|
||||
## Memory scope
|
||||
|
||||
Every memory tool accepts an optional `scope`, and you can set the **default**
|
||||
scope (used when none is passed) with the `/mem0-scope` skill:
|
||||
|
||||
| Scope | Reads | Writes |
|
||||
|-------|-------|--------|
|
||||
| `project` (default) | this repo (`user_id` + `app_id`) | this repo |
|
||||
| `session` | this run (adds `run_id`) | this run |
|
||||
| `global` | all your projects (`app_id="*"`) | user-wide (drops `app_id`) |
|
||||
|
||||
```
|
||||
/mem0-scope # show the current default scope
|
||||
/mem0-scope global # save & search across all your projects by default
|
||||
/mem0-scope project # back to repo-only (default)
|
||||
```
|
||||
|
||||
The default persists in `~/.mem0/settings.json` (`default_scope`) and is read
|
||||
fresh on each memory operation, so a change applies immediately — no restart.
|
||||
`delete_all_memories` always requires an explicit `scope="global"` to delete
|
||||
user-wide, so changing the default can't trigger a cross-project wipe.
|
||||
|
||||
## Verify
|
||||
|
||||
Start OpenCode and ask: *"Search my memories for recent decisions"*
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
"name": "@mem0/opencode-plugin",
|
||||
"dependencies": {
|
||||
"@opencode-ai/plugin": "^1.0.162",
|
||||
"mem0ai": "^3.0.7",
|
||||
"mem0ai": "^3.0.8",
|
||||
},
|
||||
"devDependencies": {
|
||||
"bun-types": ">=1.3.14",
|
||||
@@ -462,7 +462,7 @@
|
||||
|
||||
"md5": ["md5@2.3.0", "", { "dependencies": { "charenc": "0.0.2", "crypt": "0.0.2", "is-buffer": "~1.1.6" } }, "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g=="],
|
||||
|
||||
"mem0ai": ["mem0ai@3.0.7", "", { "dependencies": { "axios": "^1.16.0", "openai": "^4.93.0", "uuid": "9.0.1", "zod": "^3.24.1" }, "peerDependencies": { "@anthropic-ai/sdk": "^0.40.1", "@azure/identity": "^4.0.0", "@azure/search-documents": "^12.0.0", "@cloudflare/workers-types": "^4.20250504.0", "@google/genai": "^1.40.0", "@langchain/core": "^1.1.47", "@mistralai/mistralai": "^1.5.2", "@qdrant/js-client-rest": "^1.18.0", "@supabase/supabase-js": "^2.49.1", "@types/jest": "29.5.14", "@types/pg": "8.11.0", "better-sqlite3": "^12.6.2", "cloudflare": "^4.2.0", "compromise": "^14.0.0", "groq-sdk": "0.3.0", "natural": "^8.0.1", "ollama": "^0.5.14", "pg": "8.11.3", "redis": "^4.6.13" } }, "sha512-CUHzX7DyeKTHcI3aDsSqY9LXTD7GcFxf988796TuOa4yJgGuF2Xd2NROcBhVFRo3r9y8fVmbo3c5TF9jv1KlKw=="],
|
||||
"mem0ai": ["mem0ai@3.0.8", "", { "dependencies": { "axios": "^1.16.0", "openai": "^4.93.0", "uuid": "^11.1.1", "zod": "^3.24.1" }, "peerDependencies": { "@anthropic-ai/sdk": "^0.40.1", "@azure/identity": "^4.0.0", "@azure/search-documents": "^12.0.0", "@cloudflare/workers-types": "^4.20250504.0", "@google/genai": "^1.40.0", "@langchain/core": "^1.1.47", "@mistralai/mistralai": "^1.5.2", "@qdrant/js-client-rest": "^1.18.0", "@supabase/supabase-js": "^2.49.1", "@types/jest": "29.5.14", "@types/pg": "8.11.0", "better-sqlite3": "^12.6.2", "cloudflare": "^4.2.0", "compromise": "^14.0.0", "groq-sdk": "0.3.0", "natural": "^8.0.1", "ollama": "^0.5.14", "pg": "8.11.3", "redis": "^4.6.13" } }, "sha512-6lvHGOYc/Z2r0JEulS559MVPOOiOtfAqG02VESDY7b0WAZwsmgLWAjQkbmWnD9fbXQQ3QvSpSaVx1q6Ex01eLQ=="],
|
||||
|
||||
"memjs": ["memjs@1.3.2", "", {}, "sha512-qUEg2g8vxPe+zPn09KidjIStHPtoBO8Cttm8bgJFWWabbsjQ9Av9Ky+6UcvKx6ue0LLb/LEhtcyQpRyKfzeXcg=="],
|
||||
|
||||
@@ -652,7 +652,7 @@
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="],
|
||||
"uuid": ["uuid@11.1.1", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ=="],
|
||||
|
||||
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
|
||||
|
||||
|
||||
@@ -1,254 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
import {
|
||||
readFileSync,
|
||||
writeFileSync,
|
||||
existsSync,
|
||||
mkdirSync,
|
||||
copyFileSync,
|
||||
readdirSync,
|
||||
rmSync,
|
||||
statSync,
|
||||
} from "fs";
|
||||
import { join, dirname } from "path";
|
||||
import { homedir } from "os";
|
||||
|
||||
const PLUGIN_NAME = "@mem0/opencode-plugin";
|
||||
const MCP_CONFIG = {
|
||||
mem0: {
|
||||
type: "remote",
|
||||
url: "https://mcp.mem0.ai/mcp/",
|
||||
headers: {
|
||||
Authorization: "Token {env:MEM0_API_KEY}",
|
||||
},
|
||||
oauth: false,
|
||||
},
|
||||
};
|
||||
|
||||
const SKILLS_NAMESPACE = "mem0";
|
||||
|
||||
function getConfigDir(): string {
|
||||
const dir = join(homedir(), ".config", "opencode");
|
||||
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
||||
return dir;
|
||||
}
|
||||
|
||||
function getConfigPath(): string {
|
||||
const configDir = getConfigDir();
|
||||
const jsonc = join(configDir, "opencode.jsonc");
|
||||
if (existsSync(jsonc)) return jsonc;
|
||||
return join(configDir, "opencode.json");
|
||||
}
|
||||
|
||||
function stripJsonComments(text: string): string {
|
||||
let result = "";
|
||||
let i = 0;
|
||||
let inString = false;
|
||||
let escape = false;
|
||||
while (i < text.length) {
|
||||
const ch = text[i];
|
||||
if (escape) {
|
||||
result += ch;
|
||||
escape = false;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (inString) {
|
||||
if (ch === "\\") escape = true;
|
||||
else if (ch === '"') inString = false;
|
||||
result += ch;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (ch === '"') {
|
||||
inString = true;
|
||||
result += ch;
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (ch === "/" && text[i + 1] === "/") {
|
||||
while (i < text.length && text[i] !== "\n") i++;
|
||||
continue;
|
||||
}
|
||||
if (ch === "/" && text[i + 1] === "*") {
|
||||
i += 2;
|
||||
while (i < text.length && !(text[i] === "*" && text[i + 1] === "/")) i++;
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
result += ch;
|
||||
i++;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function resolvePluginDir(): string {
|
||||
try {
|
||||
return dirname(new URL(import.meta.url).pathname);
|
||||
} catch {}
|
||||
return __dirname ?? process.cwd();
|
||||
}
|
||||
|
||||
function findSkillsDir(): string {
|
||||
const base = resolvePluginDir();
|
||||
const candidates = [
|
||||
join(base, "opencode-skills"),
|
||||
join(dirname(base), "opencode-skills"),
|
||||
join(base, "..", "opencode-skills"),
|
||||
];
|
||||
for (const c of candidates) {
|
||||
try {
|
||||
if (existsSync(c) && statSync(c).isDirectory()) return c;
|
||||
} catch {}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function installSkills(): number {
|
||||
const skillsSource = findSkillsDir();
|
||||
if (!skillsSource) {
|
||||
console.log(" ! Skills directory not found — skipping slash command install");
|
||||
return 0;
|
||||
}
|
||||
|
||||
const skillsTarget = join(getConfigDir(), "skills");
|
||||
if (!existsSync(skillsTarget)) mkdirSync(skillsTarget, { recursive: true });
|
||||
|
||||
let count = 0;
|
||||
const entries = readdirSync(skillsSource);
|
||||
|
||||
for (const name of entries) {
|
||||
const skillDir = join(skillsSource, name);
|
||||
try {
|
||||
if (!statSync(skillDir).isDirectory()) continue;
|
||||
} catch {
|
||||
continue;
|
||||
}
|
||||
|
||||
const skillFile = join(skillDir, "SKILL.md");
|
||||
if (!existsSync(skillFile)) continue;
|
||||
|
||||
const targetDir = join(skillsTarget, `${SKILLS_NAMESPACE}-${name}`);
|
||||
if (!existsSync(targetDir)) mkdirSync(targetDir, { recursive: true });
|
||||
|
||||
copyFileSync(skillFile, join(targetDir, "SKILL.md"));
|
||||
count++;
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
function uninstallSkills(): number {
|
||||
const skillsTarget = join(getConfigDir(), "skills");
|
||||
if (!existsSync(skillsTarget)) return 0;
|
||||
|
||||
let count = 0;
|
||||
const entries = readdirSync(skillsTarget);
|
||||
|
||||
for (const name of entries) {
|
||||
if (!name.startsWith(`${SKILLS_NAMESPACE}-`)) continue;
|
||||
const fullPath = join(skillsTarget, name);
|
||||
try {
|
||||
if (!statSync(fullPath).isDirectory()) continue;
|
||||
rmSync(fullPath, { recursive: true });
|
||||
count++;
|
||||
} catch {}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
function install() {
|
||||
console.log("Installing Mem0 plugin for OpenCode...\n");
|
||||
|
||||
const configPath = getConfigPath();
|
||||
let config: any = {};
|
||||
|
||||
if (existsSync(configPath)) {
|
||||
try {
|
||||
const raw = readFileSync(configPath, "utf-8");
|
||||
config = JSON.parse(stripJsonComments(raw));
|
||||
} catch {
|
||||
console.log(` ! Could not parse ${configPath}, creating fresh config`);
|
||||
config = {};
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.isArray(config.plugin)) config.plugin = [];
|
||||
if (!config.plugin.includes(PLUGIN_NAME)) {
|
||||
config.plugin.push(PLUGIN_NAME);
|
||||
console.log(` + Added "${PLUGIN_NAME}" to plugin array`);
|
||||
} else {
|
||||
console.log(` ~ "${PLUGIN_NAME}" already in plugin array`);
|
||||
}
|
||||
|
||||
if (!config.mcp) config.mcp = {};
|
||||
if (!config.mcp.mem0) {
|
||||
config.mcp.mem0 = MCP_CONFIG.mem0;
|
||||
console.log(" + Added mem0 MCP server config");
|
||||
} else {
|
||||
console.log(" ~ mem0 MCP server already configured");
|
||||
}
|
||||
|
||||
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
||||
console.log(`\n Wrote ${configPath}`);
|
||||
|
||||
const skillCount = installSkills();
|
||||
if (skillCount > 0) {
|
||||
console.log(` + Installed ${skillCount} slash commands to ~/.config/opencode/skills/`);
|
||||
}
|
||||
|
||||
console.log("");
|
||||
|
||||
if (!process.env.MEM0_API_KEY) {
|
||||
console.log(" ! MEM0_API_KEY is not set in your environment.");
|
||||
console.log(
|
||||
' Run: echo \'export MEM0_API_KEY="m0-your-key"\' >> ~/.zshrc && source ~/.zshrc',
|
||||
);
|
||||
console.log(
|
||||
" Get a free key at: https://app.mem0.ai/dashboard/api-keys\n",
|
||||
);
|
||||
} else {
|
||||
console.log(" MEM0_API_KEY detected\n");
|
||||
}
|
||||
|
||||
console.log("Done! Restart OpenCode to activate Mem0.");
|
||||
console.log(" Then run /mem0-onboard in the TUI to complete setup.\n");
|
||||
}
|
||||
|
||||
function uninstall() {
|
||||
const configPath = getConfigPath();
|
||||
if (!existsSync(configPath)) {
|
||||
console.log("No OpenCode config found. Nothing to remove.");
|
||||
return;
|
||||
}
|
||||
|
||||
const raw = readFileSync(configPath, "utf-8");
|
||||
const config = JSON.parse(stripJsonComments(raw));
|
||||
|
||||
if (Array.isArray(config.plugin)) {
|
||||
config.plugin = config.plugin.filter((p: string) => p !== PLUGIN_NAME);
|
||||
if (config.plugin.length === 0) delete config.plugin;
|
||||
}
|
||||
|
||||
if (config.mcp?.mem0) {
|
||||
delete config.mcp.mem0;
|
||||
if (Object.keys(config.mcp).length === 0) delete config.mcp;
|
||||
}
|
||||
|
||||
writeFileSync(configPath, JSON.stringify(config, null, 2) + "\n");
|
||||
console.log(` Removed plugin + MCP from ${configPath}`);
|
||||
|
||||
const skillCount = uninstallSkills();
|
||||
if (skillCount > 0) {
|
||||
console.log(` Removed ${skillCount} slash commands from ~/.config/opencode/skills/`);
|
||||
}
|
||||
|
||||
console.log("Restart OpenCode to complete removal.");
|
||||
}
|
||||
|
||||
const cmd = process.argv[2];
|
||||
if (cmd === "uninstall" || cmd === "remove") {
|
||||
uninstall();
|
||||
} else {
|
||||
install();
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import {
|
||||
loadDreamConfig,
|
||||
incrementSessionCount,
|
||||
checkCheapGates,
|
||||
checkMemoryGate,
|
||||
acquireDreamLock,
|
||||
releaseDreamLock,
|
||||
recordDreamCompletion,
|
||||
DREAM_DEFAULTS,
|
||||
DREAM_PROTOCOL,
|
||||
} from "./dream";
|
||||
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), "mem0-dream-"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
try {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
delete process.env.MEM0_DREAM;
|
||||
});
|
||||
|
||||
describe("auto-dream gates", () => {
|
||||
test("memory gate passes at >= minMemories, fails below", () => {
|
||||
expect(checkMemoryGate(DREAM_DEFAULTS.minMemories, {}).pass).toBe(true);
|
||||
expect(checkMemoryGate(DREAM_DEFAULTS.minMemories - 1, {}).pass).toBe(false);
|
||||
});
|
||||
|
||||
test("cheap gates: fresh state blocks on session count, passes after enough sessions", () => {
|
||||
// Fresh state: time gate passes (lastConsolidatedAt=0), but 0 sessions blocks.
|
||||
expect(checkCheapGates(dir, {}).proceed).toBe(false);
|
||||
for (let i = 0; i < DREAM_DEFAULTS.minSessions; i++) {
|
||||
incrementSessionCount(dir, `ses_${i}`);
|
||||
}
|
||||
expect(checkCheapGates(dir, {}).proceed).toBe(true);
|
||||
});
|
||||
|
||||
test("incrementSessionCount only counts distinct session ids", () => {
|
||||
incrementSessionCount(dir, "ses_a");
|
||||
incrementSessionCount(dir, "ses_a");
|
||||
incrementSessionCount(dir, "ses_a");
|
||||
expect(checkCheapGates(dir, { minHours: 0 }).reason).toContain("sessions: 1");
|
||||
});
|
||||
|
||||
test("recordDreamCompletion resets gates (recent time blocks again)", () => {
|
||||
for (let i = 0; i < 6; i++) incrementSessionCount(dir, `ses_${i}`);
|
||||
expect(checkCheapGates(dir, {}).proceed).toBe(true);
|
||||
recordDreamCompletion(dir);
|
||||
const r = checkCheapGates(dir, {});
|
||||
expect(r.proceed).toBe(false);
|
||||
expect(r.reason).toContain("time");
|
||||
});
|
||||
|
||||
test("dream lock is exclusive and reclaimable after release", () => {
|
||||
expect(acquireDreamLock(dir)).toBe(true);
|
||||
expect(acquireDreamLock(dir)).toBe(false);
|
||||
releaseDreamLock(dir);
|
||||
expect(acquireDreamLock(dir)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("dream config", () => {
|
||||
test("defaults when no settings file", () => {
|
||||
const cfg = loadDreamConfig(dir);
|
||||
expect(cfg.enabled).toBe(true);
|
||||
expect(cfg.auto).toBe(true);
|
||||
expect(cfg.minMemories).toBe(DREAM_DEFAULTS.minMemories);
|
||||
});
|
||||
|
||||
test("MEM0_DREAM=false force-disables", () => {
|
||||
process.env.MEM0_DREAM = "false";
|
||||
expect(loadDreamConfig(dir).enabled).toBe(false);
|
||||
});
|
||||
|
||||
test("settings.json dream block overrides defaults", () => {
|
||||
writeFileSync(
|
||||
join(dir, "settings.json"),
|
||||
JSON.stringify({ dream: { minMemories: 99, auto: false } }),
|
||||
);
|
||||
const cfg = loadDreamConfig(dir);
|
||||
expect(cfg.minMemories).toBe(99);
|
||||
expect(cfg.auto).toBe(false);
|
||||
expect(cfg.enabled).toBe(true);
|
||||
});
|
||||
|
||||
test("protocol uses native tools, not the MCP tool", () => {
|
||||
expect(DREAM_PROTOCOL).toContain("get_memories");
|
||||
expect(DREAM_PROTOCOL).toContain("add_memory");
|
||||
expect(DREAM_PROTOCOL).not.toContain("mem0_memory");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Auto-dream: gated automatic memory consolidation for the Mem0 OpenCode plugin.
|
||||
*
|
||||
* Ported from the (stable) pi-agent plugin's dream module and adapted to
|
||||
* OpenCode's hook model. When the cheap gates (time since last consolidation +
|
||||
* sessions since) and the memory-count gate all pass, the plugin injects the
|
||||
* DREAM_PROTOCOL into the agent's context so it consolidates memories (merge
|
||||
* duplicates, drop stale/sensitive entries, rewrite vague ones) before
|
||||
* answering. A filesystem lock prevents concurrent sessions from dreaming at
|
||||
* once, and completion is recorded so it won't re-trigger until the next cycle.
|
||||
*
|
||||
* State + lock live in ~/.mem0/ alongside settings.json. Opt out with
|
||||
* MEM0_DREAM=false, or tune via the `dream` block in ~/.mem0/settings.json.
|
||||
*/
|
||||
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
export interface DreamConfig {
|
||||
enabled: boolean;
|
||||
auto: boolean;
|
||||
minHours: number;
|
||||
minSessions: number;
|
||||
minMemories: number;
|
||||
}
|
||||
|
||||
interface DreamState {
|
||||
lastConsolidatedAt: number;
|
||||
sessionsSince: number;
|
||||
lastSessionId: string | null;
|
||||
}
|
||||
|
||||
interface DreamLock {
|
||||
pid: number;
|
||||
startedAt: number;
|
||||
}
|
||||
|
||||
const LOCK_STALE_MS = 60 * 60 * 1000;
|
||||
|
||||
export const DREAM_DEFAULTS: DreamConfig = {
|
||||
enabled: true,
|
||||
auto: true,
|
||||
minHours: 24,
|
||||
minSessions: 5,
|
||||
minMemories: 20,
|
||||
};
|
||||
|
||||
function statePath(stateDir: string): string {
|
||||
return join(stateDir, "mem0-dream-state.json");
|
||||
}
|
||||
|
||||
function lockPath(stateDir: string): string {
|
||||
return join(stateDir, "mem0-dream.lock");
|
||||
}
|
||||
|
||||
function ensureDir(dir: string): void {
|
||||
try {
|
||||
mkdirSync(dir, { recursive: true });
|
||||
} catch {
|
||||
/* exists */
|
||||
}
|
||||
}
|
||||
|
||||
function readState(stateDir: string): DreamState {
|
||||
try {
|
||||
return JSON.parse(readFileSync(statePath(stateDir), "utf-8")) as DreamState;
|
||||
} catch {
|
||||
return { lastConsolidatedAt: 0, sessionsSince: 0, lastSessionId: null };
|
||||
}
|
||||
}
|
||||
|
||||
function writeState(stateDir: string, state: DreamState): void {
|
||||
ensureDir(stateDir);
|
||||
writeFileSync(statePath(stateDir), JSON.stringify(state, null, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load dream config from ~/.mem0/settings.json (`dream` block), applying
|
||||
* defaults. MEM0_DREAM=false (or 0/no/off) force-disables regardless.
|
||||
*/
|
||||
export function loadDreamConfig(settingsDir: string): DreamConfig {
|
||||
let envEnabled: boolean | undefined;
|
||||
const env = process.env.MEM0_DREAM;
|
||||
if (env !== undefined) {
|
||||
const s = env.toLowerCase();
|
||||
envEnabled = s !== "false" && s !== "0" && s !== "no" && s !== "off";
|
||||
}
|
||||
|
||||
let cfg: DreamConfig = { ...DREAM_DEFAULTS };
|
||||
try {
|
||||
const sp = join(settingsDir, "settings.json");
|
||||
if (existsSync(sp)) {
|
||||
const settings = JSON.parse(readFileSync(sp, "utf-8"));
|
||||
const d = settings?.dream;
|
||||
if (d && typeof d === "object") {
|
||||
cfg = {
|
||||
enabled: typeof d.enabled === "boolean" ? d.enabled : cfg.enabled,
|
||||
auto: typeof d.auto === "boolean" ? d.auto : cfg.auto,
|
||||
minHours: typeof d.minHours === "number" ? d.minHours : cfg.minHours,
|
||||
minSessions: typeof d.minSessions === "number" ? d.minSessions : cfg.minSessions,
|
||||
minMemories: typeof d.minMemories === "number" ? d.minMemories : cfg.minMemories,
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* defaults */
|
||||
}
|
||||
|
||||
if (envEnabled !== undefined) cfg.enabled = envEnabled;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
/** Count a new session toward the dream gate (once per distinct sessionId). */
|
||||
export function incrementSessionCount(stateDir: string, sessionId: string): void {
|
||||
const state = readState(stateDir);
|
||||
if (state.lastSessionId !== sessionId) {
|
||||
state.sessionsSince++;
|
||||
state.lastSessionId = sessionId;
|
||||
writeState(stateDir, state);
|
||||
}
|
||||
}
|
||||
|
||||
/** Cheap gates that don't need an API call: time since last + sessions since. */
|
||||
export function checkCheapGates(
|
||||
stateDir: string,
|
||||
config: Partial<DreamConfig>,
|
||||
): { proceed: boolean; reason?: string } {
|
||||
const minHours = config.minHours ?? DREAM_DEFAULTS.minHours;
|
||||
const minSessions = config.minSessions ?? DREAM_DEFAULTS.minSessions;
|
||||
const state = readState(stateDir);
|
||||
|
||||
const hoursSince = (Date.now() - state.lastConsolidatedAt) / 3_600_000;
|
||||
if (hoursSince < minHours) {
|
||||
return { proceed: false, reason: `time: ${hoursSince.toFixed(1)}h < ${minHours}h` };
|
||||
}
|
||||
if (state.sessionsSince < minSessions) {
|
||||
return { proceed: false, reason: `sessions: ${state.sessionsSince} < ${minSessions}` };
|
||||
}
|
||||
return { proceed: true };
|
||||
}
|
||||
|
||||
/** Memory-count gate (uses the count already fetched at session init). */
|
||||
export function checkMemoryGate(
|
||||
memoryCount: number,
|
||||
config: Partial<DreamConfig>,
|
||||
): { pass: boolean; reason?: string } {
|
||||
const minMemories = config.minMemories ?? DREAM_DEFAULTS.minMemories;
|
||||
if (memoryCount < minMemories) {
|
||||
return { pass: false, reason: `memories: ${memoryCount} < ${minMemories}` };
|
||||
}
|
||||
return { pass: true };
|
||||
}
|
||||
|
||||
/** Acquire an exclusive dream lock (stale locks > 1h are reclaimed). */
|
||||
export function acquireDreamLock(stateDir: string): boolean {
|
||||
ensureDir(stateDir);
|
||||
const lp = lockPath(stateDir);
|
||||
|
||||
try {
|
||||
const lock = JSON.parse(readFileSync(lp, "utf-8")) as DreamLock;
|
||||
if (Date.now() - lock.startedAt < LOCK_STALE_MS) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
unlinkSync(lp);
|
||||
} catch {
|
||||
/* race ok */
|
||||
}
|
||||
} catch {
|
||||
/* no lock file */
|
||||
}
|
||||
|
||||
const lock: DreamLock = { pid: process.pid, startedAt: Date.now() };
|
||||
try {
|
||||
writeFileSync(lp, JSON.stringify(lock), { flag: "wx" });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function releaseDreamLock(stateDir: string): void {
|
||||
try {
|
||||
unlinkSync(lockPath(stateDir));
|
||||
} catch {
|
||||
/* already gone */
|
||||
}
|
||||
}
|
||||
|
||||
/** Reset the gates after a successful consolidation. */
|
||||
export function recordDreamCompletion(stateDir: string): void {
|
||||
const state = readState(stateDir);
|
||||
state.lastConsolidatedAt = Date.now();
|
||||
state.sessionsSince = 0;
|
||||
state.lastSessionId = null;
|
||||
writeState(stateDir, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Consolidation protocol injected into the agent context when a dream is
|
||||
* triggered. Uses the plugin's native OpenCode memory tools (get_memories /
|
||||
* add_memory / delete_memory) rather than an MCP tool.
|
||||
*/
|
||||
export const DREAM_PROTOCOL = `<mem0-dream>
|
||||
You are running memory consolidation. Complete these steps using the mem0 memory tools (get_memories, add_memory, delete_memory):
|
||||
|
||||
1. ORIENT — Call get_memories to list all memories. Count by category. Note oldest/newest.
|
||||
|
||||
2. GATHER TARGETS — Review each memory. Classify as:
|
||||
- DELETE: sensitive information (API keys, passwords, tokens), expired/stale entries, noise, redundant operational details
|
||||
- MERGE: near-duplicates (same fact stated differently). Keep the better-worded one, delete the other.
|
||||
- REWRITE: vague, first-person, or poorly-categorized entries. add_memory with improved text, then delete_memory the old one.
|
||||
- KEEP: everything else.
|
||||
Skip any memory starting with "[PINNED]".
|
||||
|
||||
3. CONSOLIDATE — Execute the changes:
|
||||
- Delete stale/duplicate entries with delete_memory
|
||||
- For merges: add_memory the merged text, delete_memory both originals
|
||||
- For rewrites: add_memory the improved version, delete_memory the original
|
||||
|
||||
4. REPORT — Summarize: how many reviewed, deleted, merged, rewritten, final count.
|
||||
|
||||
Quality targets: zero sensitive data stored, zero duplicates, all entries are atomic (one fact each), 15-50 words each.
|
||||
After consolidation, respond to the user's message normally.
|
||||
</mem0-dream>`;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,80 +0,0 @@
|
||||
---
|
||||
name: export
|
||||
description: Exports all project memories to a portable Markdown file for backup or migration. Use when backing up memories, migrating to another project, sharing memory state with teammates, or archiving before cleanup.
|
||||
---
|
||||
|
||||
# Mem0 Export
|
||||
|
||||
Export all memories for the current project to a portable Markdown file.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Resolve identity
|
||||
|
||||
Determine the active identity:
|
||||
- `user_id` from `MEM0_USER_ID` env var, else `$USER`, else `"default"`
|
||||
- `project_id` (used as `app_id`) from `MEM0_PROJECT_ID` env var, or via the project resolver
|
||||
|
||||
### Step 2: Fetch all memories
|
||||
|
||||
Call `get_memories` with:
|
||||
- `filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]}`
|
||||
- `page_size=200`
|
||||
|
||||
If the response is paginated (i.e. the result contains a `next` cursor or the count equals `page_size`), continue fetching pages until all memories are retrieved.
|
||||
|
||||
### Step 3: Format each memory as a YAML-frontmatter block
|
||||
|
||||
For each memory record, produce a block in this exact format:
|
||||
|
||||
```
|
||||
---
|
||||
id: <memory.id>
|
||||
created_at: <memory.created_at>
|
||||
type: <memory.metadata.type or "">
|
||||
confidence: <memory.metadata.confidence or "">
|
||||
branch: <memory.metadata.branch or "">
|
||||
files: <memory.metadata.files joined with ", " or "">
|
||||
categories: <memory.categories joined with ", " or "">
|
||||
---
|
||||
<memory.memory or memory content string>
|
||||
|
||||
```
|
||||
|
||||
Notes:
|
||||
- The `---` delimiters must be on their own lines with no extra whitespace.
|
||||
- `files` and `categories` are written as comma-separated values on a single line.
|
||||
- Leave a blank line after the content before the next `---` (for readability).
|
||||
- If a field is missing or null, write an empty string (not "null").
|
||||
|
||||
### Step 4: Write the export file
|
||||
|
||||
Determine the output filename:
|
||||
|
||||
```
|
||||
mem0-export-<project_id>-<YYYY-MM-DD>.md
|
||||
```
|
||||
|
||||
Where `<YYYY-MM-DD>` is today's date in UTC.
|
||||
|
||||
Write all formatted blocks to this file using the Write tool (or equivalent). The file is written to the current working directory.
|
||||
|
||||
### Step 5: Print summary
|
||||
|
||||
```
|
||||
Exported <N> memories to <filename>
|
||||
```
|
||||
|
||||
Where `<N>` is the total number of memory blocks written.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If `get_memories` returns an error or zero memories, print:
|
||||
```
|
||||
No memories found for project <project_id>. Nothing exported.
|
||||
```
|
||||
- If the write fails, report the error to the user.
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -1,185 +0,0 @@
|
||||
---
|
||||
name: import
|
||||
description: Imports memories from an exported Markdown file or MEMORY.md into the current project. Use when migrating from another project, restoring from backup, importing Claude Code native MEMORY.md content, or setting up a new project with existing knowledge.
|
||||
---
|
||||
|
||||
# Mem0 Import
|
||||
|
||||
Import memories from a mem0 export file into the current project.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Determine the export file to import
|
||||
|
||||
If the user provided a filename as an argument to `/mem0:import <filename>`, use that file.
|
||||
|
||||
Otherwise, list `.md` files in the current directory whose names contain `mem0-export`:
|
||||
|
||||
```bash
|
||||
ls -1 *.md 2>/dev/null | grep mem0-export || echo "No export files found"
|
||||
```
|
||||
|
||||
If multiple files are found, ask the user which one to import. If none are found, print:
|
||||
```
|
||||
No mem0-export files found in the current directory.
|
||||
Run /mem0:export first, or provide the filename: /mem0:import <path-to-file>
|
||||
```
|
||||
|
||||
### Step 2: Parse the export file
|
||||
|
||||
Read the export file directly. It is a JSON file containing a top-level `memories` array. Each element has:
|
||||
- `id` — original memory ID (for reference only; a new ID will be assigned on import)
|
||||
- `type` — metadata type
|
||||
- `confidence` — metadata confidence value
|
||||
- `branch` — metadata branch
|
||||
- `files` — list of associated files
|
||||
- `categories` — list of categories
|
||||
- `content` — the memory text
|
||||
|
||||
Parse the JSON in-memory (do not run any external script). If the file cannot be read or parsed, or if the `memories` array is missing or empty, print:
|
||||
```
|
||||
Failed to parse <filename> or file contains no valid memory blocks.
|
||||
```
|
||||
and stop.
|
||||
|
||||
### Step 3: Resolve identity
|
||||
|
||||
Determine the active identity:
|
||||
- `user_id` from `MEM0_USER_ID` env var, else `$USER`, else `"default"`
|
||||
- `project_id` (used as `app_id`) from `MEM0_PROJECT_ID` env var, or via the project resolver
|
||||
|
||||
### Step 4: Import each memory
|
||||
|
||||
For each record in the parsed JSON array, call `add_memory` (MCP tool) with:
|
||||
|
||||
- `text="<record.content>"`
|
||||
- `user_id=<active_user_id>`
|
||||
- `app_id=<active_project_id>`
|
||||
- `metadata={`
|
||||
- `"type": "<record.type>"` (if non-empty)
|
||||
- `"confidence": "<record.confidence>"` (if non-empty)
|
||||
- `"branch": "<record.branch>"` (if non-empty)
|
||||
- `"files": <record.files>` (the list, if non-empty)
|
||||
- `"source": "import"`
|
||||
- `}`
|
||||
- `infer=False`
|
||||
|
||||
Notes:
|
||||
- Do NOT pass the original `id` — the platform assigns a new ID.
|
||||
- Skip records where `content` is empty.
|
||||
- Continue importing even if individual records fail; track the count of successes.
|
||||
|
||||
### Step 5: Print results
|
||||
|
||||
```
|
||||
Imported <N> memories into project <project_id>
|
||||
```
|
||||
|
||||
Where `<N>` is the number of successfully imported memories.
|
||||
|
||||
If any failed:
|
||||
```
|
||||
Imported <N>/<total> memories into project <project_id> (<failed> failed)
|
||||
```
|
||||
|
||||
## Importing from competing AI tools (`--tools`)
|
||||
|
||||
When invoked with `--tools` (e.g., `/mem0:import --tools`), detect and import
|
||||
from competing AI tool configuration files:
|
||||
|
||||
### Supported tools
|
||||
|
||||
| Tool | File/directory |
|
||||
|------|---------------|
|
||||
| Cursor | `.cursorrules` |
|
||||
| GitHub Copilot | `.github/copilot-instructions.md` |
|
||||
| Cline | `memory-bank/` (directory of `.md` files) |
|
||||
| Continue | `.continue/rules.md` |
|
||||
|
||||
### T1: Detect
|
||||
|
||||
```bash
|
||||
test -f .cursorrules && echo "cursor: .cursorrules"
|
||||
test -f .github/copilot-instructions.md && echo "copilot: .github/copilot-instructions.md"
|
||||
test -d memory-bank/ && echo "cline: memory-bank/"
|
||||
test -f .continue/rules.md && echo "continue: .continue/rules.md"
|
||||
```
|
||||
|
||||
### T2: Ask user
|
||||
|
||||
List found files, ask which to import (numbers, comma-separated, or "all").
|
||||
If none found:
|
||||
```
|
||||
No competing tool configuration files found.
|
||||
Checked: .cursorrules, .github/copilot-instructions.md, memory-bank/, .continue/rules.md
|
||||
```
|
||||
|
||||
### T3: Import
|
||||
|
||||
For each selected tool, read the file(s) directly and split the content into logical
|
||||
chunks to import as individual memories. No external scripts are used — all parsing
|
||||
and importing is done via MCP tools.
|
||||
|
||||
Chunking rules per tool:
|
||||
|
||||
- **cursorrules / copilot / continue**: Read the file as plain text. Split on blank
|
||||
lines or section headers (`#`, `##`). Each non-empty chunk becomes one memory.
|
||||
Skip chunks shorter than 50 characters.
|
||||
|
||||
- **cline (memory-bank/)**: List all `.md` files in the directory. Read each file.
|
||||
Split each file on blank lines or headers. Each non-empty chunk becomes one memory.
|
||||
Skip chunks shorter than 50 characters.
|
||||
|
||||
For each chunk, call `add_memory` (MCP tool) with:
|
||||
- `text="<chunk text>"`
|
||||
- `user_id=<active_user_id>`
|
||||
- `app_id=<active_project_id>`
|
||||
- `metadata={"type": "task_learning", "source": "<tool>-import", "confidence": 0.8}`
|
||||
- `infer=False`
|
||||
|
||||
Where `<tool>` is `cursorrules`, `copilot`, `cline`, or `continue`.
|
||||
|
||||
Notes: chunks longer than 10,000 characters are truncated before import. Safe to
|
||||
re-run — deduplication handles repeated entries.
|
||||
|
||||
### T4: Report
|
||||
|
||||
```
|
||||
Imported <N> memories into <project_id> (cursor: <N>, copilot: <N>)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Importing Claude Code's native MEMORY.md
|
||||
|
||||
When invoked with a path to Claude Code's native `MEMORY.md` file (typically
|
||||
`~/.claude/projects/<proj-key>/memory/MEMORY.md`), or when `on_session_start.sh`
|
||||
detects native auto-memory and the user chooses to import:
|
||||
|
||||
1. Read the file directly. It contains newline-separated memory entries (one fact per
|
||||
line, sometimes with `- ` bullet prefix).
|
||||
2. Split by non-empty lines. Each line becomes one memory.
|
||||
3. Skip lines shorter than 20 characters or lines that are just headers (`#`).
|
||||
4. For each line, call `add_memory` (MCP tool) with:
|
||||
- `text="<line>"`
|
||||
- `user_id=<active_user_id>`
|
||||
- `app_id=<active_project_id>`
|
||||
- `metadata={"type": "task_learning", "source": "memory-md-import", "confidence": 0.8}`
|
||||
- `infer=False`
|
||||
5. Report: `Imported <N> memories from MEMORY.md into project <project_id>`
|
||||
6. Suggest disabling native auto-memory:
|
||||
```
|
||||
To avoid duplicate memory systems, add to ~/.claude/settings.json:
|
||||
"autoMemoryEnabled": false
|
||||
```
|
||||
|
||||
This handles the cold-start gap when a user has been using Claude Code's native
|
||||
memory and switches to mem0.
|
||||
|
||||
## Error Handling
|
||||
|
||||
- If `add_memory` calls fail consistently (e.g. auth error), report the issue and stop early.
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -1,64 +0,0 @@
|
||||
---
|
||||
name: list-projects
|
||||
description: Lists all projects with stored memories for the current user, showing memory counts and last activity dates. Use when checking which projects have memories, comparing memory distribution across repos, or finding a specific project scope.
|
||||
---
|
||||
|
||||
# Mem0 List Projects
|
||||
|
||||
Show all known project scopes for the current user.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Fetch memories to discover app_ids
|
||||
|
||||
There is no dedicated "list projects" API endpoint. Discover projects by fetching
|
||||
the user's memories across all scopes.
|
||||
|
||||
**Important:** A filter with only `user_id` triggers implicit null scoping — it
|
||||
excludes memories that have a non-null `app_id`. Run two queries and merge:
|
||||
|
||||
1. **Null-scoped:** `get_memories` with `filters={"AND": [{"user_id": "<active_user_id>"}]}`, `page_size=200`
|
||||
— catches memories without `app_id`
|
||||
2. **App-scoped:** `get_memories` with `filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": {"exists": true}}]}`, `page_size=200`
|
||||
— catches memories with any `app_id`
|
||||
|
||||
Run both calls in parallel. Merge results, deduplicate by memory `id`.
|
||||
|
||||
If either response indicates more pages, paginate (up to 1000 total).
|
||||
|
||||
### Step 2: Extract distinct projects
|
||||
|
||||
For each memory, determine project by:
|
||||
1. Top-level `app_id` field (preferred)
|
||||
2. `metadata.project_id` (legacy memories)
|
||||
3. `metadata.project` (oldest format)
|
||||
4. `"(unscoped)"` if none found
|
||||
|
||||
Group by resolved project name. For each project, count:
|
||||
- Total memories
|
||||
- Most recent `created_at` date
|
||||
- Top 3 `metadata.type` values by frequency
|
||||
|
||||
### Step 3: Display
|
||||
|
||||
```
|
||||
## mem0 projects
|
||||
|
||||
<app_id_1> <count> memories (last: <date>) ← current
|
||||
<app_id_2> <count> memories (last: <date>)
|
||||
|
||||
<N> projects, <M> total memories
|
||||
```
|
||||
|
||||
Mark current project with `← current`. Sort by memory count descending.
|
||||
|
||||
### Step 4: Empty state
|
||||
|
||||
If zero memories found:
|
||||
```
|
||||
No projects found. Run /mem0:onboard to get started.
|
||||
```
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: context-loader
|
||||
name: mem0-context-loader
|
||||
description: Searches and injects relevant memories into context before starting work on a task. Use when beginning a new task, switching context, or when project history, past decisions, or coding conventions need to be loaded.
|
||||
---
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: dream
|
||||
name: mem0-dream
|
||||
description: Consolidates stored memories by merging duplicates, resolving contradictions, and pruning stale entries. Use when memory count is high, search results feel noisy or repetitive, or periodic cleanup is needed to maintain memory quality.
|
||||
---
|
||||
|
||||
@@ -192,7 +192,7 @@ Dream complete — merged: <N>, pruned: <N>, conflicts resolved: <N>, skipped: <
|
||||
|
||||
## Auto mode
|
||||
|
||||
When invoked with `--auto` (e.g., `/mem0:dream --auto`), run non-interactively:
|
||||
When invoked with `--auto` (e.g., `/mem0-dream --auto`), run non-interactively:
|
||||
|
||||
- **Merges**: applied automatically (no contradiction, both are compatible).
|
||||
- **Prunes**: applied automatically (age/confidence-based, no ambiguity).
|
||||
@@ -219,7 +219,7 @@ In auto mode:
|
||||
- If no match, store the reminder:
|
||||
```python
|
||||
add_memory(
|
||||
text="mem0-dream detected <N> contradiction(s) requiring manual review. Run /mem0:dream to resolve them interactively.",
|
||||
text="mem0-dream detected <N> contradiction(s) requiring manual review. Run /mem0-dream to resolve them interactively.",
|
||||
user_id="<active_user_id>",
|
||||
app_id="<active_project_id>",
|
||||
metadata={"type": "task_learning", "source": "mem0-dream-auto", "branch": "<active_branch>"},
|
||||
@@ -229,8 +229,8 @@ In auto mode:
|
||||
|
||||
## See also
|
||||
|
||||
- `/mem0:forget` — targeted deletion of specific memories (search + confirm + delete)
|
||||
- `/mem0:health --deep` — quick quality scan without applying changes
|
||||
- `/mem0-forget` — targeted deletion of specific memories (search + confirm + delete)
|
||||
- `/mem0-status --deep` — quick quality scan without applying changes
|
||||
|
||||
## Output formatting
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: forget
|
||||
name: mem0-forget
|
||||
description: Deletes memories by search query or memory ID with confirmation before removal. Use when removing outdated decisions, incorrect memories, sensitive data, or cleaning up after experiments. Also handles undo of recent additions.
|
||||
---
|
||||
|
||||
@@ -12,8 +12,8 @@ Delete specific memories from mem0.
|
||||
### Step 1: Parse input
|
||||
|
||||
The user provides either:
|
||||
- A search query: `/mem0:forget auth module decisions`
|
||||
- A memory ID: `/mem0:forget <memory_id>`
|
||||
- A search query: `/mem0-forget auth module decisions`
|
||||
- A memory ID: `/mem0-forget <memory_id>`
|
||||
|
||||
If no argument, ask: "What should I forget? Provide a search query or memory ID."
|
||||
|
||||
@@ -67,7 +67,7 @@ If the user says "undo last N memories" or "undo last write":
|
||||
3. Sort results by creation time descending and show the last N entries (default 1). Ask for confirmation.
|
||||
4. Delete confirmed entries via `delete_memory`.
|
||||
|
||||
If `MEM0_SESSION_ID` is not set or the search returns no results, tell the user: "No recent memory IDs tracked this session. Try `/mem0:tour` to browse recent memories, or `/mem0:forget <search query>` to find specific ones."
|
||||
If `MEM0_SESSION_ID` is not set or the search returns no results, tell the user: "No recent memory IDs tracked this session. Try `/mem0-tour` to browse recent memories, or `/mem0-forget <search query>` to find specific ones."
|
||||
|
||||
## Output formatting
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: pin
|
||||
name: mem0-pin
|
||||
description: Pins or unpins a memory to protect it from pruning during dream consolidation. Use when a memory is critical and must never be removed, such as architecture decisions, security constraints, or immutable team conventions.
|
||||
---
|
||||
|
||||
@@ -29,12 +29,12 @@ Call `get_memory` with the selected memory ID. Store:
|
||||
|
||||
### Step 3: Pin it
|
||||
|
||||
The MCP `update_memory` tool only accepts `memory_id`, `text`, and `source` — it
|
||||
does not accept a `metadata` parameter. To pin, append a pin marker to the text:
|
||||
The `update_memory` tool updates a memory by `id`. To pin durably, append a pin
|
||||
marker to the text so it travels with the memory:
|
||||
|
||||
```python
|
||||
pinned_text = "[PINNED] " + original_text if not original_text.startswith("[PINNED]") else original_text
|
||||
update_memory(memory_id=<selected_id>, text=pinned_text)
|
||||
update_memory(id=<selected_id>, text=pinned_text)
|
||||
```
|
||||
|
||||
**For new memories** (user wants to pin text that isn't stored yet):
|
||||
+2
-2
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: remember
|
||||
name: mem0-remember
|
||||
description: Stores a memory verbatim from user input with appropriate type classification and metadata. Use when the user says remember this, save this, store this, note that, or explicitly asks to record a decision, preference, convention, or learning.
|
||||
---
|
||||
|
||||
@@ -11,7 +11,7 @@ Store a fact or learning directly into mem0.
|
||||
|
||||
### Step 1: Extract the content
|
||||
|
||||
The user provides the content as an argument: `/mem0:remember <text>`
|
||||
The user provides the content as an argument: `/mem0-remember <text>`
|
||||
|
||||
If no text was provided, ask: "What should I remember?"
|
||||
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
name: mem0-scope
|
||||
description: Views or changes the default memory scope (project, session, or global) used when saving and searching memories. Use when the user wants to control whether memories are scoped to this repo, this run, or shared across all their projects.
|
||||
---
|
||||
|
||||
# Mem0 Scope
|
||||
|
||||
View or change the **default memory scope** — the scope the memory tools use when
|
||||
no explicit `scope` is given. The setting persists in `~/.mem0/settings.json`
|
||||
(`default_scope`) and the plugin reads it fresh on each memory operation, so a
|
||||
change takes effect immediately in the current session.
|
||||
|
||||
The three scopes:
|
||||
|
||||
- `project` (default) — this repo only. Filters by `user_id` + `app_id`.
|
||||
- `session` — this run only. Adds `run_id` (the current session) so memories are
|
||||
isolated to this conversation.
|
||||
- `global` — across ALL your projects. Reads use `app_id="*"`; writes drop
|
||||
`app_id` so the memory is user-wide.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Determine intent
|
||||
|
||||
Look at the user's message for a target scope word: `project`, `session`, or
|
||||
`global` (also accept "repo"→project, "run"→session, "all"/"everywhere"→global).
|
||||
|
||||
- No target word present → **View mode** (Step 2).
|
||||
- A target word present → **Change mode** (Step 3).
|
||||
|
||||
### Step 2: View mode — show the current scope
|
||||
|
||||
1. Read the current default scope from settings:
|
||||
|
||||
```bash
|
||||
_S="$HOME/.mem0/settings.json"
|
||||
[ -f "$_S" ] && grep -o '"default_scope"[[:space:]]*:[[:space:]]*"[a-z]*"' "$_S" | grep -o '[a-z]*"$' | tr -d '"' || echo "project"
|
||||
```
|
||||
|
||||
If the command prints nothing, the scope is `project` (the default).
|
||||
|
||||
2. (Optional) Show how many memories live in the current scope by calling
|
||||
`get_memories` with `scope="<current>"`, `page_size=1`, and reading the
|
||||
`count` (or result length) from the response.
|
||||
|
||||
3. Display using the identity the plugin exported (do NOT re-shell git):
|
||||
|
||||
```
|
||||
Mem0 memory scope
|
||||
|
||||
Current default scope: <current>
|
||||
|
||||
project - this repo only (user + app_id) <marker if active>
|
||||
session - this run only (adds run_id) <marker if active>
|
||||
global - all your projects (app_id = *) <marker if active>
|
||||
|
||||
User: ${MEM0_USER_ID}
|
||||
Project: ${MEM0_APP_ID}
|
||||
Session: ${MEM0_SESSION_ID}
|
||||
|
||||
To change: /mem0-scope session (or project / global)
|
||||
```
|
||||
|
||||
Put `[active]` next to the current scope. If you fetched a count in step 2,
|
||||
add a `Memories in scope: <N>` line.
|
||||
|
||||
### Step 3: Change mode — set a new scope
|
||||
|
||||
1. Validate the target is one of `project`, `session`, `global`. If not, show the
|
||||
three options and stop.
|
||||
|
||||
2. Read the existing settings so you preserve every other key. Use the Read tool
|
||||
on `~/.mem0/settings.json` (it may not exist yet — treat a missing file as
|
||||
`{}`).
|
||||
|
||||
3. Write the file back with the Write tool, keeping ALL existing keys and only
|
||||
setting `"default_scope"` to the target. Pretty-print with 2-space indent and
|
||||
a trailing newline. Do not drop `global_search`, `dream`, `auto_save`, or any
|
||||
other field that was present.
|
||||
|
||||
Example resulting file (when other keys already existed):
|
||||
|
||||
```json
|
||||
{
|
||||
"auto_save": true,
|
||||
"search_limit": 10,
|
||||
"default_scope": "global"
|
||||
}
|
||||
```
|
||||
|
||||
4. Confirm:
|
||||
|
||||
```
|
||||
Default memory scope changed: <old> -> <new>
|
||||
|
||||
<one line describing the effect — see below>
|
||||
Applies immediately to memory tools in this session.
|
||||
|
||||
To revert: /mem0-scope <old>
|
||||
```
|
||||
|
||||
Effect lines:
|
||||
- project → "New memories and searches are limited to this repo."
|
||||
- session → "New memories and searches are limited to this run (this conversation)."
|
||||
- global → "New memories and searches span all your projects. delete_all_memories still needs an explicit scope=global to delete user-wide."
|
||||
|
||||
### Notes
|
||||
|
||||
- This only changes the **default**. Any memory tool call can still pass an
|
||||
explicit `scope` to override it for that one call.
|
||||
- `delete_all_memories` deliberately ignores the default scope: deleting
|
||||
user-wide always requires an explicit `scope="global"`, so changing the
|
||||
default can never turn a routine cleanup into a cross-project wipe.
|
||||
- `global` scope (this user, all their projects) is distinct from the separate
|
||||
`global_search` setting (all users). Leave `global_search` untouched here.
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
+5
-5
@@ -1,17 +1,17 @@
|
||||
---
|
||||
name: peek
|
||||
name: mem0-search
|
||||
description: Searches memories and displays compact one-liner results, or looks up a specific memory by ID. Use for quick memory lookups, checking if a decision was recorded, resolving [mem0:id] citations, or browsing memories without full category detail.
|
||||
---
|
||||
|
||||
# Mem0 Peek
|
||||
# Mem0 Search
|
||||
|
||||
Quick search with compact output. Lighter than `/mem0:tour`.
|
||||
Quick search with compact output. Lighter than `/mem0-tour`.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Parse query
|
||||
|
||||
The user provides a search query: `/mem0:peek auth middleware`
|
||||
The user provides a search query: `/mem0-search auth middleware`
|
||||
|
||||
If no query provided, ask: "What should I search for?"
|
||||
|
||||
@@ -37,7 +37,7 @@ Run 2 parallel `search_memories` calls:
|
||||
Deduplicate by ID, then show compact results:
|
||||
|
||||
```
|
||||
## mem0 peek: "<query>" (<N> results)
|
||||
## mem0 search: "<query>" (<N> results)
|
||||
|
||||
1. [decision] Auth module uses JWT with RS256 keys (2025-05-15) [mem0:a3f8b2c1]
|
||||
2. [anti_pattern] Don't use symmetric HS256 — leaked in env (2025-05-10) [mem0:7e2d9f4a]
|
||||
+52
-14
@@ -1,9 +1,9 @@
|
||||
---
|
||||
name: health
|
||||
description: Diagnoses mem0 connectivity, API key validity, and memory read/write functionality. Use when memory operations fail, searches return empty, add_memory errors occur, MCP connection drops, or to verify the plugin is working correctly.
|
||||
name: mem0-status
|
||||
description: Diagnoses mem0 connectivity, API key validity, and memory read/write functionality. Use when memory operations fail, searches return empty, add_memory errors occur, or to verify the plugin is working correctly.
|
||||
---
|
||||
|
||||
# Mem0 Health Check
|
||||
# Mem0 Status
|
||||
|
||||
Run a diagnostic check on the mem0 plugin. Useful for troubleshooting.
|
||||
|
||||
@@ -23,21 +23,25 @@ _KEY="${MEM0_API_KEY:-}"
|
||||
|
||||
### Check 2: Identity resolution
|
||||
|
||||
Resolve identity from environment variables set by the plugin's `shell.env` hook:
|
||||
Resolve identity from the `MEM0_*` environment variables set by the plugin's `shell.env` hook. These are the exact values the plugin uses to scope memories, so report them directly. Do NOT re-run `git` here: the plugin already resolved branch and project from git at session start, and re-shelling git can disagree with it — e.g. it prints an empty branch that renders as `(not a git repo)` while the Session check below shows `branch=main`. One source of truth keeps the two lines consistent.
|
||||
|
||||
```bash
|
||||
echo "user_id=${MEM0_USER_ID:-${USER:-}}"
|
||||
echo "user_id=${MEM0_USER_ID:-${USER:-default}}"
|
||||
echo "project_id=${MEM0_APP_ID:-}"
|
||||
echo "branch=$(git branch --show-current 2>/dev/null || echo '')"
|
||||
echo "branch=${MEM0_BRANCH:-main}"
|
||||
_S="$HOME/.mem0/settings.json"
|
||||
_SCOPE="$(grep -o '"default_scope"[[:space:]]*:[[:space:]]*"[a-z]*"' "$_S" 2>/dev/null | grep -o '[a-z]*"$' | tr -d '"')"
|
||||
echo "default_scope=${_SCOPE:-project}"
|
||||
```
|
||||
|
||||
- `user_id`: from `MEM0_USER_ID`, falling back to `$USER`
|
||||
- `project_id`: from `MEM0_APP_ID`
|
||||
- `branch`: from `git branch --show-current`
|
||||
- `branch`: from `MEM0_BRANCH` (the plugin's resolved value; falls back to `main` outside a git repo)
|
||||
- `default_scope`: from `~/.mem0/settings.json` (`default_scope`), falling back to `project`. This is the scope memory tools use when none is given; change it with `/mem0-scope`.
|
||||
|
||||
PASS if all three are non-empty. WARN if any falls back to defaults.
|
||||
PASS if `user_id` and `project_id` are non-empty. WARN if `project_id` is empty — the `shell.env` hook may not have fired (restart OpenCode). Report the branch verbatim from `MEM0_BRANCH`; never invent a string like `(not a git repo)`.
|
||||
|
||||
### Check 3: MCP server connectivity
|
||||
### Check 3: Memory tool connectivity
|
||||
|
||||
Call `search_memories` with:
|
||||
- `query="health check"`
|
||||
@@ -75,25 +79,59 @@ echo "branch=${MEM0_BRANCH:-}"
|
||||
- If all three are non-empty: PASS — "Session active"
|
||||
- If any are missing: WARN — "Plugin env vars not set; shell.env hook may not have fired"
|
||||
|
||||
### Check 6: Auto-dream readiness
|
||||
|
||||
Explain whether auto-dream (memory consolidation) is eligible to run, and if not, exactly which gate is blocking. Auto-dream runs at most once per session and only when **all** gates pass: time since last consolidation ≥ `minHours`, sessions since ≥ `minSessions`, and project memory count ≥ `minMemories`.
|
||||
|
||||
Read the gate state and thresholds:
|
||||
|
||||
```bash
|
||||
_ST="$HOME/.mem0/mem0-dream-state.json"
|
||||
_SET="$HOME/.mem0/settings.json"
|
||||
echo "sessions_since=$(grep -o '"sessionsSince"[[:space:]]*:[[:space:]]*[0-9]*' "$_ST" 2>/dev/null | grep -o '[0-9]*$' || echo 0)"
|
||||
echo "last_consolidated_ms=$(grep -o '"lastConsolidatedAt"[[:space:]]*:[[:space:]]*[0-9]*' "$_ST" 2>/dev/null | grep -o '[0-9]*$' || echo 0)"
|
||||
echo "min_hours=$(grep -o '"minHours"[[:space:]]*:[[:space:]]*[0-9]*' "$_SET" 2>/dev/null | grep -o '[0-9]*$' || echo 24)"
|
||||
echo "min_sessions=$(grep -o '"minSessions"[[:space:]]*:[[:space:]]*[0-9]*' "$_SET" 2>/dev/null | grep -o '[0-9]*$' || echo 5)"
|
||||
echo "min_memories=$(grep -o '"minMemories"[[:space:]]*:[[:space:]]*[0-9]*' "$_SET" 2>/dev/null | grep -o '[0-9]*$' || echo 20)"
|
||||
echo "now_s=$(date +%s)"
|
||||
echo "dream_env=${MEM0_DREAM:-unset}"
|
||||
```
|
||||
|
||||
For the memory count, reuse the project memory count from Check 3/4 (or call `get_memories` with the project filter, `page_size=1`, and read `count`).
|
||||
|
||||
Compute each gate:
|
||||
- **time**: `hours_since = (now_s - last_consolidated_ms/1000) / 3600`. Passes when `≥ min_hours`. If `last_consolidated_ms` is 0 it has never run → time gate passes.
|
||||
- **sessions**: passes when `sessions_since ≥ min_sessions`.
|
||||
- **memories**: passes when project memory count `≥ min_memories`.
|
||||
|
||||
Report:
|
||||
- If `dream_env` is `false`/`0`/`no`/`off`, or `dream.enabled` is false in settings: WARN — "Auto-dream disabled".
|
||||
- If all three gates pass: PASS — "eligible (runs at next session start)".
|
||||
- Otherwise: WARN — list the blocking gate(s), e.g. `sessions 2/5, memories 3/20`. This is expected, not an error — auto-dream is just waiting. Note the user can run `/mem0-dream` to consolidate now, or lower the thresholds via the `dream` block in `~/.mem0/settings.json`.
|
||||
|
||||
### Display
|
||||
|
||||
```
|
||||
## mem0 health
|
||||
## mem0 status
|
||||
|
||||
PASS API Key m0-dVe...
|
||||
PASS Identity user=kartik, project=mem0, branch=main
|
||||
PASS MCP Connection 142ms
|
||||
PASS Default scope project
|
||||
PASS Memory Tools 142ms
|
||||
PASS Write/Read write + delete OK
|
||||
PASS Session session_id=abc123, app_id=mem0, branch=main
|
||||
WARN Auto-dream waiting — sessions 2/5, memories 3/20 (/mem0-dream to run now)
|
||||
|
||||
All checks passed.
|
||||
```
|
||||
|
||||
The Auto-dream line is informational: WARN here means "waiting on gates", not a failure. Show PASS when eligible, or "disabled" when turned off.
|
||||
|
||||
If any check fails, add a `## Troubleshooting` section with specific fix steps for each failure.
|
||||
|
||||
## Extended mode: Memory Quality Analysis
|
||||
|
||||
When invoked with `--deep` (e.g., `/mem0:health --deep`), run the standard 5 checks above **plus** a memory quality scan.
|
||||
When invoked with `--deep` (e.g., `/mem0-status --deep`), run the standard 6 checks above **plus** a memory quality scan.
|
||||
|
||||
### Quality Check 1: Duplicates
|
||||
|
||||
@@ -150,9 +188,9 @@ Duplicates: <N> · Stale: <N> · Contradictions: <N> · Orphans: <N>
|
||||
```
|
||||
|
||||
If all counts are 0: `Memory quality: clean.`
|
||||
If any non-zero: append `Run /mem0:dream to fix.`
|
||||
If any non-zero: append `Run /mem0-dream to fix.`
|
||||
|
||||
To fix issues found by `--deep`, run `/mem0:dream` for automated consolidation (merges, prunes, conflict resolution).
|
||||
To fix issues found by `--deep`, run `/mem0-dream` for automated consolidation (merges, prunes, conflict resolution).
|
||||
|
||||
## Output formatting
|
||||
|
||||
+5
-5
@@ -1,5 +1,5 @@
|
||||
---
|
||||
name: tour
|
||||
name: mem0-tour
|
||||
description: Browses all stored memories grouped by category with full content display. Use when reviewing all project memories, exploring stored knowledge, onboarding to a project, or getting an overview of captured decisions, conventions, and learnings.
|
||||
---
|
||||
|
||||
@@ -9,8 +9,8 @@ Show the user what mem0 has stored for the current project.
|
||||
|
||||
## Cross-project mode
|
||||
|
||||
When invoked with `--all-projects` (e.g., `/mem0:tour --all-projects` or
|
||||
`/mem0:tour --all-projects auth middleware`), search across ALL projects:
|
||||
When invoked with `--all-projects` (e.g., `/mem0-tour --all-projects` or
|
||||
`/mem0-tour --all-projects auth middleware`), search across ALL projects:
|
||||
|
||||
1. Call `get_memories` with `filters={"AND": [{"user_id": "<active_user_id>"}]}`, `page_size=200` — **no `app_id` filter**.
|
||||
2. If a search query was also provided, run `search_memories` with `query=<query>`,
|
||||
@@ -33,7 +33,7 @@ If `--all-projects` is NOT present, use the standard single-project flow below.
|
||||
|
||||
## Peek mode (compact search)
|
||||
|
||||
When `/mem0:tour` receives a search query argument (e.g., `/mem0:tour auth middleware`)
|
||||
When `/mem0-tour` receives a search query argument (e.g., `/mem0-tour auth middleware`)
|
||||
WITHOUT `--all-projects`, run in **peek mode** — compact one-liner results:
|
||||
|
||||
1. Run 2 parallel `search_memories` calls:
|
||||
@@ -148,7 +148,7 @@ Identity - user: <user_id> project: <project_id> branch: <branch>
|
||||
If zero memories found for this project, print:
|
||||
```
|
||||
No memories stored yet for project <project_id>.
|
||||
Run /mem0-onboard to import project files, or start working - mem0 captures learnings automatically.
|
||||
Start working - mem0 captures learnings automatically, or use /mem0-remember to save something now.
|
||||
```
|
||||
|
||||
## Output formatting
|
||||
@@ -1,189 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but not
|
||||
limited to compiled object code, generated documentation, and
|
||||
conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work.
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to the Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by the Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding any notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
Copyright 2024 Mem0.ai
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,73 +0,0 @@
|
||||
# Mem0 Skill for Claude
|
||||
|
||||
Add persistent memory to any AI application in minutes using [Mem0 Platform](https://app.mem0.ai?utm_source=oss&utm_medium=mem0-plugin-skill-readme).
|
||||
|
||||
## What This Skill Does
|
||||
|
||||
When installed, Claude can:
|
||||
|
||||
- **Set up Mem0** in your Python or TypeScript project
|
||||
- **Integrate memory** into your existing AI app (LangChain, CrewAI, Vercel AI, OpenAI Agents, LangGraph, LlamaIndex, etc.)
|
||||
- **Generate working code** using real API references and tested patterns
|
||||
- **Search live docs** on demand for the latest Mem0 documentation
|
||||
|
||||
## Installation
|
||||
|
||||
This skill is included automatically when you install the Mem0 plugin:
|
||||
|
||||
```
|
||||
/plugin marketplace add mem0ai/mem0
|
||||
/plugin install mem0@mem0-plugins
|
||||
```
|
||||
|
||||
See the [plugin README](../../README.md) for full setup instructions.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- A Mem0 Platform API key ([Get one here](https://app.mem0.ai/dashboard/api-keys?utm_source=oss&utm_medium=mem0-plugin-skill-readme))
|
||||
- Python 3.10+ or Node.js 18+
|
||||
- Set the environment variable:
|
||||
|
||||
```bash
|
||||
export MEM0_API_KEY="m0-your-api-key"
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
After installing, just ask Claude:
|
||||
|
||||
- "Set up mem0 in my project"
|
||||
- "Add memory to my chatbot"
|
||||
- "Help me search user memories with filters"
|
||||
- "Integrate mem0 with my LangChain app"
|
||||
- "Add graph memory to track entity relationships"
|
||||
|
||||
## What's Inside
|
||||
|
||||
```text
|
||||
skills/mem0/
|
||||
├── SKILL.md # Skill definition and instructions
|
||||
├── README.md # This file
|
||||
├── LICENSE # Apache-2.0
|
||||
├── scripts/
|
||||
│ └── mem0_doc_search.py # Search live Mem0 docs on demand
|
||||
└── references/ # Documentation (loaded on demand)
|
||||
├── quickstart.md # Full quickstart (Python, TS, cURL)
|
||||
├── sdk-guide.md # All SDK methods (Python + TypeScript)
|
||||
├── api-reference.md # REST endpoints, filters, memory object
|
||||
├── architecture.md # Processing pipeline, lifecycle, scoping, performance
|
||||
├── features.md # Retrieval, graph, categories, MCP, webhooks, multimodal
|
||||
├── integration-patterns.md # LangChain, CrewAI, Vercel AI, LangGraph, LlamaIndex, etc.
|
||||
└── use-cases.md # 7 real-world patterns with Python + TypeScript code
|
||||
```
|
||||
|
||||
## Links
|
||||
|
||||
- [Mem0 Platform Dashboard](https://app.mem0.ai?utm_source=oss&utm_medium=mem0-plugin-skill-readme)
|
||||
- [Mem0 Documentation](https://docs.mem0.ai)
|
||||
- [Mem0 GitHub](https://github.com/mem0ai/mem0)
|
||||
- [API Reference](https://docs.mem0.ai/api-reference)
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -1,191 +0,0 @@
|
||||
---
|
||||
name: mem0
|
||||
description: Mem0 SDK reference covering Python and TypeScript APIs, memory client methods, configuration, and framework integrations. Use when writing code that calls mem0 APIs, configuring memory providers, or integrating mem0 into an application.
|
||||
license: Apache-2.0
|
||||
metadata:
|
||||
author: mem0ai
|
||||
version: "0.1.1"
|
||||
category: ai-memory
|
||||
tags: "memory, personalization, ai, python, typescript, vector-search"
|
||||
compatibility: Requires Python 3.10+ or Node.js 18+, pip install mem0ai or npm install mem0ai, MEM0_API_KEY env var (Platform), and internet access to api.mem0.ai. Uses Mem0 v3 API.
|
||||
---
|
||||
|
||||
# Mem0 Platform Integration
|
||||
|
||||
> **Skill Graph:** This skill is part of the Mem0 skill graph:
|
||||
> - **mem0** (this skill) -- Platform Client SDK + OSS (Python + TypeScript)
|
||||
> - **[mem0-vercel-ai-sdk](https://github.com/mem0ai/mem0/tree/main/skills/mem0-vercel-ai-sdk)** -- Vercel AI SDK provider
|
||||
|
||||
Mem0 is a managed memory layer for AI applications. It stores, retrieves, and manages user memories via API — no infrastructure to deploy. For self-hosted usage, see the OSS section in the client references below.
|
||||
|
||||
## Step 1: Install and authenticate
|
||||
|
||||
**Python:**
|
||||
```bash
|
||||
pip install mem0ai
|
||||
export MEM0_API_KEY="m0-your-api-key"
|
||||
```
|
||||
|
||||
**TypeScript/JavaScript:**
|
||||
```bash
|
||||
npm install mem0ai
|
||||
export MEM0_API_KEY="m0-your-api-key"
|
||||
```
|
||||
|
||||
Get an API key at: https://app.mem0.ai/dashboard/api-keys?utm_source=oss&utm_medium=mem0-plugin-skill
|
||||
|
||||
> **Don't have a `MEM0_API_KEY`?** Sign up at https://app.mem0.ai and create one from the dashboard. Keys start with `m0-`.
|
||||
|
||||
## Step 2: Initialize the client
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
client = MemoryClient(api_key="m0-xxx")
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
```typescript
|
||||
import MemoryClient from 'mem0ai';
|
||||
const client = new MemoryClient({ apiKey: 'm0-xxx' });
|
||||
```
|
||||
|
||||
For async Python, use `AsyncMemoryClient`.
|
||||
|
||||
## Step 3: Core operations
|
||||
|
||||
Every Mem0 integration follows the same pattern: **retrieve → generate → store**.
|
||||
|
||||
### Add memories
|
||||
```python
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm a vegetarian and allergic to nuts."},
|
||||
{"role": "assistant", "content": "Got it! I'll remember that."}
|
||||
]
|
||||
client.add(messages, user_id="alice")
|
||||
```
|
||||
|
||||
### Search memories
|
||||
```python
|
||||
results = client.search("dietary preferences", filters={"user_id": "alice"})
|
||||
for mem in results.get("results", []):
|
||||
print(mem["memory"])
|
||||
```
|
||||
|
||||
### Get all memories
|
||||
```python
|
||||
all_memories = client.get_all(filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
### Update a memory
|
||||
```python
|
||||
client.update("memory-uuid", text="Updated: vegetarian, nut allergy, prefers organic")
|
||||
```
|
||||
|
||||
### Delete a memory
|
||||
```python
|
||||
client.delete("memory-uuid")
|
||||
client.delete_all(user_id="alice") # delete all for a user
|
||||
```
|
||||
|
||||
## Common integration pattern
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
from openai import OpenAI
|
||||
|
||||
mem0 = MemoryClient()
|
||||
openai = OpenAI()
|
||||
|
||||
def chat(user_input: str, user_id: str) -> str:
|
||||
# 1. Retrieve relevant memories
|
||||
memories = mem0.search(user_input, filters={"user_id": user_id})
|
||||
context = "\n".join([m["memory"] for m in memories.get("results", [])])
|
||||
|
||||
# 2. Generate response with memory context
|
||||
response = openai.chat.completions.create(
|
||||
model="gpt-5-mini",
|
||||
messages=[
|
||||
{"role": "system", "content": f"User context:\n{context}"},
|
||||
{"role": "user", "content": user_input},
|
||||
]
|
||||
)
|
||||
reply = response.choices[0].message.content
|
||||
|
||||
# 3. Store interaction for future context
|
||||
mem0.add(
|
||||
[{"role": "user", "content": user_input}, {"role": "assistant", "content": reply}],
|
||||
user_id=user_id
|
||||
)
|
||||
return reply
|
||||
```
|
||||
|
||||
## Common edge cases
|
||||
|
||||
- **Search returns empty:** v3 processes `add()` asynchronously — returns an event ID immediately. Wait 2-3s before searching. Also verify `user_id` matches exactly (case-sensitive) and use `filters={"user_id": "..."}` syntax.
|
||||
- **AND filter with user_id + agent_id returns empty:** Entities are stored separately. `{"AND": [{"user_id": "alice"}, {"agent_id": "bot"}]}` returns nothing. Use `OR` instead, or query each separately.
|
||||
- **Duplicate memories:** Don't mix `infer=True` (default) and `infer=False` for the same data. `infer=True` extracts facts via LLM with dedup. `infer=False` stores raw — same text can be stored twice.
|
||||
- **Implicit null scoping:** `filters={"user_id": "alice"}` only returns memories where `agent_id`, `app_id`, `run_id` are ALL null. Wrap in `{"OR": [...]}` to include memories with non-null scoping fields.
|
||||
- **Platform vs OSS imports:** Platform: `from mem0 import MemoryClient`. OSS: `from mem0 import Memory`. Don't mix them — `MemoryClient` talks to `api.mem0.ai`, `Memory` runs locally.
|
||||
- **v3 defaults:** `top_k=20`, `threshold=0.1`, `rerank=False`. Adjust as needed.
|
||||
|
||||
## v3 API (Current)
|
||||
|
||||
Mem0 v3 uses single-pass extraction, entity linking, and multi-signal retrieval.
|
||||
|
||||
**Key v3 changes from v2:**
|
||||
- **Endpoints:** `POST /v3/memories/add/`, `POST /v3/memories/search/`, `POST /v3/memories/` (paginated list)
|
||||
- **Extraction:** Single ADD-only pass — no more UPDATE/DELETE operations during extraction. Memories accumulate rather than consolidate.
|
||||
- **Entity linking:** Replaces graph memory. Auto-extracted during `add()`, no config needed. Remove `enable_graph` and `graph_store` from any old config.
|
||||
- **Defaults:** `top_k=20`, `threshold=0.1`, `rerank=False`
|
||||
- **Removed params:** `org_id`, `project_id`, `enable_graph` — all removed from SDK
|
||||
- **TypeScript:** Exclusively camelCase (`userId`, `agentId`, `appId`, `topK`)
|
||||
- **Add response:** Async — returns event ID immediately, poll via `GET /v1/event/{event_id}/`
|
||||
|
||||
See the [migration guide](https://docs.mem0.ai/migration/platform-v2-to-v3) for details.
|
||||
|
||||
## Live documentation search
|
||||
|
||||
For the latest docs beyond what's in the references, use the doc search tool:
|
||||
|
||||
```bash
|
||||
bun ${CLAUDE_SKILL_DIR}/scripts/mem0_doc_search.ts --query "topic"
|
||||
bun ${CLAUDE_SKILL_DIR}/scripts/mem0_doc_search.ts --page "/platform/features/graph-memory"
|
||||
bun ${CLAUDE_SKILL_DIR}/scripts/mem0_doc_search.ts --index
|
||||
```
|
||||
|
||||
No API key needed — searches docs.mem0.ai directly.
|
||||
|
||||
## Client SDK References
|
||||
|
||||
Language-specific deep references (Platform + OSS):
|
||||
|
||||
| Language | File |
|
||||
|----------|------|
|
||||
| Python (MemoryClient + AsyncMemoryClient + Memory OSS) | [client/python.md](client/python.md) |
|
||||
| TypeScript/Node.js (MemoryClient + Memory OSS) | [client/node.md](client/node.md) |
|
||||
| Python vs TypeScript differences | [client/differences.md](client/differences.md) |
|
||||
|
||||
## Platform References
|
||||
|
||||
Load these on demand for deeper detail:
|
||||
|
||||
| Topic | File |
|
||||
|-------|------|
|
||||
| Quickstart (Python, TS, cURL) | [references/quickstart.md](references/quickstart.md) |
|
||||
| SDK guide (all methods, both languages) | [references/sdk-guide.md](references/sdk-guide.md) |
|
||||
| API reference (endpoints, filters, object schema) | [references/api-reference.md](references/api-reference.md) |
|
||||
| Architecture (pipeline, lifecycle, scoping, performance) | [references/architecture.md](references/architecture.md) |
|
||||
| Platform features (retrieval, graph, categories, MCP, etc.) | [references/features.md](references/features.md) |
|
||||
| Framework integrations (LangChain, CrewAI, OpenAI Agents, etc.) | [references/integration-patterns.md](references/integration-patterns.md) |
|
||||
| Use cases & examples (real-world patterns with code) | [references/use-cases.md](references/use-cases.md) |
|
||||
|
||||
## Related Mem0 Skills
|
||||
|
||||
| Skill | When to use | Link |
|
||||
|-------|-------------|------|
|
||||
| mem0-vercel-ai-sdk | Vercel AI SDK provider with automatic memory | [GitHub](https://github.com/mem0ai/mem0/tree/main/skills/mem0-vercel-ai-sdk) |
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -1,129 +0,0 @@
|
||||
# Python vs TypeScript SDK Differences
|
||||
|
||||
Quick-reference cheatsheet for developers working across both Mem0 SDKs.
|
||||
|
||||
## Constructor
|
||||
|
||||
| Aspect | Python | TypeScript |
|
||||
|--------|--------|------------|
|
||||
| Import (Platform) | `from mem0 import MemoryClient` | `import MemoryClient from 'mem0ai'` |
|
||||
| Import (OSS) | `from mem0 import Memory` | `import { Memory } from 'mem0ai/oss'` |
|
||||
| Constructor | `MemoryClient(api_key="m0-xxx")` | `new MemoryClient({ apiKey: 'm0-xxx' })` |
|
||||
| Required param | `api_key` (positional or kwarg) | `apiKey` (in options object) |
|
||||
|
||||
Both read from `MEM0_API_KEY` env var if no key provided.
|
||||
|
||||
## Method Naming
|
||||
|
||||
| Operation | Python | TypeScript |
|
||||
|-----------|--------|------------|
|
||||
| Add | `add()` | `add()` |
|
||||
| Search | `search()` | `search()` |
|
||||
| Get | `get()` | `get()` |
|
||||
| Get all | `get_all()` | `getAll()` |
|
||||
| Update | `update()` | `update()` |
|
||||
| Delete | `delete()` | `delete()` |
|
||||
| Delete all | `delete_all()` | `deleteAll()` |
|
||||
| History | `history()` | `history()` |
|
||||
| Batch update | `batch_update()` | `batchUpdate()` |
|
||||
| Batch delete | `batch_delete()` | `batchDelete()` |
|
||||
| List users | `users()` | `users()` |
|
||||
| Delete users | `delete_users()` | `deleteUsers()` |
|
||||
| Get project | `project.get()` | `getProject()` |
|
||||
| Update project | `project.update()` | `updateProject()` |
|
||||
| Create webhook | `create_webhook()` | `createWebhook()` |
|
||||
| Get webhooks | `get_webhooks()` | `getWebhooks()` |
|
||||
| Update webhook | `update_webhook()` | `updateWebhook()` |
|
||||
| Delete webhook | `delete_webhook()` | `deleteWebhook()` |
|
||||
| Create export | `create_memory_export()` | `createMemoryExport()` |
|
||||
| Get export | `get_memory_export()` | `getMemoryExport()` |
|
||||
| Feedback | `feedback()` | `feedback()` |
|
||||
|
||||
**Rule:** Python uses `snake_case`, TypeScript uses `camelCase` for method names.
|
||||
|
||||
## Parameter Passing
|
||||
|
||||
```python
|
||||
# Python: kwargs
|
||||
client.add(messages, user_id="alice", metadata={"source": "chat"})
|
||||
client.search("query", filters={"user_id": "alice"}, top_k=5, rerank=True)
|
||||
```
|
||||
|
||||
```typescript
|
||||
// TypeScript: options object with camelCase for top-level params, snake_case for filter keys
|
||||
await client.add(messages, { userId: 'alice', metadata: { source: 'chat' } });
|
||||
await client.search('query', { filters: { user_id: 'alice' }, topK: 5, rerank: true });
|
||||
```
|
||||
|
||||
**v3:** Python uses `snake_case` everywhere. TypeScript uses `camelCase` for top-level params (`userId`, `topK`) but `snake_case` for filter keys (`user_id`, `agent_id`).
|
||||
|
||||
## Architectural Differences
|
||||
|
||||
| Aspect | Python | TypeScript |
|
||||
|--------|--------|------------|
|
||||
| HTTP library | httpx | axios |
|
||||
| Default timeout | 300s | 60s |
|
||||
| Sync support | Yes (`MemoryClient`) | No (all async) |
|
||||
| Async support | Yes (`AsyncMemoryClient`) | All methods are async |
|
||||
| Project management | `client.project.*` (separate class) | `client.getProject()` / `client.updateProject()` |
|
||||
| Context manager | `async with AsyncMemoryClient()` | Not supported |
|
||||
|
||||
## Platform Features: Python-only
|
||||
|
||||
These methods exist in Python but not TypeScript:
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `get_summary(filters)` | Get summary of memories |
|
||||
| `reset()` | Delete ALL data (users + memories) |
|
||||
| `project.create(name)` | Create a new project |
|
||||
| `project.delete()` | Delete current project |
|
||||
| `project.get_members()` | List project members |
|
||||
| `project.add_member(email, role)` | Add member to project |
|
||||
| `project.update_member(email, role)` | Change member role |
|
||||
| `project.remove_member(email)` | Remove member |
|
||||
|
||||
## Platform Features: TypeScript-only
|
||||
|
||||
| Method | Description |
|
||||
|--------|-------------|
|
||||
| `deleteUser(data)` | Convenience method for single entity deletion |
|
||||
| `ping()` | Health check endpoint |
|
||||
|
||||
## OSS Config Naming
|
||||
|
||||
| Python config key | TypeScript config key |
|
||||
|-------------------|----------------------|
|
||||
| `vector_store` | `vectorStore` |
|
||||
| `history_db_path` | `historyDbPath` |
|
||||
| `custom_instructions` | `customInstructions` |
|
||||
|
||||
## OSS Scope Parameter Naming
|
||||
|
||||
| Python | TypeScript |
|
||||
|--------|------------|
|
||||
| `user_id="alice"` | `userId: 'alice'` |
|
||||
| `agent_id="bot"` | `agentId: 'bot'` |
|
||||
| `run_id="session"` | `runId: 'session'` |
|
||||
|
||||
## Entity ID Passing (v3)
|
||||
|
||||
| Method | Python | TypeScript |
|
||||
|--------|--------|------------|
|
||||
| add() | Top-level: `user_id="alice"` | Top-level: `{ userId: 'alice' }` |
|
||||
| search() | In filters: `filters={"user_id": "alice"}` | In filters: `{ filters: { user_id: 'alice' } }` |
|
||||
| get_all() | In filters: `filters={"user_id": "alice"}` | In filters: `{ filters: { user_id: 'alice' } }` |
|
||||
|
||||
## Common Gotcha
|
||||
|
||||
When searching/filtering, both Python and TypeScript use `snake_case` for filter keys. TypeScript only uses `camelCase` for top-level method parameters:
|
||||
|
||||
```python
|
||||
# Python - snake_case in filters
|
||||
results = client.search("query", filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
```typescript
|
||||
// TypeScript - snake_case in filters, camelCase for top-level params
|
||||
const results = await client.search('query', { filters: { user_id: 'alice' }, topK: 20 });
|
||||
```
|
||||
@@ -1,418 +0,0 @@
|
||||
# Mem0 Node.js / TypeScript SDK Reference
|
||||
|
||||
Complete reference for the `mem0ai` npm package. Covers both the Platform client (managed API) and the Open Source self-hosted variant.
|
||||
|
||||
---
|
||||
|
||||
## Platform Client
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
npm install mem0ai
|
||||
export MEM0_API_KEY="m0-your-api-key"
|
||||
```
|
||||
|
||||
### MemoryClient
|
||||
|
||||
```typescript
|
||||
import MemoryClient from 'mem0ai';
|
||||
|
||||
const client = new MemoryClient({ apiKey: 'm0-xxx' });
|
||||
```
|
||||
|
||||
**Constructor:** `new MemoryClient({ apiKey })`. If `apiKey` is not provided, reads from `MEM0_API_KEY` environment variable.
|
||||
|
||||
- HTTP library: `axios`
|
||||
- Timeout: 60 seconds
|
||||
- Base URL: `https://api.mem0.ai`
|
||||
- All methods are async (return `Promise`)
|
||||
|
||||
---
|
||||
|
||||
### Memory Methods
|
||||
|
||||
#### add(messages, options?)
|
||||
|
||||
Store new memories from messages.
|
||||
|
||||
```typescript
|
||||
const messages = [
|
||||
{ role: 'user', content: "I'm a vegetarian and allergic to nuts." },
|
||||
{ role: 'assistant', content: "Got it! I'll remember that." },
|
||||
];
|
||||
await client.add(messages, { userId: 'alice' });
|
||||
```
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `messages` | `Message[]` | Array of `{role, content}` objects |
|
||||
| `options.userId` | string | User identifier |
|
||||
| `options.agentId` | string | Agent identifier |
|
||||
| `options.appId` | string | Application identifier |
|
||||
| `options.runId` | string | Session identifier |
|
||||
| `options.metadata` | object | Custom key-value pairs |
|
||||
| `options.infer` | boolean | If false, store raw text (default: true) |
|
||||
|
||||
**Returns:** `Promise<any>` -- list of events
|
||||
|
||||
#### search(query, options?)
|
||||
|
||||
Search memories by semantic similarity.
|
||||
|
||||
```typescript
|
||||
const results = await client.search('dietary preferences', { filters: { user_id: 'alice' }, topK: 20 });
|
||||
for (const mem of results.results) {
|
||||
console.log(mem.memory, mem.score);
|
||||
}
|
||||
```
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `query` | string | Natural language search query |
|
||||
| `options.filters` | object | Filter object with entity IDs (`user_id`, `agent_id`, etc.) and/or `AND`/`OR`/`NOT` conditions |
|
||||
| `options.topK` | number | Number of results (default: 20) |
|
||||
| `options.rerank` | boolean | Enable semantic reranking (default: false) |
|
||||
| `options.threshold` | number | Minimum similarity (default: 0.1) |
|
||||
|
||||
**Returns:** `Promise<SearchResult>` -- `{results: [{id, memory, score, ...}]}`
|
||||
|
||||
#### get(memoryId)
|
||||
|
||||
```typescript
|
||||
const memory = await client.get('ea925981-...');
|
||||
```
|
||||
|
||||
#### getAll(options?)
|
||||
|
||||
Retrieve all memories. Requires at least one entity identifier in filters.
|
||||
|
||||
```typescript
|
||||
const memories = await client.getAll({ filters: { user_id: 'alice' } });
|
||||
// With filters
|
||||
const filtered = await client.getAll({
|
||||
filters: { AND: [{ user_id: 'alice' }, { categories: { contains: 'health' } }] },
|
||||
});
|
||||
```
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `options.filters` | object | Filter object with entity IDs (`user_id`, `agent_id`, etc.) and/or `AND`/`OR`/`NOT` conditions |
|
||||
| `options.page` | number | Page number |
|
||||
| `options.pageSize` | number | Results per page |
|
||||
|
||||
#### update(memoryId, data)
|
||||
|
||||
```typescript
|
||||
await client.update('ea925981-...', { text: 'Updated: vegan since 2024' });
|
||||
await client.update('ea925981-...', { text: 'Updated', metadata: { verified: true } });
|
||||
```
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `memoryId` | string | Memory ID |
|
||||
| `data.text` | string | New content |
|
||||
| `data.metadata` | object | New metadata |
|
||||
| `data.timestamp` | string | New timestamp |
|
||||
|
||||
#### delete(memoryId)
|
||||
|
||||
```typescript
|
||||
await client.delete('ea925981-...');
|
||||
```
|
||||
|
||||
#### deleteAll(options?)
|
||||
|
||||
```typescript
|
||||
await client.deleteAll({ userId: 'alice' });
|
||||
```
|
||||
|
||||
#### history(memoryId)
|
||||
|
||||
```typescript
|
||||
const history = await client.history('ea925981-...');
|
||||
// Returns: [{previousValue, newValue, action, timestamps}]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Batch Methods
|
||||
|
||||
#### batchUpdate(memories)
|
||||
|
||||
```typescript
|
||||
await client.batchUpdate([
|
||||
{ memoryId: 'uuid-1', text: 'Updated text' },
|
||||
{ memoryId: 'uuid-2', text: 'Another update' },
|
||||
]);
|
||||
```
|
||||
|
||||
#### batchDelete(memories)
|
||||
|
||||
```typescript
|
||||
await client.batchDelete(['uuid-1', 'uuid-2', 'uuid-3']);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### User/Entity Management
|
||||
|
||||
#### users()
|
||||
|
||||
```typescript
|
||||
const users = await client.users();
|
||||
// Returns: {results: [{type: "user", name: "alice"}, ...]}
|
||||
```
|
||||
|
||||
#### deleteUser(data) / deleteUsers(data)
|
||||
|
||||
```typescript
|
||||
await client.deleteUser({ userId: 'alice' }); // Single entity
|
||||
await client.deleteUsers({ agentId: 'bot-1' }); // Flexible
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Project Management
|
||||
|
||||
```typescript
|
||||
// Get project config
|
||||
const config = await client.getProject({ fields: ['customCategories'] });
|
||||
|
||||
// Update project settings
|
||||
await client.updateProject({
|
||||
customInstructions: 'Extract dietary preferences and health info',
|
||||
customCategories: [{ health: 'Medical and dietary info' }],
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Webhooks
|
||||
|
||||
```typescript
|
||||
// List
|
||||
const webhooks = await client.getWebhooks({ projectId: 'proj_123' });
|
||||
|
||||
// Create
|
||||
const webhook = await client.createWebhook({
|
||||
url: 'https://your-app.com/webhook',
|
||||
name: 'Memory Logger',
|
||||
projectId: 'proj_123',
|
||||
eventTypes: ['memory_add', 'memory_update'],
|
||||
});
|
||||
|
||||
// Update
|
||||
await client.updateWebhook({
|
||||
webhookId: 'wh_123',
|
||||
name: 'Updated Logger',
|
||||
url: 'https://new-url.com',
|
||||
});
|
||||
|
||||
// Delete
|
||||
await client.deleteWebhook({ webhookId: 'wh_123' });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Feedback
|
||||
|
||||
```typescript
|
||||
await client.feedback({
|
||||
memoryId: 'mem-123',
|
||||
feedback: 'POSITIVE',
|
||||
feedbackReason: 'Accurately captured preference',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Export
|
||||
|
||||
```typescript
|
||||
const exportReq = await client.createMemoryExport({
|
||||
schema: JSON.stringify({ type: 'object', properties: { name: { type: 'string' } } }),
|
||||
filters: { user_id: 'alice' },
|
||||
});
|
||||
|
||||
const result = await client.getMemoryExport({ memoryExportId: exportReq.id });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### TypeScript Types
|
||||
|
||||
Key interfaces from `mem0.types.ts`:
|
||||
|
||||
```typescript
|
||||
interface Message { role: string; content: string; }
|
||||
interface Memory { id: string; memory: string; userId: string; categories: string[]; score?: number; /* ... */ }
|
||||
interface MemoryOptions { userId?: string; agentId?: string; appId?: string; runId?: string; metadata?: object; /* ... */ }
|
||||
interface SearchOptions { filters?: object; topK?: number; rerank?: boolean; threshold?: number; /* ... */ }
|
||||
interface MemoryHistory { id: string; memoryId: string; previousValue: string; newValue: string; action: string; /* ... */ }
|
||||
interface FeedbackPayload { memoryId: string; feedback: string; feedbackReason?: string; }
|
||||
interface WebhookCreatePayload { url: string; name: string; projectId: string; eventTypes: string[]; }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Open Source / Self-Hosted
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
npm install mem0ai
|
||||
```
|
||||
|
||||
### Memory Class
|
||||
|
||||
```typescript
|
||||
import { Memory } from 'mem0ai/oss';
|
||||
|
||||
const m = new Memory(); // Uses default config
|
||||
```
|
||||
|
||||
**Import:** `from 'mem0ai/oss'` (NOT the default export -- that is `MemoryClient` for Platform)
|
||||
|
||||
### Configuration
|
||||
|
||||
```typescript
|
||||
const config = {
|
||||
llm: {
|
||||
provider: 'openai', // openai, groq, anthropic, google, ollama, lmstudio, mistral, azure
|
||||
config: {
|
||||
model: 'gpt-5-mini',
|
||||
apiKey: 'sk-xxx',
|
||||
},
|
||||
},
|
||||
embedder: {
|
||||
provider: 'openai', // openai, ollama, lmstudio, google, azure, langchain, anthropic
|
||||
config: {
|
||||
model: 'text-embedding-3-small',
|
||||
apiKey: 'sk-xxx',
|
||||
},
|
||||
},
|
||||
vectorStore: {
|
||||
provider: 'qdrant', // memory, qdrant, redis, supabase, langchain, azure_ai_search, pgvector
|
||||
config: {
|
||||
collectionName: 'my_memories',
|
||||
host: 'localhost',
|
||||
port: 6333,
|
||||
},
|
||||
},
|
||||
historyDbPath: 'history.db',
|
||||
customInstructions: '...',
|
||||
disableHistory: false,
|
||||
};
|
||||
|
||||
const m = new Memory(config);
|
||||
// Or from dict with validation:
|
||||
const m2 = Memory.fromConfig(config);
|
||||
```
|
||||
|
||||
### Methods
|
||||
|
||||
All methods are async (return `Promise`):
|
||||
|
||||
#### add(messages, config)
|
||||
|
||||
```typescript
|
||||
await m.add('I prefer dark mode', { userId: 'alice' });
|
||||
await m.add([
|
||||
{ role: 'user', content: 'I like hiking' },
|
||||
{ role: 'assistant', content: 'Great outdoor activity!' },
|
||||
], { userId: 'alice' });
|
||||
```
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `messages` | `string \| Message[]` | Content to store |
|
||||
| `config.userId` | string | User identifier (at least one scope required) |
|
||||
| `config.agentId` | string | Agent identifier |
|
||||
| `config.runId` | string | Session identifier |
|
||||
| `config.metadata` | object | Custom key-value pairs |
|
||||
| `config.filters` | object | Additional filters |
|
||||
| `config.infer` | boolean | LLM inference (default: true) |
|
||||
|
||||
**Returns:** `Promise<{results: [...], relations?: [...]}>`
|
||||
|
||||
#### search(query, config)
|
||||
|
||||
```typescript
|
||||
const results = await m.search('dietary preferences', { filters: { user_id: 'alice' }, topK: 5 });
|
||||
```
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `query` | string | Search query |
|
||||
| `config.filters` | object | Filter object with entity IDs (`user_id`, `agent_id`, `run_id`, etc.) |
|
||||
| `config.topK` | number | Max results (default: 20) |
|
||||
|
||||
#### get(memoryId) / getAll(config) / update(memoryId, data) / delete(memoryId) / deleteAll(config) / history(memoryId)
|
||||
|
||||
Same interface patterns. Note: OSS `update` takes a string for data, not an object.
|
||||
|
||||
```typescript
|
||||
await m.update('mem-id', 'new content');
|
||||
```
|
||||
|
||||
#### reset()
|
||||
|
||||
Clear the entire vector store and history.
|
||||
|
||||
```typescript
|
||||
await m.reset();
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Differences: Platform vs OSS
|
||||
|
||||
| Aspect | Platform (`MemoryClient`) | OSS (`Memory`) |
|
||||
|--------|--------------------------|----------------|
|
||||
| **Import** | `import MemoryClient from 'mem0ai'` | `import { Memory } from 'mem0ai/oss'` |
|
||||
| **Auth** | API key required (`MEM0_API_KEY`) | No API key -- config-based |
|
||||
| **Execution** | API calls to `api.mem0.ai` | Local execution |
|
||||
| **Infrastructure** | Fully managed | Self-managed vector DB, embedder, LLM |
|
||||
| **Param style** | Top-level: `camelCase` (`userId`, `topK`), filter keys: `snake_case` (`user_id`) | Top-level: `camelCase` (`userId`, `topK`), filter keys: `snake_case` (`user_id`) |
|
||||
| **Batch ops** | `batchUpdate`, `batchDelete` | Not available |
|
||||
| **Webhooks** | Full CRUD | Not available |
|
||||
| **Export** | `createMemoryExport` | Not available |
|
||||
| **Feedback** | `feedback()` | Not available |
|
||||
| **Project mgmt** | `getProject`, `updateProject` | Not available |
|
||||
| **User listing** | `users()`, `deleteUser()` | Not available |
|
||||
| **History** | Platform-managed | SQLite (configurable) |
|
||||
|
||||
---
|
||||
|
||||
## v2 Compatibility
|
||||
|
||||
If you're using SDK v2.x:
|
||||
|
||||
**Naming Changes:**
|
||||
- Top-level params now use camelCase: `topK`, `rerank` (not `top_k`)
|
||||
- Filter keys use snake_case: `user_id`, `agent_id`
|
||||
- OSS: `limit` renamed to `topK`
|
||||
|
||||
**API Changes:**
|
||||
```typescript
|
||||
// v2 - top-level entity IDs, snake_case
|
||||
await client.search("query", { user_id: "alice", top_k: 20 });
|
||||
|
||||
// v3 - filters object with snake_case keys, camelCase top-level params
|
||||
await client.search("query", { filters: { user_id: "alice" }, topK: 20 });
|
||||
```
|
||||
|
||||
**Default Changes:**
|
||||
| Param | v2 | v3 |
|
||||
|-------|----|----|
|
||||
| `topK` | 100 | 20 |
|
||||
| `threshold` | none | 0.1 |
|
||||
| `rerank` | true | false |
|
||||
|
||||
**Removed:**
|
||||
- `OutputFormat` and `API_VERSION` enums
|
||||
- `organizationId`, `projectId` from constructor
|
||||
- `enableGraph`, `asyncMode`, `outputFormat`, `immutable`, `expirationDate`, `filterMemories`, `batchSize`, `forceAddOnly`, `includes`, `excludes`, `keywordSearch`
|
||||
|
||||
See the [v2 to v3 migration guide](https://docs.mem0.ai/migration/oss-v2-to-v3) for details.
|
||||
@@ -1,487 +0,0 @@
|
||||
# Mem0 Python SDK Reference
|
||||
|
||||
Complete reference for the `mem0ai` Python package. Covers both the Platform client (managed API) and the Open Source self-hosted variant.
|
||||
|
||||
---
|
||||
|
||||
## Platform Client
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
pip install mem0ai
|
||||
export MEM0_API_KEY="m0-your-api-key"
|
||||
```
|
||||
|
||||
### MemoryClient (Synchronous)
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="m0-xxx")
|
||||
```
|
||||
|
||||
**Constructor:** `MemoryClient(api_key=None)`. If `api_key` is not provided, reads from `MEM0_API_KEY` environment variable. Raises `ValueError` if no key found.
|
||||
|
||||
- HTTP library: `httpx`
|
||||
- Timeout: 300 seconds
|
||||
- Base URL: `https://api.mem0.ai`
|
||||
|
||||
### AsyncMemoryClient (Asynchronous)
|
||||
|
||||
```python
|
||||
from mem0 import AsyncMemoryClient
|
||||
|
||||
client = AsyncMemoryClient(api_key="m0-xxx")
|
||||
|
||||
# Or use as context manager
|
||||
async with AsyncMemoryClient(api_key="m0-xxx") as client:
|
||||
results = await client.search("query", filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
Same methods as `MemoryClient`, all `async`/`await`. Supports async context manager.
|
||||
|
||||
---
|
||||
|
||||
### Memory Methods
|
||||
|
||||
#### add(messages, **kwargs)
|
||||
|
||||
Store new memories from messages.
|
||||
|
||||
```python
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm a vegetarian and allergic to nuts."},
|
||||
{"role": "assistant", "content": "Got it! I'll remember that."}
|
||||
]
|
||||
client.add(messages, user_id="alice")
|
||||
```
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `messages` | str \| dict \| list[dict] | required | Message content. Strings auto-convert to user messages |
|
||||
| `user_id` | str | None | User identifier |
|
||||
| `agent_id` | str | None | Agent identifier |
|
||||
| `app_id` | str | None | Application identifier |
|
||||
| `run_id` | str | None | Session/run identifier |
|
||||
| `metadata` | dict | None | Custom key-value pairs |
|
||||
| `infer` | bool | True | If False, store raw text without LLM inference |
|
||||
| `custom_categories` | list | None | Override project categories |
|
||||
| `custom_instructions` | str | None | Override extraction instructions |
|
||||
| `timestamp` | int \| float \| str | None | Custom timestamp (Unix epoch or ISO 8601) |
|
||||
|
||||
**Returns:** `dict` -- list of events: `[{"id": "...", "event": "ADD", "data": {"memory": "..."}}]`
|
||||
|
||||
#### search(query, **kwargs)
|
||||
|
||||
Search memories by semantic similarity.
|
||||
|
||||
```python
|
||||
results = client.search("dietary preferences", filters={"user_id": "alice"})
|
||||
for mem in results.get("results", []):
|
||||
print(mem["memory"], mem["score"])
|
||||
```
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `query` | str | required | Natural language search query |
|
||||
| `filters` | dict | None | Filter object with entity IDs and/or `AND`/`OR`/`NOT` conditions (e.g., `{"user_id": "alice"}`) |
|
||||
| `top_k` | int | 10 | Number of results |
|
||||
| `rerank` | bool | False | Enable deep semantic reranking (+150-200ms) |
|
||||
| `threshold` | float | 0.1 | Minimum similarity score |
|
||||
| `fields` | list | None | Specific fields to return |
|
||||
| `categories` | list | None | Filter by category |
|
||||
|
||||
**Returns:** `dict` -- `{"results": [{id, memory, user_id, categories, score, created_at, ...}]}`
|
||||
|
||||
#### get(memory_id)
|
||||
|
||||
Retrieve a single memory by ID.
|
||||
|
||||
```python
|
||||
memory = client.get(memory_id="ea925981-...")
|
||||
```
|
||||
|
||||
**Returns:** `dict` -- full memory object
|
||||
|
||||
#### get_all(**kwargs)
|
||||
|
||||
Retrieve all memories with optional filtering. Requires at least one entity identifier.
|
||||
|
||||
```python
|
||||
memories = client.get_all(filters={"user_id": "alice"})
|
||||
# With compound filters
|
||||
memories = client.get_all(filters={"AND": [{"user_id": "alice"}, {"categories": {"contains": "health"}}]})
|
||||
```
|
||||
|
||||
| Parameter | Type | Default | Description |
|
||||
|-----------|------|---------|-------------|
|
||||
| `filters` | dict | None | Filter object with entity IDs and/or `AND`/`OR`/`NOT` conditions |
|
||||
| `top_k` | int | None | Limit results |
|
||||
| `page` | int | None | Page number |
|
||||
| `page_size` | int | None | Results per page |
|
||||
|
||||
**Returns:** `dict` -- `{"results": [...]}`
|
||||
|
||||
#### update(memory_id, text=None, metadata=None, timestamp=None)
|
||||
|
||||
Update a memory's content, metadata, or timestamp. At least one parameter required.
|
||||
|
||||
```python
|
||||
client.update("ea925981-...", text="Updated: vegan since 2024")
|
||||
client.update("ea925981-...", metadata={"verified": True})
|
||||
```
|
||||
|
||||
**Returns:** `dict` -- updated memory
|
||||
|
||||
#### delete(memory_id)
|
||||
|
||||
Permanently delete a single memory.
|
||||
|
||||
```python
|
||||
client.delete("ea925981-...")
|
||||
```
|
||||
|
||||
#### delete_all(**kwargs)
|
||||
|
||||
Delete all memories matching filters. Irreversible.
|
||||
|
||||
```python
|
||||
client.delete_all(user_id="alice")
|
||||
```
|
||||
|
||||
#### history(memory_id)
|
||||
|
||||
Get the change history of a memory.
|
||||
|
||||
```python
|
||||
history = client.history("ea925981-...")
|
||||
# Returns: [{previous_value, new_value, action, timestamps}]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Batch Methods
|
||||
|
||||
#### batch_update(memories)
|
||||
|
||||
Update up to 1000 memories in a single request.
|
||||
|
||||
```python
|
||||
client.batch_update([
|
||||
{"memory_id": "uuid-1", "text": "Updated text"},
|
||||
{"memory_id": "uuid-2", "text": "Another update", "metadata": {"verified": True}},
|
||||
])
|
||||
```
|
||||
|
||||
#### batch_delete(memories)
|
||||
|
||||
Delete up to 1000 memories in a single request.
|
||||
|
||||
```python
|
||||
client.batch_delete([
|
||||
{"memory_id": "uuid-1"},
|
||||
{"memory_id": "uuid-2"},
|
||||
])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### User/Entity Management
|
||||
|
||||
#### users()
|
||||
|
||||
List all users, agents, and sessions that have memories.
|
||||
|
||||
```python
|
||||
users = client.users()
|
||||
# Returns: {"results": [{"type": "user", "name": "alice"}, ...]}
|
||||
```
|
||||
|
||||
#### delete_users(user_id=None, agent_id=None, app_id=None, run_id=None)
|
||||
|
||||
Delete a specific entity and all its memories.
|
||||
|
||||
```python
|
||||
client.delete_users(user_id="alice")
|
||||
```
|
||||
|
||||
#### reset()
|
||||
|
||||
Delete ALL users, agents, sessions, and memories. Complete data reset.
|
||||
|
||||
```python
|
||||
client.reset()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Export & Summary
|
||||
|
||||
#### create_memory_export(schema, **kwargs)
|
||||
|
||||
Create a structured export of memories.
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
schema = json.dumps({
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"preferences": {"type": "array", "items": {"type": "string"}},
|
||||
}
|
||||
})
|
||||
export = client.create_memory_export(schema=schema, user_id="alice")
|
||||
```
|
||||
|
||||
#### get_memory_export(**kwargs)
|
||||
|
||||
Retrieve a previously created export.
|
||||
|
||||
```python
|
||||
result = client.get_memory_export(memory_export_id=export["id"])
|
||||
```
|
||||
|
||||
#### get_summary(filters=None)
|
||||
|
||||
Get a summary of memories.
|
||||
|
||||
```python
|
||||
summary = client.get_summary(filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Feedback
|
||||
|
||||
#### feedback(memory_id, feedback=None, feedback_reason=None)
|
||||
|
||||
Provide quality feedback on a memory.
|
||||
|
||||
```python
|
||||
client.feedback(
|
||||
memory_id="mem-123",
|
||||
feedback="POSITIVE", # POSITIVE | NEGATIVE | VERY_NEGATIVE | None (clear)
|
||||
feedback_reason="Accurately captured preference"
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Webhooks
|
||||
|
||||
```python
|
||||
# List
|
||||
webhooks = client.get_webhooks(project_id="proj_123")
|
||||
|
||||
# Create
|
||||
webhook = client.create_webhook(
|
||||
url="https://your-app.com/webhook",
|
||||
name="Memory Logger",
|
||||
project_id="proj_123",
|
||||
event_types=["memory_add", "memory_update"]
|
||||
)
|
||||
|
||||
# Update
|
||||
client.update_webhook(webhook_id=123, name="Updated", url="https://new-url.com")
|
||||
|
||||
# Delete
|
||||
client.delete_webhook(webhook_id=123)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Project Management
|
||||
|
||||
Access via `client.project.*`:
|
||||
|
||||
```python
|
||||
# Get project config
|
||||
config = client.project.get(fields=["custom_categories", "custom_instructions"])
|
||||
|
||||
# Update project settings
|
||||
client.project.update(
|
||||
custom_instructions="Extract dietary preferences and health info",
|
||||
custom_categories=[{"health": "Medical and dietary info"}],
|
||||
multilingual=True,
|
||||
)
|
||||
|
||||
# Create/delete project
|
||||
client.project.create(name="My Project", description="...")
|
||||
client.project.delete()
|
||||
|
||||
# Member management
|
||||
members = client.project.get_members()
|
||||
client.project.add_member(email="user@example.com", role="READER") # READER or OWNER
|
||||
client.project.update_member(email="user@example.com", role="OWNER")
|
||||
client.project.remove_member(email="user@example.com")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Open Source / Self-Hosted
|
||||
|
||||
### Installation
|
||||
|
||||
```bash
|
||||
pip install mem0ai
|
||||
```
|
||||
|
||||
### Memory Class
|
||||
|
||||
```python
|
||||
from mem0 import Memory
|
||||
|
||||
m = Memory() # Uses default config (OpenAI embedder + in-memory vector store)
|
||||
```
|
||||
|
||||
**Import:** `from mem0 import Memory` (NOT `MemoryClient` -- that is the Platform client)
|
||||
|
||||
### Configuration
|
||||
|
||||
```python
|
||||
config = {
|
||||
"llm": {
|
||||
"provider": "openai", # openai, groq, azure, ollama, lmstudio, google, anthropic, mistral
|
||||
"config": {
|
||||
"model": "gpt-5-mini",
|
||||
"api_key": "sk-xxx",
|
||||
}
|
||||
},
|
||||
"embedder": {
|
||||
"provider": "openai", # openai, ollama, azure, lmstudio, google, huggingface
|
||||
"config": {
|
||||
"model": "text-embedding-3-small",
|
||||
"api_key": "sk-xxx",
|
||||
}
|
||||
},
|
||||
"vector_store": {
|
||||
"provider": "qdrant", # faiss, qdrant, pgvector, redis, supabase, azure_ai_search, memory
|
||||
"config": {
|
||||
"collection_name": "my_memories",
|
||||
"host": "localhost",
|
||||
"port": 6333,
|
||||
}
|
||||
},
|
||||
"history_db_path": "history.db", # SQLite path for change history
|
||||
"custom_instructions": "...", # Custom LLM prompt for extraction
|
||||
}
|
||||
|
||||
m = Memory.from_config(config)
|
||||
```
|
||||
|
||||
### Context Manager
|
||||
|
||||
```python
|
||||
with Memory(config) as m:
|
||||
m.add("I prefer dark mode", user_id="alice")
|
||||
results = m.search("preferences", filters={"user_id": "alice"})
|
||||
# SQLite connections released automatically
|
||||
```
|
||||
|
||||
### Methods
|
||||
|
||||
All methods mirror the Platform client but run locally:
|
||||
|
||||
#### add(messages, *, user_id, agent_id, run_id, metadata, infer=True)
|
||||
|
||||
```python
|
||||
m.add("I'm a vegetarian", user_id="alice")
|
||||
m.add([
|
||||
{"role": "user", "content": "I like hiking"},
|
||||
{"role": "assistant", "content": "Great outdoor activity!"}
|
||||
], user_id="alice")
|
||||
```
|
||||
|
||||
At least one of `user_id`, `agent_id`, `run_id` required.
|
||||
|
||||
**Returns:** `{"results": [...], "relations": [...]}`
|
||||
|
||||
#### search(query, *, filters=None, top_k=20, threshold=0.1, rerank=False)
|
||||
|
||||
```python
|
||||
results = m.search("dietary preferences", filters={"user_id": "alice"}, top_k=5)
|
||||
```
|
||||
|
||||
Entity IDs (`user_id`, `agent_id`, `run_id`) must be passed inside the `filters` dict.
|
||||
|
||||
Supports filter operators: `eq`, `ne`, `in`, `nin`, `gt`, `gte`, `lt`, `lte`, `contains`, `not_contains`.
|
||||
|
||||
#### get(memory_id) / get_all(**kwargs) / update(memory_id, data, metadata=None) / delete(memory_id) / delete_all(**kwargs) / history(memory_id)
|
||||
|
||||
Same interface as Platform client.
|
||||
|
||||
#### reset()
|
||||
|
||||
Clear the entire vector store collection and history database. Recreates the vector store.
|
||||
|
||||
```python
|
||||
m.reset()
|
||||
```
|
||||
|
||||
#### close()
|
||||
|
||||
Release SQLite connections. Called automatically when using context manager.
|
||||
|
||||
### AsyncMemory
|
||||
|
||||
```python
|
||||
from mem0 import AsyncMemory
|
||||
|
||||
m = AsyncMemory(config)
|
||||
await m.add("text", user_id="alice")
|
||||
results = await m.search("query", filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Key Differences: Platform vs OSS
|
||||
|
||||
| Aspect | Platform (`MemoryClient`) | OSS (`Memory`) |
|
||||
|--------|--------------------------|----------------|
|
||||
| **Import** | `from mem0 import MemoryClient` | `from mem0 import Memory` |
|
||||
| **Auth** | API key required (`MEM0_API_KEY`) | No API key -- config-based |
|
||||
| **Execution** | API calls to `api.mem0.ai` | Local execution |
|
||||
| **Infrastructure** | Fully managed | Self-managed vector DB, embedder, LLM |
|
||||
| **Entity filtering** | `filters={"user_id": "..."}` | `filters={"user_id": "..."}` |
|
||||
| **Batch ops** | `batch_update`, `batch_delete` | Not available |
|
||||
| **Webhooks** | Full CRUD | Not available |
|
||||
| **Export** | `create_memory_export`, `get_memory_export` | Not available |
|
||||
| **Feedback** | `feedback()` | Not available |
|
||||
| **Project mgmt** | `client.project.*` | Not available |
|
||||
| **User listing** | `users()`, `delete_users()` | Not available |
|
||||
| **Custom prompts** | Via project settings | Direct config (`custom_instructions`) |
|
||||
| **History** | Platform-managed | SQLite (configurable) |
|
||||
| **Async** | `AsyncMemoryClient` | `AsyncMemory` |
|
||||
|
||||
---
|
||||
|
||||
## v2 Compatibility
|
||||
|
||||
If you're using SDK v2.x or the v2 API:
|
||||
|
||||
**API Changes:**
|
||||
- **Entity IDs in search/get_all:** Pass `user_id`, `agent_id` as top-level kwargs instead of inside `filters`
|
||||
```python
|
||||
# v2
|
||||
results = client.search("query", user_id="alice")
|
||||
# v3
|
||||
results = client.search("query", filters={"user_id": "alice"})
|
||||
```
|
||||
- **add() returns:** v2 returns ADD, UPDATE, DELETE events; v3 returns ADD only
|
||||
|
||||
**Default Changes:**
|
||||
| Param | v2 | v3 |
|
||||
|-------|----|----|
|
||||
| `top_k` | 100 | 20 |
|
||||
| `threshold` | None | 0.1 |
|
||||
| `rerank` | True | False |
|
||||
|
||||
**Removed Parameters:**
|
||||
- Constructor: `org_id`, `project_id`
|
||||
- add(): `async_mode`, `output_format`, `enable_graph`, `immutable`, `expiration_date`, `filter_memories`, `batch_size`, `force_add_only`, `includes`, `excludes`, `keyword_search`
|
||||
- search()/get_all(): `enable_graph`
|
||||
- Config: `enable_graph`, `graph_store`, `custom_fact_extraction_prompt` (renamed to `custom_instructions`)
|
||||
|
||||
See the [v2 to v3 migration guide](https://docs.mem0.ai/migration/oss-v2-to-v3) for full details.
|
||||
-150
@@ -1,150 +0,0 @@
|
||||
# Mem0 Platform API Reference
|
||||
|
||||
REST API endpoints for the Mem0 Platform. Base URL: `https://api.mem0.ai`
|
||||
|
||||
All endpoints require: `Authorization: Token <MEM0_API_KEY>`
|
||||
|
||||
## Endpoints
|
||||
|
||||
| Operation | Method | URL |
|
||||
|-----------|--------|-----|
|
||||
| Add Memories | `POST` | `/v3/memories/add/` |
|
||||
| Search Memories | `POST` | `/v3/memories/search/` |
|
||||
| Get All Memories | `POST` | `/v3/memories/` |
|
||||
| Get Single Memory | `GET` | `/v1/memories/{memory_id}/` |
|
||||
| Update Memory | `PUT` | `/v1/memories/{memory_id}/` |
|
||||
| Delete Memory | `DELETE` | `/v1/memories/{memory_id}/` |
|
||||
| Delete All Memories | `DELETE` | `/v1/memories/?user_id=X&app_id=Y` |
|
||||
| Get Event Status | `GET` | `/v1/event/{event_id}/` |
|
||||
|
||||
## Memory Object Structure
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | string (UUID) | Unique memory identifier |
|
||||
| `memory` | string | Text content of the memory |
|
||||
| `user_id` | string | Associated user |
|
||||
| `agent_id` | string (nullable) | Agent identifier |
|
||||
| `app_id` | string (nullable) | Application identifier |
|
||||
| `run_id` | string (nullable) | Run/session identifier |
|
||||
| `metadata` | object | Custom key-value pairs |
|
||||
| `categories` | array of strings | Auto-assigned category tags |
|
||||
| `hash` | string | Content hash |
|
||||
| `created_at` | datetime | Creation timestamp |
|
||||
| `updated_at` | datetime | Last modification timestamp |
|
||||
|
||||
Search results additionally include `score` (relevance metric).
|
||||
|
||||
## Scoping Identifiers
|
||||
|
||||
Memories can be scoped to different levels:
|
||||
|
||||
| Scope | Parameter | Use Case |
|
||||
|-------|-----------|----------|
|
||||
| User | `user_id` | Per-user memory isolation |
|
||||
| Agent | `agent_id` | Per-agent memory partitioning |
|
||||
| Application | `app_id` | Cross-agent app-level memory |
|
||||
| Run/Session | `run_id` | Session-scoped temporary memory |
|
||||
|
||||
**Critical:** Combining `user_id` and `agent_id` in a single AND filter yields empty results. Entities are stored separately. Use `OR` logic or separate queries.
|
||||
|
||||
## Processing Model
|
||||
|
||||
- Memories are processed **asynchronously** (v3 default)
|
||||
- Add responses return queued `ADD` events only (v3 is ADD-only, no UPDATE/DELETE)
|
||||
- Poll status via `GET /v1/event/{event_id}/`
|
||||
|
||||
## Filter System
|
||||
|
||||
Filters use nested JSON with a logical operator at the root:
|
||||
|
||||
```json
|
||||
{
|
||||
"AND": [
|
||||
{"user_id": "alice"},
|
||||
{"categories": {"contains": "finance"}},
|
||||
{"created_at": {"gte": "2024-01-01"}}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Root must be `AND`, `OR`, or `NOT`. Simple shorthand `{"user_id": "alice"}` also works.
|
||||
|
||||
### Supported Operators
|
||||
|
||||
| Operator | Description |
|
||||
|----------|-------------|
|
||||
| `eq` | Equal to (default) |
|
||||
| `ne` | Not equal to |
|
||||
| `in` | Matches any value in array |
|
||||
| `gt`, `gte` | Greater than / greater than or equal |
|
||||
| `lt`, `lte` | Less than / less than or equal |
|
||||
| `contains` | Case-sensitive containment |
|
||||
| `icontains` | Case-insensitive containment |
|
||||
| `*` | Wildcard -- matches any non-null value |
|
||||
|
||||
### Filterable Fields
|
||||
|
||||
| Field | Valid Operators |
|
||||
|-------|-----------------|
|
||||
| `user_id`, `agent_id`, `app_id`, `run_id` | `eq`, `ne`, `in`, `*` |
|
||||
| `created_at`, `updated_at`, `timestamp` | `gt`, `gte`, `lt`, `lte`, `eq`, `ne` |
|
||||
| `categories` | `eq`, `ne`, `in`, `contains` |
|
||||
| `metadata` | `eq`, `ne`, `contains` (top-level keys only) |
|
||||
| `keywords` | `contains`, `icontains` |
|
||||
| `memory_ids` | `in` |
|
||||
|
||||
### Filter Constraints
|
||||
|
||||
1. **Entity scope partitioning:** `user_id` AND `agent_id` in one `AND` block yields empty results.
|
||||
2. **Metadata limitations:** Only top-level keys. Only `eq`, `contains`, `ne`. No `in` or `gt`.
|
||||
3. **Operator syntax:** Use `gte`, `lt`, `ne`. SQL-style (`>=`, `!=`) rejected.
|
||||
4. **Entity filter required for get-all:** At least one of `user_id`, `agent_id`, `app_id`, or `run_id`.
|
||||
5. **Wildcard excludes null:** `*` matches only non-null values.
|
||||
6. **Date format:** ISO 8601 (`YYYY-MM-DDTHH:MM:SSZ`). Timezone-naive defaults to UTC.
|
||||
|
||||
## Response Formats
|
||||
|
||||
### Add Response (v3)
|
||||
|
||||
```json
|
||||
{
|
||||
"message": "Memory processing has been queued for background execution",
|
||||
"status": "PENDING",
|
||||
"event_id": "evt-uuid"
|
||||
}
|
||||
```
|
||||
|
||||
v3 is ADD-only. No UPDATE or DELETE events.
|
||||
|
||||
### Search Response
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"id": "ea925981-...",
|
||||
"memory": "Is a vegetarian and allergic to nuts.",
|
||||
"user_id": "user123",
|
||||
"categories": ["food", "health"],
|
||||
"score": 0.89,
|
||||
"created_at": "2024-07-26T10:29:36.630547-07:00"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
In v3, `score` is a combined multi-signal relevance score.
|
||||
|
||||
### Get All Response (v3)
|
||||
|
||||
```json
|
||||
{
|
||||
"count": 123,
|
||||
"next": "https://api.mem0.ai/v3/memories/?page=2&page_size=50",
|
||||
"previous": null,
|
||||
"results": [...]
|
||||
}
|
||||
```
|
||||
|
||||
v3 returns paginated envelope. Use `page` and `page_size` query params.
|
||||
-330
@@ -1,330 +0,0 @@
|
||||
# Mem0 Platform Architecture
|
||||
|
||||
How Mem0 processes, stores, and retrieves memories under the hood.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Core Concept](#core-concept)
|
||||
- [Memory Processing Pipeline](#memory-processing-pipeline)
|
||||
- [Retrieval Pipeline](#retrieval-pipeline)
|
||||
- [Memory Lifecycle](#memory-lifecycle)
|
||||
- [Memory Object Structure](#memory-object-structure)
|
||||
- [Scoping & Multi-Tenancy](#scoping--multi-tenancy)
|
||||
- [Memory Layers](#memory-layers)
|
||||
- [Performance Characteristics](#performance-characteristics)
|
||||
|
||||
---
|
||||
|
||||
## Core Concept
|
||||
|
||||
Mem0 is a managed memory layer that sits between your AI application and users. Every integration follows the same 3-step loop:
|
||||
|
||||
```
|
||||
User Input → Retrieve relevant memories → Enrich LLM prompt → Generate response → Store new memories
|
||||
```
|
||||
|
||||
Mem0 handles the complexity of extraction, deduplication, conflict resolution, and semantic retrieval so your application only needs to call `search()` and `add()`.
|
||||
|
||||
**Storage architecture:**
|
||||
- **Vector store**: Embeddings for semantic similarity search
|
||||
- **Entity store**: Automatic entity linking for relationship-aware retrieval
|
||||
|
||||
---
|
||||
|
||||
## Memory Processing Pipeline
|
||||
|
||||
### What happens when you call `client.add()`
|
||||
|
||||
```
|
||||
Messages In
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ 1. EXTRACTION │ Single LLM call extracts all distinct new facts
|
||||
│ (infer=True) │ If infer=False, stores raw text as-is
|
||||
└─────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ 2. DEDUPLICATION │ Hash-based dedup (MD5 prevents exact duplicates)
|
||||
│ │ No UPDATE/DELETE - v3 is ADD-only
|
||||
└─────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ 3. STORAGE │ Batch embed → vector store
|
||||
│ │ Entity extraction → entity store
|
||||
└─────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
Memory Object
|
||||
```
|
||||
|
||||
### Processing (v3)
|
||||
|
||||
v3 processes memories asynchronously by default:
|
||||
- API returns immediately: `{"status": "PENDING", "event_id": "evt-..."}`
|
||||
- Poll status via `GET /v1/event/{event_id}/`
|
||||
- Use webhooks for completion notifications
|
||||
|
||||
### Extraction modes
|
||||
|
||||
**Inferred (`infer=True`, default):**
|
||||
- LLM extracts structured facts from conversation
|
||||
- Conflict resolution deduplicates and resolves contradictions
|
||||
- Best for: natural conversation → memory
|
||||
|
||||
**Raw (`infer=False`):**
|
||||
- Stores text exactly as provided, no LLM processing
|
||||
- Skips conflict resolution — same fact can be stored twice
|
||||
- Only `user` role messages are stored; `assistant` messages ignored
|
||||
- Best for: bulk imports, pre-structured data, migrations
|
||||
|
||||
**Warning:** Don't mix `infer=True` and `infer=False` for the same data — the same fact will be stored twice.
|
||||
|
||||
---
|
||||
|
||||
## Retrieval Pipeline (v3)
|
||||
|
||||
### What happens when you call `client.search()`
|
||||
|
||||
```
|
||||
Query In
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ 1. PREPROCESSING │ Lemmatize keywords, extract entities
|
||||
└─────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ 2. PARALLEL SCORING │ Semantic search (vector similarity)
|
||||
│ │ BM25 keyword search (term matching)
|
||||
│ │ Entity matching (entity graph boost)
|
||||
└─────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────────┐
|
||||
│ 3. SCORE FUSION │ Combine signals into single score
|
||||
│ │ Optional: rerank=True for deep reordering
|
||||
└─────────┬───────────┘
|
||||
│
|
||||
▼
|
||||
Results (combined score per memory)
|
||||
```
|
||||
|
||||
### v3 Search Defaults
|
||||
|
||||
| Parameter | Default | Notes |
|
||||
|-----------|---------|-------|
|
||||
| `top_k` | 20 | Was 100 in v2 |
|
||||
| `threshold` | 0.1 | Was None in v2 |
|
||||
| `rerank` | False | Was True in v2 |
|
||||
|
||||
### Implicit null scoping
|
||||
|
||||
When you search with `filters={"user_id": "alice"}` only, Mem0 returns memories where `agent_id`, `app_id`, and `run_id` are all null. This prevents cross-scope leakage by default.
|
||||
|
||||
To include memories with non-null fields, use explicit filters:
|
||||
```python
|
||||
# Gets memories for alice regardless of agent/app/run
|
||||
filters={"OR": [{"user_id": "alice"}]}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory Lifecycle (v3)
|
||||
|
||||
v3 uses ADD-only extraction. Memories accumulate over time rather than being consolidated.
|
||||
|
||||
### Creation
|
||||
- `client.add(messages, user_id="...")`
|
||||
- Single-pass extraction → deduplication → storage
|
||||
- Returns `{"event_id": "...", "status": "PENDING"}`
|
||||
|
||||
### Updates
|
||||
- `client.update(memory_id, text="...")` replaces text
|
||||
- Batch: `client.batch_update([...])`
|
||||
|
||||
### Deletion
|
||||
- Single: `client.delete(memory_id)`
|
||||
- Batch: `client.batch_delete([...])`
|
||||
- Bulk: `client.delete_all(filters={"user_id": "alice"})`
|
||||
|
||||
---
|
||||
|
||||
## Memory Object Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "uuid-string",
|
||||
"memory": "Extracted memory text",
|
||||
"user_id": "user-identifier",
|
||||
"agent_id": null,
|
||||
"app_id": null,
|
||||
"run_id": null,
|
||||
"metadata": { "source": "chat", "priority": "high" },
|
||||
"categories": ["health", "preferences"],
|
||||
"created_at": "2025-03-12T12:34:56Z",
|
||||
"updated_at": "2025-03-12T12:34:56Z",
|
||||
"structured_attributes": {
|
||||
"day": 12, "month": 3, "year": 2025,
|
||||
"hour": 12, "minute": 34,
|
||||
"day_of_week": "wednesday",
|
||||
"is_weekend": false,
|
||||
"quarter": 1, "week_of_year": 11
|
||||
},
|
||||
"score": 0.85
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `id` | UUID | Unique identifier, used for update/delete |
|
||||
| `memory` | string | Extracted or stored text content |
|
||||
| `user_id` | string | Primary entity scope |
|
||||
| `agent_id` | string | Agent scope |
|
||||
| `app_id` | string | Application scope |
|
||||
| `run_id` | string | Session/run scope |
|
||||
| `metadata` | object | Custom key-value pairs for filtering |
|
||||
| `categories` | array | Auto-assigned or custom category tags |
|
||||
| `created_at` | datetime | Creation timestamp |
|
||||
| `updated_at` | datetime | Last modification timestamp |
|
||||
| `structured_attributes` | object | Temporal breakdown for time-based queries |
|
||||
| `score` | float | Semantic similarity (search results only, 0-1) |
|
||||
|
||||
---
|
||||
|
||||
## Scoping & Multi-Tenancy
|
||||
|
||||
Mem0 separates memories across four dimensions to prevent data mixing:
|
||||
|
||||
| Dimension | Field | Purpose | Example |
|
||||
|-----------|-------|---------|---------|
|
||||
| User | `user_id` | Persistent persona or account | `"customer_6412"` |
|
||||
| Agent | `agent_id` | Distinct agent or tool | `"meal_planner"` |
|
||||
| App | `app_id` | Product surface or deployment | `"ios_retail_app"` |
|
||||
| Session | `run_id` | Short-lived flow or thread | `"ticket-9241"` |
|
||||
|
||||
### Storage model
|
||||
|
||||
Each entity combination creates separate records. A memory with `user_id="alice"` is stored separately from one with `user_id="alice"` + `agent_id="bot"`.
|
||||
|
||||
### Critical: cross-entity queries
|
||||
|
||||
```python
|
||||
# This returns NOTHING — user and agent memories are stored separately
|
||||
filters={"AND": [{"user_id": "alice"}, {"agent_id": "bot"}]}
|
||||
|
||||
# Use OR to query multiple scopes
|
||||
filters={"OR": [{"user_id": "alice"}, {"agent_id": "bot"}]}
|
||||
|
||||
# Use wildcard to include any non-null value
|
||||
filters={"AND": [{"user_id": "*"}]} # All users (excludes null)
|
||||
```
|
||||
|
||||
### Recommended scoping patterns
|
||||
|
||||
```python
|
||||
# User-level: persistent preferences
|
||||
client.add(messages, user_id="alice")
|
||||
|
||||
# Session-level: temporary context
|
||||
client.add(messages, user_id="alice", run_id="session_123")
|
||||
# Clean up when done: client.delete_all(run_id="session_123")
|
||||
|
||||
# Agent-level: agent-specific knowledge
|
||||
client.add(messages, agent_id="support_bot", app_id="helpdesk")
|
||||
|
||||
# Multi-tenant: full isolation
|
||||
client.add(messages, user_id="alice", agent_id="bot", app_id="acme_corp", run_id="ticket_42")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory Layers
|
||||
|
||||
Mem0 supports three layers of memory, from shortest to longest lived:
|
||||
|
||||
### Conversation memory
|
||||
- In-flight messages within a single turn
|
||||
- Tool calls, chain-of-thought reasoning
|
||||
- **Lifetime:** Single response — lost after turn finishes
|
||||
- **Managed by:** Your application, not Mem0
|
||||
|
||||
### Session memory
|
||||
- Short-lived facts for current task or channel
|
||||
- Multi-step flows (onboarding, debugging, support tickets)
|
||||
- **Lifetime:** Minutes to hours
|
||||
- **Managed by:** Mem0 via `run_id` parameter
|
||||
- Clean up with `client.delete_all(run_id="session_id")`
|
||||
|
||||
### User memory
|
||||
- Long-lived knowledge tied to a person or account
|
||||
- Personal preferences, account state, compliance details
|
||||
- **Lifetime:** Weeks to forever
|
||||
- **Managed by:** Mem0 via `user_id` parameter
|
||||
- Persists across all sessions and interactions
|
||||
|
||||
### How layering works in practice
|
||||
|
||||
```python
|
||||
def chat(user_input: str, user_id: str, session_id: str) -> str:
|
||||
# 1. Retrieve user memories (long-term preferences)
|
||||
user_mems = mem0.search(user_input, filters={"user_id": user_id})
|
||||
|
||||
# 2. Retrieve session memories (current task context)
|
||||
session_mems = mem0.search(user_input, filters={
|
||||
"AND": [{"user_id": user_id}, {"run_id": session_id}]
|
||||
})
|
||||
|
||||
# 3. Combine both layers for LLM context
|
||||
context = format_memories(user_mems) + format_memories(session_mems)
|
||||
|
||||
# 4. Generate response
|
||||
response = llm.generate(context=context, input=user_input)
|
||||
|
||||
# 5. Store in session scope (temporary) + user scope (persistent)
|
||||
messages = [{"role": "user", "content": user_input}, {"role": "assistant", "content": response}]
|
||||
mem0.add(messages, user_id=user_id, run_id=session_id)
|
||||
|
||||
return response
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Performance Characteristics
|
||||
|
||||
### Latency
|
||||
|
||||
| Operation | Typical Latency |
|
||||
|-----------|----------------|
|
||||
| Hybrid search (v3 default) | ~100-150ms |
|
||||
| + reranking | +150-200ms |
|
||||
| Add (async) | < 50ms response |
|
||||
|
||||
### Processing
|
||||
|
||||
- **Async (default):** Returns immediately, processes in background
|
||||
- **Batch operations:** Up to 1000 memories per batch_update/batch_delete
|
||||
- **Webhooks:** Real-time notifications when async processing completes
|
||||
|
||||
### Scoping strategy for performance
|
||||
|
||||
- Use `user_id` for all user-facing queries (most common, fastest)
|
||||
- Add `run_id` for session isolation (narrows search space)
|
||||
- Avoid wildcard `"*"` filters on large datasets (scans all non-null records)
|
||||
- Use `top_k` to limit result count when you only need a few memories
|
||||
|
||||
---
|
||||
|
||||
## Comparison with Alternatives
|
||||
|
||||
| Approach | Pros | Cons |
|
||||
|----------|------|------|
|
||||
| **Raw vector DB** | Fast, full control | No extraction, no dedup, no conflict resolution |
|
||||
| **In-memory chat history** | Zero latency | Lost on restart, no cross-session, grows unbounded |
|
||||
| **RAG over documents** | Good for static knowledge | No personalization, no memory updates |
|
||||
| **Mem0 Platform** | Managed extraction + dedup + graph + scoping | External dependency, async processing delay |
|
||||
|
||||
Mem0 combines the best of vector search (semantic retrieval) with automatic extraction (LLM-powered), conflict resolution (deduplication), and structured scoping (multi-tenancy) — in a single managed API.
|
||||
-425
@@ -1,425 +0,0 @@
|
||||
# Platform Features -- Mem0 Platform
|
||||
|
||||
Additional platform capabilities beyond core CRUD operations.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Advanced Retrieval](#advanced-retrieval)
|
||||
- [Entity Linking](#entity-linking)
|
||||
- [Custom Categories](#custom-categories)
|
||||
- [Custom Instructions](#custom-instructions)
|
||||
- [Criteria Retrieval](#criteria-retrieval)
|
||||
- [Feedback Mechanism](#feedback-mechanism)
|
||||
- [Memory Export](#memory-export)
|
||||
- [Group Chat](#group-chat)
|
||||
- [MCP Integration](#mcp-integration)
|
||||
- [Webhooks](#webhooks)
|
||||
- [Multimodal Support](#multimodal-support)
|
||||
|
||||
## Advanced Retrieval
|
||||
|
||||
### Hybrid Search (v3 Default)
|
||||
|
||||
v3 uses multi-signal hybrid search combining:
|
||||
- **Semantic search** (vector similarity)
|
||||
- **BM25 keyword search** (normalized term matching)
|
||||
- **Entity matching** (entity graph boost)
|
||||
|
||||
This is automatic — no configuration needed.
|
||||
|
||||
### Reranking (`rerank=True`)
|
||||
|
||||
Deep semantic reordering of results — most relevant first.
|
||||
|
||||
- Latency: +150-200ms
|
||||
- Default: `False` (was `True` in v2)
|
||||
- Best for: user-facing results, top-N precision
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
results = client.search(query, filters={"user_id": "user123"}, rerank=True)
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
```typescript
|
||||
const results = await client.search(query, {
|
||||
filters: { user_id: 'user123' },
|
||||
rerank: true,
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Entity Linking
|
||||
|
||||
v3 replaces graph memory with built-in entity linking. Entities (proper nouns, quoted text, compound noun phrases) are automatically extracted and linked across memories.
|
||||
|
||||
### How It Works
|
||||
|
||||
1. **Extraction**: During `add()`, entities are automatically extracted from memory text
|
||||
2. **Storage**: Entities are stored in a parallel collection (`{collection}_entities`)
|
||||
3. **Retrieval**: During `search()`, query entities are matched and used to boost relevant memories
|
||||
|
||||
Entity linking is automatic — no configuration required. The boost is folded into the combined `score` on each result.
|
||||
|
||||
### v2 Migration Note
|
||||
|
||||
If you were using `enable_graph=True` in v2:
|
||||
- Remove `enable_graph` from all API calls
|
||||
- Remove `graph_store` from OSS configuration
|
||||
- Entity relationships are now consumed through retrieval ranking, not exposed as a separate `relations` array
|
||||
|
||||
See the [v2 to v3 migration guide](https://docs.mem0.ai/migration/platform-v2-to-v3) for details.
|
||||
|
||||
---
|
||||
|
||||
## Custom Categories
|
||||
|
||||
Replace Mem0's default 15 labels with domain-specific categories. The system automatically tags memories to the closest matching category.
|
||||
|
||||
### Default Categories (15)
|
||||
|
||||
`personal_details`, `family`, `professional_details`, `sports`, `travel`, `food`, `music`, `health`, `technology`, `hobbies`, `fashion`, `entertainment`, `milestones`, `user_preferences`, `misc`
|
||||
|
||||
### Configuration
|
||||
|
||||
**Set project-level categories:**
|
||||
```python
|
||||
new_categories = [
|
||||
{"lifestyle_management": "Tracks daily routines, habits, wellness activities"},
|
||||
{"seeking_structure": "Documents goals around creating routines and systems"},
|
||||
{"personal_information": "Basic information about the user"}
|
||||
]
|
||||
client.project.update(custom_categories=new_categories)
|
||||
```
|
||||
|
||||
```javascript
|
||||
await client.updateProject({ customCategories: newCategories });
|
||||
```
|
||||
|
||||
**Retrieve active categories:**
|
||||
```python
|
||||
categories = client.project.get(fields=["custom_categories"])
|
||||
```
|
||||
|
||||
### Key Constraint
|
||||
|
||||
Per-request overrides (`custom_categories=...` on `client.add`) are **not supported** on the managed API. Only project-level configuration works. Workaround: store ad-hoc labels in `metadata` field.
|
||||
|
||||
---
|
||||
|
||||
## Custom Instructions
|
||||
|
||||
Natural language filters that control what information Mem0 extracts when creating memories.
|
||||
|
||||
### Set Instructions
|
||||
|
||||
```python
|
||||
client.project.update(custom_instructions="Your guidelines here...")
|
||||
```
|
||||
|
||||
```javascript
|
||||
await client.updateProject({ customInstructions: "Your guidelines here..." });
|
||||
```
|
||||
|
||||
### Template Structure
|
||||
|
||||
1. **Task Description** -- brief extraction overview
|
||||
2. **Information Categories** -- numbered sections with specific details to capture
|
||||
3. **Processing Guidelines** -- quality and handling rules
|
||||
4. **Exclusion List** -- sensitive/irrelevant data to filter out
|
||||
|
||||
### Domain Examples
|
||||
|
||||
**E-commerce:** Capture product issues, preferences, service experience; exclude payment data.
|
||||
|
||||
**Education:** Extract learning progress, student preferences, performance patterns; exclude specific grades.
|
||||
|
||||
**Finance:** Track financial goals, life events, investment interests; exclude account numbers and SSNs.
|
||||
|
||||
### Best Practices
|
||||
|
||||
- Start simply, test with sample messages, iterate based on results
|
||||
- Avoid overly lengthy instructions
|
||||
- Be specific about what to include AND exclude
|
||||
|
||||
---
|
||||
|
||||
## Criteria Retrieval
|
||||
|
||||
Custom attribute-based memory ranking using LLM-evaluated criteria with weights. Goes beyond semantic similarity to prioritize memories based on domain-specific signals.
|
||||
|
||||
### Configuration
|
||||
|
||||
```python
|
||||
# Define criteria at project level
|
||||
retrieval_criteria = [
|
||||
{"name": "joy", "description": "Positive emotions like happiness and excitement", "weight": 3},
|
||||
{"name": "curiosity", "description": "Inquisitiveness and desire to learn", "weight": 2},
|
||||
{"name": "urgency", "description": "Time-sensitive or high-priority items", "weight": 4},
|
||||
]
|
||||
client.project.update(retrieval_criteria=retrieval_criteria)
|
||||
```
|
||||
|
||||
```typescript
|
||||
await client.updateProject({
|
||||
retrievalCriteria: [
|
||||
{ name: 'joy', description: 'Positive emotions', weight: 3 },
|
||||
{ name: 'urgency', description: 'Time-sensitive items', weight: 4 },
|
||||
],
|
||||
});
|
||||
```
|
||||
|
||||
### Usage
|
||||
|
||||
Once configured, `client.search()` automatically applies criteria ranking:
|
||||
|
||||
```python
|
||||
# Criteria-weighted results returned automatically
|
||||
results = client.search("Why am I feeling happy?", filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
**Best for:** Wellness assistants, tutoring platforms, productivity tools — any app needing intent-aware retrieval.
|
||||
|
||||
---
|
||||
|
||||
## Feedback Mechanism
|
||||
|
||||
Provide feedback on extracted memories to improve system quality over time.
|
||||
|
||||
### Feedback Types
|
||||
|
||||
| Type | Meaning |
|
||||
|------|---------|
|
||||
| `POSITIVE` | Memory is useful and accurate |
|
||||
| `NEGATIVE` | Memory is not useful |
|
||||
| `VERY_NEGATIVE` | Memory is harmful or completely wrong |
|
||||
| `None` | Clear existing feedback |
|
||||
|
||||
### Usage
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
client.feedback(
|
||||
memory_id="mem-123",
|
||||
feedback="POSITIVE",
|
||||
feedback_reason="Accurately captured dietary preference"
|
||||
)
|
||||
|
||||
# Bulk feedback
|
||||
for item in feedback_data:
|
||||
client.feedback(**item)
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
```typescript
|
||||
await client.feedback('mem-123', {
|
||||
feedback: 'POSITIVE',
|
||||
feedbackReason: 'Accurately captured dietary preference',
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Memory Export
|
||||
|
||||
Create structured exports of memories using customizable schemas with filters.
|
||||
|
||||
### Usage
|
||||
|
||||
```python
|
||||
import json
|
||||
|
||||
# Define export schema
|
||||
schema = {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"name": {"type": "string"},
|
||||
"preferences": {"type": "array", "items": {"type": "string"}},
|
||||
"health_info": {"type": "string"},
|
||||
}
|
||||
}
|
||||
|
||||
# Create export
|
||||
response = client.create_memory_export(
|
||||
schema=json.dumps(schema),
|
||||
filters={"user_id": "alice"},
|
||||
export_instructions="Create comprehensive profile based on all memories"
|
||||
)
|
||||
|
||||
# Retrieve export (may take a moment to process)
|
||||
result = client.get_memory_export(memory_export_id=response["id"])
|
||||
```
|
||||
|
||||
**Best for:** Data analytics, user profile generation, compliance audits, CRM sync.
|
||||
|
||||
---
|
||||
|
||||
## Group Chat
|
||||
|
||||
Process multi-participant conversations and automatically attribute memories to individual speakers.
|
||||
|
||||
### Usage
|
||||
|
||||
```python
|
||||
messages = [
|
||||
{"role": "user", "name": "Alice", "content": "I think we should use React for the frontend"},
|
||||
{"role": "user", "name": "Bob", "content": "I prefer Vue.js, it's simpler for our use case"},
|
||||
{"role": "assistant", "content": "Both are great choices. Let me note your preferences."},
|
||||
]
|
||||
|
||||
# Mem0 automatically attributes memories to each speaker
|
||||
response = client.add(messages, run_id="team_meeting_1")
|
||||
|
||||
# Retrieve Alice's memories from that session
|
||||
alice_mems = client.get_all(
|
||||
filters={"AND": [{"user_id": "alice"}, {"run_id": "team_meeting_1"}]}
|
||||
)
|
||||
```
|
||||
|
||||
Use the `name` field in messages to identify speakers. Mem0 maps names to entity scopes automatically.
|
||||
|
||||
---
|
||||
|
||||
## MCP Integration
|
||||
|
||||
Model Context Protocol integration enables AI clients (Claude, Claude Code, Cursor, Windsurf, VS Code, OpenCode) to manage Mem0 memory autonomously.
|
||||
|
||||
### Setup
|
||||
|
||||
Add Mem0 MCP to your clients with a single command:
|
||||
|
||||
```bash
|
||||
npx mcp-add \
|
||||
--name mem0-mcp \
|
||||
--type http \
|
||||
--url "https://mcp.mem0.ai/mcp" \
|
||||
--clients "claude,claude code,cursor,windsurf,vscode,opencode"
|
||||
```
|
||||
|
||||
### Available MCP Tools
|
||||
|
||||
The MCP server exposes 9 memory tools that AI agents can use autonomously:
|
||||
- Add, search, get, update, delete memories
|
||||
- Get history, list users, delete users
|
||||
- Search Mem0 documentation
|
||||
|
||||
### How It Works
|
||||
|
||||
1. Add Mem0 MCP to your AI client using the setup command above
|
||||
2. The agent autonomously decides when to store/retrieve memories
|
||||
3. No manual API calls needed — the agent manages memory as part of its reasoning
|
||||
|
||||
**Best for:** Universal AI client integration — one protocol works everywhere.
|
||||
|
||||
---
|
||||
|
||||
## Webhooks
|
||||
|
||||
Real-time event notifications for memory operations.
|
||||
|
||||
### Supported Events
|
||||
|
||||
| Event | Trigger |
|
||||
|-------|---------|
|
||||
| `memory_add` | Memory created |
|
||||
| `memory_update` | Memory modified |
|
||||
| `memory_delete` | Memory removed |
|
||||
| `memory_categorize` | Memory tagged |
|
||||
|
||||
### Create Webhook
|
||||
|
||||
Note: `project_id` here refers to the Mem0 dashboard project scope for webhooks — not the deprecated client init parameter.
|
||||
|
||||
```python
|
||||
webhook = client.create_webhook(
|
||||
url="https://your-app.com/webhook",
|
||||
name="Memory Logger",
|
||||
project_id="proj_123",
|
||||
event_types=["memory_add", "memory_categorize"]
|
||||
)
|
||||
```
|
||||
|
||||
### Manage Webhooks
|
||||
|
||||
```python
|
||||
# Retrieve
|
||||
webhooks = client.get_webhooks(project_id="proj_123")
|
||||
|
||||
# Update
|
||||
client.update_webhook(
|
||||
name="Updated Logger",
|
||||
url="https://your-app.com/new-webhook",
|
||||
event_types=["memory_update", "memory_add"],
|
||||
webhook_id="wh_123"
|
||||
)
|
||||
|
||||
# Delete
|
||||
client.delete_webhook(webhook_id="wh_123")
|
||||
```
|
||||
|
||||
### Payload Structure
|
||||
|
||||
Memory events contain: ID, data object with memory content, event type (`ADD`/`UPDATE`/`DELETE`).
|
||||
Categorization events contain: memory ID, event type (`CATEGORIZE`), assigned category labels.
|
||||
|
||||
---
|
||||
|
||||
## Multimodal Support
|
||||
|
||||
Mem0 can process images and documents alongside text.
|
||||
|
||||
### Supported Media Types
|
||||
|
||||
- Images: JPG, PNG
|
||||
- Documents: MDX, TXT, PDF
|
||||
|
||||
### Image via URL
|
||||
|
||||
```python
|
||||
image_message = {
|
||||
"role": "user",
|
||||
"content": {
|
||||
"type": "image_url",
|
||||
"image_url": {"url": "https://example.com/image.jpg"}
|
||||
}
|
||||
}
|
||||
client.add([image_message], user_id="alice")
|
||||
```
|
||||
|
||||
### Image via Base64
|
||||
|
||||
```python
|
||||
import base64
|
||||
with open("photo.jpg", "rb") as f:
|
||||
base64_image = base64.b64encode(f.read()).decode("utf-8")
|
||||
|
||||
image_message = {
|
||||
"role": "user",
|
||||
"content": {
|
||||
"type": "image_url",
|
||||
"image_url": {"url": f"data:image/jpeg;base64,{base64_image}"}
|
||||
}
|
||||
}
|
||||
client.add([image_message], user_id="alice")
|
||||
```
|
||||
|
||||
### Document (MDX/TXT)
|
||||
|
||||
```python
|
||||
doc_message = {
|
||||
"role": "user",
|
||||
"content": {"type": "mdx_url", "mdx_url": {"url": document_url}}
|
||||
}
|
||||
client.add([doc_message], user_id="alice")
|
||||
```
|
||||
|
||||
### PDF Document
|
||||
|
||||
```python
|
||||
pdf_message = {
|
||||
"role": "user",
|
||||
"content": {"type": "pdf_url", "pdf_url": {"url": pdf_url}}
|
||||
}
|
||||
client.add([pdf_message], user_id="alice")
|
||||
```
|
||||
-395
@@ -1,395 +0,0 @@
|
||||
# Mem0 Integration Patterns
|
||||
|
||||
Working code examples for integrating Mem0 Platform with popular AI frameworks.
|
||||
All examples use `MemoryClient` (Platform API key).
|
||||
|
||||
Code examples are sourced from official Mem0 integration docs at docs.mem0.ai, simplified for quick reference.
|
||||
|
||||
---
|
||||
|
||||
## Common Pattern
|
||||
|
||||
Every integration follows the same 3-step loop:
|
||||
|
||||
1. **Retrieve** -- search relevant memories before generating a response
|
||||
2. **Generate** -- include memories as context in the LLM prompt
|
||||
3. **Store** -- save the interaction back to Mem0 for future use
|
||||
|
||||
---
|
||||
|
||||
## LangChain
|
||||
|
||||
Source: [docs.mem0.ai/integrations/langchain](https://docs.mem0.ai/integrations/langchain)
|
||||
|
||||
```python
|
||||
from langchain_openai import ChatOpenAI
|
||||
from langchain_core.messages import SystemMessage, HumanMessage
|
||||
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
|
||||
from mem0 import MemoryClient
|
||||
|
||||
llm = ChatOpenAI(model="gpt-5-mini")
|
||||
mem0 = MemoryClient()
|
||||
|
||||
prompt = ChatPromptTemplate.from_messages([
|
||||
SystemMessage(content="You are a helpful travel agent AI. Use the provided context to personalize your responses."),
|
||||
MessagesPlaceholder(variable_name="context"),
|
||||
HumanMessage(content="{input}")
|
||||
])
|
||||
|
||||
def retrieve_context(query: str, user_id: str):
|
||||
"""Retrieve relevant memories from Mem0"""
|
||||
memories = mem0.search(query, filters={"user_id": user_id})
|
||||
memory_list = memories['results']
|
||||
serialized = ' '.join([m["memory"] for m in memory_list])
|
||||
return [
|
||||
{"role": "system", "content": f"Relevant information: {serialized}"},
|
||||
{"role": "user", "content": query}
|
||||
]
|
||||
|
||||
def chat_turn(user_input: str, user_id: str) -> str:
|
||||
# 1. Retrieve
|
||||
context = retrieve_context(user_input, user_id)
|
||||
# 2. Generate
|
||||
chain = prompt | llm
|
||||
response = chain.invoke({"context": context, "input": user_input})
|
||||
# 3. Store
|
||||
mem0.add(
|
||||
[{"role": "user", "content": user_input}, {"role": "assistant", "content": response.content}],
|
||||
user_id=user_id
|
||||
)
|
||||
return response.content
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## CrewAI
|
||||
|
||||
Source: [docs.mem0.ai/integrations/crewai](https://docs.mem0.ai/integrations/crewai)
|
||||
|
||||
CrewAI has native Mem0 integration via `memory_config`:
|
||||
|
||||
```python
|
||||
from crewai import Agent, Task, Crew, Process
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient()
|
||||
|
||||
# Store user preferences first
|
||||
messages = [
|
||||
{"role": "user", "content": "I am more of a beach person than a mountain person."},
|
||||
{"role": "assistant", "content": "Noted! I'll recommend beach destinations."},
|
||||
{"role": "user", "content": "I like Airbnb more than hotels."},
|
||||
]
|
||||
client.add(messages, user_id="crew_user_1")
|
||||
|
||||
# Create agent
|
||||
travel_agent = Agent(
|
||||
role="Personalized Travel Planner",
|
||||
goal="Plan personalized travel itineraries",
|
||||
backstory="You are a seasoned travel planner.",
|
||||
memory=True,
|
||||
)
|
||||
|
||||
# Create task
|
||||
task = Task(
|
||||
description="Find places to live, eat, and visit in San Francisco.",
|
||||
expected_output="A detailed list of places to live, eat, and visit.",
|
||||
agent=travel_agent,
|
||||
)
|
||||
|
||||
# Setup crew with Mem0 memory
|
||||
crew = Crew(
|
||||
agents=[travel_agent],
|
||||
tasks=[task],
|
||||
process=Process.sequential,
|
||||
memory=True,
|
||||
memory_config={
|
||||
"provider": "mem0",
|
||||
"config": {"user_id": "crew_user_1"},
|
||||
}
|
||||
)
|
||||
|
||||
result = crew.kickoff()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Vercel AI SDK
|
||||
|
||||
> **Dedicated skill available.** For comprehensive Vercel AI SDK documentation, see the [mem0-vercel-ai-sdk skill](https://github.com/mem0ai/mem0/tree/main/skills/mem0-vercel-ai-sdk).
|
||||
|
||||
Install: `npm install @mem0/vercel-ai-provider`
|
||||
|
||||
Quick example (wrapped model with automatic memory):
|
||||
|
||||
```typescript
|
||||
import { generateText } from "ai";
|
||||
import { createMem0 } from "@mem0/vercel-ai-provider";
|
||||
|
||||
const mem0 = createMem0();
|
||||
const { text } = await generateText({
|
||||
model: mem0("gpt-5-mini", { user_id: "borat" }),
|
||||
prompt: "Suggest me a good car to buy!",
|
||||
});
|
||||
```
|
||||
|
||||
Supported providers: `openai`, `anthropic`, `google`, `groq`, `cohere`
|
||||
|
||||
---
|
||||
|
||||
## OpenAI Agents SDK
|
||||
|
||||
Source: [docs.mem0.ai/integrations/openai-agents-sdk](https://docs.mem0.ai/integrations/openai-agents-sdk)
|
||||
|
||||
```python
|
||||
from agents import Agent, Runner, function_tool
|
||||
from mem0 import MemoryClient
|
||||
|
||||
mem0 = MemoryClient()
|
||||
|
||||
@function_tool
|
||||
def search_memory(query: str, user_id: str) -> str:
|
||||
"""Search through past conversations and memories"""
|
||||
memories = mem0.search(query, filters={"user_id": user_id}, top_k=3)
|
||||
if memories and memories.get('results'):
|
||||
return "\n".join([f"- {mem['memory']}" for mem in memories['results']])
|
||||
return "No relevant memories found."
|
||||
|
||||
@function_tool
|
||||
def save_memory(content: str, user_id: str) -> str:
|
||||
"""Save important information to memory"""
|
||||
mem0.add([{"role": "user", "content": content}], user_id=user_id)
|
||||
return "Information saved to memory."
|
||||
|
||||
agent = Agent(
|
||||
name="Personal Assistant",
|
||||
instructions="""You are a helpful personal assistant with memory capabilities.
|
||||
Use search_memory to recall past conversations.
|
||||
Use save_memory to store important information.""",
|
||||
tools=[search_memory, save_memory],
|
||||
model="gpt-5-mini"
|
||||
)
|
||||
|
||||
result = Runner.run_sync(agent, "I love Italian food and I'm planning a trip to Rome next month")
|
||||
print(result.final_output)
|
||||
```
|
||||
|
||||
### Multi-Agent with Handoffs
|
||||
|
||||
```python
|
||||
from agents import Agent, Runner, function_tool
|
||||
|
||||
travel_agent = Agent(
|
||||
name="Travel Planner",
|
||||
instructions="You are a travel planning specialist. Use search_memory and save_memory tools.",
|
||||
tools=[search_memory, save_memory],
|
||||
model="gpt-5-mini"
|
||||
)
|
||||
|
||||
health_agent = Agent(
|
||||
name="Health Advisor",
|
||||
instructions="You are a health and wellness advisor. Use search_memory and save_memory tools.",
|
||||
tools=[search_memory, save_memory],
|
||||
model="gpt-5-mini"
|
||||
)
|
||||
|
||||
triage_agent = Agent(
|
||||
name="Personal Assistant",
|
||||
instructions="""Route travel questions to Travel Planner, health questions to Health Advisor.""",
|
||||
handoffs=[travel_agent, health_agent],
|
||||
model="gpt-5-mini"
|
||||
)
|
||||
|
||||
result = Runner.run_sync(triage_agent, "Plan a healthy meal for my Italy trip")
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Pipecat (Voice / Real-Time)
|
||||
|
||||
Source: [docs.mem0.ai/integrations/pipecat](https://docs.mem0.ai/integrations/pipecat)
|
||||
|
||||
```python
|
||||
from pipecat.services.mem0 import Mem0MemoryService
|
||||
|
||||
memory = Mem0MemoryService(
|
||||
api_key=os.getenv("MEM0_API_KEY"),
|
||||
user_id="alice",
|
||||
agent_id="voice_bot",
|
||||
params={
|
||||
"search_limit": 10,
|
||||
"search_threshold": 0.1,
|
||||
"system_prompt": "Here are your past memories:",
|
||||
"add_as_system_message": True,
|
||||
}
|
||||
)
|
||||
|
||||
# Use in pipeline
|
||||
pipeline = Pipeline([
|
||||
transport.input(),
|
||||
stt,
|
||||
user_context,
|
||||
memory, # Memory enhances context automatically
|
||||
llm,
|
||||
transport.output(),
|
||||
assistant_context
|
||||
])
|
||||
```
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
## LangGraph
|
||||
|
||||
Source: [docs.mem0.ai/integrations/langgraph](https://docs.mem0.ai/integrations/langgraph)
|
||||
|
||||
State-based agent workflows with memory persistence. Best for complex conversation flows with branching logic.
|
||||
|
||||
```python
|
||||
from typing import Annotated, TypedDict, List
|
||||
from langgraph.graph import StateGraph, START
|
||||
from langgraph.graph.message import add_messages
|
||||
from langchain_openai import ChatOpenAI
|
||||
from mem0 import MemoryClient
|
||||
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
|
||||
|
||||
llm = ChatOpenAI(model="gpt-5-mini")
|
||||
mem0 = MemoryClient()
|
||||
|
||||
class State(TypedDict):
|
||||
messages: Annotated[List[HumanMessage | AIMessage], add_messages]
|
||||
mem0_user_id: str
|
||||
|
||||
def chatbot(state: State):
|
||||
messages = state["messages"]
|
||||
user_id = state["mem0_user_id"]
|
||||
|
||||
# Retrieve relevant memories
|
||||
memories = mem0.search(messages[-1].content, filters={"user_id": user_id})
|
||||
context = "Relevant context:\n"
|
||||
for memory in memories["results"]:
|
||||
context += f"- {memory['memory']}\n"
|
||||
|
||||
system_message = SystemMessage(content=f"""You are a helpful support assistant.
|
||||
{context}""")
|
||||
|
||||
response = llm.invoke([system_message] + messages)
|
||||
|
||||
# Store the interaction
|
||||
mem0.add(
|
||||
[{"role": "user", "content": messages[-1].content},
|
||||
{"role": "assistant", "content": response.content}],
|
||||
user_id=user_id
|
||||
)
|
||||
return {"messages": [response]}
|
||||
|
||||
graph = StateGraph(State)
|
||||
graph.add_node("chatbot", chatbot)
|
||||
graph.add_edge(START, "chatbot")
|
||||
app = graph.compile()
|
||||
|
||||
# Usage
|
||||
result = app.invoke({
|
||||
"messages": [HumanMessage(content="I need help with my order")],
|
||||
"mem0_user_id": "customer_123"
|
||||
})
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## LlamaIndex
|
||||
|
||||
Source: [docs.mem0.ai/integrations/llama-index](https://docs.mem0.ai/integrations/llama-index)
|
||||
|
||||
Install: `pip install llama-index-core llama-index-memory-mem0`
|
||||
|
||||
LlamaIndex has native Mem0 support via `Mem0Memory`. Works with ReAct and FunctionCalling agents.
|
||||
|
||||
```python
|
||||
from llama_index.memory.mem0 import Mem0Memory
|
||||
|
||||
context = {"user_id": "alice", "agent_id": "llama_agent_1"}
|
||||
memory = Mem0Memory.from_client(
|
||||
context=context,
|
||||
search_msg_limit=4, # messages from chat history used for retrieval (default: 5)
|
||||
)
|
||||
|
||||
# Use with LlamaIndex agent
|
||||
from llama_index.core.agent import FunctionCallingAgent
|
||||
from llama_index.llms.openai import OpenAI
|
||||
|
||||
llm = OpenAI(model="gpt-5-mini")
|
||||
agent = FunctionCallingAgent.from_tools(
|
||||
tools=[],
|
||||
llm=llm,
|
||||
memory=memory,
|
||||
verbose=True,
|
||||
)
|
||||
|
||||
response = agent.chat("I prefer vegetarian restaurants")
|
||||
# Memory automatically stores and retrieves context
|
||||
response = agent.chat("What kind of food do I like?")
|
||||
# Agent retrieves the vegetarian preference from Mem0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## AutoGen
|
||||
|
||||
Source: [docs.mem0.ai/integrations/autogen](https://docs.mem0.ai/integrations/autogen)
|
||||
|
||||
Install: `pip install autogen mem0ai`
|
||||
|
||||
Multi-agent conversational systems with memory persistence.
|
||||
|
||||
```python
|
||||
from autogen import ConversableAgent
|
||||
from mem0 import MemoryClient
|
||||
|
||||
memory_client = MemoryClient()
|
||||
USER_ID = "alice"
|
||||
|
||||
agent = ConversableAgent(
|
||||
"chatbot",
|
||||
llm_config={"config_list": [{"model": "gpt-5-mini", "api_key": os.environ["OPENAI_API_KEY"]}]},
|
||||
code_execution_config=False,
|
||||
human_input_mode="NEVER",
|
||||
)
|
||||
|
||||
def get_context_aware_response(question: str) -> str:
|
||||
# Retrieve memories for context
|
||||
relevant_memories = memory_client.search(question, filters={"user_id": USER_ID})
|
||||
context = "\n".join([m["memory"] for m in relevant_memories.get("results", [])])
|
||||
|
||||
prompt = f"""Answer considering previous interactions:
|
||||
Previous context: {context}
|
||||
Question: {question}"""
|
||||
|
||||
reply = agent.generate_reply(messages=[{"content": prompt, "role": "user"}])
|
||||
|
||||
# Store the new interaction
|
||||
memory_client.add(
|
||||
[{"role": "user", "content": question}, {"role": "assistant", "content": reply}],
|
||||
user_id=USER_ID
|
||||
)
|
||||
return reply
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## All Supported Frameworks
|
||||
|
||||
Beyond the examples above, Mem0 integrates with:
|
||||
|
||||
| Framework | Type | Install |
|
||||
|-----------|------|---------|
|
||||
| [Mastra](https://docs.mem0.ai/integrations/mastra) | TS agent framework | `npm install @mastra/mem0` |
|
||||
| [ElevenLabs](https://docs.mem0.ai/integrations/elevenlabs) | Voice AI | `pip install elevenlabs mem0ai` |
|
||||
| [LiveKit](https://docs.mem0.ai/integrations/livekit) | Real-time voice/video | `pip install livekit-agents mem0ai` |
|
||||
| [Camel AI](https://docs.mem0.ai/integrations/camel-ai) | Multi-agent framework | `pip install camel-ai[all] mem0ai` |
|
||||
| [AWS Bedrock](https://docs.mem0.ai/integrations/aws-bedrock) | Cloud LLM provider | `pip install boto3 mem0ai` |
|
||||
| [Dify](https://docs.mem0.ai/integrations/dify) | Low-code AI platform | Plugin-based |
|
||||
| [Google AI ADK](https://docs.mem0.ai/integrations/google-ai-adk) | Google agent framework | `pip install google-adk mem0ai` |
|
||||
|
||||
For the general Python pattern (no framework), see the "Common integration pattern" in [SKILL.md](../SKILL.md).
|
||||
-119
@@ -1,119 +0,0 @@
|
||||
# Mem0 Platform Quickstart
|
||||
|
||||
Get running with Mem0 in 2 minutes. No infrastructure to deploy -- just an API key.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Python 3.10+ or Node.js 18+
|
||||
- A Mem0 Platform API key ([Get one here](https://app.mem0.ai/dashboard/api-keys?utm_source=oss&utm_medium=mem0-plugin-skill-quickstart))
|
||||
|
||||
## Python Setup
|
||||
|
||||
```bash
|
||||
pip install mem0ai
|
||||
export MEM0_API_KEY="m0-your-api-key"
|
||||
```
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient(api_key="your-api-key")
|
||||
|
||||
# Add a memory
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm a vegetarian and allergic to nuts."},
|
||||
{"role": "assistant", "content": "Got it! I'll remember your dietary preferences."}
|
||||
]
|
||||
client.add(messages, user_id="user123")
|
||||
|
||||
# Search memories
|
||||
results = client.search("What are my dietary restrictions?", filters={"user_id": "user123"})
|
||||
print(results)
|
||||
```
|
||||
|
||||
### Async Client
|
||||
|
||||
```python
|
||||
from mem0 import AsyncMemoryClient
|
||||
|
||||
client = AsyncMemoryClient(api_key="your-api-key")
|
||||
|
||||
await client.add(messages, user_id="user123")
|
||||
results = await client.search("query", filters={"user_id": "user123"})
|
||||
```
|
||||
|
||||
## TypeScript / JavaScript Setup
|
||||
|
||||
```bash
|
||||
npm install mem0ai
|
||||
export MEM0_API_KEY="m0-your-api-key"
|
||||
```
|
||||
|
||||
```javascript
|
||||
import MemoryClient from 'mem0ai';
|
||||
|
||||
const client = new MemoryClient({ apiKey: 'your-api-key' });
|
||||
|
||||
// Add a memory
|
||||
const messages = [
|
||||
{"role": "user", "content": "I'm a vegetarian and allergic to nuts."},
|
||||
{"role": "assistant", "content": "Got it! I'll remember your dietary preferences."}
|
||||
];
|
||||
await client.add(messages, { userId: "user123" });
|
||||
|
||||
// Search memories
|
||||
const results = await client.search("What are my dietary restrictions?", {
|
||||
filters: { user_id: "user123" }
|
||||
});
|
||||
console.log(results);
|
||||
```
|
||||
|
||||
## cURL
|
||||
|
||||
```bash
|
||||
export MEM0_API_KEY="m0-your-api-key"
|
||||
|
||||
# Add memory
|
||||
curl -X POST https://api.mem0.ai/v3/memories/add/ \
|
||||
-H "Authorization: Token $MEM0_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"messages": [
|
||||
{"role": "user", "content": "I am a vegetarian and allergic to nuts."},
|
||||
{"role": "assistant", "content": "Got it! I will remember your dietary preferences."}
|
||||
],
|
||||
"user_id": "user123"
|
||||
}'
|
||||
|
||||
# Search memories
|
||||
curl -X POST https://api.mem0.ai/v3/memories/search/ \
|
||||
-H "Authorization: Token $MEM0_API_KEY" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"query": "What are my dietary restrictions?",
|
||||
"filters": {"user_id": "user123"}
|
||||
}'
|
||||
```
|
||||
|
||||
## Sample Response
|
||||
|
||||
```json
|
||||
{
|
||||
"results": [
|
||||
{
|
||||
"id": "14e1b28a-2014-40ad-ac42-69c9ef42193d",
|
||||
"memory": "Allergic to nuts",
|
||||
"user_id": "user123",
|
||||
"categories": ["health"],
|
||||
"created_at": "2025-10-22T04:40:22.864647-07:00",
|
||||
"score": 0.30
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
- [SDK Guide](sdk-guide.md) -- all methods for Python and TypeScript
|
||||
- [API Reference](api-reference.md) -- REST endpoints and memory object structure
|
||||
- [Integration Patterns](integration-patterns.md) -- LangChain, CrewAI, Vercel AI, etc.
|
||||
-353
@@ -1,353 +0,0 @@
|
||||
# Mem0 SDK Guide
|
||||
|
||||
Complete SDK reference for Python and TypeScript. All methods use `MemoryClient` (Platform API).
|
||||
|
||||
> **For language-specific deep references (including OSS):** See [client/python.md](../client/python.md) and [client/node.md](../client/node.md). For Python vs TypeScript differences: [client/differences.md](../client/differences.md).
|
||||
|
||||
## Initialization
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
client = MemoryClient(api_key="m0-your-api-key")
|
||||
```
|
||||
|
||||
**Python (Async):**
|
||||
```python
|
||||
from mem0 import AsyncMemoryClient
|
||||
client = AsyncMemoryClient(api_key="m0-your-api-key")
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
```typescript
|
||||
import MemoryClient from 'mem0ai';
|
||||
const client = new MemoryClient({ apiKey: 'm0-your-api-key' });
|
||||
```
|
||||
|
||||
Constructor accepts `apiKey` (required) and `host` (optional, default: `https://api.mem0.ai`).
|
||||
|
||||
---
|
||||
|
||||
## add() -- Store Memories
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
messages = [
|
||||
{"role": "user", "content": "I'm a vegetarian and allergic to nuts."},
|
||||
{"role": "assistant", "content": "Got it! I'll remember that."}
|
||||
]
|
||||
client.add(messages, user_id="alice")
|
||||
|
||||
# With metadata
|
||||
client.add(messages, user_id="alice", metadata={"source": "onboarding"})
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
```typescript
|
||||
await client.add(messages, { userId: "alice" });
|
||||
await client.add(messages, { userId: "alice", metadata: { source: "onboarding" } });
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `messages` | array | `[{"role": "user", "content": "..."}]` |
|
||||
| `user_id` | string | User identifier (recommended) |
|
||||
| `agent_id` | string | Agent identifier |
|
||||
| `run_id` | string | Session identifier |
|
||||
| `metadata` | object | Custom key-value pairs |
|
||||
| `infer` | boolean | If `false`, store raw text without inference (default: `true`) |
|
||||
|
||||
### Advanced Add Options
|
||||
|
||||
```python
|
||||
# Agent + session scoping
|
||||
client.add(messages, user_id="alice", agent_id="nutrition-agent", run_id="session-456")
|
||||
|
||||
# Raw text -- skip LLM inference
|
||||
client.add(
|
||||
[{"role": "user", "content": "User prefers dark mode."}],
|
||||
user_id="alice",
|
||||
infer=False,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## search() -- Find Memories
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
results = client.search("dietary preferences?", filters={"user_id": "alice"})
|
||||
|
||||
# With filters and reranking
|
||||
results = client.search(
|
||||
query="work experience",
|
||||
filters={"AND": [{"user_id": "alice"}, {"categories": {"contains": "professional_details"}}]},
|
||||
top_k=5,
|
||||
rerank=True,
|
||||
threshold=0.5
|
||||
)
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
```typescript
|
||||
const results = await client.search("dietary preferences", { filters: { user_id: "alice" } });
|
||||
const results = await client.search("work experience", {
|
||||
filters: { AND: [{ user_id: "alice" }, { categories: { contains: "professional_details" } }] },
|
||||
topK: 5,
|
||||
rerank: true,
|
||||
});
|
||||
```
|
||||
|
||||
### Parameters
|
||||
|
||||
| Name | Type | Description |
|
||||
|------|------|-------------|
|
||||
| `query` | string | Natural language search query |
|
||||
| `filters` | object | Filter object (AND/OR operators). Use `{"user_id": "..."}` to filter by user |
|
||||
| `top_k` | number | Number of results (default: 10 for Platform) |
|
||||
| `rerank` | boolean | Enable reranking for better relevance (default: `false`) |
|
||||
| `threshold` | number | Minimum similarity score (default: 0.1) |
|
||||
|
||||
### Common Filter Patterns
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
# Single user filter
|
||||
filters={"user_id": "alice"}
|
||||
|
||||
# OR across agents
|
||||
filters={"OR": [{"user_id": "alice"}, {"agent_id": {"in": ["travel-agent", "sports-agent"]}}]}
|
||||
|
||||
# Category filtering (partial match)
|
||||
filters={"AND": [{"user_id": "alice"}, {"categories": {"contains": "finance"}}]}
|
||||
|
||||
# Category filtering (exact match)
|
||||
filters={"AND": [{"user_id": "alice"}, {"categories": {"in": ["personal_information"]}}]}
|
||||
|
||||
# Wildcard (match any non-null run)
|
||||
filters={"AND": [{"user_id": "alice"}, {"run_id": "*"}]}
|
||||
|
||||
# Date range
|
||||
filters={"AND": [
|
||||
{"user_id": "alice"},
|
||||
{"created_at": {"gte": "2024-01-01T00:00:00Z"}},
|
||||
{"created_at": {"lt": "2024-02-01T00:00:00Z"}}
|
||||
]}
|
||||
|
||||
# Exclude categories with NOT
|
||||
filters={"AND": [{"user_id": "user_123"}, {"NOT": {"categories": {"in": ["spam", "test"]}}}]}
|
||||
|
||||
# Multi-dimensional query
|
||||
filters={"AND": [
|
||||
{"user_id": "user_123"},
|
||||
{"keywords": {"icontains": "invoice"}},
|
||||
{"categories": {"in": ["finance"]}},
|
||||
{"created_at": {"gte": "2024-01-01T00:00:00Z"}}
|
||||
]}
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
```typescript
|
||||
// Single user filter
|
||||
filters: { user_id: "alice" }
|
||||
|
||||
// OR across agents
|
||||
filters: { OR: [{ user_id: "alice" }, { agent_id: { in: ["travel-agent", "sports-agent"] } }] }
|
||||
|
||||
// Category filtering (partial match)
|
||||
filters: { AND: [{ user_id: "alice" }, { categories: { contains: "finance" } }] }
|
||||
|
||||
// Category filtering (exact match)
|
||||
filters: { AND: [{ user_id: "alice" }, { categories: { in: ["personal_information"] } }] }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## get() / getAll() -- Retrieve Memories
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
# Single memory by ID
|
||||
memory = client.get(memory_id="ea925981-...")
|
||||
|
||||
# All memories for a user
|
||||
memories = client.get_all(filters={"user_id": "alice"})
|
||||
|
||||
# With date range
|
||||
memories = client.get_all(
|
||||
filters={"AND": [
|
||||
{"user_id": "alex"},
|
||||
{"created_at": {"gte": "2024-07-01", "lte": "2024-07-31"}}
|
||||
]}
|
||||
)
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
```typescript
|
||||
const memory = await client.get("ea925981-...");
|
||||
const memories = await client.getAll({ filters: { user_id: "alice" } });
|
||||
```
|
||||
|
||||
**Note:** `get_all` requires at least one of `user_id`, `agent_id`, `app_id`, or `run_id` in filters.
|
||||
|
||||
---
|
||||
|
||||
## update() -- Modify Memories
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
client.update(memory_id="ea925981-...", text="Updated: vegan since 2024")
|
||||
client.update(memory_id="ea925981-...", text="Updated", metadata={"verified": True})
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
```typescript
|
||||
await client.update("ea925981-...", { text: "Updated: vegan since 2024" });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## delete() / deleteAll() -- Remove Memories
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
client.delete(memory_id="ea925981-...")
|
||||
client.delete_all(user_id="alice") # Irreversible bulk delete
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
```typescript
|
||||
await client.delete("ea925981-...");
|
||||
await client.deleteAll({ userId: "alice" });
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## history() -- Track Changes
|
||||
|
||||
**Python:**
|
||||
```python
|
||||
history = client.history(memory_id="ea925981-...")
|
||||
# Returns: [{previous_value, new_value, action, timestamps}]
|
||||
```
|
||||
|
||||
**TypeScript:**
|
||||
```typescript
|
||||
const history = await client.history("ea925981-...");
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Batch Operations (TypeScript)
|
||||
|
||||
```typescript
|
||||
// Batch update
|
||||
await client.batchUpdate([
|
||||
{ memoryId: "uuid-1", text: "Updated text" },
|
||||
{ memoryId: "uuid-2", text: "Another updated text" },
|
||||
]);
|
||||
|
||||
// Batch delete
|
||||
await client.batchDelete(["uuid-1", "uuid-2", "uuid-3"]);
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Additional Methods
|
||||
|
||||
```python
|
||||
# List all users/agents/sessions with memories
|
||||
users = client.users()
|
||||
|
||||
# Delete a user/agent entity
|
||||
client.delete_users(user_id="alice")
|
||||
|
||||
# Submit feedback on a memory
|
||||
client.feedback(memory_id="...", feedback="POSITIVE", feedback_reason="Accurate extraction")
|
||||
|
||||
# Export memories
|
||||
export = client.create_memory_export(filters={"AND": [{"user_id": "alice"}]})
|
||||
data = client.get_memory_export(memory_export_id=export["id"])
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Common Pitfalls
|
||||
|
||||
1. **Entity cross-filtering fails silently** -- `AND` with `user_id` + `agent_id` returns empty. Use `OR`.
|
||||
2. **SQL operators rejected** -- use `gte`, `lt`, etc. Not `>=`, `<`.
|
||||
3. **Metadata filtering is limited** -- only top-level keys with `eq`, `contains`, `ne`.
|
||||
4. **Wildcard `*` excludes null** -- only matches non-null values.
|
||||
5. **Default threshold is 0.1** -- increase for stricter matching.
|
||||
6. **Async processing** -- memories process asynchronously. Wait 2-3s after `add()` before searching.
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
Python uses `snake_case` everywhere (`user_id`, `memory_id`, `get_all`). TypeScript uses `camelCase` for methods (`getAll`, `deleteAll`, `batchUpdate`) and top-level parameters (`userId`, `topK`, `pageSize`), but filter keys use `snake_case` (`user_id`, `agent_id`).
|
||||
|
||||
---
|
||||
|
||||
## v2 to v3 Migration
|
||||
|
||||
### Breaking Changes in v3
|
||||
|
||||
**1. Entity IDs in search() and getAll()**
|
||||
|
||||
v3 requires entity IDs (`user_id`, `agent_id`, `run_id`) inside `filters` instead of as top-level parameters:
|
||||
|
||||
```python
|
||||
# v2 (deprecated)
|
||||
client.search("query", user_id="alice")
|
||||
client.get_all(user_id="alice")
|
||||
|
||||
# v3
|
||||
client.search("query", filters={"user_id": "alice"})
|
||||
client.get_all(filters={"user_id": "alice"})
|
||||
```
|
||||
|
||||
```typescript
|
||||
// v2 (deprecated)
|
||||
await client.search("query", { user_id: "alice" });
|
||||
await client.getAll({ user_id: "alice" });
|
||||
|
||||
// v3
|
||||
await client.search("query", { filters: { user_id: "alice" } });
|
||||
await client.getAll({ filters: { user_id: "alice" } });
|
||||
```
|
||||
|
||||
**2. TypeScript Parameter Naming**
|
||||
|
||||
v3 TypeScript uses camelCase for all parameters:
|
||||
|
||||
| v2 | v3 |
|
||||
|----|-----|
|
||||
| `user_id` | `userId` |
|
||||
| `agent_id` | `agentId` |
|
||||
| `run_id` | `runId` |
|
||||
| `top_k` | `topK` |
|
||||
| `page_size` | `pageSize` |
|
||||
|
||||
**3. Default Values Changed**
|
||||
|
||||
| Parameter | v2 Default | v3 Default |
|
||||
|-----------|------------|------------|
|
||||
| `threshold` | 0.3 | 0.1 |
|
||||
| `rerank` | (not specified) | `false` |
|
||||
|
||||
**4. Removed Parameters**
|
||||
|
||||
The following parameters are no longer supported:
|
||||
|
||||
| Parameter | Status |
|
||||
|-----------|--------|
|
||||
| `enable_graph` | Removed from add/search/getAll |
|
||||
| `keyword_search` | Removed from search |
|
||||
| `filter_memories` | Removed |
|
||||
| `immutable` | Removed from add |
|
||||
| `expiration_date` | Removed from add |
|
||||
| `includes` | Removed from add |
|
||||
| `excludes` | Removed from add |
|
||||
| `async_mode` | Removed from add |
|
||||
-720
@@ -1,720 +0,0 @@
|
||||
# Mem0 Use Cases & Examples
|
||||
|
||||
Real-world implementation patterns for Mem0 Platform. Each use case includes complete, runnable code in both Python and TypeScript.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Personalized AI Companion](#1-personalized-ai-companion)
|
||||
- [Customer Support with Categories](#2-customer-support-with-categories)
|
||||
- [Healthcare Coach](#3-healthcare-coach)
|
||||
- [Content Creation Workflow](#4-content-creation-workflow)
|
||||
- [Multi-Agent / Multi-Tenant](#5-multi-agent--multi-tenant)
|
||||
- [Personalized Search](#6-personalized-search)
|
||||
- [Email Intelligence](#7-email-intelligence)
|
||||
- [Common Patterns Across Use Cases](#common-patterns-across-use-cases)
|
||||
|
||||
---
|
||||
|
||||
## 1. Personalized AI Companion
|
||||
|
||||
A fitness coach that remembers goals, preferences, and progress across sessions. Mem0 persists context across app restarts — no session state needed.
|
||||
|
||||
### Implementation (Python)
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
from openai import OpenAI
|
||||
|
||||
mem0 = MemoryClient()
|
||||
openai_client = OpenAI()
|
||||
|
||||
def chat(user_input: str, user_id: str) -> str:
|
||||
# 1. Retrieve relevant memories
|
||||
memories = mem0.search(user_input, user_id=user_id)
|
||||
context = "\n".join([f"- {m['memory']}" for m in memories.get("results", [])])
|
||||
|
||||
# 2. Generate response with memory context
|
||||
system_prompt = f"""You are Ray, a personal fitness coach.
|
||||
Use these known facts about the user to personalize your response:
|
||||
{context if context else 'No prior context yet.'}"""
|
||||
|
||||
response = openai_client.chat.completions.create(
|
||||
model="gpt-5-mini",
|
||||
messages=[
|
||||
{"role": "system", "content": system_prompt},
|
||||
{"role": "user", "content": user_input},
|
||||
]
|
||||
)
|
||||
reply = response.choices[0].message.content
|
||||
|
||||
# 3. Store interaction for future context
|
||||
mem0.add(
|
||||
[{"role": "user", "content": user_input}, {"role": "assistant", "content": reply}],
|
||||
user_id=user_id
|
||||
)
|
||||
return reply
|
||||
|
||||
# Usage
|
||||
chat("I want to run a marathon in under 4 hours", user_id="max")
|
||||
# Next day, app restarted:
|
||||
chat("What should I focus on today?", user_id="max")
|
||||
# Ray remembers the sub-4 marathon goal
|
||||
```
|
||||
|
||||
### Implementation (TypeScript)
|
||||
|
||||
```typescript
|
||||
import MemoryClient from 'mem0ai';
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const mem0 = new MemoryClient({ apiKey: process.env.MEM0_API_KEY! });
|
||||
const openai = new OpenAI();
|
||||
|
||||
async function chat(userInput: string, userId: string): Promise<string> {
|
||||
// 1. Retrieve relevant memories
|
||||
const memories = await mem0.search(userInput, { filters: { user_id: userId } });
|
||||
const context = memories.results
|
||||
?.map((m: any) => `- ${m.memory}`)
|
||||
.join('\n') || 'No prior context yet.';
|
||||
|
||||
// 2. Generate response with memory context
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-5-mini',
|
||||
messages: [
|
||||
{ role: 'system', content: `You are Ray, a personal fitness coach.\nUser context:\n${context}` },
|
||||
{ role: 'user', content: userInput },
|
||||
],
|
||||
});
|
||||
const reply = response.choices[0].message.content!;
|
||||
|
||||
// 3. Store interaction
|
||||
await mem0.add(
|
||||
[{ role: 'user', content: userInput }, { role: 'assistant', content: reply }],
|
||||
{ userId: userId }
|
||||
);
|
||||
return reply;
|
||||
}
|
||||
```
|
||||
|
||||
### Key Benefits
|
||||
|
||||
- Context persists across app restarts — no session management needed
|
||||
- Memories are automatically deduplicated and updated
|
||||
- Works with any LLM provider (OpenAI, Anthropic, etc.)
|
||||
|
||||
**Best for:** Fitness coaches, tutors, therapists — any assistant that needs to remember goals across sessions.
|
||||
|
||||
---
|
||||
|
||||
## 2. Customer Support with Categories
|
||||
|
||||
Auto-categorize support data so teams retrieve the right facts fast. Uses custom categories for structured retrieval.
|
||||
|
||||
### Implementation (Python)
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient()
|
||||
|
||||
# 1. Define categories at the project level (one-time setup)
|
||||
custom_categories = [
|
||||
{"support_tickets": "Customer issues and resolutions"},
|
||||
{"account_info": "Account details and preferences"},
|
||||
{"billing": "Payment history and billing questions"},
|
||||
{"product_feedback": "Feature requests and feedback"},
|
||||
]
|
||||
client.project.update(custom_categories=custom_categories)
|
||||
|
||||
# 2. Store interactions — auto-classified into categories
|
||||
def log_support_interaction(user_id: str, message: str, priority: str = "normal"):
|
||||
client.add(
|
||||
[{"role": "user", "content": message}],
|
||||
user_id=user_id,
|
||||
metadata={"priority": priority, "source": "support_chat"}
|
||||
)
|
||||
|
||||
# 3. Retrieve by category
|
||||
def get_billing_issues(user_id: str):
|
||||
return client.get_all(
|
||||
filters={
|
||||
"AND": [
|
||||
{"user_id": user_id},
|
||||
{"categories": {"in": ["billing"]}}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
def search_support_history(user_id: str, query: str):
|
||||
return client.search(
|
||||
query,
|
||||
filters={
|
||||
"AND": [
|
||||
{"user_id": user_id},
|
||||
{"categories": {"contains": "support_tickets"}}
|
||||
]
|
||||
},
|
||||
top_k=5
|
||||
)
|
||||
|
||||
# Usage
|
||||
log_support_interaction("maria", "I was charged twice for last month's subscription", priority="high")
|
||||
log_support_interaction("maria", "The dashboard is loading slowly on mobile")
|
||||
billing = get_billing_issues("maria") # Returns only billing-related memories
|
||||
```
|
||||
|
||||
### Implementation (TypeScript)
|
||||
|
||||
```typescript
|
||||
import MemoryClient from 'mem0ai';
|
||||
|
||||
const client = new MemoryClient({ apiKey: process.env.MEM0_API_KEY! });
|
||||
|
||||
// Setup categories (one-time)
|
||||
await client.updateProject({
|
||||
custom_categories: [
|
||||
{ support_tickets: 'Customer issues and resolutions' },
|
||||
{ billing: 'Payment history and billing questions' },
|
||||
{ product_feedback: 'Feature requests and feedback' },
|
||||
],
|
||||
});
|
||||
|
||||
async function logInteraction(userId: string, message: string, priority = 'normal') {
|
||||
await client.add(
|
||||
[{ role: 'user', content: message }],
|
||||
{ userId: userId, metadata: { priority, source: 'support_chat' } }
|
||||
);
|
||||
}
|
||||
|
||||
async function getBillingIssues(userId: string) {
|
||||
return client.getAll({
|
||||
filters: { AND: [{ user_id: userId }, { categories: { in: ['billing'] } }] },
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Key Benefits
|
||||
|
||||
- Automatic categorization — no manual tagging
|
||||
- Filter by category for structured retrieval
|
||||
- Metadata (`priority`, `source`) enables multi-dimensional queries
|
||||
|
||||
**Best for:** Help desks, SaaS support, e-commerce — structured retrieval by category eliminates manual scanning.
|
||||
|
||||
---
|
||||
|
||||
## 3. Healthcare Coach
|
||||
|
||||
Guide patients with an assistant that remembers medical history. Uses high `threshold` for confident retrieval in safety-critical contexts.
|
||||
|
||||
### Implementation (Python)
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
from openai import OpenAI
|
||||
|
||||
mem0 = MemoryClient()
|
||||
openai_client = OpenAI()
|
||||
|
||||
def save_patient_info(user_id: str, information: str):
|
||||
mem0.add(
|
||||
[{"role": "user", "content": information}],
|
||||
user_id=user_id,
|
||||
run_id="healthcare_session",
|
||||
metadata={"type": "patient_information"}
|
||||
)
|
||||
|
||||
def consult(user_id: str, question: str) -> str:
|
||||
# High threshold for medical accuracy
|
||||
memories = mem0.search(question, user_id=user_id, top_k=5, threshold=0.7)
|
||||
context = "\n".join([f"- {m['memory']}" for m in memories.get("results", [])])
|
||||
|
||||
response = openai_client.chat.completions.create(
|
||||
model="gpt-5-mini",
|
||||
messages=[
|
||||
{"role": "system", "content": f"You are a health coach. Patient context:\n{context}"},
|
||||
{"role": "user", "content": question},
|
||||
]
|
||||
)
|
||||
reply = response.choices[0].message.content
|
||||
|
||||
# Store the interaction
|
||||
mem0.add(
|
||||
[{"role": "user", "content": question}, {"role": "assistant", "content": reply}],
|
||||
user_id=user_id,
|
||||
run_id="healthcare_session",
|
||||
)
|
||||
return reply
|
||||
|
||||
# Usage
|
||||
save_patient_info("alex", "I'm allergic to penicillin and take metformin for type 2 diabetes")
|
||||
consult("alex", "Can I take amoxicillin for my sore throat?")
|
||||
# Remembers penicillin allergy — amoxicillin is a penicillin-type antibiotic
|
||||
```
|
||||
|
||||
### Implementation (TypeScript)
|
||||
|
||||
```typescript
|
||||
import MemoryClient from 'mem0ai';
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const mem0 = new MemoryClient({ apiKey: process.env.MEM0_API_KEY! });
|
||||
const openai = new OpenAI();
|
||||
|
||||
async function savePatientInfo(userId: string, info: string) {
|
||||
await mem0.add(
|
||||
[{ role: 'user', content: info }],
|
||||
{ userId: userId, runId: 'healthcare_session', metadata: { type: 'patient_information' } }
|
||||
);
|
||||
}
|
||||
|
||||
async function consult(userId: string, question: string): Promise<string> {
|
||||
const memories = await mem0.search(question, {
|
||||
filters: { user_id: userId },
|
||||
topK: 5,
|
||||
threshold: 0.7,
|
||||
});
|
||||
const context = memories.results?.map((m: any) => `- ${m.memory}`).join('\n') || '';
|
||||
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-5-mini',
|
||||
messages: [
|
||||
{ role: 'system', content: `You are a health coach. Patient context:\n${context}` },
|
||||
{ role: 'user', content: question },
|
||||
],
|
||||
});
|
||||
const reply = response.choices[0].message.content!;
|
||||
|
||||
await mem0.add(
|
||||
[{ role: 'user', content: question }, { role: 'assistant', content: reply }],
|
||||
{ userId: userId, runId: 'healthcare_session' }
|
||||
);
|
||||
return reply;
|
||||
}
|
||||
```
|
||||
|
||||
### Key Benefits
|
||||
|
||||
- High threshold (0.7) ensures only confident matches for safety-critical retrieval
|
||||
- Session scoping via `run_id` groups related health interactions
|
||||
- Metadata tagging separates patient info from conversation history
|
||||
|
||||
**Best for:** Telehealth, wellness apps, patient management — persistent health context across visits.
|
||||
|
||||
---
|
||||
|
||||
## 4. Content Creation Workflow
|
||||
|
||||
Store voice guidelines once and apply them across every draft. Uses `run_id` and `metadata` to scope writing preferences per session.
|
||||
|
||||
### Implementation (Python)
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
from openai import OpenAI
|
||||
|
||||
mem0 = MemoryClient()
|
||||
openai_client = OpenAI()
|
||||
|
||||
def store_writing_preferences(user_id: str, preferences: str):
|
||||
mem0.add(
|
||||
[{"role": "user", "content": preferences}],
|
||||
user_id=user_id,
|
||||
run_id="editing_session",
|
||||
metadata={"type": "preferences", "category": "writing_style"}
|
||||
)
|
||||
|
||||
def draft_content(user_id: str, topic: str) -> str:
|
||||
# Retrieve writing preferences
|
||||
prefs = mem0.search(
|
||||
"writing style preferences",
|
||||
filters={"AND": [{"user_id": user_id}, {"run_id": "editing_session"}]}
|
||||
)
|
||||
style_context = "\n".join([f"- {m['memory']}" for m in prefs.get("results", [])])
|
||||
|
||||
response = openai_client.chat.completions.create(
|
||||
model="gpt-5-mini",
|
||||
messages=[
|
||||
{"role": "system", "content": f"Write content matching these style preferences:\n{style_context}"},
|
||||
{"role": "user", "content": f"Write a blog post about: {topic}"},
|
||||
]
|
||||
)
|
||||
return response.choices[0].message.content
|
||||
|
||||
# Usage
|
||||
store_writing_preferences("writer_01", "I prefer short sentences. Active voice. No jargon. Use analogies.")
|
||||
draft_content("writer_01", "Why AI memory matters for chatbots")
|
||||
# Drafts content matching the stored voice guidelines
|
||||
```
|
||||
|
||||
### Implementation (TypeScript)
|
||||
|
||||
```typescript
|
||||
import MemoryClient from 'mem0ai';
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const mem0 = new MemoryClient({ apiKey: process.env.MEM0_API_KEY! });
|
||||
const openai = new OpenAI();
|
||||
|
||||
async function storePreferences(userId: string, preferences: string) {
|
||||
await mem0.add(
|
||||
[{ role: 'user', content: preferences }],
|
||||
{ userId: userId, runId: 'editing_session', metadata: { type: 'preferences' } }
|
||||
);
|
||||
}
|
||||
|
||||
async function draftContent(userId: string, topic: string): Promise<string> {
|
||||
const prefs = await mem0.search('writing style preferences', {
|
||||
filters: { AND: [{ user_id: userId }, { run_id: 'editing_session' }] },
|
||||
});
|
||||
const styleContext = prefs.results?.map((m: any) => `- ${m.memory}`).join('\n') || '';
|
||||
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-5-mini',
|
||||
messages: [
|
||||
{ role: 'system', content: `Write content matching these preferences:\n${styleContext}` },
|
||||
{ role: 'user', content: `Write a blog post about: ${topic}` },
|
||||
],
|
||||
});
|
||||
return response.choices[0].message.content!;
|
||||
}
|
||||
```
|
||||
|
||||
### Key Benefits
|
||||
|
||||
- Voice consistency across all content without repeating guidelines
|
||||
- Scoped sessions let you maintain different style profiles
|
||||
- Preferences update automatically as you refine them
|
||||
|
||||
**Best for:** Marketing teams, technical writers, agencies — consistent voice across all content.
|
||||
|
||||
---
|
||||
|
||||
## 5. Multi-Agent / Multi-Tenant
|
||||
|
||||
Keep memories separate using `user_id`, `agent_id`, `app_id`, and `run_id` scoping. Critical for multi-agent workflows and multi-tenant apps.
|
||||
|
||||
### Implementation (Python)
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient()
|
||||
|
||||
# Store memories scoped to user + agent + session
|
||||
def store_scoped_memory(messages: list, user_id: str, agent_id: str, run_id: str, app_id: str):
|
||||
client.add(
|
||||
messages,
|
||||
user_id=user_id,
|
||||
agent_id=agent_id,
|
||||
run_id=run_id,
|
||||
app_id=app_id
|
||||
)
|
||||
|
||||
# Query within a specific scope
|
||||
def search_user_session(query: str, user_id: str, app_id: str, run_id: str):
|
||||
"""Search memories for a specific user within a specific session."""
|
||||
return client.search(
|
||||
query,
|
||||
filters={
|
||||
"AND": [
|
||||
{"user_id": user_id},
|
||||
{"app_id": app_id},
|
||||
{"run_id": run_id}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
def search_agent_knowledge(query: str, agent_id: str, app_id: str):
|
||||
"""Search all memories an agent has across all users."""
|
||||
return client.search(
|
||||
query,
|
||||
filters={
|
||||
"AND": [
|
||||
{"agent_id": agent_id},
|
||||
{"app_id": app_id}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
# Usage: Travel concierge app with multiple agents
|
||||
store_scoped_memory(
|
||||
[{"role": "user", "content": "I'm vegetarian and prefer window seats"}],
|
||||
user_id="traveler_cam",
|
||||
agent_id="travel_planner",
|
||||
run_id="tokyo-2025",
|
||||
app_id="concierge_app"
|
||||
)
|
||||
|
||||
# User-scoped query: "What does Cam prefer?"
|
||||
user_mems = search_user_session("dietary restrictions?", "traveler_cam", "concierge_app", "tokyo-2025")
|
||||
|
||||
# Agent-scoped query: "What do all travelers prefer?" (across users)
|
||||
agent_mems = search_agent_knowledge("common dietary restrictions?", "travel_planner", "concierge_app")
|
||||
```
|
||||
|
||||
### Implementation (TypeScript)
|
||||
|
||||
```typescript
|
||||
import MemoryClient from 'mem0ai';
|
||||
|
||||
const client = new MemoryClient({ apiKey: process.env.MEM0_API_KEY! });
|
||||
|
||||
async function storeScopedMemory(
|
||||
messages: Array<{ role: string; content: string }>,
|
||||
userId: string, agentId: string, runId: string, appId: string
|
||||
) {
|
||||
await client.add(messages, {
|
||||
userId: userId,
|
||||
agentId: agentId,
|
||||
runId: runId,
|
||||
appId: appId,
|
||||
});
|
||||
}
|
||||
|
||||
async function searchUserSession(query: string, userId: string, appId: string, runId: string) {
|
||||
return client.search(query, {
|
||||
filters: { AND: [{ user_id: userId }, { app_id: appId }, { run_id: runId }] },
|
||||
});
|
||||
}
|
||||
|
||||
async function searchAgentKnowledge(query: string, agentId: string, appId: string) {
|
||||
return client.search(query, {
|
||||
filters: { AND: [{ agent_id: agentId }, { app_id: appId }] },
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Key Benefits
|
||||
|
||||
- Full isolation between users, agents, sessions, and apps
|
||||
- Query at any scope level — user, agent, session, or app-wide
|
||||
- No memory leakage between tenants
|
||||
|
||||
**Best for:** Multi-agent workflows, multi-tenant SaaS — proper isolation at every level.
|
||||
|
||||
---
|
||||
|
||||
## 6. Personalized Search
|
||||
|
||||
Blend real-time search results with personal context. Uses `custom_instructions` to infer preferences from queries.
|
||||
|
||||
### Implementation (Python)
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
from openai import OpenAI
|
||||
|
||||
mem0 = MemoryClient()
|
||||
openai_client = OpenAI()
|
||||
|
||||
# One-time setup: configure Mem0 to infer from queries
|
||||
mem0.project.update(
|
||||
custom_instructions="""Infer user preferences and facts from their search queries.
|
||||
Extract dietary preferences, location, interests, and purchase history."""
|
||||
)
|
||||
|
||||
def personalized_search(user_id: str, query: str, search_results: list) -> str:
|
||||
# Get user context from memory
|
||||
memories = mem0.search(query, user_id=user_id, top_k=5)
|
||||
user_context = "\n".join([f"- {m['memory']}" for m in memories.get("results", [])])
|
||||
|
||||
response = openai_client.chat.completions.create(
|
||||
model="gpt-5-mini",
|
||||
messages=[
|
||||
{"role": "system", "content": f"Personalize search results using user context:\n{user_context}"},
|
||||
{"role": "user", "content": f"Query: {query}\n\nSearch results:\n{search_results}"},
|
||||
]
|
||||
)
|
||||
reply = response.choices[0].message.content
|
||||
|
||||
# Store the query to learn preferences over time
|
||||
mem0.add(
|
||||
[{"role": "user", "content": query}],
|
||||
user_id=user_id
|
||||
)
|
||||
return reply
|
||||
|
||||
# Usage
|
||||
personalized_search("user_42", "best restaurants nearby", ["Restaurant A", "Restaurant B"])
|
||||
# Over time, Mem0 learns: "user prefers vegetarian, lives in Austin"
|
||||
# Future searches are automatically personalized
|
||||
```
|
||||
|
||||
### Implementation (TypeScript)
|
||||
|
||||
```typescript
|
||||
import MemoryClient from 'mem0ai';
|
||||
import OpenAI from 'openai';
|
||||
|
||||
const mem0 = new MemoryClient({ apiKey: process.env.MEM0_API_KEY! });
|
||||
const openai = new OpenAI();
|
||||
|
||||
async function personalizedSearch(userId: string, query: string, searchResults: string[]): Promise<string> {
|
||||
const memories = await mem0.search(query, { filters: { user_id: userId }, topK: 5 });
|
||||
const context = memories.results?.map((m: any) => `- ${m.memory}`).join('\n') || '';
|
||||
|
||||
const response = await openai.chat.completions.create({
|
||||
model: 'gpt-5-mini',
|
||||
messages: [
|
||||
{ role: 'system', content: `Personalize results using user context:\n${context}` },
|
||||
{ role: 'user', content: `Query: ${query}\nResults: ${searchResults.join(', ')}` },
|
||||
],
|
||||
});
|
||||
const reply = response.choices[0].message.content!;
|
||||
|
||||
await mem0.add([{ role: 'user', content: query }], { userId: userId });
|
||||
return reply;
|
||||
}
|
||||
```
|
||||
|
||||
### Key Benefits
|
||||
|
||||
- Learns preferences from queries automatically via `custom_instructions`
|
||||
- Personalizes any search provider (Tavily, Google, Bing)
|
||||
- Zero manual preference setup — improves over time
|
||||
|
||||
**Best for:** Personalized search engines, recommendation systems — search results tailored to individual users.
|
||||
|
||||
---
|
||||
|
||||
## 7. Email Intelligence
|
||||
|
||||
Capture, categorize, and recall inbox threads using persistent memories with rich metadata.
|
||||
|
||||
### Implementation (Python)
|
||||
|
||||
```python
|
||||
from mem0 import MemoryClient
|
||||
|
||||
client = MemoryClient()
|
||||
|
||||
def store_email(user_id: str, sender: str, subject: str, body: str, date: str):
|
||||
client.add(
|
||||
[{"role": "user", "content": f"Email from {sender}: {subject}\n\n{body}"}],
|
||||
user_id=user_id,
|
||||
metadata={"email_type": "incoming", "sender": sender, "subject": subject, "date": date}
|
||||
)
|
||||
|
||||
def search_emails(user_id: str, query: str):
|
||||
return client.search(
|
||||
query,
|
||||
filters={"AND": [{"user_id": user_id}, {"categories": {"contains": "email"}}]},
|
||||
top_k=10
|
||||
)
|
||||
|
||||
def get_emails_from_sender(user_id: str, sender: str):
|
||||
return client.get_all(
|
||||
filters={
|
||||
"AND": [
|
||||
{"user_id": user_id},
|
||||
{"metadata": {"contains": sender}}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
# Usage
|
||||
store_email("alice", "bob@acme.com", "Q3 Budget Review", "Attached is the Q3 budget...", "2025-01-15")
|
||||
store_email("alice", "carol@acme.com", "Sprint Planning", "Here are the priorities...", "2025-01-16")
|
||||
|
||||
results = search_emails("alice", "budget discussions")
|
||||
sender_emails = get_emails_from_sender("alice", "bob@acme.com")
|
||||
```
|
||||
|
||||
### Implementation (TypeScript)
|
||||
|
||||
```typescript
|
||||
import MemoryClient from 'mem0ai';
|
||||
|
||||
const client = new MemoryClient({ apiKey: process.env.MEM0_API_KEY! });
|
||||
|
||||
async function storeEmail(userId: string, sender: string, subject: string, body: string, date: string) {
|
||||
await client.add(
|
||||
[{ role: 'user', content: `Email from ${sender}: ${subject}\n\n${body}` }],
|
||||
{ userId: userId, metadata: { email_type: 'incoming', sender, subject, date } }
|
||||
);
|
||||
}
|
||||
|
||||
async function searchEmails(userId: string, query: string) {
|
||||
return client.search(query, {
|
||||
filters: { AND: [{ user_id: userId }, { categories: { contains: 'email' } }] },
|
||||
topK: 10,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
### Key Benefits
|
||||
|
||||
- Rich metadata enables multi-dimensional queries (sender, date, subject)
|
||||
- Category filtering separates emails from other memory types
|
||||
- Semantic search across all email content
|
||||
|
||||
**Best for:** Inbox management, email automation — searchable email memories with metadata filtering.
|
||||
|
||||
---
|
||||
|
||||
## Common Patterns Across Use Cases
|
||||
|
||||
### Pattern 1: Retrieve → Generate → Store
|
||||
|
||||
Every use case follows the same 3-step loop:
|
||||
|
||||
```python
|
||||
# 1. Retrieve relevant context
|
||||
memories = mem0.search(user_input, user_id=user_id)
|
||||
context = "\n".join([m["memory"] for m in memories.get("results", [])])
|
||||
|
||||
# 2. Generate with context
|
||||
response = llm.generate(system_prompt=f"Context:\n{context}", user_input=user_input)
|
||||
|
||||
# 3. Store the interaction
|
||||
mem0.add(
|
||||
[{"role": "user", "content": user_input}, {"role": "assistant", "content": response}],
|
||||
user_id=user_id
|
||||
)
|
||||
```
|
||||
|
||||
### Pattern 2: Scope with Entity Identifiers
|
||||
|
||||
Use `user_id`, `agent_id`, `app_id`, and `run_id` to isolate memories:
|
||||
|
||||
```python
|
||||
# User-level: personal preferences
|
||||
client.add(messages, user_id="alice")
|
||||
|
||||
# Session-level: conversation within one session
|
||||
client.add(messages, user_id="alice", run_id="session_123")
|
||||
|
||||
# Agent-level: agent-specific knowledge
|
||||
client.add(messages, agent_id="support_bot", app_id="helpdesk")
|
||||
```
|
||||
|
||||
### Pattern 3: Rich Metadata for Filtering
|
||||
|
||||
Attach structured metadata for multi-dimensional queries:
|
||||
|
||||
```python
|
||||
# Store with metadata
|
||||
client.add(messages, user_id="alice", metadata={"priority": "high", "source": "phone_call"})
|
||||
|
||||
# Filter by category + metadata
|
||||
client.search("billing issues", filters={
|
||||
"AND": [{"user_id": "alice"}, {"categories": {"contains": "billing"}}]
|
||||
})
|
||||
```
|
||||
|
||||
### Pattern 4: Custom Instructions for Domain-Specific Extraction
|
||||
|
||||
Control what Mem0 extracts from conversations:
|
||||
|
||||
```python
|
||||
client.project.update(
|
||||
custom_instructions="Extract medical conditions, medications, and allergies. Exclude billing info."
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## More Examples
|
||||
|
||||
For 30+ cookbooks with complete working code: [docs.mem0.ai/cookbooks](https://docs.mem0.ai/cookbooks)
|
||||
-181
@@ -1,181 +0,0 @@
|
||||
#!/usr/bin/env bun
|
||||
export {};
|
||||
const DOCS_BASE = "https://docs.mem0.ai";
|
||||
const SEARCH_ENDPOINT = `${DOCS_BASE}/api/search`;
|
||||
const LLMS_INDEX = `${DOCS_BASE}/llms.txt`;
|
||||
|
||||
const SECTION_MAP: Record<string, string[]> = {
|
||||
platform: [
|
||||
"/platform/overview",
|
||||
"/platform/quickstart",
|
||||
"/platform/features",
|
||||
"/platform/features/graph-memory",
|
||||
"/platform/features/selective-memory",
|
||||
"/platform/features/custom-categories",
|
||||
"/platform/features/v2-memory-filters",
|
||||
"/platform/features/async-client",
|
||||
"/platform/features/webhooks",
|
||||
"/platform/features/multimodal-support",
|
||||
],
|
||||
api: [
|
||||
"/api-reference/memory/add-memories",
|
||||
"/api-reference/memory/v2-search-memories",
|
||||
"/api-reference/memory/v2-get-memories",
|
||||
"/api-reference/memory/get-memory",
|
||||
"/api-reference/memory/update-memory",
|
||||
"/api-reference/memory/delete-memory",
|
||||
],
|
||||
"open-source": [
|
||||
"/open-source/overview",
|
||||
"/open-source/python-quickstart",
|
||||
"/open-source/node-quickstart",
|
||||
"/open-source/features",
|
||||
"/open-source/features/graph-memory",
|
||||
"/open-source/features/rest-api",
|
||||
"/open-source/configure-components",
|
||||
],
|
||||
openmemory: [
|
||||
"/openmemory/overview",
|
||||
"/openmemory/quickstart",
|
||||
],
|
||||
sdks: ["/sdks/python", "/sdks/js"],
|
||||
integrations: ["/integrations"],
|
||||
};
|
||||
|
||||
async function fetchUrl(url: string): Promise<string> {
|
||||
try {
|
||||
const res = await fetch(url, {
|
||||
headers: { "User-Agent": "Mem0DocSearchAgent/1.0" },
|
||||
signal: AbortSignal.timeout(15000),
|
||||
});
|
||||
if (!res.ok) return `HTTP Error ${res.status}: ${res.statusText}`;
|
||||
return await res.text();
|
||||
} catch (e: any) {
|
||||
return `Error: ${e.message}`;
|
||||
}
|
||||
}
|
||||
|
||||
async function searchDocs(query: string, section?: string) {
|
||||
const params = new URLSearchParams({ query });
|
||||
try {
|
||||
const result = await fetchUrl(`${SEARCH_ENDPOINT}?${params}`);
|
||||
const data = JSON.parse(result);
|
||||
if (data?.results) {
|
||||
let results = data.results;
|
||||
if (section && SECTION_MAP[section]) {
|
||||
const paths = SECTION_MAP[section];
|
||||
results = results.filter((r: any) =>
|
||||
paths.some((p) => r.url?.startsWith(p)),
|
||||
);
|
||||
}
|
||||
return { source: "mintlify_search", results };
|
||||
}
|
||||
} catch {}
|
||||
|
||||
const indexContent = await fetchUrl(LLMS_INDEX);
|
||||
const queryLower = query.toLowerCase();
|
||||
let matching = indexContent
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter((l) => l && !l.startsWith("#") && l.toLowerCase().includes(queryLower));
|
||||
|
||||
if (section && SECTION_MAP[section]) {
|
||||
const paths = SECTION_MAP[section];
|
||||
matching = matching.filter((u) => paths.some((p) => u.includes(p)));
|
||||
}
|
||||
|
||||
return {
|
||||
source: "llms_txt_index",
|
||||
query,
|
||||
matching_urls: matching.slice(0, 20),
|
||||
suggestion: "Fetch specific URLs for detailed content",
|
||||
};
|
||||
}
|
||||
|
||||
async function fetchPage(pagePath: string) {
|
||||
const url = pagePath.startsWith("/") ? `${DOCS_BASE}${pagePath}` : pagePath;
|
||||
const content = await fetchUrl(url);
|
||||
return { url, content: content.slice(0, 10000), truncated: content.length > 10000 };
|
||||
}
|
||||
|
||||
async function getIndex() {
|
||||
const content = await fetchUrl(LLMS_INDEX);
|
||||
const urls = content
|
||||
.split("\n")
|
||||
.map((l) => l.trim())
|
||||
.filter((l) => l && !l.startsWith("#"));
|
||||
return { total_pages: urls.length, urls, sections: Object.keys(SECTION_MAP) };
|
||||
}
|
||||
|
||||
function listSection(section: string) {
|
||||
if (!SECTION_MAP[section]) {
|
||||
return { error: `Unknown section: ${section}`, available: Object.keys(SECTION_MAP) };
|
||||
}
|
||||
return { section, pages: SECTION_MAP[section].map((p) => `${DOCS_BASE}${p}`) };
|
||||
}
|
||||
|
||||
function printResult(result: any) {
|
||||
if (result.results) {
|
||||
console.log(`Source: ${result.source ?? "unknown"}`);
|
||||
for (const r of result.results) {
|
||||
console.log(` - ${r.title ?? "N/A"}: ${r.url ?? "N/A"}`);
|
||||
if (r.description) console.log(` ${r.description.slice(0, 200)}`);
|
||||
}
|
||||
} else if (result.matching_urls) {
|
||||
console.log(`Source: ${result.source}`);
|
||||
console.log(`Query: ${result.query}`);
|
||||
for (const url of result.matching_urls) console.log(` - ${url}`);
|
||||
if (result.suggestion) console.log(`\n${result.suggestion}`);
|
||||
} else if (result.urls) {
|
||||
console.log(`Total documentation pages: ${result.total_pages}`);
|
||||
console.log(`Sections: ${result.sections.join(", ")}`);
|
||||
for (const url of result.urls.slice(0, 30)) console.log(` - ${url}`);
|
||||
if (result.total_pages > 30) console.log(` ... and ${result.total_pages - 30} more`);
|
||||
} else if (result.pages) {
|
||||
console.log(`Section: ${result.section}`);
|
||||
for (const page of result.pages) console.log(` - ${page}`);
|
||||
} else if (result.content) {
|
||||
console.log(`URL: ${result.url}`);
|
||||
if (result.truncated) console.log("[Content truncated to 10000 chars]");
|
||||
console.log(result.content);
|
||||
} else if (result.error) {
|
||||
console.log(`Error: ${result.error}`);
|
||||
if (result.available) console.log(`Available sections: ${result.available.join(", ")}`);
|
||||
} else {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
}
|
||||
}
|
||||
|
||||
const args = process.argv.slice(2);
|
||||
const flags: Record<string, string | boolean> = {};
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
if (args[i] === "--query" && args[i + 1]) flags.query = args[++i];
|
||||
else if (args[i] === "--page" && args[i + 1]) flags.page = args[++i];
|
||||
else if (args[i] === "--section" && args[i + 1]) flags.section = args[++i];
|
||||
else if (args[i] === "--index") flags.index = true;
|
||||
else if (args[i] === "--json") flags.json = true;
|
||||
}
|
||||
|
||||
let result: any;
|
||||
if (flags.index) {
|
||||
result = await getIndex();
|
||||
} else if (flags.section && !flags.query) {
|
||||
result = listSection(flags.section as string);
|
||||
} else if (flags.page) {
|
||||
result = await fetchPage(flags.page as string);
|
||||
} else if (flags.query) {
|
||||
result = await searchDocs(flags.query as string, flags.section as string | undefined);
|
||||
} else {
|
||||
console.log("Usage:");
|
||||
console.log(" bun scripts/mem0_doc_search.ts --query \"topic\"");
|
||||
console.log(" bun scripts/mem0_doc_search.ts --page \"/platform/features/graph-memory\"");
|
||||
console.log(" bun scripts/mem0_doc_search.ts --index");
|
||||
console.log(" bun scripts/mem0_doc_search.ts --section platform");
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (flags.json) {
|
||||
console.log(JSON.stringify(result, null, 2));
|
||||
} else {
|
||||
printResult(result);
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
---
|
||||
name: memory-reviewer
|
||||
description: Reviews stored memory quality by detecting duplicates, contradictions, and stale entries with actionable recommendations. Use when search results seem conflicting, before running dream consolidation, or for periodic memory hygiene audits.
|
||||
---
|
||||
|
||||
# Memory Reviewer
|
||||
|
||||
Audits memory quality for the active project. Finds duplicates, contradictions, and low-confidence entries.
|
||||
|
||||
## When to use
|
||||
|
||||
- User asks "check my memories", "memory quality", "any duplicates?"
|
||||
- User runs `/mem0:memory-reviewer` directly
|
||||
- After a session with 5+ memory writes (suggest proactively)
|
||||
- After `/mem0:health --deep` identifies issues
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Fetch all memories** for active project via `get_memories` with `filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]}`, `page_size=200`. Paginate if needed — cap at 200 memories.
|
||||
|
||||
2. **Group by `metadata.type`**. Common types: `decision`, `convention`, `anti_pattern`, `task_learning`, `project_profile`, `user_preference`, `session_state`.
|
||||
|
||||
3. **Scan each group for issues:**
|
||||
|
||||
| Issue | Detection method |
|
||||
|---|---|
|
||||
| **Near-duplicates** | >60% noun overlap within same type. Compare memory text after stripping stop words. |
|
||||
| **Contradictions** | Opposing facts about same topic (e.g., "use PostgreSQL" vs "use MySQL" for same component) |
|
||||
| **Low-confidence** | `metadata.confidence < 0.3` |
|
||||
| **Missing type** | No `metadata.type` set |
|
||||
| **Stale** | `created_at` older than 180 days with no updates |
|
||||
|
||||
4. **Output compact summary:**
|
||||
|
||||
```
|
||||
memory-reviewer: project=<id> total=<N>
|
||||
duplicates: <N> found
|
||||
contradictions: <N> found
|
||||
low_confidence: <N> found
|
||||
untagged: <N> found
|
||||
stale: <N> found
|
||||
```
|
||||
|
||||
5. **If issues found**, list them with memory IDs:
|
||||
|
||||
```
|
||||
Issues:
|
||||
[duplicate] "<memory_a>" ≈ "<memory_b>" [mem0:<id_a>, mem0:<id_b>]
|
||||
[contradiction] "<memory_x>" vs "<memory_y>" [mem0:<id_x>, mem0:<id_y>]
|
||||
[low_conf] "<memory_z>" (confidence: 0.1) [mem0:<id_z>]
|
||||
```
|
||||
|
||||
6. **Suggest action**: "Run `/mem0:dream` to consolidate duplicates and resolve contradictions."
|
||||
|
||||
## Constraints
|
||||
|
||||
- **Read-only** — never modify or delete memories (that's `/mem0:dream`'s job)
|
||||
- **Max 200 memories** per scan
|
||||
- Report findings, let user decide on action
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -1,181 +0,0 @@
|
||||
---
|
||||
name: onboard
|
||||
description: Sets up mem0 for a new project including API key configuration, MCP authentication, project file import, and coding categories. Use on first run in a new project, when API key needs updating, or to re-run initial setup after configuration changes.
|
||||
---
|
||||
|
||||
# Mem0 Onboarding Wizard
|
||||
|
||||
Run this wizard to set up the mem0 plugin for the current project. Complete in ~60 seconds.
|
||||
|
||||
**IMPORTANT: Execute steps strictly in order (0 → 1 → 2 → 3 → 4 → 5 → 6). Each step depends on the previous one. Do NOT run steps in parallel or skip ahead. Complete one step fully before starting the next.**
|
||||
|
||||
## Step 0: Skip dependency install
|
||||
|
||||
The plugin lists `mem0ai` as a declared dependency — no install step is needed. Proceed directly to Step 1.
|
||||
|
||||
## Step 1: Set up API key
|
||||
|
||||
Check if the API key is available:
|
||||
|
||||
```bash
|
||||
[ -n "${MEM0_API_KEY:-}" ] && echo "SET" || echo "NOT_SET"
|
||||
```
|
||||
|
||||
IMPORTANT: Never run `echo $MEM0_API_KEY` — that prints the secret in plaintext to the conversation log.
|
||||
|
||||
### If API key IS set (output is "SET")
|
||||
|
||||
Print: `- API key found.` and proceed to Step 2.
|
||||
|
||||
### If API key is NOT set (output is "NOT_SET")
|
||||
|
||||
Guide the user through API key setup. Show this message:
|
||||
|
||||
```
|
||||
Step 1: Setting up API key.
|
||||
|
||||
- API key not found. Let's set it up.
|
||||
|
||||
1. Get your API key from https://app.mem0.ai/dashboard/api-keys
|
||||
|
||||
2. Choose ONE method:
|
||||
|
||||
Option A — Shell profile:
|
||||
echo 'export MEM0_API_KEY="m0-your-key-here"' >> ~/.zshrc
|
||||
source ~/.zshrc
|
||||
|
||||
Option B — OpenCode environment config:
|
||||
Add MEM0_API_KEY to your OpenCode environment settings
|
||||
so it is available in all sessions.
|
||||
|
||||
3. Verify:
|
||||
[ -n "${MEM0_API_KEY:-}" ] && echo "SET" || echo "NOT_SET"
|
||||
```
|
||||
|
||||
After the user confirms, re-run the verify command. If NOT_SET, repeat. If SET, proceed to Step 2.
|
||||
|
||||
## Step 2: MCP server connection
|
||||
|
||||
First, check if MCP tools are already available using ToolSearch with query `"mem0 search_memories"`. The exact tool name varies by install method (may be `mcp__mem0__search_memories` or `mcp__plugin_mem0_mem0__search_memories`).
|
||||
|
||||
**If MCP tools ARE found:** Print `- MCP already connected.` and proceed to Step 3.
|
||||
|
||||
**If MCP tools are NOT found:**
|
||||
|
||||
The MCP server authenticates using the `MEM0_API_KEY` set in Step 1. No OAuth or browser login is needed.
|
||||
|
||||
1. Verify the API key is set (re-run the Step 1 check)
|
||||
2. Check the plugin is installed and the MCP server for mem0 is listed in OpenCode's MCP configuration
|
||||
3. If the server shows an error, ask the user to restart OpenCode and run `/mem0:onboard` again
|
||||
4. If all checks pass but tools are still missing: "Restart OpenCode and run `/mem0:onboard` again."
|
||||
|
||||
**STOP here** — do not proceed without MCP tools.
|
||||
|
||||
## Step 3: Verify connectivity and show identity
|
||||
|
||||
Call `search_memories` with `query="project setup"`, `filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]}`, `top_k=1` to verify connectivity.
|
||||
|
||||
Print:
|
||||
```
|
||||
- Connected
|
||||
user: <user_id>
|
||||
project: <project_id>
|
||||
branch: <branch>
|
||||
```
|
||||
|
||||
If the search fails, troubleshoot the API key and MCP connection.
|
||||
|
||||
## Step 4: Import project files
|
||||
|
||||
Check for common project context files in the working directory root. Read any that exist and store their key facts as memories using `add_memory`.
|
||||
|
||||
### 4a: Detect project files
|
||||
|
||||
```bash
|
||||
for f in CLAUDE.md AGENTS.md .cursorrules .windsurfrules mem0.md .mem0.md; do
|
||||
[ -f "$f" ] && echo "FOUND: $f ($(wc -c < "$f") bytes)"
|
||||
done || true
|
||||
```
|
||||
|
||||
If no files found, print `- No project files found. Skipping import.` and proceed to Step 5.
|
||||
|
||||
### 4b: Read and import via MCP
|
||||
|
||||
For each file found in 4a:
|
||||
|
||||
1. Read the file contents with the Read tool.
|
||||
2. Extract the key facts, conventions, and decisions documented in the file. Do not import the entire file verbatim — summarize meaningful chunks.
|
||||
3. Call `add_memory` for each meaningful chunk with:
|
||||
- `data`: the extracted fact or convention
|
||||
- `user_id`: the active user id
|
||||
- `app_id`: the active project id
|
||||
- `metadata`: `{"source": "<filename>", "type": "project_context"}`
|
||||
|
||||
If a file is very large (over 4000 bytes), split it into logical sections (one `add_memory` call per section).
|
||||
|
||||
### 4c: Report to user
|
||||
|
||||
After processing all files, print a user-friendly summary:
|
||||
|
||||
```
|
||||
- Importing project files into mem0... done.
|
||||
<N> file(s) read, <M> memories stored.
|
||||
These are available as context for future sessions.
|
||||
```
|
||||
|
||||
If `add_memory` calls fail, print:
|
||||
```
|
||||
- Project file import failed. Check API key and MCP connection, then retry with: /mem0:onboard
|
||||
```
|
||||
|
||||
## Step 5: Verify coding categories
|
||||
|
||||
Coding categories are now configured automatically in the background when the plugin starts. This step only verifies they are set up.
|
||||
|
||||
Check if the categories are already configured by searching for a project_profile memory:
|
||||
|
||||
Call `search_memories` with `query="coding categories project profile"`, `filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]}`, `top_k=1`.
|
||||
|
||||
If a project_profile memory is found, print:
|
||||
```
|
||||
- Coding categories already configured (17 categories, auto-installed).
|
||||
```
|
||||
|
||||
If no project_profile memory is found, store one as a fallback. Call `add_memory` once with:
|
||||
|
||||
- `data`: a plain-text description of the project profile and the list of active coding categories:
|
||||
```
|
||||
Project profile for <project_id>.
|
||||
Active coding categories: architecture_decisions, api_design, data_models,
|
||||
algorithms, dependencies, environment_setup, testing_strategy, debugging_notes,
|
||||
performance, security, deployment, code_conventions, error_handling,
|
||||
refactoring_history, integrations, onboarding, project_meta.
|
||||
```
|
||||
- `user_id`: the active user id
|
||||
- `app_id`: the active project id
|
||||
- `metadata`: `{"type": "project_profile", "source": "onboard"}`
|
||||
|
||||
After the `add_memory` call succeeds, print:
|
||||
```
|
||||
- Coding categories installed (17 categories).
|
||||
```
|
||||
|
||||
## Step 6: Summary
|
||||
|
||||
Print a summary:
|
||||
```
|
||||
- Onboarding complete.
|
||||
user_id: <user_id>
|
||||
project_id: <project_id> (app_id)
|
||||
files: <N> found, <M> memories stored
|
||||
categories: <N installed | already installed | skipped by user>
|
||||
|
||||
Memory is now active for this project. Start working — mem0 will
|
||||
automatically search relevant context and capture learnings.
|
||||
|
||||
Run /mem0:tour to see what mem0 already knows about this project.
|
||||
```
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -1,135 +0,0 @@
|
||||
---
|
||||
name: stats
|
||||
description: Displays memory usage statistics for the current session and project including counts by category, age distribution, and API latency. Use when checking how many memories exist, reviewing session activity, or auditing memory distribution across categories.
|
||||
---
|
||||
|
||||
# Mem0 Stats
|
||||
|
||||
Show session and lifetime memory statistics.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Gather session context
|
||||
|
||||
Read the session identity from env vars set by the plugin's shell.env hook:
|
||||
|
||||
- `MEM0_USER_ID` (falls back to `$USER` if unset)
|
||||
- `MEM0_APP_ID` — the active project identifier
|
||||
- `MEM0_SESSION_ID` — current session identifier
|
||||
- `MEM0_BRANCH` — current git branch
|
||||
|
||||
If `MEM0_USER_ID` is unset, use `$USER`. If `MEM0_APP_ID` is unset, note "No project configured" and stop.
|
||||
|
||||
### Step 2: Fetch total memory count
|
||||
|
||||
Call `get_memories` MCP tool to get the total count for this project:
|
||||
|
||||
`filters={"AND": [{"user_id": "<MEM0_USER_ID>"}, {"app_id": "<MEM0_APP_ID>"}]}`, `page_size=1`
|
||||
|
||||
Read the `count` field from the response — this is the total number of memories for the project regardless of page size.
|
||||
|
||||
### Step 3: Fetch memories for category breakdown
|
||||
|
||||
Call `get_memories` MCP tool to retrieve memories for grouping:
|
||||
|
||||
`filters={"AND": [{"user_id": "<MEM0_USER_ID>"}, {"app_id": "<MEM0_APP_ID>"}]}`, `page_size=200`
|
||||
|
||||
Group each memory by:
|
||||
1. `categories[0]` (platform-assigned) — primary grouping
|
||||
2. `metadata.type` (agent-assigned) — secondary if `categories` is empty or absent
|
||||
3. `created_at` date — for age analysis
|
||||
|
||||
**Category normalization:** Merge `auto_capture` and `uncategorized` into a single `uncategorized` row. Do NOT show `auto_capture` as its own row.
|
||||
|
||||
Also run a `search_memories` MCP tool call with `query="project"`, `filters={"AND": [{"user_id": "<MEM0_USER_ID>"}, {"app_id": "<MEM0_APP_ID>"}]}`, `top_k=1` to measure round-trip latency. Note the time before and after the MCP call — do NOT attempt raw HTTP calls to the API.
|
||||
|
||||
### Step 4: Display
|
||||
|
||||
Print a minimal plain-text dashboard. No markdown formatting — OpenCode TUI renders text verbatim.
|
||||
|
||||
Example output shape:
|
||||
|
||||
```
|
||||
mem0 stats
|
||||
|
||||
Session (<first 12 chars of MEM0_SESSION_ID>) branch: main
|
||||
|
||||
Project: my-project — 55 memories — API: 84ms
|
||||
|
||||
Category Count
|
||||
-------------------- -----
|
||||
decision 24
|
||||
convention 15
|
||||
anti_pattern 6
|
||||
task_learning 5
|
||||
user_preference 3
|
||||
session_state 2
|
||||
|
||||
Age — oldest: 2026-02-15 newest: 2026-05-23
|
||||
< 7 days: 5 | 7-30d: 12 | 30-90d: 10 | > 90d: 8
|
||||
|
||||
Identity — user: kartik project: my-project branch: main
|
||||
```
|
||||
|
||||
**Display rules:**
|
||||
- Use plain text with spaces to align columns — no markdown tables, no | pipes, no ** bold, no ## headers
|
||||
- Category section: sort by count descending, omit categories with 0 memories
|
||||
- Age: single line with pipe-separated buckets, computed from `created_at`
|
||||
- Session line: show MEM0_SESSION_ID (first 12 chars) and MEM0_BRANCH if available; skip the line entirely if both are unset
|
||||
- If only 1-2 total memories, skip the category table — just show the count
|
||||
- Keep everything compact — no decorative borders or filler
|
||||
|
||||
## Weekly digest mode
|
||||
|
||||
When invoked with `--weekly` (e.g., `/mem0:stats --weekly`), append a weekly
|
||||
activity digest after the standard stats dashboard.
|
||||
|
||||
### W1: Fetch recent memories
|
||||
|
||||
Call `search_memories` in parallel with time-scoped queries:
|
||||
1. `query="decisions made this week"`, `filters={"AND": [{"user_id": "<MEM0_USER_ID>"}, {"app_id": "<MEM0_APP_ID>"}, {"created_at": {"gte": "<7 days ago YYYY-MM-DD>"}}]}`, `top_k=20`
|
||||
2. `query="bugs errors fixes"`, same time filter, `top_k=20`
|
||||
3. `query="patterns conventions learnings"`, same time filter, `top_k=20`
|
||||
|
||||
### W2: Analyze
|
||||
|
||||
Merge by ID. Group into "New this week" by `categories[0]` or `metadata.type`.
|
||||
Calculate: memories added last 7 days, most active categories, most active day.
|
||||
|
||||
### W3: Display
|
||||
|
||||
Append after the standard stats in plain text (no markdown):
|
||||
|
||||
```
|
||||
This week (May 16 - May 23)
|
||||
|
||||
+12 memories — most active: Wednesday (5)
|
||||
|
||||
Category New
|
||||
-------------- ---
|
||||
decision 5
|
||||
task_learning 4
|
||||
bug_fix 3
|
||||
|
||||
Highlights
|
||||
- <2-3 sentence summary of most important decisions/learnings this week>
|
||||
```
|
||||
|
||||
### W4: Write digest file
|
||||
|
||||
Write to `~/.mem0/weekly-digest.txt` (overwrite). Append one line to
|
||||
`~/.mem0/digest-history.log`:
|
||||
```
|
||||
<YYYY-MM-DD> | <MEM0_APP_ID> | +<new_count> memories | top: <top_category>
|
||||
```
|
||||
|
||||
### W5: Empty state
|
||||
|
||||
If no new memories in 7 days, output:
|
||||
```
|
||||
No new memories in the past week. Total: <N> memories in <MEM0_APP_ID>.
|
||||
```
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -1,107 +0,0 @@
|
||||
---
|
||||
name: switch-project
|
||||
description: Overrides the auto-detected project scope to read and write memories under a different project ID, or enables global search to access all memories across all users and projects. Use when working across multiple projects, accessing memories from another repo, enabling team-wide memory access, or when auto-detection resolves to the wrong project.
|
||||
---
|
||||
|
||||
# Mem0 Switch Project
|
||||
|
||||
Override the automatic project_id detection for the current directory, or enable global search mode.
|
||||
|
||||
## Usage
|
||||
|
||||
- `/mem0:switch-project <project-name>` — switch to a specific project scope
|
||||
- `/mem0:switch-project --global` — enable global search (all memories, all users, all projects)
|
||||
- `/mem0:switch-project --no-global` — disable global search and return to per-project scoping
|
||||
|
||||
## Execution
|
||||
|
||||
### If `--global` flag is provided:
|
||||
|
||||
1. Set `global_search: true` in `~/.mem0/settings.json` using the Bash tool:
|
||||
|
||||
```bash
|
||||
python3 -c "
|
||||
import json, os
|
||||
settings_file = os.path.expanduser('~/.mem0/settings.json')
|
||||
settings = {}
|
||||
if os.path.isfile(settings_file):
|
||||
with open(settings_file) as f:
|
||||
settings = json.load(f)
|
||||
settings['global_search'] = True
|
||||
with open(settings_file, 'w') as f:
|
||||
json.dump(settings, f, indent=2)
|
||||
print('Global search enabled')
|
||||
"
|
||||
```
|
||||
|
||||
2. Print:
|
||||
```
|
||||
Global search enabled.
|
||||
Searches now return all memories across all users and projects.
|
||||
Writes still use the current user_id and app_id.
|
||||
Restart the session for the change to take effect.
|
||||
```
|
||||
|
||||
### If `--no-global` flag is provided:
|
||||
|
||||
1. Set `global_search: false` in `~/.mem0/settings.json` using the Bash tool:
|
||||
|
||||
```bash
|
||||
python3 -c "
|
||||
import json, os
|
||||
settings_file = os.path.expanduser('~/.mem0/settings.json')
|
||||
settings = {}
|
||||
if os.path.isfile(settings_file):
|
||||
with open(settings_file) as f:
|
||||
settings = json.load(f)
|
||||
settings['global_search'] = False
|
||||
with open(settings_file, 'w') as f:
|
||||
json.dump(settings, f, indent=2)
|
||||
print('Global search disabled')
|
||||
"
|
||||
```
|
||||
|
||||
2. Print:
|
||||
```
|
||||
Global search disabled.
|
||||
Searches now return only memories scoped to the current project.
|
||||
Restart the session for the change to take effect.
|
||||
```
|
||||
|
||||
### If a project name is provided (no flags):
|
||||
|
||||
1. If no project name was given, ask: "What project_id should this directory use?"
|
||||
|
||||
2. Write the mapping to `~/.mem0/project_map.json` using the Bash tool:
|
||||
|
||||
```bash
|
||||
python3 -c "
|
||||
import json, os
|
||||
map_file = os.path.expanduser('~/.mem0/project_map.json')
|
||||
mapping = {}
|
||||
if os.path.isfile(map_file):
|
||||
with open(map_file) as f:
|
||||
mapping = json.load(f)
|
||||
mapping[os.getcwd()] = '<PROJECT_NAME>'
|
||||
os.makedirs(os.path.dirname(map_file), exist_ok=True)
|
||||
with open(map_file, 'w') as f:
|
||||
json.dump(mapping, f, indent=2)
|
||||
print(f'Mapped {os.getcwd()} -> <PROJECT_NAME>')
|
||||
"
|
||||
```
|
||||
|
||||
(Replace `<PROJECT_NAME>` with the user's chosen project name.)
|
||||
|
||||
3. Verify by searching for existing memories:
|
||||
- Call `search_memories` with `query="project"`, `filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<PROJECT_NAME>"}]}`, `top_k=1`
|
||||
|
||||
4. Print:
|
||||
```
|
||||
Switched to project <PROJECT_NAME>.
|
||||
<N> memories found for this project.
|
||||
Note: This override persists across sessions for this directory.
|
||||
```
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -1,14 +0,0 @@
|
||||
{
|
||||
"$schema": "https://opencode.ai/config.json",
|
||||
"plugin": ["@mem0/opencode-plugin"],
|
||||
"mcp": {
|
||||
"mem0": {
|
||||
"type": "remote",
|
||||
"url": "https://mcp.mem0.ai/mcp/",
|
||||
"headers": {
|
||||
"Authorization": "Token {env:MEM0_API_KEY}"
|
||||
},
|
||||
"oauth": false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@mem0/opencode-plugin",
|
||||
"version": "0.1.3",
|
||||
"version": "0.2.0",
|
||||
"type": "module",
|
||||
"description": "Mem0 persistent memory plugin for OpenCode — add, search, and manage memories across sessions",
|
||||
"main": "dist/index.js",
|
||||
@@ -11,9 +11,6 @@
|
||||
"import": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"bin": {
|
||||
"mem0-opencode": "./cli.ts"
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
@@ -23,7 +20,6 @@
|
||||
"opencode-plugin",
|
||||
"mem0",
|
||||
"memory",
|
||||
"mcp",
|
||||
"ai-memory",
|
||||
"persistent-memory"
|
||||
],
|
||||
@@ -34,8 +30,6 @@
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"cli.ts",
|
||||
"opencode.json",
|
||||
"LICENSE",
|
||||
"opencode-skills"
|
||||
],
|
||||
@@ -49,6 +43,7 @@
|
||||
"opencode": {
|
||||
"type": "plugin",
|
||||
"hooks": [
|
||||
"config",
|
||||
"chat.message",
|
||||
"tool.execute.before",
|
||||
"tool.execute.after",
|
||||
@@ -59,7 +54,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@opencode-ai/plugin": "^1.0.162",
|
||||
"mem0ai": "^3.0.7"
|
||||
"mem0ai": "^3.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bun-types": ">=1.3.14",
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { parseProjectFromRemote } from "./project";
|
||||
|
||||
describe("parseProjectFromRemote", () => {
|
||||
test("ssh remote with a custom host alias (github.com-work)", () => {
|
||||
expect(parseProjectFromRemote("git@github.com-mem0:mem0ai/mem0.git")).toBe("mem0ai-mem0");
|
||||
});
|
||||
|
||||
test("standard scp-style ssh remote", () => {
|
||||
expect(parseProjectFromRemote("git@github.com:openai/gym.git")).toBe("openai-gym");
|
||||
});
|
||||
|
||||
test("https remote", () => {
|
||||
expect(parseProjectFromRemote("https://github.com/mem0ai/mem0.git")).toBe("mem0ai-mem0");
|
||||
});
|
||||
|
||||
test("https remote without a .git suffix", () => {
|
||||
expect(parseProjectFromRemote("https://gitlab.com/acme/widgets")).toBe("acme-widgets");
|
||||
});
|
||||
|
||||
test("trailing slash is ignored", () => {
|
||||
expect(parseProjectFromRemote("https://github.com/acme/widgets/")).toBe("acme-widgets");
|
||||
});
|
||||
|
||||
test("returns null when no owner/repo can be parsed", () => {
|
||||
expect(parseProjectFromRemote("not-a-remote")).toBeNull();
|
||||
expect(parseProjectFromRemote("")).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Project identity resolution for the Mem0 OpenCode plugin.
|
||||
*
|
||||
* The project id (`app_id`) scopes memories to a repo. We derive it from the
|
||||
* git remote so it is stable across clones, worktrees, and sub-directories —
|
||||
* falling back (in opencode-mem0.ts) to the git repo root dir name, then the
|
||||
* cwd. Keeping the parser pure makes the tricky remote formats testable.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parse `owner/repo` out of a git remote URL and return it as `owner-repo`.
|
||||
* Handles https, scp-style ssh, custom ssh host aliases (e.g.
|
||||
* `git@github.com-work:owner/repo.git`), an optional `.git` suffix, and a
|
||||
* trailing slash. Returns null when no owner/repo can be found.
|
||||
*/
|
||||
export function parseProjectFromRemote(remote: string): string | null {
|
||||
const m = remote.trim().match(/[:/]([^/:]+)\/([^/:]+?)(?:\.git)?\/?$/);
|
||||
if (!m) return null;
|
||||
return `${m[1]}-${m[2]}`;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { scopeSearchFilters, scopeWriteParams, asScope, resolveDefaultScope } from "./scope";
|
||||
|
||||
describe("memory scope (pi-agent parity)", () => {
|
||||
test("project scope = this repo", () => {
|
||||
expect(scopeSearchFilters("project", "u", "app", "run")).toEqual({
|
||||
user_id: "u",
|
||||
app_id: "app",
|
||||
});
|
||||
expect(scopeWriteParams("project", "u", "app", "run")).toEqual({
|
||||
user_id: "u",
|
||||
app_id: "app",
|
||||
});
|
||||
});
|
||||
|
||||
test("session scope adds run_id", () => {
|
||||
expect(scopeSearchFilters("session", "u", "app", "run")).toEqual({
|
||||
user_id: "u",
|
||||
app_id: "app",
|
||||
run_id: "run",
|
||||
});
|
||||
expect(scopeWriteParams("session", "u", "app", "run")).toEqual({
|
||||
user_id: "u",
|
||||
app_id: "app",
|
||||
run_id: "run",
|
||||
});
|
||||
});
|
||||
|
||||
test("global scope spans all the user's projects (matches pi-agent)", () => {
|
||||
expect(scopeSearchFilters("global", "u", "app", "run")).toEqual({
|
||||
user_id: "u",
|
||||
app_id: "*",
|
||||
});
|
||||
// global writes drop app_id so the memory is user-wide, not project-bound
|
||||
expect(scopeWriteParams("global", "u", "app", "run")).toEqual({ user_id: "u" });
|
||||
});
|
||||
|
||||
test("default scope is project when settings are absent", () => {
|
||||
expect(resolveDefaultScope(null)).toBe("project");
|
||||
expect(resolveDefaultScope(undefined)).toBe("project");
|
||||
expect(resolveDefaultScope({})).toBe("project");
|
||||
});
|
||||
|
||||
test("default scope reads default_scope from settings", () => {
|
||||
expect(resolveDefaultScope({ default_scope: "session" })).toBe("session");
|
||||
expect(resolveDefaultScope({ default_scope: "global" })).toBe("global");
|
||||
expect(resolveDefaultScope({ default_scope: "project" })).toBe("project");
|
||||
});
|
||||
|
||||
test("default scope normalizes an invalid default_scope to project", () => {
|
||||
expect(resolveDefaultScope({ default_scope: "nonsense" })).toBe("project");
|
||||
expect(resolveDefaultScope({ default_scope: 42 })).toBe("project");
|
||||
});
|
||||
|
||||
test("asScope normalizes unknown values to project", () => {
|
||||
expect(asScope("global")).toBe("global");
|
||||
expect(asScope("session")).toBe("session");
|
||||
expect(asScope("project")).toBe("project");
|
||||
expect(asScope("nonsense")).toBe("project");
|
||||
expect(asScope(undefined)).toBe("project");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Memory scope resolution — ported from the pi-agent plugin's scoping model.
|
||||
*
|
||||
* Lets the agent choose, per memory operation, how wide to read/write:
|
||||
* - "project" (default): this repo only -> { user_id, app_id }
|
||||
* - "session": this run only -> { user_id, app_id, run_id }
|
||||
* - "global": across ALL of the user's projects -> { user_id, app_id: "*" }
|
||||
*
|
||||
* Mirrors pi-agent/src/memory/scoping.ts (resolveSearchFilters / resolveAddParams)
|
||||
* so the OpenCode plugin exposes scope the same way: as a per-call tool parameter,
|
||||
* not a stateful "switch project" command.
|
||||
*/
|
||||
|
||||
export type Scope = "project" | "session" | "global";
|
||||
|
||||
/** Filters for `search` / `get_memories` at the given scope. */
|
||||
export function scopeSearchFilters(
|
||||
scope: Scope,
|
||||
userId: string,
|
||||
appId: string,
|
||||
runId: string,
|
||||
): Record<string, string> {
|
||||
switch (scope) {
|
||||
case "session":
|
||||
return { user_id: userId, app_id: appId, run_id: runId };
|
||||
case "global":
|
||||
return { user_id: userId, app_id: "*" };
|
||||
case "project":
|
||||
default:
|
||||
return { user_id: userId, app_id: appId };
|
||||
}
|
||||
}
|
||||
|
||||
/** Identity params for `add` / `delete_all` at the given scope. */
|
||||
export function scopeWriteParams(
|
||||
scope: Scope,
|
||||
userId: string,
|
||||
appId: string,
|
||||
runId: string,
|
||||
): { user_id: string; app_id?: string; run_id?: string } {
|
||||
switch (scope) {
|
||||
case "session":
|
||||
return { user_id: userId, app_id: appId, run_id: runId };
|
||||
case "global":
|
||||
return { user_id: userId };
|
||||
case "project":
|
||||
default:
|
||||
return { user_id: userId, app_id: appId };
|
||||
}
|
||||
}
|
||||
|
||||
/** Normalize an arbitrary value to a valid Scope (defaults to "project"). */
|
||||
export function asScope(value: unknown): Scope {
|
||||
return value === "session" || value === "global" ? value : "project";
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the persisted default scope from a parsed `~/.mem0/settings.json`
|
||||
* object. This is the user-changeable default applied to memory operations when
|
||||
* no explicit `scope` is passed (set via the `mem0-scope` skill). Falls back to
|
||||
* "project" when unset or invalid.
|
||||
*/
|
||||
export function resolveDefaultScope(
|
||||
settings: Record<string, unknown> | null | undefined,
|
||||
): Scope {
|
||||
return asScope(settings?.default_scope);
|
||||
}
|
||||
|
||||
/** Guidance injected so the agent uses `global` only when explicitly asked. */
|
||||
export const SCOPE_GUIDANCE =
|
||||
'Memory tools accept an optional `scope`: omit it (or "project") for normal queries; use "session" to limit to the current run; use "global" ONLY when the user explicitly asks to search across all their projects in this workspace.';
|
||||
@@ -47,5 +47,32 @@ describe("opencode telemetry", () => {
|
||||
process.env.MEM0_TELEMETRY = "false";
|
||||
expect(() => captureEvent("session_start", {}, KEY)).not.toThrow();
|
||||
expect(() => captureEvent("session_start", {}, undefined)).not.toThrow();
|
||||
expect(() => captureEvent("session_start", {}, KEY, "proj")).not.toThrow();
|
||||
});
|
||||
|
||||
test("every event carries os_version (matches telemetry.py schema)", () => {
|
||||
const props = buildEvent("session_start", {}, KEY)!
|
||||
.properties as Record<string, unknown>;
|
||||
expect(typeof props.os_version).toBe("string");
|
||||
});
|
||||
|
||||
test("project_hash is sha256(projectId) when a project id is supplied", async () => {
|
||||
const { createHash } = await import("node:crypto");
|
||||
const expected = createHash("sha256").update("acme-repo").digest("hex");
|
||||
const props = buildEvent("session_start", {}, KEY, "acme-repo")!
|
||||
.properties as Record<string, unknown>;
|
||||
expect(props.project_hash).toBe(expected);
|
||||
});
|
||||
|
||||
test("project_hash is omitted when no project id is supplied (no raw ids leak)", () => {
|
||||
const props = buildEvent("session_start", {}, KEY)!
|
||||
.properties as Record<string, unknown>;
|
||||
expect("project_hash" in props).toBe(false);
|
||||
});
|
||||
|
||||
test("expanded event types all use the shared plugin.* namespace", () => {
|
||||
for (const ev of ["user_prompt", "bash_error", "pre_compact", "session_stop"]) {
|
||||
expect(buildEvent(ev, {}, KEY)!.event).toBe(`plugin.${ev}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -13,11 +13,13 @@
|
||||
* installs without a key emit nothing). Disable with MEM0_TELEMETRY=false.
|
||||
*
|
||||
* Never sends: memory content, API keys, raw user/project IDs. Only sends:
|
||||
* event type, platform, plugin version, anonymized hash of the API key.
|
||||
* event type, platform, plugin version, and anonymized hashes of the API key
|
||||
* and project ID.
|
||||
*/
|
||||
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { release } from "node:os";
|
||||
|
||||
const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX";
|
||||
const POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/";
|
||||
@@ -62,6 +64,7 @@ export function buildEvent(
|
||||
eventType: string,
|
||||
properties: Record<string, unknown>,
|
||||
apiKey: string | undefined,
|
||||
projectId?: string,
|
||||
): Record<string, unknown> | null {
|
||||
if (!isTelemetryEnabled() || !apiKey) return null;
|
||||
return {
|
||||
@@ -74,9 +77,14 @@ export function buildEvent(
|
||||
platform: "opencode",
|
||||
plugin_version: PLUGIN_VERSION,
|
||||
os: process.platform,
|
||||
os_version: release(),
|
||||
sample_rate: 1.0,
|
||||
$process_person_profile: false,
|
||||
$lib: "posthog-node",
|
||||
// Anonymized project segmentation, matching telemetry.py's project_hash.
|
||||
...(projectId
|
||||
? { project_hash: createHash("sha256").update(projectId).digest("hex") }
|
||||
: {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -86,8 +94,9 @@ export function captureEvent(
|
||||
eventType: string,
|
||||
properties: Record<string, unknown>,
|
||||
apiKey: string | undefined,
|
||||
projectId?: string,
|
||||
): void {
|
||||
const payload = buildEvent(eventType, properties, apiKey);
|
||||
const payload = buildEvent(eventType, properties, apiKey, projectId);
|
||||
if (!payload) return;
|
||||
try {
|
||||
void fetch(POSTHOG_HOST, {
|
||||
|
||||
@@ -158,29 +158,10 @@ Install from the [Cursor Marketplace](https://cursor.com/marketplace) for the co
|
||||
### OpenCode
|
||||
|
||||
```bash
|
||||
bunx @mem0/opencode-plugin@latest install
|
||||
opencode plugin @mem0/opencode-plugin
|
||||
```
|
||||
|
||||
Or via OpenCode's built-in CLI: `opencode plugin @mem0/opencode-plugin`
|
||||
|
||||
Then add the MCP server to your `opencode.json` (project or global at `~/.config/opencode/opencode.json`):
|
||||
|
||||
```json
|
||||
{
|
||||
"mcp": {
|
||||
"mem0": {
|
||||
"type": "remote",
|
||||
"url": "https://mcp.mem0.ai/mcp/",
|
||||
"headers": {
|
||||
"Authorization": "Token {env:MEM0_API_KEY}"
|
||||
},
|
||||
"oauth": false
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Restart OpenCode. The plugin installs hooks and skills automatically. Drop the `plugin` install if you only want MCP.
|
||||
Add `--global` to install for all projects. The plugin auto-registers its native memory tools, hooks, and skills via its `config` hook — no MCP server to configure. Restart OpenCode after installing.
|
||||
|
||||
See [OpenCode integration docs](https://docs.mem0.ai/integrations/opencode) for full details.
|
||||
|
||||
|
||||
@@ -7,12 +7,31 @@ All pre-fetch hooks use this instead of duplicating urllib boilerplate.
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import urllib.request
|
||||
|
||||
SEARCH_URL = "https://api.mem0.ai/v3/memories/search/"
|
||||
SEARCH_TIMEOUT = 5
|
||||
|
||||
|
||||
def should_rerank() -> bool:
|
||||
"""Whether auto-injection searches should request Platform reranking.
|
||||
|
||||
The REST search endpoint does not rerank when ``rerank`` is omitted, so
|
||||
auto-injected context is ordered by raw vector similarity and the single
|
||||
most relevant memory can fall outside the injected top_k window. We default
|
||||
reranking ON for the hook-driven injection path (the extra ~150-200ms is
|
||||
well within the hook's curl budget) and let users opt out via MEM0_RERANK.
|
||||
|
||||
MEM0_RERANK is read case-insensitively; ``0``, ``false``, ``no``, and
|
||||
``off`` disable reranking. Anything else (including unset) enables it.
|
||||
"""
|
||||
raw = os.environ.get("MEM0_RERANK")
|
||||
if raw is None:
|
||||
return True
|
||||
return raw.strip().lower() not in ("0", "false", "no", "off", "")
|
||||
|
||||
|
||||
def _do_search(api_key: str, payload: dict) -> list[dict]:
|
||||
body = json.dumps(payload).encode()
|
||||
req = urllib.request.Request(
|
||||
|
||||
@@ -23,7 +23,7 @@ sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
|
||||
from _formatting import TYPE_ICONS, format_age
|
||||
from _identity import resolve_api_key, resolve_user_id
|
||||
from _project import resolve_project_id
|
||||
from _search import search_memories
|
||||
from _search import search_memories, should_rerank
|
||||
|
||||
FILE_READ_GATE_MIN_BYTES = 1500
|
||||
MAX_RESULTS = 5
|
||||
@@ -93,6 +93,7 @@ def search_file_context(
|
||||
api_key, user_id, project_id, query,
|
||||
top_k=MAX_RESULTS, threshold=0.3,
|
||||
global_search=global_search,
|
||||
rerank=should_rerank(),
|
||||
)
|
||||
|
||||
results = results[:MAX_RESULTS]
|
||||
|
||||
@@ -77,15 +77,16 @@ RESULTS=$(PYTHONPATH="$SCRIPT_DIR" MEM0_SEARCH_QUERY="$ERROR_QUERY" MEM0_SEARCH_
|
||||
python3 -c "
|
||||
import os, sys
|
||||
sys.path.insert(0, os.environ.get('PYTHONPATH', '.'))
|
||||
from _search import search_memories, format_results_for_context
|
||||
from _search import search_memories, format_results_for_context, should_rerank
|
||||
|
||||
api_key = os.environ.get('MEM0_API_KEY', '')
|
||||
user_id = os.environ.get('MEM0_SEARCH_USER', 'default')
|
||||
project_id = os.environ.get('MEM0_PROJECT_ID', 'unknown')
|
||||
query = os.environ.get('MEM0_SEARCH_QUERY', '')
|
||||
rerank = should_rerank()
|
||||
|
||||
r1 = search_memories(api_key, user_id, project_id, query, metadata_type='anti_pattern', top_k=3)
|
||||
r2 = search_memories(api_key, user_id, project_id, query, metadata_type='bug_fix', top_k=3)
|
||||
r1 = search_memories(api_key, user_id, project_id, query, metadata_type='anti_pattern', top_k=3, rerank=rerank)
|
||||
r2 = search_memories(api_key, user_id, project_id, query, metadata_type='bug_fix', top_k=3, rerank=rerank)
|
||||
|
||||
seen = set()
|
||||
combined = []
|
||||
|
||||
@@ -120,14 +120,15 @@ if [ -n "$HAS_RESUME" ]; then
|
||||
RESUME_RESULTS=$(PYTHONPATH="$SCRIPT_DIR" MEM0_SEARCH_USER="$USER_ID" python3 -c "
|
||||
import os, sys
|
||||
sys.path.insert(0, os.environ.get('PYTHONPATH', '.'))
|
||||
from _search import search_memories, format_results_for_context
|
||||
from _search import search_memories, format_results_for_context, should_rerank
|
||||
|
||||
api_key = os.environ.get('MEM0_API_KEY', '')
|
||||
user_id = os.environ.get('MEM0_SEARCH_USER', 'default')
|
||||
project_id = os.environ.get('MEM0_PROJECT_ID', 'unknown')
|
||||
rerank = should_rerank()
|
||||
|
||||
state = search_memories(api_key, user_id, project_id, 'session state current task', metadata_type='session_state', top_k=3)
|
||||
decisions = search_memories(api_key, user_id, project_id, 'recent decisions and learnings', metadata_type='decision', top_k=3)
|
||||
state = search_memories(api_key, user_id, project_id, 'session state current task', metadata_type='session_state', top_k=3, rerank=rerank)
|
||||
decisions = search_memories(api_key, user_id, project_id, 'recent decisions and learnings', metadata_type='decision', top_k=3, rerank=rerank)
|
||||
|
||||
all_r = state + decisions
|
||||
seen = set()
|
||||
|
||||
@@ -101,6 +101,67 @@ def test_search_memories_no_api_key_returns_empty():
|
||||
assert results == []
|
||||
|
||||
|
||||
def test_search_memories_omits_rerank_by_default():
|
||||
"""Regression for #5684: rerank must not be sent unless requested."""
|
||||
from _search import search_memories
|
||||
|
||||
captured_body = {}
|
||||
|
||||
def mock_urlopen(req, timeout=None):
|
||||
captured_body.update(json.loads(req.data.decode()))
|
||||
resp = MagicMock()
|
||||
resp.read.return_value = json.dumps({"results": []}).encode()
|
||||
resp.__enter__ = lambda s: s
|
||||
resp.__exit__ = MagicMock(return_value=False)
|
||||
return resp
|
||||
|
||||
with patch("urllib.request.urlopen", side_effect=mock_urlopen):
|
||||
search_memories("key", "user", "proj", "query")
|
||||
|
||||
assert "rerank" not in captured_body
|
||||
|
||||
|
||||
def test_search_memories_forwards_rerank_true():
|
||||
"""Regression for #5684: rerank=True must reach the request body so the
|
||||
REST endpoint actually reranks (it does not rerank when omitted)."""
|
||||
from _search import search_memories
|
||||
|
||||
captured_body = {}
|
||||
|
||||
def mock_urlopen(req, timeout=None):
|
||||
captured_body.update(json.loads(req.data.decode()))
|
||||
resp = MagicMock()
|
||||
resp.read.return_value = json.dumps({"results": []}).encode()
|
||||
resp.__enter__ = lambda s: s
|
||||
resp.__exit__ = MagicMock(return_value=False)
|
||||
return resp
|
||||
|
||||
with patch("urllib.request.urlopen", side_effect=mock_urlopen):
|
||||
search_memories("key", "user", "proj", "query", rerank=True)
|
||||
|
||||
assert captured_body.get("rerank") is True
|
||||
|
||||
|
||||
def test_should_rerank_defaults_true(monkeypatch):
|
||||
"""Regression for #5684: auto-injection reranks by default."""
|
||||
from _search import should_rerank
|
||||
|
||||
monkeypatch.delenv("MEM0_RERANK", raising=False)
|
||||
assert should_rerank() is True
|
||||
|
||||
|
||||
def test_should_rerank_opt_out_values(monkeypatch):
|
||||
from _search import should_rerank
|
||||
|
||||
for falsey in ("0", "false", "False", "NO", "off", ""):
|
||||
monkeypatch.setenv("MEM0_RERANK", falsey)
|
||||
assert should_rerank() is False, falsey
|
||||
|
||||
for truthy in ("1", "true", "yes", "on"):
|
||||
monkeypatch.setenv("MEM0_RERANK", truthy)
|
||||
assert should_rerank() is True, truthy
|
||||
|
||||
|
||||
def test_format_results_for_context():
|
||||
from _search import format_results_for_context
|
||||
|
||||
|
||||
@@ -42,7 +42,7 @@ import {
|
||||
isSubagentSession,
|
||||
} from "./isolation.ts";
|
||||
import {
|
||||
loadTriagePrompt,
|
||||
loadCompactTriagePrompt,
|
||||
loadDreamPrompt,
|
||||
isSkillsMode,
|
||||
} from "./skill-loader.ts";
|
||||
@@ -464,7 +464,7 @@ function registerHooks(
|
||||
"openclaw-mem0: skills-mode skipping recall for system/bootstrap prompt",
|
||||
);
|
||||
// Still inject the protocol, just skip recall search
|
||||
const systemContext = loadTriagePrompt(cfg.skills ?? {});
|
||||
const systemContext = loadCompactTriagePrompt(cfg.skills ?? {});
|
||||
return { prependSystemContext: systemContext };
|
||||
}
|
||||
|
||||
@@ -474,7 +474,7 @@ function registerHooks(
|
||||
const userId = _effectiveUserId(isSubagent ? undefined : sessionId);
|
||||
|
||||
// Static protocol goes in prependSystemContext (cacheable across turns)
|
||||
let systemContext = loadTriagePrompt(cfg.skills ?? {});
|
||||
let systemContext = loadCompactTriagePrompt(cfg.skills ?? {});
|
||||
if (isSubagent) {
|
||||
systemContext =
|
||||
"You are a subagent — use these memories for context but do not assume you are this user. Do NOT store new memories.\n\n" +
|
||||
|
||||
@@ -64,12 +64,14 @@
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"form-data@<4.0.6": ">=4.0.6",
|
||||
"protobufjs@<7.5.5": "^7.5.5",
|
||||
"vite": "^8.0.5",
|
||||
"langsmith@<0.6.0": "^0.6.0",
|
||||
"picomatch@<2.3.2": "^2.3.2",
|
||||
"@qdrant/js-client-rest": "^1.18.0",
|
||||
"uuid@<11.1.1": ">=11.1.1"
|
||||
"uuid@<11.1.1": ">=11.1.1",
|
||||
"esbuild": ">=0.28.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+130
-128
@@ -5,12 +5,14 @@ settings:
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
form-data@<4.0.6: '>=4.0.6'
|
||||
protobufjs@<7.5.5: ^7.5.5
|
||||
vite: ^8.0.5
|
||||
langsmith@<0.6.0: ^0.6.0
|
||||
picomatch@<2.3.2: ^2.3.2
|
||||
'@qdrant/js-client-rest': ^1.18.0
|
||||
uuid@<11.1.1: '>=11.1.1'
|
||||
esbuild: '>=0.28.1'
|
||||
|
||||
importers:
|
||||
|
||||
@@ -40,10 +42,10 @@ importers:
|
||||
version: 5.9.3
|
||||
vite:
|
||||
specifier: ^8.0.5
|
||||
version: 8.0.16(@types/node@22.19.20)(esbuild@0.27.7)
|
||||
version: 8.0.16(@types/node@22.19.20)(esbuild@0.28.1)
|
||||
vitest:
|
||||
specifier: ^4.1.7
|
||||
version: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.27.7))
|
||||
version: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.1))
|
||||
|
||||
packages:
|
||||
|
||||
@@ -149,158 +151,158 @@ packages:
|
||||
'@emnapi/wasi-threads@1.2.1':
|
||||
resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.7':
|
||||
resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
|
||||
'@esbuild/aix-ppc64@0.28.1':
|
||||
resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/android-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
|
||||
'@esbuild/android-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.27.7':
|
||||
resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}
|
||||
'@esbuild/android-arm@0.28.1':
|
||||
resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.27.7':
|
||||
resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}
|
||||
'@esbuild/android-x64@0.28.1':
|
||||
resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
|
||||
'@esbuild/darwin-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.27.7':
|
||||
resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}
|
||||
'@esbuild/darwin-x64@0.28.1':
|
||||
resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
|
||||
'@esbuild/freebsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}
|
||||
'@esbuild/freebsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
|
||||
'@esbuild/linux-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.27.7':
|
||||
resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}
|
||||
'@esbuild/linux-arm@0.28.1':
|
||||
resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.27.7':
|
||||
resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}
|
||||
'@esbuild/linux-ia32@0.28.1':
|
||||
resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.27.7':
|
||||
resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}
|
||||
'@esbuild/linux-loong64@0.28.1':
|
||||
resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.7':
|
||||
resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}
|
||||
'@esbuild/linux-mips64el@0.28.1':
|
||||
resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.7':
|
||||
resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}
|
||||
'@esbuild/linux-ppc64@0.28.1':
|
||||
resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.7':
|
||||
resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}
|
||||
'@esbuild/linux-riscv64@0.28.1':
|
||||
resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.27.7':
|
||||
resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}
|
||||
'@esbuild/linux-s390x@0.28.1':
|
||||
resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.27.7':
|
||||
resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}
|
||||
'@esbuild/linux-x64@0.28.1':
|
||||
resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
|
||||
'@esbuild/netbsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}
|
||||
'@esbuild/netbsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
|
||||
'@esbuild/openbsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}
|
||||
'@esbuild/openbsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
|
||||
'@esbuild/openharmony-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@esbuild/sunos-x64@0.27.7':
|
||||
resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
|
||||
'@esbuild/sunos-x64@0.28.1':
|
||||
resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
|
||||
'@esbuild/win32-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.27.7':
|
||||
resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}
|
||||
'@esbuild/win32-ia32@0.28.1':
|
||||
resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.27.7':
|
||||
resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}
|
||||
'@esbuild/win32-x64@0.28.1':
|
||||
resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@@ -879,7 +881,7 @@ packages:
|
||||
resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
peerDependencies:
|
||||
esbuild: '>=0.18'
|
||||
esbuild: '>=0.28.1'
|
||||
|
||||
cac@6.7.14:
|
||||
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||
@@ -1035,8 +1037,8 @@ packages:
|
||||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
esbuild@0.27.7:
|
||||
resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
|
||||
esbuild@0.28.1:
|
||||
resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
@@ -1108,8 +1110,8 @@ packages:
|
||||
form-data-encoder@1.7.2:
|
||||
resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
|
||||
|
||||
form-data@4.0.5:
|
||||
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
||||
form-data@4.0.6:
|
||||
resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
formdata-node@4.4.1:
|
||||
@@ -2016,7 +2018,7 @@ packages:
|
||||
peerDependencies:
|
||||
'@types/node': ^20.19.0 || >=22.12.0
|
||||
'@vitejs/devtools': ^0.1.18
|
||||
esbuild: ^0.27.0 || ^0.28.0
|
||||
esbuild: '>=0.28.1'
|
||||
jiti: '>=1.21.0'
|
||||
less: ^4.0.0
|
||||
sass: ^1.70.0
|
||||
@@ -2323,82 +2325,82 @@ snapshots:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.7':
|
||||
'@esbuild/aix-ppc64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.27.7':
|
||||
'@esbuild/android-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.27.7':
|
||||
'@esbuild/android-arm@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.27.7':
|
||||
'@esbuild/android-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.7':
|
||||
'@esbuild/darwin-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.27.7':
|
||||
'@esbuild/darwin-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.7':
|
||||
'@esbuild/freebsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.7':
|
||||
'@esbuild/freebsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.27.7':
|
||||
'@esbuild/linux-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.27.7':
|
||||
'@esbuild/linux-arm@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.27.7':
|
||||
'@esbuild/linux-ia32@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.27.7':
|
||||
'@esbuild/linux-loong64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.7':
|
||||
'@esbuild/linux-mips64el@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.7':
|
||||
'@esbuild/linux-ppc64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.7':
|
||||
'@esbuild/linux-riscv64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.27.7':
|
||||
'@esbuild/linux-s390x@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.27.7':
|
||||
'@esbuild/linux-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.7':
|
||||
'@esbuild/netbsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.7':
|
||||
'@esbuild/netbsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.7':
|
||||
'@esbuild/openbsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.7':
|
||||
'@esbuild/openbsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.7':
|
||||
'@esbuild/openharmony-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.27.7':
|
||||
'@esbuild/sunos-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.27.7':
|
||||
'@esbuild/win32-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.27.7':
|
||||
'@esbuild/win32-ia32@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.27.7':
|
||||
'@esbuild/win32-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@google/genai@1.52.0':
|
||||
@@ -2727,7 +2729,7 @@ snapshots:
|
||||
'@types/node-fetch@2.6.13':
|
||||
dependencies:
|
||||
'@types/node': 22.19.20
|
||||
form-data: 4.0.5
|
||||
form-data: 4.0.6
|
||||
|
||||
'@types/node@18.19.130':
|
||||
dependencies:
|
||||
@@ -2779,7 +2781,7 @@ snapshots:
|
||||
obug: 2.1.2
|
||||
std-env: 4.1.0
|
||||
tinyrainbow: 3.1.0
|
||||
vitest: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.27.7))
|
||||
vitest: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.1))
|
||||
|
||||
'@vitest/expect@4.1.8':
|
||||
dependencies:
|
||||
@@ -2790,13 +2792,13 @@ snapshots:
|
||||
chai: 6.2.2
|
||||
tinyrainbow: 3.1.0
|
||||
|
||||
'@vitest/mocker@4.1.8(vite@8.0.16(@types/node@22.19.20)(esbuild@0.27.7))':
|
||||
'@vitest/mocker@4.1.8(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.1))':
|
||||
dependencies:
|
||||
'@vitest/spy': 4.1.8
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.21
|
||||
optionalDependencies:
|
||||
vite: 8.0.16(@types/node@22.19.20)(esbuild@0.27.7)
|
||||
vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.1)
|
||||
|
||||
'@vitest/pretty-format@4.1.8':
|
||||
dependencies:
|
||||
@@ -2869,7 +2871,7 @@ snapshots:
|
||||
axios@1.17.0:
|
||||
dependencies:
|
||||
follow-redirects: 1.16.0
|
||||
form-data: 4.0.5
|
||||
form-data: 4.0.6
|
||||
https-proxy-agent: 5.0.1
|
||||
proxy-from-env: 2.1.0
|
||||
transitivePeerDependencies:
|
||||
@@ -2914,9 +2916,9 @@ snapshots:
|
||||
dependencies:
|
||||
run-applescript: 7.1.0
|
||||
|
||||
bundle-require@5.1.0(esbuild@0.27.7):
|
||||
bundle-require@5.1.0(esbuild@0.28.1):
|
||||
dependencies:
|
||||
esbuild: 0.27.7
|
||||
esbuild: 0.28.1
|
||||
load-tsconfig: 0.2.5
|
||||
|
||||
cac@6.7.14: {}
|
||||
@@ -3050,34 +3052,34 @@ snapshots:
|
||||
has-tostringtag: 1.0.2
|
||||
hasown: 2.0.4
|
||||
|
||||
esbuild@0.27.7:
|
||||
esbuild@0.28.1:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.27.7
|
||||
'@esbuild/android-arm': 0.27.7
|
||||
'@esbuild/android-arm64': 0.27.7
|
||||
'@esbuild/android-x64': 0.27.7
|
||||
'@esbuild/darwin-arm64': 0.27.7
|
||||
'@esbuild/darwin-x64': 0.27.7
|
||||
'@esbuild/freebsd-arm64': 0.27.7
|
||||
'@esbuild/freebsd-x64': 0.27.7
|
||||
'@esbuild/linux-arm': 0.27.7
|
||||
'@esbuild/linux-arm64': 0.27.7
|
||||
'@esbuild/linux-ia32': 0.27.7
|
||||
'@esbuild/linux-loong64': 0.27.7
|
||||
'@esbuild/linux-mips64el': 0.27.7
|
||||
'@esbuild/linux-ppc64': 0.27.7
|
||||
'@esbuild/linux-riscv64': 0.27.7
|
||||
'@esbuild/linux-s390x': 0.27.7
|
||||
'@esbuild/linux-x64': 0.27.7
|
||||
'@esbuild/netbsd-arm64': 0.27.7
|
||||
'@esbuild/netbsd-x64': 0.27.7
|
||||
'@esbuild/openbsd-arm64': 0.27.7
|
||||
'@esbuild/openbsd-x64': 0.27.7
|
||||
'@esbuild/openharmony-arm64': 0.27.7
|
||||
'@esbuild/sunos-x64': 0.27.7
|
||||
'@esbuild/win32-arm64': 0.27.7
|
||||
'@esbuild/win32-ia32': 0.27.7
|
||||
'@esbuild/win32-x64': 0.27.7
|
||||
'@esbuild/aix-ppc64': 0.28.1
|
||||
'@esbuild/android-arm': 0.28.1
|
||||
'@esbuild/android-arm64': 0.28.1
|
||||
'@esbuild/android-x64': 0.28.1
|
||||
'@esbuild/darwin-arm64': 0.28.1
|
||||
'@esbuild/darwin-x64': 0.28.1
|
||||
'@esbuild/freebsd-arm64': 0.28.1
|
||||
'@esbuild/freebsd-x64': 0.28.1
|
||||
'@esbuild/linux-arm': 0.28.1
|
||||
'@esbuild/linux-arm64': 0.28.1
|
||||
'@esbuild/linux-ia32': 0.28.1
|
||||
'@esbuild/linux-loong64': 0.28.1
|
||||
'@esbuild/linux-mips64el': 0.28.1
|
||||
'@esbuild/linux-ppc64': 0.28.1
|
||||
'@esbuild/linux-riscv64': 0.28.1
|
||||
'@esbuild/linux-s390x': 0.28.1
|
||||
'@esbuild/linux-x64': 0.28.1
|
||||
'@esbuild/netbsd-arm64': 0.28.1
|
||||
'@esbuild/netbsd-x64': 0.28.1
|
||||
'@esbuild/openbsd-arm64': 0.28.1
|
||||
'@esbuild/openbsd-x64': 0.28.1
|
||||
'@esbuild/openharmony-arm64': 0.28.1
|
||||
'@esbuild/sunos-x64': 0.28.1
|
||||
'@esbuild/win32-arm64': 0.28.1
|
||||
'@esbuild/win32-ia32': 0.28.1
|
||||
'@esbuild/win32-x64': 0.28.1
|
||||
|
||||
escape-string-regexp@2.0.0: {}
|
||||
|
||||
@@ -3130,7 +3132,7 @@ snapshots:
|
||||
|
||||
form-data-encoder@1.7.2: {}
|
||||
|
||||
form-data@4.0.5:
|
||||
form-data@4.0.6:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
@@ -4021,12 +4023,12 @@ snapshots:
|
||||
|
||||
tsup@8.5.1(postcss@8.5.15)(typescript@5.9.3):
|
||||
dependencies:
|
||||
bundle-require: 5.1.0(esbuild@0.27.7)
|
||||
bundle-require: 5.1.0(esbuild@0.28.1)
|
||||
cac: 6.7.14
|
||||
chokidar: 4.0.3
|
||||
consola: 3.4.2
|
||||
debug: 4.4.3
|
||||
esbuild: 0.27.7
|
||||
esbuild: 0.28.1
|
||||
fix-dts-default-cjs-exports: 1.0.1
|
||||
joycon: 3.1.1
|
||||
picocolors: 1.1.1
|
||||
@@ -4069,7 +4071,7 @@ snapshots:
|
||||
|
||||
uuid@14.0.0: {}
|
||||
|
||||
vite@8.0.16(@types/node@22.19.20)(esbuild@0.27.7):
|
||||
vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.1):
|
||||
dependencies:
|
||||
lightningcss: 1.32.0
|
||||
picomatch: 4.0.4
|
||||
@@ -4078,13 +4080,13 @@ snapshots:
|
||||
tinyglobby: 0.2.17
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.20
|
||||
esbuild: 0.27.7
|
||||
esbuild: 0.28.1
|
||||
fsevents: 2.3.3
|
||||
|
||||
vitest@4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.27.7)):
|
||||
vitest@4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.1)):
|
||||
dependencies:
|
||||
'@vitest/expect': 4.1.8
|
||||
'@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@22.19.20)(esbuild@0.27.7))
|
||||
'@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@22.19.20)(esbuild@0.28.1))
|
||||
'@vitest/pretty-format': 4.1.8
|
||||
'@vitest/runner': 4.1.8
|
||||
'@vitest/snapshot': 4.1.8
|
||||
@@ -4101,7 +4103,7 @@ snapshots:
|
||||
tinyexec: 1.2.4
|
||||
tinyglobby: 0.2.17
|
||||
tinyrainbow: 3.1.0
|
||||
vite: 8.0.16(@types/node@22.19.20)(esbuild@0.27.7)
|
||||
vite: 8.0.16(@types/node@22.19.20)(esbuild@0.28.1)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 22.19.20
|
||||
|
||||
@@ -13,9 +13,11 @@ onlyBuiltDependencies:
|
||||
- protobufjs
|
||||
|
||||
overrides:
|
||||
"form-data@<4.0.6": ">=4.0.6"
|
||||
"protobufjs@<7.5.5": "^7.5.5"
|
||||
"vite": "^8.0.5"
|
||||
"langsmith@<0.6.0": "^0.6.0"
|
||||
"picomatch@<2.3.2": "^2.3.2"
|
||||
"@qdrant/js-client-rest": "^1.18.0"
|
||||
"uuid@<11.1.1": ">=11.1.1"
|
||||
"esbuild": ">=0.28.1"
|
||||
|
||||
@@ -2,7 +2,12 @@
|
||||
* Tests for path traversal prevention in skill-loader.
|
||||
*/
|
||||
import { describe, it, expect } from "vitest";
|
||||
import { safePath, loadSkill } from "./skill-loader.ts";
|
||||
import {
|
||||
safePath,
|
||||
loadSkill,
|
||||
loadTriagePrompt,
|
||||
loadCompactTriagePrompt,
|
||||
} from "./skill-loader.ts";
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// safePath — path containment
|
||||
@@ -68,3 +73,71 @@ describe("loadSkill path traversal", () => {
|
||||
expect(result?.prompt).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe("loadCompactTriagePrompt", () => {
|
||||
it("keeps the core triage instructions without inlining the full skill body", () => {
|
||||
const prompt = loadCompactTriagePrompt();
|
||||
|
||||
expect(prompt).toContain("Use `memory_add` tool for ALL user facts");
|
||||
expect(prompt).toContain("Batch facts by CATEGORY");
|
||||
expect(prompt).toContain("ALWAYS rewrite the query");
|
||||
expect(prompt).not.toContain("## Worked Examples");
|
||||
expect(prompt).not.toContain("### memory_search");
|
||||
expect(prompt).not.toContain("Conference requires at least 4 breakout rooms");
|
||||
expect(prompt.length).toBeLessThan(3500);
|
||||
});
|
||||
|
||||
it("keeps the always-recall guidance aligned with the full triage prompt", () => {
|
||||
const config = { recall: { strategy: "always" as const } };
|
||||
const expected =
|
||||
"Automatic recall runs for both long-term and session memory. Use manual searches only when you need more specific context.";
|
||||
|
||||
expect(loadCompactTriagePrompt(config)).toContain(expected);
|
||||
expect(loadTriagePrompt(config)).toContain(expected);
|
||||
});
|
||||
|
||||
it("keeps the manual-recall guidance in compact mode", () => {
|
||||
const prompt = loadCompactTriagePrompt({
|
||||
recall: { strategy: "manual" },
|
||||
});
|
||||
|
||||
expect(prompt).toContain("No automatic recall happens in manual mode.");
|
||||
});
|
||||
|
||||
it("includes short config summaries and truncates oversized custom rules", () => {
|
||||
const prompt = loadCompactTriagePrompt({
|
||||
categories: {
|
||||
travel: { importance: 0.7, ttl: "30d" },
|
||||
},
|
||||
customRules: {
|
||||
include: [
|
||||
"Always remember workshop venue requirements.",
|
||||
"Keep track of recurring conference planning constraints.",
|
||||
"Capture every catering preference, transit note, and presenter dependency in detail.",
|
||||
],
|
||||
exclude: [
|
||||
"Never store one-off demo logs, temporary ETA chatter, or transitory checklist updates.",
|
||||
"Skip verbose retrospectives unless the user explicitly says the lesson should persist.",
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
expect(prompt).toContain("travel (importance 0.7, expires: 30d)");
|
||||
expect(prompt).toContain("include rule(s) and 2 exclude rule(s) are configured");
|
||||
expect(prompt).toContain("Prompt kept compact, full rule text omitted");
|
||||
expect(prompt).toContain('Preview: include "Always remember workshop venue requirements."');
|
||||
});
|
||||
|
||||
it("omits default credential patterns in compact mode unless custom patterns are configured", () => {
|
||||
const defaultPrompt = loadCompactTriagePrompt();
|
||||
const customPrompt = loadCompactTriagePrompt({
|
||||
triage: {
|
||||
credentialPatterns: ["mem0-secret=", "Bearer "],
|
||||
},
|
||||
});
|
||||
|
||||
expect(defaultPrompt).not.toContain("Credential patterns to scan");
|
||||
expect(customPrompt).toContain("Credential patterns to scan");
|
||||
expect(customPrompt).toContain("mem0-secret=");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -191,8 +191,16 @@ function renderCategoriesBlock(
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function renderTriageKnobs(config: SkillsConfig): string {
|
||||
function renderTriageKnobs(
|
||||
config: SkillsConfig,
|
||||
options: {
|
||||
includeCredentialPatterns?: boolean;
|
||||
includeDefaultCredentialPatterns?: boolean;
|
||||
} = {},
|
||||
): string {
|
||||
const lines: string[] = [];
|
||||
const includeCredentialPatterns = options.includeCredentialPatterns ?? true;
|
||||
const includeDefaultCredentialPatterns = options.includeDefaultCredentialPatterns ?? true;
|
||||
|
||||
if (config.triage?.importanceThreshold !== undefined) {
|
||||
lines.push(
|
||||
@@ -200,13 +208,82 @@ function renderTriageKnobs(config: SkillsConfig): string {
|
||||
);
|
||||
}
|
||||
|
||||
const patterns = resolveCredentialPatterns(config);
|
||||
lines.push(`- Credential patterns to scan: ${patterns.map((p) => `\`${p}\``).join(", ")}`);
|
||||
const hasCustomCredentialPatterns = config.triage?.credentialPatterns !== undefined;
|
||||
if (includeCredentialPatterns && (includeDefaultCredentialPatterns || hasCustomCredentialPatterns)) {
|
||||
const patterns = resolveCredentialPatterns(config);
|
||||
lines.push(`- Credential patterns to scan: ${patterns.map((p) => `\`${p}\``).join(", ")}`);
|
||||
}
|
||||
|
||||
if (lines.length === 0) return "";
|
||||
return "\n## Active Configuration Overrides\n\n" + lines.join("\n");
|
||||
}
|
||||
|
||||
const COMPACT_CUSTOM_RULE_CHAR_BUDGET = 280;
|
||||
const COMPACT_CUSTOM_RULE_PREVIEW_LIMIT = 2;
|
||||
|
||||
function formatCategoryConfig(
|
||||
name: string,
|
||||
cat: CategoryConfig,
|
||||
): string {
|
||||
const ttlLabel = cat.ttl ? `expires: ${cat.ttl}` : "permanent";
|
||||
const immLabel = cat.immutable ? ", immutable" : "";
|
||||
return `${name} (importance ${cat.importance}, ${ttlLabel}${immLabel})`;
|
||||
}
|
||||
|
||||
function renderCompactCategories(config: SkillsConfig): string[] {
|
||||
if (!config.categories || Object.keys(config.categories).length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const mergedCategories = resolveCategories(config);
|
||||
return [
|
||||
"## Active Category Overrides",
|
||||
"",
|
||||
...Object.entries(config.categories).map(([name]) =>
|
||||
`- ${formatCategoryConfig(name, mergedCategories[name]!)}`,
|
||||
),
|
||||
];
|
||||
}
|
||||
|
||||
function renderCompactCustomRules(config: SkillsConfig): string[] {
|
||||
const includeRules = config.customRules?.include ?? [];
|
||||
const excludeRules = config.customRules?.exclude ?? [];
|
||||
if (includeRules.length === 0 && excludeRules.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const formatRuleList = (label: string, rules: string[]) =>
|
||||
`${label}: ${rules.map((rule) => `"${rule}"`).join("; ")}`;
|
||||
|
||||
const lines: string[] = [];
|
||||
if (includeRules.length > 0) {
|
||||
lines.push(formatRuleList("Include", includeRules));
|
||||
}
|
||||
if (excludeRules.length > 0) {
|
||||
lines.push(formatRuleList("Exclude", excludeRules));
|
||||
}
|
||||
|
||||
const combined = lines.join(" ");
|
||||
if (combined.length <= COMPACT_CUSTOM_RULE_CHAR_BUDGET) {
|
||||
return ["## Active Custom Rules", "", ...lines.map((line) => `- ${line}`)];
|
||||
}
|
||||
|
||||
const preview = [
|
||||
...includeRules.slice(0, COMPACT_CUSTOM_RULE_PREVIEW_LIMIT).map((rule) => `include "${rule}"`),
|
||||
...excludeRules.slice(0, COMPACT_CUSTOM_RULE_PREVIEW_LIMIT).map((rule) => `exclude "${rule}"`),
|
||||
];
|
||||
|
||||
return [
|
||||
"## Active Custom Rules",
|
||||
"",
|
||||
`- ${includeRules.length} include rule(s) and ${excludeRules.length} exclude rule(s) are configured.`,
|
||||
`- Prompt kept compact, full rule text omitted because it exceeded ${COMPACT_CUSTOM_RULE_CHAR_BUDGET} characters.`,
|
||||
...(preview.length > 0
|
||||
? [`- Preview: ${preview.join("; ")}`]
|
||||
: []),
|
||||
];
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// TTL Helpers
|
||||
// ============================================================================
|
||||
@@ -358,6 +435,11 @@ export function loadTriagePrompt(config: SkillsConfig = {}): string {
|
||||
"- Before updating a memory, search to find the existing version.",
|
||||
);
|
||||
parts.push("");
|
||||
} else if (strategy === "always") {
|
||||
parts.push(
|
||||
"Automatic recall runs for both long-term and session memory. Use manual searches only when you need more specific context.",
|
||||
);
|
||||
parts.push("");
|
||||
}
|
||||
|
||||
parts.push(
|
||||
@@ -453,6 +535,122 @@ export function loadTriagePrompt(config: SkillsConfig = {}): string {
|
||||
return parts.join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Build a compact memory system prompt for skills mode turns.
|
||||
*
|
||||
* This keeps the required storage and search protocol, plus short config
|
||||
* summaries, without inlining the full memory-triage skill body every turn.
|
||||
* If the skill file cannot be read, delegate to loadTriagePrompt(), which has
|
||||
* its own minimal inline fallback.
|
||||
*/
|
||||
export function loadCompactTriagePrompt(config: SkillsConfig = {}): string {
|
||||
if (!readSkillFile("memory-triage")) {
|
||||
return loadTriagePrompt(config);
|
||||
}
|
||||
|
||||
const parts: string[] = [];
|
||||
parts.push("<memory-system>");
|
||||
parts.push(
|
||||
"IMPORTANT: Use `memory_add` tool for ALL user facts. NEVER write user info to workspace files (USER.md, memory/).",
|
||||
);
|
||||
parts.push(
|
||||
"After every response, evaluate whether a new agent would benefit from remembering this days later. Most turns should produce zero memory operations.",
|
||||
);
|
||||
parts.push(
|
||||
"Only store durable, self-contained, third-person facts: identity, preferences with rationale, standing rules, decisions, projects, configurations, technical context, and relationships.",
|
||||
);
|
||||
parts.push(
|
||||
"Never store credentials, tokens, webhook secrets, or raw tool output. If a secret was configured, store only that the credential was configured.",
|
||||
);
|
||||
parts.push(
|
||||
"When a recalled fact materially changes, search for the existing memory and update it in place. Skip cosmetic rewording.",
|
||||
);
|
||||
parts.push("");
|
||||
parts.push("## Tool Usage");
|
||||
parts.push("");
|
||||
parts.push(
|
||||
"Batch facts by CATEGORY. All facts in one memory_add call must share the same category because category determines retention policy (TTL, immutability). If a turn has facts in different categories, make one call per category.",
|
||||
);
|
||||
parts.push(
|
||||
'Format: memory_add(facts: ["User is Alex, backend engineer at Stripe, PST timezone"], category: "identity")',
|
||||
);
|
||||
parts.push(
|
||||
'Mixed categories: memory_add(..., category: "identity") and memory_add(..., category: "decision") in separate calls.',
|
||||
);
|
||||
parts.push(
|
||||
"Categories: identity, configuration, rule, preference, decision, technical, relationship, project.",
|
||||
);
|
||||
|
||||
const categoryLines = renderCompactCategories(config);
|
||||
if (categoryLines.length > 0) {
|
||||
parts.push("");
|
||||
parts.push(...categoryLines);
|
||||
}
|
||||
|
||||
const knobLines = renderTriageKnobs(config, {
|
||||
includeDefaultCredentialPatterns: false,
|
||||
})
|
||||
.split("\n")
|
||||
.filter(Boolean);
|
||||
if (knobLines.length > 0) {
|
||||
parts.push("");
|
||||
parts.push(...knobLines);
|
||||
}
|
||||
|
||||
const customRuleLines = renderCompactCustomRules(config);
|
||||
if (customRuleLines.length > 0) {
|
||||
parts.push("");
|
||||
parts.push(...customRuleLines);
|
||||
}
|
||||
|
||||
if (config.recall?.enabled !== false) {
|
||||
const strategy = config.recall?.strategy ?? "smart";
|
||||
parts.push("");
|
||||
parts.push("## Searching Memory");
|
||||
parts.push("");
|
||||
|
||||
if (strategy === "manual") {
|
||||
parts.push(
|
||||
"No automatic recall happens in manual mode. Use memory_search proactively at conversation start, when context is missing, when topics shift, and before updating a memory.",
|
||||
);
|
||||
} else if (strategy === "always") {
|
||||
parts.push(
|
||||
"Automatic recall runs for both long-term and session memory. Use manual searches only when you need more specific context.",
|
||||
);
|
||||
} else {
|
||||
parts.push(
|
||||
"Automatic recall runs for long-term memory. Use manual searches when the injected context is not enough.",
|
||||
);
|
||||
}
|
||||
|
||||
parts.push(
|
||||
"When calling memory_search, ALWAYS rewrite the query. NEVER pass the user's raw message.",
|
||||
);
|
||||
parts.push(
|
||||
"Convert the request into 3-6 factual keywords that match stored memory language: user, decided, prefers, rule, configured, based in, plus the concrete nouns and names from the request.",
|
||||
);
|
||||
parts.push(
|
||||
'WRONG: memory_search("Who was that nutritionist my wife recommended?")',
|
||||
);
|
||||
parts.push(
|
||||
'RIGHT: memory_search("nutritionist wife recommended relationship")',
|
||||
);
|
||||
parts.push('WRONG: memory_search("What timezone am I in?")');
|
||||
parts.push('RIGHT: memory_search("user timezone location based")');
|
||||
// Intentionally omitted from the compact path: ENTITY SCOPING and SEARCH SCOPE.
|
||||
// loadTriagePrompt() keeps the full explanatory sections for the non-compact path.
|
||||
parts.push(
|
||||
'Scope: use "long-term" for durable user context, "session" for this conversation, and "all" only when you truly need both.',
|
||||
);
|
||||
parts.push(
|
||||
"When the request implies a time range or category, add filters or categories instead of broadening the query text.",
|
||||
);
|
||||
}
|
||||
|
||||
parts.push("</memory-system>");
|
||||
return parts.join("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Load the dream skill prompt for consolidation sessions.
|
||||
*/
|
||||
|
||||
@@ -71,7 +71,10 @@
|
||||
},
|
||||
"pnpm": {
|
||||
"overrides": {
|
||||
"uuid@<11.1.1": ">=11.1.1"
|
||||
"form-data@<4.0.6": ">=4.0.6",
|
||||
"uuid@<11.1.1": ">=11.1.1",
|
||||
"esbuild": ">=0.28.1",
|
||||
"undici@>=8.0.0 <8.5.0": ">=8.5.0"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+200
-133
@@ -5,7 +5,10 @@ settings:
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
form-data@<4.0.6: '>=4.0.6'
|
||||
uuid@<11.1.1: '>=11.1.1'
|
||||
esbuild: '>=0.28.1'
|
||||
undici@>=8.0.0 <8.5.0: '>=8.5.0'
|
||||
|
||||
importers:
|
||||
|
||||
@@ -13,7 +16,7 @@ importers:
|
||||
dependencies:
|
||||
mem0ai:
|
||||
specifier: ^3.0.7
|
||||
version: 3.0.7(@anthropic-ai/sdk@0.91.1(zod@3.25.76))(@azure/identity@4.13.1)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260610.1)(@google/genai@1.52.0)(@langchain/core@1.1.48(openai@6.26.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0))(@mistralai/mistralai@2.2.1)(@qdrant/js-client-rest@1.18.0(typescript@6.0.3))(@supabase/supabase-js@2.108.1)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.10.0)(cloudflare@4.5.0)(compromise@14.15.1)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.21.0)(redis@5.12.1)(ws@8.21.0)
|
||||
version: 3.0.7(@anthropic-ai/sdk@0.91.1(zod@3.25.76))(@azure/identity@4.13.1)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260610.1)(@google/genai@1.52.0)(@langchain/core@1.1.48(openai@6.26.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0))(@mistralai/mistralai@2.2.1)(@qdrant/js-client-rest@1.18.0(typescript@6.0.3))(@supabase/supabase-js@2.108.1)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.10.0)(cloudflare@4.5.0)(compromise@14.15.1)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.11.3)(redis@5.12.1)(ws@8.21.0)
|
||||
devDependencies:
|
||||
'@earendil-works/pi-ai':
|
||||
specifier: ^0.79.0
|
||||
@@ -35,7 +38,7 @@ importers:
|
||||
version: 6.0.3
|
||||
vitest:
|
||||
specifier: ^4.1.7
|
||||
version: 4.1.8(@types/node@25.9.2)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(jiti@2.7.0)(yaml@2.9.0))
|
||||
version: 4.1.8(@types/node@25.9.2)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))
|
||||
|
||||
packages:
|
||||
|
||||
@@ -253,158 +256,158 @@ packages:
|
||||
'@emnapi/wasi-threads@1.2.1':
|
||||
resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==}
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.7':
|
||||
resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
|
||||
'@esbuild/aix-ppc64@0.28.1':
|
||||
resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/android-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
|
||||
'@esbuild/android-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.27.7':
|
||||
resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}
|
||||
'@esbuild/android-arm@0.28.1':
|
||||
resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.27.7':
|
||||
resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}
|
||||
'@esbuild/android-x64@0.28.1':
|
||||
resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
|
||||
'@esbuild/darwin-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.27.7':
|
||||
resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}
|
||||
'@esbuild/darwin-x64@0.28.1':
|
||||
resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
|
||||
'@esbuild/freebsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}
|
||||
'@esbuild/freebsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
|
||||
'@esbuild/linux-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.27.7':
|
||||
resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}
|
||||
'@esbuild/linux-arm@0.28.1':
|
||||
resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.27.7':
|
||||
resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}
|
||||
'@esbuild/linux-ia32@0.28.1':
|
||||
resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.27.7':
|
||||
resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}
|
||||
'@esbuild/linux-loong64@0.28.1':
|
||||
resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.7':
|
||||
resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}
|
||||
'@esbuild/linux-mips64el@0.28.1':
|
||||
resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.7':
|
||||
resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}
|
||||
'@esbuild/linux-ppc64@0.28.1':
|
||||
resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.7':
|
||||
resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}
|
||||
'@esbuild/linux-riscv64@0.28.1':
|
||||
resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.27.7':
|
||||
resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}
|
||||
'@esbuild/linux-s390x@0.28.1':
|
||||
resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.27.7':
|
||||
resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}
|
||||
'@esbuild/linux-x64@0.28.1':
|
||||
resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
|
||||
'@esbuild/netbsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}
|
||||
'@esbuild/netbsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
|
||||
'@esbuild/openbsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}
|
||||
'@esbuild/openbsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
|
||||
'@esbuild/openharmony-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@esbuild/sunos-x64@0.27.7':
|
||||
resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
|
||||
'@esbuild/sunos-x64@0.28.1':
|
||||
resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
|
||||
'@esbuild/win32-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.27.7':
|
||||
resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}
|
||||
'@esbuild/win32-ia32@0.28.1':
|
||||
resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.27.7':
|
||||
resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}
|
||||
'@esbuild/win32-x64@0.28.1':
|
||||
resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@@ -469,30 +472,35 @@ packages:
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@mariozechner/clipboard-linux-arm64-musl@0.3.9':
|
||||
resolution: {integrity: sha512-AGuJdgKsmJdm4Pych7kv3sqe591ERRaAHW3xjLooiFzn8J+PxUyof++7YZrB5Y5tpnTO+K18Og3taj2NpluCRQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@mariozechner/clipboard-linux-riscv64-gnu@0.3.9':
|
||||
resolution: {integrity: sha512-DXBEAiuMpk7dhS1a9NzNxVAFi1vaKoPu7rQNgY8LIDLGrK3lnIp3nT10DUum+PKVJoJppIP+NAA8IZe4DMNDPw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@mariozechner/clipboard-linux-x64-gnu@0.3.9':
|
||||
resolution: {integrity: sha512-WORrMLd6EpElEME7JRKfSaY34nW1P5LbdgK5YNCS1ncG2LqmITsSMEJ8nh2mpvxb3TxqbOOKgY7k9eMJYlW9Mw==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@mariozechner/clipboard-linux-x64-musl@0.3.9':
|
||||
resolution: {integrity: sha512-/DHn+1DrfL6oRaPPWXaOKvonFFrni666fxd+zFqiQEfvBH0tsHVWjq9iqBk0oDp0qaPA72lIMy5BptxISBEhZQ==}
|
||||
engines: {node: '>= 10'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@mariozechner/clipboard-win32-arm64-msvc@0.3.9':
|
||||
resolution: {integrity: sha512-O5FHD3ErkMwMhNzAfu3ggy0ug4z7btZuoQgwwxlzPrwV2bxlD6WDpqBY4NCgICAgZdDKdp+loUEKVAVt8aYnhQ==}
|
||||
@@ -639,36 +647,42 @@ packages:
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-arm64-musl@1.0.3':
|
||||
resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rolldown/binding-linux-ppc64-gnu@1.0.3':
|
||||
resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-s390x-gnu@1.0.3':
|
||||
resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-x64-gnu@1.0.3':
|
||||
resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rolldown/binding-linux-x64-musl@1.0.3':
|
||||
resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rolldown/binding-openharmony-arm64@1.0.3':
|
||||
resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==}
|
||||
@@ -730,66 +744,79 @@ packages:
|
||||
resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm-musleabihf@4.61.1':
|
||||
resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-arm64-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-arm64-musl@4.61.1':
|
||||
resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-loong64-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-loong64-musl@4.61.1':
|
||||
resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-ppc64-musl@4.61.1':
|
||||
resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-riscv64-musl@4.61.1':
|
||||
resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-linux-s390x-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-gnu@4.61.1':
|
||||
resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
'@rollup/rollup-linux-x64-musl@4.61.1':
|
||||
resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
'@rollup/rollup-openbsd-x64@4.61.1':
|
||||
resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==}
|
||||
@@ -1080,6 +1107,10 @@ packages:
|
||||
buffer-equal-constant-time@1.0.1:
|
||||
resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==}
|
||||
|
||||
buffer-writer@2.0.0:
|
||||
resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==}
|
||||
engines: {node: '>=4'}
|
||||
|
||||
buffer@5.7.1:
|
||||
resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==}
|
||||
|
||||
@@ -1091,7 +1122,7 @@ packages:
|
||||
resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
peerDependencies:
|
||||
esbuild: '>=0.18'
|
||||
esbuild: '>=0.28.1'
|
||||
|
||||
cac@6.7.14:
|
||||
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||
@@ -1259,8 +1290,8 @@ packages:
|
||||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
esbuild@0.27.7:
|
||||
resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
|
||||
esbuild@0.28.1:
|
||||
resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
@@ -1339,8 +1370,8 @@ packages:
|
||||
form-data-encoder@1.7.2:
|
||||
resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
|
||||
|
||||
form-data@4.0.5:
|
||||
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
||||
form-data@4.0.6:
|
||||
resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
formdata-node@4.4.1:
|
||||
@@ -1600,24 +1631,28 @@ packages:
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-arm64-musl@1.32.0:
|
||||
resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-linux-x64-gnu@1.32.0:
|
||||
resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [glibc]
|
||||
|
||||
lightningcss-linux-x64-musl@1.32.0:
|
||||
resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
libc: [musl]
|
||||
|
||||
lightningcss-win32-arm64-msvc@1.32.0:
|
||||
resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==}
|
||||
@@ -1901,6 +1936,9 @@ packages:
|
||||
resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
packet-reader@1.0.0:
|
||||
resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==}
|
||||
|
||||
partial-json@0.1.7:
|
||||
resolution: {integrity: sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==}
|
||||
|
||||
@@ -1949,6 +1987,15 @@ packages:
|
||||
resolution: {integrity: sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
pg@8.11.3:
|
||||
resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==}
|
||||
engines: {node: '>= 8.0.0'}
|
||||
peerDependencies:
|
||||
pg-native: '>=3.0.1'
|
||||
peerDependenciesMeta:
|
||||
pg-native:
|
||||
optional: true
|
||||
|
||||
pg@8.21.0:
|
||||
resolution: {integrity: sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==}
|
||||
engines: {node: '>= 16.0.0'}
|
||||
@@ -2308,8 +2355,8 @@ packages:
|
||||
resolution: {integrity: sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==}
|
||||
engines: {node: '>=18.17'}
|
||||
|
||||
undici@8.3.0:
|
||||
resolution: {integrity: sha512-TkUDgb6tl7KOGZ+7e8E3d2FYgUQgF6z5YypqjWmixVQSQERFcVrVg0ySADm2LVLRh5ljAaHTCR5Fmz3Q34rB7Q==}
|
||||
undici@8.5.0:
|
||||
resolution: {integrity: sha512-xamtWoB1EshgjpmlXd7GGm2VfdDtw1+rD8uhry8pSNW3If6S8E0m2T2+orSKeZXEn/aPJMviCpDBA65WJt8zhg==}
|
||||
engines: {node: '>=22.19.0'}
|
||||
|
||||
util-deprecate@1.0.2:
|
||||
@@ -2330,7 +2377,7 @@ packages:
|
||||
peerDependencies:
|
||||
'@types/node': ^20.19.0 || >=22.12.0
|
||||
'@vitejs/devtools': ^0.1.18
|
||||
esbuild: ^0.27.0 || ^0.28.0
|
||||
esbuild: '>=0.28.1'
|
||||
jiti: '>=1.21.0'
|
||||
less: ^4.0.0
|
||||
sass: ^1.70.0
|
||||
@@ -2892,7 +2939,7 @@ snapshots:
|
||||
minimatch: 10.2.5
|
||||
proper-lockfile: 4.1.2
|
||||
typebox: 1.1.38
|
||||
undici: 8.3.0
|
||||
undici: 8.5.0
|
||||
yaml: 2.9.0
|
||||
optionalDependencies:
|
||||
'@mariozechner/clipboard': 0.3.9
|
||||
@@ -2925,82 +2972,82 @@ snapshots:
|
||||
tslib: 2.8.1
|
||||
optional: true
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.7':
|
||||
'@esbuild/aix-ppc64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.27.7':
|
||||
'@esbuild/android-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.27.7':
|
||||
'@esbuild/android-arm@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.27.7':
|
||||
'@esbuild/android-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.7':
|
||||
'@esbuild/darwin-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.27.7':
|
||||
'@esbuild/darwin-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.7':
|
||||
'@esbuild/freebsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.7':
|
||||
'@esbuild/freebsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.27.7':
|
||||
'@esbuild/linux-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.27.7':
|
||||
'@esbuild/linux-arm@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.27.7':
|
||||
'@esbuild/linux-ia32@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.27.7':
|
||||
'@esbuild/linux-loong64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.7':
|
||||
'@esbuild/linux-mips64el@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.7':
|
||||
'@esbuild/linux-ppc64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.7':
|
||||
'@esbuild/linux-riscv64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.27.7':
|
||||
'@esbuild/linux-s390x@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.27.7':
|
||||
'@esbuild/linux-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.7':
|
||||
'@esbuild/netbsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.7':
|
||||
'@esbuild/netbsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.7':
|
||||
'@esbuild/openbsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.7':
|
||||
'@esbuild/openbsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.7':
|
||||
'@esbuild/openharmony-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.27.7':
|
||||
'@esbuild/sunos-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.27.7':
|
||||
'@esbuild/win32-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.27.7':
|
||||
'@esbuild/win32-ia32@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.27.7':
|
||||
'@esbuild/win32-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@google/genai@1.52.0':
|
||||
@@ -3429,7 +3476,7 @@ snapshots:
|
||||
'@types/node-fetch@2.6.13':
|
||||
dependencies:
|
||||
'@types/node': 25.9.2
|
||||
form-data: 4.0.5
|
||||
form-data: 4.0.6
|
||||
|
||||
'@types/node@18.19.130':
|
||||
dependencies:
|
||||
@@ -3478,13 +3525,13 @@ snapshots:
|
||||
chai: 6.2.2
|
||||
tinyrainbow: 3.1.0
|
||||
|
||||
'@vitest/mocker@4.1.8(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(jiti@2.7.0)(yaml@2.9.0))':
|
||||
'@vitest/mocker@4.1.8(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))':
|
||||
dependencies:
|
||||
'@vitest/spy': 4.1.8
|
||||
estree-walker: 3.0.3
|
||||
magic-string: 0.30.21
|
||||
optionalDependencies:
|
||||
vite: 8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(jiti@2.7.0)(yaml@2.9.0)
|
||||
vite: 8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)
|
||||
|
||||
'@vitest/pretty-format@4.1.8':
|
||||
dependencies:
|
||||
@@ -3553,7 +3600,7 @@ snapshots:
|
||||
axios@1.17.0:
|
||||
dependencies:
|
||||
follow-redirects: 1.16.0
|
||||
form-data: 4.0.5
|
||||
form-data: 4.0.6
|
||||
https-proxy-agent: 5.0.1
|
||||
proxy-from-env: 2.1.0
|
||||
transitivePeerDependencies:
|
||||
@@ -3597,6 +3644,8 @@ snapshots:
|
||||
|
||||
buffer-equal-constant-time@1.0.1: {}
|
||||
|
||||
buffer-writer@2.0.0: {}
|
||||
|
||||
buffer@5.7.1:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
@@ -3606,9 +3655,9 @@ snapshots:
|
||||
dependencies:
|
||||
run-applescript: 7.1.0
|
||||
|
||||
bundle-require@5.1.0(esbuild@0.27.7):
|
||||
bundle-require@5.1.0(esbuild@0.28.1):
|
||||
dependencies:
|
||||
esbuild: 0.27.7
|
||||
esbuild: 0.28.1
|
||||
load-tsconfig: 0.2.5
|
||||
|
||||
cac@6.7.14: {}
|
||||
@@ -3752,34 +3801,34 @@ snapshots:
|
||||
has-tostringtag: 1.0.2
|
||||
hasown: 2.0.4
|
||||
|
||||
esbuild@0.27.7:
|
||||
esbuild@0.28.1:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.27.7
|
||||
'@esbuild/android-arm': 0.27.7
|
||||
'@esbuild/android-arm64': 0.27.7
|
||||
'@esbuild/android-x64': 0.27.7
|
||||
'@esbuild/darwin-arm64': 0.27.7
|
||||
'@esbuild/darwin-x64': 0.27.7
|
||||
'@esbuild/freebsd-arm64': 0.27.7
|
||||
'@esbuild/freebsd-x64': 0.27.7
|
||||
'@esbuild/linux-arm': 0.27.7
|
||||
'@esbuild/linux-arm64': 0.27.7
|
||||
'@esbuild/linux-ia32': 0.27.7
|
||||
'@esbuild/linux-loong64': 0.27.7
|
||||
'@esbuild/linux-mips64el': 0.27.7
|
||||
'@esbuild/linux-ppc64': 0.27.7
|
||||
'@esbuild/linux-riscv64': 0.27.7
|
||||
'@esbuild/linux-s390x': 0.27.7
|
||||
'@esbuild/linux-x64': 0.27.7
|
||||
'@esbuild/netbsd-arm64': 0.27.7
|
||||
'@esbuild/netbsd-x64': 0.27.7
|
||||
'@esbuild/openbsd-arm64': 0.27.7
|
||||
'@esbuild/openbsd-x64': 0.27.7
|
||||
'@esbuild/openharmony-arm64': 0.27.7
|
||||
'@esbuild/sunos-x64': 0.27.7
|
||||
'@esbuild/win32-arm64': 0.27.7
|
||||
'@esbuild/win32-ia32': 0.27.7
|
||||
'@esbuild/win32-x64': 0.27.7
|
||||
'@esbuild/aix-ppc64': 0.28.1
|
||||
'@esbuild/android-arm': 0.28.1
|
||||
'@esbuild/android-arm64': 0.28.1
|
||||
'@esbuild/android-x64': 0.28.1
|
||||
'@esbuild/darwin-arm64': 0.28.1
|
||||
'@esbuild/darwin-x64': 0.28.1
|
||||
'@esbuild/freebsd-arm64': 0.28.1
|
||||
'@esbuild/freebsd-x64': 0.28.1
|
||||
'@esbuild/linux-arm': 0.28.1
|
||||
'@esbuild/linux-arm64': 0.28.1
|
||||
'@esbuild/linux-ia32': 0.28.1
|
||||
'@esbuild/linux-loong64': 0.28.1
|
||||
'@esbuild/linux-mips64el': 0.28.1
|
||||
'@esbuild/linux-ppc64': 0.28.1
|
||||
'@esbuild/linux-riscv64': 0.28.1
|
||||
'@esbuild/linux-s390x': 0.28.1
|
||||
'@esbuild/linux-x64': 0.28.1
|
||||
'@esbuild/netbsd-arm64': 0.28.1
|
||||
'@esbuild/netbsd-x64': 0.28.1
|
||||
'@esbuild/openbsd-arm64': 0.28.1
|
||||
'@esbuild/openbsd-x64': 0.28.1
|
||||
'@esbuild/openharmony-arm64': 0.28.1
|
||||
'@esbuild/sunos-x64': 0.28.1
|
||||
'@esbuild/win32-arm64': 0.28.1
|
||||
'@esbuild/win32-ia32': 0.28.1
|
||||
'@esbuild/win32-x64': 0.28.1
|
||||
|
||||
escape-string-regexp@2.0.0: {}
|
||||
|
||||
@@ -3844,7 +3893,7 @@ snapshots:
|
||||
|
||||
form-data-encoder@1.7.2: {}
|
||||
|
||||
form-data@4.0.5:
|
||||
form-data@4.0.6:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
@@ -4190,7 +4239,7 @@ snapshots:
|
||||
crypt: 0.0.2
|
||||
is-buffer: 1.1.6
|
||||
|
||||
mem0ai@3.0.7(@anthropic-ai/sdk@0.91.1(zod@3.25.76))(@azure/identity@4.13.1)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260610.1)(@google/genai@1.52.0)(@langchain/core@1.1.48(openai@6.26.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0))(@mistralai/mistralai@2.2.1)(@qdrant/js-client-rest@1.18.0(typescript@6.0.3))(@supabase/supabase-js@2.108.1)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.10.0)(cloudflare@4.5.0)(compromise@14.15.1)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.21.0)(redis@5.12.1)(ws@8.21.0):
|
||||
mem0ai@3.0.7(@anthropic-ai/sdk@0.91.1(zod@3.25.76))(@azure/identity@4.13.1)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260610.1)(@google/genai@1.52.0)(@langchain/core@1.1.48(openai@6.26.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0))(@mistralai/mistralai@2.2.1)(@qdrant/js-client-rest@1.18.0(typescript@6.0.3))(@supabase/supabase-js@2.108.1)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.10.0)(cloudflare@4.5.0)(compromise@14.15.1)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.11.3)(redis@5.12.1)(ws@8.21.0):
|
||||
dependencies:
|
||||
'@anthropic-ai/sdk': 0.91.1(zod@3.25.76)
|
||||
'@azure/identity': 4.13.1
|
||||
@@ -4211,7 +4260,7 @@ snapshots:
|
||||
natural: 8.1.1
|
||||
ollama: 0.5.18
|
||||
openai: 4.104.0(ws@8.21.0)(zod@3.25.76)
|
||||
pg: 8.21.0
|
||||
pg: 8.11.3
|
||||
redis: 5.12.1
|
||||
uuid: 14.0.0
|
||||
zod: 3.25.76
|
||||
@@ -4402,6 +4451,8 @@ snapshots:
|
||||
dependencies:
|
||||
p-finally: 1.0.0
|
||||
|
||||
packet-reader@1.0.0: {}
|
||||
|
||||
partial-json@0.1.7: {}
|
||||
|
||||
path-expression-matcher@1.5.0: {}
|
||||
@@ -4424,6 +4475,10 @@ snapshots:
|
||||
|
||||
pg-numeric@1.0.2: {}
|
||||
|
||||
pg-pool@3.14.0(pg@8.11.3):
|
||||
dependencies:
|
||||
pg: 8.11.3
|
||||
|
||||
pg-pool@3.14.0(pg@8.21.0):
|
||||
dependencies:
|
||||
pg: 8.21.0
|
||||
@@ -4448,6 +4503,18 @@ snapshots:
|
||||
postgres-interval: 3.0.0
|
||||
postgres-range: 1.1.4
|
||||
|
||||
pg@8.11.3:
|
||||
dependencies:
|
||||
buffer-writer: 2.0.0
|
||||
packet-reader: 1.0.0
|
||||
pg-connection-string: 2.13.0
|
||||
pg-pool: 3.14.0(pg@8.11.3)
|
||||
pg-protocol: 1.14.0
|
||||
pg-types: 2.2.0
|
||||
pgpass: 1.0.5
|
||||
optionalDependencies:
|
||||
pg-cloudflare: 1.4.0
|
||||
|
||||
pg@8.21.0:
|
||||
dependencies:
|
||||
pg-connection-string: 2.13.0
|
||||
@@ -4783,12 +4850,12 @@ snapshots:
|
||||
|
||||
tsup@8.5.1(jiti@2.7.0)(postcss@8.5.15)(typescript@6.0.3)(yaml@2.9.0):
|
||||
dependencies:
|
||||
bundle-require: 5.1.0(esbuild@0.27.7)
|
||||
bundle-require: 5.1.0(esbuild@0.28.1)
|
||||
cac: 6.7.14
|
||||
chokidar: 4.0.3
|
||||
consola: 3.4.2
|
||||
debug: 4.4.3
|
||||
esbuild: 0.27.7
|
||||
esbuild: 0.28.1
|
||||
fix-dts-default-cjs-exports: 1.0.1
|
||||
joycon: 3.1.1
|
||||
picocolors: 1.1.1
|
||||
@@ -4829,7 +4896,7 @@ snapshots:
|
||||
|
||||
undici@6.26.0: {}
|
||||
|
||||
undici@8.3.0: {}
|
||||
undici@8.5.0: {}
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
@@ -4837,7 +4904,7 @@ snapshots:
|
||||
|
||||
uuid@14.0.0: {}
|
||||
|
||||
vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(jiti@2.7.0)(yaml@2.9.0):
|
||||
vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0):
|
||||
dependencies:
|
||||
lightningcss: 1.32.0
|
||||
picomatch: 4.0.4
|
||||
@@ -4846,15 +4913,15 @@ snapshots:
|
||||
tinyglobby: 0.2.17
|
||||
optionalDependencies:
|
||||
'@types/node': 25.9.2
|
||||
esbuild: 0.27.7
|
||||
esbuild: 0.28.1
|
||||
fsevents: 2.3.3
|
||||
jiti: 2.7.0
|
||||
yaml: 2.9.0
|
||||
|
||||
vitest@4.1.8(@types/node@25.9.2)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(jiti@2.7.0)(yaml@2.9.0)):
|
||||
vitest@4.1.8(@types/node@25.9.2)(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)):
|
||||
dependencies:
|
||||
'@vitest/expect': 4.1.8
|
||||
'@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(jiti@2.7.0)(yaml@2.9.0))
|
||||
'@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0))
|
||||
'@vitest/pretty-format': 4.1.8
|
||||
'@vitest/runner': 4.1.8
|
||||
'@vitest/snapshot': 4.1.8
|
||||
@@ -4871,7 +4938,7 @@ snapshots:
|
||||
tinyexec: 1.2.4
|
||||
tinyglobby: 0.2.17
|
||||
tinyrainbow: 3.1.0
|
||||
vite: 8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(jiti@2.7.0)(yaml@2.9.0)
|
||||
vite: 8.0.16(@types/node@25.9.2)(esbuild@0.28.1)(jiti@2.7.0)(yaml@2.9.0)
|
||||
why-is-node-running: 2.3.0
|
||||
optionalDependencies:
|
||||
'@types/node': 25.9.2
|
||||
|
||||
@@ -2,4 +2,7 @@ packages:
|
||||
- '.'
|
||||
|
||||
overrides:
|
||||
"form-data@<4.0.6": ">=4.0.6"
|
||||
"uuid@<11.1.1": ">=11.1.1"
|
||||
"esbuild": ">=0.28.1"
|
||||
"undici@>=8.0.0 <8.5.0": ">=8.5.0"
|
||||
|
||||
@@ -80,11 +80,13 @@
|
||||
],
|
||||
"overrides": {
|
||||
"glob@>=10.2.0 <10.5.0": "^10.5.0",
|
||||
"js-yaml@<=4.1.1": ">=4.2.0",
|
||||
"minimatch@<3.1.3": "^3.1.3",
|
||||
"minimatch@>=5.0.0 <5.1.8": "^5.1.8",
|
||||
"minimatch@>=9.0.0 <9.0.7": "^9.0.7",
|
||||
"picomatch@<2.3.2": "^2.3.2",
|
||||
"rollup@>=4.0.0 <4.59.0": "^4.59.0"
|
||||
"rollup@>=4.0.0 <4.59.0": "^4.59.0",
|
||||
"esbuild": ">=0.28.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+125
-138
@@ -6,11 +6,13 @@ settings:
|
||||
|
||||
overrides:
|
||||
glob@>=10.2.0 <10.5.0: ^10.5.0
|
||||
js-yaml@<=4.1.1: '>=4.2.0'
|
||||
minimatch@<3.1.3: ^3.1.3
|
||||
minimatch@>=5.0.0 <5.1.8: ^5.1.8
|
||||
minimatch@>=9.0.0 <9.0.7: ^9.0.7
|
||||
picomatch@<2.3.2: ^2.3.2
|
||||
rollup@>=4.0.0 <4.59.0: ^4.59.0
|
||||
esbuild: '>=0.28.1'
|
||||
|
||||
importers:
|
||||
|
||||
@@ -67,7 +69,7 @@ importers:
|
||||
version: 3.1.14
|
||||
ts-jest:
|
||||
specifier: ^29.4.11
|
||||
version: 29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(esbuild@0.27.7)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.19.130)(ts-node@10.9.2(@types/node@18.19.130)(typescript@5.9.3)))(typescript@5.9.3)
|
||||
version: 29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(esbuild@0.28.1)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.19.130)(ts-node@10.9.2(@types/node@18.19.130)(typescript@5.9.3)))(typescript@5.9.3)
|
||||
ts-node:
|
||||
specifier: ^10.9.2
|
||||
version: 10.9.2(@types/node@18.19.130)(typescript@5.9.3)
|
||||
@@ -303,158 +305,158 @@ packages:
|
||||
resolution: {integrity: sha512-0dEVyRLM/lG4gp1R/Ik5bfPl/1wX00xFwd5KcNH602tzBa09oF7pbTKETEhR1GjZ75K6OJnYFu8II2dyMhONMw==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.7':
|
||||
resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
|
||||
'@esbuild/aix-ppc64@0.28.1':
|
||||
resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/android-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
|
||||
'@esbuild/android-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.27.7':
|
||||
resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}
|
||||
'@esbuild/android-arm@0.28.1':
|
||||
resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.27.7':
|
||||
resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}
|
||||
'@esbuild/android-x64@0.28.1':
|
||||
resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
|
||||
'@esbuild/darwin-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.27.7':
|
||||
resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}
|
||||
'@esbuild/darwin-x64@0.28.1':
|
||||
resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
|
||||
'@esbuild/freebsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}
|
||||
'@esbuild/freebsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
|
||||
'@esbuild/linux-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.27.7':
|
||||
resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}
|
||||
'@esbuild/linux-arm@0.28.1':
|
||||
resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.27.7':
|
||||
resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}
|
||||
'@esbuild/linux-ia32@0.28.1':
|
||||
resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.27.7':
|
||||
resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}
|
||||
'@esbuild/linux-loong64@0.28.1':
|
||||
resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.7':
|
||||
resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}
|
||||
'@esbuild/linux-mips64el@0.28.1':
|
||||
resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.7':
|
||||
resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}
|
||||
'@esbuild/linux-ppc64@0.28.1':
|
||||
resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.7':
|
||||
resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}
|
||||
'@esbuild/linux-riscv64@0.28.1':
|
||||
resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.27.7':
|
||||
resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}
|
||||
'@esbuild/linux-s390x@0.28.1':
|
||||
resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.27.7':
|
||||
resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}
|
||||
'@esbuild/linux-x64@0.28.1':
|
||||
resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
|
||||
'@esbuild/netbsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}
|
||||
'@esbuild/netbsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
|
||||
'@esbuild/openbsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}
|
||||
'@esbuild/openbsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
|
||||
'@esbuild/openharmony-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@esbuild/sunos-x64@0.27.7':
|
||||
resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
|
||||
'@esbuild/sunos-x64@0.28.1':
|
||||
resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
|
||||
'@esbuild/win32-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.27.7':
|
||||
resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}
|
||||
'@esbuild/win32-ia32@0.28.1':
|
||||
resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.27.7':
|
||||
resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}
|
||||
'@esbuild/win32-x64@0.28.1':
|
||||
resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@@ -792,8 +794,8 @@ packages:
|
||||
arg@4.1.3:
|
||||
resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==}
|
||||
|
||||
argparse@1.0.10:
|
||||
resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==}
|
||||
argparse@2.0.1:
|
||||
resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==}
|
||||
|
||||
babel-jest@29.7.0:
|
||||
resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==}
|
||||
@@ -866,7 +868,7 @@ packages:
|
||||
resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
peerDependencies:
|
||||
esbuild: '>=0.18'
|
||||
esbuild: '>=0.28.1'
|
||||
|
||||
cac@6.7.14:
|
||||
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||
@@ -1011,8 +1013,8 @@ packages:
|
||||
resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
esbuild@0.27.7:
|
||||
resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
|
||||
esbuild@0.28.1:
|
||||
resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
@@ -1024,11 +1026,6 @@ packages:
|
||||
resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
esprima@4.0.1:
|
||||
resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==}
|
||||
engines: {node: '>=4'}
|
||||
hasBin: true
|
||||
|
||||
eventsource-parser@3.1.0:
|
||||
resolution: {integrity: sha512-kJezFj9YFAMLeORyi7aCLxLbD5/qWMQnoMVlVPyHIll7lgRJCc3JVln9Vgl9nwQi0YkMnhdGTMNn7CkRRAptMg==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
@@ -1350,8 +1347,8 @@ packages:
|
||||
js-tokens@4.0.0:
|
||||
resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==}
|
||||
|
||||
js-yaml@3.14.2:
|
||||
resolution: {integrity: sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==}
|
||||
js-yaml@4.2.0:
|
||||
resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==}
|
||||
hasBin: true
|
||||
|
||||
jsesc@3.1.0:
|
||||
@@ -1653,9 +1650,6 @@ packages:
|
||||
resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==}
|
||||
engines: {node: '>= 12'}
|
||||
|
||||
sprintf-js@1.0.3:
|
||||
resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==}
|
||||
engines: {node: '>=10'}
|
||||
@@ -2142,82 +2136,82 @@ snapshots:
|
||||
dependencies:
|
||||
'@edge-runtime/primitives': 4.1.0
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.7':
|
||||
'@esbuild/aix-ppc64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.27.7':
|
||||
'@esbuild/android-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.27.7':
|
||||
'@esbuild/android-arm@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.27.7':
|
||||
'@esbuild/android-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.7':
|
||||
'@esbuild/darwin-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.27.7':
|
||||
'@esbuild/darwin-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.7':
|
||||
'@esbuild/freebsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.7':
|
||||
'@esbuild/freebsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.27.7':
|
||||
'@esbuild/linux-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.27.7':
|
||||
'@esbuild/linux-arm@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.27.7':
|
||||
'@esbuild/linux-ia32@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.27.7':
|
||||
'@esbuild/linux-loong64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.7':
|
||||
'@esbuild/linux-mips64el@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.7':
|
||||
'@esbuild/linux-ppc64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.7':
|
||||
'@esbuild/linux-riscv64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.27.7':
|
||||
'@esbuild/linux-s390x@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.27.7':
|
||||
'@esbuild/linux-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.7':
|
||||
'@esbuild/netbsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.7':
|
||||
'@esbuild/netbsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.7':
|
||||
'@esbuild/openbsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.7':
|
||||
'@esbuild/openbsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.7':
|
||||
'@esbuild/openharmony-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.27.7':
|
||||
'@esbuild/sunos-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.27.7':
|
||||
'@esbuild/win32-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.27.7':
|
||||
'@esbuild/win32-ia32@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.27.7':
|
||||
'@esbuild/win32-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@istanbuljs/load-nyc-config@1.1.0':
|
||||
@@ -2225,7 +2219,7 @@ snapshots:
|
||||
camelcase: 5.3.1
|
||||
find-up: 4.1.0
|
||||
get-package-type: 0.1.0
|
||||
js-yaml: 3.14.2
|
||||
js-yaml: 4.2.0
|
||||
resolve-from: 5.0.0
|
||||
|
||||
'@istanbuljs/schema@0.1.6': {}
|
||||
@@ -2604,9 +2598,7 @@ snapshots:
|
||||
|
||||
arg@4.1.3: {}
|
||||
|
||||
argparse@1.0.10:
|
||||
dependencies:
|
||||
sprintf-js: 1.0.3
|
||||
argparse@2.0.1: {}
|
||||
|
||||
babel-jest@29.7.0(@babel/core@7.29.7):
|
||||
dependencies:
|
||||
@@ -2702,9 +2694,9 @@ snapshots:
|
||||
|
||||
buffer-from@1.1.2: {}
|
||||
|
||||
bundle-require@5.1.0(esbuild@0.27.7):
|
||||
bundle-require@5.1.0(esbuild@0.28.1):
|
||||
dependencies:
|
||||
esbuild: 0.27.7
|
||||
esbuild: 0.28.1
|
||||
load-tsconfig: 0.2.5
|
||||
|
||||
cac@6.7.14: {}
|
||||
@@ -2823,41 +2815,39 @@ snapshots:
|
||||
|
||||
es-errors@1.3.0: {}
|
||||
|
||||
esbuild@0.27.7:
|
||||
esbuild@0.28.1:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.27.7
|
||||
'@esbuild/android-arm': 0.27.7
|
||||
'@esbuild/android-arm64': 0.27.7
|
||||
'@esbuild/android-x64': 0.27.7
|
||||
'@esbuild/darwin-arm64': 0.27.7
|
||||
'@esbuild/darwin-x64': 0.27.7
|
||||
'@esbuild/freebsd-arm64': 0.27.7
|
||||
'@esbuild/freebsd-x64': 0.27.7
|
||||
'@esbuild/linux-arm': 0.27.7
|
||||
'@esbuild/linux-arm64': 0.27.7
|
||||
'@esbuild/linux-ia32': 0.27.7
|
||||
'@esbuild/linux-loong64': 0.27.7
|
||||
'@esbuild/linux-mips64el': 0.27.7
|
||||
'@esbuild/linux-ppc64': 0.27.7
|
||||
'@esbuild/linux-riscv64': 0.27.7
|
||||
'@esbuild/linux-s390x': 0.27.7
|
||||
'@esbuild/linux-x64': 0.27.7
|
||||
'@esbuild/netbsd-arm64': 0.27.7
|
||||
'@esbuild/netbsd-x64': 0.27.7
|
||||
'@esbuild/openbsd-arm64': 0.27.7
|
||||
'@esbuild/openbsd-x64': 0.27.7
|
||||
'@esbuild/openharmony-arm64': 0.27.7
|
||||
'@esbuild/sunos-x64': 0.27.7
|
||||
'@esbuild/win32-arm64': 0.27.7
|
||||
'@esbuild/win32-ia32': 0.27.7
|
||||
'@esbuild/win32-x64': 0.27.7
|
||||
'@esbuild/aix-ppc64': 0.28.1
|
||||
'@esbuild/android-arm': 0.28.1
|
||||
'@esbuild/android-arm64': 0.28.1
|
||||
'@esbuild/android-x64': 0.28.1
|
||||
'@esbuild/darwin-arm64': 0.28.1
|
||||
'@esbuild/darwin-x64': 0.28.1
|
||||
'@esbuild/freebsd-arm64': 0.28.1
|
||||
'@esbuild/freebsd-x64': 0.28.1
|
||||
'@esbuild/linux-arm': 0.28.1
|
||||
'@esbuild/linux-arm64': 0.28.1
|
||||
'@esbuild/linux-ia32': 0.28.1
|
||||
'@esbuild/linux-loong64': 0.28.1
|
||||
'@esbuild/linux-mips64el': 0.28.1
|
||||
'@esbuild/linux-ppc64': 0.28.1
|
||||
'@esbuild/linux-riscv64': 0.28.1
|
||||
'@esbuild/linux-s390x': 0.28.1
|
||||
'@esbuild/linux-x64': 0.28.1
|
||||
'@esbuild/netbsd-arm64': 0.28.1
|
||||
'@esbuild/netbsd-x64': 0.28.1
|
||||
'@esbuild/openbsd-arm64': 0.28.1
|
||||
'@esbuild/openbsd-x64': 0.28.1
|
||||
'@esbuild/openharmony-arm64': 0.28.1
|
||||
'@esbuild/sunos-x64': 0.28.1
|
||||
'@esbuild/win32-arm64': 0.28.1
|
||||
'@esbuild/win32-ia32': 0.28.1
|
||||
'@esbuild/win32-x64': 0.28.1
|
||||
|
||||
escalade@3.2.0: {}
|
||||
|
||||
escape-string-regexp@2.0.0: {}
|
||||
|
||||
esprima@4.0.1: {}
|
||||
|
||||
eventsource-parser@3.1.0: {}
|
||||
|
||||
execa@5.1.1:
|
||||
@@ -3354,10 +3344,9 @@ snapshots:
|
||||
|
||||
js-tokens@4.0.0: {}
|
||||
|
||||
js-yaml@3.14.2:
|
||||
js-yaml@4.2.0:
|
||||
dependencies:
|
||||
argparse: 1.0.10
|
||||
esprima: 4.0.1
|
||||
argparse: 2.0.1
|
||||
|
||||
jsesc@3.1.0: {}
|
||||
|
||||
@@ -3627,8 +3616,6 @@ snapshots:
|
||||
|
||||
source-map@0.7.6: {}
|
||||
|
||||
sprintf-js@1.0.3: {}
|
||||
|
||||
stack-utils@2.0.6:
|
||||
dependencies:
|
||||
escape-string-regexp: 2.0.0
|
||||
@@ -3711,7 +3698,7 @@ snapshots:
|
||||
|
||||
ts-interface-checker@0.1.13: {}
|
||||
|
||||
ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(esbuild@0.27.7)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.19.130)(ts-node@10.9.2(@types/node@18.19.130)(typescript@5.9.3)))(typescript@5.9.3):
|
||||
ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(esbuild@0.28.1)(jest-util@29.7.0)(jest@29.7.0(@types/node@18.19.130)(ts-node@10.9.2(@types/node@18.19.130)(typescript@5.9.3)))(typescript@5.9.3):
|
||||
dependencies:
|
||||
bs-logger: 0.2.6
|
||||
fast-json-stable-stringify: 2.1.0
|
||||
@@ -3729,7 +3716,7 @@ snapshots:
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
babel-jest: 29.7.0(@babel/core@7.29.7)
|
||||
esbuild: 0.27.7
|
||||
esbuild: 0.28.1
|
||||
jest-util: 29.7.0
|
||||
|
||||
ts-node@10.9.2(@types/node@18.19.130)(typescript@5.9.3):
|
||||
@@ -3752,12 +3739,12 @@ snapshots:
|
||||
|
||||
tsup@8.5.1(typescript@5.9.3):
|
||||
dependencies:
|
||||
bundle-require: 5.1.0(esbuild@0.27.7)
|
||||
bundle-require: 5.1.0(esbuild@0.28.1)
|
||||
cac: 6.7.14
|
||||
chokidar: 4.0.3
|
||||
consola: 3.4.2
|
||||
debug: 4.4.3(supports-color@5.5.0)
|
||||
esbuild: 0.27.7
|
||||
esbuild: 0.28.1
|
||||
fix-dts-default-cjs-exports: 1.0.1
|
||||
joycon: 3.1.1
|
||||
picocolors: 1.1.1
|
||||
|
||||
@@ -7,8 +7,10 @@ onlyBuiltDependencies:
|
||||
|
||||
overrides:
|
||||
"glob@>=10.2.0 <10.5.0": "^10.5.0"
|
||||
"js-yaml@<=4.1.1": ">=4.2.0"
|
||||
"minimatch@<3.1.3": "^3.1.3"
|
||||
"minimatch@>=5.0.0 <5.1.8": "^5.1.8"
|
||||
"minimatch@>=9.0.0 <9.0.7": "^9.0.7"
|
||||
"picomatch@<2.3.2": "^2.3.2"
|
||||
"rollup@>=4.0.0 <4.59.0": "^4.59.0"
|
||||
"esbuild": ">=0.28.1"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "mem0ai",
|
||||
"version": "3.0.8",
|
||||
"version": "3.0.9",
|
||||
"description": "The Memory Layer For Your AI Apps",
|
||||
"main": "./dist/index.js",
|
||||
"module": "./dist/index.mjs",
|
||||
@@ -139,6 +139,7 @@
|
||||
"better-sqlite3"
|
||||
],
|
||||
"overrides": {
|
||||
"form-data@<4.0.6": ">=4.0.6",
|
||||
"picomatch@<2.3.2": "^2.3.2",
|
||||
"picomatch@>=4.0.0 <4.0.4": "^4.0.4",
|
||||
"jws@4.0.0": "4.0.1",
|
||||
@@ -154,7 +155,8 @@
|
||||
"rollup@>=4.0.0 <4.59.0": "^4.59.0",
|
||||
"tar-fs@>=2.0.0 <2.1.4": "^2.1.4",
|
||||
"glob@>=10.2.0 <10.5.0": "^10.5.0",
|
||||
"@modelcontextprotocol/sdk": "^1.25.4"
|
||||
"@modelcontextprotocol/sdk": "^1.25.4",
|
||||
"esbuild": ">=0.28.1"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Generated
+122
-120
@@ -5,6 +5,7 @@ settings:
|
||||
excludeLinksFromLockfile: false
|
||||
|
||||
overrides:
|
||||
form-data@<4.0.6: '>=4.0.6'
|
||||
picomatch@<2.3.2: ^2.3.2
|
||||
picomatch@>=4.0.0 <4.0.4: ^4.0.4
|
||||
jws@4.0.0: 4.0.1
|
||||
@@ -21,6 +22,7 @@ overrides:
|
||||
tar-fs@>=2.0.0 <2.1.4: ^2.1.4
|
||||
glob@>=10.2.0 <10.5.0: ^10.5.0
|
||||
'@modelcontextprotocol/sdk': ^1.25.4
|
||||
esbuild: '>=0.28.1'
|
||||
|
||||
importers:
|
||||
|
||||
@@ -125,7 +127,7 @@ importers:
|
||||
version: 5.0.10
|
||||
ts-jest:
|
||||
specifier: ^29.2.6
|
||||
version: 29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(esbuild@0.27.7)(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)))(typescript@5.5.4)
|
||||
version: 29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(esbuild@0.28.1)(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)))(typescript@5.5.4)
|
||||
ts-node:
|
||||
specifier: ^10.9.2
|
||||
version: 10.9.2(@types/node@22.19.21)(typescript@5.5.4)
|
||||
@@ -375,158 +377,158 @@ packages:
|
||||
resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.7':
|
||||
resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==}
|
||||
'@esbuild/aix-ppc64@0.28.1':
|
||||
resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [aix]
|
||||
|
||||
'@esbuild/android-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==}
|
||||
'@esbuild/android-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-arm@0.27.7':
|
||||
resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==}
|
||||
'@esbuild/android-arm@0.28.1':
|
||||
resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/android-x64@0.27.7':
|
||||
resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==}
|
||||
'@esbuild/android-x64@0.28.1':
|
||||
resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [android]
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==}
|
||||
'@esbuild/darwin-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/darwin-x64@0.27.7':
|
||||
resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==}
|
||||
'@esbuild/darwin-x64@0.28.1':
|
||||
resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [darwin]
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==}
|
||||
'@esbuild/freebsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==}
|
||||
'@esbuild/freebsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [freebsd]
|
||||
|
||||
'@esbuild/linux-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==}
|
||||
'@esbuild/linux-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-arm@0.27.7':
|
||||
resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==}
|
||||
'@esbuild/linux-arm@0.28.1':
|
||||
resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ia32@0.27.7':
|
||||
resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==}
|
||||
'@esbuild/linux-ia32@0.28.1':
|
||||
resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-loong64@0.27.7':
|
||||
resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==}
|
||||
'@esbuild/linux-loong64@0.28.1':
|
||||
resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [loong64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.7':
|
||||
resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==}
|
||||
'@esbuild/linux-mips64el@0.28.1':
|
||||
resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [mips64el]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.7':
|
||||
resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==}
|
||||
'@esbuild/linux-ppc64@0.28.1':
|
||||
resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ppc64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.7':
|
||||
resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==}
|
||||
'@esbuild/linux-riscv64@0.28.1':
|
||||
resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [riscv64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-s390x@0.27.7':
|
||||
resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==}
|
||||
'@esbuild/linux-s390x@0.28.1':
|
||||
resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [s390x]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/linux-x64@0.27.7':
|
||||
resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==}
|
||||
'@esbuild/linux-x64@0.28.1':
|
||||
resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [linux]
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==}
|
||||
'@esbuild/netbsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==}
|
||||
'@esbuild/netbsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [netbsd]
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==}
|
||||
'@esbuild/openbsd-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.7':
|
||||
resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==}
|
||||
'@esbuild/openbsd-x64@0.28.1':
|
||||
resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [openbsd]
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==}
|
||||
'@esbuild/openharmony-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [openharmony]
|
||||
|
||||
'@esbuild/sunos-x64@0.27.7':
|
||||
resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==}
|
||||
'@esbuild/sunos-x64@0.28.1':
|
||||
resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [sunos]
|
||||
|
||||
'@esbuild/win32-arm64@0.27.7':
|
||||
resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==}
|
||||
'@esbuild/win32-arm64@0.28.1':
|
||||
resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-ia32@0.27.7':
|
||||
resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==}
|
||||
'@esbuild/win32-ia32@0.28.1':
|
||||
resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [ia32]
|
||||
os: [win32]
|
||||
|
||||
'@esbuild/win32-x64@0.27.7':
|
||||
resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==}
|
||||
'@esbuild/win32-x64@0.28.1':
|
||||
resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==}
|
||||
engines: {node: '>=18'}
|
||||
cpu: [x64]
|
||||
os: [win32]
|
||||
@@ -1206,7 +1208,7 @@ packages:
|
||||
resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==}
|
||||
engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0}
|
||||
peerDependencies:
|
||||
esbuild: '>=0.18'
|
||||
esbuild: '>=0.28.1'
|
||||
|
||||
cac@6.7.14:
|
||||
resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
|
||||
@@ -1450,8 +1452,8 @@ packages:
|
||||
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
esbuild@0.27.7:
|
||||
resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==}
|
||||
esbuild@0.28.1:
|
||||
resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==}
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
@@ -1553,8 +1555,8 @@ packages:
|
||||
form-data-encoder@1.7.2:
|
||||
resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==}
|
||||
|
||||
form-data@4.0.5:
|
||||
resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==}
|
||||
form-data@4.0.6:
|
||||
resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
formdata-node@4.4.1:
|
||||
@@ -3381,82 +3383,82 @@ snapshots:
|
||||
dependencies:
|
||||
'@jridgewell/trace-mapping': 0.3.9
|
||||
|
||||
'@esbuild/aix-ppc64@0.27.7':
|
||||
'@esbuild/aix-ppc64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm64@0.27.7':
|
||||
'@esbuild/android-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-arm@0.27.7':
|
||||
'@esbuild/android-arm@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/android-x64@0.27.7':
|
||||
'@esbuild/android-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-arm64@0.27.7':
|
||||
'@esbuild/darwin-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/darwin-x64@0.27.7':
|
||||
'@esbuild/darwin-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-arm64@0.27.7':
|
||||
'@esbuild/freebsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/freebsd-x64@0.27.7':
|
||||
'@esbuild/freebsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm64@0.27.7':
|
||||
'@esbuild/linux-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-arm@0.27.7':
|
||||
'@esbuild/linux-arm@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ia32@0.27.7':
|
||||
'@esbuild/linux-ia32@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-loong64@0.27.7':
|
||||
'@esbuild/linux-loong64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-mips64el@0.27.7':
|
||||
'@esbuild/linux-mips64el@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-ppc64@0.27.7':
|
||||
'@esbuild/linux-ppc64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-riscv64@0.27.7':
|
||||
'@esbuild/linux-riscv64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-s390x@0.27.7':
|
||||
'@esbuild/linux-s390x@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/linux-x64@0.27.7':
|
||||
'@esbuild/linux-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-arm64@0.27.7':
|
||||
'@esbuild/netbsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/netbsd-x64@0.27.7':
|
||||
'@esbuild/netbsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-arm64@0.27.7':
|
||||
'@esbuild/openbsd-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openbsd-x64@0.27.7':
|
||||
'@esbuild/openbsd-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/openharmony-arm64@0.27.7':
|
||||
'@esbuild/openharmony-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/sunos-x64@0.27.7':
|
||||
'@esbuild/sunos-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-arm64@0.27.7':
|
||||
'@esbuild/win32-arm64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-ia32@0.27.7':
|
||||
'@esbuild/win32-ia32@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@esbuild/win32-x64@0.27.7':
|
||||
'@esbuild/win32-x64@0.28.1':
|
||||
optional: true
|
||||
|
||||
'@google/genai@1.52.0':
|
||||
@@ -3971,7 +3973,7 @@ snapshots:
|
||||
'@types/node-fetch@2.6.13':
|
||||
dependencies:
|
||||
'@types/node': 22.19.21
|
||||
form-data: 4.0.5
|
||||
form-data: 4.0.6
|
||||
|
||||
'@types/node@18.19.130':
|
||||
dependencies:
|
||||
@@ -4077,7 +4079,7 @@ snapshots:
|
||||
axios@1.17.0:
|
||||
dependencies:
|
||||
follow-redirects: 1.16.0
|
||||
form-data: 4.0.5
|
||||
form-data: 4.0.6
|
||||
https-proxy-agent: 5.0.1
|
||||
proxy-from-env: 2.1.0
|
||||
transitivePeerDependencies:
|
||||
@@ -4218,9 +4220,9 @@ snapshots:
|
||||
dependencies:
|
||||
run-applescript: 7.1.0
|
||||
|
||||
bundle-require@5.1.0(esbuild@0.27.7):
|
||||
bundle-require@5.1.0(esbuild@0.28.1):
|
||||
dependencies:
|
||||
esbuild: 0.27.7
|
||||
esbuild: 0.28.1
|
||||
load-tsconfig: 0.2.5
|
||||
|
||||
cac@6.7.14: {}
|
||||
@@ -4435,34 +4437,34 @@ snapshots:
|
||||
has-tostringtag: 1.0.2
|
||||
hasown: 2.0.4
|
||||
|
||||
esbuild@0.27.7:
|
||||
esbuild@0.28.1:
|
||||
optionalDependencies:
|
||||
'@esbuild/aix-ppc64': 0.27.7
|
||||
'@esbuild/android-arm': 0.27.7
|
||||
'@esbuild/android-arm64': 0.27.7
|
||||
'@esbuild/android-x64': 0.27.7
|
||||
'@esbuild/darwin-arm64': 0.27.7
|
||||
'@esbuild/darwin-x64': 0.27.7
|
||||
'@esbuild/freebsd-arm64': 0.27.7
|
||||
'@esbuild/freebsd-x64': 0.27.7
|
||||
'@esbuild/linux-arm': 0.27.7
|
||||
'@esbuild/linux-arm64': 0.27.7
|
||||
'@esbuild/linux-ia32': 0.27.7
|
||||
'@esbuild/linux-loong64': 0.27.7
|
||||
'@esbuild/linux-mips64el': 0.27.7
|
||||
'@esbuild/linux-ppc64': 0.27.7
|
||||
'@esbuild/linux-riscv64': 0.27.7
|
||||
'@esbuild/linux-s390x': 0.27.7
|
||||
'@esbuild/linux-x64': 0.27.7
|
||||
'@esbuild/netbsd-arm64': 0.27.7
|
||||
'@esbuild/netbsd-x64': 0.27.7
|
||||
'@esbuild/openbsd-arm64': 0.27.7
|
||||
'@esbuild/openbsd-x64': 0.27.7
|
||||
'@esbuild/openharmony-arm64': 0.27.7
|
||||
'@esbuild/sunos-x64': 0.27.7
|
||||
'@esbuild/win32-arm64': 0.27.7
|
||||
'@esbuild/win32-ia32': 0.27.7
|
||||
'@esbuild/win32-x64': 0.27.7
|
||||
'@esbuild/aix-ppc64': 0.28.1
|
||||
'@esbuild/android-arm': 0.28.1
|
||||
'@esbuild/android-arm64': 0.28.1
|
||||
'@esbuild/android-x64': 0.28.1
|
||||
'@esbuild/darwin-arm64': 0.28.1
|
||||
'@esbuild/darwin-x64': 0.28.1
|
||||
'@esbuild/freebsd-arm64': 0.28.1
|
||||
'@esbuild/freebsd-x64': 0.28.1
|
||||
'@esbuild/linux-arm': 0.28.1
|
||||
'@esbuild/linux-arm64': 0.28.1
|
||||
'@esbuild/linux-ia32': 0.28.1
|
||||
'@esbuild/linux-loong64': 0.28.1
|
||||
'@esbuild/linux-mips64el': 0.28.1
|
||||
'@esbuild/linux-ppc64': 0.28.1
|
||||
'@esbuild/linux-riscv64': 0.28.1
|
||||
'@esbuild/linux-s390x': 0.28.1
|
||||
'@esbuild/linux-x64': 0.28.1
|
||||
'@esbuild/netbsd-arm64': 0.28.1
|
||||
'@esbuild/netbsd-x64': 0.28.1
|
||||
'@esbuild/openbsd-arm64': 0.28.1
|
||||
'@esbuild/openbsd-x64': 0.28.1
|
||||
'@esbuild/openharmony-arm64': 0.28.1
|
||||
'@esbuild/sunos-x64': 0.28.1
|
||||
'@esbuild/win32-arm64': 0.28.1
|
||||
'@esbuild/win32-ia32': 0.28.1
|
||||
'@esbuild/win32-x64': 0.28.1
|
||||
|
||||
escalade@3.2.0: {}
|
||||
|
||||
@@ -4560,7 +4562,7 @@ snapshots:
|
||||
|
||||
form-data-encoder@1.7.2: {}
|
||||
|
||||
form-data@4.0.5:
|
||||
form-data@4.0.6:
|
||||
dependencies:
|
||||
asynckit: 0.4.0
|
||||
combined-stream: 1.0.8
|
||||
@@ -6016,7 +6018,7 @@ snapshots:
|
||||
|
||||
ts-interface-checker@0.1.13: {}
|
||||
|
||||
ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(esbuild@0.27.7)(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)))(typescript@5.5.4):
|
||||
ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(esbuild@0.28.1)(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)))(typescript@5.5.4):
|
||||
dependencies:
|
||||
bs-logger: 0.2.6
|
||||
fast-json-stable-stringify: 2.1.0
|
||||
@@ -6034,7 +6036,7 @@ snapshots:
|
||||
'@jest/transform': 29.7.0
|
||||
'@jest/types': 29.6.3
|
||||
babel-jest: 29.7.0(@babel/core@7.29.7)
|
||||
esbuild: 0.27.7
|
||||
esbuild: 0.28.1
|
||||
jest-util: 29.7.0
|
||||
|
||||
ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4):
|
||||
@@ -6059,12 +6061,12 @@ snapshots:
|
||||
|
||||
tsup@8.5.1(typescript@5.5.4):
|
||||
dependencies:
|
||||
bundle-require: 5.1.0(esbuild@0.27.7)
|
||||
bundle-require: 5.1.0(esbuild@0.28.1)
|
||||
cac: 6.7.14
|
||||
chokidar: 4.0.3
|
||||
consola: 3.4.2
|
||||
debug: 4.4.3(supports-color@5.5.0)
|
||||
esbuild: 0.27.7
|
||||
esbuild: 0.28.1
|
||||
fix-dts-default-cjs-exports: 1.0.1
|
||||
joycon: 3.1.1
|
||||
picocolors: 1.1.1
|
||||
|
||||
@@ -6,6 +6,7 @@ onlyBuiltDependencies:
|
||||
- better-sqlite3
|
||||
|
||||
overrides:
|
||||
"form-data@<4.0.6": ">=4.0.6"
|
||||
"picomatch@<2.3.2": "^2.3.2"
|
||||
"picomatch@>=4.0.0 <4.0.4": "^4.0.4"
|
||||
"jws@4.0.0": "4.0.1"
|
||||
@@ -22,3 +23,4 @@ overrides:
|
||||
"tar-fs@>=2.0.0 <2.1.4": "^2.1.4"
|
||||
"glob@>=10.2.0 <10.5.0": "^10.5.0"
|
||||
"@modelcontextprotocol/sdk": "^1.25.4"
|
||||
"esbuild": ">=0.28.1"
|
||||
|
||||
@@ -253,6 +253,11 @@ export default class MemoryClient {
|
||||
messages: Array<Message>,
|
||||
options: AddMemoryOptions & Record<string, any> = {},
|
||||
): Promise<Array<Memory>> {
|
||||
// Tightly scoped validation guard to resolve #5465
|
||||
if (!messages || (Array.isArray(messages) && messages.length === 0)) {
|
||||
throw new Error("Cannot process an empty messages payload.");
|
||||
}
|
||||
|
||||
if (this.telemetryId === "") await this.ping();
|
||||
|
||||
const payload = this._preparePayload(messages, options);
|
||||
@@ -696,7 +701,10 @@ export default class MemoryClient {
|
||||
throw new Error("Missing filters or schema");
|
||||
}
|
||||
|
||||
const { filters, ...rest } = data;
|
||||
// filters and schema are user-controlled blobs whose keys must reach the
|
||||
// API verbatim; only the remaining SDK params (e.g. exportInstructions)
|
||||
// get camel->snake conversion. See issue #5593.
|
||||
const { filters, schema, ...rest } = data;
|
||||
const response = await this._fetchWithErrorHandling(
|
||||
`${this.host}/v1/exports/`,
|
||||
{
|
||||
@@ -705,6 +713,7 @@ export default class MemoryClient {
|
||||
body: JSON.stringify({
|
||||
...camelToSnakeKeys(rest),
|
||||
filters,
|
||||
schema,
|
||||
}),
|
||||
},
|
||||
);
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user