Compare commits

..

26 Commits

Author SHA1 Message Date
Soumil Rathi ebc82245a5 fix(oss): cap v3 add ingestion fanout 2026-06-16 17:05:20 -07:00
Aayush Soni d772f9a961 feat: support Gemini via Vertex AI as LLM provider (#4030)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-06-16 16:59:06 +05:30
Yash Raj Pandey 7c841a2bce fix(redis): do not crash on empty or None filters in search and list (#5446)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-06-16 16:10:57 +05:30
Hrushikesh Yadav 6a6dfb4935 fix(huggingface): use self.config instead of raw config parameter (#5538) 2026-06-16 15:51:27 +05:30
Hrushikesh Yadav 8b370def80 fix: AsyncMemory.reset() does not reset entity store (#5535) 2026-06-16 15:50:30 +05:30
Hrushikesh Yadav d46464282c fix(pinecone): hybrid search crashes when filters is None (#5533) 2026-06-16 15:49:26 +05:30
Hrushikesh Yadav bb4a239cb1 fix(mongodb): reset() passes wrong argument to create_col() (#5532) 2026-06-16 15:48:45 +05:30
Hrushikesh Yadav e30f0d91fe fix(weaviate): reset() crashes with missing vector_size argument (#5531) 2026-06-16 15:45:47 +05:30
Hrushikesh Yadav 9f34e858c7 fix(ollama): json format mutates caller's messages list in-place (#5539) 2026-06-16 15:37:22 +05:30
Bartok 94bbc13de0 fix(memory): skip messages without a content key in message parsers (#5575) 2026-06-16 15:29:53 +05:30
Hrushikesh Yadav a2f01a8fcc fix: async delete_all aborts on first error, leaving partial deletion (#5529) 2026-06-16 11:59:33 +05:30
Hrushikesh Yadav 30d172e826 fix: omit None config values from Gemini GenerateContentConfig (#5528) 2026-06-16 11:54:42 +05:30
ly-wang19 bb69b036b5 fix(vector_stores): return None from get() for missing IDs (milvus/weaviate/supabase) (#5562)
Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
2026-06-16 11:52:59 +05:30
Hrushikesh Yadav b55c51e004 fix(anthropic): tool_choice format and tool response parsing (#5537)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-06-15 17:28:45 +05:30
Harsh Vardhan Gupta 4492e75d04 fix(deps): bump esbuild >=0.28.1 across all npm packages (#5563)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:04:11 +05:30
Hrushikesh Yadav 4d949022f2 fix: preserve custom metadata fields during memory update (#5480) 2026-06-15 16:29:44 +05:30
ly-wang19 3ef034a9e4 fix(vector_stores): return None from ChromaDB.get() for missing IDs (#5561)
Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
2026-06-15 16:11:03 +05:30
Yash Singh b90e3c0b76 fix(reranker): respect config.top_k in Cohere and ZeroEntropy fallback paths (#5560) 2026-06-15 16:10:00 +05:30
ly-wang19 a8eeddde64 fix(llms): honor reasoning-model params in AzureOpenAIStructuredLLM (#5548)
Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
2026-06-15 16:07:19 +05:30
Hrushikesh Yadav 09a9e34382 fix(litellm): function-calling check blocks all calls on non-tool models (#5536) 2026-06-15 15:59:46 +05:30
anish 66c4394b40 fix(pyproject): rename vector_stores extra to vector-stores for PEP 503/508 compliance (#4934)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-06-15 12:38:23 +05:30
ly-wang19 32575a65fc fix(llms): honor reasoning-model params in OpenAIStructuredLLM (#5458)
Co-authored-by: ly-wang19 <ly-wang19@users.noreply.github.com>
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-06-15 12:36:15 +05:30
Yash Singh 66901d7393 fix(llms): accept and forward **kwargs in Together/LangChain/Sarvam providers (#5556) 2026-06-15 12:23:04 +05:30
Hrushikesh Yadav a1eefc31bc fix(bedrock): use dict literal instead of set in AI21 response parse default (#5527) 2026-06-15 12:09:34 +05:30
Davide Leopardi de471799d1 fix(llms): send max_completion_tokens for the GPT-5 family across providers (#5547) 2026-06-15 12:04:19 +05:30
Rod Boev 3951ad4705 fix(openclaw): reduce skills-mode triage prompt footprint (#5502) 2026-06-15 11:18:39 +05:30
77 changed files with 2799 additions and 1456 deletions
+10
View File
@@ -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"
}
}
}
+115 -399
View File
@@ -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
+1
View File
@@ -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"
+3 -3
View File
@@ -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" +
+2 -1
View File
@@ -69,7 +69,8 @@
"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"
}
}
}
+124 -123
View File
@@ -11,6 +11,7 @@ overrides:
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 +41,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 +150,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 +880,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 +1036,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
@@ -2016,7 +2017,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 +2324,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':
@@ -2779,7 +2780,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 +2791,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:
@@ -2914,9 +2915,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 +3051,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: {}
@@ -4021,12 +4022,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 +4070,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 +4079,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 +4102,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
@@ -19,3 +19,4 @@ overrides:
"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"
+74 -1
View File
@@ -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=");
});
});
+201 -3
View File
@@ -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.
*/
+2 -1
View File
@@ -71,7 +71,8 @@
},
"pnpm": {
"overrides": {
"uuid@<11.1.1": ">=11.1.1"
"uuid@<11.1.1": ">=11.1.1",
"esbuild": ">=0.28.1"
}
}
}
+189 -124
View File
@@ -6,6 +6,7 @@ settings:
overrides:
uuid@<11.1.1: '>=11.1.1'
esbuild: '>=0.28.1'
importers:
@@ -13,7 +14,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 +36,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 +254,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 +470,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 +645,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 +742,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 +1105,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 +1120,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 +1288,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
@@ -1600,24 +1629,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 +1934,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 +1985,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'}
@@ -2330,7 +2375,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
@@ -2925,82 +2970,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':
@@ -3478,13 +3523,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:
@@ -3597,6 +3642,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 +3653,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 +3799,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: {}
@@ -4190,7 +4237,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 +4258,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 +4449,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 +4473,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 +4501,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 +4848,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
@@ -4837,7 +4902,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 +4911,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 +4936,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
@@ -3,3 +3,4 @@ packages:
overrides:
"uuid@<11.1.1": ">=11.1.1"
"esbuild": ">=0.28.1"
+2 -1
View File
@@ -84,7 +84,8 @@
"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"
}
}
}
+116 -115
View File
@@ -11,6 +11,7 @@ overrides:
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 +68,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 +304,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]
@@ -866,7 +867,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 +1012,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
@@ -2142,82 +2143,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':
@@ -2702,9 +2703,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,34 +2824,34 @@ 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: {}
@@ -3711,7 +3712,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 +3730,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 +3753,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
@@ -12,3 +12,4 @@ overrides:
"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"
+2 -1
View File
@@ -154,7 +154,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"
}
}
}
+116 -115
View File
@@ -21,6 +21,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 +126,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 +376,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 +1207,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 +1451,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
@@ -3381,82 +3382,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':
@@ -4218,9 +4219,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 +4436,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: {}
@@ -6016,7 +6017,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 +6035,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 +6060,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
+1
View File
@@ -22,3 +22,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"
+1 -28
View File
@@ -3,9 +3,7 @@
*
* Used to stitch PostHog identities: SDKs and CLIs persist anonymous
* distinct_id values here, and the TS MemoryClient reads those on init to
* fire $identify and merge them into the email identity. The OSS user_id is
* only a stitch candidate when telemetry.oss_used_at proves OSS Memory
* actually ran here (the platform client never mints ids itself).
* fire $identify and merge them into the email identity.
*
* Node-only. Browsers (no `process.versions.node`) no-op.
*/
@@ -13,7 +11,6 @@
export interface Mem0AnonIds {
oss?: string;
cli?: string;
ossUsedAt?: string;
aliasedPairs: string[];
}
@@ -113,10 +110,6 @@ export async function readMem0AnonIds(): Promise<Mem0AnonIds | null> {
typeof telemetry.anonymous_id === "string"
? telemetry.anonymous_id
: undefined,
ossUsedAt:
typeof telemetry.oss_used_at === "string" && telemetry.oss_used_at
? telemetry.oss_used_at
: undefined,
aliasedPairs: Array.isArray(telemetry.aliased_pairs)
? telemetry.aliased_pairs.filter(
(item: unknown) => typeof item === "string",
@@ -144,26 +137,6 @@ export async function isMem0Aliased(
return aliasedPairs.includes(aliasPairMarker(node, anonId, email));
}
export async function markMem0OssUsed(): Promise<void> {
const node = await getNodeFs();
if (!node) return;
try {
const config = loadConfig(node) ?? {};
const telemetry =
config.telemetry && typeof config.telemetry === "object"
? config.telemetry
: {};
if (typeof telemetry.oss_used_at === "string" && telemetry.oss_used_at) {
return;
}
telemetry.oss_used_at = new Date().toISOString();
config.telemetry = telemetry;
writeConfig(node, config);
} catch {
// Best-effort: read-only filesystems and unwritable paths just skip.
}
}
export async function markMem0Aliased(
anonId: string,
email: string,
+11 -10
View File
@@ -27,7 +27,12 @@ import {
isTelemetryEnabled,
telemetry,
} from "./telemetry";
import { isMem0Aliased, markMem0Aliased, readMem0AnonIds } from "./config";
import {
getOrCreateMem0UserId,
isMem0Aliased,
markMem0Aliased,
readMem0AnonIds,
} from "./config";
import { camelToSnake, camelToSnakeKeys, snakeToCamelKeys } from "./utils";
import { createExceptionFromResponse, MemoryError } from "../common/exceptions";
@@ -146,16 +151,12 @@ export default class MemoryClient {
try {
const email = this.telemetryId;
if (!email || !email.includes("@")) return;
const sharedAnonId = await getOrCreateMem0UserId();
const anonIds = await readMem0AnonIds();
if (!anonIds) return;
// The OSS user_id is only a stitch candidate when telemetry.oss_used_at
// proves OSS Memory actually ran here; never mint an id from the
// platform client path. The CLI anon id is presence-based (only the
// CLIs write it).
const candidates = [
anonIds.ossUsedAt ? anonIds.oss : undefined,
anonIds.cli,
].filter((id): id is string => !!id && id !== email);
if (!anonIds && !sharedAnonId) return;
const candidates = [anonIds?.oss || sharedAnonId, anonIds?.cli].filter(
(id): id is string => !!id && id !== email,
);
const seen = new Set<string>();
for (const anonId of candidates) {
if (seen.has(anonId) || (await isMem0Aliased(anonId, email))) continue;
-1
View File
@@ -85,7 +85,6 @@ class UnifiedTelemetry implements TelemetryClient {
properties: {
$anon_distinct_id: anonId,
client_source: "typescript",
client_version: version,
$lib: "posthog-node",
},
};
@@ -13,7 +13,6 @@ import {
getOrCreateMem0UserId,
isMem0Aliased,
markMem0Aliased,
markMem0OssUsed,
readMem0AnonIds,
} from "../config";
import { TEST_API_KEY } from "./helpers";
@@ -146,55 +145,6 @@ describe("config.ts — readMem0AnonIds / markMem0Aliased", () => {
markMem0Aliased("oss-uuid", "user@example.com"),
).resolves.toBeUndefined();
});
test("markMem0OssUsed writes marker preserving other fields", async () => {
fs.writeFileSync(
path.join(tmpHome, "config.json"),
JSON.stringify({
user_id: "oss-uuid",
telemetry: { anonymous_id: "cli-anon", aliased_pairs: ["pair-marker"] },
}),
);
await markMem0OssUsed();
const written = JSON.parse(
fs.readFileSync(path.join(tmpHome, "config.json"), "utf8"),
);
expect(written.user_id).toBe("oss-uuid");
expect(written.telemetry.anonymous_id).toBe("cli-anon");
expect(written.telemetry.aliased_pairs).toEqual(["pair-marker"]);
expect(written.telemetry.oss_used_at).toBeTruthy();
});
test("markMem0OssUsed is idempotent", async () => {
await markMem0OssUsed();
const first = JSON.parse(
fs.readFileSync(path.join(tmpHome, "config.json"), "utf8"),
).telemetry.oss_used_at;
await markMem0OssUsed();
const second = JSON.parse(
fs.readFileSync(path.join(tmpHome, "config.json"), "utf8"),
).telemetry.oss_used_at;
expect(second).toBe(first);
});
test("readMem0AnonIds surfaces ossUsedAt", async () => {
fs.writeFileSync(
path.join(tmpHome, "config.json"),
JSON.stringify({
user_id: "oss-uuid",
telemetry: { oss_used_at: "2026-01-01T00:00:00.000Z" },
}),
);
const ids = await readMem0AnonIds();
expect(ids?.oss).toBe("oss-uuid");
expect(ids?.ossUsedAt).toBe("2026-01-01T00:00:00.000Z");
});
test("markMem0OssUsed does not throw when target dir is unwritable", async () => {
fs.writeFileSync(path.join(tmpHome, "blocker"), "x");
process.env.MEM0_DIR = path.join(tmpHome, "blocker"); // file used as dir
await expect(markMem0OssUsed()).resolves.toBeUndefined();
});
});
// ─── telemetry.captureIdentify ───────────────────────────────
@@ -216,7 +166,6 @@ describe("telemetry.captureIdentify", () => {
expect(payload.event).toBe("$identify");
expect(payload.distinct_id).toBe("user@example.com");
expect(payload.properties.$anon_distinct_id).toBe("anon-uuid");
expect(payload.properties.client_version).toBeTruthy();
expect(payload.properties.$process_person_profile).toBeUndefined();
});
@@ -271,10 +220,7 @@ describe("MemoryClient — _maybeAliasAnonToEmail", () => {
test("fires $identify on first init and persists pair marker", async () => {
fs.writeFileSync(
path.join(tmpHome, "config.json"),
JSON.stringify({
user_id: "oss-uuid",
telemetry: { oss_used_at: "2026-01-01T00:00:00.000Z" },
}),
JSON.stringify({ user_id: "oss-uuid" }),
);
const fetchMock = setupMockFetchWithPostHog();
@@ -298,38 +244,17 @@ describe("MemoryClient — _maybeAliasAnonToEmail", () => {
expect(written.telemetry.aliased_pairs).toHaveLength(1);
});
test("platform-only init mints nothing and fires no $identify", async () => {
// Regression: the client used to create the shared anon id itself and
// immediately stitch it, merging an event-less anon person into every
// platform user. The platform path must now be read-only.
test("platform-first init creates shared anon ID and identifies it", async () => {
const fetchMock = setupMockFetchWithPostHog();
const client = makeStubClient("test@example.com");
await (client as any)._maybeAliasAnonToEmail();
expect(fs.existsSync(path.join(tmpHome, "config.json"))).toBe(false);
const identifyCalls = (fetchMock.mock.calls as any[]).filter(
([, init]: [string, RequestInit]) => {
if (!init?.body) return false;
return JSON.parse(init.body as string).event === "$identify";
},
const written = JSON.parse(
fs.readFileSync(path.join(tmpHome, "config.json"), "utf8"),
);
expect(identifyCalls.length).toBe(0);
});
test("oss id without oss_used_at marker is not stitched; cli id still is", async () => {
fs.writeFileSync(
path.join(tmpHome, "config.json"),
JSON.stringify({
user_id: "oss-uuid",
telemetry: { anonymous_id: "cli-anon" },
}),
);
const fetchMock = setupMockFetchWithPostHog();
const client = makeStubClient("test@example.com");
await (client as any)._maybeAliasAnonToEmail();
expect(written.user_id).toBeTruthy();
expect(written.telemetry.aliased_pairs).toHaveLength(1);
const identifyCalls = (fetchMock.mock.calls as any[]).filter(
([, init]: [string, RequestInit]) => {
@@ -339,32 +264,8 @@ describe("MemoryClient — _maybeAliasAnonToEmail", () => {
);
expect(identifyCalls.length).toBe(1);
const body = JSON.parse(identifyCalls[0][1].body);
expect(body.properties.$anon_distinct_id).toBe("cli-anon");
});
test("stitches config written by the Python SDK (cross-SDK key compat)", async () => {
// Python writes datetime.now(timezone.utc).isoformat(): +00:00 offset.
fs.writeFileSync(
path.join(tmpHome, "config.json"),
JSON.stringify({
user_id: "oss-uuid",
telemetry: { oss_used_at: "2026-01-01T00:00:00+00:00" },
}),
);
const fetchMock = setupMockFetchWithPostHog();
const client = makeStubClient("test@example.com");
await (client as any)._maybeAliasAnonToEmail();
const identifyCalls = (fetchMock.mock.calls as any[]).filter(
([, init]: [string, RequestInit]) => {
if (!init?.body) return false;
return JSON.parse(init.body as string).event === "$identify";
},
);
expect(identifyCalls.length).toBe(1);
const body = JSON.parse(identifyCalls[0][1].body);
expect(body.properties.$anon_distinct_id).toBe("oss-uuid");
expect(body.distinct_id).toBe("test@example.com");
expect(body.properties.$anon_distinct_id).toBe(written.user_id);
});
test("second init does not refire $identify", async () => {
@@ -372,7 +273,7 @@ describe("MemoryClient — _maybeAliasAnonToEmail", () => {
path.join(tmpHome, "config.json"),
JSON.stringify({
user_id: "oss-uuid",
telemetry: { oss_used_at: "2026-01-01T00:00:00.000Z" },
telemetry: {},
}),
);
await markMem0Aliased("oss-uuid", "test@example.com");
@@ -395,10 +296,7 @@ describe("MemoryClient — _maybeAliasAnonToEmail", () => {
path.join(tmpHome, "config.json"),
JSON.stringify({
user_id: "oss-uuid",
telemetry: {
anonymous_id: "cli-anon",
oss_used_at: "2026-01-01T00:00:00.000Z",
},
telemetry: { anonymous_id: "cli-anon" },
}),
);
const fetchMock = setupMockFetchWithPostHog();
@@ -509,18 +407,4 @@ describe("config.ts in browser-like environment", () => {
jest.resetModules();
}
});
test("markMem0OssUsed is a no-op when not Node", async () => {
const originalProcess = global.process;
// @ts-expect-error force-undefining global to simulate a browser
delete global.process;
try {
jest.resetModules();
const { markMem0OssUsed: browserMark } = await import("../config");
await expect(browserMark()).resolves.toBeUndefined();
} finally {
global.process = originalProcess;
jest.resetModules();
}
});
});
+10
View File
@@ -112,6 +112,13 @@ export class ConfigManager {
| undefined) ??
userConf?.url ??
defaultConf.baseURL;
const llmRaw = userConf as Record<string, unknown> | undefined;
const temperature =
userConf?.temperature ??
(llmRaw?.temperature as number | undefined);
const topP = userConf?.topP ?? (llmRaw?.top_p as number | undefined);
const maxTokens =
userConf?.maxTokens ?? (llmRaw?.max_tokens as number | undefined);
return {
baseURL: llmBaseURL,
@@ -125,6 +132,9 @@ export class ConfigManager {
userConf?.modelProperties !== undefined
? userConf.modelProperties
: defaultConf.modelProperties,
temperature,
topP,
maxTokens,
};
})(),
},
+51 -9
View File
@@ -5,6 +5,9 @@ import { LLMConfig, Message } from "../types";
export class AnthropicLLM implements LLM {
private client: Anthropic;
private model: string;
private maxTokens: number;
private temperature?: number;
private topP?: number;
constructor(config: LLMConfig) {
const apiKey = config.apiKey || process.env.ANTHROPIC_API_KEY;
@@ -12,18 +15,24 @@ export class AnthropicLLM implements LLM {
throw new Error("Anthropic API key is required");
}
this.client = new Anthropic({ apiKey });
this.model = config.model || "claude-3-sonnet-20240229";
this.model = config.model || "claude-sonnet-4-6";
// Defaults mirror the Python provider's AnthropicConfig
// (max_tokens=2000, temperature=0.1, top_p omitted).
this.maxTokens = config.maxTokens ?? 2000;
this.temperature = config.temperature ?? 0.1;
this.topP = config.topP;
}
async generateResponse(
messages: Message[],
responseFormat?: { type: string },
): Promise<string> {
tools?: any[],
): Promise<string | LLMResponse> {
// Extract system message if present
const systemMessage = messages.find((msg) => msg.role === "system");
const otherMessages = messages.filter((msg) => msg.role !== "system");
const response = await this.client.messages.create({
const params: Anthropic.MessageCreateParamsNonStreaming = {
model: this.model,
messages: otherMessages.map((msg) => ({
role: msg.role as "user" | "assistant",
@@ -36,8 +45,41 @@ export class AnthropicLLM implements LLM {
typeof systemMessage?.content === "string"
? systemMessage.content
: undefined,
max_tokens: 4096,
});
max_tokens: this.maxTokens,
};
// Anthropic rejects requests that include both temperature and top_p;
// prefer temperature, matching the Python provider's _get_common_params.
if (this.temperature !== undefined) {
params.temperature = this.temperature;
} else if (this.topP !== undefined) {
params.top_p = this.topP;
}
if (tools) {
params.tools = tools;
params.tool_choice = { type: "auto" };
}
const response = await this.client.messages.create(params);
if (tools) {
let content = "";
const toolCalls: Array<{ name: string; arguments: string }> = [];
for (const block of response.content) {
if (block.type === "text") {
content = block.text;
} else if (block.type === "tool_use") {
toolCalls.push({
name: block.name,
arguments: JSON.stringify(block.input),
});
}
}
return { content, role: "assistant", toolCalls };
}
const firstBlock = response.content[0];
if (firstBlock.type === "text") {
@@ -49,9 +91,9 @@ export class AnthropicLLM implements LLM {
async generateChat(messages: Message[]): Promise<LLMResponse> {
const response = await this.generateResponse(messages);
return {
content: response,
role: "assistant",
};
if (typeof response === "string") {
return { content: response, role: "assistant" };
}
return response;
}
}
+79 -26
View File
@@ -40,7 +40,7 @@ import {
} from "./memory.types";
import { parse_vision_messages } from "../utils/memory";
import { HistoryManager } from "../storage/base";
import { captureClientEvent, isTelemetryEnabled } from "../utils/telemetry";
import { captureClientEvent } from "../utils/telemetry";
import {
detectScaleThresholdFromAddResult,
detectScaleThresholdFromTopK,
@@ -72,7 +72,7 @@ import {
ScoredResult,
} from "../utils/scoring";
import { getDefaultVectorStoreDbPath } from "../utils/sqlite";
import { getOrCreateMem0UserId, markMem0OssUsed } from "../../../client/config";
import { getOrCreateMem0UserId } from "../../../client/config";
// Entity params that must be passed via filters - check both snake_case and camelCase
const ENTITY_PARAMS = [
@@ -84,6 +84,58 @@ const ENTITY_PARAMS = [
"runId",
];
const ADD_CONTEXT_SEARCH_MAX_CHARS = 15000;
const ADD_MAX_EXTRACTED_MEMORIES = 20;
const ADD_MEMORY_EMBEDDING_MAX_CHARS = 15000;
const ADD_MAX_ENTITY_LINKS_PER_EVENT = 100;
const SEARCH_QUERY_MAX_CHARS = 15000;
function tailCapText(text: string, maxChars: number): string {
if (text.length <= maxChars) return text;
return text.slice(-maxChars);
}
function capExtractedMemories<T>(memories: T[]): T[] {
if (memories.length <= ADD_MAX_EXTRACTED_MEMORIES) return memories;
console.warn(
`Extracted memory cap applied: extracted=${memories.length} processing=${ADD_MAX_EXTRACTED_MEMORIES}`,
);
return memories.slice(-ADD_MAX_EXTRACTED_MEMORIES);
}
function capMemoryEmbeddingTexts(texts: string[]): string[] {
const capped = texts.map((text) =>
tailCapText(text, ADD_MEMORY_EMBEDDING_MAX_CHARS),
);
const cappedCount = texts.filter(
(text, index) => text !== capped[index],
).length;
if (cappedCount > 0) {
console.warn(
`Memory embedding input cap applied: capped_count=${cappedCount} max_chars=${ADD_MEMORY_EMBEDDING_MAX_CHARS}`,
);
}
return capped;
}
function capSearchQuery(query: string): string {
const capped = tailCapText(query, SEARCH_QUERY_MAX_CHARS);
if (capped.length !== query.length) {
console.warn(
`Search query cap applied: original_chars=${query.length} searched_chars=${capped.length}`,
);
}
return capped;
}
function capEntityKeys(keys: string[]): string[] {
if (keys.length <= ADD_MAX_ENTITY_LINKS_PER_EVENT) return keys;
console.warn(
`ADD entity-linking cap applied: extracted=${keys.length} processing=${ADD_MAX_ENTITY_LINKS_PER_EVENT}`,
);
return keys.slice(0, ADD_MAX_ENTITY_LINKS_PER_EVENT);
}
/**
* Validates that no top-level entity parameters are passed in config.
* @throws Error if entity params are found at top level
@@ -470,12 +522,6 @@ export class Memory {
try {
await this._getTelemetryId();
// Record genuine OSS usage so the platform client may stitch this
// machine's anon id to a platform email later.
if (isTelemetryEnabled()) {
await markMem0OssUsed();
}
// Capture initialization event
await captureClientEvent("init", this, {
api_version: this.apiVersion,
@@ -740,7 +786,16 @@ export class Memory {
const parsedMessages = messages.map((m) => m.content).join("\n");
// Phase 1: Existing memory retrieval
const queryEmbedding = await this.embedder.embed(parsedMessages);
const searchMessages = tailCapText(
parsedMessages,
ADD_CONTEXT_SEARCH_MAX_CHARS,
);
if (searchMessages.length !== parsedMessages.length) {
console.warn(
`ADD context-search embedding capped: original_chars=${parsedMessages.length} embedded_chars=${searchMessages.length}`,
);
}
const queryEmbedding = await this.embedder.embed(searchMessages);
const existingResults = await this.vectorStore.search(
queryEmbedding,
10,
@@ -811,6 +866,7 @@ export class Memory {
console.error("Error parsing extraction response:", e);
extractedMemories = [];
}
extractedMemories = capExtractedMemories(extractedMemories);
if (extractedMemories.length === 0) {
// Save messages even if nothing extracted
@@ -832,17 +888,21 @@ export class Memory {
const memTexts = extractedMemories
.map((m) => m.text ?? "")
.filter((t) => t.length > 0);
const memEmbeddingTexts = capMemoryEmbeddingTexts(memTexts);
let embedMap: Record<string, number[]> = {};
try {
const memEmbeddingsList = await this.embedder.embedBatch(memTexts);
const memEmbeddingsList =
await this.embedder.embedBatch(memEmbeddingTexts);
for (let i = 0; i < memTexts.length; i++) {
embedMap[memTexts[i]] = memEmbeddingsList[i];
}
} catch {
// Fallback: embed individually
for (const text of memTexts) {
for (let i = 0; i < memTexts.length; i++) {
const text = memTexts[i];
const embeddingText = memEmbeddingTexts[i];
try {
embedMap[text] = await this.embedder.embed(text);
embedMap[text] = await this.embedder.embed(embeddingText);
} catch (e) {
console.warn(`Failed to embed memory text: ${e}`);
}
@@ -1013,9 +1073,8 @@ export class Memory {
const orderedKeys = Object.keys(globalEntities);
if (orderedKeys.length > 0) {
const entityTexts = orderedKeys.map(
(k) => globalEntities[k].entityText,
);
const entityKeys = capEntityKeys(orderedKeys);
const entityTexts = entityKeys.map((k) => globalEntities[k].entityText);
// 7b: Single batch embed for all unique entities
let entityEmbeddings: (number[] | null)[];
@@ -1035,9 +1094,9 @@ export class Memory {
// Filter out entities with failed embeddings
const valid: Array<{ index: number; key: string }> = [];
for (let i = 0; i < orderedKeys.length; i++) {
for (let i = 0; i < entityKeys.length; i++) {
if (entityEmbeddings[i] !== null) {
valid.push({ index: i, key: orderedKeys[i] });
valid.push({ index: i, key: entityKeys[i] });
}
}
@@ -1262,6 +1321,7 @@ export class Memory {
}
const searchStartMs = Date.now();
query = capSearchQuery(query);
// Step 1: Preprocess query
const queryLemmatized = lemmatizeForBm25(query);
@@ -1728,20 +1788,13 @@ export class Memory {
existingEmbeddings[data] || (await this.embedder.embed(data));
const newMetadata = {
...existingMemory.payload,
...metadata,
data,
hash: createHash("md5").update(data).digest("hex"),
textLemmatized: lemmatizeForBm25(data),
createdAt: existingMemory.payload.createdAt,
updatedAt: new Date().toISOString(),
...(existingMemory.payload.user_id && {
user_id: existingMemory.payload.user_id,
}),
...(existingMemory.payload.agent_id && {
agent_id: existingMemory.payload.agent_id,
}),
...(existingMemory.payload.run_id && {
run_id: existingMemory.payload.run_id,
}),
};
await this.vectorStore.update(memoryId, embedding, newMetadata);
+6
View File
@@ -49,6 +49,9 @@ export interface LLMConfig {
model?: string | any;
modelProperties?: Record<string, any>;
timeout?: number;
temperature?: number;
topP?: number;
maxTokens?: number;
}
export interface MemoryConfig {
@@ -131,6 +134,9 @@ export const MemoryConfigSchema = z.object({
baseURL: z.string().optional(),
url: z.string().optional(),
timeout: z.number().optional(),
temperature: z.number().optional(),
topP: z.number().optional(),
maxTokens: z.number().optional(),
}),
}),
historyDbPath: z.string().optional(),
+263
View File
@@ -0,0 +1,263 @@
/// <reference types="jest" />
/**
* Anthropic LLM — unit tests (mocked @anthropic-ai/sdk).
*/
const mockCreate = jest.fn();
jest.mock("@anthropic-ai/sdk", () => {
return jest.fn().mockImplementation(() => ({
messages: { create: mockCreate },
}));
});
import { AnthropicLLM } from "../src/llms/anthropic";
describe("AnthropicLLM (unit)", () => {
beforeEach(() => mockCreate.mockClear());
it("returns text when no tools are provided and model returns a text block", async () => {
mockCreate.mockResolvedValueOnce({
content: [{ type: "text", text: '{"facts": ["fact1"]}' }],
});
const llm = new AnthropicLLM({ apiKey: "test-key" });
const result = await llm.generateResponse([
{ role: "user", content: "Hello" },
]);
expect(mockCreate).toHaveBeenCalledTimes(1);
expect(result).toBe('{"facts": ["fact1"]}');
// No tools → tool_choice must NOT be forwarded
const callArgs = mockCreate.mock.calls[0][0];
expect(callArgs.tool_choice).toBeUndefined();
});
// Bug #1 regression: bare string "auto" must NOT be sent; object form required
it("forwards tool_choice as { type: 'auto' } (not bare string) when tools are provided", async () => {
mockCreate.mockResolvedValueOnce({
content: [
{
type: "tool_use",
id: "toolu_1",
name: "add_graph_memory",
input: { source: "Alice", destination: "Bob" },
},
],
});
const tools = [
{
name: "add_graph_memory",
description: "Add a graph memory",
input_schema: { type: "object", properties: {} },
},
];
const llm = new AnthropicLLM({ apiKey: "test-key" });
await llm.generateResponse(
[{ role: "user", content: "Alice knows Bob" }],
undefined,
tools,
);
const callArgs = mockCreate.mock.calls[0][0];
// Must be object form, not a bare string
expect(callArgs.tool_choice).toEqual({ type: "auto" });
expect(callArgs.tool_choice).not.toBe("auto");
});
// Bug #2 regression: must NOT throw on a tool_use block
it("does NOT throw when the model returns a tool_use block", async () => {
mockCreate.mockResolvedValueOnce({
content: [
{
type: "tool_use",
id: "toolu_1",
name: "add_graph_memory",
input: { source: "Alice", destination: "Bob" },
},
],
});
const tools = [
{
name: "add_graph_memory",
description: "Add a graph memory",
input_schema: { type: "object", properties: {} },
},
];
const llm = new AnthropicLLM({ apiKey: "test-key" });
await expect(
llm.generateResponse(
[{ role: "user", content: "Alice knows Bob" }],
undefined,
tools,
),
).resolves.not.toThrow();
});
it("parses tool_use blocks into toolCalls with JSON-stringified arguments", async () => {
const inputObj = { source: "Alice", destination: "Bob" };
mockCreate.mockResolvedValueOnce({
content: [
{
type: "tool_use",
id: "toolu_1",
name: "add_graph_memory",
input: inputObj,
},
],
});
const tools = [
{
name: "add_graph_memory",
description: "Add a graph memory",
input_schema: { type: "object", properties: {} },
},
];
const llm = new AnthropicLLM({ apiKey: "test-key" });
const result = await llm.generateResponse(
[{ role: "user", content: "Alice knows Bob" }],
undefined,
tools,
);
expect(result).toHaveProperty("toolCalls");
const response = result as {
content: string;
role: string;
toolCalls: Array<{ name: string; arguments: string }>;
};
expect(response.toolCalls).toHaveLength(1);
expect(response.toolCalls[0].name).toBe("add_graph_memory");
expect(JSON.parse(response.toolCalls[0].arguments)).toEqual(inputObj);
});
it("handles a mixed text + tool_use response", async () => {
const inputObj = { source: "Alice", destination: "Bob" };
mockCreate.mockResolvedValueOnce({
content: [
{ type: "text", text: "Calling the tool now." },
{
type: "tool_use",
id: "toolu_2",
name: "add_graph_memory",
input: inputObj,
},
],
});
const tools = [
{
name: "add_graph_memory",
description: "Add a graph memory",
input_schema: { type: "object", properties: {} },
},
];
const llm = new AnthropicLLM({ apiKey: "test-key" });
const result = await llm.generateResponse(
[{ role: "user", content: "Alice knows Bob" }],
undefined,
tools,
);
expect(result).toHaveProperty("toolCalls");
const response = result as {
content: string;
role: string;
toolCalls: Array<{ name: string; arguments: string }>;
};
expect(response.content).toBe("Calling the tool now.");
expect(response.role).toBe("assistant");
expect(response.toolCalls).toHaveLength(1);
expect(response.toolCalls[0].name).toBe("add_graph_memory");
expect(JSON.parse(response.toolCalls[0].arguments)).toEqual(inputObj);
});
it("returns a structured response when tools are provided but the model returns only a text block", async () => {
mockCreate.mockResolvedValueOnce({
content: [{ type: "text", text: "Just a text response" }],
});
const tools = [
{
name: "noop",
description: "No operation",
input_schema: { type: "object", properties: {} },
},
];
const llm = new AnthropicLLM({ apiKey: "test-key" });
const result = await llm.generateResponse(
[{ role: "user", content: "Hello" }],
undefined,
tools,
);
expect(result).toEqual({
content: "Just a text response",
role: "assistant",
toolCalls: [],
});
});
// Parity with the Python provider's AnthropicConfig defaults:
// model claude-sonnet-4-6, max_tokens 2000, temperature 0.1, top_p omitted.
it("sends Python-parity defaults (model, max_tokens, temperature)", async () => {
mockCreate.mockResolvedValueOnce({
content: [{ type: "text", text: "ok" }],
});
const llm = new AnthropicLLM({ apiKey: "test-key" });
await llm.generateResponse([{ role: "user", content: "Hi" }]);
const callArgs = mockCreate.mock.calls[0][0];
expect(callArgs.model).toBe("claude-sonnet-4-6");
expect(callArgs.max_tokens).toBe(2000);
expect(callArgs.temperature).toBe(0.1);
expect(callArgs.top_p).toBeUndefined();
});
it("forwards maxTokens, temperature, and model from config", async () => {
mockCreate.mockResolvedValueOnce({
content: [{ type: "text", text: "ok" }],
});
const llm = new AnthropicLLM({
apiKey: "test-key",
model: "claude-opus-4-8",
maxTokens: 1024,
temperature: 0.7,
});
await llm.generateResponse([{ role: "user", content: "Hi" }]);
const callArgs = mockCreate.mock.calls[0][0];
expect(callArgs.model).toBe("claude-opus-4-8");
expect(callArgs.max_tokens).toBe(1024);
expect(callArgs.temperature).toBe(0.7);
});
// Anthropic rejects requests with both temperature and top_p set.
it("never sends both temperature and top_p (prefers temperature)", async () => {
mockCreate.mockResolvedValueOnce({
content: [{ type: "text", text: "ok" }],
});
const llm = new AnthropicLLM({
apiKey: "test-key",
temperature: 0.5,
topP: 0.9,
});
await llm.generateResponse([{ role: "user", content: "Hi" }]);
const callArgs = mockCreate.mock.calls[0][0];
expect(callArgs.temperature).toBe(0.5);
expect(callArgs.top_p).toBeUndefined();
});
});
+76
View File
@@ -176,3 +176,79 @@ describe("Memory - add()", () => {
);
});
});
describe("Memory - ingestion caps", () => {
test("caps add context-search embedding to the recent tail", async () => {
const memory = createMemory({
vectorStore: {
provider: "memory",
config: {
collectionName: `test-add-context-cap-${Date.now()}`,
dimension: 1536,
dbPath: ":memory:",
},
},
});
const embedSpy = jest.spyOn((memory as any).embedder, "embed");
const longText = `old-${"x".repeat(15000)}`;
await memory.add(longText, { userId: `cap_user_${Date.now()}` });
expect(embedSpy.mock.calls[0][0]).toBe(longText.slice(-15000));
await memory.reset();
});
test("caps extracted memories to the latest 20 before embedding", async () => {
const memory = createMemory({
vectorStore: {
provider: "memory",
config: {
collectionName: `test-add-memory-cap-${Date.now()}`,
dimension: 1536,
dbPath: ":memory:",
},
},
});
(memory as any).llm.generateResponse = jest.fn().mockResolvedValue(
JSON.stringify({
memory: Array.from({ length: 25 }, (_, i) => ({
id: String(i),
text: `memory-${i}`,
})),
}),
);
const embedBatchSpy = jest.spyOn((memory as any).embedder, "embedBatch");
const result: SearchResult = await memory.add("remember these", {
userId: `cap_user_${Date.now()}`,
});
expect(result.results).toHaveLength(20);
expect(embedBatchSpy.mock.calls[0][0]).toEqual(
Array.from({ length: 20 }, (_, i) => `memory-${i + 5}`),
);
await memory.reset();
});
test("caps search query before embedding", async () => {
const memory = createMemory({
vectorStore: {
provider: "memory",
config: {
collectionName: `test-search-cap-${Date.now()}`,
dimension: 1536,
dbPath: ":memory:",
},
},
});
const embedSpy = jest.spyOn((memory as any).embedder, "embed");
const longQuery = `old-${"q".repeat(15000)}`;
await memory.search(longQuery, {
filters: { user_id: `cap_user_${Date.now()}` },
});
expect(embedSpy.mock.calls[0][0]).toBe(longQuery.slice(-15000));
await memory.reset();
});
});
+15
View File
@@ -191,6 +191,21 @@ describe("Memory - update()", () => {
const after: MemoryItem | null = await memory.get(id);
expect(after!.hash).not.toBe(before!.hash);
});
test("preserves custom metadata fields after update", async () => {
const addResult: SearchResult = await memory.add("Original text", {
userId,
metadata: { category: "hobbies", priority: "high" },
infer: false,
});
const id = addResult.results[0].id;
await memory.update(id, "Updated text");
const after: MemoryItem | null = await memory.get(id);
expect(after!.memory).toBe("Updated text");
expect(after!.metadata).toEqual(
expect.objectContaining({ category: "hobbies", priority: "high" }),
);
});
});
// ─── delete() ────────────────────────────────────────────
+5 -7
View File
@@ -19,13 +19,16 @@ from mem0.client.types import (
from mem0.client.utils import api_error_handler
# Exception classes are referenced in docstrings only
from mem0.memory.setup import get_user_id, is_aliased, is_oss_used, mark_aliased, read_anon_ids
from mem0.memory.setup import get_user_id, is_aliased, mark_aliased, read_anon_ids, setup_config
from mem0.memory.telemetry import capture_client_event, client_telemetry
logger = logging.getLogger(__name__)
warnings.filterwarnings("default", category=DeprecationWarning)
# Setup user config
setup_config()
# Entity parameters that must be passed via filters, not top-level
ENTITY_PARAMS = frozenset({"user_id", "agent_id", "app_id", "run_id"})
@@ -42,10 +45,6 @@ def _validate_and_trim_search_query(query: str) -> str:
def _maybe_alias_anon_to_email(user_email):
"""Fire $identify per prior anon ID so PostHog merges them into email.
The OSS user_id is only a stitch candidate when telemetry.oss_used_at
proves OSS Memory actually ran here; bare user_id values were historically
minted at import time by platform-only clients and must not be stitched.
The CLI anon id keeps presence-based stitching (only the CLIs write it).
Idempotent via telemetry.aliased_pairs: only writes markers when
telemetry is actually enabled, so disabling/re-enabling MEM0_TELEMETRY still works.
Best-effort: never raises.
@@ -56,9 +55,8 @@ def _maybe_alias_anon_to_email(user_email):
return
try:
anon_ids = read_anon_ids()
oss_id = anon_ids.get("oss") if is_oss_used() else None
seen = set()
for anon_id in (oss_id, anon_ids.get("cli")):
for anon_id in (anon_ids.get("oss"), anon_ids.get("cli")):
if not anon_id or anon_id == user_email or anon_id in seen:
continue
seen.add(anon_id)
+64
View File
@@ -0,0 +1,64 @@
import os
from typing import Optional
from mem0.configs.llms.base import BaseLlmConfig
class GeminiConfig(BaseLlmConfig):
"""
Configuration class for Google Gemini LLM.
Supports both the Gemini Developer API (via API key) and Vertex AI (via GCP credentials).
"""
def __init__(
self,
# Base parameters
model: Optional[str] = None,
temperature: float = 0.1,
api_key: Optional[str] = None,
max_tokens: int = 2000,
top_p: float = 0.1,
top_k: int = 1,
enable_vision: bool = False,
vision_details: Optional[str] = "auto",
http_client_proxies: Optional[dict] = None,
# Gemini-specific parameters
vertexai: Optional[bool] = None,
project: Optional[str] = None,
location: Optional[str] = None,
):
"""
Initialize Gemini configuration.
Args:
model: Gemini model to use (e.g., "gemini-2.0-flash"), defaults to None
temperature: Controls randomness, defaults to 0.1
api_key: Google API key for the Gemini Developer API, defaults to None
max_tokens: Maximum tokens to generate, defaults to 2000
top_p: Nucleus sampling parameter, defaults to 0.1
top_k: Top-k sampling parameter, defaults to 1
enable_vision: Enable vision capabilities, defaults to False
vision_details: Vision detail level, defaults to "auto"
http_client_proxies: HTTP client proxy settings, defaults to None
vertexai: Whether to use Vertex AI backend. If None, checks GOOGLE_GENAI_USE_VERTEXAI env var.
project: GCP project ID for Vertex AI. If None, checks GOOGLE_CLOUD_PROJECT env var.
location: GCP location for Vertex AI. If None, checks GOOGLE_CLOUD_LOCATION env var.
"""
super().__init__(
model=model,
temperature=temperature,
api_key=api_key,
max_tokens=max_tokens,
top_p=top_p,
top_k=top_k,
enable_vision=enable_vision,
vision_details=vision_details,
http_client_proxies=http_client_proxies,
)
if vertexai is None:
vertexai = os.getenv("GOOGLE_GENAI_USE_VERTEXAI", "").lower() in ("true", "1", "yes")
self.vertexai = vertexai
self.project = project or os.getenv("GOOGLE_CLOUD_PROJECT")
self.location = location or os.getenv("GOOGLE_CLOUD_LOCATION", "us-central1")
+2 -2
View File
@@ -16,8 +16,8 @@ class HuggingFaceEmbedding(EmbeddingBase):
def __init__(self, config: Optional[BaseEmbedderConfig] = None):
super().__init__(config)
if config.huggingface_base_url:
self.client = OpenAI(base_url=config.huggingface_base_url)
if self.config.huggingface_base_url:
self.client = OpenAI(base_url=self.config.huggingface_base_url)
self.config.model = self.config.model or "tei"
else:
self.config.model = self.config.model or "multi-qa-MiniLM-L6-cos-v1"
+15 -2
View File
@@ -35,7 +35,7 @@ class AnthropicLLM(LLMBase):
super().__init__(config)
if not self.config.model:
self.config.model = "claude-3-5-sonnet-20240620"
self.config.model = "claude-sonnet-4-6"
api_key = self.config.api_key or os.getenv("ANTHROPIC_API_KEY")
self.client = anthropic.Anthropic(api_key=api_key)
@@ -106,7 +106,20 @@ class AnthropicLLM(LLMBase):
if tools: # TODO: Remove tools if no issues found with new memory addition logic
params["tools"] = tools
params["tool_choice"] = tool_choice
params["tool_choice"] = {"type": tool_choice}
response = self.client.messages.create(**params)
return self._parse_response(response, tools)
def _parse_response(self, response, tools):
if tools:
result = {"content": None, "tool_calls": []}
for block in response.content:
if block.type == "text":
result["content"] = block.text
elif block.type == "tool_use":
result["tool_calls"].append(
{"name": block.name, "arguments": block.input}
)
return result
return response.content[0].text
+1 -1
View File
@@ -409,7 +409,7 @@ class AWSBedrockLLM(LLMBase):
elif self.provider == "cohere":
return response_json.get("generations", [{"text": ""}])[0].get("text", "")
elif self.provider == "ai21":
return response_json.get("completions", [{"data", {"text": ""}}])[0].get("data", {}).get("text", "")
return response_json.get("completions", [{"data": {"text": ""}}])[0].get("data", {}).get("text", "")
else:
# Generic parsing - try common response fields
for field in ["content", "text", "completion", "generation"]:
+16 -3
View File
@@ -72,13 +72,26 @@ class AzureOpenAIStructuredLLM(LLMBase):
messages[-1]["content"] = user_prompt
is_reasoning = self._is_reasoning_model(self.config.model)
params = {
"model": self.config.model,
"messages": messages,
"temperature": self.config.temperature,
"max_tokens": self.config.max_tokens,
"top_p": self.config.top_p,
}
# Reasoning models (o1/o3/GPT-5 series) reject temperature/top_p; only
# forward the sampling params for non-reasoning models. Mirrors the
# reasoning-aware handling of OpenAIStructuredLLM (#5458).
if not is_reasoning:
params["temperature"] = self.config.temperature
params["top_p"] = self.config.top_p
# Reasoning models require max_completion_tokens rather than max_tokens.
if is_reasoning or self._uses_max_completion_tokens(self.config.model):
params["max_completion_tokens"] = self.config.max_tokens
else:
params["max_tokens"] = self.config.max_tokens
if is_reasoning:
reasoning_effort = getattr(self.config, "reasoning_effort", None)
if reasoning_effort:
params["reasoning_effort"] = reasoning_effort
if response_format:
params["response_format"] = response_format
if tools:
+25 -1
View File
@@ -79,6 +79,25 @@ class LLMBase(ABC):
return False
def _uses_max_completion_tokens(self, model: str) -> bool:
"""
Check if the model expects ``max_completion_tokens`` instead of ``max_tokens``.
The whole GPT-5 family (gpt-5.4-mini, gpt-5.4-nano, gpt-5.5, ...) rejects the
legacy ``max_tokens`` parameter on the Chat Completions API and requires
``max_completion_tokens``. Older models (gpt-4.x, gpt-3.5, etc.) still accept
``max_tokens``.
Args:
model: The model name to check
Returns:
bool: True if the model requires ``max_completion_tokens``
"""
# Strip provider prefixes (e.g. "openai/gpt-5.4-mini" -> "gpt-5.4-mini")
base_model = (model or "").lower().rsplit("/", 1)[-1]
return base_model.startswith("gpt-5")
def _get_supported_params(self, **kwargs) -> Dict:
"""
Get parameters that are supported by the current model.
@@ -141,10 +160,15 @@ class LLMBase(ABC):
"""
params = {
"temperature": self.config.temperature,
"max_tokens": self.config.max_tokens,
"top_p": self.config.top_p,
}
model = getattr(self.config, "model", "")
if self._uses_max_completion_tokens(model):
params["max_completion_tokens"] = self.config.max_tokens
else:
params["max_tokens"] = self.config.max_tokens
# Add provider-specific parameters from kwargs
params.update(kwargs)
+34 -10
View File
@@ -1,5 +1,5 @@
import os
from typing import Dict, List, Optional
from typing import Dict, List, Optional, Union
try:
from google import genai
@@ -8,18 +8,39 @@ except ImportError:
raise ImportError("The 'google-genai' library is required. Please install it using 'pip install google-genai'.")
from mem0.configs.llms.base import BaseLlmConfig
from mem0.configs.llms.gemini import GeminiConfig
from mem0.llms.base import LLMBase
class GeminiLLM(LLMBase):
def __init__(self, config: Optional[BaseLlmConfig] = None):
def __init__(self, config: Optional[Union[BaseLlmConfig, GeminiConfig, Dict]] = None):
# Convert to GeminiConfig if needed
if config is None:
config = GeminiConfig()
elif isinstance(config, dict):
config = GeminiConfig(**config)
elif isinstance(config, BaseLlmConfig) and not isinstance(config, GeminiConfig):
config = GeminiConfig(
model=config.model,
temperature=config.temperature,
api_key=config.api_key,
max_tokens=config.max_tokens,
top_p=config.top_p,
top_k=config.top_k,
enable_vision=config.enable_vision,
vision_details=config.vision_details,
)
super().__init__(config)
if not self.config.model:
self.config.model = "gemini-2.0-flash"
api_key = self.config.api_key or os.getenv("GOOGLE_API_KEY")
self.client = genai.Client(api_key=api_key)
if self.config.vertexai:
self.client = genai.Client(vertexai=True, project=self.config.project, location=self.config.location)
else:
api_key = self.config.api_key or os.getenv("GOOGLE_API_KEY")
self.client = genai.Client(api_key=api_key)
def _parse_response(self, response, tools):
"""
@@ -157,12 +178,15 @@ class GeminiLLM(LLMBase):
# Extract system instruction and reformat messages
system_instruction, contents = self._reformat_messages(messages)
# Prepare generation config
config_params = {
"temperature": self.config.temperature,
"max_output_tokens": self.config.max_tokens,
"top_p": self.config.top_p,
}
# Prepare generation config — only include non-None values so the
# Gemini SDK uses its own defaults instead of rejecting None.
config_params = {}
if self.config.temperature is not None:
config_params["temperature"] = self.config.temperature
if self.config.max_tokens is not None:
config_params["max_output_tokens"] = self.config.max_tokens
if self.config.top_p is not None:
config_params["top_p"] = self.config.top_p
# Add system instruction to config if present
if system_instruction:
+4 -1
View File
@@ -57,6 +57,7 @@ class LangchainLLM(LLMBase):
response_format=None,
tools: Optional[List[Dict]] = None,
tool_choice: str = "auto",
**kwargs,
):
"""
Generate a response based on the given messages using langchain_community.
@@ -66,6 +67,8 @@ class LangchainLLM(LLMBase):
response_format (str or object, optional): Format of the response. Not used in Langchain.
tools (list, optional): List of tools that the model can call.
tool_choice (str, optional): Tool choice method.
**kwargs: Additional model parameters forwarded to the underlying LangChain
model's ``invoke`` (matches the ``LLMBase.generate_response`` contract).
Returns:
str: The generated response.
@@ -90,5 +93,5 @@ class LangchainLLM(LLMBase):
if tools:
langchain_model = langchain_model.bind_tools(tools=tools, tool_choice=tool_choice)
response: AIMessage = langchain_model.invoke(langchain_messages)
response: AIMessage = langchain_model.invoke(langchain_messages, **kwargs)
return self._parse_response(response, tools)
+5 -2
View File
@@ -67,16 +67,19 @@ class LiteLLM(LLMBase):
Returns:
str: The generated response.
"""
if not litellm.supports_function_calling(self.config.model):
if tools and not litellm.supports_function_calling(self.config.model):
raise ValueError(f"Model '{self.config.model}' in litellm does not support function calling.")
params = {
"model": self.config.model,
"messages": messages,
"temperature": self.config.temperature,
"max_tokens": self.config.max_tokens,
"top_p": self.config.top_p,
}
if self._uses_max_completion_tokens(self.config.model):
params["max_completion_tokens"] = self.config.max_tokens
else:
params["max_tokens"] = self.config.max_tokens
if response_format:
params["response_format"] = response_format
if tools: # TODO: Remove tools if no issues found with new memory addition logic
+2 -1
View File
@@ -119,11 +119,12 @@ class OllamaLLM(LLMBase):
# Handle JSON response format by using Ollama's native format parameter
if response_format and response_format.get("type") == "json_object":
params["format"] = "json"
# Also add JSON format instruction to the last message as a fallback
messages = [dict(m) for m in messages]
if messages and messages[-1]["role"] == "user":
messages[-1]["content"] += "\n\nPlease respond with valid JSON only."
else:
messages.append({"role": "user", "content": "Please respond with valid JSON only."})
params["messages"] = messages
# Add options for Ollama (temperature, num_predict, top_p)
options = {
+2 -5
View File
@@ -36,11 +36,8 @@ class OpenAIStructuredLLM(LLMBase):
Returns:
str: The generated response.
"""
params = {
"model": self.config.model,
"messages": messages,
"temperature": self.config.temperature,
}
params = self._get_supported_params(messages=messages)
params["model"] = self.config.model
if response_format:
params["response_format"] = response_format
+6 -1
View File
@@ -28,7 +28,7 @@ class SarvamLLM(LLMBase):
getattr(self.config, "sarvam_base_url", None) or os.getenv("SARVAM_API_BASE") or "https://api.sarvam.ai/v1"
)
def generate_response(self, messages: List[Dict[str, str]], response_format=None) -> str:
def generate_response(self, messages: List[Dict[str, str]], response_format=None, **kwargs) -> str:
"""
Generate a response based on the given messages using Sarvam-M.
@@ -36,6 +36,8 @@ class SarvamLLM(LLMBase):
messages (list): List of message dicts containing 'role' and 'content'.
response_format (str or object, optional): Format of the response.
Currently not used by Sarvam API.
**kwargs: Additional provider-specific parameters forwarded to the Sarvam
request payload (matches the ``LLMBase.generate_response`` contract).
Returns:
str: The generated response.
@@ -72,6 +74,9 @@ class SarvamLLM(LLMBase):
if param in self.config.model:
params[param] = self.config.model[param]
# Forward any per-call provider-specific parameters (LLMBase contract).
params.update(kwargs)
try:
response = requests.post(url, headers=headers, json=params, timeout=30)
response.raise_for_status()
+4
View File
@@ -58,6 +58,7 @@ class TogetherLLM(LLMBase):
response_format=None,
tools: Optional[List[Dict]] = None,
tool_choice: str = "auto",
**kwargs,
):
"""
Generate a response based on the given messages using TogetherAI.
@@ -67,6 +68,8 @@ class TogetherLLM(LLMBase):
response_format (str or object, optional): Format of the response. Defaults to "text".
tools (list, optional): List of tools that the model can call. Defaults to None.
tool_choice (str, optional): Tool choice method. Defaults to "auto".
**kwargs: Additional provider-specific parameters forwarded to the Together
client (matches the ``LLMBase.generate_response`` contract).
Returns:
str: The generated response.
@@ -78,6 +81,7 @@ class TogetherLLM(LLMBase):
"max_tokens": self.config.max_tokens,
"top_p": self.config.top_p,
}
params.update(kwargs)
if response_format:
params["response_format"] = response_format
if tools: # TODO: Remove tools if no issues found with new memory addition logic
+114 -46
View File
@@ -24,7 +24,7 @@ from mem0.configs.prompts import (
)
from mem0.exceptions import ValidationError as Mem0ValidationError
from mem0.memory.base import MemoryBase
from mem0.memory.setup import mark_oss_used, mem0_dir, setup_config
from mem0.memory.setup import mem0_dir, setup_config
from mem0.memory.storage import SQLiteManager
from mem0.memory.telemetry import MEM0_TELEMETRY, capture_event
from mem0.memory.notices import (
@@ -80,6 +80,63 @@ warnings.filterwarnings("ignore", category=DeprecationWarning, message=".*swigva
# Initialize logger early for util functions
logger = logging.getLogger(__name__)
ADD_CONTEXT_SEARCH_MAX_CHARS = 15000
ADD_MAX_EXTRACTED_MEMORIES = 20
ADD_MEMORY_EMBEDDING_MAX_CHARS = 15000
ADD_MAX_ENTITY_LINKS_PER_EVENT = 100
SEARCH_QUERY_MAX_CHARS = 15000
def _tail_cap_text(text: str, max_chars: int) -> str:
if len(text) <= max_chars:
return text
return text[-max_chars:]
def _cap_extracted_memories(extracted_memories: list) -> list:
if len(extracted_memories) <= ADD_MAX_EXTRACTED_MEMORIES:
return extracted_memories
logger.warning(
"Extracted memory cap applied: extracted=%d processing=%d",
len(extracted_memories),
ADD_MAX_EXTRACTED_MEMORIES,
)
return extracted_memories[-ADD_MAX_EXTRACTED_MEMORIES:]
def _cap_memory_embedding_texts(mem_texts: list[str]) -> list[str]:
capped = [_tail_cap_text(t, ADD_MEMORY_EMBEDDING_MAX_CHARS) for t in mem_texts]
capped_count = sum(1 for original, capped_text in zip(mem_texts, capped) if len(original) != len(capped_text))
if capped_count:
logger.warning(
"Memory embedding input cap applied: capped_count=%d max_chars=%d",
capped_count,
ADD_MEMORY_EMBEDDING_MAX_CHARS,
)
return capped
def _cap_search_query(query: str) -> str:
capped = _tail_cap_text(query, SEARCH_QUERY_MAX_CHARS)
if len(capped) != len(query):
logger.warning(
"Search query cap applied: original_chars=%d searched_chars=%d",
len(query),
len(capped),
)
return capped
def _cap_entity_keys(entity_keys: list[str]) -> list[str]:
if len(entity_keys) <= ADD_MAX_ENTITY_LINKS_PER_EVENT:
return entity_keys
logger.warning(
"ADD entity-linking cap applied: extracted=%d processing=%d",
len(entity_keys),
ADD_MAX_ENTITY_LINKS_PER_EVENT,
)
return entity_keys[:ADD_MAX_ENTITY_LINKS_PER_EVENT]
# Fields that hold runtime auth/connection objects and must be preserved.
# These are non-serializable objects (e.g. AWSV4SignerAuth, RequestsHttpConnection)
@@ -370,6 +427,7 @@ def _entity_collection_name(provider: str, collection_name: str) -> str:
return f"{collection_name}{separator}entities"
setup_config()
logger = logging.getLogger(__name__)
_PROJECT_UPDATE_UNSUPPORTED_ERROR = "Project updates are not supported by the OSS Memory SDK."
@@ -468,13 +526,6 @@ class Memory(MemoryBase):
self.config.vector_store.provider,
)
# Mint the OSS anon id (and record genuine OSS usage) only on real OSS
# init, never at import time: platform-only clients must not create
# stitch candidates. Must run before the first capture_event so the
# telemetry singleton picks up the minted user_id.
if MEM0_TELEMETRY:
setup_config()
mark_oss_used()
capture_event("mem0.init", self, {"sync_type": "sync"})
@property
@@ -810,9 +861,16 @@ class Memory(MemoryBase):
# Phase 1: Existing memory retrieval
search_filters = {k: v for k, v in filters.items() if k in ("user_id", "agent_id", "run_id") and v}
query_embedding = self.embedding_model.embed(parsed_messages, "search")
search_messages = _tail_cap_text(parsed_messages, ADD_CONTEXT_SEARCH_MAX_CHARS)
if len(search_messages) != len(parsed_messages):
logger.warning(
"ADD context-search embedding capped: original_chars=%d embedded_chars=%d",
len(parsed_messages),
len(search_messages),
)
query_embedding = self.embedding_model.embed(search_messages, "search")
existing_results = self.vector_store.search(
query=parsed_messages,
query=search_messages,
vectors=query_embedding,
top_k=10,
filters=search_filters,
@@ -866,6 +924,7 @@ class Memory(MemoryBase):
except Exception as e:
logger.error(f"Error parsing extraction response: {e}")
extracted_memories = []
extracted_memories = _cap_extracted_memories(extracted_memories)
if not extracted_memories:
# Save messages even if nothing extracted
@@ -874,15 +933,16 @@ class Memory(MemoryBase):
# Phase 3: Batch embed all extracted memory texts
mem_texts = [m.get("text", "") for m in extracted_memories if m.get("text")]
mem_embedding_texts = _cap_memory_embedding_texts(mem_texts)
try:
mem_embeddings_list = self.embedding_model.embed_batch(mem_texts, "add")
mem_embeddings_list = self.embedding_model.embed_batch(mem_embedding_texts, "add")
embed_map = dict(zip(mem_texts, mem_embeddings_list))
except Exception:
# Fallback: embed individually
embed_map = {}
for text in mem_texts:
for text, embedding_text in zip(mem_texts, mem_embedding_texts):
try:
embed_map[text] = self.embedding_model.embed(text, "add")
embed_map[text] = self.embedding_model.embed(embedding_text, "add")
except Exception as e:
logger.warning(f"Failed to embed memory text: {e}")
@@ -984,7 +1044,7 @@ class Memory(MemoryBase):
global_entities[key] = [entity_type, entity_text, {memory_id}]
if global_entities:
ordered_keys = list(global_entities.keys())
ordered_keys = _cap_entity_keys(list(global_entities.keys()))
entity_texts = [global_entities[k][1] for k in ordered_keys]
# 7b: Single batch embed for all unique entities
@@ -1484,6 +1544,7 @@ class Memory(MemoryBase):
# Guard against None threshold (backward compat)
if threshold is None:
threshold = 0.1
query = _cap_search_query(query)
# Step 1: Preprocess query
query_lemmatized = lemmatize_for_bm25(query)
@@ -1845,7 +1906,9 @@ class Memory(MemoryBase):
prev_value = existing_memory.payload.get("data")
new_metadata = deepcopy(metadata) if metadata is not None else {}
new_metadata = deepcopy(existing_memory.payload)
if metadata is not None:
new_metadata.update(metadata)
new_metadata["data"] = data
new_metadata["hash"] = hashlib.md5(data.encode()).hexdigest()
@@ -1853,17 +1916,9 @@ class Memory(MemoryBase):
new_metadata["created_at"] = existing_memory.payload.get("created_at")
new_metadata["updated_at"] = datetime.now(timezone.utc).isoformat()
# Preserve session identifiers from existing memory only if not provided in new metadata
if "user_id" not in new_metadata and "user_id" in existing_memory.payload:
new_metadata["user_id"] = existing_memory.payload["user_id"]
if "agent_id" not in new_metadata and "agent_id" in existing_memory.payload:
new_metadata["agent_id"] = existing_memory.payload["agent_id"]
if "run_id" not in new_metadata and "run_id" in existing_memory.payload:
new_metadata["run_id"] = existing_memory.payload["run_id"]
# actor_id is immutable after creation (issue #4490)
if "actor_id" in existing_memory.payload:
new_metadata["actor_id"] = existing_memory.payload["actor_id"]
if "role" not in new_metadata and "role" in existing_memory.payload:
new_metadata["role"] = existing_memory.payload["role"]
if data in existing_embeddings:
embeddings = existing_embeddings[data]
@@ -2015,9 +2070,6 @@ class AsyncMemory(MemoryBase):
self.config.vector_store.provider,
)
if MEM0_TELEMETRY:
setup_config()
mark_oss_used()
capture_event("mem0.init", self, {"sync_type": "async"})
@property
@@ -2325,10 +2377,17 @@ class AsyncMemory(MemoryBase):
# Phase 1: Existing memory retrieval
search_filters = {k: v for k, v in effective_filters.items() if k in ("user_id", "agent_id", "run_id") and v}
query_embedding = await asyncio.to_thread(self.embedding_model.embed, parsed_messages, "search")
search_messages = _tail_cap_text(parsed_messages, ADD_CONTEXT_SEARCH_MAX_CHARS)
if len(search_messages) != len(parsed_messages):
logger.warning(
"ADD context-search embedding capped: original_chars=%d embedded_chars=%d",
len(parsed_messages),
len(search_messages),
)
query_embedding = await asyncio.to_thread(self.embedding_model.embed, search_messages, "search")
existing_results = await asyncio.to_thread(
self.vector_store.search,
query=parsed_messages,
query=search_messages,
vectors=query_embedding,
top_k=10,
filters=search_filters,
@@ -2383,6 +2442,7 @@ class AsyncMemory(MemoryBase):
except Exception as e:
logger.error(f"Error parsing extraction response (async): {e}")
extracted_memories = []
extracted_memories = _cap_extracted_memories(extracted_memories)
if not extracted_memories:
await asyncio.to_thread(self.db.save_messages, messages, session_scope)
@@ -2390,14 +2450,15 @@ class AsyncMemory(MemoryBase):
# Phase 3: Batch embed all extracted memory texts
mem_texts = [m.get("text", "") for m in extracted_memories if m.get("text")]
mem_embedding_texts = _cap_memory_embedding_texts(mem_texts)
try:
mem_embeddings_list = await asyncio.to_thread(self.embedding_model.embed_batch, mem_texts, "add")
mem_embeddings_list = await asyncio.to_thread(self.embedding_model.embed_batch, mem_embedding_texts, "add")
embed_map = dict(zip(mem_texts, mem_embeddings_list))
except Exception:
embed_map = {}
for text in mem_texts:
for text, embedding_text in zip(mem_texts, mem_embedding_texts):
try:
embed_map[text] = await asyncio.to_thread(self.embedding_model.embed, text, "add")
embed_map[text] = await asyncio.to_thread(self.embedding_model.embed, embedding_text, "add")
except Exception as e:
logger.warning(f"Failed to embed memory text (async): {e}")
@@ -2500,7 +2561,7 @@ class AsyncMemory(MemoryBase):
global_entities[key] = [entity_type, entity_text, {memory_id}]
if global_entities:
ordered_keys = list(global_entities.keys())
ordered_keys = _cap_entity_keys(list(global_entities.keys()))
entity_texts = [global_entities[k][1] for k in ordered_keys]
# 7b: Batch embed entities
@@ -3005,6 +3066,7 @@ class AsyncMemory(MemoryBase):
async def _search_vector_store(self, query, filters, limit, threshold=0.1, explain=False):
if threshold is None:
threshold = 0.1
query = _cap_search_query(query)
# Step 1: Preprocess query (CPU-bound)
query_lemmatized = await asyncio.to_thread(lemmatize_for_bm25, query)
@@ -3250,9 +3312,15 @@ class AsyncMemory(MemoryBase):
for memory in memories[0]:
delete_tasks.append(self._delete_memory(memory.id))
await asyncio.gather(*delete_tasks)
results = await asyncio.gather(*delete_tasks, return_exceptions=True)
logger.info(f"Deleted {len(memories[0])} memories")
errors = [r for r in results if isinstance(r, BaseException)]
if errors:
logger.warning("Failed to delete %d out of %d memories", len(errors), len(results))
for err in errors:
logger.warning("Delete error: %s", err)
logger.info(f"Deleted {len(results) - len(errors)} memories")
decay_usage_notice = detect_decay_usage_from_delete_all(len(memories[0]))
if decay_usage_notice:
@@ -3380,7 +3448,9 @@ class AsyncMemory(MemoryBase):
prev_value = existing_memory.payload.get("data")
new_metadata = deepcopy(metadata) if metadata is not None else {}
new_metadata = deepcopy(existing_memory.payload)
if metadata is not None:
new_metadata.update(metadata)
new_metadata["data"] = data
new_metadata["hash"] = hashlib.md5(data.encode()).hexdigest()
@@ -3388,18 +3458,9 @@ class AsyncMemory(MemoryBase):
new_metadata["created_at"] = existing_memory.payload.get("created_at")
new_metadata["updated_at"] = datetime.now(timezone.utc).isoformat()
# Preserve session identifiers from existing memory only if not provided in new metadata
if "user_id" not in new_metadata and "user_id" in existing_memory.payload:
new_metadata["user_id"] = existing_memory.payload["user_id"]
if "agent_id" not in new_metadata and "agent_id" in existing_memory.payload:
new_metadata["agent_id"] = existing_memory.payload["agent_id"]
if "run_id" not in new_metadata and "run_id" in existing_memory.payload:
new_metadata["run_id"] = existing_memory.payload["run_id"]
# actor_id is immutable after creation (issue #4490)
if "actor_id" in existing_memory.payload:
new_metadata["actor_id"] = existing_memory.payload["actor_id"]
if "role" not in new_metadata and "role" in existing_memory.payload:
new_metadata["role"] = existing_memory.payload["role"]
if data in existing_embeddings:
embeddings = existing_embeddings[data]
@@ -3491,6 +3552,13 @@ class AsyncMemory(MemoryBase):
self.config.vector_store.provider, self.config.vector_store.config
)
if self._entity_store is not None:
try:
await asyncio.to_thread(self._entity_store.reset)
except Exception as e:
logger.warning(f"Failed to reset entity store: {e}")
self._entity_store = None
capture_event("mem0.reset", self, {"sync_type": "async"})
await display_first_run_notice_async(self, "async", "reset")
+4 -32
View File
@@ -3,7 +3,6 @@ import logging
import os
import tempfile
import uuid
from datetime import datetime, timezone
from hashlib import sha256
# Set up the directory path
@@ -57,12 +56,10 @@ def _write_config(config):
def setup_config():
"""Ensure ~/.mem0/config.json exists with a top-level user_id.
Called lazily from OSS Memory/AsyncMemory init (never at import time, so
platform-only MemoryClient usage mints no anon id). Idempotent: backfills
user_id for users whose config was written by the CLI (which writes
telemetry.anonymous_id but no top-level user_id). Without this, OSS Python
telemetry is silently dropped because get_user_id() returns None when
user_id is missing.
Idempotent: backfills user_id for users whose config was written by the
CLI (which writes telemetry.anonymous_id but no top-level user_id).
Without this, OSS Python telemetry is silently dropped because
get_user_id() returns None when user_id is missing.
"""
config = _load_config()
if config.get("user_id"):
@@ -134,31 +131,6 @@ def mark_aliased(anon_id, email):
_write_config(config)
def is_oss_used():
"""Return whether OSS Memory has genuinely initialized on this machine.
Gates identity stitching: the top-level user_id is only a stitch candidate
when telemetry.oss_used_at is present, because bare user_id values were
historically minted at import time by platform-only clients.
"""
config = _load_config()
telemetry = config.get("telemetry") if isinstance(config.get("telemetry"), dict) else {}
return bool(telemetry.get("oss_used_at"))
def mark_oss_used():
"""Persist telemetry.oss_used_at on first genuine OSS Memory init. Never raises."""
config = _load_config()
telemetry = config.get("telemetry")
if not isinstance(telemetry, dict):
telemetry = {}
if telemetry.get("oss_used_at"):
return
telemetry["oss_used_at"] = datetime.now(timezone.utc).isoformat()
config["telemetry"] = telemetry
_write_config(config)
def get_or_create_user_id(vector_store=None):
"""Store user_id in vector store and return it.
+1 -5
View File
@@ -129,11 +129,7 @@ class AnonymousTelemetry:
self.posthog.capture(
distinct_id=email,
event="$identify",
properties={
"$anon_distinct_id": anon_id,
"client_source": "python",
"client_version": mem0.__version__,
},
properties={"$anon_distinct_id": anon_id, "client_source": "python"},
)
return True
except Exception as e:
+26 -13
View File
@@ -61,12 +61,18 @@ def ensure_json_instruction(system_prompt, user_prompt):
def parse_messages(messages):
response = ""
for msg in messages:
if msg["role"] == "system":
response += f"system: {msg['content']}\n"
if msg["role"] == "user":
response += f"user: {msg['content']}\n"
if msg["role"] == "assistant":
response += f"assistant: {msg['content']}\n"
role = msg.get("role")
content = msg.get("content")
# Skip messages without textual content (e.g. assistant tool-call
# messages that carry `tool_calls` but no `content` key).
if content is None:
continue
if role == "system":
response += f"system: {content}\n"
elif role == "user":
response += f"user: {content}\n"
elif role == "assistant":
response += f"assistant: {content}\n"
return response
@@ -173,12 +179,19 @@ def parse_vision_messages(messages, llm=None, vision_details="auto"):
"""
returned_messages = []
for msg in messages:
if msg["role"] == "system":
role = msg.get("role")
content = msg.get("content")
if role == "system":
returned_messages.append(msg)
continue
# Skip messages without content (e.g. assistant tool-call messages
# that carry `tool_calls` but no `content` key).
if content is None:
continue
# Handle message content
if isinstance(msg["content"], list):
if isinstance(content, list):
if llm is None:
text_parts = [
part["text"] for part in msg["content"]
@@ -186,17 +199,17 @@ def parse_vision_messages(messages, llm=None, vision_details="auto"):
]
if not text_parts:
continue
returned_messages.append({"role": msg["role"], "content": " ".join(text_parts)})
returned_messages.append({"role": role, "content": " ".join(text_parts)})
else:
description = get_image_description(msg, llm, vision_details)
returned_messages.append({"role": msg["role"], "content": description})
elif isinstance(msg["content"], dict) and msg["content"].get("type") == "image_url":
returned_messages.append({"role": role, "content": description})
elif isinstance(content, dict) and content.get("type") == "image_url":
if llm is None:
continue
image_url = msg["content"]["image_url"]["url"]
image_url = content["image_url"]["url"]
try:
description = get_image_description(image_url, llm, vision_details)
returned_messages.append({"role": msg["role"], "content": description})
returned_messages.append({"role": role, "content": description})
except Exception:
raise Exception(f"Error while downloading {image_url}.")
else:
+2 -1
View File
@@ -82,4 +82,5 @@ class CohereReranker(BaseReranker):
# Fallback to original order if reranking fails
for doc in documents:
doc['rerank_score'] = 0.0
return documents[:top_k] if top_k else documents
final_top_k = top_k or self.config.top_k
return documents[:final_top_k] if final_top_k else documents
+2 -1
View File
@@ -93,4 +93,5 @@ class ZeroEntropyReranker(BaseReranker):
# Fallback to original order if reranking fails
for doc in documents:
doc['rerank_score'] = 0.0
return documents[:top_k] if top_k else documents
final_top_k = top_k or self.config.top_k
return documents[:final_top_k] if final_top_k else documents
+2 -1
View File
@@ -7,6 +7,7 @@ from mem0.configs.llms.aws_bedrock import AWSBedrockConfig
from mem0.configs.llms.azure import AzureOpenAIConfig
from mem0.configs.llms.base import BaseLlmConfig
from mem0.configs.llms.deepseek import DeepSeekConfig
from mem0.configs.llms.gemini import GeminiConfig
from mem0.configs.llms.lmstudio import LMStudioConfig
from mem0.configs.llms.minimax import MinimaxConfig
from mem0.configs.llms.ollama import OllamaConfig
@@ -46,7 +47,7 @@ class LlmFactory:
"openai_structured": ("mem0.llms.openai_structured.OpenAIStructuredLLM", OpenAIConfig),
"anthropic": ("mem0.llms.anthropic.AnthropicLLM", AnthropicConfig),
"azure_openai_structured": ("mem0.llms.azure_openai_structured.AzureOpenAIStructuredLLM", AzureOpenAIConfig),
"gemini": ("mem0.llms.gemini.GeminiLLM", BaseLlmConfig),
"gemini": ("mem0.llms.gemini.GeminiLLM", GeminiConfig),
"deepseek": ("mem0.llms.deepseek.DeepSeekLLM", DeepSeekConfig),
"minimax": ("mem0.llms.minimax.MiniMaxLLM", MinimaxConfig),
"xai": ("mem0.llms.xai.XAILLM", XAIConfig),
+4 -3
View File
@@ -187,7 +187,7 @@ class ChromaDB(VectorStoreBase):
"""
self.collection.update(ids=vector_id, embeddings=vector, metadatas=payload)
def get(self, vector_id: str) -> OutputData:
def get(self, vector_id: str) -> Optional[OutputData]:
"""
Retrieve a vector by ID.
@@ -195,10 +195,11 @@ class ChromaDB(VectorStoreBase):
vector_id (str): ID of the vector to retrieve.
Returns:
OutputData: Retrieved vector.
Optional[OutputData]: Retrieved vector, or None if the ID is not found.
"""
result = self.collection.get(ids=[vector_id])
return self._parse_output(result)[0]
parsed = self._parse_output(result)
return parsed[0] if parsed else None
def list_cols(self) -> List[chromadb.Collection]:
"""
+4 -2
View File
@@ -290,7 +290,7 @@ class MilvusDB(VectorStoreBase):
schema = {"id": vector_id, "vectors": vector, "metadata": payload, "text": text}
self.client.upsert(collection_name=self.collection_name, data=schema)
def get(self, vector_id):
def get(self, vector_id) -> Optional[OutputData]:
"""
Retrieve a vector by ID.
@@ -298,9 +298,11 @@ class MilvusDB(VectorStoreBase):
vector_id (str): ID of the vector to retrieve.
Returns:
OutputData: Retrieved vector.
Optional[OutputData]: Retrieved vector, or None if the ID is not found.
"""
result = self.client.get(collection_name=self.collection_name, ids=vector_id)
if not result:
return None
output = OutputData(
id=result[0].get("id", None),
score=None,
+1 -1
View File
@@ -391,7 +391,7 @@ class MongoDB(VectorStoreBase):
"""Reset the index by deleting and recreating it."""
logger.warning(f"Resetting index {self.collection_name}...")
self.delete_col()
self.collection = self.create_col(self.collection_name)
self.collection = self.create_col()
def __del__(self) -> None:
"""Close the database connection when the object is deleted."""
+1 -1
View File
@@ -230,7 +230,7 @@ class PineconeDB(VectorStoreBase):
if filter_dict:
query_params["filter"] = filter_dict
if self.hybrid_search and self.sparse_encoder and "text" in filters:
if self.hybrid_search and self.sparse_encoder and filters and "text" in filters:
query_text = filters.get("text")
if query_text:
sparse_vector = self.sparse_encoder.encode_queries(query_text)
+12 -6
View File
@@ -142,8 +142,11 @@ class RedisDB(VectorStoreBase):
self.index.load(data, id_field="memory_id")
def search(self, query: str, vectors: list, top_k: int = 5, filters: dict = None):
conditions = [Tag(key) == value for key, value in filters.items() if value is not None]
filter = reduce(lambda x, y: x & y, conditions)
filter = None
if filters:
conditions = [Tag(key) == value for key, value in filters.items() if value is not None]
if conditions:
filter = reduce(lambda x, y: x & y, conditions)
v = VectorQuery(
vector=np.array(vectors, dtype=np.float32).tobytes(),
@@ -312,11 +315,14 @@ class RedisDB(VectorStoreBase):
"""
List all recent created memories from the vector store.
"""
conditions = [Tag(key) == value for key, value in filters.items() if value is not None]
filter = reduce(lambda x, y: x & y, conditions)
query = Query(str(filter)).sort_by("created_at", asc=False)
filter = None
if filters:
conditions = [Tag(key) == value for key, value in filters.items() if value is not None]
if conditions:
filter = reduce(lambda x, y: x & y, conditions)
query = Query(str(filter) if filter is not None else "*").sort_by("created_at", asc=False)
if top_k is not None:
query = Query(str(filter)).sort_by("created_at", asc=False).paging(0, top_k)
query = query.paging(0, top_k)
results = self.index.search(query)
return [
+1 -1
View File
@@ -176,7 +176,7 @@ class Supabase(VectorStoreBase):
"""
result = self.collection.fetch([(vector_id,)])
if not result:
return []
return None
record = result[0]
return OutputData(id=str(record.id), score=None, payload=record.metadata)
+5 -6
View File
@@ -305,7 +305,7 @@ class Weaviate(VectorStoreBase):
existing_payload: Mapping[str, str] = existing_data
collection.data.update(uuid=vector_id, properties=existing_payload, vector=vector)
def get(self, vector_id):
def get(self, vector_id) -> Optional[OutputData]:
"""
Retrieve a vector by ID.
@@ -313,7 +313,7 @@ class Weaviate(VectorStoreBase):
vector_id: ID of the vector to retrieve.
Returns:
dict: Retrieved vector and metadata.
Optional[OutputData]: Retrieved vector, or None if the ID is not found.
"""
vector_id = get_valid_uuid(vector_id)
collection = self.client.collections.get(str(self.collection_name))
@@ -322,9 +322,8 @@ class Weaviate(VectorStoreBase):
uuid=vector_id,
return_properties=["hash", "created_at", "updated_at", "user_id", "agent_id", "run_id", "data", "category"],
)
# results = {}
# print("reponse",response)
# for obj in response.objects:
if response is None:
return None
payload = response.properties.copy()
payload["id"] = str(response.uuid).split("'")[0]
results = OutputData(
@@ -389,4 +388,4 @@ class Weaviate(VectorStoreBase):
"""Reset the index by deleting and recreating it."""
logger.warning(f"Resetting index {self.collection_name}...")
self.delete_col()
self.create_col()
self.create_col(self.embedding_model_dims)
+4 -4
View File
@@ -27,7 +27,7 @@ dependencies = [
nlp = [
"spacy>=3.7.0",
]
vector_stores = [
vector-stores = [
"vecs>=0.4.0",
"chromadb>=0.4.24",
"cassandra-driver>=3.29.0",
@@ -103,7 +103,7 @@ only-include = ["mem0"]
python = "3.10"
features = [
"test",
"vector_stores",
"vector-stores",
"llms",
"extras",
]
@@ -112,7 +112,7 @@ features = [
python = "3.11"
features = [
"test",
"vector_stores",
"vector-stores",
"llms",
"extras",
]
@@ -121,7 +121,7 @@ features = [
python = "3.12"
features = [
"test",
"vector_stores",
"vector-stores",
"llms",
"extras",
]
+28
View File
@@ -424,3 +424,31 @@ class TestMiniMaxProvider:
assert msg["role"] != "system"
# user message must be present
assert kwargs["messages"][0]["role"] == "user"
# ---------------------------------------------------------------------------
# _parse_response — legacy InvokeModel provider-specific parsing
# ---------------------------------------------------------------------------
class TestParseResponseLegacy:
def test_ai21_missing_completions_returns_empty(self, mock_boto3):
"""When AI21 response lacks 'completions', the fallback default must
be a valid dict (not a set literal), returning empty string."""
llm = _make_llm("ai21.j2-mid-v1", mock_boto3)
import io
import json
body = io.BytesIO(json.dumps({"not_completions": True}).encode())
response = {"body": body}
result = llm._parse_response(response, tools=None)
assert result == ""
def test_ai21_normal_response(self, mock_boto3):
llm = _make_llm("ai21.j2-mid-v1", mock_boto3)
import io
import json
body = io.BytesIO(json.dumps({
"completions": [{"data": {"text": "hello from ai21"}}]
}).encode())
response = {"body": body}
result = llm._parse_response(response, tools=None)
assert result == "hello from ai21"
@@ -29,6 +29,7 @@ class DummyConfig:
max_tokens=256,
top_p=1.0,
http_client=None,
reasoning_effort=None,
):
self.model = model
self.azure_kwargs = azure_kwargs or DummyAzureKwargs()
@@ -36,6 +37,7 @@ class DummyConfig:
self.max_tokens = max_tokens
self.top_p = top_p
self.http_client = http_client
self.reasoning_effort = reasoning_effort
@mock.patch("mem0.llms.azure_openai_structured.AzureOpenAI")
@@ -119,6 +121,44 @@ def test_generate_response_without_tools(mock_azure_openai):
assert response == "Hello there!"
@mock.patch("mem0.llms.azure_openai_structured.AzureOpenAI")
def test_generate_response_gpt5_uses_max_completion_tokens(mock_azure_openai):
mock_client = Mock()
mock_azure_openai.return_value = mock_client
config = DummyConfig(model="gpt-5.4-mini", azure_kwargs=DummyAzureKwargs(api_key="real-key"))
llm = AzureOpenAIStructuredLLM(config)
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content="Hi"))]
mock_client.chat.completions.create.return_value = mock_response
llm.generate_response([{"role": "user", "content": "Hi"}])
_, kwargs = mock_client.chat.completions.create.call_args
assert kwargs["max_completion_tokens"] == 256
assert "max_tokens" not in kwargs
@mock.patch("mem0.llms.azure_openai_structured.AzureOpenAI")
def test_generate_response_legacy_model_uses_max_tokens(mock_azure_openai):
mock_client = Mock()
mock_azure_openai.return_value = mock_client
config = DummyConfig(model="gpt-4.1", azure_kwargs=DummyAzureKwargs(api_key="real-key"))
llm = AzureOpenAIStructuredLLM(config)
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content="Hi"))]
mock_client.chat.completions.create.return_value = mock_response
llm.generate_response([{"role": "user", "content": "Hi"}])
_, kwargs = mock_client.chat.completions.create.call_args
assert kwargs["max_tokens"] == 256
assert "max_completion_tokens" not in kwargs
@mock.patch("mem0.llms.azure_openai_structured.AzureOpenAI")
def test_generate_response_with_tools(mock_azure_openai):
mock_client = Mock()
@@ -171,3 +211,56 @@ def test_generate_response_with_tools_no_tool_calls(mock_azure_openai):
assert response["content"] == "No tools needed."
assert response["tool_calls"] == []
@mock.patch("mem0.llms.azure_openai_structured.AzureOpenAI")
def test_reasoning_model_drops_sampling_params(mock_azure_openai):
"""Reasoning models (o1/o3/GPT-5) reject temperature/max_tokens/top_p."""
mock_client = Mock()
mock_azure_openai.return_value = mock_client
config = DummyConfig(
model="o3-mini",
azure_kwargs=DummyAzureKwargs(api_key="real-key"),
reasoning_effort="low",
)
llm = AzureOpenAIStructuredLLM(config)
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content="ok"))]
mock_client.chat.completions.create.return_value = mock_response
llm.generate_response([{"role": "user", "content": "Hi"}])
call_kwargs = mock_client.chat.completions.create.call_args[1]
assert "temperature" not in call_kwargs # reasoning models reject these
assert "max_tokens" not in call_kwargs
assert "top_p" not in call_kwargs
assert call_kwargs["reasoning_effort"] == "low"
assert call_kwargs["model"] == "o3-mini"
@mock.patch("mem0.llms.azure_openai_structured.AzureOpenAI")
def test_regular_model_sends_sampling_params(mock_azure_openai):
"""Regular models still receive the standard sampling params."""
mock_client = Mock()
mock_azure_openai.return_value = mock_client
config = DummyConfig(
model="gpt-4o",
azure_kwargs=DummyAzureKwargs(api_key="real-key"),
temperature=0.3,
)
llm = AzureOpenAIStructuredLLM(config)
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content="ok"))]
mock_client.chat.completions.create.return_value = mock_response
llm.generate_response([{"role": "user", "content": "Hi"}])
call_kwargs = mock_client.chat.completions.create.call_args[1]
assert call_kwargs["temperature"] == 0.3
assert "max_tokens" in call_kwargs # standard sampling params still forwarded
assert "top_p" in call_kwargs
assert call_kwargs["model"] == "gpt-4o"
+111
View File
@@ -4,6 +4,7 @@ import pytest
from google.genai import types
from mem0.configs.llms.base import BaseLlmConfig
from mem0.configs.llms.gemini import GeminiConfig
from mem0.llms.gemini import GeminiLLM
@@ -187,3 +188,113 @@ def test_parse_response_empty_parts_with_tools(mock_gemini_client: Mock):
result = llm._parse_response(mock_response, tools=[{"function": {"name": "test"}}])
assert result == {"content": None, "tool_calls": []}
def test_none_config_values_omitted_from_generation_config(mock_gemini_client: Mock):
"""When temperature/max_tokens/top_p are None, they must not be passed
to GenerateContentConfig (verified via model_fields_set)."""
config = BaseLlmConfig(model="gemini-2.0-flash", temperature=None, max_tokens=None, top_p=None)
llm = GeminiLLM(config)
mock_part = Mock(text="ok")
mock_content = Mock(parts=[mock_part])
mock_candidate = Mock(content=mock_content)
mock_response = Mock(candidates=[mock_candidate])
mock_gemini_client.models.generate_content.return_value = mock_response
llm.generate_response([{"role": "user", "content": "hi"}])
config_arg = mock_gemini_client.models.generate_content.call_args.kwargs["config"]
assert "temperature" not in config_arg.model_fields_set
assert "max_output_tokens" not in config_arg.model_fields_set
assert "top_p" not in config_arg.model_fields_set
def test_explicit_config_values_passed_to_generation_config(mock_gemini_client: Mock):
"""When temperature/max_tokens/top_p are explicitly set, they must appear."""
config = BaseLlmConfig(model="gemini-2.0-flash", temperature=0.5, max_tokens=200, top_p=0.9)
llm = GeminiLLM(config)
mock_part = Mock(text="ok")
mock_content = Mock(parts=[mock_part])
mock_candidate = Mock(content=mock_content)
mock_response = Mock(candidates=[mock_candidate])
mock_gemini_client.models.generate_content.return_value = mock_response
llm.generate_response([{"role": "user", "content": "hi"}])
config_arg = mock_gemini_client.models.generate_content.call_args.kwargs["config"]
assert "temperature" in config_arg.model_fields_set
assert config_arg.temperature == 0.5
assert "max_output_tokens" in config_arg.model_fields_set
assert config_arg.max_output_tokens == 200
assert "top_p" in config_arg.model_fields_set
assert config_arg.top_p == 0.9
# --- Vertex AI backend initialization (issue #3990, PR #4030) ---
def test_init_default_path_uses_api_key(monkeypatch):
"""Default (non-Vertex) path stays backward compatible: client built with api_key, never vertexai."""
monkeypatch.delenv("GOOGLE_GENAI_USE_VERTEXAI", raising=False)
monkeypatch.setenv("GOOGLE_API_KEY", "test-key")
with patch("mem0.llms.gemini.genai.Client") as mock_client_class:
GeminiLLM(GeminiConfig(model="gemini-2.0-flash"))
mock_client_class.assert_called_once_with(api_key="test-key")
def test_init_backward_compat_with_base_config(monkeypatch):
"""A legacy BaseLlmConfig still works and uses the API-key path (no missing-attr error)."""
monkeypatch.delenv("GOOGLE_GENAI_USE_VERTEXAI", raising=False)
monkeypatch.setenv("GOOGLE_API_KEY", "legacy-key")
with patch("mem0.llms.gemini.genai.Client") as mock_client_class:
GeminiLLM(BaseLlmConfig(model="gemini-2.0-flash"))
mock_client_class.assert_called_once_with(api_key="legacy-key")
def test_init_vertexai_via_explicit_config(monkeypatch):
"""vertexai=True in GeminiConfig routes to the Vertex AI client with project/location, no api_key."""
monkeypatch.delenv("GOOGLE_GENAI_USE_VERTEXAI", raising=False)
with patch("mem0.llms.gemini.genai.Client") as mock_client_class:
GeminiLLM(GeminiConfig(vertexai=True, project="my-project", location="europe-west1"))
mock_client_class.assert_called_once_with(vertexai=True, project="my-project", location="europe-west1")
def test_init_vertexai_via_dict_config(monkeypatch):
"""The factory hands GeminiLLM a dict; the vertexai key still routes to the Vertex client."""
monkeypatch.delenv("GOOGLE_GENAI_USE_VERTEXAI", raising=False)
with patch("mem0.llms.gemini.genai.Client") as mock_client_class:
GeminiLLM({"vertexai": True, "project": "p", "location": "us-central1"})
mock_client_class.assert_called_once_with(vertexai=True, project="p", location="us-central1")
def test_init_vertexai_via_env_vars(monkeypatch):
"""GOOGLE_GENAI_USE_VERTEXAI + project/location env vars enable Vertex (the exact ask in issue #3990)."""
monkeypatch.setenv("GOOGLE_GENAI_USE_VERTEXAI", "true")
monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "env-project")
monkeypatch.setenv("GOOGLE_CLOUD_LOCATION", "asia-south1")
with patch("mem0.llms.gemini.genai.Client") as mock_client_class:
GeminiLLM(GeminiConfig())
mock_client_class.assert_called_once_with(vertexai=True, project="env-project", location="asia-south1")
def test_init_vertexai_location_defaults_to_us_central1(monkeypatch):
"""When Vertex is on but no location is supplied, it defaults to us-central1."""
monkeypatch.delenv("GOOGLE_CLOUD_LOCATION", raising=False)
monkeypatch.setenv("GOOGLE_GENAI_USE_VERTEXAI", "true")
with patch("mem0.llms.gemini.genai.Client") as mock_client_class:
GeminiLLM(GeminiConfig(project="p"))
mock_client_class.assert_called_once_with(vertexai=True, project="p", location="us-central1")
def test_init_base_config_respects_vertexai_env(monkeypatch):
"""GOOGLE_GENAI_USE_VERTEXAI is the authoritative global switch (Google's own
convention): a legacy BaseLlmConfig is routed to Vertex when the env var is set,
even if it carries an api_key. This pins the precedence as intentional."""
monkeypatch.setenv("GOOGLE_GENAI_USE_VERTEXAI", "true")
monkeypatch.setenv("GOOGLE_CLOUD_PROJECT", "env-project")
monkeypatch.setenv("GOOGLE_CLOUD_LOCATION", "us-west1")
with patch("mem0.llms.gemini.genai.Client") as mock_client_class:
GeminiLLM(BaseLlmConfig(model="gemini-2.0-flash", api_key="ignored-key"))
mock_client_class.assert_called_once_with(vertexai=True, project="env-project", location="us-west1")
+14
View File
@@ -115,6 +115,20 @@ def test_generate_response_with_tools(mock_langchain_model):
assert response["tool_calls"][0]["arguments"] == {"data": "Today is a sunny day."}
def test_generate_response_forwards_extra_kwargs(mock_langchain_model):
"""Per the LLMBase contract, extra model kwargs must be accepted and forwarded to
the underlying LangChain model's ``invoke``."""
config = BaseLlmConfig(model=mock_langchain_model, temperature=0.7, max_tokens=100, api_key="test-api-key")
llm = LangchainLLM(config)
messages = [{"role": "user", "content": "Hello"}]
response = llm.generate_response(messages, frequency_penalty=0.5)
mock_langchain_model.invoke.assert_called_once()
assert mock_langchain_model.invoke.call_args.kwargs["frequency_penalty"] == 0.5
assert response == "This is a test response"
def test_invalid_model():
"""Test that LangchainLLM raises an error with an invalid model."""
config = BaseLlmConfig(model="not-a-valid-model-instance", temperature=0.7, max_tokens=100, api_key="test-api-key")
+53 -1
View File
@@ -19,8 +19,26 @@ def test_generate_response_with_unsupported_model(mock_litellm):
mock_litellm.supports_function_calling.return_value = False
tools = [{"type": "function", "function": {"name": "test", "parameters": {}}}]
with pytest.raises(ValueError, match="Model 'unsupported-model' in litellm does not support function calling."):
llm.generate_response(messages)
llm.generate_response(messages, tools=tools)
def test_generate_response_with_unsupported_model_no_tools(mock_litellm):
config = BaseLlmConfig(model="unsupported-model", temperature=0.7, max_tokens=100, top_p=1)
llm = litellm.LiteLLM(config)
messages = [{"role": "user", "content": "Hello"}]
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content="hi"))]
mock_litellm.completion.return_value = mock_response
mock_litellm.supports_function_calling.return_value = False
response = llm.generate_response(messages)
assert response == "hi"
mock_litellm.supports_function_calling.assert_not_called()
def test_generate_response_without_tools(mock_litellm):
@@ -44,6 +62,40 @@ def test_generate_response_without_tools(mock_litellm):
assert response == "I'm doing well, thank you for asking!"
def test_generate_response_gpt5_uses_max_completion_tokens(mock_litellm):
config = BaseLlmConfig(model="gpt-5.4-mini", temperature=0.7, max_tokens=100, top_p=1)
llm = litellm.LiteLLM(config)
messages = [{"role": "user", "content": "Hello"}]
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content="Hi"))]
mock_litellm.completion.return_value = mock_response
mock_litellm.supports_function_calling.return_value = True
llm.generate_response(messages)
_, kwargs = mock_litellm.completion.call_args
assert kwargs["max_completion_tokens"] == 100
assert "max_tokens" not in kwargs
def test_generate_response_legacy_model_uses_max_tokens(mock_litellm):
config = BaseLlmConfig(model="gpt-4.1", temperature=0.7, max_tokens=100, top_p=1)
llm = litellm.LiteLLM(config)
messages = [{"role": "user", "content": "Hello"}]
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content="Hi"))]
mock_litellm.completion.return_value = mock_response
mock_litellm.supports_function_calling.return_value = True
llm.generate_response(messages)
_, kwargs = mock_litellm.completion.call_args
assert kwargs["max_tokens"] == 100
assert "max_completion_tokens" not in kwargs
def test_generate_response_with_tools(mock_litellm):
config = BaseLlmConfig(model="gpt-4.1-nano-2025-04-14", temperature=0.7, max_tokens=100, top_p=1)
llm = litellm.LiteLLM(config)
+39
View File
@@ -375,6 +375,45 @@ def test_is_reasoning_model_override_generates_correct_params(mock_openai_client
assert "temperature" not in call_kwargs
def test_gpt5_uses_max_completion_tokens(mock_openai_client):
"""gpt-5.x (non-reasoning) must send max_completion_tokens, not max_tokens.
The GPT-5 family rejects the legacy max_tokens param on Chat Completions and
requires max_completion_tokens. Regression test for
https://github.com/mem0ai/mem0/issues/5054
"""
config = OpenAIConfig(model="gpt-5.4-mini", temperature=0.7, max_tokens=100, top_p=1.0)
llm = OpenAILLM(config)
messages = [{"role": "user", "content": "Hello"}]
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content="ok"))]
mock_openai_client.chat.completions.create.return_value = mock_response
llm.generate_response(messages)
call_kwargs = mock_openai_client.chat.completions.create.call_args[1]
assert call_kwargs.get("max_completion_tokens") == 100
assert "max_tokens" not in call_kwargs
def test_gpt4_uses_max_tokens(mock_openai_client):
"""Older models (gpt-4.x) keep using max_tokens — guards against regressions."""
config = OpenAIConfig(model="gpt-4.1-nano-2025-04-14", temperature=0.7, max_tokens=100, top_p=1.0)
llm = OpenAILLM(config)
messages = [{"role": "user", "content": "Hello"}]
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content="ok"))]
mock_openai_client.chat.completions.create.return_value = mock_response
llm.generate_response(messages)
call_kwargs = mock_openai_client.chat.completions.create.call_args[1]
assert call_kwargs.get("max_tokens") == 100
assert "max_completion_tokens" not in call_kwargs
def test_callback_with_tools(mock_openai_client):
mock_callback = Mock()
config = OpenAIConfig(model="gpt-4.1-nano-2025-04-14", response_callback=mock_callback)
+51
View File
@@ -0,0 +1,51 @@
from unittest.mock import Mock, patch
import pytest
from mem0.configs.llms.openai import OpenAIConfig
from mem0.llms.openai_structured import OpenAIStructuredLLM
@pytest.fixture
def mock_openai_client():
with patch("mem0.llms.openai_structured.OpenAI") as mock_openai:
mock_client = Mock()
mock_openai.return_value = mock_client
yield mock_client
def _mock_parse(mock_client, content="ok"):
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content=content))]
mock_client.beta.chat.completions.parse.return_value = mock_response
def test_reasoning_model_drops_temperature(mock_openai_client):
"""Reasoning models reject `temperature`; structured output must not send it."""
config = OpenAIConfig(model="o3-mini", reasoning_effort="low")
llm = OpenAIStructuredLLM(config)
_mock_parse(mock_openai_client)
llm.generate_response([{"role": "user", "content": "Hello"}])
call_kwargs = mock_openai_client.beta.chat.completions.parse.call_args[1]
assert "temperature" not in call_kwargs # reasoning models don't accept temperature
assert "max_tokens" not in call_kwargs # also dropped for reasoning models
assert "top_p" not in call_kwargs # also dropped for reasoning models
assert call_kwargs["reasoning_effort"] == "low"
assert call_kwargs["model"] == "o3-mini"
def test_regular_model_sends_sampling_params(mock_openai_client):
"""Regular models still receive the standard sampling params."""
config = OpenAIConfig(model="gpt-4o", temperature=0.3)
llm = OpenAIStructuredLLM(config)
_mock_parse(mock_openai_client)
llm.generate_response([{"role": "user", "content": "Hello"}])
call_kwargs = mock_openai_client.beta.chat.completions.parse.call_args[1]
assert call_kwargs["temperature"] == 0.3
assert "max_tokens" in call_kwargs # standard sampling params still forwarded
assert "top_p" in call_kwargs
assert call_kwargs["model"] == "gpt-4o"
+46
View File
@@ -0,0 +1,46 @@
from unittest.mock import Mock, patch
import pytest
from mem0.configs.llms.base import BaseLlmConfig
from mem0.llms.sarvam import SarvamLLM
@pytest.fixture
def sarvam_llm():
config = BaseLlmConfig(model="sarvam-m", temperature=0.7, max_tokens=100, top_p=1.0, api_key="test-api-key")
return SarvamLLM(config)
def _mock_post(content="Hello there!"):
mock_response = Mock()
mock_response.raise_for_status.return_value = None
mock_response.json.return_value = {"choices": [{"message": {"content": content}}]}
return mock_response
def test_generate_response_returns_content(sarvam_llm):
with patch("mem0.llms.sarvam.requests.post", return_value=_mock_post("Hi!")) as mock_post:
messages = [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello, how are you?"},
]
response = sarvam_llm.generate_response(messages)
assert response == "Hi!"
sent_payload = mock_post.call_args.kwargs["json"]
assert sent_payload["model"] == "sarvam-m"
assert sent_payload["messages"] == messages
assert sent_payload["temperature"] == 0.7
def test_generate_response_forwards_extra_kwargs(sarvam_llm):
"""Per the LLMBase contract, extra provider-specific kwargs must be accepted and
forwarded into the Sarvam request payload."""
with patch("mem0.llms.sarvam.requests.post", return_value=_mock_post("Hi!")) as mock_post:
messages = [{"role": "user", "content": "Hello"}]
response = sarvam_llm.generate_response(messages, frequency_penalty=0.5)
assert response == "Hi!"
sent_payload = mock_post.call_args.kwargs["json"]
assert sent_payload["frequency_penalty"] == 0.5
+18
View File
@@ -84,3 +84,21 @@ def test_generate_response_with_tools(mock_together_client):
assert len(response["tool_calls"]) == 1
assert response["tool_calls"][0]["name"] == "add_memory"
assert response["tool_calls"][0]["arguments"] == {"data": "Today is a sunny day."}
def test_generate_response_forwards_extra_kwargs(mock_together_client):
"""Per the LLMBase contract, extra provider-specific kwargs must be accepted and
forwarded to the Together client (matching openai/deepseek/vllm/xai behavior)."""
config = BaseLlmConfig(model="mistralai/Mixtral-8x7B-Instruct-v0.1", temperature=0.7, max_tokens=100, top_p=1.0)
llm = TogetherLLM(config)
messages = [{"role": "user", "content": "Hello"}]
mock_response = Mock()
mock_response.choices = [Mock(message=Mock(content="Hi"))]
mock_together_client.chat.completions.create.return_value = mock_response
response = llm.generate_response(messages, frequency_penalty=0.5)
assert response == "Hi"
call_kwargs = mock_together_client.chat.completions.create.call_args.kwargs
assert call_kwargs["frequency_penalty"] == 0.5
+69
View File
@@ -0,0 +1,69 @@
import logging
from unittest.mock import Mock
from mem0.memory.main import (
ADD_CONTEXT_SEARCH_MAX_CHARS,
ADD_MAX_ENTITY_LINKS_PER_EVENT,
ADD_MAX_EXTRACTED_MEMORIES,
ADD_MEMORY_EMBEDDING_MAX_CHARS,
Memory,
SEARCH_QUERY_MAX_CHARS,
_cap_entity_keys,
_cap_extracted_memories,
_cap_memory_embedding_texts,
_tail_cap_text,
)
def test_tail_cap_text_keeps_recent_suffix():
text = "old-" + ("x" * ADD_CONTEXT_SEARCH_MAX_CHARS)
capped = _tail_cap_text(text, ADD_CONTEXT_SEARCH_MAX_CHARS)
assert capped == text[-ADD_CONTEXT_SEARCH_MAX_CHARS:]
def test_cap_extracted_memories_keeps_last_memories(caplog):
memories = [{"text": f"memory-{i}"} for i in range(ADD_MAX_EXTRACTED_MEMORIES + 3)]
with caplog.at_level(logging.WARNING):
capped = _cap_extracted_memories(memories)
assert capped == memories[-ADD_MAX_EXTRACTED_MEMORIES:]
assert any("Extracted memory cap applied" in record.message for record in caplog.records)
def test_cap_memory_embedding_texts_keeps_original_text_untouched(caplog):
original = "old-" + ("z" * ADD_MEMORY_EMBEDDING_MAX_CHARS)
with caplog.at_level(logging.WARNING):
capped = _cap_memory_embedding_texts([original])
assert capped == [original[-ADD_MEMORY_EMBEDDING_MAX_CHARS:]]
assert original.startswith("old-")
assert any("Memory embedding input cap applied" in record.message for record in caplog.records)
def test_cap_entity_keys_keeps_first_100_entities(caplog):
keys = [f"entity-{i}" for i in range(ADD_MAX_ENTITY_LINKS_PER_EVENT + 3)]
with caplog.at_level(logging.WARNING):
capped = _cap_entity_keys(keys)
assert capped == keys[:ADD_MAX_ENTITY_LINKS_PER_EVENT]
assert any("ADD entity-linking cap applied" in record.message for record in caplog.records)
def test_search_vector_store_caps_query_before_embedding():
memory = object.__new__(Memory)
memory.embedding_model = Mock()
memory.embedding_model.embed.return_value = [0.1, 0.2, 0.3]
memory.vector_store = Mock()
memory.vector_store.search.return_value = []
memory.vector_store.keyword_search.return_value = None
memory._compute_entity_boosts = Mock(return_value={})
query = "old-" + ("q" * SEARCH_QUERY_MAX_CHARS)
Memory._search_vector_store(memory, query, filters={}, limit=5)
memory.embedding_model.embed.assert_called_once_with(query[-SEARCH_QUERY_MAX_CHARS:], "search")
+41 -1
View File
@@ -1,10 +1,50 @@
import pytest
from unittest.mock import Mock
from mem0.memory.utils import parse_vision_messages, remove_spaces_from_entities, sanitize_relationship_for_cypher
from mem0.memory.utils import (
parse_messages,
parse_vision_messages,
remove_spaces_from_entities,
sanitize_relationship_for_cypher,
)
class TestParseMessages:
def test_skips_message_without_content_key(self):
# Reproduces #5067: a FunctionCalling assistant message carries
# `tool_calls` but no `content` key -> used to raise KeyError.
messages = [
{"role": "user", "content": "hi"},
{"role": "assistant", "tool_calls": [{"id": "1", "function": {"name": "search"}}]},
{"role": "assistant", "content": "done"},
]
result = parse_messages(messages)
assert result == "user: hi\nassistant: done\n"
def test_skips_explicit_none_content(self):
messages = [{"role": "assistant", "content": None}, {"role": "user", "content": "ok"}]
assert parse_messages(messages) == "user: ok\n"
def test_plain_roles_pass_through(self):
messages = [
{"role": "system", "content": "sys"},
{"role": "user", "content": "u"},
{"role": "assistant", "content": "a"},
]
assert parse_messages(messages) == "system: sys\nuser: u\nassistant: a\n"
class TestParseVisionMessages:
def test_skips_message_without_content_key(self):
# Reproduces #5067 for the vision parser path.
messages = [
{"role": "user", "content": "hi"},
{"role": "assistant", "tool_calls": [{"id": "1", "function": {"name": "search"}}]},
]
result = parse_vision_messages(messages, llm=None)
assert len(result) == 1
assert result[0] == {"role": "user", "content": "hi"}
def test_multimodal_list_without_llm_extracts_text(self):
messages = [
{"role": "user", "content": [
@@ -0,0 +1,109 @@
"""Regression tests for the reranker fallback path honoring ``config.top_k``.
When the underlying rerank call fails, the reranker falls back to returning the
documents in their original order. That fallback must still respect the
configured ``top_k`` limit, exactly like the success path does. The HuggingFace
and SentenceTransformer rerankers already behave this way; these tests pin the
same contract for the Cohere and ZeroEntropy rerankers.
"""
import sys
from types import ModuleType
from unittest.mock import MagicMock
import pytest
from mem0.configs.rerankers.cohere import CohereRerankerConfig
from mem0.configs.rerankers.zero_entropy import ZeroEntropyRerankerConfig
@pytest.fixture
def mock_cohere(monkeypatch):
"""Provide a fake ``cohere`` module so CohereReranker imports/constructs."""
fake_cohere = ModuleType("cohere")
fake_client = MagicMock()
fake_cohere.Client = MagicMock(return_value=fake_client)
monkeypatch.setitem(sys.modules, "cohere", fake_cohere)
import mem0.reranker.cohere_reranker as cohere_reranker
monkeypatch.setattr(cohere_reranker, "cohere", fake_cohere, raising=False)
monkeypatch.setattr(cohere_reranker, "COHERE_AVAILABLE", True, raising=False)
return cohere_reranker, fake_client
@pytest.fixture
def mock_zero_entropy(monkeypatch):
"""Provide a fake ``zeroentropy`` module so ZeroEntropyReranker imports."""
fake_module = ModuleType("zeroentropy")
fake_client = MagicMock()
fake_module.ZeroEntropy = MagicMock(return_value=fake_client)
monkeypatch.setitem(sys.modules, "zeroentropy", fake_module)
import mem0.reranker.zero_entropy_reranker as zero_entropy_reranker
monkeypatch.setattr(zero_entropy_reranker, "ZeroEntropy", fake_module.ZeroEntropy, raising=False)
monkeypatch.setattr(zero_entropy_reranker, "ZERO_ENTROPY_AVAILABLE", True, raising=False)
return zero_entropy_reranker, fake_client
def _docs(n):
return [{"memory": f"doc{i}"} for i in range(n)]
class TestCohereFallbackTopK:
def test_fallback_respects_config_top_k(self, mock_cohere):
module, fake_client = mock_cohere
fake_client.rerank.side_effect = RuntimeError("API error")
reranker = module.CohereReranker(CohereRerankerConfig(api_key="test-key", top_k=2))
result = reranker.rerank("query", _docs(5))
assert len(result) == 2
def test_fallback_per_call_top_k_overrides_config(self, mock_cohere):
module, fake_client = mock_cohere
fake_client.rerank.side_effect = RuntimeError("API error")
reranker = module.CohereReranker(CohereRerankerConfig(api_key="test-key", top_k=4))
result = reranker.rerank("query", _docs(5), top_k=1)
assert len(result) == 1
def test_fallback_returns_all_when_no_top_k(self, mock_cohere):
module, fake_client = mock_cohere
fake_client.rerank.side_effect = RuntimeError("API error")
reranker = module.CohereReranker(CohereRerankerConfig(api_key="test-key"))
result = reranker.rerank("query", _docs(5))
assert len(result) == 5
class TestZeroEntropyFallbackTopK:
def test_fallback_respects_config_top_k(self, mock_zero_entropy):
module, fake_client = mock_zero_entropy
fake_client.models.rerank.side_effect = RuntimeError("API error")
reranker = module.ZeroEntropyReranker(ZeroEntropyRerankerConfig(api_key="test-key", top_k=2))
result = reranker.rerank("query", _docs(5))
assert len(result) == 2
def test_fallback_per_call_top_k_overrides_config(self, mock_zero_entropy):
module, fake_client = mock_zero_entropy
fake_client.models.rerank.side_effect = RuntimeError("API error")
reranker = module.ZeroEntropyReranker(ZeroEntropyRerankerConfig(api_key="test-key", top_k=4))
result = reranker.rerank("query", _docs(5), top_k=1)
assert len(result) == 1
def test_fallback_returns_all_when_no_top_k(self, mock_zero_entropy):
module, fake_client = mock_zero_entropy
fake_client.models.rerank.side_effect = RuntimeError("API error")
reranker = module.ZeroEntropyReranker(ZeroEntropyRerankerConfig(api_key="test-key"))
result = reranker.rerank("query", _docs(5))
assert len(result) == 5
+200
View File
@@ -762,6 +762,55 @@ async def test_async_delete_memory_history_has_timestamps(mock_sqlite, mock_llm_
datetime.fromisoformat(call_kwargs["updated_at"]) # verify valid ISO timestamp
@pytest.mark.asyncio
@patch('mem0.utils.factory.EmbedderFactory.create')
@patch('mem0.utils.factory.VectorStoreFactory.create')
@patch('mem0.utils.factory.LlmFactory.create')
@patch('mem0.memory.main.SQLiteManager')
async def test_async_delete_all_continues_on_partial_failure(mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
"""async delete_all must not abort when a single memory fails to delete.
Without return_exceptions=True, asyncio.gather raises on the first error
and cancels remaining tasks, leaving a partial deletion.
"""
mock_embedder_factory.return_value = MagicMock()
mock_vector_store = MagicMock()
mock_vector_factory.return_value = mock_vector_store
mock_llm_factory.return_value = MagicMock()
mock_sqlite.return_value = MagicMock()
from mem0.memory.main import AsyncMemory
config = MemoryConfig()
memory = AsyncMemory(config)
mem1 = MagicMock()
mem1.id = "mem-1"
mem1.payload = {"data": "one", "created_at": "2024-01-01T00:00:00+00:00", "actor_id": None, "role": None}
mem2 = MagicMock()
mem2.id = "mem-2"
mem2.payload = {"data": "two", "created_at": "2024-01-01T00:00:00+00:00", "actor_id": None, "role": None}
mem3 = MagicMock()
mem3.id = "mem-3"
mem3.payload = {"data": "three", "created_at": "2024-01-01T00:00:00+00:00", "actor_id": None, "role": None}
mock_vector_store.list.return_value = ([mem1, mem2, mem3],)
def _get_side_effect(vector_id):
if vector_id == "mem-2":
raise RuntimeError("simulated store failure")
return {
"mem-1": mem1,
"mem-3": mem3,
}.get(vector_id)
mock_vector_store.get.side_effect = _get_side_effect
result = await memory.delete_all(user_id="test-user")
assert result == {"message": "Memories deleted successfully!"}
assert mock_vector_store.delete.call_count == 2
@patch('mem0.utils.factory.EmbedderFactory.create')
@patch('mem0.utils.factory.VectorStoreFactory.create')
@patch('mem0.utils.factory.LlmFactory.create')
@@ -1056,3 +1105,154 @@ class TestHybridSearchWarning:
Memory(config)
assert not any("does not support keyword search" in r.message for r in caplog.records)
class TestPreserveCustomMetadata:
@patch('mem0.utils.factory.EmbedderFactory.create')
@patch('mem0.utils.factory.VectorStoreFactory.create')
@patch('mem0.utils.factory.LlmFactory.create')
@patch('mem0.memory.storage.SQLiteManager')
def test_update_preserves_custom_metadata(self, mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
mock_embedder_factory.return_value = MagicMock()
mock_vector_store = MagicMock()
mock_vector_factory.return_value = mock_vector_store
mock_llm_factory.return_value = MagicMock()
mock_sqlite.return_value = MagicMock()
existing_payload = {
"data": "I love playing tennis",
"hash": "abc123",
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-01T00:00:00+00:00",
"user_id": "user_1",
"category": "hobbies",
"priority": "high",
"source": "chat",
}
mock_vector_store.get.return_value = MockVectorMemory("mem-1", existing_payload)
embedder = MagicMock()
embedder.embed.return_value = [0.1, 0.2, 0.3]
mock_embedder_factory.return_value = embedder
config = MemoryConfig()
memory = Memory(config)
memory._update_memory("mem-1", "I love playing tennis and swimming", {"I love playing tennis and swimming": [0.1, 0.2, 0.3]})
call_args = mock_vector_store.update.call_args
payload = call_args.kwargs.get("payload") or call_args[1].get("payload")
assert payload["category"] == "hobbies"
assert payload["priority"] == "high"
assert payload["source"] == "chat"
assert payload["data"] == "I love playing tennis and swimming"
assert payload["user_id"] == "user_1"
@patch('mem0.utils.factory.EmbedderFactory.create')
@patch('mem0.utils.factory.VectorStoreFactory.create')
@patch('mem0.utils.factory.LlmFactory.create')
@patch('mem0.memory.storage.SQLiteManager')
def test_update_with_new_metadata_overrides_existing(self, mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
mock_embedder_factory.return_value = MagicMock()
mock_vector_store = MagicMock()
mock_vector_factory.return_value = mock_vector_store
mock_llm_factory.return_value = MagicMock()
mock_sqlite.return_value = MagicMock()
existing_payload = {
"data": "I love playing tennis",
"hash": "abc123",
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-01T00:00:00+00:00",
"user_id": "user_1",
"category": "hobbies",
"priority": "high",
}
mock_vector_store.get.return_value = MockVectorMemory("mem-1", existing_payload)
config = MemoryConfig()
memory = Memory(config)
memory._update_memory(
"mem-1", "Updated text",
{"Updated text": [0.1, 0.2, 0.3]},
metadata={"priority": "low", "new_field": "value"},
)
call_args = mock_vector_store.update.call_args
payload = call_args.kwargs.get("payload") or call_args[1].get("payload")
assert payload["category"] == "hobbies"
assert payload["priority"] == "low"
assert payload["new_field"] == "value"
assert payload["data"] == "Updated text"
@patch('mem0.utils.factory.EmbedderFactory.create')
@patch('mem0.utils.factory.VectorStoreFactory.create')
@patch('mem0.utils.factory.LlmFactory.create')
@patch('mem0.memory.storage.SQLiteManager')
def test_update_preserves_actor_id_from_original(self, mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
mock_embedder_factory.return_value = MagicMock()
mock_vector_store = MagicMock()
mock_vector_factory.return_value = mock_vector_store
mock_llm_factory.return_value = MagicMock()
mock_sqlite.return_value = MagicMock()
existing_payload = {
"data": "I am player #1",
"hash": "abc123",
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-01T00:00:00+00:00",
"user_id": "team",
"actor_id": "Alice",
}
mock_vector_store.get.return_value = MockVectorMemory("mem-1", existing_payload)
config = MemoryConfig()
memory = Memory(config)
memory._update_memory(
"mem-1", "Player #1 is great",
{"Player #1 is great": [0.1, 0.2, 0.3]},
metadata={"user_id": "team", "actor_id": "Bob"},
)
call_args = mock_vector_store.update.call_args
payload = call_args.kwargs.get("payload") or call_args[1].get("payload")
assert payload["actor_id"] == "Alice"
@pytest.mark.asyncio
@patch('mem0.utils.factory.EmbedderFactory.create')
@patch('mem0.utils.factory.VectorStoreFactory.create')
@patch('mem0.utils.factory.LlmFactory.create')
@patch('mem0.memory.storage.SQLiteManager')
async def test_async_update_preserves_custom_metadata(self, mock_sqlite, mock_llm_factory, mock_vector_factory, mock_embedder_factory):
mock_embedder_factory.return_value = MagicMock()
mock_vector_store = MagicMock()
mock_vector_factory.return_value = mock_vector_store
mock_llm_factory.return_value = MagicMock()
mock_sqlite.return_value = MagicMock()
existing_payload = {
"data": "I love playing tennis",
"hash": "abc123",
"created_at": "2026-01-01T00:00:00+00:00",
"updated_at": "2026-01-01T00:00:00+00:00",
"user_id": "user_1",
"category": "hobbies",
"source": "chat",
}
mock_vector_store.get.return_value = MockVectorMemory("mem-1", existing_payload)
from mem0.memory.main import AsyncMemory
config = MemoryConfig()
memory = AsyncMemory(config)
await memory._update_memory("mem-1", "I love swimming", {"I love swimming": [0.1, 0.2, 0.3]})
call_args = mock_vector_store.update.call_args
payload = call_args.kwargs.get("payload") or call_args[1].get("payload")
assert payload["category"] == "hobbies"
assert payload["source"] == "chat"
assert payload["data"] == "I love swimming"
assert payload["user_id"] == "user_1"
+4 -201
View File
@@ -164,68 +164,11 @@ class TestMarkAliased:
assert not setup_module.is_aliased("oss-uuid", "other@example.com")
# ─── oss_used marker ─────────────────────────────────────────────────────────
class TestOssUsedMarker:
def test_mark_writes_marker_preserving_fields(self, tmp_mem0_dir):
import mem0.memory.setup as setup_module
_write_config(
tmp_mem0_dir,
{
"user_id": "oss-uuid",
"telemetry": {"anonymous_id": "cli-anon", "aliased_pairs": ["pair-marker"]},
},
)
setup_module.mark_oss_used()
config = json.loads((tmp_mem0_dir / "config.json").read_text())
assert config["user_id"] == "oss-uuid"
assert config["telemetry"]["anonymous_id"] == "cli-anon"
assert config["telemetry"]["aliased_pairs"] == ["pair-marker"]
assert config["telemetry"]["oss_used_at"]
assert setup_module.is_oss_used()
def test_mark_is_idempotent(self, tmp_mem0_dir):
import mem0.memory.setup as setup_module
setup_module.mark_oss_used()
first = json.loads((tmp_mem0_dir / "config.json").read_text())["telemetry"]["oss_used_at"]
setup_module.mark_oss_used()
second = json.loads((tmp_mem0_dir / "config.json").read_text())["telemetry"]["oss_used_at"]
assert first == second
def test_is_oss_used_false_when_no_config(self, tmp_mem0_dir):
import mem0.memory.setup as setup_module
assert not setup_module.is_oss_used()
def test_is_oss_used_false_without_marker(self, tmp_mem0_dir):
import mem0.memory.setup as setup_module
_write_config(tmp_mem0_dir, {"user_id": "oss-uuid", "telemetry": {"anonymous_id": "cli-anon"}})
assert not setup_module.is_oss_used()
def test_is_oss_used_false_on_malformed_json(self, tmp_mem0_dir):
import mem0.memory.setup as setup_module
(tmp_mem0_dir / "config.json").write_text("{not json")
assert not setup_module.is_oss_used()
def test_mark_does_not_raise_when_config_path_unwritable(self, tmp_mem0_dir):
import mem0.memory.setup as setup_module
(tmp_mem0_dir / "config.json").mkdir() # open() on a directory fails
setup_module.mark_oss_used() # must not raise
assert not setup_module.is_oss_used()
# ─── capture_identify ────────────────────────────────────────────────────────
class TestCaptureIdentify:
def test_fires_identify_with_anon_distinct_id(self):
import mem0
import mem0.memory.telemetry as telemetry_module
with patch.object(telemetry_module, "MEM0_TELEMETRY", True):
@@ -238,7 +181,6 @@ class TestCaptureIdentify:
assert kwargs["distinct_id"] == "user@example.com"
assert kwargs["event"] == "$identify"
assert kwargs["properties"]["$anon_distinct_id"] == "anon-123"
assert kwargs["properties"]["client_version"] == mem0.__version__
def test_skips_when_anon_equals_email(self):
import mem0.memory.telemetry as telemetry_module
@@ -288,7 +230,8 @@ class TestCaptureIdentify:
class TestMaybeAliasAnonToEmail:
"""Test the alias helper in isolation by mocking out the config readers
and the telemetry client, so no real ~/.mem0/config.json is touched."""
and the telemetry client, since module-level setup_config() side effects
make end-to-end fixturing awkward."""
def test_fires_identify_for_oss_uuid(self):
from mem0.client import main as client_main
@@ -299,7 +242,6 @@ class TestMaybeAliasAnonToEmail:
"read_anon_ids",
return_value={"oss": "oss-uuid", "cli": None, "aliased_pairs": []},
),
patch.object(client_main, "is_oss_used", return_value=True),
patch.object(client_main, "is_aliased", return_value=False),
patch.object(client_main, "mark_aliased") as mark,
patch.object(client_main, "client_telemetry") as telemetry,
@@ -310,7 +252,6 @@ class TestMaybeAliasAnonToEmail:
mark.assert_called_once_with("oss-uuid", "user@example.com")
def test_fires_identify_for_cli_anon(self):
"""The CLI anon id is presence-based: it stitches even without the oss_used marker."""
from mem0.client import main as client_main
with (
@@ -319,7 +260,6 @@ class TestMaybeAliasAnonToEmail:
"read_anon_ids",
return_value={"oss": None, "cli": "cli-anon-xyz", "aliased_pairs": []},
),
patch.object(client_main, "is_oss_used", return_value=False),
patch.object(client_main, "is_aliased", return_value=False),
patch.object(client_main, "mark_aliased"),
patch.object(client_main, "client_telemetry") as telemetry,
@@ -337,7 +277,6 @@ class TestMaybeAliasAnonToEmail:
"read_anon_ids",
return_value={"oss": "oss-uuid", "cli": "cli-anon", "aliased_pairs": []},
),
patch.object(client_main, "is_oss_used", return_value=True),
patch.object(client_main, "is_aliased", return_value=False),
patch.object(client_main, "mark_aliased"),
patch.object(client_main, "client_telemetry") as telemetry,
@@ -349,26 +288,6 @@ class TestMaybeAliasAnonToEmail:
assert ("oss-uuid", "user@example.com") in calls
assert ("cli-anon", "user@example.com") in calls
def test_oss_id_requires_oss_used_marker(self):
"""Without proof of real OSS usage, the oss user_id must never be
stitched (it may have been minted by an old platform-only client)."""
from mem0.client import main as client_main
with (
patch.object(
client_main,
"read_anon_ids",
return_value={"oss": "oss-uuid", "cli": "cli-anon", "aliased_pairs": []},
),
patch.object(client_main, "is_oss_used", return_value=False),
patch.object(client_main, "is_aliased", return_value=False),
patch.object(client_main, "mark_aliased"),
patch.object(client_main, "client_telemetry") as telemetry,
):
telemetry.capture_identify.return_value = True
client_main._maybe_alias_anon_to_email("user@example.com")
telemetry.capture_identify.assert_called_once_with("cli-anon", "user@example.com")
def test_skips_when_pair_already_aliased(self):
from mem0.client import main as client_main
@@ -378,7 +297,6 @@ class TestMaybeAliasAnonToEmail:
"read_anon_ids",
return_value={"oss": "oss-uuid", "cli": None, "aliased_pairs": ["pair-marker"]},
),
patch.object(client_main, "is_oss_used", return_value=True),
patch.object(client_main, "is_aliased", return_value=True),
patch.object(client_main, "mark_aliased") as mark,
patch.object(client_main, "client_telemetry") as telemetry,
@@ -424,7 +342,6 @@ class TestMaybeAliasAnonToEmail:
"read_anon_ids",
return_value={"oss": "oss-uuid", "cli": None, "aliased_pairs": []},
),
patch.object(client_main, "is_oss_used", return_value=True),
patch.object(client_main, "is_aliased", return_value=False),
patch.object(client_main, "mark_aliased") as mark,
patch.object(client_main, "client_telemetry", mock_telemetry),
@@ -442,7 +359,6 @@ class TestMaybeAliasAnonToEmail:
"read_anon_ids",
return_value={"oss": "user@example.com", "cli": None, "aliased_pairs": []},
),
patch.object(client_main, "is_oss_used", return_value=True),
patch.object(client_main, "is_aliased", return_value=False),
patch.object(client_main, "mark_aliased"),
patch.object(client_main, "client_telemetry") as telemetry,
@@ -470,12 +386,8 @@ class TestEndToEndIdempotency:
calls fire $identify exactly once thanks to the persisted pair marker."""
def test_second_call_is_noop_after_pair_marker_persisted(self, tmp_mem0_dir):
# Pre-populate config with an OSS user_id plus the oss_used_at marker
# (without the marker the oss id is no longer a stitch candidate).
_write_config(
tmp_mem0_dir,
{"user_id": "oss-uuid", "telemetry": {"oss_used_at": "2026-01-01T00:00:00+00:00"}},
)
# Pre-populate config with an OSS user_id only.
_write_config(tmp_mem0_dir, {"user_id": "oss-uuid"})
# Reload setup so it uses the tempdir, then reload client.main so it
# picks up the freshly-loaded read_anon_ids/mark_aliased bindings.
import mem0.memory.setup as setup_module
@@ -497,112 +409,3 @@ class TestEndToEndIdempotency:
config = json.loads((tmp_mem0_dir / "config.json").read_text())
assert len(config["telemetry"]["aliased_pairs"]) == 1
def test_oss_then_platform_stitches_exactly_once(self, tmp_mem0_dir):
"""Genuine OSS usage (setup_config + mark_oss_used, as Memory.__init__
does) followed by platform inits stitches the minted id exactly once."""
import mem0.memory.setup as setup_module
setup_module.setup_config()
setup_module.mark_oss_used()
oss_id = json.loads((tmp_mem0_dir / "config.json").read_text())["user_id"]
from mem0.client import main as client_main
importlib.reload(client_main)
with patch.object(client_main, "client_telemetry") as telemetry:
telemetry.capture_identify.return_value = True
client_main._maybe_alias_anon_to_email("user@example.com")
telemetry.capture_identify.assert_called_once_with(oss_id, "user@example.com")
client_main._maybe_alias_anon_to_email("user@example.com")
assert telemetry.capture_identify.call_count == 1
config = json.loads((tmp_mem0_dir / "config.json").read_text())
assert len(config["telemetry"]["aliased_pairs"]) == 1
assert config["telemetry"]["oss_used_at"]
# ─── Platform-only init must not mint or stitch ──────────────────────────────
class TestPlatformOnlyDoesNotMint:
"""Regression for the bug where importing the client minted an anon id at
import time and then stitched it on MemoryClient init, merging an
event-less anon person into every platform user."""
def test_no_config_created_and_no_identify(self, tmp_mem0_dir):
from mem0.client import main as client_main
# Re-execute the client module: it must not create config.json.
importlib.reload(client_main)
assert not (tmp_mem0_dir / "config.json").exists()
with patch.object(client_main, "client_telemetry") as telemetry:
telemetry.capture_identify.return_value = True
client_main._maybe_alias_anon_to_email("user@example.com")
telemetry.capture_identify.assert_not_called()
assert not (tmp_mem0_dir / "config.json").exists()
# ─── Lazy minting from OSS Memory init ───────────────────────────────────────
class TestLazyOssMinting:
"""OSS Memory/AsyncMemory init mints the anon id and records genuine OSS
usage; nothing happens at import time or with telemetry disabled."""
def test_memory_init_mints_and_marks(self, tmp_path):
import mem0.memory.main as memory_main
with (
patch.object(memory_main, "EmbedderFactory"),
patch.object(memory_main, "VectorStoreFactory"),
patch.object(memory_main, "LlmFactory"),
patch.object(memory_main, "SQLiteManager"),
patch.object(memory_main, "capture_event"),
patch.object(memory_main, "mem0_dir", str(tmp_path)),
patch.object(memory_main, "setup_config") as setup,
patch.object(memory_main, "mark_oss_used") as mark,
patch.object(memory_main, "MEM0_TELEMETRY", True),
):
memory_main.Memory()
setup.assert_called_once()
mark.assert_called_once()
def test_memory_init_skips_minting_when_telemetry_disabled(self, tmp_path):
import mem0.memory.main as memory_main
with (
patch.object(memory_main, "EmbedderFactory"),
patch.object(memory_main, "VectorStoreFactory"),
patch.object(memory_main, "LlmFactory"),
patch.object(memory_main, "SQLiteManager"),
patch.object(memory_main, "capture_event"),
patch.object(memory_main, "mem0_dir", str(tmp_path)),
patch.object(memory_main, "setup_config") as setup,
patch.object(memory_main, "mark_oss_used") as mark,
patch.object(memory_main, "MEM0_TELEMETRY", False),
):
memory_main.Memory()
setup.assert_not_called()
mark.assert_not_called()
def test_async_memory_init_mints_and_marks(self, tmp_path):
import mem0.memory.main as memory_main
with (
patch.object(memory_main, "EmbedderFactory"),
patch.object(memory_main, "VectorStoreFactory"),
patch.object(memory_main, "LlmFactory"),
patch.object(memory_main, "SQLiteManager"),
patch.object(memory_main, "capture_event"),
patch.object(memory_main, "mem0_dir", str(tmp_path)),
patch.object(memory_main, "setup_config") as setup,
patch.object(memory_main, "mark_oss_used") as mark,
patch.object(memory_main, "MEM0_TELEMETRY", True),
):
memory_main.AsyncMemory()
setup.assert_called_once()
mark.assert_called_once()
+10
View File
@@ -154,6 +154,16 @@ def test_get_vector(chromadb_instance):
assert result.payload == {"name": "vector1"}
def test_get_missing_vector_returns_none(chromadb_instance):
# Chroma returns empty lists for an unknown id; get() must return None
# rather than raising IndexError (parity with qdrant/pgvector/faiss).
chromadb_instance.collection.get.return_value = {"ids": [], "metadatas": []}
result = chromadb_instance.get(vector_id="does-not-exist")
assert result is None
def test_list_vectors(chromadb_instance):
mock_result = {
"ids": [["id1", "id2"]],
+7 -1
View File
@@ -189,11 +189,17 @@ class TestMilvusDB:
]
result = milvus_db.get(vector_id)
assert result.id == vector_id
assert result.payload == {"user_id": "alice"}
assert result.score is None
def test_get_missing_returns_none(self, milvus_db, mock_milvus_client):
"""get() must return None (not raise IndexError) for an unknown id."""
mock_milvus_client.get.return_value = []
assert milvus_db.get("missing") is None
def test_list_with_filters(self, milvus_db, mock_milvus_client):
"""Test listing memories with filters."""
mock_milvus_client.query.return_value = [
+42
View File
@@ -71,3 +71,45 @@ def test_update_with_vector_includes_embedding():
)
expected_bytes = np.array(vector, dtype=np.float32).tobytes()
assert data_dict["embedding"] == expected_bytes
def test_search_with_none_filters_does_not_crash():
"""search() with the base-class default filters=None must run an unfiltered
query, not raise. Regression: filters.items() on None raised AttributeError
and reduce() over an empty list raised TypeError. keyword_search() in the
same file already guards this; search() did not."""
db, mock_index = _make_redis_db()
mock_index.query.return_value = []
assert db.search("query", [0.1, 0.2, 0.3, 0.4], top_k=5, filters=None) == []
mock_index.query.assert_called_once()
def test_search_with_empty_or_all_none_filters_does_not_crash():
db, mock_index = _make_redis_db()
mock_index.query.return_value = []
assert db.search("query", [0.1, 0.2, 0.3, 0.4], filters={}) == []
assert db.search("query", [0.1, 0.2, 0.3, 0.4], filters={"user_id": None}) == []
def test_list_with_none_filters_matches_all():
"""list() with no filters must issue a match-all ('*') query, not raise."""
db, mock_index = _make_redis_db()
mock_index.search.return_value = MagicMock(docs=[])
db.list(filters=None)
query = mock_index.search.call_args[0][0]
assert query.query_string() == "*"
def test_list_with_filter_builds_query():
"""A real filter is still translated into a tag query (no regression)."""
db, mock_index = _make_redis_db()
mock_index.search.return_value = MagicMock(docs=[])
db.list(filters={"user_id": "alice"})
query = mock_index.search.call_args[0][0]
assert query.query_string() == "@user_id:{alice}"
+7
View File
@@ -111,6 +111,13 @@ def test_get_vector(supabase_instance, mock_collection):
assert result.payload == {"name": "vector1"}
def test_get_missing_returns_none(supabase_instance, mock_collection):
# An unknown id yields an empty fetch; get() must return None (not []).
mock_collection.fetch.return_value = []
assert supabase_instance.get(vector_id="missing") is None
def test_list_vectors(supabase_instance, mock_collection):
mock_query_results = [("id1", 0.9, {}), ("id2", 0.8, {})]
mock_fetch_results = [("id1", [0.1, 0.2, 0.3], {"name": "vector1"}), ("id2", [0.4, 0.5, 0.6], {"name": "vector2"})]
+6 -6
View File
@@ -4,9 +4,7 @@ import uuid
from unittest.mock import MagicMock, patch
import dotenv
import httpx
import weaviate
from weaviate.exceptions import UnexpectedStatusCodeException
from mem0.vector_stores.weaviate import Weaviate
@@ -112,11 +110,13 @@ class TestWeaviateDB(unittest.TestCase):
assert result.payload == expected_payload
def test_get_not_found(self):
mock_response = httpx.Response(status_code=404, json={"error": "Not found"})
# fetch_object_by_id returns None for an unknown id; get() must return
# None rather than raising AttributeError on response.properties.
self.client_mock.collections.get.return_value.query.fetch_object_by_id.return_value = None
self.client_mock.collections.get.return_value.data.get_by_id.side_effect = UnexpectedStatusCodeException(
"Not found", mock_response
)
result = self.weaviate_db.get(vector_id=str(uuid.uuid4()))
assert result is None
def test_search(self):
mock_objects = [{"uuid": "id1", "properties": {"key1": "value1"}, "metadata": {"distance": 0.2}}]