chore: import upstream snapshot with attribution
pi-agent-plugin checks / lint (push) Has been cancelled
pi-agent-plugin checks / test (20) (push) Has been cancelled
pi-agent-plugin checks / test (22) (push) Has been cancelled
pi-agent-plugin checks / build (push) Has been cancelled
TypeScript SDK CI / check_changes (push) Has been cancelled
TypeScript SDK CI / changelog_check (push) Has been cancelled
ci / changelog_check (push) Has been cancelled
ci / check_changes (push) Has been cancelled
ci / build_mem0 (3.10) (push) Has been cancelled
ci / build_mem0 (3.11) (push) Has been cancelled
ci / build_mem0 (3.12) (push) Has been cancelled
CLI Node CI / lint (push) Has been cancelled
CLI Node CI / test (20) (push) Has been cancelled
CLI Node CI / test (22) (push) Has been cancelled
CLI Node CI / build (push) Has been cancelled
CLI Python CI / lint (push) Has been cancelled
CLI Python CI / test (3.10) (push) Has been cancelled
CLI Python CI / test (3.11) (push) Has been cancelled
CLI Python CI / test (3.12) (push) Has been cancelled
CLI Python CI / build (push) Has been cancelled
openclaw checks / lint (push) Has been cancelled
openclaw checks / test (20) (push) Has been cancelled
openclaw checks / test (22) (push) Has been cancelled
openclaw checks / build (push) Has been cancelled
opencode-plugin checks / build (push) Has been cancelled
TypeScript SDK CI / build_ts_sdk (20) (push) Has been cancelled
TypeScript SDK CI / build_ts_sdk (22) (push) Has been cancelled
TypeScript SDK CI / integration_ts_sdk (20) (push) Has been cancelled
TypeScript SDK CI / integration_ts_sdk (22) (push) Has been cancelled
pi-agent-plugin checks / lint (push) Has been cancelled
pi-agent-plugin checks / test (20) (push) Has been cancelled
pi-agent-plugin checks / test (22) (push) Has been cancelled
pi-agent-plugin checks / build (push) Has been cancelled
TypeScript SDK CI / check_changes (push) Has been cancelled
TypeScript SDK CI / changelog_check (push) Has been cancelled
ci / changelog_check (push) Has been cancelled
ci / check_changes (push) Has been cancelled
ci / build_mem0 (3.10) (push) Has been cancelled
ci / build_mem0 (3.11) (push) Has been cancelled
ci / build_mem0 (3.12) (push) Has been cancelled
CLI Node CI / lint (push) Has been cancelled
CLI Node CI / test (20) (push) Has been cancelled
CLI Node CI / test (22) (push) Has been cancelled
CLI Node CI / build (push) Has been cancelled
CLI Python CI / lint (push) Has been cancelled
CLI Python CI / test (3.10) (push) Has been cancelled
CLI Python CI / test (3.11) (push) Has been cancelled
CLI Python CI / test (3.12) (push) Has been cancelled
CLI Python CI / build (push) Has been cancelled
openclaw checks / lint (push) Has been cancelled
openclaw checks / test (20) (push) Has been cancelled
openclaw checks / test (22) (push) Has been cancelled
openclaw checks / build (push) Has been cancelled
opencode-plugin checks / build (push) Has been cancelled
TypeScript SDK CI / build_ts_sdk (20) (push) Has been cancelled
TypeScript SDK CI / build_ts_sdk (22) (push) Has been cancelled
TypeScript SDK CI / integration_ts_sdk (20) (push) Has been cancelled
TypeScript SDK CI / integration_ts_sdk (22) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,201 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [2026] [Taranjeet Singh]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -0,0 +1,101 @@
|
||||
# @mem0/opencode-plugin
|
||||
|
||||
Persistent memory for [OpenCode](https://opencode.ai). Your agent remembers decisions, preferences, and learnings across sessions automatically.
|
||||
|
||||
## Install
|
||||
|
||||
```bash
|
||||
opencode plugin @mem0/opencode-plugin
|
||||
```
|
||||
|
||||
This adds the plugin to your `~/.config/opencode/opencode.json`. The plugin registers its memory tools and skills itself — there is no MCP server to configure.
|
||||
|
||||
**Or let your agent do it** — paste this into OpenCode:
|
||||
|
||||
```
|
||||
Install @mem0/opencode-plugin by following https://raw.githubusercontent.com/mem0ai/mem0/main/integrations/mem0-plugin/.opencode-plugin/README.md
|
||||
```
|
||||
|
||||
Get your API key (free): [app.mem0.ai/dashboard/api-keys](https://app.mem0.ai/dashboard/api-keys)
|
||||
|
||||
```bash
|
||||
echo 'export MEM0_API_KEY="m0-your-key"' >> ~/.zshrc && source ~/.zshrc
|
||||
```
|
||||
|
||||
Restart OpenCode.
|
||||
|
||||
## What's included
|
||||
|
||||
| Component | Description |
|
||||
|-----------|-------------|
|
||||
| **9 Native Memory Tools** | `add_memory`, `search_memories`, `get_memories`, `update_memory`, `delete_memory`, and more — registered as OpenCode tools, backed by the `mem0ai` SDK (no MCP server required) |
|
||||
| **Lifecycle Hooks** | Auto-search on session start and every prompt, error memory lookup, compaction context, secret redaction |
|
||||
| **9 Skills** | `/mem0-remember`, `/mem0-tour`, `/mem0-search`, `/mem0-status`, `/mem0-scope`, `/mem0-dream`, `/mem0-forget`, `/mem0-pin`, `/mem0-context-loader` — discovered in place from the plugin via OpenCode's `skills.paths` |
|
||||
|
||||
## Hooks
|
||||
|
||||
Pure TypeScript — no Python, no shell scripts. Memory operations are native OpenCode tools backed by the [mem0ai](https://www.npmjs.com/package/mem0ai) SDK directly.
|
||||
|
||||
| Hook | Event | What it does |
|
||||
|------|-------|-------------|
|
||||
| **Config** | `config` | Registers the `/mem0-*` slash commands (via `config.command`) and adds the plugin's own `opencode-skills/` dir to OpenCode's `skills.paths` for in-place skill discovery — no copying into `~/.config/opencode/skills` |
|
||||
| **Chat message** | `chat.message` | Loads prior memories on session start, searches relevant memories before each prompt, auto-captures learnings periodically |
|
||||
| **Pre-tool** | `tool.execute.before` | Blocks MEMORY.md writes, steering them to the `add_memory` tool |
|
||||
| **Post-tool** | `tool.execute.after` | Scans bash errors and pre-fetches related memories |
|
||||
| **Messages transform** | `experimental.chat.messages.transform` | Injects memory context (session memories, search results, error lookups) into the prompt |
|
||||
| **Compaction** | `experimental.session.compacting` | Stores session state memory, then injects prior memories into compaction context so nothing is lost |
|
||||
| **Shell env** | `shell.env` | Exports `MEM0_USER_ID`, `MEM0_APP_ID`, `MEM0_SESSION_ID`, and `MEM0_BRANCH` to shell |
|
||||
|
||||
## Memory Tools
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `add_memory` | Save text or conversation history |
|
||||
| `search_memories` | Semantic search across memories |
|
||||
| `get_memories` | List memories with filters and pagination |
|
||||
| `get_memory` | Retrieve a specific memory by ID |
|
||||
| `update_memory` | Overwrite a memory's text by ID |
|
||||
| `delete_memory` | Delete a single memory by ID |
|
||||
| `delete_all_memories` | Bulk delete all memories in scope |
|
||||
| `delete_entities` | Delete an entity and its memories |
|
||||
| `list_entities` | List users/agents/apps stored in Mem0 |
|
||||
|
||||
## Memory scope
|
||||
|
||||
Every memory tool accepts an optional `scope`, and you can set the **default**
|
||||
scope (used when none is passed) with the `/mem0-scope` skill:
|
||||
|
||||
| Scope | Reads | Writes |
|
||||
|-------|-------|--------|
|
||||
| `project` (default) | this repo (`user_id` + `app_id`) | this repo |
|
||||
| `session` | this run (adds `run_id`) | this run |
|
||||
| `global` | all your projects (`app_id="*"`) | user-wide (drops `app_id`) |
|
||||
|
||||
```
|
||||
/mem0-scope # show the current default scope
|
||||
/mem0-scope global # save & search across all your projects by default
|
||||
/mem0-scope project # back to repo-only (default)
|
||||
```
|
||||
|
||||
The default persists in `~/.mem0/settings.json` (`default_scope`) and is read
|
||||
fresh on each memory operation, so a change applies immediately — no restart.
|
||||
`delete_all_memories` always requires an explicit `scope="global"` to delete
|
||||
user-wide, so changing the default can't trigger a cross-project wipe.
|
||||
|
||||
## Verify
|
||||
|
||||
Start OpenCode and ask: *"Search my memories for recent decisions"*
|
||||
|
||||
If the `mem0` tools respond, you're all set.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Problem | Fix |
|
||||
|---------|-----|
|
||||
| No tools appearing | Restart OpenCode after installing |
|
||||
| 401 Unauthorized | `echo $MEM0_API_KEY` must print your `m0-` key |
|
||||
| Plugin not loading | Run `opencode plugin @mem0/opencode-plugin` again |
|
||||
|
||||
## License
|
||||
|
||||
Apache-2.0
|
||||
@@ -0,0 +1,765 @@
|
||||
{
|
||||
"lockfileVersion": 1,
|
||||
"configVersion": 1,
|
||||
"workspaces": {
|
||||
"": {
|
||||
"name": "@mem0/opencode-plugin",
|
||||
"dependencies": {
|
||||
"@opencode-ai/plugin": "^1.0.162",
|
||||
"mem0ai": "^3.0.8",
|
||||
},
|
||||
"devDependencies": {
|
||||
"bun-types": ">=1.3.14",
|
||||
"typescript": "^5.7.3",
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bun": ">=1.0.0",
|
||||
},
|
||||
},
|
||||
},
|
||||
"packages": {
|
||||
"@anthropic-ai/sdk": ["@anthropic-ai/sdk@0.40.1", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-DJMWm8lTEM9Lk/MSFL+V+ugF7jKOn0M2Ujvb5fN8r2nY14aHbGPZ1k6sgjL+tpJ3VuOGJNG+4R83jEpOuYPv8w=="],
|
||||
|
||||
"@azure/abort-controller": ["@azure/abort-controller@2.1.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA=="],
|
||||
|
||||
"@azure/core-auth": ["@azure/core-auth@1.10.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-util": "^1.13.0", "tslib": "^2.6.2" } }, "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg=="],
|
||||
|
||||
"@azure/core-client": ["@azure/core-client@1.10.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0", "@azure/core-tracing": "^1.3.0", "@azure/core-util": "^1.13.0", "@azure/logger": "^1.3.0", "tslib": "^2.6.2" } }, "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w=="],
|
||||
|
||||
"@azure/core-http-compat": ["@azure/core-http-compat@2.4.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2" }, "peerDependencies": { "@azure/core-client": "^1.10.0", "@azure/core-rest-pipeline": "^1.22.0" } }, "sha512-f1P96IB399YiN2ARYHP7EpZi3Bf3wH4SN2lGzrw7JVwm7bbsVYtf2iKSBwTywD2P62NOPZGHFSZi+6jjb75JuA=="],
|
||||
|
||||
"@azure/core-paging": ["@azure/core-paging@1.6.2", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA=="],
|
||||
|
||||
"@azure/core-rest-pipeline": ["@azure/core-rest-pipeline@1.23.0", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-auth": "^1.10.0", "@azure/core-tracing": "^1.3.0", "@azure/core-util": "^1.13.0", "@azure/logger": "^1.3.0", "@typespec/ts-http-runtime": "^0.3.4", "tslib": "^2.6.2" } }, "sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ=="],
|
||||
|
||||
"@azure/core-tracing": ["@azure/core-tracing@1.3.1", "", { "dependencies": { "tslib": "^2.6.2" } }, "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ=="],
|
||||
|
||||
"@azure/core-util": ["@azure/core-util@1.13.1", "", { "dependencies": { "@azure/abort-controller": "^2.1.2", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A=="],
|
||||
|
||||
"@azure/identity": ["@azure/identity@4.13.1", "", { "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.9.0", "@azure/core-client": "^1.9.2", "@azure/core-rest-pipeline": "^1.17.0", "@azure/core-tracing": "^1.0.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.0.0", "@azure/msal-browser": "^5.5.0", "@azure/msal-node": "^5.1.0", "open": "^10.1.0", "tslib": "^2.2.0" } }, "sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw=="],
|
||||
|
||||
"@azure/logger": ["@azure/logger@1.3.0", "", { "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" } }, "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA=="],
|
||||
|
||||
"@azure/msal-browser": ["@azure/msal-browser@5.11.0", "", { "dependencies": { "@azure/msal-common": "16.6.2" } }, "sha512-zkGNYS3TwY8lUpPIafAmsFCYZbgFixY9y/LZB9GUg0IILoHTqpN26j5OrkL1AQThh/YdZsawe4iWXfp85lFVxg=="],
|
||||
|
||||
"@azure/msal-common": ["@azure/msal-common@16.6.2", "", {}, "sha512-hQjjsekAjB00cM1EmatWJlzhEoK2Qhz7Rj5gvM6tYf8iL7RM3tkxlpU9fG0+ofkulzg9AEEA6dIEnSmDr5ZqUA=="],
|
||||
|
||||
"@azure/msal-node": ["@azure/msal-node@5.2.2", "", { "dependencies": { "@azure/msal-common": "16.6.2", "jsonwebtoken": "^9.0.0" } }, "sha512-toS+2AePxqyzb0YOKttDOOiSl3jrkK9aiqIvpurpis0O34QcIS5gToqrgT39p04Dpxw3YoUU0lxJKTpSFFfA6Q=="],
|
||||
|
||||
"@azure/search-documents": ["@azure/search-documents@12.2.0", "", { "dependencies": { "@azure/core-auth": "^1.9.0", "@azure/core-client": "^1.9.2", "@azure/core-http-compat": "^2.1.2", "@azure/core-paging": "^1.6.2", "@azure/core-rest-pipeline": "^1.18.0", "@azure/core-tracing": "^1.2.0", "@azure/core-util": "^1.11.0", "@azure/logger": "^1.1.4", "events": "^3.0.0", "tslib": "^2.8.1" } }, "sha512-4+Qw+qaGqnkdUCq/vEFzk/bkROogTvdbPb1fmI8poxNfDDN1q2WHxBmhI7CYwesrBj1yXC4i5E0aISBxZqZi0g=="],
|
||||
|
||||
"@babel/code-frame": ["@babel/code-frame@7.29.7", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.29.7", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw=="],
|
||||
|
||||
"@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.29.7", "", {}, "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg=="],
|
||||
|
||||
"@cfworker/json-schema": ["@cfworker/json-schema@4.1.1", "", {}, "sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og=="],
|
||||
|
||||
"@cloudflare/workers-types": ["@cloudflare/workers-types@4.20260527.1", "", {}, "sha512-GK+C05N4fGptp1uG+pbjC/7Udonz8EhMEYvYFnVKdLLnEGS9nsmVOFi/RLQr7tq9l/0UEzcWjudzeY2ssuqyIA=="],
|
||||
|
||||
"@fastify/busboy": ["@fastify/busboy@2.1.1", "", {}, "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA=="],
|
||||
|
||||
"@google/genai": ["@google/genai@1.52.0", "", { "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "optionalPeers": ["@modelcontextprotocol/sdk"] }, "sha512-gwSvbpiN/17O9TbsqSsE/OzZcpv5Fo4RQjdngGgogtuB9RsyJ8ZHhX5KjHj1bp5N9snN2eK8LDGXSaWW2hof8Q=="],
|
||||
|
||||
"@jest/expect-utils": ["@jest/expect-utils@29.7.0", "", { "dependencies": { "jest-get-type": "^29.6.3" } }, "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA=="],
|
||||
|
||||
"@jest/schemas": ["@jest/schemas@29.6.3", "", { "dependencies": { "@sinclair/typebox": "^0.27.8" } }, "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA=="],
|
||||
|
||||
"@jest/types": ["@jest/types@29.6.3", "", { "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" } }, "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw=="],
|
||||
|
||||
"@langchain/core": ["@langchain/core@1.1.48", "", { "dependencies": { "@cfworker/json-schema": "^4.0.2", "@standard-schema/spec": "^1.1.0", "js-tiktoken": "^1.0.12", "langsmith": ">=0.5.0 <1.0.0", "mustache": "^4.2.0", "p-queue": "^6.6.2", "zod": "^3.25.76 || ^4" } }, "sha512-fQU6Guyb1pwc2fEplmA8FPbKfOMAofjnyJzExevro0FxEiuGHE18Ov/ZHmT9trWCDTZRI9eW1VIc6aChxV8pAQ=="],
|
||||
|
||||
"@mistralai/mistralai": ["@mistralai/mistralai@1.15.1", "", { "dependencies": { "ws": "^8.18.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.1" } }, "sha512-fb995eiz3r0KsBGtRjFV+/iLbX+UpfalxpF+YitT3R6ukrPD4PN+FGwwmYcRFhNAzVzDUtTVxQYnjQWEnwV5nw=="],
|
||||
|
||||
"@mongodb-js/saslprep": ["@mongodb-js/saslprep@1.4.11", "", { "dependencies": { "sparse-bitfield": "^3.0.3" } }, "sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA=="],
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-darwin-arm64": ["@msgpackr-extract/msgpackr-extract-darwin-arm64@3.0.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-LCkGo6JDfaBhgST7UpPWgNgLINpcpabaHfyz5OBx75nUYxBsaEPxjnyNjWpeb/xBup/682QnBfRBy2/LvPutZQ=="],
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-darwin-x64": ["@msgpackr-extract/msgpackr-extract-darwin-x64@3.0.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-zExlW9zUJKZH/tOtVMttwjKa4Xm/3KcNjnE3dPN92uCktwavMxpgCA3MoJK/DOnTWsQgo224OaST27/mPNAf+w=="],
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-linux-arm": ["@msgpackr-extract/msgpackr-extract-linux-arm@3.0.4", "", { "os": "linux", "cpu": "arm" }, "sha512-Tg3yX65f5GbtXLkrYEHE5oibZG9epyYWas7FogTTEJeDEF9JlXJzKgXaNhT3UXlTOeA+AfZpYZYZ0uPj7Cfquw=="],
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-linux-arm64": ["@msgpackr-extract/msgpackr-extract-linux-arm64@3.0.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-dgX0P/9wGPJeHFBG+ZmhgE6bmtMt7NP5CRBGyyktpopdk/mW4POnrpQsSLtKI1dwpc+pPLuXHDh6vvskyQE/sw=="],
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-linux-x64": ["@msgpackr-extract/msgpackr-extract-linux-x64@3.0.4", "", { "os": "linux", "cpu": "x64" }, "sha512-8TNXMEjJc3QEy7R/x1INhgiU+XakDAFUzBhaz7+Rbrs8NH5UQeHQxxmzsSBJGyV6I1jW79undiQm8tOI+D+8FQ=="],
|
||||
|
||||
"@msgpackr-extract/msgpackr-extract-win32-x64": ["@msgpackr-extract/msgpackr-extract-win32-x64@3.0.4", "", { "os": "win32", "cpu": "x64" }, "sha512-CmCXPQrkbwExx3j946/PtHWHbYJiCRBRDl4BlkRQcJB/YOwQxJRTpoo7aTsortjgoJ1x7opzTSxn7C+ASSLVjQ=="],
|
||||
|
||||
"@opencode-ai/plugin": ["@opencode-ai/plugin@1.15.11", "", { "dependencies": { "@opencode-ai/sdk": "1.15.11", "effect": "4.0.0-beta.66", "zod": "4.1.8" }, "peerDependencies": { "@opentui/core": ">=0.2.15", "@opentui/keymap": ">=0.2.15", "@opentui/solid": ">=0.2.15" }, "optionalPeers": ["@opentui/core", "@opentui/keymap", "@opentui/solid"] }, "sha512-RDvYDCHO0+3OAGD590oDQqtryrENrUW04SjtoA4sgRV2efpZeBFjx3TnonBsXKq6nWqYg172nhltUEZsihGnXQ=="],
|
||||
|
||||
"@opencode-ai/sdk": ["@opencode-ai/sdk@1.15.11", "", { "dependencies": { "cross-spawn": "7.0.6" } }, "sha512-IyYyDVsO8SKbKbkSadHpDuYnYC+2vmEeLU+rW+rH2M54Sigq6l3gDHno16+U6SRut+lowbph7v/ry3WbV67V3w=="],
|
||||
|
||||
"@oven/bun-darwin-aarch64": ["@oven/bun-darwin-aarch64@1.3.14", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Omj20SuiHBOUjUBIyqtkNjSUIjOtEOJwmbix/ZyFH4BaQ6OZTaaRWIR4TjHVz0yadHgli6lLTiAh1uarnvD49A=="],
|
||||
|
||||
"@oven/bun-darwin-x64": ["@oven/bun-darwin-x64@1.3.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-FFj3QdU/OhlDyZOJ8CWfN5eWLpRlT4qjZg7lMQi7jA6GuoY5ajlO1zWLP/MuHYRSbXQUvV52RejNi8DVnAp13w=="],
|
||||
|
||||
"@oven/bun-darwin-x64-baseline": ["@oven/bun-darwin-x64-baseline@1.3.14", "", { "os": "darwin", "cpu": "x64" }, "sha512-OSfsTZstc898HHElhU4NccaBGOSSDn5VfahiVTnidZ9B/+wb7WTyfZJaBeJcfjwJ9H2W9uTh2TGtl3UfcXgV9g=="],
|
||||
|
||||
"@oven/bun-freebsd-aarch64": ["@oven/bun-freebsd-aarch64@1.3.14", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-LIKrXaFxAHybVO5Pf+9XP2FHUj/5APvXTUKk9dqHm5iFz4oH+W24cmhjkJirNujh9hKeTyrpWSe3no9JZKowIw=="],
|
||||
|
||||
"@oven/bun-freebsd-x64": ["@oven/bun-freebsd-x64@1.3.14", "", { "os": "freebsd", "cpu": "x64" }, "sha512-uwD+fGUH1ADpIF3B1U2jWzzb20QwRLZfj5QZ28GUCGrAJ/nTmWrD6YYGsblCY1wuhldRez3lU40AyuvSCyLYmw=="],
|
||||
|
||||
"@oven/bun-linux-aarch64": ["@oven/bun-linux-aarch64@1.3.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-X5SsPZHs+iYO8R/efIcRtc7gT2Q2DgPfliCxEkx4cXBumwkw0c/EsHMNwH3EgGpCDaZ7IYVPhpCG/xBOQHEwZw=="],
|
||||
|
||||
"@oven/bun-linux-aarch64-android": ["@oven/bun-linux-aarch64-android@1.3.14", "", { "os": "android", "cpu": "arm64" }, "sha512-y4kq5b85lsrmFb9Xvi4w9mA5IEFJkLMrSmYn06q24KjL9rUWDWO3VFZEtteZxUN5+ec3Zm5S8OnJw1umaCbVjA=="],
|
||||
|
||||
"@oven/bun-linux-aarch64-musl": ["@oven/bun-linux-aarch64-musl@1.3.14", "", { "os": "linux", "cpu": "arm64" }, "sha512-jmqOA92Cd1NL/1XBd4bFkJLxQ86K0RW7ohxS2qzzAvuitO4JiIxjjTeCspoU44zCozH72HpfZfUE2On31OjnWA=="],
|
||||
|
||||
"@oven/bun-linux-x64": ["@oven/bun-linux-x64@1.3.14", "", { "os": "linux", "cpu": "x64" }, "sha512-7OVTAKvwfPmSbIV1HpdOoVVx5VRc427GuPPne93N6vk4eQBPId9nXmZDh9/zGaKPdbVjVtQSZafWQoUjx38Utw=="],
|
||||
|
||||
"@oven/bun-linux-x64-android": ["@oven/bun-linux-x64-android@1.3.14", "", { "os": "android", "cpu": "x64" }, "sha512-qe9e1d+3VAEU7nAA2ol9Jvmy/o99PVMSgZhHn7Q/9O3YcDrfEqyQ8zm4zoe5qTEo8HZH0dN03Le0Ys2eQPs7eg=="],
|
||||
|
||||
"@oven/bun-linux-x64-baseline": ["@oven/bun-linux-x64-baseline@1.3.14", "", { "os": "linux", "cpu": "x64" }, "sha512-q/8EdOC0yUE8FPeoOVq8/Pw5I9/tJaYmUfO/uDUAREx8IUnOJH1RJ5A3BjFqre8pvJoiZA9AovPJq5FnNNjSxA=="],
|
||||
|
||||
"@oven/bun-linux-x64-musl": ["@oven/bun-linux-x64-musl@1.3.14", "", { "os": "linux", "cpu": "x64" }, "sha512-GBCB/k/sIqcr06eTNgg7g46qiUv35Jasx4XiccJ/n7RGqrE4RWUD/XJBbWFprVPjvqd59+QtSnS99XGqvftHfg=="],
|
||||
|
||||
"@oven/bun-linux-x64-musl-baseline": ["@oven/bun-linux-x64-musl-baseline@1.3.14", "", { "os": "linux", "cpu": "x64" }, "sha512-n6iE71G4lQE4XkrZhQQcL5YUlxDbnq6nqV7zeQi33PMsLT/0kYE+RvHOtBWZ3w0wMdXZfINmp63hIb9ijUBGtw=="],
|
||||
|
||||
"@oven/bun-windows-aarch64": ["@oven/bun-windows-aarch64@1.3.14", "", { "os": "win32", "cpu": "arm64" }, "sha512-T7s3x/BsVKQObGU6QDkZeI6wKynzqGbBH1yI77jrrj5siElclxr3DQrDIk8CV4G5/SJq2HHq4kpLyYY2DKCSmA=="],
|
||||
|
||||
"@oven/bun-windows-x64": ["@oven/bun-windows-x64@1.3.14", "", { "os": "win32", "cpu": "x64" }, "sha512-mUFWL3BoYkNpjd8e9PqROiFF/1Xeotq20mABJsiQH62jM1g5zqWh4khw1RZ6bX8Q8fWvlPaxG1PjofkmjUi3vg=="],
|
||||
|
||||
"@oven/bun-windows-x64-baseline": ["@oven/bun-windows-x64-baseline@1.3.14", "", { "os": "win32", "cpu": "x64" }, "sha512-uIjLUC1S9DWgICzuoMba7vurBJnBruE4S5CxnvmZkdqWVXRzx1Rgu636HoH+k0qeaQCFh3jeG3JQ1y6fRHv0sw=="],
|
||||
|
||||
"@protobufjs/aspromise": ["@protobufjs/aspromise@1.1.2", "", {}, "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="],
|
||||
|
||||
"@protobufjs/base64": ["@protobufjs/base64@1.1.2", "", {}, "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="],
|
||||
|
||||
"@protobufjs/codegen": ["@protobufjs/codegen@2.0.5", "", {}, "sha512-zgXFLzW3Ap33e6d0Wlj4MGIm6Ce8O89n/apUaGNB/jx+hw+ruWEp7EwGUshdLKVRCxZW12fp9r40E1mQrf/34g=="],
|
||||
|
||||
"@protobufjs/eventemitter": ["@protobufjs/eventemitter@1.1.1", "", {}, "sha512-vW1GmwMZNnL+gMRaovlh9yZX74kc+TTU3FObkkurpMaRtBfLP3ldjS9KQWlwZgraRE0+dheEEoAxdzcJQ8eXZg=="],
|
||||
|
||||
"@protobufjs/fetch": ["@protobufjs/fetch@1.1.1", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.1" } }, "sha512-GpptLrs57adMSuHi3VNj0mAF8dwh36LMaYF6XyJ6JMWlVsc+t42tm1HSEDmOs3A8fC9yyeisgLhsTVQokOZ0zw=="],
|
||||
|
||||
"@protobufjs/float": ["@protobufjs/float@1.0.2", "", {}, "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="],
|
||||
|
||||
"@protobufjs/inquire": ["@protobufjs/inquire@1.1.2", "", {}, "sha512-pa0vFRuws4wkvaXKK1uXZMAwAX4/t8ANaJo45iw/oQHNQ9q5xUzwgFmVJGXiga2BeN+zpX7Vf9vmsiIa2J+MUw=="],
|
||||
|
||||
"@protobufjs/path": ["@protobufjs/path@1.1.2", "", {}, "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="],
|
||||
|
||||
"@protobufjs/pool": ["@protobufjs/pool@1.1.0", "", {}, "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="],
|
||||
|
||||
"@protobufjs/utf8": ["@protobufjs/utf8@1.1.1", "", {}, "sha512-oOAWABowe8EAbMyWKM0tYDKi8Yaox52D+HWZhAIJqQXbqe0xI/GV7FhLWqlEKreMkfDjshR5FKgi3mnle0h6Eg=="],
|
||||
|
||||
"@qdrant/js-client-rest": ["@qdrant/js-client-rest@1.13.0", "", { "dependencies": { "@qdrant/openapi-typescript-fetch": "1.2.6", "@sevinf/maybe": "0.5.0", "undici": "~5.28.4" }, "peerDependencies": { "typescript": ">=4.7" } }, "sha512-bewMtnXlGvhhnfXsp0sLoLXOGvnrCM15z9lNlG0Snp021OedNAnRtKkerjk5vkOcbQWUmJHXYCuxDfcT93aSkA=="],
|
||||
|
||||
"@qdrant/openapi-typescript-fetch": ["@qdrant/openapi-typescript-fetch@1.2.6", "", {}, "sha512-oQG/FejNpItrxRHoyctYvT3rwGZOnK4jr3JdppO/c78ktDvkWiPXPHNsrDf33K9sZdRb6PR7gi4noIapu5q4HA=="],
|
||||
|
||||
"@redis/bloom": ["@redis/bloom@1.2.0", "", { "peerDependencies": { "@redis/client": "^1.0.0" } }, "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg=="],
|
||||
|
||||
"@redis/client": ["@redis/client@1.6.1", "", { "dependencies": { "cluster-key-slot": "1.1.2", "generic-pool": "3.9.0", "yallist": "4.0.0" } }, "sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw=="],
|
||||
|
||||
"@redis/graph": ["@redis/graph@1.1.1", "", { "peerDependencies": { "@redis/client": "^1.0.0" } }, "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw=="],
|
||||
|
||||
"@redis/json": ["@redis/json@1.0.7", "", { "peerDependencies": { "@redis/client": "^1.0.0" } }, "sha512-6UyXfjVaTBTJtKNG4/9Z8PSpKE6XgSyEb8iwaqDcy+uKrd/DGYHTWkUdnQDyzm727V7p21WUMhsqz5oy65kPcQ=="],
|
||||
|
||||
"@redis/search": ["@redis/search@1.2.0", "", { "peerDependencies": { "@redis/client": "^1.0.0" } }, "sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw=="],
|
||||
|
||||
"@redis/time-series": ["@redis/time-series@1.1.0", "", { "peerDependencies": { "@redis/client": "^1.0.0" } }, "sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g=="],
|
||||
|
||||
"@sevinf/maybe": ["@sevinf/maybe@0.5.0", "", {}, "sha512-ARhyoYDnY1LES3vYI0fiG6e9esWfTNcXcO6+MPJJXcnyMV3bim4lnFt45VXouV7y82F4x3YH8nOQ6VztuvUiWg=="],
|
||||
|
||||
"@sinclair/typebox": ["@sinclair/typebox@0.27.10", "", {}, "sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA=="],
|
||||
|
||||
"@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="],
|
||||
|
||||
"@supabase/auth-js": ["@supabase/auth-js@2.106.2", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-VcAjUErkHkhC5Jaf+g/G1qbkQrFh8edaCdHa7pxJmHUjkWKjT7UnYCtPA89XV0N0GIYRkEqJZw5V62CtOxTmBQ=="],
|
||||
|
||||
"@supabase/functions-js": ["@supabase/functions-js@2.106.2", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-oRnr0QrL8H+zTO1YyQ1QjiHZU/957jvubbxSJTUm2XLAgzoGGV9Tahfyd+uvLsBLRVmXLtpU3oyCjdQIvkGMOA=="],
|
||||
|
||||
"@supabase/phoenix": ["@supabase/phoenix@0.4.2", "", {}, "sha512-YSAGnmDAfuleFCVt3CeurQZAhxRfXWeZIIkwp7NhYzQ1UwW6ePSnzsFAiUm/mbCkfoCf70QQHKW/K6RKh52a4A=="],
|
||||
|
||||
"@supabase/postgrest-js": ["@supabase/postgrest-js@2.106.2", "", { "dependencies": { "tslib": "2.8.1" } }, "sha512-tDOzyPgp9pIRMR2x6C9+uDSJrnXSzxLtt3d7nC+Lrsy3jnJDHYfdQC/xcRyhJE/TOBJ0heSqRKR3UmejDjZxsw=="],
|
||||
|
||||
"@supabase/realtime-js": ["@supabase/realtime-js@2.106.2", "", { "dependencies": { "@supabase/phoenix": "^0.4.2", "tslib": "2.8.1" } }, "sha512-LdRGT7DNhyZkPjubUv5bSdAZ0jSEX8wTHvx7htj7+K59TOZRvz4TuQK7tL2RWxyIZVeFMRluL04SzWS61rKnUA=="],
|
||||
|
||||
"@supabase/storage-js": ["@supabase/storage-js@2.106.2", "", { "dependencies": { "iceberg-js": "^0.8.1", "tslib": "2.8.1" } }, "sha512-xgKCSYuev1YarV+iVqr+zlfgSyremnJtn8T0NCT8L4XmMv1CLtESc0Q6kNp8+mKWdX/8ND0nzm7OMKx08kwNAw=="],
|
||||
|
||||
"@supabase/supabase-js": ["@supabase/supabase-js@2.106.2", "", { "dependencies": { "@supabase/auth-js": "2.106.2", "@supabase/functions-js": "2.106.2", "@supabase/postgrest-js": "2.106.2", "@supabase/realtime-js": "2.106.2", "@supabase/storage-js": "2.106.2" } }, "sha512-2/RZ/1fmJx/MRSEDG2Xk8+J4JVk5clM9V0uSI6kUTrcS32KA89DtqI5RUOC9r6mzY3WBC9qexLjssIHjbLyVJA=="],
|
||||
|
||||
"@types/istanbul-lib-coverage": ["@types/istanbul-lib-coverage@2.0.6", "", {}, "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w=="],
|
||||
|
||||
"@types/istanbul-lib-report": ["@types/istanbul-lib-report@3.0.3", "", { "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA=="],
|
||||
|
||||
"@types/istanbul-reports": ["@types/istanbul-reports@3.0.4", "", { "dependencies": { "@types/istanbul-lib-report": "*" } }, "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ=="],
|
||||
|
||||
"@types/jest": ["@types/jest@29.5.14", "", { "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ=="],
|
||||
|
||||
"@types/node": ["@types/node@25.9.1", "", { "dependencies": { "undici-types": ">=7.24.0 <7.24.7" } }, "sha512-xfrlY7UD5rMJk3ZVJP8BNzS28J36YJg+xp+LPXV1TdWxr8uMH5A860QNxYDGQe/ylDSgjxE52Q9VnO7p75tJxg=="],
|
||||
|
||||
"@types/node-fetch": ["@types/node-fetch@2.6.13", "", { "dependencies": { "@types/node": "*", "form-data": "^4.0.4" } }, "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw=="],
|
||||
|
||||
"@types/pg": ["@types/pg@8.11.0", "", { "dependencies": { "@types/node": "*", "pg-protocol": "*", "pg-types": "^4.0.1" } }, "sha512-sDAlRiBNthGjNFfvt0k6mtotoVYVQ63pA8R4EMWka7crawSR60waVYR0HAgmPRs/e2YaeJTD/43OoZ3PFw80pw=="],
|
||||
|
||||
"@types/retry": ["@types/retry@0.12.0", "", {}, "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA=="],
|
||||
|
||||
"@types/stack-utils": ["@types/stack-utils@2.0.3", "", {}, "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="],
|
||||
|
||||
"@types/webidl-conversions": ["@types/webidl-conversions@7.0.3", "", {}, "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA=="],
|
||||
|
||||
"@types/whatwg-url": ["@types/whatwg-url@13.0.0", "", { "dependencies": { "@types/webidl-conversions": "*" } }, "sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q=="],
|
||||
|
||||
"@types/yargs": ["@types/yargs@17.0.35", "", { "dependencies": { "@types/yargs-parser": "*" } }, "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg=="],
|
||||
|
||||
"@types/yargs-parser": ["@types/yargs-parser@21.0.3", "", {}, "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ=="],
|
||||
|
||||
"@typespec/ts-http-runtime": ["@typespec/ts-http-runtime@0.3.5", "", { "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" } }, "sha512-yURCknZhvywvQItHMMmFSo+fq5arCUIyz/CVk7jD89MSai7dkaX8ufjCWp3NttLojoTVbcE72ri+be/TnEbMHw=="],
|
||||
|
||||
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
|
||||
|
||||
"afinn-165": ["afinn-165@2.0.2", "", {}, "sha512-mJ/RLUfpXfQA6bzugv+bBsc/QYkVrKaLYeS8fWBpKbTCsonv4iuV9ET0fgReEunm9vKLkaNgnekuSNlTC3WQ1Q=="],
|
||||
|
||||
"afinn-165-financialmarketnews": ["afinn-165-financialmarketnews@3.0.0", "", {}, "sha512-0g9A1S3ZomFIGDTzZ0t6xmv4AuokBvBmpes8htiyHpH7N4xDmvSQL6UxL/Zcs2ypRb3VwgCscaD8Q3zEawKYhw=="],
|
||||
|
||||
"agent-base": ["agent-base@6.0.2", "", { "dependencies": { "debug": "4" } }, "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ=="],
|
||||
|
||||
"agentkeepalive": ["agentkeepalive@4.6.0", "", { "dependencies": { "humanize-ms": "^1.2.1" } }, "sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ=="],
|
||||
|
||||
"ansi-styles": ["ansi-styles@5.2.0", "", {}, "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA=="],
|
||||
|
||||
"apparatus": ["apparatus@0.0.10", "", { "dependencies": { "sylvester": ">= 0.0.8" } }, "sha512-KLy/ugo33KZA7nugtQ7O0E1c8kQ52N3IvD/XgIh4w/Nr28ypfkwDfA67F1ev4N1m5D+BOk1+b2dEJDfpj/VvZg=="],
|
||||
|
||||
"asynckit": ["asynckit@0.4.0", "", {}, "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="],
|
||||
|
||||
"axios": ["axios@1.16.1", "", { "dependencies": { "follow-redirects": "^1.16.0", "form-data": "^4.0.5", "https-proxy-agent": "^5.0.1", "proxy-from-env": "^2.1.0" } }, "sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A=="],
|
||||
|
||||
"base-64": ["base-64@0.1.0", "", {}, "sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA=="],
|
||||
|
||||
"base64-js": ["base64-js@1.5.1", "", {}, "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA=="],
|
||||
|
||||
"better-sqlite3": ["better-sqlite3@12.10.0", "", { "dependencies": { "bindings": "^1.5.0", "prebuild-install": "^7.1.1" } }, "sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ=="],
|
||||
|
||||
"bignumber.js": ["bignumber.js@9.3.1", "", {}, "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ=="],
|
||||
|
||||
"bindings": ["bindings@1.5.0", "", { "dependencies": { "file-uri-to-path": "1.0.0" } }, "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ=="],
|
||||
|
||||
"bl": ["bl@4.1.0", "", { "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w=="],
|
||||
|
||||
"braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="],
|
||||
|
||||
"bson": ["bson@7.2.0", "", {}, "sha512-YCEo7KjMlbNlyHhz7zAZNDpIpQbd+wOEHJYezv0nMYTn4x31eIUM2yomNNubclAt63dObUzKHWsBLJ9QcZNSnQ=="],
|
||||
|
||||
"buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
||||
|
||||
"buffer-equal-constant-time": ["buffer-equal-constant-time@1.0.1", "", {}, "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA=="],
|
||||
|
||||
"buffer-writer": ["buffer-writer@2.0.0", "", {}, "sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw=="],
|
||||
|
||||
"bun": ["bun@1.3.14", "", { "optionalDependencies": { "@oven/bun-darwin-aarch64": "1.3.14", "@oven/bun-darwin-x64": "1.3.14", "@oven/bun-darwin-x64-baseline": "1.3.14", "@oven/bun-freebsd-aarch64": "1.3.14", "@oven/bun-freebsd-x64": "1.3.14", "@oven/bun-linux-aarch64": "1.3.14", "@oven/bun-linux-aarch64-android": "1.3.14", "@oven/bun-linux-aarch64-musl": "1.3.14", "@oven/bun-linux-x64": "1.3.14", "@oven/bun-linux-x64-android": "1.3.14", "@oven/bun-linux-x64-baseline": "1.3.14", "@oven/bun-linux-x64-musl": "1.3.14", "@oven/bun-linux-x64-musl-baseline": "1.3.14", "@oven/bun-windows-aarch64": "1.3.14", "@oven/bun-windows-x64": "1.3.14", "@oven/bun-windows-x64-baseline": "1.3.14" }, "os": [ "!aix", "!sunos", "!openbsd", ], "cpu": [ "x64", "arm64", ], "bin": { "bun": "bin/bun.exe", "bunx": "bin/bunx.exe" } }, "sha512-aB6GVd42x1Y5ie1K16SF+oLGtgSkwX9hgoDdIW88pjvfTccU8F1vfpoOt34QLv0dZ1v3XimtaxPlZUG81Gx9Zg=="],
|
||||
|
||||
"bun-types": ["bun-types@1.3.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-4N0ig0fEomHt5R0KCFWjovxow98rIoRwKolrYdCcknNwMekCXRnWEUvgu5soYV8QXtVsrUD8B95MBOZGPvr6KQ=="],
|
||||
|
||||
"bundle-name": ["bundle-name@4.1.0", "", { "dependencies": { "run-applescript": "^7.0.0" } }, "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q=="],
|
||||
|
||||
"call-bind-apply-helpers": ["call-bind-apply-helpers@1.0.2", "", { "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" } }, "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ=="],
|
||||
|
||||
"chalk": ["chalk@4.1.2", "", { "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" } }, "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA=="],
|
||||
|
||||
"charenc": ["charenc@0.0.2", "", {}, "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA=="],
|
||||
|
||||
"chownr": ["chownr@1.1.4", "", {}, "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="],
|
||||
|
||||
"ci-info": ["ci-info@3.9.0", "", {}, "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ=="],
|
||||
|
||||
"cloudflare": ["cloudflare@4.5.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" } }, "sha512-fPcbPKx4zF45jBvQ0z7PCdgejVAPBBCZxwqk1k7krQNfpM07Cfj97/Q6wBzvYqlWXx/zt1S9+m8vnfCe06umbQ=="],
|
||||
|
||||
"cluster-key-slot": ["cluster-key-slot@1.1.2", "", {}, "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA=="],
|
||||
|
||||
"color-convert": ["color-convert@2.0.1", "", { "dependencies": { "color-name": "~1.1.4" } }, "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ=="],
|
||||
|
||||
"color-name": ["color-name@1.1.4", "", {}, "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA=="],
|
||||
|
||||
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||
|
||||
"compromise": ["compromise@14.15.1", "", { "dependencies": { "efrt": "2.7.0", "grad-school": "0.0.5", "suffix-thumb": "5.0.2" } }, "sha512-9F3UkUaEU1PPz2fgStkE/TI4tk++0wHxS8xfWq9PQWL/v28dy8bEcPVVSLh3dISIRD7PEhJ8YTzHRKF8y9tnLA=="],
|
||||
|
||||
"cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="],
|
||||
|
||||
"crypt": ["crypt@0.0.2", "", {}, "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow=="],
|
||||
|
||||
"data-uri-to-buffer": ["data-uri-to-buffer@4.0.1", "", {}, "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A=="],
|
||||
|
||||
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||
|
||||
"decompress-response": ["decompress-response@6.0.0", "", { "dependencies": { "mimic-response": "^3.1.0" } }, "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ=="],
|
||||
|
||||
"deep-extend": ["deep-extend@0.6.0", "", {}, "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="],
|
||||
|
||||
"default-browser": ["default-browser@5.5.0", "", { "dependencies": { "bundle-name": "^4.1.0", "default-browser-id": "^5.0.0" } }, "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw=="],
|
||||
|
||||
"default-browser-id": ["default-browser-id@5.0.1", "", {}, "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q=="],
|
||||
|
||||
"define-lazy-prop": ["define-lazy-prop@3.0.0", "", {}, "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg=="],
|
||||
|
||||
"delayed-stream": ["delayed-stream@1.0.0", "", {}, "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ=="],
|
||||
|
||||
"detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="],
|
||||
|
||||
"diff-sequences": ["diff-sequences@29.6.3", "", {}, "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q=="],
|
||||
|
||||
"digest-fetch": ["digest-fetch@1.3.0", "", { "dependencies": { "base-64": "^0.1.0", "md5": "^2.3.0" } }, "sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA=="],
|
||||
|
||||
"dotenv": ["dotenv@17.4.2", "", {}, "sha512-nI4U3TottKAcAD9LLud4Cb7b2QztQMUEfHbvhTH09bqXTxnSie8WnjPALV/WMCrJZ6UV/qHJ6L03OqO3LcdYZw=="],
|
||||
|
||||
"dunder-proto": ["dunder-proto@1.0.1", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" } }, "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A=="],
|
||||
|
||||
"ecdsa-sig-formatter": ["ecdsa-sig-formatter@1.0.11", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ=="],
|
||||
|
||||
"effect": ["effect@4.0.0-beta.66", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "fast-check": "^4.6.0", "find-my-way-ts": "^0.1.6", "ini": "^6.0.0", "kubernetes-types": "^1.30.0", "msgpackr": "^1.11.9", "multipasta": "^0.2.7", "toml": "^4.1.1", "uuid": "^13.0.0", "yaml": "^2.8.3" } }, "sha512-4arEr62cziFa8BBVDUwJCJJmaVepXf/kRg7KtC0h8+bufngscrHbwWFhr9c+HonwOF+31U3iD3xUJmw9KzX7Dw=="],
|
||||
|
||||
"efrt": ["efrt@2.7.0", "", {}, "sha512-/RInbCy1d4P6Zdfa+TMVsf/ufZVotat5hCw3QXmWtjU+3pFEOvOQ7ibo3aIxyCJw2leIeAMjmPj+1SLJiCpdrQ=="],
|
||||
|
||||
"end-of-stream": ["end-of-stream@1.4.5", "", { "dependencies": { "once": "^1.4.0" } }, "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg=="],
|
||||
|
||||
"es-define-property": ["es-define-property@1.0.1", "", {}, "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g=="],
|
||||
|
||||
"es-errors": ["es-errors@1.3.0", "", {}, "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw=="],
|
||||
|
||||
"es-object-atoms": ["es-object-atoms@1.1.2", "", { "dependencies": { "es-errors": "^1.3.0" } }, "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw=="],
|
||||
|
||||
"es-set-tostringtag": ["es-set-tostringtag@2.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA=="],
|
||||
|
||||
"escape-string-regexp": ["escape-string-regexp@2.0.0", "", {}, "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w=="],
|
||||
|
||||
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
|
||||
|
||||
"eventemitter3": ["eventemitter3@4.0.7", "", {}, "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw=="],
|
||||
|
||||
"events": ["events@3.3.0", "", {}, "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q=="],
|
||||
|
||||
"expand-template": ["expand-template@2.0.3", "", {}, "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg=="],
|
||||
|
||||
"expect": ["expect@29.7.0", "", { "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" } }, "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw=="],
|
||||
|
||||
"extend": ["extend@3.0.2", "", {}, "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="],
|
||||
|
||||
"fast-check": ["fast-check@4.8.0", "", { "dependencies": { "pure-rand": "^8.0.0" } }, "sha512-GOJ158CUMnN6cSahsv4+ExARvIDuzzinFjkp0E9WtiBa5zcVeLozVkWaE4IzFcc+Y48Wp1EDlUZsXRyAztQcSg=="],
|
||||
|
||||
"fetch-blob": ["fetch-blob@3.2.0", "", { "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" } }, "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ=="],
|
||||
|
||||
"file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="],
|
||||
|
||||
"fill-range": ["fill-range@7.1.1", "", { "dependencies": { "to-regex-range": "^5.0.1" } }, "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg=="],
|
||||
|
||||
"find-my-way-ts": ["find-my-way-ts@0.1.6", "", {}, "sha512-a85L9ZoXtNAey3Y6Z+eBWW658kO/MwR7zIafkIUPUMf3isZG0NCs2pjW2wtjxAKuJPxMAsHUIP4ZPGv0o5gyTA=="],
|
||||
|
||||
"follow-redirects": ["follow-redirects@1.16.0", "", {}, "sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw=="],
|
||||
|
||||
"form-data": ["form-data@4.0.5", "", { "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w=="],
|
||||
|
||||
"form-data-encoder": ["form-data-encoder@1.7.2", "", {}, "sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A=="],
|
||||
|
||||
"formdata-node": ["formdata-node@4.4.1", "", { "dependencies": { "node-domexception": "1.0.0", "web-streams-polyfill": "4.0.0-beta.3" } }, "sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ=="],
|
||||
|
||||
"formdata-polyfill": ["formdata-polyfill@4.0.10", "", { "dependencies": { "fetch-blob": "^3.1.2" } }, "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g=="],
|
||||
|
||||
"fs-constants": ["fs-constants@1.0.0", "", {}, "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="],
|
||||
|
||||
"function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="],
|
||||
|
||||
"gaxios": ["gaxios@7.1.4", "", { "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2" } }, "sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA=="],
|
||||
|
||||
"gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="],
|
||||
|
||||
"generic-pool": ["generic-pool@3.9.0", "", {}, "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g=="],
|
||||
|
||||
"get-intrinsic": ["get-intrinsic@1.3.0", "", { "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" } }, "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ=="],
|
||||
|
||||
"get-proto": ["get-proto@1.0.1", "", { "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" } }, "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g=="],
|
||||
|
||||
"github-from-package": ["github-from-package@0.0.0", "", {}, "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw=="],
|
||||
|
||||
"google-auth-library": ["google-auth-library@10.6.2", "", { "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "^7.1.4", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" } }, "sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw=="],
|
||||
|
||||
"google-logging-utils": ["google-logging-utils@1.1.3", "", {}, "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA=="],
|
||||
|
||||
"gopd": ["gopd@1.2.0", "", {}, "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg=="],
|
||||
|
||||
"graceful-fs": ["graceful-fs@4.2.11", "", {}, "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="],
|
||||
|
||||
"grad-school": ["grad-school@0.0.5", "", {}, "sha512-rXunEHF9M9EkMydTBux7+IryYXEZinRk6g8OBOGDBzo/qWJjhTxy86i5q7lQYpCLHN8Sqv1XX3OIOc7ka2gtvQ=="],
|
||||
|
||||
"groq-sdk": ["groq-sdk@0.3.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "digest-fetch": "^1.3.0", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7", "web-streams-polyfill": "^3.2.1" } }, "sha512-Cdgjh4YoSBE2X4S9sxPGXaAy1dlN4bRtAaDZ3cnq+XsxhhN9WSBeHF64l7LWwuD5ntmw7YC5Vf4Ff1oHCg1LOg=="],
|
||||
|
||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
|
||||
"has-tostringtag": ["has-tostringtag@1.0.2", "", { "dependencies": { "has-symbols": "^1.0.3" } }, "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw=="],
|
||||
|
||||
"hasown": ["hasown@2.0.3", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-ej4AhfhfL2Q2zpMmLo7U1Uv9+PyhIZpgQLGT1F9miIGmiCJIoCgSmczFdrc97mWT4kVY72KA+WnnhJ5pghSvSg=="],
|
||||
|
||||
"http-proxy-agent": ["http-proxy-agent@7.0.2", "", { "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" } }, "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig=="],
|
||||
|
||||
"https-proxy-agent": ["https-proxy-agent@5.0.1", "", { "dependencies": { "agent-base": "6", "debug": "4" } }, "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA=="],
|
||||
|
||||
"humanize-ms": ["humanize-ms@1.2.1", "", { "dependencies": { "ms": "^2.0.0" } }, "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ=="],
|
||||
|
||||
"iceberg-js": ["iceberg-js@0.8.1", "", {}, "sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA=="],
|
||||
|
||||
"ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="],
|
||||
|
||||
"inherits": ["inherits@2.0.4", "", {}, "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="],
|
||||
|
||||
"ini": ["ini@6.0.0", "", {}, "sha512-IBTdIkzZNOpqm7q3dRqJvMaldXjDHWkEDfrwGEQTs5eaQMWV+djAhR+wahyNNMAa+qpbDUhBMVt4ZKNwpPm7xQ=="],
|
||||
|
||||
"is-buffer": ["is-buffer@1.1.6", "", {}, "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="],
|
||||
|
||||
"is-docker": ["is-docker@3.0.0", "", { "bin": { "is-docker": "cli.js" } }, "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ=="],
|
||||
|
||||
"is-inside-container": ["is-inside-container@1.0.0", "", { "dependencies": { "is-docker": "^3.0.0" }, "bin": { "is-inside-container": "cli.js" } }, "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA=="],
|
||||
|
||||
"is-number": ["is-number@7.0.0", "", {}, "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng=="],
|
||||
|
||||
"is-wsl": ["is-wsl@3.1.1", "", { "dependencies": { "is-inside-container": "^1.0.0" } }, "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw=="],
|
||||
|
||||
"isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="],
|
||||
|
||||
"jest-diff": ["jest-diff@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw=="],
|
||||
|
||||
"jest-get-type": ["jest-get-type@29.6.3", "", {}, "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw=="],
|
||||
|
||||
"jest-matcher-utils": ["jest-matcher-utils@29.7.0", "", { "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" } }, "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g=="],
|
||||
|
||||
"jest-message-util": ["jest-message-util@29.7.0", "", { "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" } }, "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w=="],
|
||||
|
||||
"jest-util": ["jest-util@29.7.0", "", { "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" } }, "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA=="],
|
||||
|
||||
"js-tiktoken": ["js-tiktoken@1.0.21", "", { "dependencies": { "base64-js": "^1.5.1" } }, "sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g=="],
|
||||
|
||||
"js-tokens": ["js-tokens@4.0.0", "", {}, "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ=="],
|
||||
|
||||
"json-bigint": ["json-bigint@1.0.0", "", { "dependencies": { "bignumber.js": "^9.0.0" } }, "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ=="],
|
||||
|
||||
"jsonwebtoken": ["jsonwebtoken@9.0.3", "", { "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" } }, "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g=="],
|
||||
|
||||
"jwa": ["jwa@2.0.1", "", { "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg=="],
|
||||
|
||||
"jws": ["jws@4.0.1", "", { "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA=="],
|
||||
|
||||
"kareem": ["kareem@3.3.0", "", {}, "sha512-kpSuLD3/7RenBnjnJdOHXCKC8dTd1JzeOiJhN0necWWci6cC+qX+VuwPnMVgb+a4+KNJSfgqahpnfWaeDXCimw=="],
|
||||
|
||||
"kubernetes-types": ["kubernetes-types@1.30.0", "", {}, "sha512-Dew1okvhM/SQcIa2rcgujNndZwU8VnSapDgdxlYoB84ZlpAD43U6KLAFqYo17ykSFGHNPrg0qry0bP+GJd9v7Q=="],
|
||||
|
||||
"langsmith": ["langsmith@0.7.2", "", { "dependencies": { "p-queue": "6.6.2" }, "peerDependencies": { "@opentelemetry/api": "*", "@opentelemetry/exporter-trace-otlp-proto": "*", "@opentelemetry/sdk-trace-base": "*", "openai": "*", "ws": ">=7" }, "optionalPeers": ["@opentelemetry/api", "@opentelemetry/exporter-trace-otlp-proto", "@opentelemetry/sdk-trace-base", "openai", "ws"] }, "sha512-3dZUwQDJluxi2ih5eIygFODtlrQKrs3Tua0Ck3l+DAUkSGFkB1Dc0JPqkGbqiVSN+TQDElnkev/ydAOAz6jndA=="],
|
||||
|
||||
"lodash.includes": ["lodash.includes@4.3.0", "", {}, "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w=="],
|
||||
|
||||
"lodash.isboolean": ["lodash.isboolean@3.0.3", "", {}, "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg=="],
|
||||
|
||||
"lodash.isinteger": ["lodash.isinteger@4.0.4", "", {}, "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA=="],
|
||||
|
||||
"lodash.isnumber": ["lodash.isnumber@3.0.3", "", {}, "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw=="],
|
||||
|
||||
"lodash.isplainobject": ["lodash.isplainobject@4.0.6", "", {}, "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA=="],
|
||||
|
||||
"lodash.isstring": ["lodash.isstring@4.0.1", "", {}, "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw=="],
|
||||
|
||||
"lodash.once": ["lodash.once@4.1.1", "", {}, "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg=="],
|
||||
|
||||
"long": ["long@5.3.2", "", {}, "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA=="],
|
||||
|
||||
"math-intrinsics": ["math-intrinsics@1.1.0", "", {}, "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g=="],
|
||||
|
||||
"md5": ["md5@2.3.0", "", { "dependencies": { "charenc": "0.0.2", "crypt": "0.0.2", "is-buffer": "~1.1.6" } }, "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g=="],
|
||||
|
||||
"mem0ai": ["mem0ai@3.0.8", "", { "dependencies": { "axios": "^1.16.0", "openai": "^4.93.0", "uuid": "^11.1.1", "zod": "^3.24.1" }, "peerDependencies": { "@anthropic-ai/sdk": "^0.40.1", "@azure/identity": "^4.0.0", "@azure/search-documents": "^12.0.0", "@cloudflare/workers-types": "^4.20250504.0", "@google/genai": "^1.40.0", "@langchain/core": "^1.1.47", "@mistralai/mistralai": "^1.5.2", "@qdrant/js-client-rest": "^1.18.0", "@supabase/supabase-js": "^2.49.1", "@types/jest": "29.5.14", "@types/pg": "8.11.0", "better-sqlite3": "^12.6.2", "cloudflare": "^4.2.0", "compromise": "^14.0.0", "groq-sdk": "0.3.0", "natural": "^8.0.1", "ollama": "^0.5.14", "pg": "8.11.3", "redis": "^4.6.13" } }, "sha512-6lvHGOYc/Z2r0JEulS559MVPOOiOtfAqG02VESDY7b0WAZwsmgLWAjQkbmWnD9fbXQQ3QvSpSaVx1q6Ex01eLQ=="],
|
||||
|
||||
"memjs": ["memjs@1.3.2", "", {}, "sha512-qUEg2g8vxPe+zPn09KidjIStHPtoBO8Cttm8bgJFWWabbsjQ9Av9Ky+6UcvKx6ue0LLb/LEhtcyQpRyKfzeXcg=="],
|
||||
|
||||
"memory-pager": ["memory-pager@1.5.0", "", {}, "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg=="],
|
||||
|
||||
"micromatch": ["micromatch@4.0.8", "", { "dependencies": { "braces": "^3.0.3", "picomatch": "^2.3.1" } }, "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA=="],
|
||||
|
||||
"mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="],
|
||||
|
||||
"mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="],
|
||||
|
||||
"mimic-response": ["mimic-response@3.1.0", "", {}, "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ=="],
|
||||
|
||||
"minimist": ["minimist@1.2.8", "", {}, "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA=="],
|
||||
|
||||
"mkdirp-classic": ["mkdirp-classic@0.5.3", "", {}, "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A=="],
|
||||
|
||||
"mongodb": ["mongodb@7.2.0", "", { "dependencies": { "@mongodb-js/saslprep": "^1.3.0", "bson": "^7.2.0", "mongodb-connection-string-url": "^7.0.0" }, "peerDependencies": { "@aws-sdk/credential-providers": "^3.806.0", "@mongodb-js/zstd": "^7.0.0", "gcp-metadata": "^7.0.1", "kerberos": "^7.0.0", "mongodb-client-encryption": ">=7.0.0 <7.1.0", "snappy": "^7.3.2", "socks": "^2.8.6" }, "optionalPeers": ["@aws-sdk/credential-providers", "@mongodb-js/zstd", "gcp-metadata", "kerberos", "mongodb-client-encryption", "snappy", "socks"] }, "sha512-F/2+BMZtLVhY30ioZp0dAmZ+IRZMBqI+nrv6t5+9/1AIwCa8sMRC3jBf81lpxMhnZgqq8CoUD503Z1oZWq1/sw=="],
|
||||
|
||||
"mongodb-connection-string-url": ["mongodb-connection-string-url@7.0.1", "", { "dependencies": { "@types/whatwg-url": "^13.0.0", "whatwg-url": "^14.1.0" } }, "sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ=="],
|
||||
|
||||
"mongoose": ["mongoose@9.6.3", "", { "dependencies": { "kareem": "3.3.0", "mongodb": "~7.2", "mpath": "0.9.0", "mquery": "6.0.0", "ms": "2.1.3", "sift": "17.1.3" } }, "sha512-vI6dTTlQnfMCyyQ5TrvhG0bCRs4dq5e1uFNPtOOWsOhn0fSg8AoIHjfyyCYr8aybyvPs845dRHGxsC3w/fHcBA=="],
|
||||
|
||||
"mpath": ["mpath@0.9.0", "", {}, "sha512-ikJRQTk8hw5DEoFVxHG1Gn9T/xcjtdnOKIU1JTmGjZZlg9LST2mBLmcX3/ICIbgJydT2GOc15RnNy5mHmzfSew=="],
|
||||
|
||||
"mquery": ["mquery@6.0.0", "", {}, "sha512-b2KQNsmgtkscfeDgkYMcWGn9vZI9YoXh802VDEwE6qc50zxBFQ0Oo8ROkawbPAsXCY1/Z1yp0MagqsZStPWJjw=="],
|
||||
|
||||
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||
|
||||
"msgpackr": ["msgpackr@1.11.12", "", { "optionalDependencies": { "msgpackr-extract": "^3.0.2" } }, "sha512-RBdJ1Un7yGlXWajrkxcSa93nvQ0w4zBf60c0yYv7YtBelP8H2FA7XsfBbMHtXKXUMUxH7zV3Zuozh+kUQWhHvg=="],
|
||||
|
||||
"msgpackr-extract": ["msgpackr-extract@3.0.4", "", { "dependencies": { "node-gyp-build-optional-packages": "5.2.2" }, "optionalDependencies": { "@msgpackr-extract/msgpackr-extract-darwin-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-darwin-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-arm64": "3.0.4", "@msgpackr-extract/msgpackr-extract-linux-x64": "3.0.4", "@msgpackr-extract/msgpackr-extract-win32-x64": "3.0.4" }, "bin": { "download-msgpackr-prebuilds": "bin/download-prebuilds.js" } }, "sha512-4kmO/MdyUIkLIvTPr8VHLil4AtoKIoniWPIEk5+CDy0xnWC84azhSFmuJ7PxZdsYtiP5kEeQsORAVIeMgxT+Hw=="],
|
||||
|
||||
"multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="],
|
||||
|
||||
"mustache": ["mustache@4.2.0", "", { "bin": { "mustache": "bin/mustache" } }, "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ=="],
|
||||
|
||||
"napi-build-utils": ["napi-build-utils@2.0.0", "", {}, "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA=="],
|
||||
|
||||
"natural": ["natural@8.1.1", "", { "dependencies": { "afinn-165": "^2.0.2", "afinn-165-financialmarketnews": "^3.0.0", "apparatus": "^0.0.10", "dotenv": "^17.3.1", "memjs": "^1.3.2", "mongoose": "^9.2.1", "pg": "^8.18.0", "redis": "^5.11.0", "safe-stable-stringify": "^2.5.0", "stopwords-iso": "^1.1.0", "sylvester": "^0.0.21", "underscore": "^1.13.0", "uuid": "^13.0.0", "wordnet-db": "^3.1.14" } }, "sha512-Ucb+lsUcGxUqu3rn8cwHjT6gJQosO63nIX/aBQXB3+IDkNbFV7PuviysO+Rzz3aKn7PZhPj3bNF4PS9gDVjYCQ=="],
|
||||
|
||||
"node-abi": ["node-abi@3.92.0", "", { "dependencies": { "semver": "^7.3.5" } }, "sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ=="],
|
||||
|
||||
"node-domexception": ["node-domexception@1.0.0", "", {}, "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ=="],
|
||||
|
||||
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
||||
|
||||
"node-gyp-build-optional-packages": ["node-gyp-build-optional-packages@5.2.2", "", { "dependencies": { "detect-libc": "^2.0.1" }, "bin": { "node-gyp-build-optional-packages": "bin.js", "node-gyp-build-optional-packages-optional": "optional.js", "node-gyp-build-optional-packages-test": "build-test.js" } }, "sha512-s+w+rBWnpTMwSFbaE0UXsRlg7hU4FjekKU4eyAih5T8nJuNZT1nNsskXpxmeqSK9UzkBl6UgRlnKc8hz8IEqOw=="],
|
||||
|
||||
"obuf": ["obuf@1.1.2", "", {}, "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg=="],
|
||||
|
||||
"ollama": ["ollama@0.5.18", "", { "dependencies": { "whatwg-fetch": "^3.6.20" } }, "sha512-lTFqTf9bo7Cd3hpF6CviBe/DEhewjoZYd9N/uCe7O20qYTvGqrNOFOBDj3lbZgFWHUgDv5EeyusYxsZSLS8nvg=="],
|
||||
|
||||
"once": ["once@1.4.0", "", { "dependencies": { "wrappy": "1" } }, "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w=="],
|
||||
|
||||
"open": ["open@10.2.0", "", { "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" } }, "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA=="],
|
||||
|
||||
"openai": ["openai@4.104.0", "", { "dependencies": { "@types/node": "^18.11.18", "@types/node-fetch": "^2.6.4", "abort-controller": "^3.0.0", "agentkeepalive": "^4.2.1", "form-data-encoder": "1.7.2", "formdata-node": "^4.3.2", "node-fetch": "^2.6.7" }, "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA=="],
|
||||
|
||||
"p-finally": ["p-finally@1.0.0", "", {}, "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow=="],
|
||||
|
||||
"p-queue": ["p-queue@6.6.2", "", { "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" } }, "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ=="],
|
||||
|
||||
"p-retry": ["p-retry@4.6.2", "", { "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" } }, "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ=="],
|
||||
|
||||
"p-timeout": ["p-timeout@3.2.0", "", { "dependencies": { "p-finally": "^1.0.0" } }, "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg=="],
|
||||
|
||||
"packet-reader": ["packet-reader@1.0.0", "", {}, "sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ=="],
|
||||
|
||||
"path-key": ["path-key@3.1.1", "", {}, "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q=="],
|
||||
|
||||
"pg": ["pg@8.11.3", "", { "dependencies": { "buffer-writer": "2.0.0", "packet-reader": "1.0.0", "pg-connection-string": "^2.6.2", "pg-pool": "^3.6.1", "pg-protocol": "^1.6.0", "pg-types": "^2.1.0", "pgpass": "1.x" }, "optionalDependencies": { "pg-cloudflare": "^1.1.1" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g=="],
|
||||
|
||||
"pg-cloudflare": ["pg-cloudflare@1.4.0", "", {}, "sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A=="],
|
||||
|
||||
"pg-connection-string": ["pg-connection-string@2.13.0", "", {}, "sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig=="],
|
||||
|
||||
"pg-int8": ["pg-int8@1.0.1", "", {}, "sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw=="],
|
||||
|
||||
"pg-numeric": ["pg-numeric@1.0.2", "", {}, "sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw=="],
|
||||
|
||||
"pg-pool": ["pg-pool@3.14.0", "", { "peerDependencies": { "pg": ">=8.0" } }, "sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw=="],
|
||||
|
||||
"pg-protocol": ["pg-protocol@1.14.0", "", {}, "sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA=="],
|
||||
|
||||
"pg-types": ["pg-types@4.1.0", "", { "dependencies": { "pg-int8": "1.0.1", "pg-numeric": "1.0.2", "postgres-array": "~3.0.1", "postgres-bytea": "~3.0.0", "postgres-date": "~2.1.0", "postgres-interval": "^3.0.0", "postgres-range": "^1.1.1" } }, "sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg=="],
|
||||
|
||||
"pgpass": ["pgpass@1.0.5", "", { "dependencies": { "split2": "^4.1.0" } }, "sha512-FdW9r/jQZhSeohs1Z3sI1yxFQNFvMcnmfuj4WBMUTxOrAyLMaTcE1aAMBiTlbMNaXvBCQuVi0R7hd8udDSP7ug=="],
|
||||
|
||||
"picocolors": ["picocolors@1.1.1", "", {}, "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA=="],
|
||||
|
||||
"picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="],
|
||||
|
||||
"postgres-array": ["postgres-array@3.0.4", "", {}, "sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ=="],
|
||||
|
||||
"postgres-bytea": ["postgres-bytea@3.0.0", "", { "dependencies": { "obuf": "~1.1.2" } }, "sha512-CNd4jim9RFPkObHSjVHlVrxoVQXz7quwNFpz7RY1okNNme49+sVyiTvTRobiLV548Hx/hb1BG+iE7h9493WzFw=="],
|
||||
|
||||
"postgres-date": ["postgres-date@2.1.0", "", {}, "sha512-K7Juri8gtgXVcDfZttFKVmhglp7epKb1K4pgrkLxehjqkrgPhfG6OO8LHLkfaqkbpjNRnra018XwAr1yQFWGcA=="],
|
||||
|
||||
"postgres-interval": ["postgres-interval@3.0.0", "", {}, "sha512-BSNDnbyZCXSxgA+1f5UU2GmwhoI0aU5yMxRGO8CdFEcY2BQF9xm/7MqKnYoM1nJDk8nONNWDk9WeSmePFhQdlw=="],
|
||||
|
||||
"postgres-range": ["postgres-range@1.1.4", "", {}, "sha512-i/hbxIE9803Alj/6ytL7UHQxRvZkI9O4Sy+J3HGc4F4oo/2eQAjTSNJ0bfxyse3bH0nuVesCk+3IRLaMtG3H6w=="],
|
||||
|
||||
"prebuild-install": ["prebuild-install@7.1.3", "", { "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", "github-from-package": "0.0.0", "minimist": "^1.2.3", "mkdirp-classic": "^0.5.3", "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", "simple-get": "^4.0.0", "tar-fs": "^2.0.0", "tunnel-agent": "^0.6.0" }, "bin": { "prebuild-install": "bin.js" } }, "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug=="],
|
||||
|
||||
"pretty-format": ["pretty-format@29.7.0", "", { "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" } }, "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ=="],
|
||||
|
||||
"protobufjs": ["protobufjs@7.6.1", "", { "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.5", "@protobufjs/eventemitter": "^1.1.1", "@protobufjs/fetch": "^1.1.1", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.2", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.1", "@types/node": ">=13.7.0", "long": "^5.3.2" } }, "sha512-4K0myLaWL5EteuSAro91EGFgcfVgxb64Jx+7oDAY6GOkXD4M69yuSEljNcInGVCA5sOPxmZ/EqDLj2x0Q0+Ygg=="],
|
||||
|
||||
"proxy-from-env": ["proxy-from-env@2.1.0", "", {}, "sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA=="],
|
||||
|
||||
"pump": ["pump@3.0.4", "", { "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA=="],
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"pure-rand": ["pure-rand@8.4.0", "", {}, "sha512-IoM8YF/jY0hiugFo/wOWqfmarlE6J0wc6fDK1PhftMk7MGhVZl88sZimmqBBFomLOCSmcCCpsfj7wXASCpvK9A=="],
|
||||
|
||||
"rc": ["rc@1.2.8", "", { "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "./cli.js" } }, "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw=="],
|
||||
|
||||
"react-is": ["react-is@18.3.1", "", {}, "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg=="],
|
||||
|
||||
"readable-stream": ["readable-stream@3.6.2", "", { "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" } }, "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA=="],
|
||||
|
||||
"redis": ["redis@4.7.1", "", { "dependencies": { "@redis/bloom": "1.2.0", "@redis/client": "1.6.1", "@redis/graph": "1.1.1", "@redis/json": "1.0.7", "@redis/search": "1.2.0", "@redis/time-series": "1.1.0" } }, "sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ=="],
|
||||
|
||||
"retry": ["retry@0.13.1", "", {}, "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg=="],
|
||||
|
||||
"run-applescript": ["run-applescript@7.1.0", "", {}, "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q=="],
|
||||
|
||||
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
|
||||
|
||||
"safe-stable-stringify": ["safe-stable-stringify@2.5.0", "", {}, "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA=="],
|
||||
|
||||
"semver": ["semver@7.8.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg=="],
|
||||
|
||||
"shebang-command": ["shebang-command@2.0.0", "", { "dependencies": { "shebang-regex": "^3.0.0" } }, "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA=="],
|
||||
|
||||
"shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="],
|
||||
|
||||
"sift": ["sift@17.1.3", "", {}, "sha512-Rtlj66/b0ICeFzYTuNvX/EF1igRbbnGSvEyT79McoZa/DeGhMyC5pWKOEsZKnpkqtSeovd5FL/bjHWC3CIIvCQ=="],
|
||||
|
||||
"simple-concat": ["simple-concat@1.0.1", "", {}, "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q=="],
|
||||
|
||||
"simple-get": ["simple-get@4.0.1", "", { "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA=="],
|
||||
|
||||
"slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
||||
|
||||
"sparse-bitfield": ["sparse-bitfield@3.0.3", "", { "dependencies": { "memory-pager": "^1.0.2" } }, "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ=="],
|
||||
|
||||
"split2": ["split2@4.2.0", "", {}, "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg=="],
|
||||
|
||||
"stack-utils": ["stack-utils@2.0.6", "", { "dependencies": { "escape-string-regexp": "^2.0.0" } }, "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ=="],
|
||||
|
||||
"stopwords-iso": ["stopwords-iso@1.1.0", "", {}, "sha512-I6GPS/E0zyieHehMRPQcqkiBMJKGgLta+1hREixhoLPqEA0AlVFiC43dl8uPpmkkeRdDMzYRWFWk5/l9x7nmNg=="],
|
||||
|
||||
"string_decoder": ["string_decoder@1.3.0", "", { "dependencies": { "safe-buffer": "~5.2.0" } }, "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA=="],
|
||||
|
||||
"strip-json-comments": ["strip-json-comments@2.0.1", "", {}, "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ=="],
|
||||
|
||||
"suffix-thumb": ["suffix-thumb@5.0.2", "", {}, "sha512-I5PWXAFKx3FYnI9a+dQMWNqTxoRt6vdBdb0O+BJ1sxXCWtSoQCusc13E58f+9p4MYx/qCnEMkD5jac6K2j3dgA=="],
|
||||
|
||||
"supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="],
|
||||
|
||||
"sylvester": ["sylvester@0.0.21", "", {}, "sha512-yUT0ukFkFEt4nb+NY+n2ag51aS/u9UHXoZw+A4jgD77/jzZsBoSDHuqysrVCBC4CYR4TYvUJq54ONpXgDBH8tA=="],
|
||||
|
||||
"tar-fs": ["tar-fs@2.1.4", "", { "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", "pump": "^3.0.0", "tar-stream": "^2.1.4" } }, "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ=="],
|
||||
|
||||
"tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
|
||||
|
||||
"to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="],
|
||||
|
||||
"toml": ["toml@4.1.1", "", {}, "sha512-EBJnVBr3dTXdA89WVFoAIPUqkBjxPMwRqsfuo1r240tKFHXv3zgca4+NJib/h6TyvGF7vOawz0jGuryJCdNHrw=="],
|
||||
|
||||
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||
|
||||
"tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="],
|
||||
|
||||
"tunnel-agent": ["tunnel-agent@0.6.0", "", { "dependencies": { "safe-buffer": "^5.0.1" } }, "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w=="],
|
||||
|
||||
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||
|
||||
"underscore": ["underscore@1.13.8", "", {}, "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ=="],
|
||||
|
||||
"undici": ["undici@5.28.5", "", { "dependencies": { "@fastify/busboy": "^2.0.0" } }, "sha512-zICwjrDrcrUE0pyyJc1I2QzBkLM8FINsgOrt6WjA+BgajVq9Nxu2PbFFXUrAggLfDXlZGZBVZYw7WNV5KiBiBA=="],
|
||||
|
||||
"undici-types": ["undici-types@7.24.6", "", {}, "sha512-WRNW+sJgj5OBN4/0JpHFqtqzhpbnV0GuB+OozA9gCL7a993SmU+1JBZCzLNxYsbMfIeDL+lTsphD5jN5N+n0zg=="],
|
||||
|
||||
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
|
||||
|
||||
"uuid": ["uuid@11.1.1", "", { "bin": { "uuid": "dist/esm/bin/uuid" } }, "sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ=="],
|
||||
|
||||
"web-streams-polyfill": ["web-streams-polyfill@3.3.3", "", {}, "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw=="],
|
||||
|
||||
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||
|
||||
"whatwg-fetch": ["whatwg-fetch@3.6.20", "", {}, "sha512-EqhiFU6daOA8kpjOWTL0olhVOF3i7OrFzSYiGsEMB8GcXS+RrzauAERX65xMeNWVqxA6HXH2m69Z9LaKKdisfg=="],
|
||||
|
||||
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
||||
|
||||
"which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="],
|
||||
|
||||
"wordnet-db": ["wordnet-db@3.1.14", "", {}, "sha512-zVyFsvE+mq9MCmwXUWHIcpfbrHHClZWZiVOzKSxNJruIcFn2RbY55zkhiAMMxM8zCVSmtNiViq8FsAZSFpMYag=="],
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"ws": ["ws@8.21.0", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g=="],
|
||||
|
||||
"wsl-utils": ["wsl-utils@0.1.0", "", { "dependencies": { "is-wsl": "^3.1.0" } }, "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw=="],
|
||||
|
||||
"xtend": ["xtend@4.0.2", "", {}, "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="],
|
||||
|
||||
"yallist": ["yallist@4.0.0", "", {}, "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="],
|
||||
|
||||
"yaml": ["yaml@2.9.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA=="],
|
||||
|
||||
"zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="],
|
||||
|
||||
"zod-to-json-schema": ["zod-to-json-schema@3.25.2", "", { "peerDependencies": { "zod": "^3.25.28 || ^4" } }, "sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA=="],
|
||||
|
||||
"@anthropic-ai/sdk/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
||||
|
||||
"@typespec/ts-http-runtime/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"chalk/ansi-styles": ["ansi-styles@4.3.0", "", { "dependencies": { "color-convert": "^2.0.1" } }, "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg=="],
|
||||
|
||||
"cloudflare/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
||||
|
||||
"effect/uuid": ["uuid@13.0.2", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw=="],
|
||||
|
||||
"formdata-node/web-streams-polyfill": ["web-streams-polyfill@4.0.0-beta.3", "", {}, "sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug=="],
|
||||
|
||||
"gaxios/https-proxy-agent": ["https-proxy-agent@7.0.6", "", { "dependencies": { "agent-base": "^7.1.2", "debug": "4" } }, "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw=="],
|
||||
|
||||
"gaxios/node-fetch": ["node-fetch@3.3.2", "", { "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" } }, "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA=="],
|
||||
|
||||
"groq-sdk/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
||||
|
||||
"http-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"mem0ai/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="],
|
||||
|
||||
"mongodb-connection-string-url/whatwg-url": ["whatwg-url@14.2.0", "", { "dependencies": { "tr46": "^5.1.0", "webidl-conversions": "^7.0.0" } }, "sha512-De72GdQZzNTUBBChsXueQUnPKDkg/5A5zp7pFDuQAj5UFoENpiACU0wlCvzpAGnTkj++ihpKwKyYewn/XNUbKw=="],
|
||||
|
||||
"natural/pg": ["pg@8.21.0", "", { "dependencies": { "pg-connection-string": "^2.13.0", "pg-pool": "^3.14.0", "pg-protocol": "^1.14.0", "pg-types": "2.2.0", "pgpass": "1.0.5" }, "optionalDependencies": { "pg-cloudflare": "^1.4.0" }, "peerDependencies": { "pg-native": ">=3.0.1" }, "optionalPeers": ["pg-native"] }, "sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA=="],
|
||||
|
||||
"natural/redis": ["redis@5.12.1", "", { "dependencies": { "@redis/bloom": "5.12.1", "@redis/client": "5.12.1", "@redis/json": "5.12.1", "@redis/search": "5.12.1", "@redis/time-series": "5.12.1" } }, "sha512-LDsoVvb/CpoV9EN3FXvgvSHNJWuCIzl9MiO3ppOevuGLpSGJhwfQjpEwfFJcQvNSddHADDdZaWx0HnmMxRXG7g=="],
|
||||
|
||||
"natural/uuid": ["uuid@13.0.2", "", { "bin": { "uuid": "dist-node/bin/uuid" } }, "sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw=="],
|
||||
|
||||
"openai/@types/node": ["@types/node@18.19.130", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg=="],
|
||||
|
||||
"pg/pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||
|
||||
"rc/ini": ["ini@1.3.8", "", {}, "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="],
|
||||
|
||||
"@anthropic-ai/sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
|
||||
|
||||
"@typespec/ts-http-runtime/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"cloudflare/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
|
||||
|
||||
"gaxios/https-proxy-agent/agent-base": ["agent-base@7.1.4", "", {}, "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ=="],
|
||||
|
||||
"groq-sdk/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
|
||||
|
||||
"mongodb-connection-string-url/whatwg-url/tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="],
|
||||
|
||||
"mongodb-connection-string-url/whatwg-url/webidl-conversions": ["webidl-conversions@7.0.0", "", {}, "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g=="],
|
||||
|
||||
"natural/pg/pg-types": ["pg-types@2.2.0", "", { "dependencies": { "pg-int8": "1.0.1", "postgres-array": "~2.0.0", "postgres-bytea": "~1.0.0", "postgres-date": "~1.0.4", "postgres-interval": "^1.1.0" } }, "sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA=="],
|
||||
|
||||
"natural/redis/@redis/bloom": ["@redis/bloom@5.12.1", "", { "peerDependencies": { "@redis/client": "^5.12.1" } }, "sha512-PUUfv+ms7jgPSBVoo/DN4AkPHj4D5TZSd6SbJX7egzBplkYUcKmHRE8RKia7UtZ8bSQbLguLvxVO+asKtQfZWA=="],
|
||||
|
||||
"natural/redis/@redis/client": ["@redis/client@5.12.1", "", { "dependencies": { "cluster-key-slot": "1.1.2" }, "peerDependencies": { "@node-rs/xxhash": "^1.1.0", "@opentelemetry/api": ">=1 <2" }, "optionalPeers": ["@node-rs/xxhash", "@opentelemetry/api"] }, "sha512-7aPGWeqA3uFm43o19umzdl16CEjK/JQGtSXVPevplTaOU3VJA/rseBC1QvYUz9lLDIMBimc4SW/zrW4S89BaCA=="],
|
||||
|
||||
"natural/redis/@redis/json": ["@redis/json@5.12.1", "", { "peerDependencies": { "@redis/client": "^5.12.1" } }, "sha512-eOze75esLve4vfqDel7aMX08CNaiLLQS2fV8mpRN9NxPe1rVR4vQyYiW/OgtGUysF6QOr9ANhfxABKNOJfXdKg=="],
|
||||
|
||||
"natural/redis/@redis/search": ["@redis/search@5.12.1", "", { "peerDependencies": { "@redis/client": "^5.12.1" } }, "sha512-ItlxbxC9cKI6IU1TLWoczwJCRb6TdmkEpWv05UrPawqaAnWGRu3rcIqsc5vN483T2fSociuyV1UkWIL5I4//2w=="],
|
||||
|
||||
"natural/redis/@redis/time-series": ["@redis/time-series@5.12.1", "", { "peerDependencies": { "@redis/client": "^5.12.1" } }, "sha512-c6JL6E3EcZJuNqKFz+KM+l9l5mpcQiKvTwgA3blt5glWJ8hjDk0yeHN3beE/MpqYIQ8UEX44ItQzgkE/gCBELQ=="],
|
||||
|
||||
"openai/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
|
||||
|
||||
"pg/pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
||||
|
||||
"pg/pg-types/postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="],
|
||||
|
||||
"pg/pg-types/postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
|
||||
|
||||
"pg/pg-types/postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||
|
||||
"natural/pg/pg-types/postgres-array": ["postgres-array@2.0.0", "", {}, "sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA=="],
|
||||
|
||||
"natural/pg/pg-types/postgres-bytea": ["postgres-bytea@1.0.1", "", {}, "sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ=="],
|
||||
|
||||
"natural/pg/pg-types/postgres-date": ["postgres-date@1.0.7", "", {}, "sha512-suDmjLVQg78nMK2UZ454hAG+OAW+HQPZ6n++TNDUX+L0+uUlLywnoxJKDou51Zm+zTCjrCl0Nq6J9C5hP9vK/Q=="],
|
||||
|
||||
"natural/pg/pg-types/postgres-interval": ["postgres-interval@1.2.0", "", { "dependencies": { "xtend": "^4.0.0" } }, "sha512-9ZhXKM/rw350N1ovuWHbGxnGh/SNJ4cnxHiM0rxE4VN41wsg8P8zWn9hv/buK00RP4WvlOyr/RBDiptyxVbkZQ=="],
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
import { afterEach, beforeEach, describe, expect, test } from "bun:test";
|
||||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import {
|
||||
loadDreamConfig,
|
||||
incrementSessionCount,
|
||||
checkCheapGates,
|
||||
checkMemoryGate,
|
||||
acquireDreamLock,
|
||||
releaseDreamLock,
|
||||
recordDreamCompletion,
|
||||
DREAM_DEFAULTS,
|
||||
DREAM_PROTOCOL,
|
||||
} from "./dream";
|
||||
|
||||
let dir: string;
|
||||
|
||||
beforeEach(() => {
|
||||
dir = mkdtempSync(join(tmpdir(), "mem0-dream-"));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
try {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
} catch {
|
||||
/* ignore */
|
||||
}
|
||||
delete process.env.MEM0_DREAM;
|
||||
});
|
||||
|
||||
describe("auto-dream gates", () => {
|
||||
test("memory gate passes at >= minMemories, fails below", () => {
|
||||
expect(checkMemoryGate(DREAM_DEFAULTS.minMemories, {}).pass).toBe(true);
|
||||
expect(checkMemoryGate(DREAM_DEFAULTS.minMemories - 1, {}).pass).toBe(false);
|
||||
});
|
||||
|
||||
test("cheap gates: fresh state blocks on session count, passes after enough sessions", () => {
|
||||
// Fresh state: time gate passes (lastConsolidatedAt=0), but 0 sessions blocks.
|
||||
expect(checkCheapGates(dir, {}).proceed).toBe(false);
|
||||
for (let i = 0; i < DREAM_DEFAULTS.minSessions; i++) {
|
||||
incrementSessionCount(dir, `ses_${i}`);
|
||||
}
|
||||
expect(checkCheapGates(dir, {}).proceed).toBe(true);
|
||||
});
|
||||
|
||||
test("incrementSessionCount only counts distinct session ids", () => {
|
||||
incrementSessionCount(dir, "ses_a");
|
||||
incrementSessionCount(dir, "ses_a");
|
||||
incrementSessionCount(dir, "ses_a");
|
||||
expect(checkCheapGates(dir, { minHours: 0 }).reason).toContain("sessions: 1");
|
||||
});
|
||||
|
||||
test("recordDreamCompletion resets gates (recent time blocks again)", () => {
|
||||
for (let i = 0; i < 6; i++) incrementSessionCount(dir, `ses_${i}`);
|
||||
expect(checkCheapGates(dir, {}).proceed).toBe(true);
|
||||
recordDreamCompletion(dir);
|
||||
const r = checkCheapGates(dir, {});
|
||||
expect(r.proceed).toBe(false);
|
||||
expect(r.reason).toContain("time");
|
||||
});
|
||||
|
||||
test("dream lock is exclusive and reclaimable after release", () => {
|
||||
expect(acquireDreamLock(dir)).toBe(true);
|
||||
expect(acquireDreamLock(dir)).toBe(false);
|
||||
releaseDreamLock(dir);
|
||||
expect(acquireDreamLock(dir)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe("dream config", () => {
|
||||
test("defaults when no settings file", () => {
|
||||
const cfg = loadDreamConfig(dir);
|
||||
expect(cfg.enabled).toBe(true);
|
||||
expect(cfg.auto).toBe(true);
|
||||
expect(cfg.minMemories).toBe(DREAM_DEFAULTS.minMemories);
|
||||
});
|
||||
|
||||
test("MEM0_DREAM=false force-disables", () => {
|
||||
process.env.MEM0_DREAM = "false";
|
||||
expect(loadDreamConfig(dir).enabled).toBe(false);
|
||||
});
|
||||
|
||||
test("settings.json dream block overrides defaults", () => {
|
||||
writeFileSync(
|
||||
join(dir, "settings.json"),
|
||||
JSON.stringify({ dream: { minMemories: 99, auto: false } }),
|
||||
);
|
||||
const cfg = loadDreamConfig(dir);
|
||||
expect(cfg.minMemories).toBe(99);
|
||||
expect(cfg.auto).toBe(false);
|
||||
expect(cfg.enabled).toBe(true);
|
||||
});
|
||||
|
||||
test("protocol uses native tools, not the MCP tool", () => {
|
||||
expect(DREAM_PROTOCOL).toContain("get_memories");
|
||||
expect(DREAM_PROTOCOL).toContain("add_memory");
|
||||
expect(DREAM_PROTOCOL).not.toContain("mem0_memory");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,225 @@
|
||||
/**
|
||||
* Auto-dream: gated automatic memory consolidation for the Mem0 OpenCode plugin.
|
||||
*
|
||||
* Ported from the (stable) pi-agent plugin's dream module and adapted to
|
||||
* OpenCode's hook model. When the cheap gates (time since last consolidation +
|
||||
* sessions since) and the memory-count gate all pass, the plugin injects the
|
||||
* DREAM_PROTOCOL into the agent's context so it consolidates memories (merge
|
||||
* duplicates, drop stale/sensitive entries, rewrite vague ones) before
|
||||
* answering. A filesystem lock prevents concurrent sessions from dreaming at
|
||||
* once, and completion is recorded so it won't re-trigger until the next cycle.
|
||||
*
|
||||
* State + lock live in ~/.mem0/ alongside settings.json. Opt out with
|
||||
* MEM0_DREAM=false, or tune via the `dream` block in ~/.mem0/settings.json.
|
||||
*/
|
||||
|
||||
import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
export interface DreamConfig {
|
||||
enabled: boolean;
|
||||
auto: boolean;
|
||||
minHours: number;
|
||||
minSessions: number;
|
||||
minMemories: number;
|
||||
}
|
||||
|
||||
interface DreamState {
|
||||
lastConsolidatedAt: number;
|
||||
sessionsSince: number;
|
||||
lastSessionId: string | null;
|
||||
}
|
||||
|
||||
interface DreamLock {
|
||||
pid: number;
|
||||
startedAt: number;
|
||||
}
|
||||
|
||||
const LOCK_STALE_MS = 60 * 60 * 1000;
|
||||
|
||||
export const DREAM_DEFAULTS: DreamConfig = {
|
||||
enabled: true,
|
||||
auto: true,
|
||||
minHours: 24,
|
||||
minSessions: 5,
|
||||
minMemories: 20,
|
||||
};
|
||||
|
||||
function statePath(stateDir: string): string {
|
||||
return join(stateDir, "mem0-dream-state.json");
|
||||
}
|
||||
|
||||
function lockPath(stateDir: string): string {
|
||||
return join(stateDir, "mem0-dream.lock");
|
||||
}
|
||||
|
||||
function ensureDir(dir: string): void {
|
||||
try {
|
||||
mkdirSync(dir, { recursive: true });
|
||||
} catch {
|
||||
/* exists */
|
||||
}
|
||||
}
|
||||
|
||||
function readState(stateDir: string): DreamState {
|
||||
try {
|
||||
return JSON.parse(readFileSync(statePath(stateDir), "utf-8")) as DreamState;
|
||||
} catch {
|
||||
return { lastConsolidatedAt: 0, sessionsSince: 0, lastSessionId: null };
|
||||
}
|
||||
}
|
||||
|
||||
function writeState(stateDir: string, state: DreamState): void {
|
||||
ensureDir(stateDir);
|
||||
writeFileSync(statePath(stateDir), JSON.stringify(state, null, 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* Load dream config from ~/.mem0/settings.json (`dream` block), applying
|
||||
* defaults. MEM0_DREAM=false (or 0/no/off) force-disables regardless.
|
||||
*/
|
||||
export function loadDreamConfig(settingsDir: string): DreamConfig {
|
||||
let envEnabled: boolean | undefined;
|
||||
const env = process.env.MEM0_DREAM;
|
||||
if (env !== undefined) {
|
||||
const s = env.toLowerCase();
|
||||
envEnabled = s !== "false" && s !== "0" && s !== "no" && s !== "off";
|
||||
}
|
||||
|
||||
let cfg: DreamConfig = { ...DREAM_DEFAULTS };
|
||||
try {
|
||||
const sp = join(settingsDir, "settings.json");
|
||||
if (existsSync(sp)) {
|
||||
const settings = JSON.parse(readFileSync(sp, "utf-8"));
|
||||
const d = settings?.dream;
|
||||
if (d && typeof d === "object") {
|
||||
cfg = {
|
||||
enabled: typeof d.enabled === "boolean" ? d.enabled : cfg.enabled,
|
||||
auto: typeof d.auto === "boolean" ? d.auto : cfg.auto,
|
||||
minHours: typeof d.minHours === "number" ? d.minHours : cfg.minHours,
|
||||
minSessions: typeof d.minSessions === "number" ? d.minSessions : cfg.minSessions,
|
||||
minMemories: typeof d.minMemories === "number" ? d.minMemories : cfg.minMemories,
|
||||
};
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
/* defaults */
|
||||
}
|
||||
|
||||
if (envEnabled !== undefined) cfg.enabled = envEnabled;
|
||||
return cfg;
|
||||
}
|
||||
|
||||
/** Count a new session toward the dream gate (once per distinct sessionId). */
|
||||
export function incrementSessionCount(stateDir: string, sessionId: string): void {
|
||||
const state = readState(stateDir);
|
||||
if (state.lastSessionId !== sessionId) {
|
||||
state.sessionsSince++;
|
||||
state.lastSessionId = sessionId;
|
||||
writeState(stateDir, state);
|
||||
}
|
||||
}
|
||||
|
||||
/** Cheap gates that don't need an API call: time since last + sessions since. */
|
||||
export function checkCheapGates(
|
||||
stateDir: string,
|
||||
config: Partial<DreamConfig>,
|
||||
): { proceed: boolean; reason?: string } {
|
||||
const minHours = config.minHours ?? DREAM_DEFAULTS.minHours;
|
||||
const minSessions = config.minSessions ?? DREAM_DEFAULTS.minSessions;
|
||||
const state = readState(stateDir);
|
||||
|
||||
const hoursSince = (Date.now() - state.lastConsolidatedAt) / 3_600_000;
|
||||
if (hoursSince < minHours) {
|
||||
return { proceed: false, reason: `time: ${hoursSince.toFixed(1)}h < ${minHours}h` };
|
||||
}
|
||||
if (state.sessionsSince < minSessions) {
|
||||
return { proceed: false, reason: `sessions: ${state.sessionsSince} < ${minSessions}` };
|
||||
}
|
||||
return { proceed: true };
|
||||
}
|
||||
|
||||
/** Memory-count gate (uses the count already fetched at session init). */
|
||||
export function checkMemoryGate(
|
||||
memoryCount: number,
|
||||
config: Partial<DreamConfig>,
|
||||
): { pass: boolean; reason?: string } {
|
||||
const minMemories = config.minMemories ?? DREAM_DEFAULTS.minMemories;
|
||||
if (memoryCount < minMemories) {
|
||||
return { pass: false, reason: `memories: ${memoryCount} < ${minMemories}` };
|
||||
}
|
||||
return { pass: true };
|
||||
}
|
||||
|
||||
/** Acquire an exclusive dream lock (stale locks > 1h are reclaimed). */
|
||||
export function acquireDreamLock(stateDir: string): boolean {
|
||||
ensureDir(stateDir);
|
||||
const lp = lockPath(stateDir);
|
||||
|
||||
try {
|
||||
const lock = JSON.parse(readFileSync(lp, "utf-8")) as DreamLock;
|
||||
if (Date.now() - lock.startedAt < LOCK_STALE_MS) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
unlinkSync(lp);
|
||||
} catch {
|
||||
/* race ok */
|
||||
}
|
||||
} catch {
|
||||
/* no lock file */
|
||||
}
|
||||
|
||||
const lock: DreamLock = { pid: process.pid, startedAt: Date.now() };
|
||||
try {
|
||||
writeFileSync(lp, JSON.stringify(lock), { flag: "wx" });
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
export function releaseDreamLock(stateDir: string): void {
|
||||
try {
|
||||
unlinkSync(lockPath(stateDir));
|
||||
} catch {
|
||||
/* already gone */
|
||||
}
|
||||
}
|
||||
|
||||
/** Reset the gates after a successful consolidation. */
|
||||
export function recordDreamCompletion(stateDir: string): void {
|
||||
const state = readState(stateDir);
|
||||
state.lastConsolidatedAt = Date.now();
|
||||
state.sessionsSince = 0;
|
||||
state.lastSessionId = null;
|
||||
writeState(stateDir, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* Consolidation protocol injected into the agent context when a dream is
|
||||
* triggered. Uses the plugin's native OpenCode memory tools (get_memories /
|
||||
* add_memory / delete_memory) rather than an MCP tool.
|
||||
*/
|
||||
export const DREAM_PROTOCOL = `<mem0-dream>
|
||||
You are running memory consolidation. Complete these steps using the mem0 memory tools (get_memories, add_memory, delete_memory):
|
||||
|
||||
1. ORIENT — Call get_memories to list all memories. Count by category. Note oldest/newest.
|
||||
|
||||
2. GATHER TARGETS — Review each memory. Classify as:
|
||||
- DELETE: sensitive information (API keys, passwords, tokens), expired/stale entries, noise, redundant operational details
|
||||
- MERGE: near-duplicates (same fact stated differently). Keep the better-worded one, delete the other.
|
||||
- REWRITE: vague, first-person, or poorly-categorized entries. add_memory with improved text, then delete_memory the old one.
|
||||
- KEEP: everything else.
|
||||
Skip any memory starting with "[PINNED]".
|
||||
|
||||
3. CONSOLIDATE — Execute the changes:
|
||||
- Delete stale/duplicate entries with delete_memory
|
||||
- For merges: add_memory the merged text, delete_memory both originals
|
||||
- For rewrites: add_memory the improved version, delete_memory the original
|
||||
|
||||
4. REPORT — Summarize: how many reviewed, deleted, merged, rewritten, final count.
|
||||
|
||||
Quality targets: zero sensitive data stored, zero duplicates, all entries are atomic (one fact each), 15-50 words each.
|
||||
After consolidation, respond to the user's message normally.
|
||||
</mem0-dream>`;
|
||||
@@ -0,0 +1,999 @@
|
||||
// Mem0 memory plugin for OpenCode: captures and recalls memories across sessions
|
||||
// (add / search / manage) via the Mem0 platform, wired through OpenCode plugin hooks.
|
||||
// Memory operations are exposed as native OpenCode tools backed by the mem0ai SDK
|
||||
// (no MCP server required).
|
||||
import type {Plugin} from "@opencode-ai/plugin";
|
||||
import {tool} from "@opencode-ai/plugin";
|
||||
import {MemoryClient} from "mem0ai";
|
||||
import {userInfo} from "os";
|
||||
import {basename, resolve, dirname} from "path";
|
||||
import {randomBytes} from "crypto";
|
||||
import {existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync} from "fs";
|
||||
import {homedir} from "os";
|
||||
import {join} from "path";
|
||||
import {createHash} from "crypto";
|
||||
import {captureEvent} from "./telemetry";
|
||||
import {
|
||||
loadDreamConfig,
|
||||
incrementSessionCount,
|
||||
checkCheapGates,
|
||||
checkMemoryGate,
|
||||
acquireDreamLock,
|
||||
releaseDreamLock,
|
||||
recordDreamCompletion,
|
||||
DREAM_PROTOCOL,
|
||||
} from "./dream";
|
||||
import {asScope, scopeSearchFilters, scopeWriteParams, resolveDefaultScope, SCOPE_GUIDANCE, type Scope} from "./scope";
|
||||
import {parseProjectFromRemote} from "./project";
|
||||
|
||||
async function getUserId(): Promise<string> {
|
||||
if (process.env.MEM0_USER_ID) return process.env.MEM0_USER_ID;
|
||||
try {
|
||||
return userInfo().username;
|
||||
} catch {
|
||||
}
|
||||
return process.env.USER || process.env.USERNAME || "unknown";
|
||||
}
|
||||
|
||||
async function getProjectId($: any): Promise<string> {
|
||||
if (process.env.MEM0_APP_ID) return process.env.MEM0_APP_ID;
|
||||
// Prefer the git remote's owner/repo — stable across clones, worktrees, and
|
||||
// sub-directories (handles https + ssh, incl. custom host aliases).
|
||||
try {
|
||||
const r = await $`git remote get-url origin`.quiet();
|
||||
const project = parseProjectFromRemote(r.stdout.toString());
|
||||
if (project) return project;
|
||||
} catch {
|
||||
}
|
||||
// No usable remote: use the git repo ROOT dir name, not cwd (which may be a
|
||||
// sub-directory, or your home dir if OpenCode was launched outside a repo).
|
||||
try {
|
||||
const r = await $`git rev-parse --show-toplevel`.quiet();
|
||||
const top = r.stdout.toString().trim();
|
||||
if (top) return basename(top);
|
||||
} catch {
|
||||
}
|
||||
return basename(process.cwd());
|
||||
}
|
||||
|
||||
async function getBranch($: any): Promise<string> {
|
||||
try {
|
||||
const r = await $`git branch --show-current`.quiet();
|
||||
return r.stdout.toString().trim() || "main";
|
||||
} catch {
|
||||
}
|
||||
return "main";
|
||||
}
|
||||
|
||||
function extractMemories(res: any): Array<{ memory: string; id: string }> {
|
||||
const arr = res?.results ?? res;
|
||||
if (!Array.isArray(arr)) return [];
|
||||
return arr.map((m: any) => ({memory: m.memory ?? "", id: m.id ?? ""}));
|
||||
}
|
||||
|
||||
function generateSessionId(): string {
|
||||
const ts = Math.floor(Date.now() / 1000);
|
||||
const rnd = randomBytes(3).toString("hex");
|
||||
return `ses_${ts}_${rnd}`;
|
||||
}
|
||||
|
||||
const SECRET_PATTERNS = [
|
||||
/sk-[A-Za-z0-9]{20,}/g,
|
||||
/m0-[A-Za-z0-9]{20,}/g,
|
||||
/AKIA[0-9A-Z]{16}/g,
|
||||
/xox[baprs]-[A-Za-z0-9-]{20,}/g,
|
||||
/ghp_[A-Za-z0-9]{36,}/g,
|
||||
/gho_[A-Za-z0-9]{36,}/g,
|
||||
];
|
||||
|
||||
function redact(text: string): string {
|
||||
let out = text;
|
||||
for (const re of SECRET_PATTERNS) {
|
||||
out = out.replace(re, "[REDACTED]");
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
/** Read & parse `~/.mem0/settings.json`, returning {} when missing/invalid. */
|
||||
function loadSettings(): Record<string, unknown> {
|
||||
try {
|
||||
const settingsPath = join(homedir(), ".mem0", "settings.json");
|
||||
if (!existsSync(settingsPath)) return {};
|
||||
return JSON.parse(readFileSync(settingsPath, "utf8"));
|
||||
} catch {
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
function loadGlobalSearch(): boolean {
|
||||
return loadSettings().global_search === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* The user's persisted default memory scope (set via the `mem0-scope` skill).
|
||||
* Read fresh so a scope change takes effect on the next memory operation without
|
||||
* restarting OpenCode. Defaults to "project".
|
||||
*/
|
||||
function loadDefaultScope(): Scope {
|
||||
return resolveDefaultScope(loadSettings());
|
||||
}
|
||||
|
||||
const CODING_CATEGORIES = [
|
||||
"architecture_decisions", "api_design", "data_models", "algorithms",
|
||||
"dependencies", "environment_setup", "testing_strategy", "debugging_notes",
|
||||
"performance", "security", "deployment", "code_conventions",
|
||||
"error_handling", "refactoring_history", "integrations", "onboarding",
|
||||
"project_meta",
|
||||
];
|
||||
|
||||
function categoriesFingerprint(): string {
|
||||
const sorted = [...CODING_CATEGORIES].sort();
|
||||
return createHash("sha256").update(sorted.join("\n")).digest("hex").slice(0, 16);
|
||||
}
|
||||
|
||||
function apiKeyFingerprint(apiKey: string): string {
|
||||
return createHash("sha256").update(apiKey).digest("hex").slice(0, 16);
|
||||
}
|
||||
|
||||
async function autoSetupCategories(mem0: MemoryClient, apiKey: string): Promise<void> {
|
||||
const stateDir = join(homedir(), ".mem0");
|
||||
const stateFile = join(stateDir, "categories_setup.json");
|
||||
const keyFp = apiKeyFingerprint(apiKey);
|
||||
const catFp = categoriesFingerprint();
|
||||
|
||||
let state: Record<string, string> = {};
|
||||
try {
|
||||
if (existsSync(stateFile)) {
|
||||
state = JSON.parse(readFileSync(stateFile, "utf8"));
|
||||
}
|
||||
} catch {}
|
||||
|
||||
if (state[keyFp] === catFp) return;
|
||||
|
||||
try {
|
||||
const project = await mem0.getProject({fields: ["customCategories"]});
|
||||
const existing: string[] = (project as any)?.custom_categories ?? (project as any)?.customCategories ?? [];
|
||||
const sortedExisting = [...existing].sort();
|
||||
const sortedTarget = [...CODING_CATEGORIES].sort();
|
||||
if (JSON.stringify(sortedExisting) === JSON.stringify(sortedTarget)) {
|
||||
state[keyFp] = catFp;
|
||||
mkdirSync(stateDir, {recursive: true});
|
||||
writeFileSync(stateFile, JSON.stringify(state, null, 2) + "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
await mem0.updateProject({customCategories: CODING_CATEGORIES as any});
|
||||
|
||||
state[keyFp] = catFp;
|
||||
mkdirSync(stateDir, {recursive: true});
|
||||
writeFileSync(stateFile, JSON.stringify(state, null, 2) + "\n");
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
const NUDGE_RE =
|
||||
/\b(remember\s+(this|that)|memorize|save\s+this|note\s+(this|that)|don'?t\s+forget|always\s+remember|never\s+forget|keep\s+(this|that)\s+in\s+(mind|memory)|store\s+(this|that))\b/i;
|
||||
|
||||
const RESUME_RE =
|
||||
/where\s+(did\s+)?(we|I)\s+(leave|left)\s+off|continue\s+(from\s+)?(where|last)|what\s+were\s+we\s+(working|doing)|pick\s+up\s+where|resume\s+(from\s+|where\s+)|what.s\s+the\s+(current|latest)\s+(state|status)|catch\s+me\s+up|where\s+are\s+we/i;
|
||||
|
||||
const ERROR_STRONG_RE =
|
||||
/Traceback \(most recent call last\)|panic: |FATAL:|error\[E\d+\]/;
|
||||
const ERROR_MULTI_RE = /(Error:|Exception:)/g;
|
||||
const WRITE_TOOLS = new Set(["Write", "Edit", "MultiEdit", "write", "edit", "multiEdit"]);
|
||||
|
||||
function resolveFilters(args: any, globalSearch: boolean, userId: string, appId: string): any {
|
||||
if (args.filters) {
|
||||
const existingFilters = args.filters;
|
||||
if (typeof existingFilters === "object" && existingFilters !== null) {
|
||||
const andClauses: any[] = existingFilters.AND;
|
||||
if (Array.isArray(andClauses)) {
|
||||
const hasUid = andClauses.some(
|
||||
(c: any) => c && typeof c === "object" && "user_id" in c,
|
||||
);
|
||||
const hasAid = andClauses.some(
|
||||
(c: any) => c && typeof c === "object" && "app_id" in c,
|
||||
);
|
||||
const hasAgentId = andClauses.some(
|
||||
(c: any) => c && typeof c === "object" && "agent_id" in c,
|
||||
);
|
||||
const newClauses = [...andClauses];
|
||||
if (args.agent_id || hasAgentId) {
|
||||
if (!hasAgentId) newClauses.push({ agent_id: args.agent_id });
|
||||
} else {
|
||||
if (!hasUid) newClauses.push({ user_id: args.user_id ?? userId });
|
||||
}
|
||||
if (!hasAid) newClauses.push({ app_id: args.app_id ?? appId });
|
||||
return { AND: newClauses };
|
||||
} else if (andClauses === undefined) {
|
||||
const hasUid = "user_id" in existingFilters;
|
||||
const hasAid = "app_id" in existingFilters;
|
||||
const hasAgentId = "agent_id" in existingFilters;
|
||||
if (!hasAid || (!hasUid && !hasAgentId)) {
|
||||
const existing = Object.entries(existingFilters).map(
|
||||
([k, v]) => ({ [k]: v }),
|
||||
);
|
||||
if (args.agent_id || hasAgentId) {
|
||||
if (!hasAgentId) existing.push({ agent_id: args.agent_id });
|
||||
} else {
|
||||
if (!hasUid) existing.push({ user_id: args.user_id ?? userId });
|
||||
}
|
||||
if (!hasAid) existing.push({ app_id: args.app_id ?? appId });
|
||||
return { AND: existing };
|
||||
}
|
||||
}
|
||||
}
|
||||
return args.filters;
|
||||
}
|
||||
|
||||
if (globalSearch) {
|
||||
return { OR: [{ user_id: "*" }] };
|
||||
}
|
||||
|
||||
if (args.agent_id) {
|
||||
return {
|
||||
AND: [{ agent_id: args.agent_id }, { app_id: args.app_id ?? appId }],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
AND: [{ user_id: args.user_id ?? userId }, { app_id: args.app_id ?? appId }],
|
||||
};
|
||||
}
|
||||
|
||||
function extractUserText(input: any, output: any): string {
|
||||
const parts: any[] = output?.parts;
|
||||
if (Array.isArray(parts)) {
|
||||
return parts
|
||||
.filter((p: any) => p.type === "text" && !p.synthetic)
|
||||
.map((p: any) => p.text ?? "")
|
||||
.join("\n");
|
||||
}
|
||||
const msg = output?.message ?? input?.message;
|
||||
if (typeof msg?.content === "string") return msg.content;
|
||||
if (typeof msg?.text === "string") return msg.text;
|
||||
return "";
|
||||
}
|
||||
|
||||
const Mem0Plugin: Plugin = async (ctx) => {
|
||||
const {$, client} = ctx;
|
||||
|
||||
const apiKey = process.env.MEM0_API_KEY;
|
||||
|
||||
if (!apiKey) {
|
||||
try {
|
||||
await client.app.log({
|
||||
body: {
|
||||
service: "mem0",
|
||||
level: "error",
|
||||
message:
|
||||
"MEM0_API_KEY environment variable not set. Get one at https://app.mem0.ai/dashboard/api-keys",
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
const mem0 = new MemoryClient({apiKey});
|
||||
const userId = await getUserId();
|
||||
const appId = await getProjectId($);
|
||||
const branch = await getBranch($);
|
||||
const stats = {adds: 0, searches: 0, messages: 0};
|
||||
const sessionId = generateSessionId();
|
||||
const globalSearch = loadGlobalSearch();
|
||||
|
||||
let initialized = false;
|
||||
let memoryCount = 0;
|
||||
let msgCount = 0;
|
||||
|
||||
const systemContext: string[] = [];
|
||||
|
||||
// Auto-dream: gated memory-consolidation state (ported from the pi-agent plugin).
|
||||
const mem0StateDir = join(homedir(), ".mem0");
|
||||
const dreamConfig = loadDreamConfig(mem0StateDir);
|
||||
let dreamTriggered = false;
|
||||
let dreamWriteSeen = false;
|
||||
|
||||
// Emit a session_stop telemetry event once when the process winds down.
|
||||
let sessionStopSent = false;
|
||||
const emitSessionStop = () => {
|
||||
if (sessionStopSent) return;
|
||||
sessionStopSent = true;
|
||||
captureEvent(
|
||||
"session_stop",
|
||||
{adds: stats.adds, searches: stats.searches, messages: stats.messages},
|
||||
apiKey,
|
||||
appId,
|
||||
);
|
||||
// Finish an in-flight auto-dream: record completion if the agent consolidated,
|
||||
// and always release the lock so the next eligible session can dream.
|
||||
if (dreamTriggered) {
|
||||
if (dreamWriteSeen) {
|
||||
recordDreamCompletion(mem0StateDir);
|
||||
captureEvent("dream_completed", {}, apiKey, appId);
|
||||
}
|
||||
releaseDreamLock(mem0StateDir);
|
||||
dreamTriggered = false;
|
||||
}
|
||||
};
|
||||
try {
|
||||
process.on("beforeExit", emitSessionStop);
|
||||
} catch {
|
||||
}
|
||||
|
||||
// Auto-configure coding categories in background (idempotent, never blocks)
|
||||
Promise.resolve().then(() => autoSetupCategories(mem0, apiKey)).catch(() => {
|
||||
});
|
||||
|
||||
// Register a `/mem0-<skill>` slash command per bundled skill. OpenCode's TUI
|
||||
// slash menu is populated from `config.command` entries (skills discovered via
|
||||
// `skills.paths` are available to the agent's skill tool but do NOT appear as
|
||||
// slash commands), so this is what makes `/mem0-scope` etc. typeable.
|
||||
function registerCommands(skillsDir: string, opencodeConfig: any) {
|
||||
for (const entry of readdirSync(skillsDir, {withFileTypes: true})) {
|
||||
if (!entry.isDirectory()) continue;
|
||||
const skillMd = resolve(skillsDir, entry.name, "SKILL.md");
|
||||
if (!existsSync(skillMd)) continue;
|
||||
|
||||
let desc = `Mem0 ${entry.name} skill`;
|
||||
try {
|
||||
const content = readFileSync(skillMd, "utf8");
|
||||
const m = content.match(/^description:\s*(.+)$/m);
|
||||
if (m) desc = m[1].trim();
|
||||
} catch {
|
||||
}
|
||||
|
||||
opencodeConfig.command ??= {};
|
||||
opencodeConfig.command[entry.name] = {
|
||||
template: `Load and execute the \`${entry.name}\` skill.
|
||||
|
||||
Use the mem0 memory tools (add_memory, search_memories, get_memories, get_memory, update_memory, delete_memory, delete_all_memories, delete_entities, list_entities, get_event_status) as instructed by the skill.
|
||||
|
||||
Identity context (resolved at plugin startup):
|
||||
- user_id: ${userId}
|
||||
- app_id: ${appId}
|
||||
- session_id: ${sessionId}
|
||||
- branch: ${branch}`,
|
||||
description: desc,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// Resolve read filters for the memory tools. Precedence: an explicit `scope`
|
||||
// arg wins; then explicit `filters`/`agent_id`; otherwise fall back to the
|
||||
// user's persisted default scope (read fresh so /mem0-scope applies at once).
|
||||
// A "project" default preserves the existing behavior, including global_search.
|
||||
function readScopeFilters(args: any): any {
|
||||
if (args.scope) return scopeSearchFilters(asScope(args.scope), userId, appId, sessionId);
|
||||
if (args.filters || args.agent_id) return resolveFilters(args, globalSearch, userId, appId);
|
||||
const ds = loadDefaultScope();
|
||||
return ds === "project"
|
||||
? resolveFilters(args, globalSearch, userId, appId)
|
||||
: scopeSearchFilters(ds, userId, appId, sessionId);
|
||||
}
|
||||
|
||||
return {
|
||||
"chat.message": chatMessageHook,
|
||||
"experimental.chat.messages.transform": chatMessagesTransformHook,
|
||||
"tool.execute.before": toolExecuteBeforeHook,
|
||||
"tool.execute.after": toolExecuteAfterHook,
|
||||
"experimental.session.compacting": compactionHook,
|
||||
|
||||
"shell.env": async (
|
||||
_input: { cwd: string; sessionID?: string },
|
||||
output: { env: Record<string, string> },
|
||||
) => {
|
||||
if (output?.env) {
|
||||
output.env.MEM0_USER_ID = userId;
|
||||
output.env.MEM0_APP_ID = appId;
|
||||
output.env.MEM0_SESSION_ID = sessionId;
|
||||
output.env.MEM0_BRANCH = branch;
|
||||
output.env.MEM0_GLOBAL_SEARCH = globalSearch ? "true" : "false";
|
||||
}
|
||||
},
|
||||
|
||||
config: async (opencodeConfig: any) => {
|
||||
// Point OpenCode at the plugin's OWN skills directory via `skills.paths`
|
||||
const here = import.meta.filename;
|
||||
const skillsDir = [
|
||||
resolve(dirname(dirname(here)), "opencode-skills"),
|
||||
resolve(dirname(here), "opencode-skills"),
|
||||
].find(existsSync);
|
||||
if (!skillsDir) return;
|
||||
|
||||
opencodeConfig.skills ??= {};
|
||||
opencodeConfig.skills.paths ??= [];
|
||||
if (!opencodeConfig.skills.paths.includes(skillsDir)) {
|
||||
opencodeConfig.skills.paths.push(skillsDir);
|
||||
}
|
||||
|
||||
// Register the /mem0-* slash commands (the TUI slash menu reads these from
|
||||
// config.command; skills.paths alone does not create slash commands).
|
||||
registerCommands(skillsDir, opencodeConfig);
|
||||
},
|
||||
|
||||
tool: {
|
||||
add_memory: tool({
|
||||
description: "Add a new memory. This method is called everytime the user informs anything about themselves, their preferences, or anything that has any relevant information which can be useful in the future conversation. This can also be called when the user asks you to remember something. Set infer to false to store the memory verbatim without LLM fact extraction.",
|
||||
args: {
|
||||
text: tool.schema.string().describe("Memory text content"),
|
||||
user_id: tool.schema.string().optional().describe("User ID"),
|
||||
app_id: tool.schema.string().optional().describe("App/Project ID"),
|
||||
agent_id: tool.schema.string().optional().describe("Agent ID"),
|
||||
metadata: tool.schema.record(tool.schema.string(), tool.schema.any()).optional().describe("Metadata key-value pairs"),
|
||||
infer: tool.schema.boolean().optional().describe("Set to false to store memory verbatim without LLM fact extraction"),
|
||||
scope: tool.schema.string().optional().describe('Write scope: "project" (this repo, default), "session" (this run), or "global" (user-wide, all projects). Use "global" only when explicitly asked.')
|
||||
},
|
||||
async execute(args) {
|
||||
stats.adds++;
|
||||
if (dreamTriggered) dreamWriteSeen = true;
|
||||
captureEvent("tool_use", {tool: "add_memory"}, apiKey, appId);
|
||||
const effScope: Scope = args.scope ? asScope(args.scope) : loadDefaultScope();
|
||||
const sp = scopeWriteParams(effScope, userId, appId, sessionId);
|
||||
const finalUserId = args.agent_id ? args.user_id : (args.user_id ?? sp.user_id);
|
||||
const finalAppId = args.app_id ?? sp.app_id;
|
||||
|
||||
const meta = args.metadata ?? {};
|
||||
if (meta.confidence === undefined) meta.confidence = 0.7;
|
||||
if (!meta.source) meta.source = "opencode";
|
||||
if (!meta.type) meta.type = "task_learning";
|
||||
if (!meta.session_id) meta.session_id = sessionId;
|
||||
if (!meta.files) meta.files = ["*"];
|
||||
if (!meta.branch) meta.branch = branch;
|
||||
|
||||
let infer = args.infer;
|
||||
if (meta.confidence >= 1.0 && infer === undefined) {
|
||||
infer = false;
|
||||
}
|
||||
|
||||
const res = await mem0.add(
|
||||
[{ role: "user", content: args.text }],
|
||||
{
|
||||
user_id: finalUserId,
|
||||
app_id: finalAppId,
|
||||
run_id: sp.run_id,
|
||||
agent_id: args.agent_id,
|
||||
metadata: meta,
|
||||
infer
|
||||
} as any
|
||||
);
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}),
|
||||
|
||||
search_memories: tool({
|
||||
description: "Search stored memories by semantic meaning. Use this proactively before answering when the request may depend on the user's past work, preferences, decisions, or environment -- relevant memories are not always auto-injected. For multi-part or comparative questions, run several searches with different phrasings and combine the results rather than stopping after one (multi-hop).",
|
||||
args: {
|
||||
query: tool.schema.string().describe("Search query"),
|
||||
user_id: tool.schema.string().optional().describe("User ID"),
|
||||
app_id: tool.schema.string().optional().describe("App/Project ID"),
|
||||
agent_id: tool.schema.string().optional().describe("Agent ID"),
|
||||
filters: tool.schema.record(tool.schema.string(), tool.schema.any()).optional().describe("Key-value filters (e.g. metadata or user/app filters)"),
|
||||
limit: tool.schema.number().optional().describe("Maximum number of results to return (top_k)"),
|
||||
top_k: tool.schema.number().optional().describe("Maximum number of results to return (alternative parameter)"),
|
||||
scope: tool.schema.string().optional().describe('Search scope: "project" (this repo, default), "session" (this run only), or "global" (across ALL your projects). Only use "global" when the user explicitly asks to search across projects.'),
|
||||
},
|
||||
async execute(args) {
|
||||
stats.searches++;
|
||||
captureEvent("tool_use", {tool: "search_memories"}, apiKey, appId);
|
||||
const topK = args.limit ?? args.top_k ?? 10;
|
||||
const filters = readScopeFilters(args);
|
||||
|
||||
const res = await mem0.search(args.query, {
|
||||
filters,
|
||||
topK,
|
||||
});
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}),
|
||||
|
||||
get_memories: tool({
|
||||
description: "List or browse stored memories without a search query -- useful for auditing what is remembered or paging through everything in a scope. To find memories relevant to a question, use search_memories instead (it ranks by semantic relevance).",
|
||||
args: {
|
||||
user_id: tool.schema.string().optional().describe("User ID"),
|
||||
app_id: tool.schema.string().optional().describe("App/Project ID"),
|
||||
agent_id: tool.schema.string().optional().describe("Agent ID"),
|
||||
filters: tool.schema.record(tool.schema.string(), tool.schema.any()).optional().describe("Metadata/identity filters"),
|
||||
page: tool.schema.number().optional().describe("Page number"),
|
||||
page_size: tool.schema.number().optional().describe("Page size"),
|
||||
scope: tool.schema.string().optional().describe('Scope: "project" (default), "session", or "global" (across ALL your projects). Use "global" only when explicitly asked.'),
|
||||
},
|
||||
async execute(args) {
|
||||
captureEvent("tool_use", {tool: "get_memories"}, apiKey, appId);
|
||||
const filters = readScopeFilters(args);
|
||||
|
||||
const res = await mem0.getAll({
|
||||
page: args.page,
|
||||
pageSize: args.page_size,
|
||||
filters,
|
||||
});
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}),
|
||||
|
||||
get_memory: tool({
|
||||
description: "Fetch one memory by its exact ID (e.g. an ID returned by search_memories or get_memories) to read its full content and metadata.",
|
||||
args: {
|
||||
id: tool.schema.string().describe("The ID of the memory to retrieve"),
|
||||
},
|
||||
async execute(args) {
|
||||
captureEvent("tool_use", {tool: "get_memory"}, apiKey, appId);
|
||||
const res = await mem0.get(args.id);
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}),
|
||||
|
||||
update_memory: tool({
|
||||
description: "Update an existing memory in place when a stored fact has changed -- requires the memory ID. Preserves the ID and history, so prefer this over deleting and re-adding.",
|
||||
args: {
|
||||
id: tool.schema.string().describe("The ID of the memory to update"),
|
||||
text: tool.schema.string().optional().describe("New text content for the memory"),
|
||||
metadata: tool.schema.record(tool.schema.string(), tool.schema.any()).optional().describe("New metadata key-value pairs"),
|
||||
},
|
||||
async execute(args) {
|
||||
captureEvent("tool_use", {tool: "update_memory"}, apiKey, appId);
|
||||
const res = await mem0.update(args.id, {
|
||||
text: args.text,
|
||||
metadata: args.metadata,
|
||||
});
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}),
|
||||
|
||||
delete_memory: tool({
|
||||
description: "Delete one or more memories by ID when they are wrong, obsolete, or the user asks to forget them. Irreversible -- only delete what is clearly no longer wanted.",
|
||||
args: {
|
||||
id: tool.schema.string().describe("The ID of the memory to delete"),
|
||||
},
|
||||
async execute(args) {
|
||||
if (dreamTriggered) dreamWriteSeen = true;
|
||||
captureEvent("tool_use", {tool: "delete_memory"}, apiKey, appId);
|
||||
const res = await mem0.delete(args.id);
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}),
|
||||
|
||||
delete_all_memories: tool({
|
||||
description: "Delete ALL memories in the given scope. Destructive and irreversible -- only use when the user explicitly asks to wipe their memory. Never call speculatively.",
|
||||
args: {
|
||||
user_id: tool.schema.string().optional().describe("User ID whose memories to delete"),
|
||||
app_id: tool.schema.string().optional().describe("App ID whose memories to delete"),
|
||||
agent_id: tool.schema.string().optional().describe("Agent ID whose memories to delete"),
|
||||
scope: tool.schema.string().optional().describe('Scope to delete: "project" (default), "session", or "global" (user-wide). Use "global" only when explicitly asked.'),
|
||||
},
|
||||
async execute(args) {
|
||||
if (dreamTriggered) dreamWriteSeen = true;
|
||||
captureEvent("tool_use", {tool: "delete_all_memories"}, apiKey, appId);
|
||||
const sp = args.scope ? scopeWriteParams(asScope(args.scope), userId, appId, sessionId) : null;
|
||||
const res = await mem0.deleteAll({
|
||||
user_id: sp ? sp.user_id : (args.agent_id ? args.user_id : (args.user_id ?? userId)),
|
||||
app_id: sp ? sp.app_id : (args.app_id ?? appId),
|
||||
run_id: sp?.run_id,
|
||||
agent_id: args.agent_id,
|
||||
} as any);
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}),
|
||||
|
||||
delete_entities: tool({
|
||||
description: "Delete entire user/agent/app/run entities and every memory attached to them. Destructive and irreversible -- only on explicit user request to remove a whole user, agent, or project.",
|
||||
args: {
|
||||
user_id: tool.schema.string().optional().describe("User ID of the entity to delete"),
|
||||
agent_id: tool.schema.string().optional().describe("Agent ID of the entity to delete"),
|
||||
app_id: tool.schema.string().optional().describe("App/Project ID of the entity to delete"),
|
||||
run_id: tool.schema.string().optional().describe("Run ID of the entity to delete"),
|
||||
},
|
||||
async execute(args) {
|
||||
captureEvent("tool_use", {tool: "delete_entities"}, apiKey, appId);
|
||||
const res = await mem0.deleteUsers({
|
||||
userId: args.user_id,
|
||||
agentId: args.agent_id,
|
||||
appId: args.app_id,
|
||||
runId: args.run_id,
|
||||
});
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}),
|
||||
|
||||
list_entities: tool({
|
||||
description: "List the user/agent/app/run entities that have memories. Use to discover which scopes exist before searching, listing, or deleting within a specific one.",
|
||||
args: {
|
||||
page: tool.schema.number().optional().describe("Page number"),
|
||||
page_size: tool.schema.number().optional().describe("Page size"),
|
||||
},
|
||||
async execute(args) {
|
||||
captureEvent("tool_use", {tool: "list_entities"}, apiKey, appId);
|
||||
const res = await mem0.users({
|
||||
page: args.page,
|
||||
pageSize: args.page_size,
|
||||
});
|
||||
return JSON.stringify(res);
|
||||
}
|
||||
}),
|
||||
|
||||
get_event_status: tool({
|
||||
description: "Check whether an asynchronous memory write (add/update/delete) finished, using the event_id that call returned. Poll this when you need to confirm a write was persisted before relying on it.",
|
||||
args: {
|
||||
event_id: tool.schema.string().describe("The ID of the event/async operation to check"),
|
||||
},
|
||||
async execute(args) {
|
||||
captureEvent("tool_use", {tool: "get_event_status"}, apiKey, appId);
|
||||
const response = await mem0.client.get(`/v1/event/${args.event_id}/`);
|
||||
return JSON.stringify(response.data);
|
||||
}
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
async function chatMessageHook(input: any, output: any) {
|
||||
const userText = extractUserText(input, output);
|
||||
if (!userText || userText.length < 10) return;
|
||||
|
||||
const safeText = redact(userText);
|
||||
msgCount++;
|
||||
stats.messages++;
|
||||
|
||||
if (!initialized) {
|
||||
initialized = true;
|
||||
|
||||
if (dreamConfig.enabled) {
|
||||
incrementSessionCount(mem0StateDir, sessionId);
|
||||
}
|
||||
|
||||
const searchFilters = globalSearch
|
||||
? {OR: [{user_id: "*"}]}
|
||||
: {AND: [{user_id: userId}, {app_id: appId}]};
|
||||
|
||||
try {
|
||||
const all = await mem0.getAll({
|
||||
filters: searchFilters,
|
||||
page: 1,
|
||||
pageSize: 1,
|
||||
});
|
||||
const a: any = all;
|
||||
memoryCount =
|
||||
typeof a?.count === "number"
|
||||
? a.count
|
||||
: Array.isArray(a)
|
||||
? a.length
|
||||
: Array.isArray(a?.results)
|
||||
? a.results.length
|
||||
: 0;
|
||||
|
||||
if (globalSearch) {
|
||||
systemContext.push(
|
||||
`Global search is ON — searches return all memories across all users and projects. Writes still use user_id="${userId}", app_id="${appId}".`,
|
||||
);
|
||||
} else {
|
||||
systemContext.push(
|
||||
`Always include user_id="${userId}" and app_id="${appId}" in every search_memories filter and add_memory call.`,
|
||||
);
|
||||
}
|
||||
|
||||
if (memoryCount === 0) {
|
||||
systemContext.push(
|
||||
"New project with 0 memories. Capture decisions, conventions, and learnings as you work via the add_memory tool or the remember skill.",
|
||||
);
|
||||
}
|
||||
|
||||
if (memoryCount > 0) {
|
||||
systemContext.push(
|
||||
"Search mem0 for recent decisions and task learnings before responding. Run 2 parallel searches: one for decision type, one for task_learning type.",
|
||||
);
|
||||
try {
|
||||
const res = await mem0.search(
|
||||
"recent session state decisions and learnings",
|
||||
{
|
||||
filters: searchFilters,
|
||||
topK: 5,
|
||||
},
|
||||
);
|
||||
stats.searches++;
|
||||
const memories = extractMemories(res);
|
||||
if (memories.length > 0) {
|
||||
const memLines = memories
|
||||
.map((m) => `- ${m.memory}`)
|
||||
.join("\n");
|
||||
systemContext.push(`Prior context from mem0:\n${memLines}`);
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
systemContext.push(
|
||||
"Mem0 searches apply when user references past work, decision questions, errors, or non-trivial tasks. Queries use noun-phrases, 2-4 parallel calls with different metadata.type filters, and include user_id + app_id.",
|
||||
);
|
||||
systemContext.push(SCOPE_GUIDANCE);
|
||||
const activeScope = loadDefaultScope();
|
||||
if (activeScope !== "project") {
|
||||
systemContext.push(
|
||||
`Active default memory scope is "${activeScope}" (set via /mem0-scope). Memory tools use this when no explicit scope is given: "session" limits to this run (run_id="${sessionId}"); "global" spans all your projects (app_id="*"). Pass an explicit scope to override per call. delete_all_memories still requires an explicit scope="global" to delete user-wide.`,
|
||||
);
|
||||
}
|
||||
} catch (err: any) {
|
||||
try {
|
||||
await client.app.log({
|
||||
body: {
|
||||
service: "mem0",
|
||||
level: "error",
|
||||
message: `Session init error: ${err?.message}`,
|
||||
},
|
||||
});
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
captureEvent("session_start", {memory_count: memoryCount}, apiKey, appId);
|
||||
|
||||
// Auto-dream: when the time/session/memory gates pass, inject the
|
||||
// consolidation protocol so the agent tidies memories before answering.
|
||||
if (dreamConfig.enabled && dreamConfig.auto && !dreamTriggered) {
|
||||
const gates = checkCheapGates(mem0StateDir, dreamConfig);
|
||||
const memGate = checkMemoryGate(memoryCount, dreamConfig);
|
||||
if (gates.proceed && memGate.pass && acquireDreamLock(mem0StateDir)) {
|
||||
dreamTriggered = true;
|
||||
systemContext.push(DREAM_PROTOCOL);
|
||||
captureEvent("dream_triggered", {memory_count: memoryCount}, apiKey, appId);
|
||||
} else {
|
||||
// Make "why didn't auto-dream run?" answerable from the logs.
|
||||
const waiting = [gates.reason, memGate.reason].filter(Boolean).join("; ");
|
||||
if (waiting) {
|
||||
try {
|
||||
await client.app.log({
|
||||
body: {service: "mem0", level: "info", message: `auto-dream waiting — ${waiting}`},
|
||||
});
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const hasRemember = NUDGE_RE.test(safeText);
|
||||
if (hasRemember) {
|
||||
systemContext.push(
|
||||
"[MEMORY TRIGGER] User asked to remember something. Call add_memory with the user's statement, confidence=1.0, infer=false.",
|
||||
);
|
||||
}
|
||||
|
||||
const hasResume = RESUME_RE.test(safeText);
|
||||
if (hasResume) {
|
||||
try {
|
||||
const resumeFilters = globalSearch
|
||||
? {OR: [{user_id: "*"}]}
|
||||
: {
|
||||
AND: [
|
||||
{user_id: userId},
|
||||
{app_id: appId},
|
||||
],
|
||||
};
|
||||
const [stateRes, decisionsRes] = await Promise.all([
|
||||
mem0.search("session state current task", {
|
||||
filters: resumeFilters,
|
||||
topK: 3,
|
||||
}),
|
||||
mem0.search("recent decisions and learnings", {
|
||||
filters: resumeFilters,
|
||||
topK: 3,
|
||||
}),
|
||||
]);
|
||||
stats.searches += 2;
|
||||
const all = [
|
||||
...extractMemories(stateRes),
|
||||
...extractMemories(decisionsRes),
|
||||
];
|
||||
const seen = new Set<string>();
|
||||
const unique = all.filter((m) => {
|
||||
if (seen.has(m.id)) return false;
|
||||
seen.add(m.id);
|
||||
return true;
|
||||
});
|
||||
if (unique.length > 0) {
|
||||
const memLines = unique.map((m) => `- ${m.memory}`).join("\n");
|
||||
systemContext.push(
|
||||
`Session resume context:\n${memLines}\n\nThese memories provide context for resuming work.`,
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasResume && memoryCount > 0) {
|
||||
try {
|
||||
const msgFilters = globalSearch
|
||||
? {OR: [{user_id: "*"}]}
|
||||
: {AND: [{user_id: userId}, {app_id: appId}]};
|
||||
const res = await mem0.search(safeText, {
|
||||
filters: msgFilters,
|
||||
topK: 5,
|
||||
});
|
||||
stats.searches++;
|
||||
const memories = extractMemories(res);
|
||||
if (memories.length > 0) {
|
||||
const memLines = memories.map((m) => `- ${m.memory}`).join("\n");
|
||||
systemContext.push(`Relevant memories:\n${memLines}`);
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
|
||||
if (msgCount % 3 === 0) {
|
||||
Promise.resolve().then(async () => {
|
||||
try {
|
||||
await mem0.add([{role: "user", content: safeText}], {
|
||||
user_id: userId,
|
||||
app_id: appId,
|
||||
metadata: {
|
||||
type: "auto_capture",
|
||||
source: "opencode",
|
||||
confidence: 0.7,
|
||||
session_id: sessionId,
|
||||
branch,
|
||||
},
|
||||
infer: true,
|
||||
} as any);
|
||||
stats.adds++;
|
||||
} catch {
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (msgCount % 5 === 0 && stats.adds < Math.floor(msgCount / 3)) {
|
||||
systemContext.push(
|
||||
"After responding, store any new decisions, learnings, or preferences from this exchange via add_memory. Keep it to 1 sentence per memory.",
|
||||
);
|
||||
}
|
||||
|
||||
captureEvent(
|
||||
"user_prompt",
|
||||
{remember_detected: hasRemember, resume_detected: hasResume},
|
||||
apiKey,
|
||||
appId,
|
||||
);
|
||||
}
|
||||
|
||||
async function toolExecuteBeforeHook(input: any, output: any) {
|
||||
const toolName: string = input?.tool ?? "";
|
||||
|
||||
if (WRITE_TOOLS.has(toolName)) {
|
||||
const fp = String(
|
||||
output?.args?.file_path ?? output?.args?.filePath ?? "",
|
||||
);
|
||||
if (/MEMORY\.md|\.claude\/memory/i.test(fp)) {
|
||||
throw new Error(
|
||||
"Use the add_memory tool instead of writing to MEMORY.md",
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function chatMessagesTransformHook(_input: any, output: { messages: { info: any; parts: any[] }[] }) {
|
||||
if (systemContext.length === 0 || !output?.messages?.length) return;
|
||||
|
||||
const firstUser = output.messages.find(
|
||||
(m) => m.info.role === "user",
|
||||
);
|
||||
if (!firstUser || !firstUser.parts.length) return;
|
||||
|
||||
const marker = "## Mem0 Memory Context";
|
||||
if (firstUser.parts.some((p: any) => p.type === "text" && p.text?.includes(marker))) return;
|
||||
|
||||
const block = `${marker}\n\n${systemContext.join("\n\n")}`;
|
||||
const ref = firstUser.parts[0];
|
||||
firstUser.parts.unshift({...ref, type: "text", text: block});
|
||||
}
|
||||
|
||||
async function toolExecuteAfterHook(input: any, _output: any) {
|
||||
const toolName: string = input?.tool ?? "";
|
||||
const toolOutput: string = input?.output ?? _output?.output ?? "";
|
||||
|
||||
if (toolName === "bash" && toolOutput.length >= 50) {
|
||||
const command: string = input?.args?.command ?? "";
|
||||
if (/git\s+(commit|merge|rebase)/.test(command)) return;
|
||||
|
||||
const hasStrongError = ERROR_STRONG_RE.test(toolOutput);
|
||||
const multiErrors = (toolOutput.match(ERROR_MULTI_RE) ?? []).length;
|
||||
if (!hasStrongError && multiErrors < 2) return;
|
||||
|
||||
try {
|
||||
const errorLine =
|
||||
toolOutput
|
||||
.split("\n")
|
||||
.find((l: string) =>
|
||||
/Error:|Exception:|panic:|FAIL:|fatal:/i.test(l),
|
||||
)
|
||||
?.replace(/^\s+/, "")
|
||||
.slice(0, 120) ?? "";
|
||||
|
||||
const traceFiles = [
|
||||
...new Set(
|
||||
toolOutput.match(
|
||||
/[a-zA-Z0-9_./-]+\.(py|ts|tsx|js|jsx|rs|go|rb|java|sh)(:\d+)?/g,
|
||||
) ?? [],
|
||||
),
|
||||
].slice(0, 5);
|
||||
|
||||
const errorQuery = errorLine.slice(0, 80);
|
||||
if (errorQuery.length < 10) return;
|
||||
|
||||
captureEvent("bash_error", {error_detected: true}, apiKey, appId);
|
||||
|
||||
const errorFilters = globalSearch
|
||||
? {OR: [{user_id: "*"}]}
|
||||
: {
|
||||
AND: [
|
||||
{user_id: userId},
|
||||
{app_id: appId},
|
||||
],
|
||||
};
|
||||
const res = await mem0.search(`error: ${errorQuery}`, {
|
||||
filters: errorFilters,
|
||||
topK: 6,
|
||||
});
|
||||
stats.searches++;
|
||||
const unique = extractMemories(res);
|
||||
|
||||
let ctx = `Error detected: \`${command.slice(0, 100)}\` produced:\n> ${errorLine}`;
|
||||
if (traceFiles.length > 0) {
|
||||
ctx += `\nFiles in stack trace: ${traceFiles.join(", ")}`;
|
||||
}
|
||||
if (unique.length > 0) {
|
||||
const lines = unique.map((m) => `- ${m.memory}`).join("\n");
|
||||
ctx += `\nPrior error memories:\n${lines}`;
|
||||
}
|
||||
ctx +=
|
||||
"\nStore resolved errors as anti_pattern or bug_fix memories for future reference.";
|
||||
systemContext.push(ctx);
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function compactionHook(input: { sessionID?: string }, output: { context: string[]; prompt?: string }) {
|
||||
try {
|
||||
const compactSessionId = input?.sessionID ?? sessionId;
|
||||
captureEvent(
|
||||
"pre_compact",
|
||||
{adds: stats.adds, searches: stats.searches, messages: stats.messages},
|
||||
apiKey,
|
||||
appId,
|
||||
);
|
||||
const summaryContent = `Session compacting. Project: ${appId}. Branch: ${branch}. Session: ${compactSessionId}. Stats: ${stats.adds} memories stored, ${stats.searches} searches, ${stats.messages} messages.`;
|
||||
Promise.resolve().then(async () => {
|
||||
try {
|
||||
await mem0.add([{role: "user", content: summaryContent}], {
|
||||
user_id: userId,
|
||||
app_id: appId,
|
||||
metadata: {
|
||||
type: "session_state",
|
||||
source: "pre-compaction",
|
||||
session_id: compactSessionId,
|
||||
branch,
|
||||
},
|
||||
infer: true,
|
||||
} as any);
|
||||
} catch {
|
||||
}
|
||||
});
|
||||
|
||||
const compactFilters = globalSearch
|
||||
? {OR: [{user_id: "*"}]}
|
||||
: {AND: [{user_id: userId}, {app_id: appId}]};
|
||||
const res = await mem0.search("session state decisions learnings", {
|
||||
filters: compactFilters,
|
||||
topK: 10,
|
||||
});
|
||||
const memories = extractMemories(res);
|
||||
if (memories.length > 0 && output?.context) {
|
||||
const lines = memories.map((m) => `- ${m.memory}`).join("\n");
|
||||
output.context.push(
|
||||
`## Mem0 Memories (preserve across compaction)\n\n${lines}\n\nIMPORTANT: After compaction, store any key decisions or learnings using the add_memory tool.`,
|
||||
);
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
export default Mem0Plugin;
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
---
|
||||
name: mem0-context-loader
|
||||
description: Searches and injects relevant memories into context before starting work on a task. Use when beginning a new task, switching context, or when project history, past decisions, or coding conventions need to be loaded.
|
||||
---
|
||||
|
||||
# Context Loader
|
||||
|
||||
Pre-fetches relevant memories to prime context before working on a task.
|
||||
|
||||
## When to use
|
||||
|
||||
- Session start (invoke manually or auto-triggered by skill description matching)
|
||||
- User starts work on a specific feature or file set
|
||||
- Complex multi-step task begins
|
||||
- User says "what do we know about X" or "context for X"
|
||||
|
||||
## Steps
|
||||
|
||||
1. **Extract topics** from current message/task. Identify: file paths, module names, feature areas, error patterns.
|
||||
|
||||
2. **Run 2-4 parallel `search_memories` calls** with different angles:
|
||||
|
||||
| Query angle | Filter | Purpose |
|
||||
|---|---|---|
|
||||
| Feature/module name | `{"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}, {"metadata": {"type": "decision"}}]}` | Architecture decisions |
|
||||
| File paths mentioned | `{"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}, {"metadata": {"type": "convention"}}]}` | Coding patterns |
|
||||
| Error keywords (if any) | `{"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}, {"metadata": {"type": "anti_pattern"}}]}` | Known pitfalls |
|
||||
| Broad project context | `{"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}]}` | Catch-all |
|
||||
|
||||
3. **Deduplicate** results by memory ID across all search responses.
|
||||
|
||||
4. **Output compact context block** (max 10 memories):
|
||||
|
||||
```
|
||||
context-loader: loaded <N> memories for "<task summary>"
|
||||
- [decision] <content> [mem0:<short_id>]
|
||||
- [convention] <content> [mem0:<short_id>]
|
||||
- [anti_pattern] <content> [mem0:<short_id>]
|
||||
```
|
||||
|
||||
5. If **zero results**: output nothing. Don't announce empty context.
|
||||
|
||||
## Constraints
|
||||
|
||||
- **Read-only** — never modify or delete memories
|
||||
- **Max 10 memories** returned (most relevant only)
|
||||
- **Silent on empty** — only surfaces findings if relevant context exists
|
||||
- Skip memories already visible in current session context
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -0,0 +1,237 @@
|
||||
---
|
||||
name: mem0-dream
|
||||
description: Consolidates stored memories by merging duplicates, resolving contradictions, and pruning stale entries. Use when memory count is high, search results feel noisy or repetitive, or periodic cleanup is needed to maintain memory quality.
|
||||
---
|
||||
|
||||
# Mem0 Dream — Memory Consolidation
|
||||
|
||||
This skill performs a memory consolidation pass: it fetches all project memories,
|
||||
identifies near-duplicates, flags contradictions, and prunes stale entries based on
|
||||
configured retention policies. All proposed changes are shown as a diff for user
|
||||
approval before anything is modified.
|
||||
|
||||
**IMPORTANT: Execute steps strictly in order (1 → 2 → 3 → 4 → 5 → 6). Each step depends on the previous one. Do NOT run steps in parallel or skip ahead.**
|
||||
|
||||
## Step 1: Load Retention Policies
|
||||
|
||||
Check for a project config file in the project root (current working directory):
|
||||
|
||||
1. Look for `.mem0.json` first. If it exists, parse it as JSON and read the
|
||||
`retention` field (a dict of `category → days | null`).
|
||||
2. If `.mem0.json` is not present, look for `.mem0.md`. If it exists, scan it
|
||||
for a `retention:` section or YAML front matter with retention settings and
|
||||
parse what you find.
|
||||
3. If neither file exists, skip config loading entirely.
|
||||
|
||||
If no config is found or the config contains no retention settings, fall back to
|
||||
these built-in defaults:
|
||||
|
||||
| `metadata.type` | Default retention |
|
||||
|---|---|
|
||||
| `session_state` | 90 days |
|
||||
| `compact_summary` | 90 days |
|
||||
| all others | no pruning |
|
||||
|
||||
Store the resolved policies for use in Step 3.
|
||||
|
||||
---
|
||||
|
||||
## Step 2: Fetch ALL Project Memories
|
||||
|
||||
Call `get_memories` to retrieve every memory for the active project:
|
||||
|
||||
```python
|
||||
get_memories(
|
||||
filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]},
|
||||
page_size=200,
|
||||
)
|
||||
```
|
||||
|
||||
If the response indicates more pages exist, paginate until all memories are fetched.
|
||||
Collect the full list before proceeding. If zero memories are found, print:
|
||||
|
||||
```
|
||||
No memories found for project <project_id>. Nothing to consolidate.
|
||||
```
|
||||
|
||||
…and stop.
|
||||
|
||||
---
|
||||
|
||||
## Step 3: Analyze — Find Issues
|
||||
|
||||
Work entirely in-memory; do not modify anything yet.
|
||||
|
||||
Group memories by `metadata.type` (use `"unknown"` when the field is absent).
|
||||
For each group, identify the following:
|
||||
|
||||
### 3a. Near-duplicate pairs (merge candidates)
|
||||
|
||||
Two memories are near-duplicates when they express the same fact or decision but
|
||||
phrased differently (e.g., "Use PostgreSQL for auth" and "Auth DB is PostgreSQL").
|
||||
|
||||
Heuristics — two memories are near-duplicates if **all** of these hold:
|
||||
- Similarity threshold: estimated cosine similarity > 0.9 (use noun/keyword overlap as proxy — if >60% of significant nouns overlap, treat as >0.9 similarity).
|
||||
- Same `metadata.type`.
|
||||
- Neither memory is pinned (`metadata.pinned != true`).
|
||||
|
||||
For each qualifying pair, draft a merged version that is more complete and specific
|
||||
than either original.
|
||||
|
||||
### 3b. Contradictions
|
||||
|
||||
Two memories contradict when they assert opposing facts about the same topic
|
||||
(e.g., "Deploy to ECS" vs. "Deploy to Vercel").
|
||||
|
||||
Identify the likely winner: the more recent memory with higher confidence wins.
|
||||
Store both IDs and their content for user review.
|
||||
|
||||
### 3c. Prune candidates
|
||||
|
||||
A memory is a prune candidate when **any** of the following is true:
|
||||
|
||||
1. Its `metadata.type` has a retention policy and the memory is older than the
|
||||
configured number of days (compare `created_at` to today).
|
||||
2. Its confidence score is below 0.3 AND it contains no information unique to
|
||||
this project (no file paths, identifiers, or domain-specific nouns).
|
||||
|
||||
**Always skip memories where `metadata.pinned == true`**, regardless of age or
|
||||
confidence.
|
||||
|
||||
---
|
||||
|
||||
## Step 4: Print Diff Report
|
||||
|
||||
Print a structured diff to the terminal before making any changes. Use exactly
|
||||
this format:
|
||||
|
||||
```
|
||||
## dream — consolidation report
|
||||
|
||||
Merges (<N>):
|
||||
[mem0:<id1>] + [mem0:<id2>] → "<merged content, 100 chars>"
|
||||
|
||||
Conflicts (<N>):
|
||||
[mem0:<idA>] vs [mem0:<idB>] — "<topic>" [A/B/skip]
|
||||
|
||||
Prune (<N>):
|
||||
[mem0:<id>] — <type>, <age>d old
|
||||
|
||||
Proposed: <N> merges, <N> prunes, <N> conflicts. Apply? [Y/n]
|
||||
```
|
||||
|
||||
If there are zero items in any category, omit that section entirely.
|
||||
|
||||
If there are zero total proposals (no merges, no prunes, no conflicts), print:
|
||||
|
||||
```
|
||||
Dream complete. No duplicate, contradictory, or stale memories found.
|
||||
```
|
||||
|
||||
…and stop.
|
||||
|
||||
---
|
||||
|
||||
## Step 5: Wait for User Input and Apply
|
||||
|
||||
### 5a. Contradictions
|
||||
|
||||
For each `CONFLICT` pair in the report, wait for the user to type `A`, `B`, or
|
||||
`skip` (case-insensitive). If they enter nothing (empty), treat as `skip`.
|
||||
|
||||
Record the winner for each pair before proceeding to the final apply confirmation.
|
||||
|
||||
### 5b. Final confirmation
|
||||
|
||||
After all conflict resolutions are collected, prompt:
|
||||
|
||||
```
|
||||
Apply? [Y/n]
|
||||
```
|
||||
|
||||
If the user types `n` or `no` (case-insensitive), print `Cancelled. No changes made.`
|
||||
and stop.
|
||||
|
||||
If the user confirms (`Y`, `yes`, or empty / Enter), apply all changes in this order:
|
||||
|
||||
#### Merges
|
||||
|
||||
For each approved merge pair:
|
||||
1. `delete_memory(<id1>)`
|
||||
2. `delete_memory(<id2>)`
|
||||
3. `add_memory` with:
|
||||
- `text="<merged content>"`
|
||||
- `user_id=<active_user_id>`
|
||||
- `app_id=<active_project_id>` (top-level, not in metadata)
|
||||
- `metadata={"type": "<original type>", "branch": "<active_branch>", "confidence": <higher of the two original scores>, "source": "mem0-dream"}`
|
||||
- `infer=False`
|
||||
|
||||
#### Contradictions (resolved)
|
||||
|
||||
For each resolved conflict where the user chose A or B:
|
||||
- Delete the loser (the non-chosen memory): `delete_memory(memory_id=<loser_id>)`
|
||||
|
||||
Contradictions where the user chose `skip` are left untouched.
|
||||
|
||||
#### Prunes
|
||||
|
||||
For each prune candidate:
|
||||
- `delete_memory(<memory_id>)`
|
||||
|
||||
---
|
||||
|
||||
## Step 6: Print Summary
|
||||
|
||||
After all changes are applied, print:
|
||||
|
||||
```
|
||||
Dream complete — merged: <N>, pruned: <N>, conflicts resolved: <N>, skipped: <N>
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Auto mode
|
||||
|
||||
When invoked with `--auto` (e.g., `/mem0-dream --auto`), run non-interactively:
|
||||
|
||||
- **Merges**: applied automatically (no contradiction, both are compatible).
|
||||
- **Prunes**: applied automatically (age/confidence-based, no ambiguity).
|
||||
- **Contradictions**: skipped — they require human judgment.
|
||||
|
||||
### Concurrency guard
|
||||
|
||||
Before doing any work, check for a lock file at `/tmp/mem0_dream_auto.lock`:
|
||||
- If the lock file exists and is less than 10 minutes old, print `[mem0-dream --auto] Another run in progress — skipping.` and stop.
|
||||
- Otherwise, create the lock file (write the current timestamp). Delete it when done (in all exit paths).
|
||||
|
||||
### Execution
|
||||
|
||||
In auto mode:
|
||||
1. Load policies and fetch memories (Steps 1–3) as normal.
|
||||
2. Apply merges and prunes silently without printing the diff or prompting.
|
||||
3. Print a compact summary:
|
||||
```
|
||||
[mem0-dream --auto] project=<id> merged=<N> pruned=<N> conflicts_skipped=<N>
|
||||
```
|
||||
4. If contradictions were detected but skipped, check if a `mem0-dream-auto` reminder already exists before storing one:
|
||||
- Search for existing reminders: `search_memories(query="mem0-dream contradictions manual review", filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}, {"metadata": {"source": "mem0-dream-auto"}}]}, top_k=1)`
|
||||
- If a result exists with similarity > 0.9, skip storing the reminder (one already exists).
|
||||
- If no match, store the reminder:
|
||||
```python
|
||||
add_memory(
|
||||
text="mem0-dream detected <N> contradiction(s) requiring manual review. Run /mem0-dream to resolve them interactively.",
|
||||
user_id="<active_user_id>",
|
||||
app_id="<active_project_id>",
|
||||
metadata={"type": "task_learning", "source": "mem0-dream-auto", "branch": "<active_branch>"},
|
||||
infer=False,
|
||||
)
|
||||
```
|
||||
|
||||
## See also
|
||||
|
||||
- `/mem0-forget` — targeted deletion of specific memories (search + confirm + delete)
|
||||
- `/mem0-status --deep` — quick quality scan without applying changes
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -0,0 +1,74 @@
|
||||
---
|
||||
name: mem0-forget
|
||||
description: Deletes memories by search query or memory ID with confirmation before removal. Use when removing outdated decisions, incorrect memories, sensitive data, or cleaning up after experiments. Also handles undo of recent additions.
|
||||
---
|
||||
|
||||
# Mem0 Forget
|
||||
|
||||
Delete specific memories from mem0.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Parse input
|
||||
|
||||
The user provides either:
|
||||
- A search query: `/mem0-forget auth module decisions`
|
||||
- A memory ID: `/mem0-forget <memory_id>`
|
||||
|
||||
If no argument, ask: "What should I forget? Provide a search query or memory ID."
|
||||
|
||||
### Step 2: Find memories
|
||||
|
||||
**If memory ID provided** (looks like a UUID or hex string):
|
||||
- Call `get_memory` with the ID to verify it exists.
|
||||
- Show: `Found: "<memory content first 120 chars>" (created <date>)`
|
||||
|
||||
**If search query provided:**
|
||||
- Call `search_memories` with:
|
||||
- `query=<user's query>`
|
||||
- `filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<project_id>"}]}`
|
||||
- `top_k=10`
|
||||
- Show numbered list:
|
||||
```
|
||||
Found <N> memories matching "<query>":
|
||||
1. <content, 120 chars> (type: <type>, created: <date>) [ID: <short_id>]
|
||||
2. ...
|
||||
```
|
||||
|
||||
### Step 3: Confirm
|
||||
|
||||
Ask: "Delete which memories? Enter numbers (e.g., 1,3,5), 'all', or 'cancel'."
|
||||
|
||||
For a single memory ID, ask: "Delete this memory? [y/N]"
|
||||
|
||||
**Never delete without confirmation.** This is destructive.
|
||||
|
||||
### Step 4: Delete
|
||||
|
||||
For each confirmed memory, call `delete_memory` with the memory ID.
|
||||
|
||||
### Step 5: Report
|
||||
|
||||
```
|
||||
Deleted <N> memories.
|
||||
```
|
||||
|
||||
If any deletions failed, report which ones and why.
|
||||
|
||||
## Undo recent writes
|
||||
|
||||
If the user says "undo last N memories" or "undo last write":
|
||||
|
||||
1. Check the `MEM0_SESSION_ID` environment variable (set by the plugin's shell.env hook).
|
||||
2. If `MEM0_SESSION_ID` is set, call `search_memories` with:
|
||||
- `query="recently added"`
|
||||
- `filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<project_id>"}, {"metadata": {"session_id": "<MEM0_SESSION_ID>"}}]}`
|
||||
- `top_k=20`
|
||||
3. Sort results by creation time descending and show the last N entries (default 1). Ask for confirmation.
|
||||
4. Delete confirmed entries via `delete_memory`.
|
||||
|
||||
If `MEM0_SESSION_ID` is not set or the search returns no results, tell the user: "No recent memory IDs tracked this session. Try `/mem0-tour` to browse recent memories, or `/mem0-forget <search query>` to find specific ones."
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -0,0 +1,71 @@
|
||||
---
|
||||
name: mem0-pin
|
||||
description: Pins or unpins a memory to protect it from pruning during dream consolidation. Use when a memory is critical and must never be removed, such as architecture decisions, security constraints, or immutable team conventions.
|
||||
---
|
||||
|
||||
# Mem0 Pin
|
||||
|
||||
Pin a memory to mark it as high-priority and protect from pruning.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Find the memory
|
||||
|
||||
The user provides either a search query or memory ID.
|
||||
|
||||
**If memory ID:**
|
||||
- Call `get_memory` with the ID.
|
||||
|
||||
**If search query:**
|
||||
- Call `search_memories` with the query, `filters={"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}]}`, `top_k=5`.
|
||||
- Show numbered list with content previews.
|
||||
- Ask: "Which memory to pin? Enter a number."
|
||||
|
||||
### Step 2: Read current content
|
||||
|
||||
Call `get_memory` with the selected memory ID. Store:
|
||||
- `original_text` — the memory's text content
|
||||
- `original_metadata` — the existing `metadata` dict
|
||||
|
||||
### Step 3: Pin it
|
||||
|
||||
The `update_memory` tool updates a memory by `id`. To pin durably, append a pin
|
||||
marker to the text so it travels with the memory:
|
||||
|
||||
```python
|
||||
pinned_text = "[PINNED] " + original_text if not original_text.startswith("[PINNED]") else original_text
|
||||
update_memory(id=<selected_id>, text=pinned_text)
|
||||
```
|
||||
|
||||
**For new memories** (user wants to pin text that isn't stored yet):
|
||||
1. Call `add_memory` with:
|
||||
- `text="[PINNED] <the user's text>"`
|
||||
- `user_id=<active_user_id>`
|
||||
- `app_id=<active_project_id>`
|
||||
- `metadata={"pinned": true, "type": "decision", "confidence": 1.0}`
|
||||
- `infer=False`
|
||||
2. The response contains `event_id`. Call `get_event_status(event_id=<event_id>)` once to retrieve the memory ID, then confirm.
|
||||
|
||||
### Step 4: Confirm
|
||||
|
||||
```
|
||||
Pinned: "<memory content, first 80 chars>"
|
||||
Memory ID: <id>
|
||||
```
|
||||
|
||||
Append `...` only if content exceeds 80 characters.
|
||||
|
||||
### Unpin
|
||||
|
||||
If the user says "unpin":
|
||||
1. Call `get_memory` to read current content.
|
||||
2. Remove the pin marker from the text:
|
||||
```python
|
||||
unpinned_text = original_text.removeprefix("[PINNED] ")
|
||||
update_memory(memory_id=<id>, text=unpinned_text)
|
||||
```
|
||||
3. Print: `Unpinned: "<content>..."`
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -0,0 +1,61 @@
|
||||
---
|
||||
name: mem0-remember
|
||||
description: Stores a memory verbatim from user input with appropriate type classification and metadata. Use when the user says remember this, save this, store this, note that, or explicitly asks to record a decision, preference, convention, or learning.
|
||||
---
|
||||
|
||||
# Mem0 Remember
|
||||
|
||||
Store a fact or learning directly into mem0.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Extract the content
|
||||
|
||||
The user provides the content as an argument: `/mem0-remember <text>`
|
||||
|
||||
If no text was provided, ask: "What should I remember?"
|
||||
|
||||
### Step 2: Classify the memory
|
||||
|
||||
Based on the content, pick the best `metadata.type`:
|
||||
|
||||
| Content signal | Type |
|
||||
|---|---|
|
||||
| "we decided...", "always use...", "never..." | `decision` |
|
||||
| "X doesn't work because...", "don't try..." | `anti_pattern` |
|
||||
| "I prefer...", "use X instead of Y" | `user_preference` |
|
||||
| "the convention is...", "we always..." | `convention` |
|
||||
| "learned that...", "figured out..." | `task_learning` |
|
||||
| setup, env, tooling, config | `environmental` |
|
||||
| anything else | `task_learning` |
|
||||
|
||||
### Step 3: Store
|
||||
|
||||
Call `add_memory` with:
|
||||
- `text="<the user's text>"`
|
||||
- `user_id=<active_user_id>`
|
||||
- `app_id=<active_project_id>`
|
||||
- `metadata={"type": "<classified_type>", "branch": "<active_branch>", "confidence": 1.0, "source": "remember_command"}`
|
||||
- `infer=False`
|
||||
|
||||
`infer=False` because the user stated the fact explicitly — no extraction needed.
|
||||
`confidence=1.0` because the user explicitly asked to store this.
|
||||
|
||||
### Step 4: Confirm
|
||||
|
||||
The `add_memory` response returns `event_id` (not `memory_id`) because writes are async.
|
||||
Call `get_event_status(event_id=<event_id>)` once.
|
||||
|
||||
- If status is `SUCCEEDED`: print the memory ID from the result.
|
||||
- If status is `PENDING` or `processing`: print with the event ID as fallback.
|
||||
|
||||
```
|
||||
Remembered as <type>: "<content, first 80 chars>"
|
||||
Memory ID: <id from event status>
|
||||
```
|
||||
|
||||
Append `...` only if content was truncated (longer than 80 chars).
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -0,0 +1,119 @@
|
||||
---
|
||||
name: mem0-scope
|
||||
description: Views or changes the default memory scope (project, session, or global) used when saving and searching memories. Use when the user wants to control whether memories are scoped to this repo, this run, or shared across all their projects.
|
||||
---
|
||||
|
||||
# Mem0 Scope
|
||||
|
||||
View or change the **default memory scope** — the scope the memory tools use when
|
||||
no explicit `scope` is given. The setting persists in `~/.mem0/settings.json`
|
||||
(`default_scope`) and the plugin reads it fresh on each memory operation, so a
|
||||
change takes effect immediately in the current session.
|
||||
|
||||
The three scopes:
|
||||
|
||||
- `project` (default) — this repo only. Filters by `user_id` + `app_id`.
|
||||
- `session` — this run only. Adds `run_id` (the current session) so memories are
|
||||
isolated to this conversation.
|
||||
- `global` — across ALL your projects. Reads use `app_id="*"`; writes drop
|
||||
`app_id` so the memory is user-wide.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Determine intent
|
||||
|
||||
Look at the user's message for a target scope word: `project`, `session`, or
|
||||
`global` (also accept "repo"→project, "run"→session, "all"/"everywhere"→global).
|
||||
|
||||
- No target word present → **View mode** (Step 2).
|
||||
- A target word present → **Change mode** (Step 3).
|
||||
|
||||
### Step 2: View mode — show the current scope
|
||||
|
||||
1. Read the current default scope from settings:
|
||||
|
||||
```bash
|
||||
_S="$HOME/.mem0/settings.json"
|
||||
[ -f "$_S" ] && grep -o '"default_scope"[[:space:]]*:[[:space:]]*"[a-z]*"' "$_S" | grep -o '[a-z]*"$' | tr -d '"' || echo "project"
|
||||
```
|
||||
|
||||
If the command prints nothing, the scope is `project` (the default).
|
||||
|
||||
2. (Optional) Show how many memories live in the current scope by calling
|
||||
`get_memories` with `scope="<current>"`, `page_size=1`, and reading the
|
||||
`count` (or result length) from the response.
|
||||
|
||||
3. Display using the identity the plugin exported (do NOT re-shell git):
|
||||
|
||||
```
|
||||
Mem0 memory scope
|
||||
|
||||
Current default scope: <current>
|
||||
|
||||
project - this repo only (user + app_id) <marker if active>
|
||||
session - this run only (adds run_id) <marker if active>
|
||||
global - all your projects (app_id = *) <marker if active>
|
||||
|
||||
User: ${MEM0_USER_ID}
|
||||
Project: ${MEM0_APP_ID}
|
||||
Session: ${MEM0_SESSION_ID}
|
||||
|
||||
To change: /mem0-scope session (or project / global)
|
||||
```
|
||||
|
||||
Put `[active]` next to the current scope. If you fetched a count in step 2,
|
||||
add a `Memories in scope: <N>` line.
|
||||
|
||||
### Step 3: Change mode — set a new scope
|
||||
|
||||
1. Validate the target is one of `project`, `session`, `global`. If not, show the
|
||||
three options and stop.
|
||||
|
||||
2. Read the existing settings so you preserve every other key. Use the Read tool
|
||||
on `~/.mem0/settings.json` (it may not exist yet — treat a missing file as
|
||||
`{}`).
|
||||
|
||||
3. Write the file back with the Write tool, keeping ALL existing keys and only
|
||||
setting `"default_scope"` to the target. Pretty-print with 2-space indent and
|
||||
a trailing newline. Do not drop `global_search`, `dream`, `auto_save`, or any
|
||||
other field that was present.
|
||||
|
||||
Example resulting file (when other keys already existed):
|
||||
|
||||
```json
|
||||
{
|
||||
"auto_save": true,
|
||||
"search_limit": 10,
|
||||
"default_scope": "global"
|
||||
}
|
||||
```
|
||||
|
||||
4. Confirm:
|
||||
|
||||
```
|
||||
Default memory scope changed: <old> -> <new>
|
||||
|
||||
<one line describing the effect — see below>
|
||||
Applies immediately to memory tools in this session.
|
||||
|
||||
To revert: /mem0-scope <old>
|
||||
```
|
||||
|
||||
Effect lines:
|
||||
- project → "New memories and searches are limited to this repo."
|
||||
- session → "New memories and searches are limited to this run (this conversation)."
|
||||
- global → "New memories and searches span all your projects. delete_all_memories still needs an explicit scope=global to delete user-wide."
|
||||
|
||||
### Notes
|
||||
|
||||
- This only changes the **default**. Any memory tool call can still pass an
|
||||
explicit `scope` to override it for that one call.
|
||||
- `delete_all_memories` deliberately ignores the default scope: deleting
|
||||
user-wide always requires an explicit `scope="global"`, so changing the
|
||||
default can never turn a routine cleanup into a cross-project wipe.
|
||||
- `global` scope (this user, all their projects) is distinct from the separate
|
||||
`global_search` setting (all users). Leave `global_search` untouched here.
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -0,0 +1,56 @@
|
||||
---
|
||||
name: mem0-search
|
||||
description: Searches memories and displays compact one-liner results, or looks up a specific memory by ID. Use for quick memory lookups, checking if a decision was recorded, resolving [mem0:id] citations, or browsing memories without full category detail.
|
||||
---
|
||||
|
||||
# Mem0 Search
|
||||
|
||||
Quick search with compact output. Lighter than `/mem0-tour`.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Parse query
|
||||
|
||||
The user provides a search query: `/mem0-search auth middleware`
|
||||
|
||||
If no query provided, ask: "What should I search for?"
|
||||
|
||||
**Memory ID detection:** If the query matches any of these patterns, treat it as a
|
||||
direct memory ID lookup instead of a search:
|
||||
- Bare hex: `^[a-f0-9]{8}$` (short ID) or `^[a-f0-9]{8}-[a-f0-9-]+$` (full UUID)
|
||||
- Citation ref: `[mem0:<hex>]` — extract the hex portion
|
||||
|
||||
When an ID is detected:
|
||||
1. Call `get_memory(<id>)` directly (if short ID, try as prefix of full UUID)
|
||||
2. If found, skip to Step 3 and display the single result
|
||||
3. If not found, fall through to search using the ID as query text
|
||||
|
||||
### Step 2: Search
|
||||
|
||||
Run 2 parallel `search_memories` calls:
|
||||
|
||||
1. Broad: `query=<user's query>`, `filters={"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}]}`, `top_k=10`, `rerank=true`
|
||||
2. Targeted: `query=<user's query>`, `filters={"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}, {"metadata": {"type": "decision"}}]}`, `top_k=5`, `rerank=true`
|
||||
|
||||
### Step 3: Display
|
||||
|
||||
Deduplicate by ID, then show compact results:
|
||||
|
||||
```
|
||||
## mem0 search: "<query>" (<N> results)
|
||||
|
||||
1. [decision] Auth module uses JWT with RS256 keys (2025-05-15) [mem0:a3f8b2c1]
|
||||
2. [anti_pattern] Don't use symmetric HS256 — leaked in env (2025-05-10) [mem0:7e2d9f4a]
|
||||
3. [convention] All middleware in src/middleware/ (2025-05-08) [mem0:c4d5e6f7]
|
||||
```
|
||||
|
||||
Format: `<number>. [<type>] <content, 80 chars> (<date>) [mem0:<short_id>]`
|
||||
|
||||
If no results:
|
||||
```
|
||||
No memories matching "<query>" for project <project_id>.
|
||||
```
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -0,0 +1,197 @@
|
||||
---
|
||||
name: mem0-status
|
||||
description: Diagnoses mem0 connectivity, API key validity, and memory read/write functionality. Use when memory operations fail, searches return empty, add_memory errors occur, or to verify the plugin is working correctly.
|
||||
---
|
||||
|
||||
# Mem0 Status
|
||||
|
||||
Run a diagnostic check on the mem0 plugin. Useful for troubleshooting.
|
||||
|
||||
## Execution
|
||||
|
||||
Run ALL checks, then display a single summary. Do not stop on the first failure.
|
||||
|
||||
### Check 1: API key
|
||||
|
||||
```bash
|
||||
_KEY="${MEM0_API_KEY:-}"
|
||||
[ -n "$_KEY" ] && echo "${_KEY:0:6}..." || echo "NOT_SET"
|
||||
```
|
||||
|
||||
- If `NOT_SET`: FAIL — "No API key configured"
|
||||
- If set: PASS — the command already prints only the first 6 chars
|
||||
|
||||
### Check 2: Identity resolution
|
||||
|
||||
Resolve identity from the `MEM0_*` environment variables set by the plugin's `shell.env` hook. These are the exact values the plugin uses to scope memories, so report them directly. Do NOT re-run `git` here: the plugin already resolved branch and project from git at session start, and re-shelling git can disagree with it — e.g. it prints an empty branch that renders as `(not a git repo)` while the Session check below shows `branch=main`. One source of truth keeps the two lines consistent.
|
||||
|
||||
```bash
|
||||
echo "user_id=${MEM0_USER_ID:-${USER:-default}}"
|
||||
echo "project_id=${MEM0_APP_ID:-}"
|
||||
echo "branch=${MEM0_BRANCH:-main}"
|
||||
_S="$HOME/.mem0/settings.json"
|
||||
_SCOPE="$(grep -o '"default_scope"[[:space:]]*:[[:space:]]*"[a-z]*"' "$_S" 2>/dev/null | grep -o '[a-z]*"$' | tr -d '"')"
|
||||
echo "default_scope=${_SCOPE:-project}"
|
||||
```
|
||||
|
||||
- `user_id`: from `MEM0_USER_ID`, falling back to `$USER`
|
||||
- `project_id`: from `MEM0_APP_ID`
|
||||
- `branch`: from `MEM0_BRANCH` (the plugin's resolved value; falls back to `main` outside a git repo)
|
||||
- `default_scope`: from `~/.mem0/settings.json` (`default_scope`), falling back to `project`. This is the scope memory tools use when none is given; change it with `/mem0-scope`.
|
||||
|
||||
PASS if `user_id` and `project_id` are non-empty. WARN if `project_id` is empty — the `shell.env` hook may not have fired (restart OpenCode). Report the branch verbatim from `MEM0_BRANCH`; never invent a string like `(not a git repo)`.
|
||||
|
||||
### Check 3: Memory tool connectivity
|
||||
|
||||
Call `search_memories` with:
|
||||
- `query="health check"`
|
||||
- `filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]}`
|
||||
- `top_k=1`
|
||||
|
||||
- If returns successfully (even empty): PASS
|
||||
- If errors: FAIL — show the error message
|
||||
|
||||
### Check 4: Memory write capability
|
||||
|
||||
Call `add_memory` with:
|
||||
- `text="Health check probe — safe to delete."`
|
||||
- `user_id=<active_user_id>`
|
||||
- `app_id=<active_project_id>`
|
||||
- `metadata={"type": "health_check", "probe": true}`
|
||||
- `infer=False`
|
||||
|
||||
The response returns `event_id` (v3 writes are async). Call `get_event_status(event_id=<event_id>)` to check processing.
|
||||
|
||||
- If status is `SUCCEEDED`: PASS — extract the memory ID from the event result, then call `delete_memory` with that ID to clean up.
|
||||
- If status is `PENDING` after 5 seconds: PASS (write accepted, processing delayed)
|
||||
- If errors: FAIL — show the error.
|
||||
|
||||
### Check 5: Session context
|
||||
|
||||
Check that the plugin's `shell.env` hook has injected session context into the environment:
|
||||
|
||||
```bash
|
||||
echo "session_id=${MEM0_SESSION_ID:-}"
|
||||
echo "app_id=${MEM0_APP_ID:-}"
|
||||
echo "branch=${MEM0_BRANCH:-}"
|
||||
```
|
||||
|
||||
- If all three are non-empty: PASS — "Session active"
|
||||
- If any are missing: WARN — "Plugin env vars not set; shell.env hook may not have fired"
|
||||
|
||||
### Check 6: Auto-dream readiness
|
||||
|
||||
Explain whether auto-dream (memory consolidation) is eligible to run, and if not, exactly which gate is blocking. Auto-dream runs at most once per session and only when **all** gates pass: time since last consolidation ≥ `minHours`, sessions since ≥ `minSessions`, and project memory count ≥ `minMemories`.
|
||||
|
||||
Read the gate state and thresholds:
|
||||
|
||||
```bash
|
||||
_ST="$HOME/.mem0/mem0-dream-state.json"
|
||||
_SET="$HOME/.mem0/settings.json"
|
||||
echo "sessions_since=$(grep -o '"sessionsSince"[[:space:]]*:[[:space:]]*[0-9]*' "$_ST" 2>/dev/null | grep -o '[0-9]*$' || echo 0)"
|
||||
echo "last_consolidated_ms=$(grep -o '"lastConsolidatedAt"[[:space:]]*:[[:space:]]*[0-9]*' "$_ST" 2>/dev/null | grep -o '[0-9]*$' || echo 0)"
|
||||
echo "min_hours=$(grep -o '"minHours"[[:space:]]*:[[:space:]]*[0-9]*' "$_SET" 2>/dev/null | grep -o '[0-9]*$' || echo 24)"
|
||||
echo "min_sessions=$(grep -o '"minSessions"[[:space:]]*:[[:space:]]*[0-9]*' "$_SET" 2>/dev/null | grep -o '[0-9]*$' || echo 5)"
|
||||
echo "min_memories=$(grep -o '"minMemories"[[:space:]]*:[[:space:]]*[0-9]*' "$_SET" 2>/dev/null | grep -o '[0-9]*$' || echo 20)"
|
||||
echo "now_s=$(date +%s)"
|
||||
echo "dream_env=${MEM0_DREAM:-unset}"
|
||||
```
|
||||
|
||||
For the memory count, reuse the project memory count from Check 3/4 (or call `get_memories` with the project filter, `page_size=1`, and read `count`).
|
||||
|
||||
Compute each gate:
|
||||
- **time**: `hours_since = (now_s - last_consolidated_ms/1000) / 3600`. Passes when `≥ min_hours`. If `last_consolidated_ms` is 0 it has never run → time gate passes.
|
||||
- **sessions**: passes when `sessions_since ≥ min_sessions`.
|
||||
- **memories**: passes when project memory count `≥ min_memories`.
|
||||
|
||||
Report:
|
||||
- If `dream_env` is `false`/`0`/`no`/`off`, or `dream.enabled` is false in settings: WARN — "Auto-dream disabled".
|
||||
- If all three gates pass: PASS — "eligible (runs at next session start)".
|
||||
- Otherwise: WARN — list the blocking gate(s), e.g. `sessions 2/5, memories 3/20`. This is expected, not an error — auto-dream is just waiting. Note the user can run `/mem0-dream` to consolidate now, or lower the thresholds via the `dream` block in `~/.mem0/settings.json`.
|
||||
|
||||
### Display
|
||||
|
||||
```
|
||||
## mem0 status
|
||||
|
||||
PASS API Key m0-dVe...
|
||||
PASS Identity user=kartik, project=mem0, branch=main
|
||||
PASS Default scope project
|
||||
PASS Memory Tools 142ms
|
||||
PASS Write/Read write + delete OK
|
||||
PASS Session session_id=abc123, app_id=mem0, branch=main
|
||||
WARN Auto-dream waiting — sessions 2/5, memories 3/20 (/mem0-dream to run now)
|
||||
|
||||
All checks passed.
|
||||
```
|
||||
|
||||
The Auto-dream line is informational: WARN here means "waiting on gates", not a failure. Show PASS when eligible, or "disabled" when turned off.
|
||||
|
||||
If any check fails, add a `## Troubleshooting` section with specific fix steps for each failure.
|
||||
|
||||
## Extended mode: Memory Quality Analysis
|
||||
|
||||
When invoked with `--deep` (e.g., `/mem0-status --deep`), run the standard 6 checks above **plus** a memory quality scan.
|
||||
|
||||
### Quality Check 1: Duplicates
|
||||
|
||||
Call `get_memories` with `filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]}`, `page_size=200`. Compare all pairs within the same `metadata.type` group for high textual overlap (shared nouns/keywords > 60%). Report:
|
||||
|
||||
```
|
||||
Potential duplicates: <N> pairs
|
||||
[mem0:<id1>] ≈ [mem0:<id2>] — both about "<shared topic>"
|
||||
```
|
||||
|
||||
### Quality Check 2: Stale memories
|
||||
|
||||
Flag memories where:
|
||||
- `metadata.type` is `session_state` or `compact_summary` AND older than 90 days
|
||||
- `metadata.confidence` < 0.3 AND older than 30 days
|
||||
|
||||
```
|
||||
Stale candidates: <N>
|
||||
[mem0:<id>] — session_state, 142d old
|
||||
```
|
||||
|
||||
### Quality Check 2b: Low-confidence memories
|
||||
|
||||
Flag memories where `metadata.confidence` < 0.5 (regardless of age). Report separately from stale:
|
||||
|
||||
```
|
||||
Low-confidence memories: <N>
|
||||
[mem0:<id>] — confidence=0.3, "<content preview>"
|
||||
```
|
||||
|
||||
### Quality Check 3: Contradictions
|
||||
|
||||
Within each `metadata.type` group, flag pairs that assert opposing facts about the same topic. Use semantic judgment — look for negation patterns, conflicting tool/framework choices, or reversed decisions.
|
||||
|
||||
```
|
||||
Possible contradictions: <N>
|
||||
[mem0:<idA>] vs [mem0:<idB>] — conflicting on "<topic>"
|
||||
```
|
||||
|
||||
### Quality Check 4: Orphan memories
|
||||
|
||||
Memories with no `metadata.type` set, or with `metadata.type` not in the 17 known coding categories. These were likely written without proper tagging.
|
||||
|
||||
```
|
||||
Untagged/orphan memories: <N>
|
||||
```
|
||||
|
||||
### Quality summary
|
||||
|
||||
```
|
||||
## Memory Quality
|
||||
|
||||
Duplicates: <N> · Stale: <N> · Contradictions: <N> · Orphans: <N>
|
||||
```
|
||||
|
||||
If all counts are 0: `Memory quality: clean.`
|
||||
If any non-zero: append `Run /mem0-dream to fix.`
|
||||
|
||||
To fix issues found by `--deep`, run `/mem0-dream` for automated consolidation (merges, prunes, conflict resolution).
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim — markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -0,0 +1,156 @@
|
||||
---
|
||||
name: mem0-tour
|
||||
description: Browses all stored memories grouped by category with full content display. Use when reviewing all project memories, exploring stored knowledge, onboarding to a project, or getting an overview of captured decisions, conventions, and learnings.
|
||||
---
|
||||
|
||||
# Mem0 Project Tour
|
||||
|
||||
Show the user what mem0 has stored for the current project.
|
||||
|
||||
## Cross-project mode
|
||||
|
||||
When invoked with `--all-projects` (e.g., `/mem0-tour --all-projects` or
|
||||
`/mem0-tour --all-projects auth middleware`), search across ALL projects:
|
||||
|
||||
1. Call `get_memories` with `filters={"AND": [{"user_id": "<active_user_id>"}]}`, `page_size=200` — **no `app_id` filter**.
|
||||
2. If a search query was also provided, run `search_memories` with `query=<query>`,
|
||||
`filters={"AND": [{"user_id": "<active_user_id>"}]}`, `top_k=20` — again no `app_id`.
|
||||
3. Group results by `app_id` first, then by category within each project.
|
||||
4. Display:
|
||||
```
|
||||
## <app_id_1> (<N> memories) ← current
|
||||
**Architecture Decisions** — <memory content>
|
||||
...
|
||||
|
||||
## <app_id_2> (<N> memories)
|
||||
...
|
||||
|
||||
<N> memories across <M> projects
|
||||
```
|
||||
5. Mark the current project with `← (current)` in the heading.
|
||||
|
||||
If `--all-projects` is NOT present, use the standard single-project flow below.
|
||||
|
||||
## Peek mode (compact search)
|
||||
|
||||
When `/mem0-tour` receives a search query argument (e.g., `/mem0-tour auth middleware`)
|
||||
WITHOUT `--all-projects`, run in **peek mode** — compact one-liner results:
|
||||
|
||||
1. Run 2 parallel `search_memories` calls:
|
||||
- Broad: `query=<query>`, `filters={"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}]}`, `top_k=10`, `rerank=true`
|
||||
- Targeted: `query=<query>`, `filters={"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}, {"metadata": {"type": "decision"}}]}`, `top_k=5`, `rerank=true`
|
||||
2. Deduplicate by ID, display compact results:
|
||||
```
|
||||
## mem0 search: "<query>" (<N> results)
|
||||
|
||||
1. [decision] Auth module uses JWT with RS256 keys (2025-05-15) [mem0:a3f8b2c1]
|
||||
2. [anti_pattern] Don't use symmetric HS256 — leaked in env (2025-05-10) [mem0:7e2d9f4a]
|
||||
3. [convention] All middleware in src/middleware/ (2025-05-08) [mem0:c4d5e6f7]
|
||||
```
|
||||
Format: `<number>. [<type>] <content, 80 chars> (<date>) [mem0:<short_id>]`
|
||||
3. If no results: `No memories matching "<query>" for project <project_id>.`
|
||||
|
||||
If no query argument and no `--all-projects` flag, use the full tour flow below.
|
||||
|
||||
## Execution
|
||||
|
||||
### Step 1: Fetch ALL memories for this project
|
||||
|
||||
Call `get_memories` to fetch all memories for this project:
|
||||
|
||||
`filters={"AND": [{"user_id": "<active_user_id>"}, {"app_id": "<active_project_id>"}]}`, `page_size=100`
|
||||
|
||||
### Step 2: Run supplementary semantic searches
|
||||
|
||||
In parallel, run these `search_memories` calls to get relevance-ranked results for key topics:
|
||||
|
||||
- `query="architecture decisions design choices"`, `filters={"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}]}`, `top_k=10`, `rerank=true`
|
||||
- `query="bugs errors failures anti-patterns"`, `filters={"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}]}`, `top_k=10`, `rerank=true`
|
||||
- `query="project setup tooling conventions preferences"`, `filters={"AND": [{"user_id": "<id>"}, {"app_id": "<pid>"}]}`, `top_k=10`, `rerank=true`
|
||||
|
||||
**Do NOT filter by `metadata.type` in these calls.** The platform auto-assigns `categories` — filtering on `metadata.type` misses memories that were auto-categorized but don't have an explicit `metadata.type`.
|
||||
|
||||
### Step 3: Merge and group
|
||||
|
||||
Merge all results by memory ID (deduplicate). For each memory, determine its group using this priority:
|
||||
|
||||
1. **Platform `categories` field** (array on each memory, auto-assigned by Mem0). Use the first category value.
|
||||
2. **`metadata.type` field** (if present, set explicitly by hooks/agent). Use as fallback if no `categories`.
|
||||
3. **"other"** bucket for memories with neither.
|
||||
|
||||
Map category names to display names:
|
||||
|
||||
| Platform category / metadata.type | Display name |
|
||||
|---|---|
|
||||
| `architecture decisions`, `architecture_decisions`, `decision` | Architecture Decisions |
|
||||
| `anti patterns`, `anti_patterns`, `anti_pattern` | Anti-Patterns |
|
||||
| `task learnings`, `task_learnings`, `task_learning` | Task Learnings |
|
||||
| `coding conventions`, `coding_conventions`, `convention` | Coding Conventions |
|
||||
| `user preferences`, `user_preferences`, `user_preference` | User Preferences |
|
||||
| `project profile`, `project_profile` | Project Profile |
|
||||
| `tooling setup`, `tooling_setup`, `environmental` | Tooling & Setup |
|
||||
| `technology`, `professional_details` | Tooling & Setup |
|
||||
| `session_state` | Session State |
|
||||
| `compact_summary` | Compact Summaries |
|
||||
| anything else | Other |
|
||||
|
||||
### Step 4: Display results
|
||||
|
||||
Sort groups by descending memory count. Display in compact tabular format:
|
||||
|
||||
First show the category summary table:
|
||||
|
||||
```
|
||||
mem0 tour
|
||||
|
||||
Session (ses_abc123) branch: main
|
||||
Project: my-project - 349 memories
|
||||
|
||||
Category Count
|
||||
-----------------------------------------
|
||||
tooling_setup 119
|
||||
bug_fixes 78
|
||||
architecture_decisions 32
|
||||
task_learnings 14
|
||||
...
|
||||
```
|
||||
|
||||
Then for each category (sorted by count descending), show memories as numbered one-liners. Truncate each memory to 100 chars max:
|
||||
|
||||
```
|
||||
tooling_setup (119)
|
||||
1. User requires that no git commit or push be performed without explicit permission...
|
||||
2. OpenCode plugins are loaded from ~/.config/opencode/plugins/ for global installation...
|
||||
3. Assistant determined that the symlink method for loading the Mem0 plugin was failing...
|
||||
... and 116 more
|
||||
|
||||
bug_fixes (78)
|
||||
1. Fixed getAll filter format from flat object to AND-wrapped array for mem0ai TS SDK v3...
|
||||
2. Root cause of user_id mismatch: plugin derived kartik.labhshetwar from git email...
|
||||
... and 76 more
|
||||
```
|
||||
|
||||
Show top 5 memories per category by recency. If a group has more than 5, note `... and <N> more`.
|
||||
|
||||
Skip empty groups entirely.
|
||||
|
||||
### Step 5: Print totals
|
||||
|
||||
```
|
||||
<N> memories across <M> categories
|
||||
project: <project_id> branch: <active_branch>
|
||||
|
||||
Identity - user: <user_id> project: <project_id> branch: <branch>
|
||||
```
|
||||
|
||||
### Step 6: Empty state
|
||||
|
||||
If zero memories found for this project, print:
|
||||
```
|
||||
No memories stored yet for project <project_id>.
|
||||
Start working - mem0 captures learnings automatically, or use /mem0-remember to save something now.
|
||||
```
|
||||
|
||||
## Output formatting
|
||||
|
||||
IMPORTANT: Do NOT use markdown in your output. OpenCode TUI renders text verbatim - markdown like **bold**, ## headers, and | table | syntax appears as raw characters. Use plain text with indentation for structure. Use dashes for lists. Use spaces to align columns instead of markdown tables.
|
||||
@@ -0,0 +1,66 @@
|
||||
{
|
||||
"name": "@mem0/opencode-plugin",
|
||||
"version": "0.2.1",
|
||||
"type": "module",
|
||||
"description": "Mem0 persistent memory plugin for OpenCode — add, search, and manage memories across sessions",
|
||||
"main": "dist/index.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"exports": {
|
||||
".": {
|
||||
"types": "./dist/index.d.ts",
|
||||
"import": "./dist/index.js"
|
||||
}
|
||||
},
|
||||
"publishConfig": {
|
||||
"access": "public"
|
||||
},
|
||||
"license": "Apache-2.0",
|
||||
"keywords": [
|
||||
"opencode",
|
||||
"opencode-plugin",
|
||||
"mem0",
|
||||
"memory",
|
||||
"ai-memory",
|
||||
"persistent-memory"
|
||||
],
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/mem0ai/mem0",
|
||||
"directory": "integrations/mem0-plugin/.opencode-plugin"
|
||||
},
|
||||
"files": [
|
||||
"dist",
|
||||
"LICENSE",
|
||||
"opencode-skills"
|
||||
],
|
||||
"scripts": {
|
||||
"build": "bun build opencode-mem0.ts --outdir dist --target bun --format esm --entry-naming index.[ext] && tsc --emitDeclarationOnly --outDir dist --declaration",
|
||||
"dev": "bun build opencode-mem0.ts --outdir dist --target bun --format esm --entry-naming index.[ext] --watch",
|
||||
"type-check": "tsc --noEmit",
|
||||
"prepack": "bun run build",
|
||||
"postpack": ""
|
||||
},
|
||||
"opencode": {
|
||||
"type": "plugin",
|
||||
"hooks": [
|
||||
"config",
|
||||
"chat.message",
|
||||
"tool.execute.before",
|
||||
"tool.execute.after",
|
||||
"experimental.chat.messages.transform",
|
||||
"experimental.session.compacting",
|
||||
"shell.env"
|
||||
]
|
||||
},
|
||||
"dependencies": {
|
||||
"@opencode-ai/plugin": "^1.0.162",
|
||||
"mem0ai": "^3.0.8"
|
||||
},
|
||||
"devDependencies": {
|
||||
"bun-types": ">=1.3.14",
|
||||
"typescript": "^5.7.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"bun": ">=1.0.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { parseProjectFromRemote } from "./project";
|
||||
|
||||
describe("parseProjectFromRemote", () => {
|
||||
test("ssh remote with a custom host alias (github.com-work)", () => {
|
||||
expect(parseProjectFromRemote("git@github.com-mem0:mem0ai/mem0.git")).toBe("mem0ai-mem0");
|
||||
});
|
||||
|
||||
test("standard scp-style ssh remote", () => {
|
||||
expect(parseProjectFromRemote("git@github.com:openai/gym.git")).toBe("openai-gym");
|
||||
});
|
||||
|
||||
test("https remote", () => {
|
||||
expect(parseProjectFromRemote("https://github.com/mem0ai/mem0.git")).toBe("mem0ai-mem0");
|
||||
});
|
||||
|
||||
test("https remote without a .git suffix", () => {
|
||||
expect(parseProjectFromRemote("https://gitlab.com/acme/widgets")).toBe("acme-widgets");
|
||||
});
|
||||
|
||||
test("trailing slash is ignored", () => {
|
||||
expect(parseProjectFromRemote("https://github.com/acme/widgets/")).toBe("acme-widgets");
|
||||
});
|
||||
|
||||
test("returns null when no owner/repo can be parsed", () => {
|
||||
expect(parseProjectFromRemote("not-a-remote")).toBeNull();
|
||||
expect(parseProjectFromRemote("")).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* Project identity resolution for the Mem0 OpenCode plugin.
|
||||
*
|
||||
* The project id (`app_id`) scopes memories to a repo. We derive it from the
|
||||
* git remote so it is stable across clones, worktrees, and sub-directories —
|
||||
* falling back (in opencode-mem0.ts) to the git repo root dir name, then the
|
||||
* cwd. Keeping the parser pure makes the tricky remote formats testable.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parse `owner/repo` out of a git remote URL and return it as `owner-repo`.
|
||||
* Handles https, scp-style ssh, custom ssh host aliases (e.g.
|
||||
* `git@github.com-work:owner/repo.git`), an optional `.git` suffix, and a
|
||||
* trailing slash. Returns null when no owner/repo can be found.
|
||||
*/
|
||||
export function parseProjectFromRemote(remote: string): string | null {
|
||||
const m = remote.trim().match(/[:/]([^/:]+)\/([^/:]+?)(?:\.git)?\/?$/);
|
||||
if (!m) return null;
|
||||
return `${m[1]}-${m[2]}`;
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
import { describe, expect, test } from "bun:test";
|
||||
import { scopeSearchFilters, scopeWriteParams, asScope, resolveDefaultScope } from "./scope";
|
||||
|
||||
describe("memory scope (pi-agent parity)", () => {
|
||||
test("project scope = this repo", () => {
|
||||
expect(scopeSearchFilters("project", "u", "app", "run")).toEqual({
|
||||
user_id: "u",
|
||||
app_id: "app",
|
||||
});
|
||||
expect(scopeWriteParams("project", "u", "app", "run")).toEqual({
|
||||
user_id: "u",
|
||||
app_id: "app",
|
||||
});
|
||||
});
|
||||
|
||||
test("session scope adds run_id", () => {
|
||||
expect(scopeSearchFilters("session", "u", "app", "run")).toEqual({
|
||||
user_id: "u",
|
||||
app_id: "app",
|
||||
run_id: "run",
|
||||
});
|
||||
expect(scopeWriteParams("session", "u", "app", "run")).toEqual({
|
||||
user_id: "u",
|
||||
app_id: "app",
|
||||
run_id: "run",
|
||||
});
|
||||
});
|
||||
|
||||
test("global scope spans all the user's projects (matches pi-agent)", () => {
|
||||
expect(scopeSearchFilters("global", "u", "app", "run")).toEqual({
|
||||
user_id: "u",
|
||||
app_id: "*",
|
||||
});
|
||||
// global writes drop app_id so the memory is user-wide, not project-bound
|
||||
expect(scopeWriteParams("global", "u", "app", "run")).toEqual({ user_id: "u" });
|
||||
});
|
||||
|
||||
test("default scope is project when settings are absent", () => {
|
||||
expect(resolveDefaultScope(null)).toBe("project");
|
||||
expect(resolveDefaultScope(undefined)).toBe("project");
|
||||
expect(resolveDefaultScope({})).toBe("project");
|
||||
});
|
||||
|
||||
test("default scope reads default_scope from settings", () => {
|
||||
expect(resolveDefaultScope({ default_scope: "session" })).toBe("session");
|
||||
expect(resolveDefaultScope({ default_scope: "global" })).toBe("global");
|
||||
expect(resolveDefaultScope({ default_scope: "project" })).toBe("project");
|
||||
});
|
||||
|
||||
test("default scope normalizes an invalid default_scope to project", () => {
|
||||
expect(resolveDefaultScope({ default_scope: "nonsense" })).toBe("project");
|
||||
expect(resolveDefaultScope({ default_scope: 42 })).toBe("project");
|
||||
});
|
||||
|
||||
test("asScope normalizes unknown values to project", () => {
|
||||
expect(asScope("global")).toBe("global");
|
||||
expect(asScope("session")).toBe("session");
|
||||
expect(asScope("project")).toBe("project");
|
||||
expect(asScope("nonsense")).toBe("project");
|
||||
expect(asScope(undefined)).toBe("project");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,71 @@
|
||||
/**
|
||||
* Memory scope resolution — ported from the pi-agent plugin's scoping model.
|
||||
*
|
||||
* Lets the agent choose, per memory operation, how wide to read/write:
|
||||
* - "project" (default): this repo only -> { user_id, app_id }
|
||||
* - "session": this run only -> { user_id, app_id, run_id }
|
||||
* - "global": across ALL of the user's projects -> { user_id, app_id: "*" }
|
||||
*
|
||||
* Mirrors pi-agent/src/memory/scoping.ts (resolveSearchFilters / resolveAddParams)
|
||||
* so the OpenCode plugin exposes scope the same way: as a per-call tool parameter,
|
||||
* not a stateful "switch project" command.
|
||||
*/
|
||||
|
||||
export type Scope = "project" | "session" | "global";
|
||||
|
||||
/** Filters for `search` / `get_memories` at the given scope. */
|
||||
export function scopeSearchFilters(
|
||||
scope: Scope,
|
||||
userId: string,
|
||||
appId: string,
|
||||
runId: string,
|
||||
): Record<string, string> {
|
||||
switch (scope) {
|
||||
case "session":
|
||||
return { user_id: userId, app_id: appId, run_id: runId };
|
||||
case "global":
|
||||
return { user_id: userId, app_id: "*" };
|
||||
case "project":
|
||||
default:
|
||||
return { user_id: userId, app_id: appId };
|
||||
}
|
||||
}
|
||||
|
||||
/** Identity params for `add` / `delete_all` at the given scope. */
|
||||
export function scopeWriteParams(
|
||||
scope: Scope,
|
||||
userId: string,
|
||||
appId: string,
|
||||
runId: string,
|
||||
): { user_id: string; app_id?: string; run_id?: string } {
|
||||
switch (scope) {
|
||||
case "session":
|
||||
return { user_id: userId, app_id: appId, run_id: runId };
|
||||
case "global":
|
||||
return { user_id: userId };
|
||||
case "project":
|
||||
default:
|
||||
return { user_id: userId, app_id: appId };
|
||||
}
|
||||
}
|
||||
|
||||
/** Normalize an arbitrary value to a valid Scope (defaults to "project"). */
|
||||
export function asScope(value: unknown): Scope {
|
||||
return value === "session" || value === "global" ? value : "project";
|
||||
}
|
||||
|
||||
/**
|
||||
* Resolve the persisted default scope from a parsed `~/.mem0/settings.json`
|
||||
* object. This is the user-changeable default applied to memory operations when
|
||||
* no explicit `scope` is passed (set via the `mem0-scope` skill). Falls back to
|
||||
* "project" when unset or invalid.
|
||||
*/
|
||||
export function resolveDefaultScope(
|
||||
settings: Record<string, unknown> | null | undefined,
|
||||
): Scope {
|
||||
return asScope(settings?.default_scope);
|
||||
}
|
||||
|
||||
/** Guidance injected so the agent uses `global` only when explicitly asked. */
|
||||
export const SCOPE_GUIDANCE =
|
||||
'Memory tools accept an optional `scope`: omit it (or "project") for normal queries; use "session" to limit to the current run; use "global" ONLY when the user explicitly asks to search across all their projects in this workspace.';
|
||||
@@ -0,0 +1,78 @@
|
||||
import { afterEach, describe, expect, test } from "bun:test";
|
||||
import { buildEvent, captureEvent, isTelemetryEnabled } from "./telemetry";
|
||||
|
||||
const KEY = "m0-testkey123";
|
||||
|
||||
afterEach(() => {
|
||||
delete process.env.MEM0_TELEMETRY;
|
||||
});
|
||||
|
||||
describe("opencode telemetry", () => {
|
||||
test("buildEvent uses the shared plugin.* schema with platform=opencode", () => {
|
||||
const payload = buildEvent("session_start", { memory_count: 5 }, KEY);
|
||||
expect(payload).not.toBeNull();
|
||||
const props = payload!.properties as Record<string, unknown>;
|
||||
expect(payload!.event).toBe("plugin.session_start");
|
||||
expect(props.source).toBe("plugin");
|
||||
expect(props.platform).toBe("opencode");
|
||||
expect(props.memory_count).toBe(5);
|
||||
expect(props.$process_person_profile).toBe(false);
|
||||
expect(typeof props.plugin_version).toBe("string");
|
||||
});
|
||||
|
||||
test("distinct_id is sha256(apiKey)[:32] — matches the editor plugin", async () => {
|
||||
const { createHash } = await import("node:crypto");
|
||||
const expected = createHash("sha256").update(KEY).digest("hex").slice(0, 32);
|
||||
expect(buildEvent("session_start", {}, KEY)!.distinct_id).toBe(expected);
|
||||
});
|
||||
|
||||
test("system properties win over caller-supplied ones", () => {
|
||||
const props = buildEvent("x", { platform: "HACK", source: "HACK" }, KEY)!
|
||||
.properties as Record<string, unknown>;
|
||||
expect(props.platform).toBe("opencode");
|
||||
expect(props.source).toBe("plugin");
|
||||
});
|
||||
|
||||
test("returns null without an API key (no anonymous events)", () => {
|
||||
expect(buildEvent("session_start", {}, undefined)).toBeNull();
|
||||
});
|
||||
|
||||
test("opt-out via MEM0_TELEMETRY disables events", () => {
|
||||
process.env.MEM0_TELEMETRY = "false";
|
||||
expect(isTelemetryEnabled()).toBe(false);
|
||||
expect(buildEvent("session_start", {}, KEY)).toBeNull();
|
||||
});
|
||||
|
||||
test("captureEvent never throws (and sends nothing when opted out)", () => {
|
||||
process.env.MEM0_TELEMETRY = "false";
|
||||
expect(() => captureEvent("session_start", {}, KEY)).not.toThrow();
|
||||
expect(() => captureEvent("session_start", {}, undefined)).not.toThrow();
|
||||
expect(() => captureEvent("session_start", {}, KEY, "proj")).not.toThrow();
|
||||
});
|
||||
|
||||
test("every event carries os_version (matches telemetry.py schema)", () => {
|
||||
const props = buildEvent("session_start", {}, KEY)!
|
||||
.properties as Record<string, unknown>;
|
||||
expect(typeof props.os_version).toBe("string");
|
||||
});
|
||||
|
||||
test("project_hash is sha256(projectId) when a project id is supplied", async () => {
|
||||
const { createHash } = await import("node:crypto");
|
||||
const expected = createHash("sha256").update("acme-repo").digest("hex");
|
||||
const props = buildEvent("session_start", {}, KEY, "acme-repo")!
|
||||
.properties as Record<string, unknown>;
|
||||
expect(props.project_hash).toBe(expected);
|
||||
});
|
||||
|
||||
test("project_hash is omitted when no project id is supplied (no raw ids leak)", () => {
|
||||
const props = buildEvent("session_start", {}, KEY)!
|
||||
.properties as Record<string, unknown>;
|
||||
expect("project_hash" in props).toBe(false);
|
||||
});
|
||||
|
||||
test("expanded event types all use the shared plugin.* namespace", () => {
|
||||
for (const ev of ["user_prompt", "bash_error", "pre_compact", "session_stop"]) {
|
||||
expect(buildEvent(ev, {}, KEY)!.event).toBe(`plugin.${ev}`);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,113 @@
|
||||
/**
|
||||
* Plugin telemetry for the Mem0 OpenCode plugin — anonymous usage tracking
|
||||
* via PostHog.
|
||||
*
|
||||
* Emits the SAME event schema as the Mem0 editor plugin's telemetry.py
|
||||
* (event names prefixed `plugin.`, `source: "plugin"`, `platform: "opencode"`,
|
||||
* `distinct_id = sha256(apiKey)[:32]`) so OpenCode shows up as just another
|
||||
* `platform` value in the shared plugin dashboard instead of a separate
|
||||
* event namespace.
|
||||
*
|
||||
* Fire-and-forget: never throws, never blocks, failures are swallowed. Only
|
||||
* fires when an API key is present (same as the editor plugin — anonymous
|
||||
* installs without a key emit nothing). Disable with MEM0_TELEMETRY=false.
|
||||
*
|
||||
* Never sends: memory content, API keys, raw user/project IDs. Only sends:
|
||||
* event type, platform, plugin version, and anonymized hashes of the API key
|
||||
* and project ID.
|
||||
*/
|
||||
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { release } from "node:os";
|
||||
|
||||
const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX";
|
||||
const POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/";
|
||||
const REQUEST_TIMEOUT_MS = 2_000;
|
||||
|
||||
function _loadPluginVersion(): string {
|
||||
// Source context: telemetry.ts sits next to package.json (./).
|
||||
// Bundled context: dist/index.js sits one level below it (../).
|
||||
for (const rel of ["./package.json", "../package.json"]) {
|
||||
try {
|
||||
const pkg = JSON.parse(readFileSync(new URL(rel, import.meta.url), "utf-8"));
|
||||
if (pkg?.name === "@mem0/opencode-plugin" && pkg.version) return pkg.version;
|
||||
} catch {
|
||||
/* try next candidate */
|
||||
}
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
const PLUGIN_VERSION = _loadPluginVersion();
|
||||
|
||||
export function isTelemetryEnabled(): boolean {
|
||||
const val = process.env.MEM0_TELEMETRY;
|
||||
if (val === undefined) return true;
|
||||
const s = val.toLowerCase();
|
||||
return s !== "false" && s !== "0" && s !== "no" && s !== "off";
|
||||
}
|
||||
|
||||
function distinctId(apiKey: string): string {
|
||||
// Matches telemetry.py `_distinct_id()` so the same user is one person in
|
||||
// PostHog whether they use OpenCode or any other Mem0 editor plugin.
|
||||
return createHash("sha256").update(apiKey).digest("hex").slice(0, 32);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build the PostHog event payload, or null when telemetry is disabled or no
|
||||
* API key is available. Pure (aside from env/version reads) and exported for
|
||||
* testing. System-controlled properties are applied last so a caller cannot
|
||||
* override `source`/`platform`/etc.
|
||||
*/
|
||||
export function buildEvent(
|
||||
eventType: string,
|
||||
properties: Record<string, unknown>,
|
||||
apiKey: string | undefined,
|
||||
projectId?: string,
|
||||
): Record<string, unknown> | null {
|
||||
if (!isTelemetryEnabled() || !apiKey) return null;
|
||||
return {
|
||||
api_key: POSTHOG_API_KEY,
|
||||
distinct_id: distinctId(apiKey),
|
||||
event: `plugin.${eventType}`,
|
||||
properties: {
|
||||
...properties,
|
||||
source: "plugin",
|
||||
platform: "opencode",
|
||||
plugin_version: PLUGIN_VERSION,
|
||||
os: process.platform,
|
||||
os_version: release(),
|
||||
sample_rate: 1.0,
|
||||
$process_person_profile: false,
|
||||
$lib: "posthog-node",
|
||||
// Anonymized project segmentation, matching telemetry.py's project_hash.
|
||||
...(projectId
|
||||
? { project_hash: createHash("sha256").update(projectId).digest("hex") }
|
||||
: {}),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
/** Send a usage event, fire-and-forget. Never throws, never blocks. */
|
||||
export function captureEvent(
|
||||
eventType: string,
|
||||
properties: Record<string, unknown>,
|
||||
apiKey: string | undefined,
|
||||
projectId?: string,
|
||||
): void {
|
||||
const payload = buildEvent(eventType, properties, apiKey, projectId);
|
||||
if (!payload) return;
|
||||
try {
|
||||
void fetch(POSTHOG_HOST, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(payload),
|
||||
signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS),
|
||||
}).catch(() => {
|
||||
/* fire-and-forget */
|
||||
});
|
||||
} catch {
|
||||
/* never throw */
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "ESNext",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "bundler",
|
||||
"declaration": true,
|
||||
"declarationDir": "dist",
|
||||
"outDir": "dist",
|
||||
"rootDir": ".",
|
||||
"strict": true,
|
||||
"esModuleInterop": true,
|
||||
"skipLibCheck": true,
|
||||
"lib": ["ESNext"],
|
||||
"types": ["bun-types"],
|
||||
"noEmit": false,
|
||||
"emitDeclarationOnly": true
|
||||
},
|
||||
"include": ["**/*"],
|
||||
"exclude": ["node_modules", "dist", "**/*.test.ts"]
|
||||
}
|
||||
Reference in New Issue
Block a user