From 7e096155714cd67360f91cc8d82fbbb8bda3c903 Mon Sep 17 00:00:00 2001 From: Himanshu <104683257+Himanshu-Sangshetti@users.noreply.github.com> Date: Mon, 24 Aug 2026 14:03:50 +0530 Subject: [PATCH] =?UTF-8?q?feat(integrations):=20dsh-mem0=20=E2=80=94=20Me?= =?UTF-8?q?m0=20as=20a=20native=20DeepSeek=20Harness=20plugin=20(#7027)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: kartik-mem0 --- .github/AGENTS.md | 2 + .github/workflows/ci-gate.yml | 13 + .github/workflows/dsh-mem0-cd.yml | 60 + .github/workflows/dsh-mem0-checks.yml | 90 + .github/workflows/release.yml | 1 + .gitignore | 4 + docs/docs.json | 3 +- docs/integrations/dsh-mem0.mdx | 89 + docs/llms.txt | 1 + integrations/AGENTS.md | 3 +- integrations/dsh-mem0/.npmrc | 7 + integrations/dsh-mem0/LICENSE | 201 ++ integrations/dsh-mem0/README.md | 60 + integrations/dsh-mem0/cordis.example.yml | 19 + integrations/dsh-mem0/package.json | 59 + integrations/dsh-mem0/pnpm-lock.yaml | 2094 +++++++++++++++++ integrations/dsh-mem0/src/formatting.ts | 69 + integrations/dsh-mem0/src/index.ts | 138 ++ integrations/dsh-mem0/src/output.ts | 33 + integrations/dsh-mem0/src/scoping.ts | 53 + integrations/dsh-mem0/tests/apply.test.ts | 143 ++ .../dsh-mem0/tests/formatting.test.ts | 71 + integrations/dsh-mem0/tests/output.test.ts | 30 + integrations/dsh-mem0/tests/scoping.test.ts | 44 + integrations/dsh-mem0/tsconfig.json | 22 + integrations/dsh-mem0/tsup.config.ts | 12 + 26 files changed, 3319 insertions(+), 2 deletions(-) create mode 100644 .github/workflows/dsh-mem0-cd.yml create mode 100644 .github/workflows/dsh-mem0-checks.yml create mode 100644 docs/integrations/dsh-mem0.mdx create mode 100644 integrations/dsh-mem0/.npmrc create mode 100644 integrations/dsh-mem0/LICENSE create mode 100644 integrations/dsh-mem0/README.md create mode 100644 integrations/dsh-mem0/cordis.example.yml create mode 100644 integrations/dsh-mem0/package.json create mode 100644 integrations/dsh-mem0/pnpm-lock.yaml create mode 100644 integrations/dsh-mem0/src/formatting.ts create mode 100644 integrations/dsh-mem0/src/index.ts create mode 100644 integrations/dsh-mem0/src/output.ts create mode 100644 integrations/dsh-mem0/src/scoping.ts create mode 100644 integrations/dsh-mem0/tests/apply.test.ts create mode 100644 integrations/dsh-mem0/tests/formatting.test.ts create mode 100644 integrations/dsh-mem0/tests/output.test.ts create mode 100644 integrations/dsh-mem0/tests/scoping.test.ts create mode 100644 integrations/dsh-mem0/tsconfig.json create mode 100644 integrations/dsh-mem0/tsup.config.ts diff --git a/.github/AGENTS.md b/.github/AGENTS.md index b88729b60..e3c5ceaf1 100644 --- a/.github/AGENTS.md +++ b/.github/AGENTS.md @@ -21,6 +21,7 @@ Package workflows keep their own push-to-main and manual triggers. Their `pull_r | Mem0 Plugin | `mem0-plugin-checks.yml` | Push to main (`integrations/mem0-plugin/`, excluding `.opencode-plugin/`), manual | pytest + hook exec bits + JSON manifest validation on Python 3.10, 3.11, 3.12 | | OpenCode Plugin | `opencode-plugin-checks.yml` | Push to main (`.opencode-plugin/`), manual | Bun: tsc + build + dist artifact check | | Pi Agent Plugin | `pi-agent-plugin-checks.yml` | Push to main (`integrations/pi-agent-plugin/`), manual | tsc + vitest + tsup on Node 20, 22 | +| DeepSeek Harness Plugin | `dsh-mem0-checks.yml` | Push to main (`integrations/dsh-mem0/`), manual | tsc + vitest + tsup on Node 20, 22 | | n8n Node | `n8n-nodes-mem0-checks.yml` | Push to main (`integrations/n8n-nodes-mem0/`), manual | ESLint + tsc build on Node 20 | | Zapier App | `zapier-mem0-checks.yml` | Push to main (`integrations/zapier-mem0/`), manual | tsc + `zapier validate` + offline unit tests on Node 22 | | strands-mem0 | `strands-mem0-checks.yml` | Push to main (`integrations/strands-mem0/`), manual | Ruff + mypy + pytest + hatch build on Python 3.10, 3.11, 3.12 | @@ -59,6 +60,7 @@ Requiring `CI Gate` also means fork PRs from first-time contributors cannot merg | OpenClaw | `openclaw-cd.yml` | `openclaw-v*` | npm (`@mem0/openclaw-mem0`) | | OpenCode Plugin | `opencode-plugin-cd.yml` | `opencode-v*` | npm (`@mem0/opencode-plugin`) | | Pi Agent Plugin | `pi-agent-plugin-cd.yml` | `pi-agent-v*` | npm (`@mem0/pi-agent-plugin`) | +| DeepSeek Harness Plugin | `dsh-mem0-cd.yml` | `dsh-mem0-v*` | npm (`@mem0/dsh-mem0`) | | n8n Node | `n8n-nodes-mem0-cd.yml` | `n8n-nodes-mem0-v*` | npm (`@mem0/n8n-nodes-mem0`) | | strands-mem0 | `strands-mem0-cd.yml` | `strands-mem0-v*` | PyPI (`strands-mem0`) | diff --git a/.github/workflows/ci-gate.yml b/.github/workflows/ci-gate.yml index 826e2db38..93a2c755d 100644 --- a/.github/workflows/ci-gate.yml +++ b/.github/workflows/ci-gate.yml @@ -41,6 +41,7 @@ jobs: mem0_plugin: ${{ steps.filter.outputs.mem0_plugin }} opencode_plugin: ${{ steps.filter.outputs.opencode_plugin }} pi_agent_plugin: ${{ steps.filter.outputs.pi_agent_plugin }} + dsh_mem0: ${{ steps.filter.outputs.dsh_mem0 }} n8n_nodes_mem0: ${{ steps.filter.outputs.n8n_nodes_mem0 }} zapier_mem0: ${{ steps.filter.outputs.zapier_mem0 }} strands_mem0: ${{ steps.filter.outputs.strands_mem0 }} @@ -89,6 +90,10 @@ jobs: - 'integrations/pi-agent-plugin/**' - '.github/workflows/pi-agent-plugin-checks.yml' - '.github/workflows/ci-gate.yml' + dsh_mem0: + - 'integrations/dsh-mem0/**' + - '.github/workflows/dsh-mem0-checks.yml' + - '.github/workflows/ci-gate.yml' n8n_nodes_mem0: - 'integrations/n8n-nodes-mem0/**' - '.github/workflows/n8n-nodes-mem0-checks.yml' @@ -171,6 +176,13 @@ jobs: uses: ./.github/workflows/pi-agent-plugin-checks.yml secrets: inherit + dsh-mem0: + name: DeepSeek Harness Plugin + needs: changes + if: needs.changes.outputs.dsh_mem0 == 'true' + uses: ./.github/workflows/dsh-mem0-checks.yml + secrets: inherit + n8n-nodes-mem0: name: n8n Node needs: changes @@ -227,6 +239,7 @@ jobs: - mem0-plugin - opencode-plugin - pi-agent-plugin + - dsh-mem0 - n8n-nodes-mem0 - zapier-mem0 - strands-mem0 diff --git a/.github/workflows/dsh-mem0-cd.yml b/.github/workflows/dsh-mem0-cd.yml new file mode 100644 index 000000000..68021fc9d --- /dev/null +++ b/.github/workflows/dsh-mem0-cd.yml @@ -0,0 +1,60 @@ +name: Publish @mem0/dsh-mem0 📦 to npm + +# Dispatched by release.yml (Release Router) when a release tagged +# dsh-mem0-v* is published. Can also be dispatched manually to re-publish +# a tag. +on: + workflow_dispatch: + inputs: + tag: + description: 'Release tag to build and publish (e.g. dsh-mem0-v0.1.1)' + required: true + type: string + prerelease: + description: 'Publish under the version preid dist-tag instead of latest' + required: false + type: boolean + default: false + +jobs: + build-n-publish: + name: Build and publish @mem0/dsh-mem0 📦 to npm + if: startsWith(inputs.tag, 'dsh-mem0-v') + runs-on: ubuntu-latest + permissions: + id-token: write + defaults: + run: + working-directory: integrations/dsh-mem0 + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ inputs.tag }} + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + registry-url: 'https://registry.npmjs.org' + cache: 'pnpm' + cache-dependency-path: integrations/dsh-mem0/pnpm-lock.yaml + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build + run: pnpm build + + - name: Publish to npm + run: | + if [ "${{ inputs.prerelease }}" = "true" ]; then + PREID=$(node -p "require('./package.json').version.split('-')[1].split('.')[0]") + npx npm@latest publish --provenance --access public --tag "$PREID" + else + npx npm@latest publish --provenance --access public + fi diff --git a/.github/workflows/dsh-mem0-checks.yml b/.github/workflows/dsh-mem0-checks.yml new file mode 100644 index 000000000..a81997982 --- /dev/null +++ b/.github/workflows/dsh-mem0-checks.yml @@ -0,0 +1,90 @@ +name: dsh-mem0 checks + +# On PRs this is invoked by ci-gate.yml (the single required check); +# push-to-main and manual runs remain standalone. +on: + workflow_dispatch: + push: + branches: [main] + paths: + - 'integrations/dsh-mem0/**' + - '.github/workflows/dsh-mem0-checks.yml' + workflow_call: + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + cache-dependency-path: integrations/dsh-mem0/pnpm-lock.yaml + + - name: Install dependencies + run: cd integrations/dsh-mem0 && pnpm install --frozen-lockfile + + - name: Type check + run: cd integrations/dsh-mem0 && pnpm exec tsc --noEmit + + test: + runs-on: ubuntu-latest + strategy: + matrix: + node-version: [20, 22] + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Setup Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v4 + with: + node-version: ${{ matrix.node-version }} + cache: 'pnpm' + cache-dependency-path: integrations/dsh-mem0/pnpm-lock.yaml + + - name: Install dependencies + run: cd integrations/dsh-mem0 && pnpm install --frozen-lockfile + + - name: Run tests + run: cd integrations/dsh-mem0 && pnpm exec vitest run + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install pnpm + uses: pnpm/action-setup@v4 + with: + version: 9 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'pnpm' + cache-dependency-path: integrations/dsh-mem0/pnpm-lock.yaml + + - name: Install dependencies + run: cd integrations/dsh-mem0 && pnpm install --frozen-lockfile + + - name: Build + run: cd integrations/dsh-mem0 && pnpm build + + - name: Verify dist output exists + run: | + test -f integrations/dsh-mem0/dist/index.js || (echo "Build output missing: dist/index.js" && exit 1) + test -f integrations/dsh-mem0/dist/index.d.ts || (echo "Build output missing: dist/index.d.ts" && exit 1) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 423a4542b..50e76ea5e 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -45,6 +45,7 @@ jobs: openclaw-v*) workflow="openclaw-cd.yml" ;; opencode-v*) workflow="opencode-plugin-cd.yml" ;; pi-agent-v*) workflow="pi-agent-plugin-cd.yml" ;; + dsh-mem0-v*) workflow="dsh-mem0-cd.yml" ;; n8n-nodes-mem0-v*) workflow="n8n-nodes-mem0-cd.yml" ;; strands-mem0-v*) workflow="strands-mem0-cd.yml" ;; v*) workflow="cd.yml" ;; diff --git a/.gitignore b/.gitignore index 568da3757..133f6cae2 100644 --- a/.gitignore +++ b/.gitignore @@ -191,3 +191,7 @@ qdrant_storage/ testing.ipynb .weave/ + +# TypeScript incremental build info and local, uncommitted e2e scripts (used by the integrations, e.g. integrations/dsh-mem0) +*.tsbuildinfo +*.local.mjs diff --git a/docs/docs.json b/docs/docs.json index 5e7aa8c2b..1b914e263 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -382,7 +382,8 @@ "pages": [ "integrations/openclaw", "integrations/hermes", - "integrations/pi-agent" + "integrations/pi-agent", + "integrations/dsh-mem0" ] } ] diff --git a/docs/integrations/dsh-mem0.mdx b/docs/integrations/dsh-mem0.mdx new file mode 100644 index 000000000..40d63ced1 --- /dev/null +++ b/docs/integrations/dsh-mem0.mdx @@ -0,0 +1,89 @@ +--- +title: DeepSeek Harness +description: "Add persistent Mem0 memory to the DeepSeek Harness (Cordis) agent with two native tools: search and add." +--- + +Add persistent memory to the [**DeepSeek Harness**](https://github.com/deepseek-ai/deepseek-harness) with `@mem0/dsh-mem0`. The Harness agent forgets everything between sessions. This plugin gives it two Mem0-backed tools so recall and writes persist across runs, sharing the same memory bank you already use from Claude Code, Codex, and other agents. + +## Overview + +The plugin registers two agent-callable tools: + +| Tool | Does | +|---|---| +| `search_memory` | Recall facts from Mem0 relevant to a query | +| `add_memory` | Store a fact in Mem0 for future sessions | + +Unlike file-based memory plugins, Mem0 is a managed backend: server-side extraction, semantic dedup, and conflict resolution, with the same memory reusable across every agent you connect. + +## How it works + +A Cordis plugin is a module exporting `apply(ctx, config)`. This one declares `inject = ['tools']` so it waits for the harness tool registry, then registers the two tools via `ctx.tools.register(...)`. When the plugin unmounts, the tools are removed automatically (Cordis revertible effects). + +## Prerequisites + +1. A Mem0 Platform account and API key: + - Sign up at app.mem0.ai + - Get your API key (starts with `m0-`) + +2. The DeepSeek Harness installed. + +3. Your API key exported in your shell: + + +```bash zsh +echo 'export MEM0_API_KEY="m0-your-api-key"' >> ~/.zshrc +source ~/.zshrc +``` + +```bash bash +echo 'export MEM0_API_KEY="m0-your-api-key"' >> ~/.bashrc +source ~/.bashrc +``` + + +## Try it locally + +1. Build the plugin: + ```sh + cd integrations/dsh-mem0 + pnpm install + pnpm build + ``` + +2. Point the Harness at it. Copy `cordis.example.yml`, set the absolute path to `dist/index.js` and your `userId`, then load it: + ```sh + pnpm dsh web --patch ./integrations/dsh-mem0/cordis.example.yml + ``` + +3. Open the web UI and ask the agent to remember something, then recall it in a later turn. + +The `cordis.yml` entry looks like this: + +```yaml +- name: "@deepseek-ai/dsh-system-prompt" +- name: "@deepseek-ai/dsh-tools" +- insert: + - id: mem0 + name: "/absolute/path/to/integrations/dsh-mem0/dist/index.js" + config: + # apiKey is read from MEM0_API_KEY when omitted here. + userId: "your-user-id" + # host: "https://your-onprem.mem0.ai" # optional: Platform on-prem / dedicated base URL +``` + +For a Mem0 Platform on-prem or dedicated deployment, point `config.host` at that base URL (defaults to `api.mem0.ai`). `host` is a Platform base-URL override, not a switch to self-hosted Mem0 OSS. + +## Configuration + +| Field | Required | Default | Notes | +|---|---|---|---| +| `apiKey` | no | `$MEM0_API_KEY` | Mem0 platform API key | +| `userId` | yes | | Default entity that owns the memories | +| `host` | no | `api.mem0.ai` | Platform base URL (on-prem / dedicated) | + +Both tools also accept optional per-call `userId`, `agentId`, and `runId` params so a single install can partition memory by entity, agent, or session; when omitted they fall back to the configured `userId`. + +## Telemetry + +Writes are tagged `source="DEEPSEEK_HARNESS"` so Mem0's backend can attribute usage to this integration. diff --git a/docs/llms.txt b/docs/llms.txt index 83b2c644a..45087e438 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -257,6 +257,7 @@ If the user is on a pre-current major (Python < 2, TS < 3, or a Platform call st - [ChatDev](https://docs.mem0.ai/integrations/chatdev) [Platform]: Use when the user is on ChatDev. - [Hermes](https://docs.mem0.ai/integrations/hermes) [Both]: Use when the user is on Hermes. - [Pi Agent](https://docs.mem0.ai/integrations/pi-agent) [Platform]: Use when adding persistent memory to Pi Agent with the Mem0 plugin. +- [DeepSeek Harness](https://docs.mem0.ai/integrations/dsh-mem0) [Platform]: Use when adding persistent memory to the DeepSeek Harness (Cordis) agent via the Mem0 plugin. - [OpenAI Agents SDK](https://docs.mem0.ai/integrations/openai-agents-sdk) [Platform]: Use when the user is on the OpenAI Agents SDK. - [Google AI ADK](https://docs.mem0.ai/integrations/google-ai-adk) [Platform]: Use when the user is on Google's Agent Development Kit. - [Mastra](https://docs.mem0.ai/integrations/mastra) [Platform]: Use when the user is on Mastra (TypeScript). diff --git a/integrations/AGENTS.md b/integrations/AGENTS.md index cca1ed43c..71f0211dd 100644 --- a/integrations/AGENTS.md +++ b/integrations/AGENTS.md @@ -9,6 +9,7 @@ Agent and editor integrations. Each subdirectory is self-contained: its own `pac | `mem0-plugin/` | Claude Code / Cursor / Codex plugin | none | none | pytest | | `mem0-plugin/.opencode-plugin/` | `@mem0/opencode-plugin` | Bun | none | tsc type-check | | `pi-agent-plugin/` | `@mem0/pi-agent-plugin` | tsup | none | vitest | +| `dsh-mem0/` | `@mem0/dsh-mem0` | tsup (ESM) | none | vitest | | `n8n-nodes-mem0/` | `@mem0/n8n-nodes-mem0` | tsc | ESLint (n8n-nodes-base) | none | | `zapier-mem0/` | `@mem0/zapier` | tsc | none | offline unit tests + `zapier validate` | | `strands-mem0/` | `strands-mem0` (PyPI) | hatch | Ruff + mypy | pytest | @@ -40,7 +41,7 @@ Run the type check after every TypeScript change: `pnpm run typecheck` or `tsc - - **`vercel-ai-sdk/`** wraps the Vercel AI SDK through a `createMem0` provider. Integrations for AI-SDK repos go through this wrapper, not raw `MemoryClient`. - **`mem0-plugin/`** connects Claude Code, Cursor, and Codex to the MCP server at `mcp.mem0.ai` and installs lifecycle hooks for automatic memory capture. Exposes 9 MCP tools: `add_memory`, `search_memories`, `get_memories`, `get_memory`, `update_memory`, `delete_memory`, `delete_all_memories`, `delete_entities`, `list_entities`. -- **`openclaw/`**, **`pi-agent-plugin/`** are editor and agent plugins with the same shape. +- **`openclaw/`**, **`pi-agent-plugin/`**, **`dsh-mem0/`** are editor and agent plugins with the same shape. `dsh-mem0/` registers Mem0 search/add tools as a native DeepSeek Harness (Cordis) plugin. - **`n8n-nodes-mem0/`** is an n8n community node: add, search, get, update, delete. - **`zapier-mem0/`** is a Zapier Platform CLI app: add, search, get, delete. It deploys to Zapier, not npm, so it is **not** in the release router. Deploy it with `gh workflow run zapier-mem0-cd.yml --ref main` (needs the `ZAPIER_DEPLOY_KEY` secret). - **`strands-mem0/`** is a native Strands `MemoryStore` (Python, published to PyPI as `strands-mem0`). It plugs into the Strands `MemoryManager` for automatic recall and server-side extraction, over the hosted Mem0 platform or self-hosted Mem0 OSS. The package lives under `strands-mem0/python/`. diff --git a/integrations/dsh-mem0/.npmrc b/integrations/dsh-mem0/.npmrc new file mode 100644 index 000000000..d50c9734c --- /dev/null +++ b/integrations/dsh-mem0/.npmrc @@ -0,0 +1,7 @@ +# The DeepSeek Harness SDK (@deepseek-ai/dsh-tools) declares the rest of the +# harness runtime (dsh-llm, dsh-agent, dsh-scope, ...) as peer dependencies. +# The host harness provides those at runtime; this adapter only needs cordis + +# dsh-tools to build and type-check. Auto-installing the full peer graph pulls +# unpublished internal packages (e.g. dsh-type-meta), so keep it off. +auto-install-peers=false +package-manager-strict-version=false diff --git a/integrations/dsh-mem0/LICENSE b/integrations/dsh-mem0/LICENSE new file mode 100644 index 000000000..d20d5102c --- /dev/null +++ b/integrations/dsh-mem0/LICENSE @@ -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 [2023] [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. diff --git a/integrations/dsh-mem0/README.md b/integrations/dsh-mem0/README.md new file mode 100644 index 000000000..29ea45a7c --- /dev/null +++ b/integrations/dsh-mem0/README.md @@ -0,0 +1,60 @@ +# dsh-mem0 + +[Mem0](https://mem0.ai) long-term memory as a native [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) (Cordis) plugin. + +It gives a Harness agent two memory tools backed by the Mem0 SDK, so recall and writes persist across sessions: + +| Tool | Does | +|---|---| +| `search_memory` | Recall facts from Mem0 relevant to a query | +| `add_memory` | Store a fact in Mem0 for future sessions | + +Unlike the local/file-based memory plugins in the ecosystem, Mem0 is a managed backend: server-side extraction, semantic dedup and conflict resolution, and the same memory bank reusable across Harness, Claude Code, Codex, and other agents. + +## How it works + +A Cordis plugin is a module exporting `apply(ctx, config)`. This one declares `inject = ['tools']` so it waits for the harness tool registry, then registers the two tools via `ctx.tools.register(defineTool(...))`. When the plugin unmounts, the tools are removed automatically (Cordis revertible effects). + +``` +[ mem0ai SDK ] <-- managed memory, owned by Mem0 + | +[ dsh-mem0: apply(ctx) -> ctx.tools.register(...) ] <-- this package + | +[ DeepSeek Harness ] <-- the agent, loaded via cordis.yml +``` + +## Try it locally + +1. Build the plugin: + ```sh + cd integrations/dsh-mem0 + pnpm install + pnpm build + ``` +2. Set your Mem0 key: + ```sh + export MEM0_API_KEY=... + ``` +3. Point Harness at it. Copy `cordis.example.yml`, set the absolute path to `dist/index.js` and your `userId`, then: + ```sh + pnpm dsh web --patch ./integrations/dsh-mem0/cordis.example.yml + ``` +4. Open http://127.0.0.1:3080 and ask the agent to remember something, then recall it in a later turn. + +For a Mem0 Platform on-prem or dedicated deployment, point `config.host` at that base URL (defaults to `api.mem0.ai`). `host` is a Platform base-URL override — it is not a switch to self-hosted Mem0 OSS, whose server exposes a different API surface. + +## Configuration + +| Field | Required | Default | Notes | +|---|---|---|---| +| `apiKey` | no | `$MEM0_API_KEY` | Mem0 platform API key | +| `userId` | yes | | Entity that owns the memories | +| `host` | no | `api.mem0.ai` | Platform base URL (on-prem / dedicated) | + +## Telemetry + +Writes are tagged `source="DEEPSEEK_HARNESS"` so Mem0's backend can attribute usage to this integration. For it to surface by name (rather than bucketing into `OTHERS`), `DEEPSEEK_HARNESS` must be present in the backend's `KNOWN_EVENT_SOURCES` allowlist, a one-line platform change matching the existing `ZAPIER` / `STRANDS` sources. + +## Status + +Developer preview. Tracks the DeepSeek Harness v0.1 plugin API (`@deepseek-ai/cordis`, `@deepseek-ai/dsh-tools`), which is young and moving; pin versions once it stabilizes. Auto-capture (store turns without an explicit tool call) and auto-recall (inject memory into the prompt at assembly) are planned once the harness session/assembly event API is confirmed. diff --git a/integrations/dsh-mem0/cordis.example.yml b/integrations/dsh-mem0/cordis.example.yml new file mode 100644 index 000000000..38dc4aaa5 --- /dev/null +++ b/integrations/dsh-mem0/cordis.example.yml @@ -0,0 +1,19 @@ +# Example DeepSeek Harness config that loads dsh-mem0 alongside the built-in +# tools plugin. Load with: +# +# pnpm dsh web --patch ./integrations/dsh-mem0/cordis.example.yml +# +# The tools plugin (and its system-prompt dependency) must be present, because +# the memory tools contribute schemas the system prompt renders. + +- name: "@deepseek-ai/dsh-system-prompt" +- name: "@deepseek-ai/dsh-tools" +- insert: + - id: mem0 + # Absolute path to the built plugin (run `pnpm build` first), or point + # at src/index.ts when running through tsx during development. + name: "/absolute/path/to/mem0/integrations/dsh-mem0/dist/index.js" + config: + # apiKey is read from the MEM0_API_KEY env var when omitted here. + userId: "your-user-id" + # host: "https://your-onprem.mem0.ai" # optional: Platform on-prem / dedicated base URL diff --git a/integrations/dsh-mem0/package.json b/integrations/dsh-mem0/package.json new file mode 100644 index 000000000..4e9e563f0 --- /dev/null +++ b/integrations/dsh-mem0/package.json @@ -0,0 +1,59 @@ +{ + "name": "@mem0/dsh-mem0", + "version": "0.1.0", + "description": "Mem0 long-term memory as a native DeepSeek Harness (Cordis) plugin.", + "type": "module", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/mem0ai/mem0", + "directory": "integrations/dsh-mem0" + }, + "keywords": [ + "deepseek-harness", + "dsh", + "dsh-plugin", + "cordis", + "mem0", + "memory", + "agent" + ], + "main": "./dist/index.js", + "types": "./dist/index.d.ts", + "exports": { + ".": { + "types": "./dist/index.d.ts", + "import": "./dist/index.js" + } + }, + "publishConfig": { + "access": "public" + }, + "files": [ + "dist", + "src", + "README.md", + "LICENSE" + ], + "scripts": { + "build": "tsup", + "test": "vitest run", + "test:watch": "vitest", + "typecheck": "tsc --noEmit" + }, + "dependencies": { + "mem0ai": "^3.0.7" + }, + "peerDependencies": { + "@deepseek-ai/cordis": "*", + "@deepseek-ai/dsh-tools": "*" + }, + "devDependencies": { + "@deepseek-ai/cordis": "4.0.1", + "@deepseek-ai/dsh-tools": "0.0.1-rc.1", + "@types/node": "^22.15.0", + "tsup": "^8.5.0", + "typescript": "^5.6.0", + "vitest": "^4.1.7" + } +} diff --git a/integrations/dsh-mem0/pnpm-lock.yaml b/integrations/dsh-mem0/pnpm-lock.yaml new file mode 100644 index 000000000..26bc065c8 --- /dev/null +++ b/integrations/dsh-mem0/pnpm-lock.yaml @@ -0,0 +1,2094 @@ +lockfileVersion: '9.0' + +settings: + autoInstallPeers: false + excludeLinksFromLockfile: false + +importers: + + .: + dependencies: + mem0ai: + specifier: ^3.0.7 + version: 3.1.6 + devDependencies: + '@deepseek-ai/cordis': + specifier: 4.0.1 + version: 4.0.1 + '@deepseek-ai/dsh-tools': + specifier: 0.0.1-rc.1 + version: 0.0.1-rc.1(@deepseek-ai/cordis@4.0.1) + '@types/node': + specifier: ^22.15.0 + version: 22.20.1 + tsup: + specifier: ^8.5.0 + version: 8.5.1(postcss@8.5.26)(typescript@5.9.3) + typescript: + specifier: ^5.6.0 + version: 5.9.3 + vitest: + specifier: ^4.1.7 + version: 4.1.11(@types/node@22.20.1)(esbuild@0.27.7) + +packages: + + '@deepseek-ai/cordis@4.0.1': + resolution: {integrity: sha512-YBdskTU2Po1kru3GgcUWUbkTsPMA9LkSQDAY8rBkFJeajdgcQad3QPJZE26JyK99Xb6HaASvoXg2DSUTeN/0Nw==} + hasBin: true + peerDependencies: + '@deepseek-ai/cordis-plugin-include': ^1.0.6 + '@deepseek-ai/cordis-plugin-loader': ^1.0.2 + peerDependenciesMeta: + '@deepseek-ai/cordis-plugin-include': + optional: true + '@deepseek-ai/cordis-plugin-loader': + optional: true + + '@deepseek-ai/cosmokit@1.8.2': + resolution: {integrity: sha512-muBOKtSrUKU5m/xpq8ZXWL6hQ/jgd4PhU2PqH97bcxIiLEJfNwZOGQEx4t/aS/GgxRAR+ra9pMHPMtTHU4sqqA==} + + '@deepseek-ai/dsh-tools@0.0.1-rc.1': + resolution: {integrity: sha512-mrtq+UXc6UVidrGo4GM8RAH0Gqc4iFOmtPeQAz59Phhjl3EmzkFI0Mo6dQlHQamrc2J9jppPtGtUzW9qHmwxDQ==} + peerDependencies: + '@deepseek-ai/cordis': ^4.0.1-rc.1 + '@deepseek-ai/dsh-agent': ^0.0.1-rc.1 + '@deepseek-ai/dsh-code-runtime': ^0.0.1-rc.1 + '@deepseek-ai/dsh-invariants': ^0.0.1-rc.1 + '@deepseek-ai/dsh-llm': ^0.0.1-rc.1 + '@deepseek-ai/dsh-scope': ^0.0.1-rc.1 + '@deepseek-ai/dsh-session': ^0.0.1-rc.1 + '@deepseek-ai/dsh-system-prompt': ^0.0.1-rc.1 + '@deepseek-ai/dsh-user-approval': ^0.0.1-rc.1 + + '@deepseek-ai/schemastery@3.18.1': + resolution: {integrity: sha512-Qn0FCSwCQnpnj6SB31I6i2sIKgKWnkbJM8O0EU91Gv2UsYVvtZTl6IA0sCwk2e2MZf5S8w5hpq9QkeVvK9qwxg==} + + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [aix] + + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} + engines: {node: '>=18'} + cpu: [arm64] + os: [android] + + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} + engines: {node: '>=18'} + cpu: [arm] + os: [android] + + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} + engines: {node: '>=18'} + cpu: [x64] + os: [android] + + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [darwin] + + '@esbuild/darwin-x64@0.27.7': + resolution: {integrity: sha512-rYnXrKcXuT7Z+WL5K980jVFdvVKhCHhUwid+dDYQpH+qu+TefcomiMAJpIiC2EM3Rjtq0sO3StMV/+3w3MyyqQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [darwin] + + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [freebsd] + + '@esbuild/freebsd-x64@0.27.7': + resolution: {integrity: sha512-jOBDK5XEjA4m5IJK3bpAQF9/Lelu/Z9ZcdhTRLf4cajlB+8VEhFFRjWgfy3M1O4rO2GQ/b2dLwCUGpiF/eATNQ==} + engines: {node: '>=18'} + cpu: [x64] + os: [freebsd] + + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [linux] + + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} + engines: {node: '>=18'} + cpu: [arm] + os: [linux] + + '@esbuild/linux-ia32@0.27.7': + resolution: {integrity: sha512-GA48aKNkyQDbd3KtkplYWT102C5sn/EZTY4XROkxONgruHPU72l+gW+FfF8tf2cFjeHaRbWpOYa/uRBz/Xq1Pg==} + engines: {node: '>=18'} + cpu: [ia32] + os: [linux] + + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} + engines: {node: '>=18'} + cpu: [loong64] + os: [linux] + + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} + engines: {node: '>=18'} + cpu: [mips64el] + os: [linux] + + '@esbuild/linux-ppc64@0.27.7': + resolution: {integrity: sha512-gRsL4x6wsGHGRqhtI+ifpN/vpOFTQtnbsupUF5R5YTAg+y/lKelYR1hXbnBdzDjGbMYjVJLJTd2OFmMewAgwlQ==} + engines: {node: '>=18'} + cpu: [ppc64] + os: [linux] + + '@esbuild/linux-riscv64@0.27.7': + resolution: {integrity: sha512-hL25LbxO1QOngGzu2U5xeXtxXcW+/GvMN3ejANqXkxZ/opySAZMrc+9LY/WyjAan41unrR3YrmtTsUpwT66InQ==} + engines: {node: '>=18'} + cpu: [riscv64] + os: [linux] + + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} + engines: {node: '>=18'} + cpu: [s390x] + os: [linux] + + '@esbuild/linux-x64@0.27.7': + resolution: {integrity: sha512-hzznmADPt+OmsYzw1EE33ccA+HPdIqiCRq7cQeL1Jlq2gb1+OyWBkMCrYGBJ+sxVzve2ZJEVeePbLM2iEIZSxA==} + engines: {node: '>=18'} + cpu: [x64] + os: [linux] + + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} + engines: {node: '>=18'} + cpu: [arm64] + os: [netbsd] + + '@esbuild/netbsd-x64@0.27.7': + resolution: {integrity: sha512-OfatkLojr6U+WN5EDYuoQhtM+1xco+/6FSzJJnuWiUw5eVcicbyK3dq5EeV/QHT1uy6GoDhGbFpprUiHUYggrw==} + engines: {node: '>=18'} + cpu: [x64] + os: [netbsd] + + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openbsd] + + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} + engines: {node: '>=18'} + cpu: [x64] + os: [openbsd] + + '@esbuild/openharmony-arm64@0.27.7': + resolution: {integrity: sha512-+KrvYb/C8zA9CU/g0sR6w2RBw7IGc5J2BPnc3dYc5VJxHCSF1yNMxTV5LQ7GuKteQXZtspjFbiuW5/dOj7H4Yw==} + engines: {node: '>=18'} + cpu: [arm64] + os: [openharmony] + + '@esbuild/sunos-x64@0.27.7': + resolution: {integrity: sha512-ikktIhFBzQNt/QDyOL580ti9+5mL/YZeUPKU2ivGtGjdTYoqz6jObj6nOMfhASpS4GU4Q/Clh1QtxWAvcYKamA==} + engines: {node: '>=18'} + cpu: [x64] + os: [sunos] + + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} + engines: {node: '>=18'} + cpu: [arm64] + os: [win32] + + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} + engines: {node: '>=18'} + cpu: [ia32] + os: [win32] + + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} + engines: {node: '>=18'} + cpu: [x64] + os: [win32] + + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/resolve-uri@3.1.2': + resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} + engines: {node: '>=6.0.0'} + + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} + + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + resolution: {integrity: sha512-oTXEIha4SsuXdTA4Iyskj0kpdx2yVXdhd75c2v3xGrHFfVMsbhTPZU/nMPL4sWKo4pBHm3aucLaqGlF696dTyQ==} + engines: {node: ^22.20 || ^24.12 || >=25} + cpu: [x64] + os: [linux] + + '@oxc-project/types@0.146.0': + resolution: {integrity: sha512-XC0QsnnhVe7sLIWmYmdPw7x5P0h4W8vUU3Nv1ySgWXtvCz8NizoAEpGXA0sOYoJQV2Rl13LgURAHQ5cI5ILCSA==} + + '@rolldown/binding-android-arm-eabi@1.2.5': + resolution: {integrity: sha512-DLe/i+l8ynIBY7XEQ191TeZvCoowIGa18R+dIV30GW7DiOtp74i/xX8hs8GUjW5ARV7VZuie3d6AumSmCwbeRA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [android] + + '@rolldown/binding-android-arm64@1.2.5': + resolution: {integrity: sha512-zXcwKlQApYAOELHd8PwKDFkagYF9Wy4e0RJ+0qnzl9Pjnpj75TEG8ufv40p2J7kCEfwZAsNiuzRIyNNMWT38ig==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [android] + + '@rolldown/binding-darwin-arm64@1.2.5': + resolution: {integrity: sha512-dK4QakI42nzWgJT5sm4y4y/O//D4OxM75/cH28RLV+nzIN9AY+YsbuUVrUTjlLjXR6vpyxFbSsbmNuJ6BP9sww==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [darwin] + + '@rolldown/binding-darwin-x64@1.2.5': + resolution: {integrity: sha512-fqSALaUu1Wjd1nK2uW2kJDWdLCc8lx1IcY+MTY26Aurfdx19anlzhqXOgCFbBFQnlFDTn4TC1/7Nz4Bl2mLP3A==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [darwin] + + '@rolldown/binding-freebsd-x64@1.2.5': + resolution: {integrity: sha512-/vCnNxlkxs9tKxNDcyWUePpJ/PgTzxIaVhoM5SmG8UV+GR/IcPam4VYxi7GIMo7PSDuNqlJqvprqii9NqqVCMw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [freebsd] + + '@rolldown/binding-linux-arm-gnueabihf@1.2.5': + resolution: {integrity: sha512-abk0NLA519LxRCszmbE0jYKuQ9YPocOXTiOXOo6Yr+YAT95VH+PtqYAjOJvGKt3viEd/x4qzabAlwd5bHOOARg==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm] + os: [linux] + + '@rolldown/binding-linux-arm64-gnu@1.2.5': + resolution: {integrity: sha512-Y7eALiJ8lr0M2HH103Js+g7V34wf6snlpZLAsHI90uLhr3PVlNsbFVAXJC9d/V6BnPyKtpSwI+NcB/RLxsQxuA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-arm64-musl@1.2.5': + resolution: {integrity: sha512-xMvZgnbZg4YVnR/AX2b3oOPDTFYJvUVaJg5FedA/LuvexAtXibZQej4cnTkw3rjsJ/ggUROB64TdtETiim+FYA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [linux] + + '@rolldown/binding-linux-ppc64-gnu@1.2.5': + resolution: {integrity: sha512-GRjeqTUDHTo5GwntsLaAMcBahG3nlpjftXWZLN73HiYQlhwEowvarFgQnRnQZtIp4keXX7quXFbG38uPZBa2EA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [ppc64] + os: [linux] + + '@rolldown/binding-linux-s390x-gnu@1.2.5': + resolution: {integrity: sha512-vLNTR45F2Uwc8AufkNXPmB4VliaXs+FvcheEogIzOXzO4l+LzieXF5A/TWxLy5HtqpsRCHUfd0lPVrrdgXdLHQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [s390x] + os: [linux] + + '@rolldown/binding-linux-x64-gnu@1.2.5': + resolution: {integrity: sha512-Mgj59/HTuYeK9Gz2MA+mBWKnHsAgkBSec15ZMb1st3oIfFbX7gCjOae7GydHhzcyQi9Z/7M1QuN9bR3oFqF0jQ==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-linux-x64-musl@1.2.5': + resolution: {integrity: sha512-mY8AP0/ichsbhAxGnLa3d3+MwV0EfgrPND2bplI3Ym8T6R2pJ0N87bvrKVwNXmdy3jnr6eQBecdqx/HMknBmpA==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [linux] + + '@rolldown/binding-openharmony-arm64@1.2.5': + resolution: {integrity: sha512-8SLssA2oweAxyRgDp789ACfRb/3P+zNRJpzZxSizxF9m8NUDQ4+3xjo8ttjhVGGw6Qxb70oZiEtIjaKikCO7Yw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [openharmony] + + '@rolldown/binding-win32-arm64-msvc@1.2.5': + resolution: {integrity: sha512-vGbruD5zquhoc8D9SViXgN2FBJtNdTyQ4DtG+SWiEGlJiAzoKcZ2xp+xuXCffhubVdt0NJlTZqkeRuERy7g8Cw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [arm64] + os: [win32] + + '@rolldown/binding-win32-x64-msvc@1.2.5': + resolution: {integrity: sha512-e/SXpgISz+IoqVcSSI0rx/d/he8zqLex+/rCWpnHpmVfmPIUjag9H6P7zotf0gJHwPUhQxZ/mF8tr6acebT9yw==} + engines: {node: ^20.19.0 || >=22.12.0} + cpu: [x64] + os: [win32] + + '@rolldown/pluginutils@1.0.1': + resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} + + '@rollup/rollup-android-arm-eabi@4.62.5': + resolution: {integrity: sha512-jfkGfTwhQpsiSckPF8r9bU3pn3vyd72NlWaO+TgEO6WPSDnUhXzrNYCHBMOYj0ACaUgjm6eERLF+XV9a6RstoA==} + cpu: [arm] + os: [android] + + '@rollup/rollup-android-arm64@4.62.5': + resolution: {integrity: sha512-oGVqyQlxnrz9/ty89oHpU857VUHEl5/Xu4R2lS+aivCTrNnSsbiENzTnNaBsjxH0CNWGPhzHArOLFwo+oKXveA==} + cpu: [arm64] + os: [android] + + '@rollup/rollup-darwin-arm64@4.62.5': + resolution: {integrity: sha512-bW7B8xMEq8n99Q3ieEcPRGuphurdZAaFzQc9Efyyw3FL6DZO6pMy9xhdN+kBoD7Sy05xNXSr4OyPPnpkYriS/A==} + cpu: [arm64] + os: [darwin] + + '@rollup/rollup-darwin-x64@4.62.5': + resolution: {integrity: sha512-YSwBS86QeHOGlrxJ1PSOIZSkzRL/JmKeunhc+lV6M1a6En8QuVCD/T/qIA0J4Gd2Y86RIOBYrLcOUtqGh9+/1w==} + cpu: [x64] + os: [darwin] + + '@rollup/rollup-freebsd-arm64@4.62.5': + resolution: {integrity: sha512-2fST8lILgl7cKbme/1KDdPCmbXbG+gqoV3bHp19L0ypX/3akYMBVdOunPleRCwonoLnXOZ/0F+Mt/v8POFmfcQ==} + cpu: [arm64] + os: [freebsd] + + '@rollup/rollup-freebsd-x64@4.62.5': + resolution: {integrity: sha512-cpIxQCP9J+EVad0a6LO1kY3ZGODlk80VlI+2I96B8xMcdHZ4pLVhfQ49JFpYqjPF91FFkQWftf57YlDcTiw9yQ==} + cpu: [x64] + os: [freebsd] + + '@rollup/rollup-linux-arm-gnueabihf@4.62.5': + resolution: {integrity: sha512-r9fGh3eFs3e/udWh5ZjXQtxiYK/xoFxQaYR/cELxac/Udkl5Th+IsFm0CX3Kl9hmUH/we7EoMpjJgeQNnE0+IA==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm-musleabihf@4.62.5': + resolution: {integrity: sha512-xdvFdp7OM6KLJviJT2g/YuRSUjnZgGHk4RNgwIbN7X6cPugOucV60DdHXWzsBVCUdrGb6qSXnJQrrAKMmQuj3Q==} + cpu: [arm] + os: [linux] + + '@rollup/rollup-linux-arm64-gnu@4.62.5': + resolution: {integrity: sha512-rRqILAndyzHzP7T9NFQrq+4HFWNhqkqkKur7eiBpfLmz01PO0JKx5Vchu3YllE4YXI/Ftgq/szrDWg5GJ0mI8g==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-arm64-musl@4.62.5': + resolution: {integrity: sha512-Gf4X3qVMucayUvux6aXXPgXovocSFUC0rrffDuPI/S2nHhNMhjcZxsrAFYCOF350PRreW1XwzFj3CT/3bKsWCw==} + cpu: [arm64] + os: [linux] + + '@rollup/rollup-linux-loong64-gnu@4.62.5': + resolution: {integrity: sha512-+s5qA0TNM0qm8PK/a5gt/1Hpx+NV08uSuCncvhziIlQzT6AEV2fnUQo7eBtFTFO0nA9scauvoR2HusfXmQnO4w==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-loong64-musl@4.62.5': + resolution: {integrity: sha512-ybb6QvWwWJCbBWqERpc8K3pYVGIrXlG8MEQ8IIuJY6Y9KdHQxoFoNyfkAOtKn1VHu3KuLidXvwrvGR1mEjeWCw==} + cpu: [loong64] + os: [linux] + + '@rollup/rollup-linux-ppc64-gnu@4.62.5': + resolution: {integrity: sha512-nZb1DtnOyhCmYvsC8A2CwOkopVg+IS1+fPUa7rMOAXtNw5+lLCLLPqd6XAiNrGtoQKsbvIBOwsHnBH/3wnb4HQ==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-ppc64-musl@4.62.5': + resolution: {integrity: sha512-yMbj63Sp89ryrXLWyz+sy+fYD2HpOnMCLGbe4Oa1smclFSUukdtD/BgdiHaAetJNb74URD8U4hM+qG5KVzMEkg==} + cpu: [ppc64] + os: [linux] + + '@rollup/rollup-linux-riscv64-gnu@4.62.5': + resolution: {integrity: sha512-mhoan3OJw2kYV/e1jtIdmvUZgyBFeA6zGWsOswmR0Tg19TQbowZuR+JMLID6spbbBN7Zee2ejrgmy3+FxGrIdA==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-riscv64-musl@4.62.5': + resolution: {integrity: sha512-5ZTLmjWbb1VZdjuyhe83K/8QO0/h11midQCBP+X5OYn32ra7eOBoM0ZqtaY4nkgNsYgmdVhMYPoyVPTjUpHf3w==} + cpu: [riscv64] + os: [linux] + + '@rollup/rollup-linux-s390x-gnu@4.62.5': + resolution: {integrity: sha512-m53kG+br6PGxOTmgBEM2DHSDs9RVjsyEbUwjJPJGTFm1grWOG8EKJggDCTb60unD4Tjby8fi7/m9XfkEWasVWg==} + cpu: [s390x] + os: [linux] + + '@rollup/rollup-linux-x64-gnu@4.62.5': + resolution: {integrity: sha512-6RHPJR1g/uvdYU8uXBnfq3nlqyZCP82Fr6NHgfGoaIeSh0YEqnX/x6uA9MmJJbnSH7swqX4F+CkGdUF+6doiQA==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-linux-x64-musl@4.62.5': + resolution: {integrity: sha512-xs+OXQtEXgpXT0DmA5+U3qnRZHdCST/5HRQxS8wSPZTUZN/EMWeHuSIod32LQklTBZBV9DyfncKBQ8n5V3eFdw==} + cpu: [x64] + os: [linux] + + '@rollup/rollup-openbsd-x64@4.62.5': + resolution: {integrity: sha512-e7hD+sl3s+mcLQDZ8pbudBVsdG6r5yN4w3LqG2TJ8sQHDpblWj5lrJs/3m01Cvlxbt4x13zu5thLjgypgtkYzw==} + cpu: [x64] + os: [openbsd] + + '@rollup/rollup-openharmony-arm64@4.62.5': + resolution: {integrity: sha512-GiyJaCf+WpMub/17aPcKk27QMl5W6f+KhdPTjlFOn5akH5Wa/DCM9Stdx5cDfmasyKB08MqpVQ1uJE2RkkpbXg==} + cpu: [arm64] + os: [openharmony] + + '@rollup/rollup-win32-arm64-msvc@4.62.5': + resolution: {integrity: sha512-+OQ8U2DdoEfXl8T4Fb18AjmEwbXMerKDKCL8yCPAYhKCEEKoul7rkbeGCBFCbAlaGaa7pmtRTpkAJM2LE/i5FA==} + cpu: [arm64] + os: [win32] + + '@rollup/rollup-win32-ia32-msvc@4.62.5': + resolution: {integrity: sha512-KanvAZrPKbDBFwrgiU9yEVpQoox9QPV1WZOXX7HudJQY+eSlu82CtWxDU8WtuRRvtN5EGkLczkd6Y6DTcvm9wA==} + cpu: [ia32] + os: [win32] + + '@rollup/rollup-win32-x64-gnu@4.62.5': + resolution: {integrity: sha512-1aC3UEWTtRl3RK3VpDJ/Tqk1XI4SLTmXIthAq6wRWo8XiSXJNd+VprJM4/1P4+i6HIaFEFlVi9sTTziniD2tOQ==} + cpu: [x64] + os: [win32] + + '@rollup/rollup-win32-x64-msvc@4.62.5': + resolution: {integrity: sha512-/gDJaRs4gl0NPIwqCz+6PkpmhhjRAD2j6P4rSNHBzUkO3naEx2mIU0pRle1vUNRQ7mE/+8OOeXLTv/J56FKiQg==} + cpu: [x64] + os: [win32] + + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} + + '@types/chai@5.2.3': + resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} + + '@types/deep-eql@4.0.2': + resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} + + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} + + '@types/node-fetch@2.6.13': + resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} + + '@types/node@18.19.130': + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} + + '@types/node@22.20.1': + resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==} + + '@vitest/expect@4.1.11': + resolution: {integrity: sha512-VX2x5vNJXET47KAFzwERI+KRMtTTCSWTfSMKsW7JsUsXV4psq++e3DvZpuTDOpHcxytiDs6p2nhVb2tVDiiUYw==} + + '@vitest/mocker@4.1.11': + resolution: {integrity: sha512-2XJVD55d1o5AZous5CCGKS74g/riOj9odEt2bQpCVZeblHyHdnMeFl4jl0XjU21stf4mbjUkew2eXQZt65g5CQ==} + peerDependencies: + msw: ^2.4.9 + vite: ^6.0.0 || ^7.0.0 || ^8.0.0 + peerDependenciesMeta: + msw: + optional: true + vite: + optional: true + + '@vitest/pretty-format@4.1.11': + resolution: {integrity: sha512-yiZzPbGTS9Sr/JpFl8zHrcIkAofNbFV6k21vIgQN/cY/oxZeXhJv5sc/MBJ5jFKWmWs+oJHw0UXLZjmf931+Vw==} + + '@vitest/runner@4.1.11': + resolution: {integrity: sha512-LztvUgdwMNJMIkj3hQnnxiC2Xy1zNxq928W/xhjCLaNCzqTZOudjwbQf6v9IntZGPw132i2Lq2rgTRZHD3JHNw==} + + '@vitest/snapshot@4.1.11': + resolution: {integrity: sha512-pN7ikn1ON7h8ee4gIAp4AzyK+zBtJPzVbqOgu5LCEh4VaJVbPQcgYQYJIMGQPXVeJJq1fnfazis7a5pFNPahog==} + + '@vitest/spy@4.1.11': + resolution: {integrity: sha512-apNa/prQy2qCeywhnixOHPRCgGNhvg7T4Dapfl1GahLp/R+uhBm5cPyFoNVyqsNd2h1nJxL6BqqdIjiABL60YA==} + + '@vitest/utils@4.1.11': + resolution: {integrity: sha512-zTCVGpyFsGWBhllOyKlTw/vnr6D9qxsfSDyfbyZmTyjHw5N/VuvzHpHoQjm2ZJzn4RJgx5w4r7V0er69CmLgPQ==} + + abort-controller@3.0.0: + resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} + engines: {node: '>=6.5'} + + acorn@8.18.0: + resolution: {integrity: sha512-lGq+9yr1/GuAWaVYIHRjvvySG5/4VfKIvC8EWxStPdcDh/Ka7FG3twP6v4d5BkravUilhIAsG4Qj83t02LWUPQ==} + engines: {node: '>=0.4.0'} + hasBin: true + + agent-base@6.0.2: + resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} + engines: {node: '>= 6.0.0'} + + agentkeepalive@4.6.0: + resolution: {integrity: sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ==} + engines: {node: '>= 8.0.0'} + + any-promise@1.3.0: + resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + + assertion-error@2.0.1: + resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} + engines: {node: '>=12'} + + asynckit@0.4.0: + resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} + + axios@1.19.0: + resolution: {integrity: sha512-ht/iuYZXEjFxLH/Hkezgd7m6JKlHHXEUSneaDz8uZe1Gj5QZtCnpyDsckvAiEnT89OEbCLmnte4R4sn7P0EKFw==} + + bundle-require@5.1.0: + resolution: {integrity: sha512-3WrrOuZiyaaZPWiEt4G3+IffISVC9HYlWueJEBWED4ZH4aIAC2PnkdnuRrR94M+w6yGWn4AglWtJtBI8YqvgoA==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + peerDependencies: + esbuild: '>=0.18' + + cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + + call-bind-apply-helpers@1.0.2: + resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} + engines: {node: '>= 0.4'} + + chai@6.2.2: + resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} + engines: {node: '>=18'} + + chokidar@4.0.3: + resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} + engines: {node: '>= 14.16.0'} + + combined-stream@1.0.8: + resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} + engines: {node: '>= 0.8'} + + commander@4.1.1: + resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} + engines: {node: '>= 6'} + + confbox@0.1.8: + resolution: {integrity: sha512-RMtmw0iFkeR4YV+fUOSucriAQNb9g8zFR52MWCtl+cCZOFRNL6zeB395vPzFhEjjn4fMxXudmELnl/KF/WrK6w==} + + consola@3.4.2: + resolution: {integrity: sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==} + engines: {node: ^14.18.0 || >=16.10.0} + + convert-source-map@2.0.0: + resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} + engines: {node: '>=6.0'} + peerDependencies: + supports-color: '*' + peerDependenciesMeta: + supports-color: + optional: true + + delayed-stream@1.0.0: + resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} + engines: {node: '>=0.4.0'} + + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + + dunder-proto@1.0.1: + resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} + engines: {node: '>= 0.4'} + + es-define-property@1.0.1: + resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} + engines: {node: '>= 0.4'} + + es-errors@1.3.0: + resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} + engines: {node: '>= 0.4'} + + es-module-lexer@2.3.2: + resolution: {integrity: sha512-poHGpORABojJJucnV9KbOavETW8lBVnphkW77ER5/BQ5Fz7oXSoCNek7IH3vR5nRjdsEz926ibFYX8KtLQmdyw==} + + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} + engines: {node: '>= 0.4'} + + es-set-tostringtag@2.1.0: + resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} + engines: {node: '>= 0.4'} + + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} + engines: {node: '>=18'} + hasBin: true + + estree-walker@3.0.3: + resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} + + event-target-shim@5.0.1: + resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} + engines: {node: '>=6'} + + expect-type@1.4.0: + resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} + engines: {node: '>=12.0.0'} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true + + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + + follow-redirects@1.16.0: + resolution: {integrity: sha512-y5rN/uOsadFT/JfYwhxRS5R7Qce+g3zG97+JrtFZlC9klX/W5hD7iiLzScI4nZqUS7DNUdhPgw4xI8W2LuXlUw==} + engines: {node: '>=4.0'} + peerDependencies: + debug: '*' + peerDependenciesMeta: + debug: + optional: true + + form-data-encoder@1.7.2: + resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} + + form-data@4.0.6: + resolution: {integrity: sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==} + engines: {node: '>= 6'} + + formdata-node@4.4.1: + resolution: {integrity: sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ==} + engines: {node: '>= 12.20'} + + fsevents@2.3.3: + resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + + function-bind@1.1.2: + resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} + + get-intrinsic@1.3.0: + resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} + engines: {node: '>= 0.4'} + + get-proto@1.0.1: + resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} + engines: {node: '>= 0.4'} + + gopd@1.2.0: + resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} + engines: {node: '>= 0.4'} + + has-symbols@1.1.0: + resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} + engines: {node: '>= 0.4'} + + has-tostringtag@1.0.2: + resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} + engines: {node: '>= 0.4'} + + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} + engines: {node: '>= 0.4'} + + https-proxy-agent@5.0.1: + resolution: {integrity: sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==} + engines: {node: '>= 6'} + + humanize-ms@1.2.1: + resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} + + joycon@3.1.1: + resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} + engines: {node: '>=10'} + + lightningcss-android-arm64@1.33.0: + resolution: {integrity: sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [android] + + lightningcss-darwin-arm64@1.33.0: + resolution: {integrity: sha512-Sciaz8eenNTKn9b3t7+xr0ipTp9YxKQY4npwQ3mrRuL0BAVHBLyZxofhaKBAVtzmtRZ/zTyo0/to4B1uWG/Djg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [darwin] + + lightningcss-darwin-x64@1.33.0: + resolution: {integrity: sha512-Z5UPAxzrjlWNNyGy6i65cJzzvgJ5D3T6wMvs+gWpY9d7qRhANrxqAp6LhxIgZhWEw18RfJTGcRxjuLIBr+m8XQ==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [darwin] + + lightningcss-freebsd-x64@1.33.0: + resolution: {integrity: sha512-QQM/Ti/hQajJwCY+RiWuCZ9sdtI/XQk7nDK5vC8kkdwixezOlDgvDx7+RT+QjK6FcFT4MpsuoBnHIo/O3StRRg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [freebsd] + + lightningcss-linux-arm-gnueabihf@1.33.0: + resolution: {integrity: sha512-N7FVBe6iS24MlM6R/4RBTxGhQheZGs7tiQ9U32UtF75NzP5Q7xWPRqLBCKxlRQRk3rY1jCIPLzx7WzOhuUIRLQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm] + os: [linux] + + lightningcss-linux-arm64-gnu@1.33.0: + resolution: {integrity: sha512-j2v/itmy4HlNxlc6voKXYgBqNi0Ng2LShg4z7GufpEgs05P+2suBVyi9I6YHq5uoVFx9ETin3eCEhLVyXGQnKg==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-arm64-musl@1.33.0: + resolution: {integrity: sha512-yiO5ROMuYQgXbC60yjZU5CYSFZGKXL0HFATXt9mHJn1+zW55oCtMI9NfcVhYLMFDL7gV7oBPon/EmMMGg2OvtQ==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [linux] + + lightningcss-linux-x64-gnu@1.33.0: + resolution: {integrity: sha512-ar+Ju7LmcN0Jo4FpL4hpFybwNG9/3A/Br5KW2n2jyODg3MEZXaDYADdemoNS+BDNfMgKvylJLj4S5tyRActuAg==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-linux-x64-musl@1.33.0: + resolution: {integrity: sha512-RYiYbkokw0trfKqqzfF55lginwEPrD3OJDfTuJzFs1MK6iFnDenaz1fqLLtX4ITG3OktJQXOeTaw1awrBAlZPw==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [linux] + + lightningcss-win32-arm64-msvc@1.33.0: + resolution: {integrity: sha512-1K+MPfLSFVpphzpdbfkhlWk6wBrTObBzS2T6db10PNOZgR9GoVsAWzwNyuhUYYbTp23j+4RrncfujZ4uAzXvwA==} + engines: {node: '>= 12.0.0'} + cpu: [arm64] + os: [win32] + + lightningcss-win32-x64-msvc@1.33.0: + resolution: {integrity: sha512-OlEICDx/Xl0FqSp4bry8zFnCvGpig3Gl4gCquvYwHuqJKEC1+n9NgDniFvqHGmMv1ZkqDJrDqKKSykTDX+ehuA==} + engines: {node: '>= 12.0.0'} + cpu: [x64] + os: [win32] + + lightningcss@1.33.0: + resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==} + engines: {node: '>= 12.0.0'} + + lilconfig@3.1.3: + resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} + engines: {node: '>=14'} + + lines-and-columns@1.2.4: + resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} + + load-tsconfig@0.2.5: + resolution: {integrity: sha512-IXO6OCs9yg8tMKzfPZ1YmheJbZCiEsnBdcB03l0OcfK9prKnJb96siuHCr5Fl37/yo9DnKU+TLpxzTUspw9shg==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + + math-intrinsics@1.1.0: + resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} + engines: {node: '>= 0.4'} + + mem0ai@3.1.6: + resolution: {integrity: sha512-arbotbiNxevHgs/GoVMfqS77SV9yO7F7tUWU9VqU8qKHNzXUXH7CNGk7dKeRpdQESdkooPIpI3E4B6ppc1WTEA==} + engines: {node: '>=18'} + peerDependencies: + '@anthropic-ai/sdk': ^0.40.1 + '@aws-sdk/client-bedrock-runtime': '>=3.0.0 <3.968.0' + '@aws-sdk/client-neptune-graph': '>=3.0.0 <3.968.0' + '@aws-sdk/client-s3vectors': 3.967.0 + '@azure/identity': ^4.0.0 + '@azure/search-documents': ^12.0.0 + '@cloudflare/workers-types': ^4.20250504.0 + '@databricks/sql': ^1.16.0 + '@elastic/elasticsearch': ^9.0.0 + '@google-cloud/aiplatform': ^6.8.0 + '@google/genai': ^1.40.0 + '@huggingface/transformers': ^3.0.0 || ^4.0.0 + '@langchain/core': ^1.1.47 + '@mistralai/mistralai': ^1.5.2 + '@mochow/mochow-sdk-node': ^2.1.5 + '@opensearch-project/opensearch': ^3.5.1 + '@pinecone-database/pinecone': ^8.0.0 + '@qdrant/js-client-rest': ^1.18.0 + '@supabase/supabase-js': ^2.49.1 + '@turbopuffer/turbopuffer': ^2.0.0 + '@types/jest': 29.5.14 + '@types/pg': 8.11.0 + '@upstash/vector': ^1.2.3 + '@zilliz/milvus2-sdk-node': ^2.4.0 || ^3.0.0 + better-sqlite3: ^12.6.2 + cassandra-driver: 4.8.0 + chromadb: ^3.5.0 + cloudflare: ^4.2.0 + cohere-ai: ^7.17.0 || ^8.0.0 + compromise: ^14.0.0 + fastembed: ^2.1.0 + groq-sdk: 0.3.0 + iovalkey: ^0.3.3 + mongodb: ^7.0.0 + mysql2: ^3.0.0 + natural: ^8.0.1 + ollama: ^0.5.14 + oracledb: ^6.5.0 || ^7.0.0 + pg: 8.11.3 + redis: ^4.6.13 + weaviate-client: ^3.0.0 + zeroentropy: ^0.1.0-alpha.10 + peerDependenciesMeta: + '@anthropic-ai/sdk': + optional: true + '@aws-sdk/client-bedrock-runtime': + optional: true + '@aws-sdk/client-neptune-graph': + optional: true + '@aws-sdk/client-s3vectors': + optional: true + '@azure/identity': + optional: true + '@azure/search-documents': + optional: true + '@databricks/sql': + optional: true + '@elastic/elasticsearch': + optional: true + '@google-cloud/aiplatform': + optional: true + '@google/genai': + optional: true + '@huggingface/transformers': + optional: true + '@langchain/core': + optional: true + '@mistralai/mistralai': + optional: true + '@mochow/mochow-sdk-node': + optional: true + '@opensearch-project/opensearch': + optional: true + '@pinecone-database/pinecone': + optional: true + '@qdrant/js-client-rest': + optional: true + '@supabase/supabase-js': + optional: true + '@turbopuffer/turbopuffer': + optional: true + '@upstash/vector': + optional: true + '@zilliz/milvus2-sdk-node': + optional: true + cassandra-driver: + optional: true + chromadb: + optional: true + cloudflare: + optional: true + cohere-ai: + optional: true + fastembed: + optional: true + groq-sdk: + optional: true + iovalkey: + optional: true + mongodb: + optional: true + mysql2: + optional: true + ollama: + optional: true + oracledb: + optional: true + redis: + optional: true + weaviate-client: + optional: true + zeroentropy: + optional: true + + mime-db@1.52.0: + resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} + engines: {node: '>= 0.6'} + + mime-types@2.1.35: + resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} + engines: {node: '>= 0.6'} + + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + + ms@2.1.3: + resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} + + mz@2.7.0: + resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} + + nanoid@3.3.18: + resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + + node-domexception@1.0.0: + resolution: {integrity: sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==} + engines: {node: '>=10.5.0'} + deprecated: Use your platform's native DOMException instead + + node-fetch@2.7.0: + resolution: {integrity: sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==} + engines: {node: 4.x || >=6.0.0} + peerDependencies: + encoding: ^0.1.0 + peerDependenciesMeta: + encoding: + optional: true + + object-assign@4.1.1: + resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} + engines: {node: '>=0.10.0'} + + obug@2.1.4: + resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} + engines: {node: '>=12.20.0'} + + openai@4.104.0: + resolution: {integrity: sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==} + hasBin: true + peerDependencies: + ws: ^8.18.0 + zod: ^3.23.8 + peerDependenciesMeta: + ws: + optional: true + zod: + optional: true + + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} + + picocolors@1.1.1: + resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + + picomatch@4.0.5: + resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} + engines: {node: '>=12'} + + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} + engines: {node: '>= 6'} + + pkg-types@1.3.1: + resolution: {integrity: sha512-/Jm5M4RvtBFVkKWRu2BLUTNP8/M2a+UwuAX+ae4770q1qVGtfjG+WTCupoZixokjmHiry8uI+dlY8KXYV5HVVQ==} + + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} + peerDependencies: + jiti: '>=1.21.0' + postcss: '>=8.0.9' + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + jiti: + optional: true + postcss: + optional: true + tsx: + optional: true + yaml: + optional: true + + postcss@8.5.26: + resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==} + engines: {node: ^10 || ^12 || >=14} + + proxy-from-env@2.1.0: + resolution: {integrity: sha512-cJ+oHTW1VAEa8cJslgmUZrc+sjRKgAKl3Zyse6+PV38hZe/V6Z14TbCuXcan9F9ghlz4QrFr2c92TNF82UkYHA==} + engines: {node: '>=10'} + + readdirp@4.1.2: + resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} + engines: {node: '>= 14.18.0'} + + resolve-from@5.0.0: + resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} + engines: {node: '>=8'} + + rolldown@1.2.5: + resolution: {integrity: sha512-VD2IE5PUG4Oj8zz2VGykiYd5wbnjdIiSsNQb8Qu5B+noEp+A78mu2iVvpp27g8es14Tk9rofNs5Tku9iQCS4fA==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + + rollup@4.62.5: + resolution: {integrity: sha512-/tqMfgP7GPA3PHhCmuiS4vIjrSVhHLgY++i+dhbG462euyAj7FpM4D9uq1X3BgjlqRdpcOrYhcQtfiQLNc8tqw==} + engines: {node: '>=18.0.0', npm: '>=8.0.0'} + hasBin: true + + siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + + source-map-js@1.2.1: + resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} + engines: {node: '>=0.10.0'} + + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} + + stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + + std-env@4.2.0: + resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} + + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} + engines: {node: '>=16 || 14 >=14.17'} + hasBin: true + + thenify-all@1.6.0: + resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} + engines: {node: '>=0.8'} + + thenify@3.3.1: + resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} + + tinybench@2.9.0: + resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} + + tinyexec@0.3.2: + resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} + + tinyexec@1.3.0: + resolution: {integrity: sha512-QKAl9m8gWWGHV8jZcPeym6j+XULi6tOf1mT83WYJ4Lk2ytW/uwAWkrP0uFsdoYMdueVJ0qs26wZ+23xeB4ibNQ==} + engines: {node: '>=18'} + + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + + tinyrainbow@3.1.1: + resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==} + engines: {node: '>=14.0.0'} + + tr46@0.0.3: + resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} + + tree-kill@1.2.2: + resolution: {integrity: sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==} + hasBin: true + + ts-interface-checker@0.1.13: + resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} + + tsup@8.5.1: + resolution: {integrity: sha512-xtgkqwdhpKWr3tKPmCkvYmS9xnQK3m3XgxZHwSUjvfTjp7YfXe5tT3GgWi0F2N+ZSMsOeWeZFh7ZZFg5iPhing==} + engines: {node: '>=18'} + hasBin: true + peerDependencies: + '@microsoft/api-extractor': ^7.36.0 + '@swc/core': ^1 + postcss: ^8.4.12 + typescript: '>=4.5.0' + peerDependenciesMeta: + '@microsoft/api-extractor': + optional: true + '@swc/core': + optional: true + postcss: + optional: true + typescript: + optional: true + + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} + hasBin: true + + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + undici-types@5.26.5: + resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} + + uuid@11.1.1: + resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} + hasBin: true + + vite@8.2.2: + resolution: {integrity: sha512-cFKLV/PRgAUlIRm5WjMjJ86jrftzpqcgH+Us+DS8mI3CDNiH30Whrz8uHL3+MOLPAgqbMBAqWdAHAphOAM+z/Q==} + engines: {node: ^20.19.0 || >=22.12.0} + hasBin: true + peerDependencies: + '@types/node': ^20.19.0 || >=22.12.0 + '@vitejs/devtools': ^0.4.0 || ^0.5.0 + esbuild: ^0.27.0 || ^0.28.0 + jiti: '>=1.21.0' + less: ^4.0.0 + sass: ^1.70.0 + sass-embedded: ^1.70.0 + stylus: '>=0.54.8' + sugarss: ^5.0.0 + terser: ^5.16.0 + tsx: ^4.8.1 + yaml: ^2.4.2 + peerDependenciesMeta: + '@types/node': + optional: true + '@vitejs/devtools': + optional: true + esbuild: + optional: true + jiti: + optional: true + less: + optional: true + sass: + optional: true + sass-embedded: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + tsx: + optional: true + yaml: + optional: true + + vitest@4.1.11: + resolution: {integrity: sha512-fhACrNXUidIbGSBr5FlbuBkO7VWC1ZyLl0DO4CU2DrQoAPxX84Ysxs+HeGQpii5lZWV1Q4gBZTTu49mF+A6Edw==} + engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@opentelemetry/api': ^1.9.0 + '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 + '@vitest/browser-playwright': 4.1.11 + '@vitest/browser-preview': 4.1.11 + '@vitest/browser-webdriverio': 4.1.11 + '@vitest/coverage-istanbul': 4.1.11 + '@vitest/coverage-v8': 4.1.11 + '@vitest/ui': 4.1.11 + happy-dom: '*' + jsdom: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@opentelemetry/api': + optional: true + '@types/node': + optional: true + '@vitest/browser-playwright': + optional: true + '@vitest/browser-preview': + optional: true + '@vitest/browser-webdriverio': + optional: true + '@vitest/coverage-istanbul': + optional: true + '@vitest/coverage-v8': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + + web-streams-polyfill@4.0.0-beta.3: + resolution: {integrity: sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==} + engines: {node: '>= 14'} + + webidl-conversions@3.0.1: + resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} + + whatwg-url@5.0.0: + resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} + + why-is-node-running@2.3.0: + resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} + engines: {node: '>=8'} + hasBin: true + + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + +snapshots: + + '@deepseek-ai/cordis@4.0.1': + dependencies: + '@deepseek-ai/cosmokit': 1.8.2 + '@standard-schema/spec': 1.1.0 + + '@deepseek-ai/cosmokit@1.8.2': {} + + '@deepseek-ai/dsh-tools@0.0.1-rc.1(@deepseek-ai/cordis@4.0.1)': + dependencies: + '@deepseek-ai/cordis': 4.0.1 + '@deepseek-ai/schemastery': 3.18.1 + + '@deepseek-ai/schemastery@3.18.1': + dependencies: + '@deepseek-ai/cosmokit': 1.8.2 + '@standard-schema/spec': 1.1.0 + + '@esbuild/aix-ppc64@0.27.7': + optional: true + + '@esbuild/android-arm64@0.27.7': + optional: true + + '@esbuild/android-arm@0.27.7': + optional: true + + '@esbuild/android-x64@0.27.7': + optional: true + + '@esbuild/darwin-arm64@0.27.7': + optional: true + + '@esbuild/darwin-x64@0.27.7': + optional: true + + '@esbuild/freebsd-arm64@0.27.7': + optional: true + + '@esbuild/freebsd-x64@0.27.7': + optional: true + + '@esbuild/linux-arm64@0.27.7': + optional: true + + '@esbuild/linux-arm@0.27.7': + optional: true + + '@esbuild/linux-ia32@0.27.7': + optional: true + + '@esbuild/linux-loong64@0.27.7': + optional: true + + '@esbuild/linux-mips64el@0.27.7': + optional: true + + '@esbuild/linux-ppc64@0.27.7': + optional: true + + '@esbuild/linux-riscv64@0.27.7': + optional: true + + '@esbuild/linux-s390x@0.27.7': + optional: true + + '@esbuild/linux-x64@0.27.7': + optional: true + + '@esbuild/netbsd-arm64@0.27.7': + optional: true + + '@esbuild/netbsd-x64@0.27.7': + optional: true + + '@esbuild/openbsd-arm64@0.27.7': + optional: true + + '@esbuild/openbsd-x64@0.27.7': + optional: true + + '@esbuild/openharmony-arm64@0.27.7': + optional: true + + '@esbuild/sunos-x64@0.27.7': + optional: true + + '@esbuild/win32-arm64@0.27.7': + optional: true + + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.27.7': + optional: true + + '@jridgewell/gen-mapping@0.3.13': + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/resolve-uri@3.1.2': {} + + '@jridgewell/sourcemap-codec@1.5.5': {} + + '@jridgewell/trace-mapping@0.3.31': + dependencies: + '@jridgewell/resolve-uri': 3.1.2 + '@jridgewell/sourcemap-codec': 1.5.5 + + '@napi-rs/lzma-linux-x64-gnu@1.5.1': + optional: true + + '@oxc-project/types@0.146.0': {} + + '@rolldown/binding-android-arm-eabi@1.2.5': + optional: true + + '@rolldown/binding-android-arm64@1.2.5': + optional: true + + '@rolldown/binding-darwin-arm64@1.2.5': + optional: true + + '@rolldown/binding-darwin-x64@1.2.5': + optional: true + + '@rolldown/binding-freebsd-x64@1.2.5': + optional: true + + '@rolldown/binding-linux-arm-gnueabihf@1.2.5': + optional: true + + '@rolldown/binding-linux-arm64-gnu@1.2.5': + optional: true + + '@rolldown/binding-linux-arm64-musl@1.2.5': + optional: true + + '@rolldown/binding-linux-ppc64-gnu@1.2.5': + optional: true + + '@rolldown/binding-linux-s390x-gnu@1.2.5': + optional: true + + '@rolldown/binding-linux-x64-gnu@1.2.5': + optional: true + + '@rolldown/binding-linux-x64-musl@1.2.5': + optional: true + + '@rolldown/binding-openharmony-arm64@1.2.5': + optional: true + + '@rolldown/binding-win32-arm64-msvc@1.2.5': + optional: true + + '@rolldown/binding-win32-x64-msvc@1.2.5': + optional: true + + '@rolldown/pluginutils@1.0.1': {} + + '@rollup/rollup-android-arm-eabi@4.62.5': + optional: true + + '@rollup/rollup-android-arm64@4.62.5': + optional: true + + '@rollup/rollup-darwin-arm64@4.62.5': + optional: true + + '@rollup/rollup-darwin-x64@4.62.5': + optional: true + + '@rollup/rollup-freebsd-arm64@4.62.5': + optional: true + + '@rollup/rollup-freebsd-x64@4.62.5': + optional: true + + '@rollup/rollup-linux-arm-gnueabihf@4.62.5': + optional: true + + '@rollup/rollup-linux-arm-musleabihf@4.62.5': + optional: true + + '@rollup/rollup-linux-arm64-gnu@4.62.5': + optional: true + + '@rollup/rollup-linux-arm64-musl@4.62.5': + optional: true + + '@rollup/rollup-linux-loong64-gnu@4.62.5': + optional: true + + '@rollup/rollup-linux-loong64-musl@4.62.5': + optional: true + + '@rollup/rollup-linux-ppc64-gnu@4.62.5': + optional: true + + '@rollup/rollup-linux-ppc64-musl@4.62.5': + optional: true + + '@rollup/rollup-linux-riscv64-gnu@4.62.5': + optional: true + + '@rollup/rollup-linux-riscv64-musl@4.62.5': + optional: true + + '@rollup/rollup-linux-s390x-gnu@4.62.5': + optional: true + + '@rollup/rollup-linux-x64-gnu@4.62.5': + optional: true + + '@rollup/rollup-linux-x64-musl@4.62.5': + optional: true + + '@rollup/rollup-openbsd-x64@4.62.5': + optional: true + + '@rollup/rollup-openharmony-arm64@4.62.5': + optional: true + + '@rollup/rollup-win32-arm64-msvc@4.62.5': + optional: true + + '@rollup/rollup-win32-ia32-msvc@4.62.5': + optional: true + + '@rollup/rollup-win32-x64-gnu@4.62.5': + optional: true + + '@rollup/rollup-win32-x64-msvc@4.62.5': + optional: true + + '@standard-schema/spec@1.1.0': {} + + '@types/chai@5.2.3': + dependencies: + '@types/deep-eql': 4.0.2 + assertion-error: 2.0.1 + + '@types/deep-eql@4.0.2': {} + + '@types/estree@1.0.9': {} + + '@types/node-fetch@2.6.13': + dependencies: + '@types/node': 22.20.1 + form-data: 4.0.6 + + '@types/node@18.19.130': + dependencies: + undici-types: 5.26.5 + + '@types/node@22.20.1': + dependencies: + undici-types: 6.21.0 + + '@vitest/expect@4.1.11': + dependencies: + '@standard-schema/spec': 1.1.0 + '@types/chai': 5.2.3 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 + chai: 6.2.2 + tinyrainbow: 3.1.1 + + '@vitest/mocker@4.1.11(vite@8.2.2(@types/node@22.20.1)(esbuild@0.27.7))': + dependencies: + '@vitest/spy': 4.1.11 + estree-walker: 3.0.3 + magic-string: 0.30.21 + optionalDependencies: + vite: 8.2.2(@types/node@22.20.1)(esbuild@0.27.7) + + '@vitest/pretty-format@4.1.11': + dependencies: + tinyrainbow: 3.1.1 + + '@vitest/runner@4.1.11': + dependencies: + '@vitest/utils': 4.1.11 + pathe: 2.0.3 + + '@vitest/snapshot@4.1.11': + dependencies: + '@vitest/pretty-format': 4.1.11 + '@vitest/utils': 4.1.11 + magic-string: 0.30.21 + pathe: 2.0.3 + + '@vitest/spy@4.1.11': {} + + '@vitest/utils@4.1.11': + dependencies: + '@vitest/pretty-format': 4.1.11 + convert-source-map: 2.0.0 + tinyrainbow: 3.1.1 + + abort-controller@3.0.0: + dependencies: + event-target-shim: 5.0.1 + + acorn@8.18.0: {} + + agent-base@6.0.2: + dependencies: + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + agentkeepalive@4.6.0: + dependencies: + humanize-ms: 1.2.1 + + any-promise@1.3.0: {} + + assertion-error@2.0.1: {} + + asynckit@0.4.0: {} + + axios@1.19.0: + dependencies: + follow-redirects: 1.16.0 + form-data: 4.0.6 + https-proxy-agent: 5.0.1 + proxy-from-env: 2.1.0 + transitivePeerDependencies: + - debug + - supports-color + + bundle-require@5.1.0(esbuild@0.27.7): + dependencies: + esbuild: 0.27.7 + load-tsconfig: 0.2.5 + + cac@6.7.14: {} + + call-bind-apply-helpers@1.0.2: + dependencies: + es-errors: 1.3.0 + function-bind: 1.1.2 + + chai@6.2.2: {} + + chokidar@4.0.3: + dependencies: + readdirp: 4.1.2 + + combined-stream@1.0.8: + dependencies: + delayed-stream: 1.0.0 + + commander@4.1.1: {} + + confbox@0.1.8: {} + + consola@3.4.2: {} + + convert-source-map@2.0.0: {} + + debug@4.4.3: + dependencies: + ms: 2.1.3 + + delayed-stream@1.0.0: {} + + detect-libc@2.1.2: {} + + dunder-proto@1.0.1: + dependencies: + call-bind-apply-helpers: 1.0.2 + es-errors: 1.3.0 + gopd: 1.2.0 + + es-define-property@1.0.1: {} + + es-errors@1.3.0: {} + + es-module-lexer@2.3.2: {} + + es-object-atoms@1.1.2: + dependencies: + es-errors: 1.3.0 + + es-set-tostringtag@2.1.0: + dependencies: + es-errors: 1.3.0 + get-intrinsic: 1.3.0 + has-tostringtag: 1.0.2 + hasown: 2.0.4 + + esbuild@0.27.7: + optionalDependencies: + '@esbuild/aix-ppc64': 0.27.7 + '@esbuild/android-arm': 0.27.7 + '@esbuild/android-arm64': 0.27.7 + '@esbuild/android-x64': 0.27.7 + '@esbuild/darwin-arm64': 0.27.7 + '@esbuild/darwin-x64': 0.27.7 + '@esbuild/freebsd-arm64': 0.27.7 + '@esbuild/freebsd-x64': 0.27.7 + '@esbuild/linux-arm': 0.27.7 + '@esbuild/linux-arm64': 0.27.7 + '@esbuild/linux-ia32': 0.27.7 + '@esbuild/linux-loong64': 0.27.7 + '@esbuild/linux-mips64el': 0.27.7 + '@esbuild/linux-ppc64': 0.27.7 + '@esbuild/linux-riscv64': 0.27.7 + '@esbuild/linux-s390x': 0.27.7 + '@esbuild/linux-x64': 0.27.7 + '@esbuild/netbsd-arm64': 0.27.7 + '@esbuild/netbsd-x64': 0.27.7 + '@esbuild/openbsd-arm64': 0.27.7 + '@esbuild/openbsd-x64': 0.27.7 + '@esbuild/openharmony-arm64': 0.27.7 + '@esbuild/sunos-x64': 0.27.7 + '@esbuild/win32-arm64': 0.27.7 + '@esbuild/win32-ia32': 0.27.7 + '@esbuild/win32-x64': 0.27.7 + + estree-walker@3.0.3: + dependencies: + '@types/estree': 1.0.9 + + event-target-shim@5.0.1: {} + + expect-type@1.4.0: {} + + fdir@6.5.0(picomatch@4.0.5): + optionalDependencies: + picomatch: 4.0.5 + + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.21 + mlly: 1.8.2 + rollup: 4.62.5 + + follow-redirects@1.16.0: {} + + form-data-encoder@1.7.2: {} + + form-data@4.0.6: + dependencies: + asynckit: 0.4.0 + combined-stream: 1.0.8 + es-set-tostringtag: 2.1.0 + hasown: 2.0.4 + mime-types: 2.1.35 + + formdata-node@4.4.1: + dependencies: + node-domexception: 1.0.0 + web-streams-polyfill: 4.0.0-beta.3 + + fsevents@2.3.3: + optional: true + + function-bind@1.1.2: {} + + 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.2 + function-bind: 1.1.2 + get-proto: 1.0.1 + gopd: 1.2.0 + has-symbols: 1.1.0 + hasown: 2.0.4 + math-intrinsics: 1.1.0 + + get-proto@1.0.1: + dependencies: + dunder-proto: 1.0.1 + es-object-atoms: 1.1.2 + + gopd@1.2.0: {} + + has-symbols@1.1.0: {} + + has-tostringtag@1.0.2: + dependencies: + has-symbols: 1.1.0 + + hasown@2.0.4: + dependencies: + function-bind: 1.1.2 + + https-proxy-agent@5.0.1: + dependencies: + agent-base: 6.0.2 + debug: 4.4.3 + transitivePeerDependencies: + - supports-color + + humanize-ms@1.2.1: + dependencies: + ms: 2.1.3 + + joycon@3.1.1: {} + + lightningcss-android-arm64@1.33.0: + optional: true + + lightningcss-darwin-arm64@1.33.0: + optional: true + + lightningcss-darwin-x64@1.33.0: + optional: true + + lightningcss-freebsd-x64@1.33.0: + optional: true + + lightningcss-linux-arm-gnueabihf@1.33.0: + optional: true + + lightningcss-linux-arm64-gnu@1.33.0: + optional: true + + lightningcss-linux-arm64-musl@1.33.0: + optional: true + + lightningcss-linux-x64-gnu@1.33.0: + optional: true + + lightningcss-linux-x64-musl@1.33.0: + optional: true + + lightningcss-win32-arm64-msvc@1.33.0: + optional: true + + lightningcss-win32-x64-msvc@1.33.0: + optional: true + + lightningcss@1.33.0: + dependencies: + detect-libc: 2.1.2 + optionalDependencies: + lightningcss-android-arm64: 1.33.0 + lightningcss-darwin-arm64: 1.33.0 + lightningcss-darwin-x64: 1.33.0 + lightningcss-freebsd-x64: 1.33.0 + lightningcss-linux-arm-gnueabihf: 1.33.0 + lightningcss-linux-arm64-gnu: 1.33.0 + lightningcss-linux-arm64-musl: 1.33.0 + lightningcss-linux-x64-gnu: 1.33.0 + lightningcss-linux-x64-musl: 1.33.0 + lightningcss-win32-arm64-msvc: 1.33.0 + lightningcss-win32-x64-msvc: 1.33.0 + + lilconfig@3.1.3: {} + + lines-and-columns@1.2.4: {} + + load-tsconfig@0.2.5: {} + + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + + math-intrinsics@1.1.0: {} + + mem0ai@3.1.6: + dependencies: + axios: 1.19.0 + openai: 4.104.0(zod@3.25.76) + uuid: 11.1.1 + zod: 3.25.76 + transitivePeerDependencies: + - debug + - encoding + - supports-color + - ws + + mime-db@1.52.0: {} + + mime-types@2.1.35: + dependencies: + mime-db: 1.52.0 + + mlly@1.8.2: + dependencies: + acorn: 8.18.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + + ms@2.1.3: {} + + mz@2.7.0: + dependencies: + any-promise: 1.3.0 + object-assign: 4.1.1 + thenify-all: 1.6.0 + + nanoid@3.3.18: {} + + node-domexception@1.0.0: {} + + node-fetch@2.7.0: + dependencies: + whatwg-url: 5.0.0 + + object-assign@4.1.1: {} + + obug@2.1.4: {} + + openai@4.104.0(zod@3.25.76): + dependencies: + '@types/node': 18.19.130 + '@types/node-fetch': 2.6.13 + abort-controller: 3.0.0 + agentkeepalive: 4.6.0 + form-data-encoder: 1.7.2 + formdata-node: 4.4.1 + node-fetch: 2.7.0 + optionalDependencies: + zod: 3.25.76 + transitivePeerDependencies: + - encoding + + pathe@2.0.3: {} + + picocolors@1.1.1: {} + + picomatch@4.0.5: {} + + pirates@4.0.7: {} + + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + + postcss-load-config@6.0.1(postcss@8.5.26): + dependencies: + lilconfig: 3.1.3 + optionalDependencies: + postcss: 8.5.26 + + postcss@8.5.26: + dependencies: + nanoid: 3.3.18 + picocolors: 1.1.1 + source-map-js: 1.2.1 + + proxy-from-env@2.1.0: {} + + readdirp@4.1.2: {} + + resolve-from@5.0.0: {} + + rolldown@1.2.5: + dependencies: + '@oxc-project/types': 0.146.0 + '@rolldown/pluginutils': 1.0.1 + optionalDependencies: + '@rolldown/binding-android-arm-eabi': 1.2.5 + '@rolldown/binding-android-arm64': 1.2.5 + '@rolldown/binding-darwin-arm64': 1.2.5 + '@rolldown/binding-darwin-x64': 1.2.5 + '@rolldown/binding-freebsd-x64': 1.2.5 + '@rolldown/binding-linux-arm-gnueabihf': 1.2.5 + '@rolldown/binding-linux-arm64-gnu': 1.2.5 + '@rolldown/binding-linux-arm64-musl': 1.2.5 + '@rolldown/binding-linux-ppc64-gnu': 1.2.5 + '@rolldown/binding-linux-s390x-gnu': 1.2.5 + '@rolldown/binding-linux-x64-gnu': 1.2.5 + '@rolldown/binding-linux-x64-musl': 1.2.5 + '@rolldown/binding-openharmony-arm64': 1.2.5 + '@rolldown/binding-win32-arm64-msvc': 1.2.5 + '@rolldown/binding-win32-x64-msvc': 1.2.5 + + rollup@4.62.5: + dependencies: + '@types/estree': 1.0.9 + optionalDependencies: + '@napi-rs/lzma-linux-x64-gnu': 1.5.1 + '@rollup/rollup-android-arm-eabi': 4.62.5 + '@rollup/rollup-android-arm64': 4.62.5 + '@rollup/rollup-darwin-arm64': 4.62.5 + '@rollup/rollup-darwin-x64': 4.62.5 + '@rollup/rollup-freebsd-arm64': 4.62.5 + '@rollup/rollup-freebsd-x64': 4.62.5 + '@rollup/rollup-linux-arm-gnueabihf': 4.62.5 + '@rollup/rollup-linux-arm-musleabihf': 4.62.5 + '@rollup/rollup-linux-arm64-gnu': 4.62.5 + '@rollup/rollup-linux-arm64-musl': 4.62.5 + '@rollup/rollup-linux-loong64-gnu': 4.62.5 + '@rollup/rollup-linux-loong64-musl': 4.62.5 + '@rollup/rollup-linux-ppc64-gnu': 4.62.5 + '@rollup/rollup-linux-ppc64-musl': 4.62.5 + '@rollup/rollup-linux-riscv64-gnu': 4.62.5 + '@rollup/rollup-linux-riscv64-musl': 4.62.5 + '@rollup/rollup-linux-s390x-gnu': 4.62.5 + '@rollup/rollup-linux-x64-gnu': 4.62.5 + '@rollup/rollup-linux-x64-musl': 4.62.5 + '@rollup/rollup-openbsd-x64': 4.62.5 + '@rollup/rollup-openharmony-arm64': 4.62.5 + '@rollup/rollup-win32-arm64-msvc': 4.62.5 + '@rollup/rollup-win32-ia32-msvc': 4.62.5 + '@rollup/rollup-win32-x64-gnu': 4.62.5 + '@rollup/rollup-win32-x64-msvc': 4.62.5 + fsevents: 2.3.3 + + siginfo@2.0.0: {} + + source-map-js@1.2.1: {} + + source-map@0.7.6: {} + + stackback@0.0.2: {} + + std-env@4.2.0: {} + + sucrase@3.35.1: + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + commander: 4.1.1 + lines-and-columns: 1.2.4 + mz: 2.7.0 + pirates: 4.0.7 + tinyglobby: 0.2.17 + ts-interface-checker: 0.1.13 + + thenify-all@1.6.0: + dependencies: + thenify: 3.3.1 + + thenify@3.3.1: + dependencies: + any-promise: 1.3.0 + + tinybench@2.9.0: {} + + tinyexec@0.3.2: {} + + tinyexec@1.3.0: {} + + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.5) + picomatch: 4.0.5 + + tinyrainbow@3.1.1: {} + + tr46@0.0.3: {} + + tree-kill@1.2.2: {} + + ts-interface-checker@0.1.13: {} + + tsup@8.5.1(postcss@8.5.26)(typescript@5.9.3): + dependencies: + bundle-require: 5.1.0(esbuild@0.27.7) + cac: 6.7.14 + chokidar: 4.0.3 + consola: 3.4.2 + debug: 4.4.3 + esbuild: 0.27.7 + fix-dts-default-cjs-exports: 1.0.1 + joycon: 3.1.1 + picocolors: 1.1.1 + postcss-load-config: 6.0.1(postcss@8.5.26) + resolve-from: 5.0.0 + rollup: 4.62.5 + source-map: 0.7.6 + sucrase: 3.35.1 + tinyexec: 0.3.2 + tinyglobby: 0.2.17 + tree-kill: 1.2.2 + optionalDependencies: + postcss: 8.5.26 + typescript: 5.9.3 + transitivePeerDependencies: + - jiti + - supports-color + - tsx + - yaml + + typescript@5.9.3: {} + + ufo@1.6.4: {} + + undici-types@5.26.5: {} + + undici-types@6.21.0: {} + + uuid@11.1.1: {} + + vite@8.2.2(@types/node@22.20.1)(esbuild@0.27.7): + dependencies: + lightningcss: 1.33.0 + picomatch: 4.0.5 + postcss: 8.5.26 + rolldown: 1.2.5 + tinyglobby: 0.2.17 + optionalDependencies: + '@types/node': 22.20.1 + esbuild: 0.27.7 + fsevents: 2.3.3 + + vitest@4.1.11(@types/node@22.20.1)(esbuild@0.27.7): + dependencies: + '@vitest/expect': 4.1.11 + '@vitest/mocker': 4.1.11(vite@8.2.2(@types/node@22.20.1)(esbuild@0.27.7)) + '@vitest/pretty-format': 4.1.11 + '@vitest/runner': 4.1.11 + '@vitest/snapshot': 4.1.11 + '@vitest/spy': 4.1.11 + '@vitest/utils': 4.1.11 + es-module-lexer: 2.3.2 + expect-type: 1.4.0 + magic-string: 0.30.21 + obug: 2.1.4 + pathe: 2.0.3 + picomatch: 4.0.5 + std-env: 4.2.0 + tinybench: 2.9.0 + tinyexec: 1.3.0 + tinyglobby: 0.2.17 + tinyrainbow: 3.1.1 + vite: 8.2.2(@types/node@22.20.1)(esbuild@0.27.7) + why-is-node-running: 2.3.0 + optionalDependencies: + '@types/node': 22.20.1 + transitivePeerDependencies: + - '@vitejs/devtools' + - esbuild + - jiti + - less + - msw + - sass + - sass-embedded + - stylus + - sugarss + - terser + - tsx + - yaml + + web-streams-polyfill@4.0.0-beta.3: {} + + webidl-conversions@3.0.1: {} + + whatwg-url@5.0.0: + dependencies: + tr46: 0.0.3 + webidl-conversions: 3.0.1 + + why-is-node-running@2.3.0: + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + + zod@3.25.76: {} diff --git a/integrations/dsh-mem0/src/formatting.ts b/integrations/dsh-mem0/src/formatting.ts new file mode 100644 index 000000000..63296a185 --- /dev/null +++ b/integrations/dsh-mem0/src/formatting.ts @@ -0,0 +1,69 @@ +/** + * Compact, token-cheap rendering of Mem0 results for the model context. + * + * Mirrors the format used by the sibling Mem0 plugins + * (integrations/pi-agent-plugin/src/memory/formatting.ts) so a memory reads the + * same way across every harness: `[category] text (age) [mem0:id]`. Dumping the + * raw search envelope instead would spend most of the tokens on JSON scaffolding. + */ + +export interface MemoryLike { + id: string; + memory?: string; + categories?: string[]; + createdAt?: Date | string; +} + +export function formatAge(date: Date | string): string { + const d = typeof date === "string" ? new Date(date) : date; + const ms = Date.now() - d.getTime(); + const minutes = Math.floor(ms / 60_000); + if (minutes < 60) return `${minutes}m ago`; + const hours = Math.floor(minutes / 60); + if (hours < 24) return `${hours}h ago`; + const days = Math.floor(hours / 24); + return `${days}d ago`; +} + +export function formatMemoryCompact(mem: MemoryLike): string { + const cat = mem.categories?.[0] ?? "uncategorized"; + const age = mem.createdAt ? ` (${formatAge(mem.createdAt)})` : ""; + return `[${cat}] ${mem.memory ?? "(empty)"}${age} [mem0:${mem.id}]`; +} + +export function formatMemoryList(memories: MemoryLike[]): string { + if (memories.length === 0) return "No memories found."; + return memories + .map((m, i) => `${i + 1}. ${formatMemoryCompact(m)}`) + .join("\n"); +} + +/** + * One-line confirmation for a write. + * + * `client.add` hits the async `/v3/memories/add/` endpoint, which returns + * `{ event_id, status: "PENDING" }` — extraction runs server-side *after* the + * call returns, so the extracted memories are not in this response. Report the + * write as queued in that case; only render a list when the backend actually + * returns memories (older / OSS shapes). + */ +export function formatAddResult(result: unknown): string { + const items: MemoryLike[] = Array.isArray(result) + ? (result as MemoryLike[]) + : ((result as { results?: MemoryLike[] } | null)?.results ?? + (result ? [result as MemoryLike] : [])); + + const pending = items.find( + (r) => (r as { status?: string }).status === "PENDING", + ) as { eventId?: string; event_id?: string } | undefined; + if (pending) { + // The SDK camel-cases response keys (event_id -> eventId); accept either. + const id = pending.eventId ?? pending.event_id; + const evt = id ? ` (event ${id})` : ""; + return `Memory queued for background extraction${evt}; it will be searchable shortly.`; + } + + if (items.length === 0) return "Memory stored."; + const noun = items.length === 1 ? "memory" : "memories"; + return `Stored ${items.length} ${noun}:\n${formatMemoryList(items)}`; +} diff --git a/integrations/dsh-mem0/src/index.ts b/integrations/dsh-mem0/src/index.ts new file mode 100644 index 000000000..47f98eaaa --- /dev/null +++ b/integrations/dsh-mem0/src/index.ts @@ -0,0 +1,138 @@ +/** + * dsh-mem0: Mem0 long-term memory as a native DeepSeek Harness (Cordis) plugin. + * + * Registers two agent-callable tools backed by the Mem0 SDK: + * - `search_memory` recalls facts relevant to a query + * - `add_memory` stores a fact for future sessions + * + * A plugin is a Cordis module that exports `apply(ctx, config)`. Declaring + * `inject = ['tools']` holds the plugin until the harness tool registry exists; + * tools registered via `ctx.tools.register(...)` are auto-unregistered when the + * plugin unmounts (Cordis revertible effects). + */ +import type { Context } from "@deepseek-ai/cordis"; +import { defineTool } from "@deepseek-ai/dsh-tools"; +import { MemoryClient } from "mem0ai"; +import { formatMemoryList, formatAddResult } from "./formatting.ts"; +import { truncateOutput } from "./output.ts"; +import { resolveSearchFilters, resolveAddParams } from "./scoping.ts"; + +export const name = "mem0"; +export const inject = ["tools"]; + +// Tags writes so Mem0's backend attributes them to this integration in +// telemetry. The backend keeps recognized values via its KNOWN_EVENT_SOURCES +// allowlist; unknown values bucket into "OTHERS", so "DEEPSEEK_HARNESS" must be +// added to that allowlist for usage to surface by name (a one-line backend PR, +// same pattern as the ZAPIER / STRANDS sources). +const SOURCE = "DEEPSEEK_HARNESS"; + +const DEFAULT_SEARCH_LIMIT = 10; + +export interface Config { + /** Mem0 API key. Defaults to the MEM0_API_KEY env var. */ + apiKey?: string; + /** Default entity that owns the memories (Mem0 user scope). */ + userId: string; + /** Optional Mem0 Platform base-URL override (on-prem / dedicated); defaults to api.mem0.ai. Not a switch to self-hosted OSS. */ + host?: string; +} + +// Both tools return a single text string; the render is identical, so lift it +// into one shared declaration instead of repeating the block per tool. +const textOutput = { + schema: { type: "string" } as const, + render: (_args: unknown, value: string) => [ + { type: "text" as const, text: value }, + ], +}; + +// Optional per-call scoping params, shared by both tools. A single harness +// install can serve more than one entity, so the model may override the +// mount-time default per call (see scoping.ts). +const scopeParams = { + userId: { + type: "string", + description: + "Entity that owns the memory. Defaults to the plugin's configured userId; set this only to read or write another user's memories.", + }, + agentId: { + type: "string", + description: "Optional agent scope, to partition memories by agent.", + }, + runId: { + type: "string", + description: "Optional run/session scope, to partition memories by session.", + }, +} as const; + +export function apply(ctx: Context, config: Config): void { + const apiKey = config.apiKey ?? process.env.MEM0_API_KEY; + if (!apiKey) { + throw new Error("dsh-mem0: set config.apiKey or the MEM0_API_KEY env var"); + } + const userId = config.userId; + if (!userId) { + throw new Error("dsh-mem0: config.userId is required"); + } + + const client = new MemoryClient({ + apiKey, + ...(config.host ? { host: config.host } : {}), + }); + + // Recall. The platform rejects top-level entity params on search, so scope + // goes inside `filters` (unlike add below, which takes them top-level). + ctx.tools.register( + defineTool({ + name: "search_memory", + description: + "Search the user's long-term Mem0 memory for facts relevant to a query. Use proactively before answering anything that may depend on what the user told you earlier.", + parameters: { + query: { type: "string", description: "What to recall.", required: true }, + limit: { + type: "integer", + description: `Max results to return (default ${DEFAULT_SEARCH_LIMIT}).`, + }, + ...scopeParams, + }, + output: textOutput, + async execute({ query, limit, userId: u, agentId, runId }) { + const filters = resolveSearchFilters({ userId: u, agentId, runId }, userId); + try { + const topK = limit && limit > 0 ? limit : DEFAULT_SEARCH_LIMIT; + const { results } = await client.search(query, { filters, topK }); + return truncateOutput(formatMemoryList(results ?? [])); + } catch (err) { + return `search_memory failed: ${err instanceof Error ? err.message : String(err)}`; + } + }, + }), + ); + + // Write. `source` tags the memory for telemetry attribution. + ctx.tools.register( + defineTool({ + name: "add_memory", + description: + "Store a fact in the user's long-term Mem0 memory for later sessions. Extraction runs asynchronously server-side, so a stored fact may take a moment to become searchable; do not immediately search to confirm the write.", + parameters: { + text: { type: "string", description: "The fact to remember.", required: true }, + ...scopeParams, + }, + output: textOutput, + async execute({ text, userId: u, agentId, runId }) { + const addParams = resolveAddParams({ userId: u, agentId, runId }, userId); + try { + const result = await client.add([{ role: "user", content: text }], { + ...addParams, + source: SOURCE, + }); + return truncateOutput(formatAddResult(result)); + } catch (err) { + return `add_memory failed: ${err instanceof Error ? err.message : String(err)}`; + } + }, + }), + ); +} diff --git a/integrations/dsh-mem0/src/output.ts b/integrations/dsh-mem0/src/output.ts new file mode 100644 index 000000000..44eee7c30 --- /dev/null +++ b/integrations/dsh-mem0/src/output.ts @@ -0,0 +1,33 @@ +/** + * Hard cap on tool output before it reaches the model context. + * + * Same guard the sibling plugins apply (200 lines / 50KB, see + * integrations/pi-agent-plugin/src/memory/tools.ts): a large recall or a wide + * result set can otherwise flood the context window in a single tool call. + */ + +export const MAX_OUTPUT_LINES = 200; +export const MAX_OUTPUT_BYTES = 50_000; + +export function truncateOutput(text: string): string { + const lines = text.split("\n"); + if (lines.length <= MAX_OUTPUT_LINES && text.length <= MAX_OUTPUT_BYTES) { + return text; + } + + const kept = lines.slice(0, MAX_OUTPUT_LINES); + let result = kept.join("\n"); + const byteCapped = result.length > MAX_OUTPUT_BYTES; + if (byteCapped) { + result = result.slice(0, MAX_OUTPUT_BYTES); + } + + const dropped = lines.length - kept.length; + const reasons: string[] = []; + if (dropped > 0) reasons.push(`showing ${kept.length} of ${lines.length} lines`); + if (byteCapped) reasons.push(`cut at ${Math.floor(MAX_OUTPUT_BYTES / 1000)}KB`); + if (reasons.length > 0) { + result += `\n\n[Output truncated: ${reasons.join(", ")}]`; + } + return result; +} diff --git a/integrations/dsh-mem0/src/scoping.ts b/integrations/dsh-mem0/src/scoping.ts new file mode 100644 index 000000000..ee5bfd81d --- /dev/null +++ b/integrations/dsh-mem0/src/scoping.ts @@ -0,0 +1,53 @@ +/** + * Per-call memory scoping. + * + * The plugin is mounted with one default `userId`, but a single harness install + * can serve more than one entity, so both tools accept optional `userId` / + * `agentId` / `runId` params that override the mount-time default per call. + * A missing or blank param falls back to the configured user. + * + * The two call sites need different key casing, and it is deliberate rather than + * incidental: search passes scope inside `filters`, sent to the platform raw, so + * it must be snake_case; add takes the entity params top-level, through the + * SDK's camel->snake converter, so it must be camelCase. Keeping the split + * explicit (like integrations/pi-agent-plugin/src/memory/scoping.ts) means the + * asymmetry is visible in the code, not load-bearing on a converter no-op. + */ + +export interface EntityParams { + userId?: string; + agentId?: string; + runId?: string; +} + +const clean = (v: string | undefined) => v?.trim() || undefined; + +/** Search: snake_case, spread into `filters` and passed to the platform raw. */ +export function resolveSearchFilters( + params: EntityParams, + defaultUserId: string, +): Record { + const filters: Record = { + user_id: clean(params.userId) ?? defaultUserId, + }; + const agentId = clean(params.agentId); + if (agentId) filters.agent_id = agentId; + const runId = clean(params.runId); + if (runId) filters.run_id = runId; + return filters; +} + +/** Add: camelCase, top-level params run through the SDK's camel->snake converter. */ +export function resolveAddParams( + params: EntityParams, + defaultUserId: string, +): Record { + const out: Record = { + userId: clean(params.userId) ?? defaultUserId, + }; + const agentId = clean(params.agentId); + if (agentId) out.agentId = agentId; + const runId = clean(params.runId); + if (runId) out.runId = runId; + return out; +} diff --git a/integrations/dsh-mem0/tests/apply.test.ts b/integrations/dsh-mem0/tests/apply.test.ts new file mode 100644 index 000000000..2aa20433e --- /dev/null +++ b/integrations/dsh-mem0/tests/apply.test.ts @@ -0,0 +1,143 @@ +import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; + +// Offline mock of the Mem0 SDK so these tests never touch the network. +const mockSearch = vi.fn(); +const mockAdd = vi.fn(); +vi.mock("mem0ai", () => ({ + MemoryClient: class { + search = mockSearch; + add = mockAdd; + }, +})); + +// The real `@deepseek-ai/dsh-tools` runtime transitively imports harness peer +// packages the host provides at runtime but which aren't installed here. For +// these unit tests we only need `defineTool` to hand back the definition it was +// given, so the registered tool's `execute`/`name` can be exercised directly. +vi.mock("@deepseek-ai/dsh-tools", () => ({ + defineTool: (options: unknown) => options, +})); + +import { apply, type Config } from "../src/index.ts"; + +interface RegisteredTool { + name: string; + execute(args: unknown, exec: unknown): Promise; +} + +function applyAndCollect(config: Config): Map { + const tools = new Map(); + const ctx = { + tools: { register: (t: RegisteredTool) => tools.set(t.name, t) }, + }; + apply(ctx as never, config); + return tools; +} + +let savedKey: string | undefined; + +beforeEach(() => { + savedKey = process.env.MEM0_API_KEY; + mockSearch.mockReset(); + mockAdd.mockReset(); +}); + +afterEach(() => { + if (savedKey === undefined) delete process.env.MEM0_API_KEY; + else process.env.MEM0_API_KEY = savedKey; +}); + +describe("apply() config validation", () => { + it("throws when no apiKey is set and MEM0_API_KEY is absent", () => { + delete process.env.MEM0_API_KEY; + expect(() => applyAndCollect({ userId: "u" } as Config)).toThrow(/apiKey|MEM0_API_KEY/); + }); + + it("throws when userId is missing", () => { + expect(() => applyAndCollect({ apiKey: "k", userId: "" } as Config)).toThrow(/userId/); + }); + + it("registers both memory tools", () => { + const tools = applyAndCollect({ apiKey: "k", userId: "u" }); + expect([...tools.keys()].sort()).toEqual(["add_memory", "search_memory"]); + }); +}); + +describe("search_memory tool", () => { + it("returns a formatted list scoped to the configured user", async () => { + mockSearch.mockResolvedValue({ + results: [{ id: "m1", memory: "Likes tea", categories: ["preference"] }], + }); + const tools = applyAndCollect({ apiKey: "k", userId: "u" }); + + const out = await tools.get("search_memory")!.execute({ query: "drink" }, {}); + + expect(out).toContain("Likes tea"); + expect(out).toContain("[mem0:m1]"); + expect(mockSearch).toHaveBeenCalledWith("drink", { + filters: { user_id: "u" }, + topK: 10, + }); + }); + + it("honors a per-call userId override and limit", async () => { + mockSearch.mockResolvedValue({ results: [] }); + const tools = applyAndCollect({ apiKey: "k", userId: "u" }); + + await tools.get("search_memory")!.execute({ query: "x", userId: "alice", limit: 3 }, {}); + + expect(mockSearch).toHaveBeenCalledWith("x", { + filters: { user_id: "alice" }, + topK: 3, + }); + }); + + it("returns a graceful failure line instead of rejecting on error", async () => { + mockSearch.mockRejectedValue(new Error("network down")); + const tools = applyAndCollect({ apiKey: "k", userId: "u" }); + + const out = await tools.get("search_memory")!.execute({ query: "x" }, {}); + + expect(out).toContain("search_memory failed"); + expect(out).toContain("network down"); + }); +}); + +describe("add_memory tool", () => { + it("reports the write as queued on the async PENDING response, with camelCase scope + source", async () => { + // The real /v3/memories/add/ response — not an array of memories. The SDK + // camel-cases response keys, so it surfaces as `eventId`, not `event_id`. + mockAdd.mockResolvedValue({ eventId: "evt-123", status: "PENDING" }); + const tools = applyAndCollect({ apiKey: "k", userId: "u" }); + + const out = await tools.get("add_memory")!.execute({ text: "remember this" }, {}); + + expect(out).toContain("queued"); + expect(out).toContain("evt-123"); + expect(out).not.toContain("No new distinct memory"); + expect(mockAdd).toHaveBeenCalledWith( + [{ role: "user", content: "remember this" }], + { userId: "u", source: "DEEPSEEK_HARNESS" }, + ); + }); + + it("renders a list when the backend returns memories", async () => { + mockAdd.mockResolvedValue([{ id: "m1", memory: "Fact" }]); + const tools = applyAndCollect({ apiKey: "k", userId: "u" }); + + const out = await tools.get("add_memory")!.execute({ text: "x" }, {}); + + expect(out).toContain("Stored 1 memory"); + expect(out).toContain("[mem0:m1]"); + }); + + it("returns a graceful failure line on error", async () => { + mockAdd.mockRejectedValue(new Error("boom")); + const tools = applyAndCollect({ apiKey: "k", userId: "u" }); + + const out = await tools.get("add_memory")!.execute({ text: "x" }, {}); + + expect(out).toContain("add_memory failed"); + expect(out).toContain("boom"); + }); +}); diff --git a/integrations/dsh-mem0/tests/formatting.test.ts b/integrations/dsh-mem0/tests/formatting.test.ts new file mode 100644 index 000000000..2d98ac340 --- /dev/null +++ b/integrations/dsh-mem0/tests/formatting.test.ts @@ -0,0 +1,71 @@ +import { describe, it, expect } from "vitest"; +import { + formatAge, + formatMemoryCompact, + formatMemoryList, + formatAddResult, +} from "../src/formatting.ts"; + +describe("formatAge", () => { + it("formats minutes, hours, and days", () => { + expect(formatAge(new Date(Date.now() - 30 * 60_000))).toBe("30m ago"); + expect(formatAge(new Date(Date.now() - 3 * 3_600_000))).toBe("3h ago"); + expect(formatAge(new Date(Date.now() - 5 * 86_400_000))).toBe("5d ago"); + }); +}); + +describe("formatMemoryCompact", () => { + it("renders one line with category, text, and id", () => { + const line = formatMemoryCompact({ + id: "abc-123", + memory: "User prefers dark mode", + categories: ["preference"], + createdAt: new Date(), + }); + expect(line).toContain("[preference]"); + expect(line).toContain("User prefers dark mode"); + expect(line).toContain("[mem0:abc-123]"); + }); + + it("falls back to uncategorized and (empty)", () => { + expect(formatMemoryCompact({ id: "x" })).toContain("[uncategorized]"); + expect(formatMemoryCompact({ id: "x" })).toContain("(empty)"); + }); +}); + +describe("formatMemoryList", () => { + it("numbers multiple memories", () => { + const output = formatMemoryList([ + { id: "id-1", memory: "Fact one", categories: ["insight"] }, + { id: "id-2", memory: "Fact two", categories: ["convention"] }, + ]); + expect(output).toContain("1."); + expect(output).toContain("2."); + }); + + it("returns a plain message when there are no memories", () => { + expect(formatMemoryList([])).toBe("No memories found."); + }); +}); + +describe("formatAddResult", () => { + it("reports queued for the async PENDING response, with the event id", () => { + // SDK camel-cases response keys, so the real shape is `eventId`. + const out = formatAddResult({ eventId: "evt-9", status: "PENDING" }); + expect(out).toContain("queued"); + expect(out).toContain("evt-9"); + }); + + it("reports the stored count when the backend returns memories", () => { + expect(formatAddResult([{ id: "1", memory: "A" }])).toContain("Stored 1 memory"); + expect(formatAddResult([{ id: "1" }, { id: "2" }])).toContain("Stored 2 memories"); + }); + + it("unwraps a { results: [...] } envelope", () => { + expect(formatAddResult({ results: [{ id: "1", memory: "A" }] })).toContain("Stored 1 memory"); + }); + + it("handles an empty result", () => { + expect(formatAddResult([])).toBe("Memory stored."); + }); +}); diff --git a/integrations/dsh-mem0/tests/output.test.ts b/integrations/dsh-mem0/tests/output.test.ts new file mode 100644 index 000000000..9bdcdefe9 --- /dev/null +++ b/integrations/dsh-mem0/tests/output.test.ts @@ -0,0 +1,30 @@ +import { describe, it, expect } from "vitest"; +import { truncateOutput, MAX_OUTPUT_LINES } from "../src/output.ts"; + +describe("truncateOutput", () => { + it("passes small output through untouched", () => { + expect(truncateOutput("a\nb\nc")).toBe("a\nb\nc"); + }); + + it("caps output at MAX_OUTPUT_LINES and appends a notice", () => { + const many = Array.from({ length: MAX_OUTPUT_LINES + 50 }, (_, i) => `line ${i}`).join("\n"); + const out = truncateOutput(many); + expect(out.split("\n").length).toBeLessThanOrEqual(MAX_OUTPUT_LINES + 3); + expect(out).toContain("[Output truncated:"); + expect(out).toContain(`of ${MAX_OUTPUT_LINES + 50} lines`); + }); + + it("caps output that is few lines but very large by bytes", () => { + const huge = "x".repeat(60_000); + const out = truncateOutput(huge); + expect(out.length).toBeLessThan(huge.length); + expect(out).toContain("[Output truncated:"); + }); + + it("reports both reasons when the line cap and the byte cap fire together", () => { + const wide = Array.from({ length: MAX_OUTPUT_LINES + 50 }, () => "x".repeat(300)).join("\n"); + const out = truncateOutput(wide); + expect(out).toContain(`of ${MAX_OUTPUT_LINES + 50} lines`); + expect(out).toContain("cut at 50KB"); + }); +}); diff --git a/integrations/dsh-mem0/tests/scoping.test.ts b/integrations/dsh-mem0/tests/scoping.test.ts new file mode 100644 index 000000000..989a83888 --- /dev/null +++ b/integrations/dsh-mem0/tests/scoping.test.ts @@ -0,0 +1,44 @@ +import { describe, it, expect } from "vitest"; +import { resolveSearchFilters, resolveAddParams } from "../src/scoping.ts"; + +describe("resolveSearchFilters (snake_case, for filters)", () => { + it("falls back to the configured default userId", () => { + expect(resolveSearchFilters({}, "default-user")).toEqual({ user_id: "default-user" }); + }); + + it("lets a per-call userId override the default", () => { + expect(resolveSearchFilters({ userId: "alice" }, "default-user")).toEqual({ + user_id: "alice", + }); + }); + + it("treats a blank/whitespace userId as absent and falls back", () => { + expect(resolveSearchFilters({ userId: " " }, "default-user")).toEqual({ + user_id: "default-user", + }); + }); + + it("includes snake_case agent/run scope only when provided", () => { + expect( + resolveSearchFilters({ userId: "alice", agentId: "agent-1", runId: "run-9" }, "d"), + ).toEqual({ user_id: "alice", agent_id: "agent-1", run_id: "run-9" }); + }); + + it("omits blank agent/run scope", () => { + const f = resolveSearchFilters({ agentId: " ", runId: "run-9" }, "default"); + expect(f).toEqual({ user_id: "default", run_id: "run-9" }); + expect(f).not.toHaveProperty("agent_id"); + }); +}); + +describe("resolveAddParams (camelCase, for top-level add params)", () => { + it("uses camelCase keys and falls back to the default userId", () => { + expect(resolveAddParams({}, "default-user")).toEqual({ userId: "default-user" }); + }); + + it("includes camelCase agent/run scope only when provided", () => { + expect( + resolveAddParams({ userId: "alice", agentId: "agent-1", runId: "run-9" }, "d"), + ).toEqual({ userId: "alice", agentId: "agent-1", runId: "run-9" }); + }); +}); diff --git a/integrations/dsh-mem0/tsconfig.json b/integrations/dsh-mem0/tsconfig.json new file mode 100644 index 000000000..412c13bac --- /dev/null +++ b/integrations/dsh-mem0/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "moduleResolution": "bundler", + "lib": ["ES2022"], + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "strict": true, + "types": ["node"], + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "isolatedModules": true, + "verbatimModuleSyntax": true, + "allowImportingTsExtensions": true, + "noEmit": true + }, + "include": ["src"], + "exclude": ["node_modules", "dist", "**/*.test.ts"] +} diff --git a/integrations/dsh-mem0/tsup.config.ts b/integrations/dsh-mem0/tsup.config.ts new file mode 100644 index 000000000..cb95342fb --- /dev/null +++ b/integrations/dsh-mem0/tsup.config.ts @@ -0,0 +1,12 @@ +import { defineConfig } from "tsup"; + +export default defineConfig({ + entry: ["src/index.ts"], + format: ["esm"], + dts: true, + sourcemap: true, + clean: true, + // The harness runtime and the Mem0 SDK are provided by the host / installed + // separately; keep them out of the bundle. + external: [/^node:/, /^@deepseek-ai\//, "mem0ai", /^mem0ai\//], +});