diff --git a/.agents/plugins/marketplace.json b/.agents/plugins/marketplace.json index 6e0e240a1..b09335c80 100644 --- a/.agents/plugins/marketplace.json +++ b/.agents/plugins/marketplace.json @@ -8,7 +8,7 @@ "name": "mem0", "source": { "source": "local", - "path": "./mem0-plugin" + "path": "./integrations/mem0-plugin" }, "policy": { "installation": "AVAILABLE", diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 5859339b7..fdbe45f43 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -10,9 +10,9 @@ "plugins": [ { "name": "mem0", - "source": "./mem0-plugin", + "source": "./integrations/mem0-plugin", "description": "Mem0 memory layer for AI applications. Add persistent memory, personalization, and semantic search to Claude workflows.", - "version": "0.2.9" + "version": "0.2.10" } ] } diff --git a/.codex-plugin/marketplace.json b/.codex-plugin/marketplace.json index 6e0e240a1..b09335c80 100644 --- a/.codex-plugin/marketplace.json +++ b/.codex-plugin/marketplace.json @@ -8,7 +8,7 @@ "name": "mem0", "source": { "source": "local", - "path": "./mem0-plugin" + "path": "./integrations/mem0-plugin" }, "policy": { "installation": "AVAILABLE", diff --git a/.cursor-plugin/marketplace.json b/.cursor-plugin/marketplace.json index 9576f2545..0d559f6c7 100644 --- a/.cursor-plugin/marketplace.json +++ b/.cursor-plugin/marketplace.json @@ -10,9 +10,9 @@ "plugins": [ { "name": "mem0", - "source": "./mem0-plugin", + "source": "./integrations/mem0-plugin", "description": "Mem0 memory layer for AI applications. Add persistent memory, personalization, and semantic search.", - "version": "0.2.9" + "version": "0.2.10" } ] } diff --git a/.github/workflows/ci-gate.yml b/.github/workflows/ci-gate.yml index 97840150c..01309a731 100644 --- a/.github/workflows/ci-gate.yml +++ b/.github/workflows/ci-gate.yml @@ -68,15 +68,15 @@ jobs: - '.github/workflows/cli-node-ci.yml' - '.github/workflows/ci-gate.yml' openclaw: - - 'openclaw/**' + - 'integrations/openclaw/**' - '.github/workflows/openclaw-checks.yml' - '.github/workflows/ci-gate.yml' opencode_plugin: - - 'mem0-plugin/.opencode-plugin/**' + - 'integrations/mem0-plugin/.opencode-plugin/**' - '.github/workflows/opencode-plugin-checks.yml' - '.github/workflows/ci-gate.yml' pi_agent_plugin: - - 'pi-agent-plugin/**' + - 'integrations/pi-agent-plugin/**' - '.github/workflows/pi-agent-plugin-checks.yml' - '.github/workflows/ci-gate.yml' docs_llms_txt: diff --git a/.github/workflows/openclaw-cd.yml b/.github/workflows/openclaw-cd.yml index cc9aaf874..55bcfa080 100644 --- a/.github/workflows/openclaw-cd.yml +++ b/.github/workflows/openclaw-cd.yml @@ -25,7 +25,7 @@ jobs: id-token: write defaults: run: - working-directory: openclaw + working-directory: integrations/openclaw steps: - uses: actions/checkout@v4 with: @@ -42,7 +42,7 @@ jobs: node-version: '22' registry-url: 'https://registry.npmjs.org' cache: 'pnpm' - cache-dependency-path: openclaw/pnpm-lock.yaml + cache-dependency-path: integrations/openclaw/pnpm-lock.yaml - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.github/workflows/openclaw-checks.yml b/.github/workflows/openclaw-checks.yml index 611ac0a0a..ec3bfee54 100644 --- a/.github/workflows/openclaw-checks.yml +++ b/.github/workflows/openclaw-checks.yml @@ -7,7 +7,7 @@ on: push: branches: [main] paths: - - 'openclaw/**' + - 'integrations/openclaw/**' - '.github/workflows/openclaw-checks.yml' workflow_call: @@ -27,13 +27,13 @@ jobs: with: node-version: 20 cache: 'pnpm' - cache-dependency-path: openclaw/pnpm-lock.yaml + cache-dependency-path: integrations/openclaw/pnpm-lock.yaml - name: Install dependencies - run: cd openclaw && pnpm install --frozen-lockfile + run: cd integrations/openclaw && pnpm install --frozen-lockfile - name: Type check - run: cd openclaw && pnpm exec tsc --noEmit + run: cd integrations/openclaw && pnpm exec tsc --noEmit test: runs-on: ubuntu-latest @@ -53,20 +53,20 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: 'pnpm' - cache-dependency-path: openclaw/pnpm-lock.yaml + cache-dependency-path: integrations/openclaw/pnpm-lock.yaml - name: Install dependencies - run: cd openclaw && pnpm install --frozen-lockfile + run: cd integrations/openclaw && pnpm install --frozen-lockfile - name: Run tests with coverage - run: cd openclaw && pnpm exec vitest run --coverage + run: cd integrations/openclaw && pnpm exec vitest run --coverage - name: Upload coverage to Codecov if: matrix.node-version == 20 uses: codecov/codecov-action@v4 with: flags: openclaw - directory: openclaw/coverage + directory: integrations/openclaw/coverage env: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} @@ -85,15 +85,15 @@ jobs: with: node-version: 20 cache: 'pnpm' - cache-dependency-path: openclaw/pnpm-lock.yaml + cache-dependency-path: integrations/openclaw/pnpm-lock.yaml - name: Install dependencies - run: cd openclaw && pnpm install --frozen-lockfile + run: cd integrations/openclaw && pnpm install --frozen-lockfile - name: Build - run: cd openclaw && pnpm build + run: cd integrations/openclaw && pnpm build - name: Verify dist output exists run: | - test -f openclaw/dist/index.js || (echo "Build output missing: dist/index.js" && exit 1) - test -f openclaw/dist/index.d.ts || (echo "Build output missing: dist/index.d.ts" && exit 1) + test -f integrations/openclaw/dist/index.js || (echo "Build output missing: dist/index.js" && exit 1) + test -f integrations/openclaw/dist/index.d.ts || (echo "Build output missing: dist/index.d.ts" && exit 1) diff --git a/.github/workflows/opencode-plugin-cd.yml b/.github/workflows/opencode-plugin-cd.yml index dd1a293d9..bf37f739f 100644 --- a/.github/workflows/opencode-plugin-cd.yml +++ b/.github/workflows/opencode-plugin-cd.yml @@ -25,7 +25,7 @@ jobs: id-token: write defaults: run: - working-directory: mem0-plugin/.opencode-plugin + working-directory: integrations/mem0-plugin/.opencode-plugin steps: - uses: actions/checkout@v4 with: diff --git a/.github/workflows/opencode-plugin-checks.yml b/.github/workflows/opencode-plugin-checks.yml index 514a20166..f1d9585f1 100644 --- a/.github/workflows/opencode-plugin-checks.yml +++ b/.github/workflows/opencode-plugin-checks.yml @@ -7,7 +7,7 @@ on: push: branches: [main] paths: - - 'mem0-plugin/.opencode-plugin/**' + - 'integrations/mem0-plugin/.opencode-plugin/**' - '.github/workflows/opencode-plugin-checks.yml' workflow_call: @@ -16,7 +16,7 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: mem0-plugin/.opencode-plugin + working-directory: integrations/mem0-plugin/.opencode-plugin steps: - uses: actions/checkout@v4 diff --git a/.github/workflows/pi-agent-plugin-cd.yml b/.github/workflows/pi-agent-plugin-cd.yml index f32bb7196..bd593a01f 100644 --- a/.github/workflows/pi-agent-plugin-cd.yml +++ b/.github/workflows/pi-agent-plugin-cd.yml @@ -25,7 +25,7 @@ jobs: id-token: write defaults: run: - working-directory: pi-agent-plugin + working-directory: integrations/pi-agent-plugin steps: - uses: actions/checkout@v4 with: @@ -42,7 +42,7 @@ jobs: node-version: '22' registry-url: 'https://registry.npmjs.org' cache: 'pnpm' - cache-dependency-path: pi-agent-plugin/pnpm-lock.yaml + cache-dependency-path: integrations/pi-agent-plugin/pnpm-lock.yaml - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.github/workflows/pi-agent-plugin-checks.yml b/.github/workflows/pi-agent-plugin-checks.yml index ba18c07bc..258863f9e 100644 --- a/.github/workflows/pi-agent-plugin-checks.yml +++ b/.github/workflows/pi-agent-plugin-checks.yml @@ -7,7 +7,7 @@ on: push: branches: [main] paths: - - 'pi-agent-plugin/**' + - 'integrations/pi-agent-plugin/**' - '.github/workflows/pi-agent-plugin-checks.yml' workflow_call: @@ -27,13 +27,13 @@ jobs: with: node-version: 20 cache: 'pnpm' - cache-dependency-path: pi-agent-plugin/pnpm-lock.yaml + cache-dependency-path: integrations/pi-agent-plugin/pnpm-lock.yaml - name: Install dependencies - run: cd pi-agent-plugin && pnpm install --frozen-lockfile + run: cd integrations/pi-agent-plugin && pnpm install --frozen-lockfile - name: Type check - run: cd pi-agent-plugin && pnpm exec tsc --noEmit + run: cd integrations/pi-agent-plugin && pnpm exec tsc --noEmit test: runs-on: ubuntu-latest @@ -53,13 +53,13 @@ jobs: with: node-version: ${{ matrix.node-version }} cache: 'pnpm' - cache-dependency-path: pi-agent-plugin/pnpm-lock.yaml + cache-dependency-path: integrations/pi-agent-plugin/pnpm-lock.yaml - name: Install dependencies - run: cd pi-agent-plugin && pnpm install --frozen-lockfile + run: cd integrations/pi-agent-plugin && pnpm install --frozen-lockfile - name: Run tests - run: cd pi-agent-plugin && pnpm exec vitest run + run: cd integrations/pi-agent-plugin && pnpm exec vitest run build: runs-on: ubuntu-latest @@ -76,17 +76,17 @@ jobs: with: node-version: 20 cache: 'pnpm' - cache-dependency-path: pi-agent-plugin/pnpm-lock.yaml + cache-dependency-path: integrations/pi-agent-plugin/pnpm-lock.yaml - name: Install dependencies - run: cd pi-agent-plugin && pnpm install --frozen-lockfile + run: cd integrations/pi-agent-plugin && pnpm install --frozen-lockfile - name: Build - run: cd pi-agent-plugin && pnpm build + run: cd integrations/pi-agent-plugin && pnpm build - name: Verify dist output exists run: | - test -f pi-agent-plugin/dist/index.js || (echo "Build output missing: dist/index.js" && exit 1) - test -f pi-agent-plugin/dist/index.d.ts || (echo "Build output missing: dist/index.d.ts" && exit 1) - test -f pi-agent-plugin/dist/entry.js || (echo "Build output missing: dist/entry.js" && exit 1) - test -f pi-agent-plugin/dist/entry.d.ts || (echo "Build output missing: dist/entry.d.ts" && exit 1) + test -f integrations/pi-agent-plugin/dist/index.js || (echo "Build output missing: dist/index.js" && exit 1) + test -f integrations/pi-agent-plugin/dist/index.d.ts || (echo "Build output missing: dist/index.d.ts" && exit 1) + test -f integrations/pi-agent-plugin/dist/entry.js || (echo "Build output missing: dist/entry.js" && exit 1) + test -f integrations/pi-agent-plugin/dist/entry.d.ts || (echo "Build output missing: dist/entry.d.ts" && exit 1) diff --git a/.github/workflows/vercel-ai-cd.yml b/.github/workflows/vercel-ai-cd.yml index 18dd4b31d..a249f8c63 100644 --- a/.github/workflows/vercel-ai-cd.yml +++ b/.github/workflows/vercel-ai-cd.yml @@ -25,7 +25,7 @@ jobs: id-token: write defaults: run: - working-directory: vercel-ai-sdk + working-directory: integrations/vercel-ai-sdk steps: - uses: actions/checkout@v4 with: @@ -42,7 +42,7 @@ jobs: node-version: '22' registry-url: 'https://registry.npmjs.org' cache: 'pnpm' - cache-dependency-path: vercel-ai-sdk/pnpm-lock.yaml + cache-dependency-path: integrations/vercel-ai-sdk/pnpm-lock.yaml - name: Install dependencies run: pnpm install --frozen-lockfile diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..260a12d8c --- /dev/null +++ b/.gitmodules @@ -0,0 +1,4 @@ +[submodule "evaluation"] + path = evaluation + url = https://github.com/mem0ai/memory-benchmarks + branch = main diff --git a/AGENTS.md b/AGENTS.md index 32f07f97d..b10ac7b90 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -12,7 +12,7 @@ This file provides context for AI coding assistants (Claude Code, Cursor, GitHub ## Repository Structure -This is a **polyglot monorepo** containing Python and TypeScript packages, CLIs, servers, plugins, documentation, and evaluation tooling. +This is a **polyglot monorepo** containing Python and TypeScript packages, CLIs, servers, plugins, and documentation. ### Key Directories @@ -22,17 +22,18 @@ This is a **polyglot monorepo** containing Python and TypeScript packages, CLIs, | `mem0-ts/` | TypeScript SDK (`mem0ai` on npm) — client + OSS memory | | `cli/python/` | Python CLI (`mem0-cli` on PyPI) — Typer-based, entry point `mem0` | | `cli/node/` | Node CLI (`@mem0/cli` on npm) — Commander-based, entry point `mem0` | -| `vercel-ai-sdk/` | `@mem0/vercel-ai-provider` — Vercel AI SDK memory provider | -| `openclaw/` | `@mem0/openclaw-mem0` — OpenClaw plugin for Claude Code / AI editors | +| `integrations/` | **Agent & editor integrations**, one directory per integration (see "Adding a New Integration") | +| `integrations/mem0-plugin/` | AI editor plugins (Claude Code, Cursor, Codex) — MCP server connection, lifecycle hooks, skills. Contains nested `.opencode-plugin/` (`@mem0/opencode-plugin`) | +| `integrations/openclaw/` | `@mem0/openclaw-mem0` — OpenClaw plugin for Claude Code / AI editors | +| `integrations/pi-agent-plugin/` | `@mem0/pi-agent-plugin` — Pi Agent plugin | +| `integrations/vercel-ai-sdk/` | `@mem0/vercel-ai-provider` — Vercel AI SDK memory provider | | `server/` | FastAPI REST server for self-hosted Mem0 (Docker: FastAPI + PostgreSQL/pgvector + Neo4j) | | `openmemory/` | Self-hosted memory platform — `api/` (FastAPI + Alembic + MCP server) and `ui/` (Next.js 15 + React 19) | -| `mem0-plugin/` | AI editor plugins (Claude Code, Cursor, Codex) — MCP server connection, lifecycle hooks, skills | -| `skills/` | Claude Code skill definitions. Reference skills (SDK knowledge, always-on): `mem0/`, `mem0-cli/`, `mem0-vercel-ai-sdk/`. Pipeline skills (run on demand): `mem0-integrate/`, `mem0-test-integration/` | +| `skills/` | Claude Code skill definitions. Reference skills (SDK knowledge, always-on): `mem0/`, `mem0-cli/`, `mem0-vercel-ai-sdk/`. Pipeline skills (run on demand): `mem0-integrate/`, `mem0-test-integration/`, `mem0-oss-to-platform/` | | `docs/` | Documentation site (Mintlify) | | `tests/` | Python SDK tests (pytest) | -| `evaluation/` | Benchmarking framework — LOCOMO evals, experiment runner, score generation | -| `examples/` | Sample projects — demo apps, Chrome extension, multi-agent patterns | -| `cookbooks/` | Jupyter notebooks — customer support chatbot, AutoGen integration | +| `evaluation/` | Submodule → [`mem0ai/memory-benchmarks`](https://github.com/mem0ai/memory-benchmarks) — benchmarking (LOCOMO, LongMemEval, BEAM) lives in that repo | +| `examples/` | Sample projects & runnable demos — apps, Chrome extension, multi-agent patterns, and Jupyter notebooks (`notebooks/`) | | `pr-reviews/` | Pull request review materials | | `scripts/` | Repo-wide utility scripts (e.g., `check-llms-txt-coverage.py` for docs/llms.txt sync) | @@ -49,8 +50,8 @@ mem0 (Python SDK) mem0-ts (TypeScript SDK) cli/python/ ──▶ mem0ai (optional, for OSS mode) cli/node/ ──▶ mem0ai (npm, for API calls) -vercel-ai-sdk/ ──▶ ai, @ai-sdk/* providers -openclaw/ ──▶ mem0ai (npm) +integrations/vercel-ai-sdk/ ──▶ ai, @ai-sdk/* providers +integrations/openclaw/ ──▶ mem0ai (npm) ``` ## Development Setup @@ -73,8 +74,8 @@ pre-commit install # install git hooks # TypeScript packages cd mem0-ts && pnpm install # TS SDK cd cli/node && pnpm install # Node CLI -cd vercel-ai-sdk && pnpm install # Vercel AI provider -cd openclaw && pnpm install # OpenClaw plugin +cd integrations/vercel-ai-sdk && pnpm install # Vercel AI provider +cd integrations/openclaw && pnpm install # OpenClaw plugin ``` ## Build, Lint, and Test Commands @@ -162,10 +163,10 @@ pnpm run dev # tsx src/index.ts (development) - **Test:** vitest (not jest) - **Framework:** Commander + Chalk + ora + cli-table3 -### Vercel AI SDK Provider (`vercel-ai-sdk/`) +### Vercel AI SDK Provider (`integrations/vercel-ai-sdk/`) ```bash -cd vercel-ai-sdk +cd integrations/vercel-ai-sdk pnpm install pnpm run build # tsup pnpm run lint # eslint @@ -180,10 +181,10 @@ pnpm run test:node # vitest (node runtime) - **Lint:** ESLint + Prettier - **Test:** jest + vitest (edge/node configs) -### OpenClaw Plugin (`openclaw/`) +### OpenClaw Plugin (`integrations/openclaw/`) ```bash -cd openclaw +cd integrations/openclaw pnpm install pnpm run build # tsup pnpm run test # vitest run @@ -245,18 +246,19 @@ make docs # or: cd docs && mintlify dev - **API spec:** `docs/openapi.json` - **Structure:** `api-reference/`, `open-source/`, `platform/`, `integrations/`, `cookbooks/`, `core-concepts/` -### Evaluation (`evaluation/`) +### Evaluation / Benchmarking + +Benchmarking lives in the external [`mem0ai/memory-benchmarks`](https://github.com/mem0ai/memory-benchmarks) repo (LOCOMO + LongMemEval + BEAM). The in-repo `evaluation/` path is a **git submodule** pinned to that repo's `main` — populate it with `git submodule update --init evaluation` (or clone mem0 with `--recurse-submodules`), or clone the benchmarks repo standalone: ```bash -cd evaluation -make run-mem0-add # Run mem0 add experiments -make run-mem0-search # Run mem0 search experiments -make run-mem0-plus-add # With graph memory -make run-mem0-plus-search # With graph memory -make run-rag # RAG baseline -make run-full-context # Full context baseline -make run-langmem # LangMem comparison -make run-openai # OpenAI comparison +git clone https://github.com/mem0ai/memory-benchmarks.git +cd memory-benchmarks +pip install -r requirements.txt + +# Run a benchmark (Mem0 Cloud; use docker compose for OSS) +python -m benchmarks.locomo.run --project-name my-test --backend cloud --mem0-api-key $MEM0_API_KEY +python -m benchmarks.longmemeval.run --project-name my-test --backend cloud --mem0-api-key $MEM0_API_KEY --all-questions +python -m benchmarks.beam.run --project-name my-test --backend cloud --mem0-api-key $MEM0_API_KEY --chat-sizes 100K --conversations 0-9 ``` ## Core APIs @@ -342,8 +344,8 @@ make run-openai # OpenAI comparison |---------|--------|-----------|---------------| | `mem0-ts/` | — | Prettier | jest | | `cli/node/` | Biome | Biome | vitest | -| `vercel-ai-sdk/` | ESLint | Prettier | jest + vitest | -| `openclaw/` | — | — | vitest | +| `integrations/vercel-ai-sdk/` | ESLint | Prettier | jest + vitest | +| `integrations/openclaw/` | — | — | vitest | ### Type Checking @@ -381,14 +383,14 @@ Model Context Protocol support in multiple places: - **Remote:** MCP server at `mcp.mem0.ai` - **Local:** MCP server in `openmemory/api/` (FastAPI-based) -- **Plugin:** MCP tools in `mem0-plugin/` — 9 tools: `add_memory`, `search_memories`, `get_memories`, `get_memory`, `update_memory`, `delete_memory`, `delete_all_memories`, `delete_entities`, `list_entities` +- **Plugin:** MCP tools in `integrations/mem0-plugin/` — 9 tools: `add_memory`, `search_memories`, `get_memories`, `get_memory`, `update_memory`, `delete_memory`, `delete_all_memories`, `delete_entities`, `list_entities` ### Plugin & Skills System -- `mem0-plugin/` provides integrations for Claude Code, Cursor, and Codex via MCP server connections and lifecycle hooks for automatic memory capture. +- `integrations/mem0-plugin/` provides integrations for Claude Code, Cursor, and Codex via MCP server connections and lifecycle hooks for automatic memory capture. - `skills/` contains structured skill definitions for AI agents, split into two categories: - **Reference skills** (always-on SDK knowledge): `mem0` (Python + TS SDKs, framework integrations), `mem0-cli` (terminal workflows), `mem0-vercel-ai-sdk` (Vercel AI provider). - - **Pipeline skills** (run on demand): `mem0-integrate` wires Mem0 into an existing repo via a TDD pipeline; `mem0-test-integration` verifies what the integrator produced on the same branch. The two are loosely coupled via `.mem0-integration/` artifacts. + - **Pipeline skills** (run on demand): `mem0-integrate` wires Mem0 into an existing repo via a TDD pipeline; `mem0-test-integration` verifies what the integrator produced on the same branch (the two are loosely coupled via `.mem0-integration/` artifacts); `mem0-oss-to-platform` migrates an existing project from Mem0 OSS to the hosted Platform SDK (plan, then execute on approval). ### Adding a New Provider @@ -402,6 +404,17 @@ To add a new LLM, embedding, vector store, or reranker provider: 6. Add any new dependencies to the appropriate optional group in `pyproject.toml` (never to core `dependencies`) 7. Follow the exact pattern of existing providers in the same category — match method signatures, error handling, and config structure +### Adding a New Integration + +Agent/editor integrations live under `integrations/`. Each is a self-contained directory (its own `package.json`/lockfile, build, and tests). To add one: + +1. Create `integrations//` and build the integration there. +2. If it publishes to a registry, set `repository.directory: "integrations/"` in its `package.json` so npm provenance links to the correct subdirectory. +3. Add CI/CD under `.github/workflows/` (`-checks.yml`, `-cd.yml`). Use `integrations/` in `paths:` triggers, `working-directory`, and `cache-dependency-path`. Register the release tag prefix in the `case` block in `release.yml` (keep the bare `v*` arm last). Keep workflow **filenames** stable — npm OIDC trusted publishing is pinned to repo + workflow filename. +4. If it is a Claude Code / editor marketplace plugin, register its path in the five `marketplace.json` files (root + `.claude-plugin/`, `.cursor-plugin/`, `.codex-plugin/`, `.agents/plugins/`). +5. Document it under `docs/integrations/` and add the page to `docs/docs.json` and `docs/llms.txt`. +6. Add rows to the "Key Directories" table and the CI/CD tables in this file. + ## CI/CD ### CI Workflows (automated testing) @@ -415,9 +428,9 @@ PR testing is orchestrated by a single entry point: **`ci-gate.yml` (CI Gate)** | TypeScript SDK | `ts-sdk-ci.yml` | Push to main (on `mem0-ts/`) | Prettier + build + jest on Node 20, 22 | | Python CLI | `cli-python-ci.yml` | Push to main (on `cli/python/`), manual | Ruff lint + pytest + hatch build on Python 3.10, 3.11, 3.12 | | Node CLI | `cli-node-ci.yml` | Push to main (on `cli/node/`), manual | Biome lint + tsc + vitest + tsup build on Node 20, 22 | -| OpenClaw | `openclaw-checks.yml` | Push to main (on `openclaw/`), manual | tsc + vitest (with Codecov) + tsup build on Node 20, 22 | -| OpenCode Plugin | `opencode-plugin-checks.yml` | Push to main (on `mem0-plugin/.opencode-plugin/`), manual | Bun: tsc type-check + build + dist artifact check | -| Pi Agent Plugin | `pi-agent-plugin-checks.yml` | Push to main (on `pi-agent-plugin/`), manual | tsc + vitest + tsup build (dist artifact check) on Node 20, 22 | +| OpenClaw | `openclaw-checks.yml` | Push to main (on `integrations/openclaw/`), manual | tsc + vitest (with Codecov) + tsup build on Node 20, 22 | +| OpenCode Plugin | `opencode-plugin-checks.yml` | Push to main (on `integrations/mem0-plugin/.opencode-plugin/`), manual | Bun: tsc type-check + build + dist artifact check | +| Pi Agent Plugin | `pi-agent-plugin-checks.yml` | Push to main (on `integrations/pi-agent-plugin/`), manual | tsc + vitest + tsup build (dist artifact check) on Node 20, 22 | | docs llms.txt | `docs-llms-txt-check.yml` | Manual | `docs/llms.txt` coverage check | When adding a new package CI workflow: give it `workflow_call` (plus `push`/`workflow_dispatch` as needed, but no `pull_request` trigger), then register it in `ci-gate.yml` — a path filter under the `changes` job, a call job, and an entry in the gate job's `needs` list. diff --git a/LLM.md b/LLM.md index f0304257f..d8b07b34e 100644 --- a/LLM.md +++ b/LLM.md @@ -1026,7 +1026,8 @@ def get_user_preferences(user_id: str): ### AutoGen Integration ```python -from cookbooks.helper.mem0_teachability import Mem0Teachability +# Mem0Teachability lives in examples/notebooks/helper/ — see examples/notebooks/mem0-autogen.ipynb +from helper.mem0_teachability import Mem0Teachability from mem0 import Memory # Add memory capability to AutoGen agents diff --git a/README.md b/README.md index 6ef3a8e7e..15dcc992e 100644 --- a/README.md +++ b/README.md @@ -186,9 +186,10 @@ npx skills add https://github.com/mem0ai/mem0 --skill mem0-vercel-ai-sdk ```bash npx skills add https://github.com/mem0ai/mem0 --skill mem0-integrate npx skills add https://github.com/mem0ai/mem0 --skill mem0-test-integration +npx skills add https://github.com/mem0ai/mem0 --skill mem0-oss-to-platform ``` -Use `/mem0-integrate` to wire Mem0 into an existing repo via a test-first pipeline, then `/mem0-test-integration` to verify. See the [skills catalog](./skills/) or [Vibecoding with Mem0](https://docs.mem0.ai/vibecoding) for the full picture. +Use `/mem0-integrate` to wire Mem0 into an existing repo via a test-first pipeline, then `/mem0-test-integration` to verify. Use `/mem0-oss-to-platform` to migrate an existing project from Mem0 OSS to the hosted Platform SDK. See the [skills catalog](./skills/) or [Vibecoding with Mem0](https://docs.mem0.ai/vibecoding) for the full picture. ### Basic Usage diff --git a/cli/node/pnpm-lock.yaml b/cli/node/pnpm-lock.yaml index c216f5168..3015d2108 100644 --- a/cli/node/pnpm-lock.yaml +++ b/cli/node/pnpm-lock.yaml @@ -9,6 +9,7 @@ overrides: langsmith@<0.6.0: ^0.6.0 tar-fs@>=2.0.0 <2.1.4: ^2.1.4 picomatch@<2.3.2: ^2.3.2 + postcss@<8.5.10: '>=8.5.10' importers: @@ -38,7 +39,7 @@ importers: version: 20.19.37 tsup: specifier: ^8.0.0 - version: 8.5.1(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3) + version: 8.5.1(postcss@8.5.15)(tsx@4.21.0)(typescript@5.9.3) tsx: specifier: ^4.7.0 version: 4.21.0 @@ -829,8 +830,8 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -871,7 +872,7 @@ packages: engines: {node: '>= 18'} peerDependencies: jiti: '>=1.21.0' - postcss: '>=8.0.9' + postcss: '>=8.5.10' tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: @@ -884,8 +885,8 @@ packages: yaml: optional: true - postcss@8.5.8: - resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} readdirp@4.1.2: @@ -997,7 +998,7 @@ packages: peerDependencies: '@microsoft/api-extractor': ^7.36.0 '@swc/core': ^1 - postcss: ^8.4.12 + postcss: '>=8.5.10' typescript: '>=4.5.0' peerDependenciesMeta: '@microsoft/api-extractor': @@ -1671,7 +1672,7 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.11: {} + nanoid@3.3.12: {} object-assign@4.1.1: {} @@ -1707,16 +1708,16 @@ snapshots: mlly: 1.8.2 pathe: 2.0.3 - postcss-load-config@6.0.1(postcss@8.5.8)(tsx@4.21.0): + postcss-load-config@6.0.1(postcss@8.5.15)(tsx@4.21.0): dependencies: lilconfig: 3.1.3 optionalDependencies: - postcss: 8.5.8 + postcss: 8.5.15 tsx: 4.21.0 - postcss@8.5.8: + postcss@8.5.15: dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -1837,7 +1838,7 @@ snapshots: ts-interface-checker@0.1.13: {} - tsup@8.5.1(postcss@8.5.8)(tsx@4.21.0)(typescript@5.9.3): + tsup@8.5.1(postcss@8.5.15)(tsx@4.21.0)(typescript@5.9.3): dependencies: bundle-require: 5.1.0(esbuild@0.27.4) cac: 6.7.14 @@ -1848,7 +1849,7 @@ snapshots: 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.8)(tsx@4.21.0) + postcss-load-config: 6.0.1(postcss@8.5.15)(tsx@4.21.0) resolve-from: 5.0.0 rollup: 4.60.0 source-map: 0.7.6 @@ -1857,7 +1858,7 @@ snapshots: tinyglobby: 0.2.15 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.5.8 + postcss: 8.5.15 typescript: 5.9.3 transitivePeerDependencies: - jiti @@ -1885,7 +1886,7 @@ snapshots: esbuild: 0.25.12 fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 - postcss: 8.5.8 + postcss: 8.5.15 rollup: 4.60.0 tinyglobby: 0.2.15 optionalDependencies: diff --git a/cli/node/pnpm-workspace.yaml b/cli/node/pnpm-workspace.yaml index 8f371f95f..e7acb93c5 100644 --- a/cli/node/pnpm-workspace.yaml +++ b/cli/node/pnpm-workspace.yaml @@ -10,3 +10,4 @@ overrides: langsmith@<0.6.0: ^0.6.0 tar-fs@>=2.0.0 <2.1.4: ^2.1.4 picomatch@<2.3.2: ^2.3.2 + "postcss@<8.5.10": ">=8.5.10" diff --git a/cli/python/tests/test_agent_mode.py b/cli/python/tests/test_agent_mode.py index 78f0ca48c..d7b609e15 100644 --- a/cli/python/tests/test_agent_mode.py +++ b/cli/python/tests/test_agent_mode.py @@ -32,12 +32,13 @@ def _run(args: list[str], home_dir: str | None = None) -> subprocess.CompletedPr if key.startswith("MEM0_"): del env[key] env.pop("FORCE_COLOR", None) + env["PYTHONIOENCODING"] = "utf-8" if home_dir: env["HOME"] = home_dir result = subprocess.run( [sys.executable, "-m", "mem0_cli", *args], capture_output=True, - text=True, + encoding="utf-8", env=env, timeout=15, ) @@ -99,12 +100,13 @@ class TestArgvPreprocessing: result = subprocess.run( [sys.executable, "-m", "mem0_cli", "init", "--agent"], capture_output=True, - text=True, + encoding="utf-8", env={ **{k: v for k, v in os.environ.items() if not k.startswith("MEM0_")}, "HOME": clean_home, "MEM0_BASE_URL": "http://127.0.0.1:1", # blackhole "FORCE_COLOR": "0", + "PYTHONIOENCODING": "utf-8", }, timeout=15, ) @@ -133,12 +135,13 @@ class TestJsonEnvelopeParity: result = subprocess.run( [sys.executable, "-m", "mem0_cli", "init", "--agent", "--json"], capture_output=True, - text=True, + encoding="utf-8", env={ **{k: v for k, v in os.environ.items() if not k.startswith("MEM0_")}, "HOME": clean_home, "MEM0_BASE_URL": "http://127.0.0.1:1", "FORCE_COLOR": "0", + "PYTHONIOENCODING": "utf-8", }, timeout=15, ) diff --git a/cli/python/tests/test_cli_integration.py b/cli/python/tests/test_cli_integration.py index ea6607201..8abd145d0 100644 --- a/cli/python/tests/test_cli_integration.py +++ b/cli/python/tests/test_cli_integration.py @@ -49,6 +49,7 @@ def _run( if key.startswith("MEM0_"): del env[key] env.pop("FORCE_COLOR", None) + env["PYTHONIOENCODING"] = "utf-8" if home_dir: env["HOME"] = home_dir if env_override: @@ -56,7 +57,7 @@ def _run( result = subprocess.run( [sys.executable, "-m", "mem0_cli", *args], capture_output=True, - text=True, + encoding="utf-8", env=env, ) return subprocess.CompletedProcess( diff --git a/cli/python/tests/test_config.py b/cli/python/tests/test_config.py index 48db01481..86dc2561b 100644 --- a/cli/python/tests/test_config.py +++ b/cli/python/tests/test_config.py @@ -67,7 +67,8 @@ class TestConfig: from mem0_cli.config import CONFIG_FILE mode = os.stat(CONFIG_FILE).st_mode & 0o777 - assert mode == 0o600 + if os.name != "nt": + assert mode == 0o600 def test_defaults_save_and_load(self, isolate_config): config = Mem0Config() diff --git a/docs/changelog/openclaw.mdx b/docs/changelog/openclaw.mdx index 0e7f1dca1..dcad458e0 100644 --- a/docs/changelog/openclaw.mdx +++ b/docs/changelog/openclaw.mdx @@ -4,6 +4,39 @@ description: "Release notes for the OpenClaw plugin and agent harness." mode: "wide" --- + + +**Fixes:** +- **Custom categories payload:** `customCategories` (a `Record` map) is now converted via the new `customCategoryMapToList()` helper into the `Array>` shape the Mem0 SDK expects on `add` calls — previously the raw object was passed as `custom_categories` and silently ignored ([#5345](https://github.com/mem0ai/mem0/pull/5345)) +- **Skip runtime setup during metadata registration:** `register()` now detects `registrationMode === "cli-metadata"`, registers only the CLI commands, and returns early — avoiding backend initialization, service/tool registration, and hook installation during OpenClaw's metadata-only registration pass ([#5383](https://github.com/mem0ai/mem0/pull/5383)) + +**Security:** +- Bumped `mem0ai` from `3.0.3` to `3.0.7` (latest Node SDK) — includes the transitive axios CVE remediation shipped in `3.0.6` ([#5460](https://github.com/mem0ai/mem0/pull/5460)) +- Added pnpm override `uuid@<11.1.1` → `>=11.1.1` to resolve an open MEDIUM Dependabot alert ([#5489](https://github.com/mem0ai/mem0/pull/5489)) + +**Improvements:** +- **Repo consolidation:** Plugin moved from repo-root `openclaw/` to `integrations/openclaw/`; `package.json` `repository.directory` updated to match so npm provenance links to the correct subdirectory ([#5491](https://github.com/mem0ai/mem0/pull/5491)) + +**Tests:** +- Added `customCategoryMapToList` unit tests and a `PlatformProvider` test asserting `custom_categories` is passed to the Mem0 SDK as a list ([#5345](https://github.com/mem0ai/mem0/pull/5345)) +- Added a regression test asserting `cli-metadata` registration registers only CLI commands and triggers no runtime side effects ([#5383](https://github.com/mem0ai/mem0/pull/5383)) + + + + + +**Docs:** +- **Agent Mode onboarding:** README now documents an autonomous setup path for AI agents — `mem0 init --agent --json` mints an evaluation Mem0 API key with no email, OTP, or browser and exports it as `MEM0_API_KEY` for `openclaw mem0 init`; a human owner can later run `mem0 init --email ` to claim ownership without disrupting the agent ([#5123](https://github.com/mem0ai/mem0/pull/5123)) + +**Security:** +- Added pnpm overrides to remediate advisories in transitive dependencies: `langsmith@<0.6.0` → `^0.6.0`, `picomatch@<2.3.2` → `^2.3.2`, `vite` → `^8.0.5`, and `@qdrant/js-client-rest` → `^1.18.0` ([#5294](https://github.com/mem0ai/mem0/pull/5294)) + +**Dependencies:** +- Bumped `mem0ai` from `3.0.2` to `3.0.3` ([#5212](https://github.com/mem0ai/mem0/pull/5212)) +- Bumped dev dependencies `@vitest/coverage-v8` and `vitest` from `^4.0.18` to `^4.1.7`; added `vite@^8.0.5` and `@qdrant/js-client-rest@^1.18.0` ([#5294](https://github.com/mem0ai/mem0/pull/5294)) + + + **New Features:** diff --git a/docs/changelog/sdk.mdx b/docs/changelog/sdk.mdx index 9418f2eda..b8382ed05 100644 --- a/docs/changelog/sdk.mdx +++ b/docs/changelog/sdk.mdx @@ -7,6 +7,22 @@ mode: "wide" + + +**New Features:** +- **Memory:** Add a contextual OSS-to-Platform notices system that surfaces occasional, situation-aware messages (first run, scale/performance thresholds, slow queries, and when temporal/decay features are relevant) pointing to the corresponding Mem0 Platform capabilities; disable via `MEM0_TELEMETRY=false` ([#5494](https://github.com/mem0ai/mem0/pull/5494)) + +**Bug Fixes:** +- **Memory:** Prevent a crash in `parse_vision_messages` when vision support is disabled ([#5487](https://github.com/mem0ai/mem0/pull/5487)) +- **Vector Stores:** Expose the `https` option on the Qdrant vector store configuration so TLS endpoints can be targeted explicitly ([#5380](https://github.com/mem0ai/mem0/pull/5380)) +- **Vector Stores:** Use valid S3 Vectors entity index names, fixing index operations that failed on invalid names ([#5416](https://github.com/mem0ai/mem0/pull/5416)) +- **Vector Stores:** Fix `search()` crashing with a `TypeError` in the LangChain vector store when a result score is `None` ([#5072](https://github.com/mem0ai/mem0/pull/5072)) +- **Vector Stores:** Use `is not None` instead of a truthiness check for vector/payload in the PGVector `update()` path, so valid empty/zero values are no longer skipped ([#5488](https://github.com/mem0ai/mem0/pull/5488)) +- **Vector Stores:** Index the Valkey `memory` field as `TEXT` rather than `TAG` so full-text search behaves correctly ([#5443](https://github.com/mem0ai/mem0/pull/5443)) +- **Vector Stores:** Implement `$not` filter support in the ChromaDB vector store ([#5485](https://github.com/mem0ai/mem0/pull/5485)) + + + **New Features:** @@ -960,6 +976,17 @@ See the [OSS v1 to v2 migration guide](https://docs.mem0.ai/migration/oss-v1-to- + + +**New Features:** +- **Memory:** Add a contextual OSS-to-Platform notices system that surfaces occasional, situation-aware messages (first run, scale/performance thresholds, slow queries, and when temporal/decay features are relevant) pointing to the corresponding Mem0 Platform capabilities; disable via `MEM0_TELEMETRY=false` ([#5494](https://github.com/mem0ai/mem0/pull/5494)) + +**Security:** +- **Dependencies:** Upgrade `@langchain/community` to `^1.1.18` to remediate CVE-2026-27795 and CVE-2026-26019 ([#5510](https://github.com/mem0ai/mem0/pull/5510)) +- **Dependencies:** Resolve all open MEDIUM Dependabot alerts via pnpm overrides ([#5489](https://github.com/mem0ai/mem0/pull/5489)) + + + **New Features:** diff --git a/docs/components/vectordbs/dbs/qdrant.mdx b/docs/components/vectordbs/dbs/qdrant.mdx index 8f6d00ac9..93c11a070 100644 --- a/docs/components/vectordbs/dbs/qdrant.mdx +++ b/docs/components/vectordbs/dbs/qdrant.mdx @@ -76,6 +76,7 @@ Let's see the available parameters for the `qdrant` config: | `path` | Path for the qdrant database | `/tmp/qdrant` | | `url` | Full URL for the qdrant server | `None` | | `api_key` | API key for the qdrant server | `None` | +| `https` | Whether to force HTTPS on or off. `None` lets the client decide; set `False` for plain HTTP Qdrant with API key authentication. | `None` | | `on_disk` | For enabling persistent storage | `False` | @@ -90,4 +91,4 @@ Let's see the available parameters for the `qdrant` config: | `apiKey` | API key for the Qdrant server | `None` | | `onDisk` | For enabling persistent storage | `False` | - \ No newline at end of file + diff --git a/docs/integrations/antigravity.mdx b/docs/integrations/antigravity.mdx index 3a6836f58..9a9d9b1f4 100644 --- a/docs/integrations/antigravity.mdx +++ b/docs/integrations/antigravity.mdx @@ -28,7 +28,7 @@ echo 'export MEM0_API_KEY="m0-your-api-key"' >> ~/.bashrc && source ~/.bashrc ```bash # Install the plugin (MCP server, hooks, scripts) -npx degit mem0ai/mem0/mem0-plugin ~/.gemini/config/plugins/mem0 +npx degit mem0ai/mem0/integrations/mem0-plugin ~/.gemini/config/plugins/mem0 ``` This installs the MCP server, lifecycle hooks, and shared scripts. diff --git a/docs/integrations/opencode.mdx b/docs/integrations/opencode.mdx index e069ab8e6..6b608fc02 100644 --- a/docs/integrations/opencode.mdx +++ b/docs/integrations/opencode.mdx @@ -43,7 +43,7 @@ bunx @mem0/opencode-plugin@latest install **Or let your agent do it** — paste this into OpenCode: ``` -Install @mem0/opencode-plugin by following https://raw.githubusercontent.com/mem0ai/mem0/main/mem0-plugin/.opencode-plugin/README.md +Install @mem0/opencode-plugin by following https://raw.githubusercontent.com/mem0ai/mem0/main/integrations/mem0-plugin/.opencode-plugin/README.md ``` All commands auto-add the plugin and MCP server to your `~/.config/opencode/opencode.json`. Restart OpenCode — you get the MCP server, lifecycle hooks, and all `/mem0:` slash commands. diff --git a/docs/integrations/pi-agent.mdx b/docs/integrations/pi-agent.mdx index 82e7a2d0b..6114f9cc1 100644 --- a/docs/integrations/pi-agent.mdx +++ b/docs/integrations/pi-agent.mdx @@ -59,6 +59,7 @@ For advanced settings, create `~/.pi/agent/mem0-config.json`: "userId": "your-username", "autoCapture": true, "defaultScope": "project", + "searchThreshold": 0.3, "dream": { "enabled": true, "auto": true, @@ -75,12 +76,14 @@ For advanced settings, create `~/.pi/agent/mem0-config.json`: | `userId` | `string` | `$MEM0_USER_ID` or `"default"` | User identity for memory scoping | | `autoCapture` | `boolean` | `true` | Store facts from conversations automatically | | `defaultScope` | `string` | `"project"` | Default memory scope: `project`, `session`, or `global` | +| `searchThreshold` | `number` | `0.3` | Minimum similarity score (0–1) a memory must reach to count as a match for `/mem0-search`, `/mem0-forget`, and `/mem0-pin`, enforced on each result's relevance score. Raise it to be stricter; lower it if relevant results are missed. | | `dream.enabled` | `boolean` | `true` | Enable dream consolidation | | `dream.auto` | `boolean` | `true` | Auto-trigger dreams when thresholds are met | | `dream.minHours` | `number` | `24` | Minimum hours between auto-dreams | | `dream.minSessions` | `number` | `5` | Minimum sessions before first auto-dream | | `dream.minMemories` | `number` | `20` | Minimum memories before auto-dream triggers | + ## What's Included | Component | Description | diff --git a/docs/llms.txt b/docs/llms.txt index eb73d796c..ccca47734 100644 --- a/docs/llms.txt +++ b/docs/llms.txt @@ -398,9 +398,9 @@ Each subdirectory is a Claude Code Skill (`SKILL.md` + supporting assets). Load ### Editor Plugin (shared glue) -Source: https://github.com/mem0ai/mem0/tree/main/mem0-plugin +Source: https://github.com/mem0ai/mem0/tree/main/integrations/mem0-plugin -The `mem0-plugin/` directory provides MCP server connection, lifecycle hooks, and skill bundling for Claude Code, Cursor, Codex, OpenCode, and Antigravity. It exposes 9 MCP tools: `add_memory`, `search_memories`, `get_memories`, `get_memory`, `update_memory`, `delete_memory`, `delete_all_memories`, `delete_entities`, `list_entities`. +The `integrations/mem0-plugin/` directory provides MCP server connection, lifecycle hooks, and skill bundling for Claude Code, Cursor, Codex, OpenCode, and Antigravity. It exposes 9 MCP tools: `add_memory`, `search_memories`, `get_memories`, `get_memory`, `update_memory`, `delete_memory`, `delete_all_memories`, `delete_entities`, `list_entities`. Editor-specific setup docs (already listed above under `## Integrations > AI Coding Tools`): diff --git a/docs/migration/oss-to-platform.mdx b/docs/migration/oss-to-platform.mdx index 8b70e363d..c478701e2 100644 --- a/docs/migration/oss-to-platform.mdx +++ b/docs/migration/oss-to-platform.mdx @@ -6,9 +6,7 @@ versionFrom: "Open Source" versionTo: "Platform" --- -# Migrate from Open Source to Platform - -Move your Mem0 implementation to managed infrastructure with enterprise features. +## Overview | Scope | Effort | Downtime | | --------------------- | -------------- | ---------------------------- | @@ -30,12 +28,29 @@ Move your Mem0 implementation to managed infrastructure with enterprise features - **Production Grade**: Auto-scaling, high availability, dedicated support -## Plan +### Plan 1. **Sign up**: Create an account on Mem0 Platform. 2. **Get API Key**: Navigate to **Settings > API Keys** and generate a new key. 3. **Review Usage**: Identify where you instantiate `Memory` and where you call `search` or `get_all`. +## Migrate with Agent Skill + +Paste this prompt into your coding agent. It uses a migration skill to produce a plan; once you review and approve it, the agent implements the changes. + +```text +Migrate my project from Mem0 OSS to the Mem0 Platform SDK using the +mem0-oss-to-platform skill in the mem0ai/mem0 repo, at +skills/mem0-oss-to-platform/ + +Get the skill whichever way is easiest: +- install it: npx skills add https://github.com/mem0ai/mem0 --skill mem0-oss-to-platform +- if the mem0 repo is cloned locally, read it from skills/mem0-oss-to-platform/ +- otherwise fetch that folder from github.com/mem0ai/mem0 (SKILL.md + references/) + +Then read SKILL.md and begin the migration. +``` + ## Migrate ### 1. Import Memories Into Platform diff --git a/docs/vibecoding.mdx b/docs/vibecoding.mdx index 1de91f645..2cb4c826b 100644 --- a/docs/vibecoding.mdx +++ b/docs/vibecoding.mdx @@ -45,10 +45,12 @@ Let your assistant execute an end-to-end workflow in an existing repo. Invoked a ```bash npx skills add https://github.com/mem0ai/mem0 --skill mem0-integrate npx skills add https://github.com/mem0ai/mem0 --skill mem0-test-integration +npx skills add https://github.com/mem0ai/mem0 --skill mem0-oss-to-platform ``` - `/mem0-integrate` — wire Mem0 into an existing repository using a goal-driven, test-first pipeline. Detects the stack, asks whether to use Platform or OSS, writes failing tests first, and keeps the integration additive and feature-flagged. - `/mem0-test-integration` — verify what `/mem0-integrate` produced. Runs the repo's native test suite and a real end-to-end smoke flow against your API key, then produces a scorecard. +- `/mem0-oss-to-platform` — migrate an existing project from Mem0 OSS to the hosted Platform SDK. Audits where Mem0 is used, writes a reviewable migration plan, then executes it on approval. See the [skills index](https://github.com/mem0ai/mem0/tree/main/skills) for the full catalog. diff --git a/evaluation b/evaluation new file mode 160000 index 000000000..4b61c5d31 --- /dev/null +++ b/evaluation @@ -0,0 +1 @@ +Subproject commit 4b61c5d31b9c668a12b4f5e78064248a02c82d2b diff --git a/evaluation/Makefile b/evaluation/Makefile deleted file mode 100644 index 7f0072e6e..000000000 --- a/evaluation/Makefile +++ /dev/null @@ -1,31 +0,0 @@ - -# Run the experiments -run-mem0-add: - python run_experiments.py --technique_type mem0 --method add - -run-mem0-search: - python run_experiments.py --technique_type mem0 --method search --output_folder results/ --top_k 30 - -run-mem0-plus-add: - python run_experiments.py --technique_type mem0 --method add --is_graph - -run-mem0-plus-search: - python run_experiments.py --technique_type mem0 --method search --is_graph --output_folder results/ --top_k 30 - -run-rag: - python run_experiments.py --technique_type rag --chunk_size 500 --num_chunks 1 --output_folder results/ - -run-full-context: - python run_experiments.py --technique_type rag --chunk_size -1 --num_chunks 1 --output_folder results/ - -run-langmem: - python run_experiments.py --technique_type langmem --output_folder results/ - -run-zep-add: - python run_experiments.py --technique_type zep --method add --output_folder results/ - -run-zep-search: - python run_experiments.py --technique_type zep --method search --output_folder results/ - -run-openai: - python run_experiments.py --technique_type openai --output_folder results/ diff --git a/evaluation/README.md b/evaluation/README.md deleted file mode 100644 index e034050f9..000000000 --- a/evaluation/README.md +++ /dev/null @@ -1,198 +0,0 @@ -# Mem0: Building Production‑Ready AI Agents with Scalable Long‑Term Memory - -[![arXiv](https://img.shields.io/badge/arXiv-Paper-b31b1b.svg)](https://arxiv.org/abs/2504.19413) -[![Website](https://img.shields.io/badge/Website-Project-blue)](https://mem0.ai/research) - -This repository contains the code and dataset for our paper: **Mem0: Building Production‑Ready AI Agents with Scalable Long‑Term Memory**. - -## 📋 Overview - -This project evaluates Mem0 and compares it with different memory and retrieval techniques for AI systems: - -1. **Established LOCOMO Benchmarks**: We evaluate against five established approaches from the literature: LoCoMo, ReadAgent, MemoryBank, MemGPT, and A-Mem. -2. **Open-Source Memory Solutions**: We test promising open-source memory architectures including LangMem, which provides flexible memory management capabilities. -3. **RAG Systems**: We implement Retrieval-Augmented Generation with various configurations, testing different chunk sizes and retrieval counts to optimize performance. -4. **Full-Context Processing**: We examine the effectiveness of passing the entire conversation history within the context window of the LLM as a baseline approach. -5. **Proprietary Memory Systems**: We evaluate OpenAI's built-in memory feature available in their ChatGPT interface to compare against commercial solutions. -6. **Third-Party Memory Providers**: We incorporate Zep, a specialized memory management platform designed for AI agents, to assess the performance of dedicated memory infrastructure. - -We test these techniques on the LOCOMO dataset, which contains conversational data with various question types to evaluate memory recall and understanding. - -## 🔍 Dataset - -The LOCOMO dataset used in our experiments can be downloaded from our Google Drive repository: - -[Download LOCOMO Dataset](https://drive.google.com/drive/folders/1L-cTjTm0ohMsitsHg4dijSPJtqNflwX-?usp=drive_link) - -The dataset contains conversational data specifically designed to test memory recall and understanding across various question types and complexity levels. - -Place the dataset files in the `dataset/` directory: -- `locomo10.json`: Original dataset -- `locomo10_rag.json`: Dataset formatted for RAG experiments - -## 📁 Project Structure - -``` -. -├── src/ # Source code for different memory techniques -│ ├── mem0/ # Implementation of the Mem0 technique -│ ├── openai/ # Implementation of the OpenAI memory -│ ├── zep/ # Implementation of the Zep memory -│ ├── rag.py # Implementation of the RAG technique -│ └── langmem.py # Implementation of the Language-based memory -├── metrics/ # Code for evaluation metrics -├── results/ # Results of experiments -├── dataset/ # Dataset files -├── evals.py # Evaluation script -├── run_experiments.py # Script to run experiments -├── generate_scores.py # Script to generate scores from results -└── prompts.py # Prompts used for the models -``` - -## 🚀 Getting Started - -### Prerequisites - -Create a `.env` file with your API keys and configurations. The following keys are required: - -``` -# OpenAI API key for GPT models and embeddings -OPENAI_API_KEY="your-openai-api-key" - -# Mem0 API keys (for Mem0 and Mem0+ techniques) -MEM0_API_KEY="your-mem0-api-key" -MEM0_PROJECT_ID="your-mem0-project-id" -MEM0_ORGANIZATION_ID="your-mem0-organization-id" - -# Model configuration -MODEL="gpt-4o-mini" # or your preferred model -EMBEDDING_MODEL="text-embedding-3-small" # or your preferred embedding model -ZEP_API_KEY="api-key-from-zep" -``` - -### Running Experiments - -You can run experiments using the provided Makefile commands: - -#### Memory Techniques - -```bash -# Run Mem0 experiments -make run-mem0-add # Add memories using Mem0 -make run-mem0-search # Search memories using Mem0 - -# Run Mem0+ experiments (with graph-based search) -make run-mem0-plus-add # Add memories using Mem0+ -make run-mem0-plus-search # Search memories using Mem0+ - -# Run RAG experiments -make run-rag # Run RAG with chunk size 500 -make run-full-context # Run RAG with full context - -# Run LangMem experiments -make run-langmem # Run LangMem - -# Run Zep experiments -make run-zep-add # Add memories using Zep -make run-zep-search # Search memories using Zep - -# Run OpenAI experiments -make run-openai # Run OpenAI experiments -``` - -Alternatively, you can run experiments directly with custom parameters: - -```bash -python run_experiments.py --technique_type [mem0|rag|langmem] [additional parameters] -``` - -#### Command-line Parameters: - -| Parameter | Description | Default | -|-----------|-------------|---------| -| `--technique_type` | Memory technique to use (mem0, rag, langmem) | mem0 | -| `--method` | Method to use (add, search) | add | -| `--chunk_size` | Chunk size for processing | 1000 | -| `--top_k` | Number of top memories to retrieve | 30 | -| `--filter_memories` | Whether to filter memories | False | -| `--is_graph` | Whether to use graph-based search | False | -| `--num_chunks` | Number of chunks to process for RAG | 1 | - -### 📊 Evaluation - -To evaluate results, run: - -```bash -python evals.py --input_file [path_to_results] --output_file [output_path] -``` - -This script: -1. Processes each question-answer pair -2. Calculates BLEU and F1 scores automatically -3. Uses an LLM judge to evaluate answer correctness -4. Saves the combined results to the output file - -### 📈 Generating Scores - -Generate final scores with: - -```bash -python generate_scores.py -``` - -This script: -1. Loads the evaluation metrics data -2. Calculates mean scores for each category (BLEU, F1, LLM) -3. Reports the number of questions per category -4. Calculates overall mean scores across all categories - -Example output: -``` -Mean Scores Per Category: - bleu_score f1_score llm_score count -category -1 0.xxxx 0.xxxx 0.xxxx xx -2 0.xxxx 0.xxxx 0.xxxx xx -3 0.xxxx 0.xxxx 0.xxxx xx - -Overall Mean Scores: -bleu_score 0.xxxx -f1_score 0.xxxx -llm_score 0.xxxx -``` - -## 📏 Evaluation Metrics - -We use several metrics to evaluate the performance of different memory techniques: - -1. **BLEU Score**: Measures the similarity between the model's response and the ground truth -2. **F1 Score**: Measures the harmonic mean of precision and recall -3. **LLM Score**: A binary score (0 or 1) determined by an LLM judge evaluating the correctness of responses -4. **Token Consumption**: Number of tokens required to generate final answer. -5. **Latency**: Time required during search and to generate response. - -## 📚 Citation - -If you use this code or dataset in your research, please cite our paper: - -```bibtex -@article{mem0, - title={Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory}, - author={Chhikara, Prateek and Khant, Dev and Aryan, Saket and Singh, Taranjeet and Yadav, Deshraj}, - journal={arXiv preprint arXiv:2504.19413}, - year={2025} -} -``` - -## 📄 License - -[MIT License](LICENSE) - -## 👥 Contributors - -- [Prateek Chhikara](https://github.com/prateekchhikara) -- [Dev Khant](https://github.com/Dev-Khant) -- [Saket Aryan](https://github.com/whysosaket) -- [Taranjeet Singh](https://github.com/taranjeet) -- [Deshraj Yadav](https://github.com/deshraj) - diff --git a/evaluation/evals.py b/evaluation/evals.py deleted file mode 100644 index 5045acf3a..000000000 --- a/evaluation/evals.py +++ /dev/null @@ -1,81 +0,0 @@ -import argparse -import concurrent.futures -import json -import threading -from collections import defaultdict - -from metrics.llm_judge import evaluate_llm_judge -from metrics.utils import calculate_bleu_scores, calculate_metrics -from tqdm import tqdm - - -def process_item(item_data): - k, v = item_data - local_results = defaultdict(list) - - for item in v: - gt_answer = str(item["answer"]) - pred_answer = str(item["response"]) - category = str(item["category"]) - question = str(item["question"]) - - # Skip category 5 - if category == "5": - continue - - metrics = calculate_metrics(pred_answer, gt_answer) - bleu_scores = calculate_bleu_scores(pred_answer, gt_answer) - llm_score = evaluate_llm_judge(question, gt_answer, pred_answer) - - local_results[k].append( - { - "question": question, - "answer": gt_answer, - "response": pred_answer, - "category": category, - "bleu_score": bleu_scores["bleu1"], - "f1_score": metrics["f1"], - "llm_score": llm_score, - } - ) - - return local_results - - -def main(): - parser = argparse.ArgumentParser(description="Evaluate RAG results") - parser.add_argument( - "--input_file", type=str, default="results/rag_results_500_k1.json", help="Path to the input dataset file" - ) - parser.add_argument( - "--output_file", type=str, default="evaluation_metrics.json", help="Path to save the evaluation results" - ) - parser.add_argument("--max_workers", type=int, default=10, help="Maximum number of worker threads") - - args = parser.parse_args() - - with open(args.input_file, "r") as f: - data = json.load(f) - - results = defaultdict(list) - results_lock = threading.Lock() - - # Use ThreadPoolExecutor with specified workers - with concurrent.futures.ThreadPoolExecutor(max_workers=args.max_workers) as executor: - futures = [executor.submit(process_item, item_data) for item_data in data.items()] - - for future in tqdm(concurrent.futures.as_completed(futures), total=len(futures)): - local_results = future.result() - with results_lock: - for k, items in local_results.items(): - results[k].extend(items) - - # Save results to JSON file - with open(args.output_file, "w") as f: - json.dump(results, f, indent=4) - - print(f"Results saved to {args.output_file}") - - -if __name__ == "__main__": - main() diff --git a/evaluation/generate_scores.py b/evaluation/generate_scores.py deleted file mode 100644 index 8cb4e848b..000000000 --- a/evaluation/generate_scores.py +++ /dev/null @@ -1,34 +0,0 @@ -import json - -import pandas as pd - -# Load the evaluation metrics data -with open("evaluation_metrics.json", "r") as f: - data = json.load(f) - -# Flatten the data into a list of question items -all_items = [] -for key in data: - all_items.extend(data[key]) - -# Convert to DataFrame -df = pd.DataFrame(all_items) - -# Convert category to numeric type -df["category"] = pd.to_numeric(df["category"]) - -# Calculate mean scores by category -result = df.groupby("category").agg({"bleu_score": "mean", "f1_score": "mean", "llm_score": "mean"}).round(4) - -# Add count of questions per category -result["count"] = df.groupby("category").size() - -# Print the results -print("Mean Scores Per Category:") -print(result) - -# Calculate overall means -overall_means = df.agg({"bleu_score": "mean", "f1_score": "mean", "llm_score": "mean"}).round(4) - -print("\nOverall Mean Scores:") -print(overall_means) diff --git a/evaluation/metrics/llm_judge.py b/evaluation/metrics/llm_judge.py deleted file mode 100644 index 55c946a0d..000000000 --- a/evaluation/metrics/llm_judge.py +++ /dev/null @@ -1,130 +0,0 @@ -import argparse -import json -from collections import defaultdict - -import numpy as np -from openai import OpenAI - -from mem0.memory.utils import extract_json - -client = OpenAI() - -ACCURACY_PROMPT = """ -Your task is to label an answer to a question as ’CORRECT’ or ’WRONG’. You will be given the following data: - (1) a question (posed by one user to another user), - (2) a ’gold’ (ground truth) answer, - (3) a generated answer -which you will score as CORRECT/WRONG. - -The point of the question is to ask about something one user should know about the other user based on their prior conversations. -The gold answer will usually be a concise and short answer that includes the referenced topic, for example: -Question: Do you remember what I got the last time I went to Hawaii? -Gold answer: A shell necklace -The generated answer might be much longer, but you should be generous with your grading - as long as it touches on the same topic as the gold answer, it should be counted as CORRECT. - -For time related questions, the gold answer will be a specific date, month, year, etc. The generated answer might be much longer or use relative time references (like "last Tuesday" or "next month"), but you should be generous with your grading - as long as it refers to the same date or time period as the gold answer, it should be counted as CORRECT. Even if the format differs (e.g., "May 7th" vs "7 May"), consider it CORRECT if it's the same date. - -Now it's time for the real question: -Question: {question} -Gold answer: {gold_answer} -Generated answer: {generated_answer} - -First, provide a short (one sentence) explanation of your reasoning, then finish with CORRECT or WRONG. -Do NOT include both CORRECT and WRONG in your response, or it will break the evaluation script. - -Just return the label CORRECT or WRONG in a json format with the key as "label". -""" - - -def evaluate_llm_judge(question, gold_answer, generated_answer): - """Evaluate the generated answer against the gold answer using an LLM judge.""" - response = client.chat.completions.create( - model="gpt-4o-mini", - messages=[ - { - "role": "user", - "content": ACCURACY_PROMPT.format( - question=question, gold_answer=gold_answer, generated_answer=generated_answer - ), - } - ], - response_format={"type": "json_object"}, - temperature=0.0, - ) - label = json.loads(extract_json(response.choices[0].message.content))["label"] - return 1 if label == "CORRECT" else 0 - - -def main(): - """Main function to evaluate RAG results using LLM judge.""" - parser = argparse.ArgumentParser(description="Evaluate RAG results using LLM judge") - parser.add_argument( - "--input_file", - type=str, - default="results/default_run_v4_k30_new_graph.json", - help="Path to the input dataset file", - ) - - args = parser.parse_args() - - dataset_path = args.input_file - output_path = f"results/llm_judge_{dataset_path.split('/')[-1]}" - - with open(dataset_path, "r") as f: - data = json.load(f) - - LLM_JUDGE = defaultdict(list) - RESULTS = defaultdict(list) - - index = 0 - for k, v in data.items(): - for x in v: - question = x["question"] - gold_answer = x["answer"] - generated_answer = x["response"] - category = x["category"] - - # Skip category 5 - if int(category) == 5: - continue - - # Evaluate the answer - label = evaluate_llm_judge(question, gold_answer, generated_answer) - LLM_JUDGE[category].append(label) - - # Store the results - RESULTS[index].append( - { - "question": question, - "gt_answer": gold_answer, - "response": generated_answer, - "category": category, - "llm_label": label, - } - ) - - # Save intermediate results - with open(output_path, "w") as f: - json.dump(RESULTS, f, indent=4) - - # Print current accuracy for all categories - print("All categories accuracy:") - for cat, results in LLM_JUDGE.items(): - if results: # Only print if there are results for this category - print(f" Category {cat}: {np.mean(results):.4f} ({sum(results)}/{len(results)})") - print("------------------------------------------") - index += 1 - - # Save final results - with open(output_path, "w") as f: - json.dump(RESULTS, f, indent=4) - - # Print final summary - print("PATH: ", dataset_path) - print("------------------------------------------") - for k, v in LLM_JUDGE.items(): - print(k, np.mean(v)) - - -if __name__ == "__main__": - main() diff --git a/evaluation/metrics/utils.py b/evaluation/metrics/utils.py deleted file mode 100644 index a832d5ad6..000000000 --- a/evaluation/metrics/utils.py +++ /dev/null @@ -1,211 +0,0 @@ -""" -Borrowed from https://github.com/WujiangXu/AgenticMemory/blob/main/utils.py - -@article{xu2025mem, - title={A-mem: Agentic memory for llm agents}, - author={Xu, Wujiang and Liang, Zujie and Mei, Kai and Gao, Hang and Tan, Juntao - and Zhang, Yongfeng}, - journal={arXiv preprint arXiv:2502.12110}, - year={2025} -} -""" - -import statistics -from collections import defaultdict -from typing import Dict, List, Union - -import nltk -from bert_score import score as bert_score -from nltk.translate.bleu_score import SmoothingFunction, sentence_bleu -from nltk.translate.meteor_score import meteor_score -from rouge_score import rouge_scorer -from sentence_transformers import SentenceTransformer - -# from load_dataset import load_locomo_dataset, QA, Turn, Session, Conversation -from sentence_transformers.util import pytorch_cos_sim - -# Download required NLTK data -try: - nltk.download("punkt", quiet=True) - nltk.download("wordnet", quiet=True) -except Exception as e: - print(f"Error downloading NLTK data: {e}") - -# Initialize SentenceTransformer model (this will be reused) -try: - sentence_model = SentenceTransformer("all-MiniLM-L6-v2") -except Exception as e: - print(f"Warning: Could not load SentenceTransformer model: {e}") - sentence_model = None - - -def simple_tokenize(text): - """Simple tokenization function.""" - # Convert to string if not already - text = str(text) - return text.lower().replace(".", " ").replace(",", " ").replace("!", " ").replace("?", " ").split() - - -def calculate_rouge_scores(prediction: str, reference: str) -> Dict[str, float]: - """Calculate ROUGE scores for prediction against reference.""" - scorer = rouge_scorer.RougeScorer(["rouge1", "rouge2", "rougeL"], use_stemmer=True) - scores = scorer.score(reference, prediction) - return { - "rouge1_f": scores["rouge1"].fmeasure, - "rouge2_f": scores["rouge2"].fmeasure, - "rougeL_f": scores["rougeL"].fmeasure, - } - - -def calculate_bleu_scores(prediction: str, reference: str) -> Dict[str, float]: - """Calculate BLEU scores with different n-gram settings.""" - pred_tokens = nltk.word_tokenize(prediction.lower()) - ref_tokens = [nltk.word_tokenize(reference.lower())] - - weights_list = [(1, 0, 0, 0), (0.5, 0.5, 0, 0), (0.33, 0.33, 0.33, 0), (0.25, 0.25, 0.25, 0.25)] - smooth = SmoothingFunction().method1 - - scores = {} - for n, weights in enumerate(weights_list, start=1): - try: - score = sentence_bleu(ref_tokens, pred_tokens, weights=weights, smoothing_function=smooth) - except Exception as e: - print(f"Error calculating BLEU score: {e}") - score = 0.0 - scores[f"bleu{n}"] = score - - return scores - - -def calculate_bert_scores(prediction: str, reference: str) -> Dict[str, float]: - """Calculate BERTScore for semantic similarity.""" - try: - P, R, F1 = bert_score([prediction], [reference], lang="en", verbose=False) - return {"bert_precision": P.item(), "bert_recall": R.item(), "bert_f1": F1.item()} - except Exception as e: - print(f"Error calculating BERTScore: {e}") - return {"bert_precision": 0.0, "bert_recall": 0.0, "bert_f1": 0.0} - - -def calculate_meteor_score(prediction: str, reference: str) -> float: - """Calculate METEOR score for the prediction.""" - try: - return meteor_score([reference.split()], prediction.split()) - except Exception as e: - print(f"Error calculating METEOR score: {e}") - return 0.0 - - -def calculate_sentence_similarity(prediction: str, reference: str) -> float: - """Calculate sentence embedding similarity using SentenceBERT.""" - if sentence_model is None: - return 0.0 - try: - # Encode sentences - embedding1 = sentence_model.encode([prediction], convert_to_tensor=True) - embedding2 = sentence_model.encode([reference], convert_to_tensor=True) - - # Calculate cosine similarity - similarity = pytorch_cos_sim(embedding1, embedding2).item() - return float(similarity) - except Exception as e: - print(f"Error calculating sentence similarity: {e}") - return 0.0 - - -def calculate_metrics(prediction: str, reference: str) -> Dict[str, float]: - """Calculate comprehensive evaluation metrics for a prediction.""" - # Handle empty or None values - if not prediction or not reference: - return { - "exact_match": 0, - "f1": 0.0, - "rouge1_f": 0.0, - "rouge2_f": 0.0, - "rougeL_f": 0.0, - "bleu1": 0.0, - "bleu2": 0.0, - "bleu3": 0.0, - "bleu4": 0.0, - "bert_f1": 0.0, - "meteor": 0.0, - "sbert_similarity": 0.0, - } - - # Convert to strings if they're not already - prediction = str(prediction).strip() - reference = str(reference).strip() - - # Calculate exact match - exact_match = int(prediction.lower() == reference.lower()) - - # Calculate token-based F1 score - pred_tokens = set(simple_tokenize(prediction)) - ref_tokens = set(simple_tokenize(reference)) - common_tokens = pred_tokens & ref_tokens - - if not pred_tokens or not ref_tokens: - f1 = 0.0 - else: - precision = len(common_tokens) / len(pred_tokens) - recall = len(common_tokens) / len(ref_tokens) - f1 = 2 * precision * recall / (precision + recall) if (precision + recall) > 0 else 0.0 - - # Calculate all scores - bleu_scores = calculate_bleu_scores(prediction, reference) - - # Combine all metrics - metrics = { - "exact_match": exact_match, - "f1": f1, - **bleu_scores, - } - - return metrics - - -def aggregate_metrics( - all_metrics: List[Dict[str, float]], all_categories: List[int] -) -> Dict[str, Dict[str, Union[float, Dict[str, float]]]]: - """Calculate aggregate statistics for all metrics, split by category.""" - if not all_metrics: - return {} - - # Initialize aggregates for overall and per-category metrics - aggregates = defaultdict(list) - category_aggregates = defaultdict(lambda: defaultdict(list)) - - # Collect all values for each metric, both overall and per category - for metrics, category in zip(all_metrics, all_categories): - for metric_name, value in metrics.items(): - aggregates[metric_name].append(value) - category_aggregates[category][metric_name].append(value) - - # Calculate statistics for overall metrics - results = {"overall": {}} - - for metric_name, values in aggregates.items(): - results["overall"][metric_name] = { - "mean": statistics.mean(values), - "std": statistics.stdev(values) if len(values) > 1 else 0.0, - "median": statistics.median(values), - "min": min(values), - "max": max(values), - "count": len(values), - } - - # Calculate statistics for each category - for category in sorted(category_aggregates.keys()): - results[f"category_{category}"] = {} - for metric_name, values in category_aggregates[category].items(): - if values: # Only calculate if we have values for this category - results[f"category_{category}"][metric_name] = { - "mean": statistics.mean(values), - "std": statistics.stdev(values) if len(values) > 1 else 0.0, - "median": statistics.median(values), - "min": min(values), - "max": max(values), - "count": len(values), - } - - return results diff --git a/evaluation/prompts.py b/evaluation/prompts.py deleted file mode 100644 index 1d857b4f6..000000000 --- a/evaluation/prompts.py +++ /dev/null @@ -1,147 +0,0 @@ -ANSWER_PROMPT_GRAPH = """ - You are an intelligent memory assistant tasked with retrieving accurate information from - conversation memories. - - # CONTEXT: - You have access to memories from two speakers in a conversation. These memories contain - timestamped information that may be relevant to answering the question. You also have - access to knowledge graph relations for each user, showing connections between entities, - concepts, and events relevant to that user. - - # INSTRUCTIONS: - 1. Carefully analyze all provided memories from both speakers - 2. Pay special attention to the timestamps to determine the answer - 3. If the question asks about a specific event or fact, look for direct evidence in the - memories - 4. If the memories contain contradictory information, prioritize the most recent memory - 5. If there is a question about time references (like "last year", "two months ago", - etc.), calculate the actual date based on the memory timestamp. For example, if a - memory from 4 May 2022 mentions "went to India last year," then the trip occurred - in 2021. - 6. Always convert relative time references to specific dates, months, or years. For - example, convert "last year" to "2022" or "two months ago" to "March 2023" based - on the memory timestamp. Ignore the reference while answering the question. - 7. Focus only on the content of the memories from both speakers. Do not confuse - character names mentioned in memories with the actual users who created those - memories. - 8. The answer should be less than 5-6 words. - 9. Use the knowledge graph relations to understand the user's knowledge network and - identify important relationships between entities in the user's world. - - # APPROACH (Think step by step): - 1. First, examine all memories that contain information related to the question - 2. Examine the timestamps and content of these memories carefully - 3. Look for explicit mentions of dates, times, locations, or events that answer the - question - 4. If the answer requires calculation (e.g., converting relative time references), - show your work - 5. Analyze the knowledge graph relations to understand the user's knowledge context - 6. Formulate a precise, concise answer based solely on the evidence in the memories - 7. Double-check that your answer directly addresses the question asked - 8. Ensure your final answer is specific and avoids vague time references - - Memories for user {{speaker_1_user_id}}: - - {{speaker_1_memories}} - - Relations for user {{speaker_1_user_id}}: - - {{speaker_1_graph_memories}} - - Memories for user {{speaker_2_user_id}}: - - {{speaker_2_memories}} - - Relations for user {{speaker_2_user_id}}: - - {{speaker_2_graph_memories}} - - Question: {{question}} - - Answer: - """ - - -ANSWER_PROMPT = """ - You are an intelligent memory assistant tasked with retrieving accurate information from conversation memories. - - # CONTEXT: - You have access to memories from two speakers in a conversation. These memories contain - timestamped information that may be relevant to answering the question. - - # INSTRUCTIONS: - 1. Carefully analyze all provided memories from both speakers - 2. Pay special attention to the timestamps to determine the answer - 3. If the question asks about a specific event or fact, look for direct evidence in the memories - 4. If the memories contain contradictory information, prioritize the most recent memory - 5. If there is a question about time references (like "last year", "two months ago", etc.), - calculate the actual date based on the memory timestamp. For example, if a memory from - 4 May 2022 mentions "went to India last year," then the trip occurred in 2021. - 6. Always convert relative time references to specific dates, months, or years. For example, - convert "last year" to "2022" or "two months ago" to "March 2023" based on the memory - timestamp. Ignore the reference while answering the question. - 7. Focus only on the content of the memories from both speakers. Do not confuse character - names mentioned in memories with the actual users who created those memories. - 8. The answer should be less than 5-6 words. - - # APPROACH (Think step by step): - 1. First, examine all memories that contain information related to the question - 2. Examine the timestamps and content of these memories carefully - 3. Look for explicit mentions of dates, times, locations, or events that answer the question - 4. If the answer requires calculation (e.g., converting relative time references), show your work - 5. Formulate a precise, concise answer based solely on the evidence in the memories - 6. Double-check that your answer directly addresses the question asked - 7. Ensure your final answer is specific and avoids vague time references - - Memories for user {{speaker_1_user_id}}: - - {{speaker_1_memories}} - - Memories for user {{speaker_2_user_id}}: - - {{speaker_2_memories}} - - Question: {{question}} - - Answer: - """ - - -ANSWER_PROMPT_ZEP = """ - You are an intelligent memory assistant tasked with retrieving accurate information from conversation memories. - - # CONTEXT: - You have access to memories from a conversation. These memories contain - timestamped information that may be relevant to answering the question. - - # INSTRUCTIONS: - 1. Carefully analyze all provided memories - 2. Pay special attention to the timestamps to determine the answer - 3. If the question asks about a specific event or fact, look for direct evidence in the memories - 4. If the memories contain contradictory information, prioritize the most recent memory - 5. If there is a question about time references (like "last year", "two months ago", etc.), - calculate the actual date based on the memory timestamp. For example, if a memory from - 4 May 2022 mentions "went to India last year," then the trip occurred in 2021. - 6. Always convert relative time references to specific dates, months, or years. For example, - convert "last year" to "2022" or "two months ago" to "March 2023" based on the memory - timestamp. Ignore the reference while answering the question. - 7. Focus only on the content of the memories. Do not confuse character - names mentioned in memories with the actual users who created those memories. - 8. The answer should be less than 5-6 words. - - # APPROACH (Think step by step): - 1. First, examine all memories that contain information related to the question - 2. Examine the timestamps and content of these memories carefully - 3. Look for explicit mentions of dates, times, locations, or events that answer the question - 4. If the answer requires calculation (e.g., converting relative time references), show your work - 5. Formulate a precise, concise answer based solely on the evidence in the memories - 6. Double-check that your answer directly addresses the question asked - 7. Ensure your final answer is specific and avoids vague time references - - Memories: - - {{memories}} - - Question: {{question}} - Answer: - """ diff --git a/evaluation/run_experiments.py b/evaluation/run_experiments.py deleted file mode 100644 index 374602e1f..000000000 --- a/evaluation/run_experiments.py +++ /dev/null @@ -1,75 +0,0 @@ -import argparse -import os - -from src.langmem import LangMemManager -from src.memzero.add import MemoryADD -from src.memzero.search import MemorySearch -from src.openai.predict import OpenAIPredict -from src.rag import RAGManager -from src.utils import METHODS, TECHNIQUES -from src.zep.add import ZepAdd -from src.zep.search import ZepSearch - - -class Experiment: - def __init__(self, technique_type, chunk_size): - self.technique_type = technique_type - self.chunk_size = chunk_size - - def run(self): - print(f"Running experiment with technique: {self.technique_type}, chunk size: {self.chunk_size}") - - -def main(): - parser = argparse.ArgumentParser(description="Run memory experiments") - parser.add_argument("--technique_type", choices=TECHNIQUES, default="mem0", help="Memory technique to use") - parser.add_argument("--method", choices=METHODS, default="add", help="Method to use") - parser.add_argument("--chunk_size", type=int, default=1000, help="Chunk size for processing") - parser.add_argument("--output_folder", type=str, default="results/", help="Output path for results") - parser.add_argument("--top_k", type=int, default=30, help="Number of top memories to retrieve") - parser.add_argument("--filter_memories", action="store_true", default=False, help="Whether to filter memories") - parser.add_argument("--is_graph", action="store_true", default=False, help="Whether to use graph-based search") - parser.add_argument("--num_chunks", type=int, default=1, help="Number of chunks to process") - - args = parser.parse_args() - - # Add your experiment logic here - print(f"Running experiments with technique: {args.technique_type}, chunk size: {args.chunk_size}") - - if args.technique_type == "mem0": - if args.method == "add": - memory_manager = MemoryADD(data_path="dataset/locomo10.json", is_graph=args.is_graph) - memory_manager.process_all_conversations() - elif args.method == "search": - output_file_path = os.path.join( - args.output_folder, - f"mem0_results_top_{args.top_k}_filter_{args.filter_memories}_graph_{args.is_graph}.json", - ) - memory_searcher = MemorySearch(output_file_path, args.top_k, args.filter_memories, args.is_graph) - memory_searcher.process_data_file("dataset/locomo10.json") - elif args.technique_type == "rag": - output_file_path = os.path.join(args.output_folder, f"rag_results_{args.chunk_size}_k{args.num_chunks}.json") - rag_manager = RAGManager(data_path="dataset/locomo10_rag.json", chunk_size=args.chunk_size, k=args.num_chunks) - rag_manager.process_all_conversations(output_file_path) - elif args.technique_type == "langmem": - output_file_path = os.path.join(args.output_folder, "langmem_results.json") - langmem_manager = LangMemManager(dataset_path="dataset/locomo10_rag.json") - langmem_manager.process_all_conversations(output_file_path) - elif args.technique_type == "zep": - if args.method == "add": - zep_manager = ZepAdd(data_path="dataset/locomo10.json") - zep_manager.process_all_conversations("1") - elif args.method == "search": - output_file_path = os.path.join(args.output_folder, "zep_search_results.json") - zep_manager = ZepSearch() - zep_manager.process_data_file("dataset/locomo10.json", "1", output_file_path) - elif args.technique_type == "openai": - output_file_path = os.path.join(args.output_folder, "openai_results.json") - openai_manager = OpenAIPredict() - openai_manager.process_data_file("dataset/locomo10.json", output_file_path) - else: - raise ValueError(f"Invalid technique type: {args.technique_type}") - - -if __name__ == "__main__": - main() diff --git a/evaluation/src/langmem.py b/evaluation/src/langmem.py deleted file mode 100644 index 033343e41..000000000 --- a/evaluation/src/langmem.py +++ /dev/null @@ -1,185 +0,0 @@ -import json -import multiprocessing as mp -import os -import time -from collections import defaultdict - -from dotenv import load_dotenv -from jinja2 import Template -from langgraph.checkpoint.memory import MemorySaver -from langgraph.prebuilt import create_react_agent -from langgraph.store.memory import InMemoryStore -from langgraph.utils.config import get_store -from langmem import create_manage_memory_tool, create_search_memory_tool -from openai import OpenAI -from prompts import ANSWER_PROMPT -from tqdm import tqdm - -load_dotenv() - -client = OpenAI() - -ANSWER_PROMPT_TEMPLATE = Template(ANSWER_PROMPT) - - -def get_answer(question, speaker_1_user_id, speaker_1_memories, speaker_2_user_id, speaker_2_memories): - prompt = ANSWER_PROMPT_TEMPLATE.render( - question=question, - speaker_1_user_id=speaker_1_user_id, - speaker_1_memories=speaker_1_memories, - speaker_2_user_id=speaker_2_user_id, - speaker_2_memories=speaker_2_memories, - ) - - t1 = time.time() - response = client.chat.completions.create( - model=os.getenv("MODEL"), messages=[{"role": "system", "content": prompt}], temperature=0.0 - ) - t2 = time.time() - return response.choices[0].message.content, t2 - t1 - - -def prompt(state): - """Prepare the messages for the LLM.""" - store = get_store() - memories = store.search( - ("memories",), - query=state["messages"][-1].content, - ) - system_msg = f"""You are a helpful assistant. - -## Memories - -{memories} - -""" - return [{"role": "system", "content": system_msg}, *state["messages"]] - - -class LangMem: - def __init__( - self, - ): - self.store = InMemoryStore( - index={ - "dims": 1536, - "embed": f"openai:{os.getenv('EMBEDDING_MODEL')}", - } - ) - self.checkpointer = MemorySaver() # Checkpoint graph state - - self.agent = create_react_agent( - f"openai:{os.getenv('MODEL')}", - prompt=prompt, - tools=[ - create_manage_memory_tool(namespace=("memories",)), - create_search_memory_tool(namespace=("memories",)), - ], - store=self.store, - checkpointer=self.checkpointer, - ) - - def add_memory(self, message, config): - return self.agent.invoke({"messages": [{"role": "user", "content": message}]}, config=config) - - def search_memory(self, query, config): - try: - t1 = time.time() - response = self.agent.invoke({"messages": [{"role": "user", "content": query}]}, config=config) - t2 = time.time() - return response["messages"][-1].content, t2 - t1 - except Exception as e: - print(f"Error in search_memory: {e}") - return "", t2 - t1 - - -class LangMemManager: - def __init__(self, dataset_path): - self.dataset_path = dataset_path - with open(self.dataset_path, "r") as f: - self.data = json.load(f) - - def process_all_conversations(self, output_file_path): - OUTPUT = defaultdict(list) - - # Process conversations in parallel with multiple workers - def process_conversation(key_value_pair): - key, value = key_value_pair - result = defaultdict(list) - - chat_history = value["conversation"] - questions = value["question"] - - agent1 = LangMem() - agent2 = LangMem() - config = {"configurable": {"thread_id": f"thread-{key}"}} - speakers = set() - - # Identify speakers - for conv in chat_history: - speakers.add(conv["speaker"]) - - if len(speakers) != 2: - raise ValueError(f"Expected 2 speakers, got {len(speakers)}") - - speaker1 = list(speakers)[0] - speaker2 = list(speakers)[1] - - # Add memories for each message - for conv in tqdm(chat_history, desc=f"Processing messages {key}", leave=False): - message = f"{conv['timestamp']} | {conv['speaker']}: {conv['text']}" - if conv["speaker"] == speaker1: - agent1.add_memory(message, config) - elif conv["speaker"] == speaker2: - agent2.add_memory(message, config) - else: - raise ValueError(f"Expected speaker1 or speaker2, got {conv['speaker']}") - - # Process questions - for q in tqdm(questions, desc=f"Processing questions {key}", leave=False): - category = q["category"] - - if int(category) == 5: - continue - - answer = q["answer"] - question = q["question"] - response1, speaker1_memory_time = agent1.search_memory(question, config) - response2, speaker2_memory_time = agent2.search_memory(question, config) - - generated_answer, response_time = get_answer(question, speaker1, response1, speaker2, response2) - - result[key].append( - { - "question": question, - "answer": answer, - "response1": response1, - "response2": response2, - "category": category, - "speaker1_memory_time": speaker1_memory_time, - "speaker2_memory_time": speaker2_memory_time, - "response_time": response_time, - "response": generated_answer, - } - ) - - return result - - # Use multiprocessing to process conversations in parallel - with mp.Pool(processes=10) as pool: - results = list( - tqdm( - pool.imap(process_conversation, list(self.data.items())), - total=len(self.data), - desc="Processing conversations", - ) - ) - - # Combine results from all workers - for result in results: - for key, items in result.items(): - OUTPUT[key].extend(items) - - # Save final results - with open(output_file_path, "w") as f: - json.dump(OUTPUT, f, indent=4) diff --git a/evaluation/src/memzero/add.py b/evaluation/src/memzero/add.py deleted file mode 100644 index 7c8bd12e8..000000000 --- a/evaluation/src/memzero/add.py +++ /dev/null @@ -1,141 +0,0 @@ -import json -import os -import threading -import time -from concurrent.futures import ThreadPoolExecutor - -from dotenv import load_dotenv -from tqdm import tqdm - -from mem0 import MemoryClient - -load_dotenv() - - -# Update custom instructions -custom_instructions = """ -Generate personal memories that follow these guidelines: - -1. Each memory should be self-contained with complete context, including: - - The person's name, do not use "user" while creating memories - - Personal details (career aspirations, hobbies, life circumstances) - - Emotional states and reactions - - Ongoing journeys or future plans - - Specific dates when events occurred - -2. Include meaningful personal narratives focusing on: - - Identity and self-acceptance journeys - - Family planning and parenting - - Creative outlets and hobbies - - Mental health and self-care activities - - Career aspirations and education goals - - Important life events and milestones - -3. Make each memory rich with specific details rather than general statements - - Include timeframes (exact dates when possible) - - Name specific activities (e.g., "charity race for mental health" rather than just "exercise") - - Include emotional context and personal growth elements - -4. Extract memories only from user messages, not incorporating assistant responses - -5. Format each memory as a paragraph with a clear narrative structure that captures the person's experience, challenges, and aspirations -""" - - -class MemoryADD: - def __init__(self, data_path=None, batch_size=2, is_graph=False): - self.mem0_client = MemoryClient( - api_key=os.getenv("MEM0_API_KEY"), - org_id=os.getenv("MEM0_ORGANIZATION_ID"), - project_id=os.getenv("MEM0_PROJECT_ID"), - ) - - self.mem0_client.update_project(custom_instructions=custom_instructions) - self.batch_size = batch_size - self.data_path = data_path - self.data = None - self.is_graph = is_graph - if data_path: - self.load_data() - - def load_data(self): - with open(self.data_path, "r") as f: - self.data = json.load(f) - return self.data - - def add_memory(self, user_id, message, metadata, retries=3): - for attempt in range(retries): - try: - _ = self.mem0_client.add( - message, user_id=user_id, version="v2", metadata=metadata, enable_graph=self.is_graph - ) - return - except Exception as e: - if attempt < retries - 1: - time.sleep(1) # Wait before retrying - continue - else: - raise e - - def add_memories_for_speaker(self, speaker, messages, timestamp, desc): - for i in tqdm(range(0, len(messages), self.batch_size), desc=desc): - batch_messages = messages[i : i + self.batch_size] - self.add_memory(speaker, batch_messages, metadata={"timestamp": timestamp}) - - def process_conversation(self, item, idx): - conversation = item["conversation"] - speaker_a = conversation["speaker_a"] - speaker_b = conversation["speaker_b"] - - speaker_a_user_id = f"{speaker_a}_{idx}" - speaker_b_user_id = f"{speaker_b}_{idx}" - - # delete all memories for the two users - self.mem0_client.delete_all(user_id=speaker_a_user_id) - self.mem0_client.delete_all(user_id=speaker_b_user_id) - - for key in conversation.keys(): - if key in ["speaker_a", "speaker_b"] or "date" in key or "timestamp" in key: - continue - - date_time_key = key + "_date_time" - timestamp = conversation[date_time_key] - chats = conversation[key] - - messages = [] - messages_reverse = [] - for chat in chats: - if chat["speaker"] == speaker_a: - messages.append({"role": "user", "content": f"{speaker_a}: {chat['text']}"}) - messages_reverse.append({"role": "assistant", "content": f"{speaker_a}: {chat['text']}"}) - elif chat["speaker"] == speaker_b: - messages.append({"role": "assistant", "content": f"{speaker_b}: {chat['text']}"}) - messages_reverse.append({"role": "user", "content": f"{speaker_b}: {chat['text']}"}) - else: - raise ValueError(f"Unknown speaker: {chat['speaker']}") - - # add memories for the two users on different threads - thread_a = threading.Thread( - target=self.add_memories_for_speaker, - args=(speaker_a_user_id, messages, timestamp, "Adding Memories for Speaker A"), - ) - thread_b = threading.Thread( - target=self.add_memories_for_speaker, - args=(speaker_b_user_id, messages_reverse, timestamp, "Adding Memories for Speaker B"), - ) - - thread_a.start() - thread_b.start() - thread_a.join() - thread_b.join() - - print("Messages added successfully") - - def process_all_conversations(self, max_workers=10): - if not self.data: - raise ValueError("No data loaded. Please set data_path and call load_data() first.") - with ThreadPoolExecutor(max_workers=max_workers) as executor: - futures = [executor.submit(self.process_conversation, item, idx) for idx, item in enumerate(self.data)] - - for future in futures: - future.result() diff --git a/evaluation/src/memzero/search.py b/evaluation/src/memzero/search.py deleted file mode 100644 index cf99c2bed..000000000 --- a/evaluation/src/memzero/search.py +++ /dev/null @@ -1,215 +0,0 @@ -import json -import os -import time -from collections import defaultdict -from concurrent.futures import ThreadPoolExecutor - -from dotenv import load_dotenv -from jinja2 import Template -from openai import OpenAI -from prompts import ANSWER_PROMPT, ANSWER_PROMPT_GRAPH -from tqdm import tqdm - -from mem0 import MemoryClient - -load_dotenv() - - -class MemorySearch: - def __init__(self, output_path="results.json", top_k=10, filter_memories=False, is_graph=False): - self.mem0_client = MemoryClient( - api_key=os.getenv("MEM0_API_KEY"), - org_id=os.getenv("MEM0_ORGANIZATION_ID"), - project_id=os.getenv("MEM0_PROJECT_ID"), - ) - self.top_k = top_k - self.openai_client = OpenAI() - self.results = defaultdict(list) - self.output_path = output_path - self.filter_memories = filter_memories - self.is_graph = is_graph - - if self.is_graph: - self.ANSWER_PROMPT = ANSWER_PROMPT_GRAPH - else: - self.ANSWER_PROMPT = ANSWER_PROMPT - - def search_memory(self, user_id, query, max_retries=3, retry_delay=1): - start_time = time.time() - retries = 0 - while retries < max_retries: - try: - if self.is_graph: - print("Searching with graph") - memories = self.mem0_client.search( - query, - user_id=user_id, - top_k=self.top_k, - filter_memories=self.filter_memories, - enable_graph=True, - output_format="v1.1", - ) - else: - memories = self.mem0_client.search( - query, user_id=user_id, top_k=self.top_k, filter_memories=self.filter_memories - ) - break - except Exception as e: - print("Retrying...") - retries += 1 - if retries >= max_retries: - raise e - time.sleep(retry_delay) - - end_time = time.time() - if not self.is_graph: - semantic_memories = [ - { - "memory": memory["memory"], - "timestamp": memory["metadata"]["timestamp"], - "score": round(memory["score"], 2), - } - for memory in memories - ] - graph_memories = None - else: - semantic_memories = [ - { - "memory": memory["memory"], - "timestamp": memory["metadata"]["timestamp"], - "score": round(memory["score"], 2), - } - for memory in memories["results"] - ] - graph_memories = [ - {"source": relation["source"], "relationship": relation["relationship"], "target": relation["target"]} - for relation in memories["relations"] - ] - return semantic_memories, graph_memories, end_time - start_time - - def answer_question(self, speaker_1_user_id, speaker_2_user_id, question, answer, category): - speaker_1_memories, speaker_1_graph_memories, speaker_1_memory_time = self.search_memory( - speaker_1_user_id, question - ) - speaker_2_memories, speaker_2_graph_memories, speaker_2_memory_time = self.search_memory( - speaker_2_user_id, question - ) - - search_1_memory = [f"{item['timestamp']}: {item['memory']}" for item in speaker_1_memories] - search_2_memory = [f"{item['timestamp']}: {item['memory']}" for item in speaker_2_memories] - - template = Template(self.ANSWER_PROMPT) - answer_prompt = template.render( - speaker_1_user_id=speaker_1_user_id.split("_")[0], - speaker_2_user_id=speaker_2_user_id.split("_")[0], - speaker_1_memories=json.dumps(search_1_memory, indent=4), - speaker_2_memories=json.dumps(search_2_memory, indent=4), - speaker_1_graph_memories=json.dumps(speaker_1_graph_memories, indent=4), - speaker_2_graph_memories=json.dumps(speaker_2_graph_memories, indent=4), - question=question, - ) - - t1 = time.time() - response = self.openai_client.chat.completions.create( - model=os.getenv("MODEL"), messages=[{"role": "system", "content": answer_prompt}], temperature=0.0 - ) - t2 = time.time() - response_time = t2 - t1 - return ( - response.choices[0].message.content, - speaker_1_memories, - speaker_2_memories, - speaker_1_memory_time, - speaker_2_memory_time, - speaker_1_graph_memories, - speaker_2_graph_memories, - response_time, - ) - - def process_question(self, val, speaker_a_user_id, speaker_b_user_id): - question = val.get("question", "") - answer = val.get("answer", "") - category = val.get("category", -1) - evidence = val.get("evidence", []) - adversarial_answer = val.get("adversarial_answer", "") - - ( - response, - speaker_1_memories, - speaker_2_memories, - speaker_1_memory_time, - speaker_2_memory_time, - speaker_1_graph_memories, - speaker_2_graph_memories, - response_time, - ) = self.answer_question(speaker_a_user_id, speaker_b_user_id, question, answer, category) - - result = { - "question": question, - "answer": answer, - "category": category, - "evidence": evidence, - "response": response, - "adversarial_answer": adversarial_answer, - "speaker_1_memories": speaker_1_memories, - "speaker_2_memories": speaker_2_memories, - "num_speaker_1_memories": len(speaker_1_memories), - "num_speaker_2_memories": len(speaker_2_memories), - "speaker_1_memory_time": speaker_1_memory_time, - "speaker_2_memory_time": speaker_2_memory_time, - "speaker_1_graph_memories": speaker_1_graph_memories, - "speaker_2_graph_memories": speaker_2_graph_memories, - "response_time": response_time, - } - - # Save results after each question is processed - with open(self.output_path, "w") as f: - json.dump(self.results, f, indent=4) - - return result - - def process_data_file(self, file_path): - with open(file_path, "r") as f: - data = json.load(f) - - for idx, item in tqdm(enumerate(data), total=len(data), desc="Processing conversations"): - qa = item["qa"] - conversation = item["conversation"] - speaker_a = conversation["speaker_a"] - speaker_b = conversation["speaker_b"] - - speaker_a_user_id = f"{speaker_a}_{idx}" - speaker_b_user_id = f"{speaker_b}_{idx}" - - for question_item in tqdm( - qa, total=len(qa), desc=f"Processing questions for conversation {idx}", leave=False - ): - result = self.process_question(question_item, speaker_a_user_id, speaker_b_user_id) - self.results[idx].append(result) - - # Save results after each question is processed - with open(self.output_path, "w") as f: - json.dump(self.results, f, indent=4) - - # Final save at the end - with open(self.output_path, "w") as f: - json.dump(self.results, f, indent=4) - - def process_questions_parallel(self, qa_list, speaker_a_user_id, speaker_b_user_id, max_workers=1): - def process_single_question(val): - result = self.process_question(val, speaker_a_user_id, speaker_b_user_id) - # Save results after each question is processed - with open(self.output_path, "w") as f: - json.dump(self.results, f, indent=4) - return result - - with ThreadPoolExecutor(max_workers=max_workers) as executor: - results = list( - tqdm(executor.map(process_single_question, qa_list), total=len(qa_list), desc="Answering Questions") - ) - - # Final save at the end - with open(self.output_path, "w") as f: - json.dump(self.results, f, indent=4) - - return results diff --git a/evaluation/src/openai/predict.py b/evaluation/src/openai/predict.py deleted file mode 100644 index de80626a8..000000000 --- a/evaluation/src/openai/predict.py +++ /dev/null @@ -1,131 +0,0 @@ -import argparse -import json -import os -import time -from collections import defaultdict - -from dotenv import load_dotenv -from jinja2 import Template -from openai import OpenAI -from tqdm import tqdm - -load_dotenv() - - -ANSWER_PROMPT = """ - You are an intelligent memory assistant tasked with retrieving accurate information from conversation memories. - - # CONTEXT: - You have access to memories from a conversation. These memories contain - timestamped information that may be relevant to answering the question. - - # INSTRUCTIONS: - 1. Carefully analyze all provided memories - 2. Pay special attention to the timestamps to determine the answer - 3. If the question asks about a specific event or fact, look for direct evidence in the memories - 4. If the memories contain contradictory information, prioritize the most recent memory - 5. If there is a question about time references (like "last year", "two months ago", etc.), - calculate the actual date based on the memory timestamp. For example, if a memory from - 4 May 2022 mentions "went to India last year," then the trip occurred in 2021. - 6. Always convert relative time references to specific dates, months, or years. For example, - convert "last year" to "2022" or "two months ago" to "March 2023" based on the memory - timestamp. Ignore the reference while answering the question. - 7. Focus only on the content of the memories. Do not confuse character - names mentioned in memories with the actual users who created those memories. - 8. The answer should be less than 5-6 words. - - # APPROACH (Think step by step): - 1. First, examine all memories that contain information related to the question - 2. Examine the timestamps and content of these memories carefully - 3. Look for explicit mentions of dates, times, locations, or events that answer the question - 4. If the answer requires calculation (e.g., converting relative time references), show your work - 5. Formulate a precise, concise answer based solely on the evidence in the memories - 6. Double-check that your answer directly addresses the question asked - 7. Ensure your final answer is specific and avoids vague time references - - Memories: - - {{memories}} - - Question: {{question}} - Answer: - """ - - -class OpenAIPredict: - def __init__(self, model="gpt-4o-mini"): - self.model = model - self.openai_client = OpenAI() - self.results = defaultdict(list) - - def search_memory(self, idx): - with open(f"memories/{idx}.txt", "r") as file: - memories = file.read() - - return memories, 0 - - def process_question(self, val, idx): - question = val.get("question", "") - answer = val.get("answer", "") - category = val.get("category", -1) - evidence = val.get("evidence", []) - adversarial_answer = val.get("adversarial_answer", "") - - response, search_memory_time, response_time, context = self.answer_question(idx, question) - - result = { - "question": question, - "answer": answer, - "category": category, - "evidence": evidence, - "response": response, - "adversarial_answer": adversarial_answer, - "search_memory_time": search_memory_time, - "response_time": response_time, - "context": context, - } - - return result - - def answer_question(self, idx, question): - memories, search_memory_time = self.search_memory(idx) - - template = Template(ANSWER_PROMPT) - answer_prompt = template.render(memories=memories, question=question) - - t1 = time.time() - response = self.openai_client.chat.completions.create( - model=os.getenv("MODEL"), messages=[{"role": "system", "content": answer_prompt}], temperature=0.0 - ) - t2 = time.time() - response_time = t2 - t1 - return response.choices[0].message.content, search_memory_time, response_time, memories - - def process_data_file(self, file_path, output_file_path): - with open(file_path, "r") as f: - data = json.load(f) - - for idx, item in tqdm(enumerate(data), total=len(data), desc="Processing conversations"): - qa = item["qa"] - - for question_item in tqdm( - qa, total=len(qa), desc=f"Processing questions for conversation {idx}", leave=False - ): - result = self.process_question(question_item, idx) - self.results[idx].append(result) - - # Save results after each question is processed - with open(output_file_path, "w") as f: - json.dump(self.results, f, indent=4) - - # Final save at the end - with open(output_file_path, "w") as f: - json.dump(self.results, f, indent=4) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--output_file_path", type=str, required=True) - args = parser.parse_args() - openai_predict = OpenAIPredict() - openai_predict.process_data_file("../../dataset/locomo10.json", args.output_file_path) diff --git a/evaluation/src/rag.py b/evaluation/src/rag.py deleted file mode 100644 index 97a2e1d8c..000000000 --- a/evaluation/src/rag.py +++ /dev/null @@ -1,183 +0,0 @@ -import json -import os -import time -from collections import defaultdict - -import numpy as np -import tiktoken -from dotenv import load_dotenv -from jinja2 import Template -from openai import OpenAI -from tqdm import tqdm - -load_dotenv() - -PROMPT = """ -# Question: -{{QUESTION}} - -# Context: -{{CONTEXT}} - -# Short answer: -""" - - -class RAGManager: - def __init__(self, data_path="dataset/locomo10_rag.json", chunk_size=500, k=1): - self.model = os.getenv("MODEL") - self.client = OpenAI() - self.data_path = data_path - self.chunk_size = chunk_size - self.k = k - - def generate_response(self, question, context): - template = Template(PROMPT) - prompt = template.render(CONTEXT=context, QUESTION=question) - - max_retries = 3 - retries = 0 - - while retries <= max_retries: - try: - t1 = time.time() - response = self.client.chat.completions.create( - model=self.model, - messages=[ - { - "role": "system", - "content": "You are a helpful assistant that can answer " - "questions based on the provided context." - "If the question involves timing, use the conversation date for reference." - "Provide the shortest possible answer." - "Use words directly from the conversation when possible." - "Avoid using subjects in your answer.", - }, - {"role": "user", "content": prompt}, - ], - temperature=0, - ) - t2 = time.time() - return response.choices[0].message.content.strip(), t2 - t1 - except Exception as e: - retries += 1 - if retries > max_retries: - raise e - time.sleep(1) # Wait before retrying - - def clean_chat_history(self, chat_history): - cleaned_chat_history = "" - for c in chat_history: - cleaned_chat_history += f"{c['timestamp']} | {c['speaker']}: {c['text']}\n" - - return cleaned_chat_history - - def calculate_embedding(self, document): - response = self.client.embeddings.create(model=os.getenv("EMBEDDING_MODEL"), input=document) - return response.data[0].embedding - - def calculate_similarity(self, embedding1, embedding2): - return np.dot(embedding1, embedding2) / (np.linalg.norm(embedding1) * np.linalg.norm(embedding2)) - - def search(self, query, chunks, embeddings, k=1): - """ - Search for the top-k most similar chunks to the query. - - Args: - query: The query string - chunks: List of text chunks - embeddings: List of embeddings for each chunk - k: Number of top chunks to return (default: 1) - - Returns: - combined_chunks: The combined text of the top-k chunks - search_time: Time taken for the search - """ - t1 = time.time() - query_embedding = self.calculate_embedding(query) - similarities = [self.calculate_similarity(query_embedding, embedding) for embedding in embeddings] - - # Get indices of top-k most similar chunks - if k == 1: - # Original behavior - just get the most similar chunk - top_indices = [np.argmax(similarities)] - else: - # Get indices of top-k chunks - top_indices = np.argsort(similarities)[-k:][::-1] - - # Combine the top-k chunks - combined_chunks = "\n<->\n".join([chunks[i] for i in top_indices]) - - t2 = time.time() - return combined_chunks, t2 - t1 - - def create_chunks(self, chat_history, chunk_size=500): - """ - Create chunks using tiktoken for more accurate token counting - """ - # Get the encoding for the model - encoding = tiktoken.encoding_for_model(os.getenv("EMBEDDING_MODEL")) - - documents = self.clean_chat_history(chat_history) - - if chunk_size == -1: - return [documents], [] - - chunks = [] - - # Encode the document - tokens = encoding.encode(documents) - - # Split into chunks based on token count - for i in range(0, len(tokens), chunk_size): - chunk_tokens = tokens[i : i + chunk_size] - chunk = encoding.decode(chunk_tokens) - chunks.append(chunk) - - embeddings = [] - for chunk in chunks: - embedding = self.calculate_embedding(chunk) - embeddings.append(embedding) - - return chunks, embeddings - - def process_all_conversations(self, output_file_path): - with open(self.data_path, "r") as f: - data = json.load(f) - - FINAL_RESULTS = defaultdict(list) - for key, value in tqdm(data.items(), desc="Processing conversations"): - chat_history = value["conversation"] - questions = value["question"] - - chunks, embeddings = self.create_chunks(chat_history, self.chunk_size) - - for item in tqdm(questions, desc="Answering questions", leave=False): - question = item["question"] - answer = item.get("answer", "") - category = item["category"] - - if self.chunk_size == -1: - context = chunks[0] - search_time = 0 - else: - context, search_time = self.search(question, chunks, embeddings, k=self.k) - response, response_time = self.generate_response(question, context) - - FINAL_RESULTS[key].append( - { - "question": question, - "answer": answer, - "category": category, - "context": context, - "response": response, - "search_time": search_time, - "response_time": response_time, - } - ) - with open(output_file_path, "w+") as f: - json.dump(FINAL_RESULTS, f, indent=4) - - # Save results - with open(output_file_path, "w+") as f: - json.dump(FINAL_RESULTS, f, indent=4) diff --git a/evaluation/src/utils.py b/evaluation/src/utils.py deleted file mode 100644 index 7ee8e4935..000000000 --- a/evaluation/src/utils.py +++ /dev/null @@ -1,3 +0,0 @@ -TECHNIQUES = ["mem0", "rag", "langmem", "zep", "openai"] - -METHODS = ["add", "search"] diff --git a/evaluation/src/zep/add.py b/evaluation/src/zep/add.py deleted file mode 100644 index 43198b092..000000000 --- a/evaluation/src/zep/add.py +++ /dev/null @@ -1,76 +0,0 @@ -import argparse -import json -import os - -from dotenv import load_dotenv -from tqdm import tqdm -from zep_cloud import Message -from zep_cloud.client import Zep - -load_dotenv() - - -class ZepAdd: - def __init__(self, data_path=None): - self.zep_client = Zep(api_key=os.getenv("ZEP_API_KEY")) - self.data_path = data_path - self.data = None - if data_path: - self.load_data() - - def load_data(self): - with open(self.data_path, "r") as f: - self.data = json.load(f) - return self.data - - def process_conversation(self, run_id, item, idx): - conversation = item["conversation"] - - user_id = f"run_id_{run_id}_experiment_user_{idx}" - session_id = f"run_id_{run_id}_experiment_session_{idx}" - - # # delete all memories for the two users - # self.zep_client.user.delete(user_id=user_id) - # self.zep_client.memory.delete(session_id=session_id) - - self.zep_client.user.add(user_id=user_id) - self.zep_client.memory.add_session( - user_id=user_id, - session_id=session_id, - ) - - print("Starting to add memories... for user", user_id) - for key in tqdm(conversation.keys(), desc=f"Processing user {user_id}"): - if key in ["speaker_a", "speaker_b"] or "date" in key: - continue - - date_time_key = key + "_date_time" - timestamp = conversation[date_time_key] - chats = conversation[key] - - for chat in tqdm(chats, desc=f"Adding chats for {key}", leave=False): - self.zep_client.memory.add( - session_id=session_id, - messages=[ - Message( - role=chat["speaker"], - role_type="user", - content=f"{timestamp}: {chat['text']}", - ) - ], - ) - - def process_all_conversations(self, run_id): - if not self.data: - raise ValueError("No data loaded. Please set data_path and call load_data() first.") - for idx, item in tqdm(enumerate(self.data)): - if idx == 0: - self.process_conversation(run_id, item, idx) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--run_id", type=str, required=True) - args = parser.parse_args() - zep_add = ZepAdd(data_path="../../dataset/locomo10.json") - zep_add.process_all_conversations(args.run_id) diff --git a/evaluation/src/zep/search.py b/evaluation/src/zep/search.py deleted file mode 100644 index cfb1df447..000000000 --- a/evaluation/src/zep/search.py +++ /dev/null @@ -1,140 +0,0 @@ -import argparse -import json -import os -import time -from collections import defaultdict - -from dotenv import load_dotenv -from jinja2 import Template -from openai import OpenAI -from prompts import ANSWER_PROMPT_ZEP -from tqdm import tqdm -from zep_cloud import EntityEdge, EntityNode -from zep_cloud.client import Zep - -load_dotenv() - -TEMPLATE = """ -FACTS and ENTITIES represent relevant context to the current conversation. - -# These are the most relevant facts and their valid date ranges -# format: FACT (Date range: from - to) - -{facts} - - -# These are the most relevant entities -# ENTITY_NAME: entity summary - -{entities} - -""" - - -class ZepSearch: - def __init__(self): - self.zep_client = Zep(api_key=os.getenv("ZEP_API_KEY")) - self.results = defaultdict(list) - self.openai_client = OpenAI() - - def format_edge_date_range(self, edge: EntityEdge) -> str: - # return f"{datetime(edge.valid_at).strftime('%Y-%m-%d %H:%M:%S') if edge.valid_at else 'date unknown'} - {(edge.invalid_at.strftime('%Y-%m-%d %H:%M:%S') if edge.invalid_at else 'present')}" - return f"{edge.valid_at if edge.valid_at else 'date unknown'} - {(edge.invalid_at if edge.invalid_at else 'present')}" - - def compose_search_context(self, edges: list[EntityEdge], nodes: list[EntityNode]) -> str: - facts = [f" - {edge.fact} ({self.format_edge_date_range(edge)})" for edge in edges] - entities = [f" - {node.name}: {node.summary}" for node in nodes] - return TEMPLATE.format(facts="\n".join(facts), entities="\n".join(entities)) - - def search_memory(self, run_id, idx, query, max_retries=3, retry_delay=1): - start_time = time.time() - retries = 0 - while retries < max_retries: - try: - user_id = f"run_id_{run_id}_experiment_user_{idx}" - edges_results = ( - self.zep_client.graph.search( - user_id=user_id, reranker="cross_encoder", query=query, scope="edges", limit=20 - ) - ).edges - node_results = ( - self.zep_client.graph.search(user_id=user_id, reranker="rrf", query=query, scope="nodes", limit=20) - ).nodes - context = self.compose_search_context(edges_results, node_results) - break - except Exception as e: - print("Retrying...") - retries += 1 - if retries >= max_retries: - raise e - time.sleep(retry_delay) - - end_time = time.time() - - return context, end_time - start_time - - def process_question(self, run_id, val, idx): - question = val.get("question", "") - answer = val.get("answer", "") - category = val.get("category", -1) - evidence = val.get("evidence", []) - adversarial_answer = val.get("adversarial_answer", "") - - response, search_memory_time, response_time, context = self.answer_question(run_id, idx, question) - - result = { - "question": question, - "answer": answer, - "category": category, - "evidence": evidence, - "response": response, - "adversarial_answer": adversarial_answer, - "search_memory_time": search_memory_time, - "response_time": response_time, - "context": context, - } - - return result - - def answer_question(self, run_id, idx, question): - context, search_memory_time = self.search_memory(run_id, idx, question) - - template = Template(ANSWER_PROMPT_ZEP) - answer_prompt = template.render(memories=context, question=question) - - t1 = time.time() - response = self.openai_client.chat.completions.create( - model=os.getenv("MODEL"), messages=[{"role": "system", "content": answer_prompt}], temperature=0.0 - ) - t2 = time.time() - response_time = t2 - t1 - return response.choices[0].message.content, search_memory_time, response_time, context - - def process_data_file(self, file_path, run_id, output_file_path): - with open(file_path, "r") as f: - data = json.load(f) - - for idx, item in tqdm(enumerate(data), total=len(data), desc="Processing conversations"): - qa = item["qa"] - - for question_item in tqdm( - qa, total=len(qa), desc=f"Processing questions for conversation {idx}", leave=False - ): - result = self.process_question(run_id, question_item, idx) - self.results[idx].append(result) - - # Save results after each question is processed - with open(output_file_path, "w") as f: - json.dump(self.results, f, indent=4) - - # Final save at the end - with open(output_file_path, "w") as f: - json.dump(self.results, f, indent=4) - - -if __name__ == "__main__": - parser = argparse.ArgumentParser() - parser.add_argument("--run_id", type=str, required=True) - args = parser.parse_args() - zep_search = ZepSearch() - zep_search.process_data_file("../../dataset/locomo10.json", args.run_id, "results/zep_search_results.json") diff --git a/cookbooks/customer-support-chatbot.ipynb b/examples/notebooks/customer-support-chatbot.ipynb similarity index 100% rename from cookbooks/customer-support-chatbot.ipynb rename to examples/notebooks/customer-support-chatbot.ipynb diff --git a/cookbooks/helper/__init__.py b/examples/notebooks/helper/__init__.py similarity index 100% rename from cookbooks/helper/__init__.py rename to examples/notebooks/helper/__init__.py diff --git a/cookbooks/helper/mem0_teachability.py b/examples/notebooks/helper/mem0_teachability.py similarity index 100% rename from cookbooks/helper/mem0_teachability.py rename to examples/notebooks/helper/mem0_teachability.py diff --git a/cookbooks/mem0-autogen.ipynb b/examples/notebooks/mem0-autogen.ipynb similarity index 99% rename from cookbooks/mem0-autogen.ipynb rename to examples/notebooks/mem0-autogen.ipynb index 6e9adb0fe..50498c979 100644 --- a/cookbooks/mem0-autogen.ipynb +++ b/examples/notebooks/mem0-autogen.ipynb @@ -555,7 +555,7 @@ "# - Enables creation of AI agents with long-term memory and learning abilities.\n", "# - Improves consistency and reduces repetition in user-agent interactions.\n", "\n", - "from cookbooks.helper.mem0_teachability import Mem0Teachability\n", + "from helper.mem0_teachability import Mem0Teachability\n", "\n", "teachability = Mem0Teachability(\n", " verbosity=2, # for visibility of what's happening\n", diff --git a/mem0-plugin/.claude-plugin/plugin.json b/integrations/mem0-plugin/.claude-plugin/plugin.json similarity index 96% rename from mem0-plugin/.claude-plugin/plugin.json rename to integrations/mem0-plugin/.claude-plugin/plugin.json index 163731f33..0ab6583ee 100644 --- a/mem0-plugin/.claude-plugin/plugin.json +++ b/integrations/mem0-plugin/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "mem0", - "version": "0.2.9", + "version": "0.2.10", "description": "Persistent memory for Claude Code. Remembers decisions, patterns, and preferences across sessions.", "author": { "name": "Mem0", diff --git a/mem0-plugin/.codex-mcp.json b/integrations/mem0-plugin/.codex-mcp.json similarity index 100% rename from mem0-plugin/.codex-mcp.json rename to integrations/mem0-plugin/.codex-mcp.json diff --git a/mem0-plugin/.codex-plugin/plugin.json b/integrations/mem0-plugin/.codex-plugin/plugin.json similarity index 98% rename from mem0-plugin/.codex-plugin/plugin.json rename to integrations/mem0-plugin/.codex-plugin/plugin.json index 44aab8b6e..65c39271b 100644 --- a/mem0-plugin/.codex-plugin/plugin.json +++ b/integrations/mem0-plugin/.codex-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "mem0", - "version": "0.2.9", + "version": "0.2.10", "description": "Persistent memory for Codex. Remembers decisions, patterns, and preferences across sessions.", "author": { "name": "Mem0", diff --git a/mem0-plugin/.cursor-mcp.json b/integrations/mem0-plugin/.cursor-mcp.json similarity index 100% rename from mem0-plugin/.cursor-mcp.json rename to integrations/mem0-plugin/.cursor-mcp.json diff --git a/mem0-plugin/.cursor-plugin/plugin.json b/integrations/mem0-plugin/.cursor-plugin/plugin.json similarity index 96% rename from mem0-plugin/.cursor-plugin/plugin.json rename to integrations/mem0-plugin/.cursor-plugin/plugin.json index d82f5e06e..fafbf6aa6 100644 --- a/mem0-plugin/.cursor-plugin/plugin.json +++ b/integrations/mem0-plugin/.cursor-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "mem0", - "version": "0.2.9", + "version": "0.2.10", "description": "Mem0 memory layer for AI applications. Add persistent memory, personalization, and semantic search using the Mem0 Platform MCP server.", "author": { "name": "Mem0", diff --git a/mem0-plugin/.mcp.json b/integrations/mem0-plugin/.mcp.json similarity index 100% rename from mem0-plugin/.mcp.json rename to integrations/mem0-plugin/.mcp.json diff --git a/mem0-plugin/.opencode-plugin/CHANGELOG.md b/integrations/mem0-plugin/.opencode-plugin/CHANGELOG.md similarity index 90% rename from mem0-plugin/.opencode-plugin/CHANGELOG.md rename to integrations/mem0-plugin/.opencode-plugin/CHANGELOG.md index 1b88159a7..fa076768a 100644 --- a/mem0-plugin/.opencode-plugin/CHANGELOG.md +++ b/integrations/mem0-plugin/.opencode-plugin/CHANGELOG.md @@ -2,13 +2,14 @@ All notable changes to the `@mem0/opencode-plugin` will be documented in this file. -## 0.1.3 — File-context injection, session summaries & activity timeline +## 0.1.3 — File-context injection, session summaries & activity timeline, anonymous telemetry ### Added - **File-context injection (`tool.execute.before` / Read):** Before the agent reads a file, the plugin searches mem0 for memories referencing that file path and injects prior work as system context. Gates on file size (>= 1,500 bytes). Gives the agent "I've worked on this file before" awareness automatically. - **Stop hook session summary (`experimental.session.compacting`):** Enhanced session compaction to store a structured `session_summary` memory with `infer=True`, letting the mem0 backend AI extract key facts (request, decisions, learnings, next steps). Previously only stored a raw stats string. - **SessionStart activity timeline:** The initial memory loading now formats recent memories with type icons (⚖️ decision, 🔴 bug_fix, 🔵 task_learning, etc.) and relative age indicators (2h ago, 1d ago) instead of bare text. Provides a visual "Recent Activity" timeline on first message. +- **PostHog telemetry (`telemetry.ts`):** Anonymous, fire-and-forget usage events. Opt out with `MEM0_TELEMETRY=false`. Only fires when an API key is present; never sends memory content, prompts, or the API key — only an anonymized `sha256(apiKey)[:32]` identity plus event type, platform, and plugin version. Emits the same schema as the Mem0 editor plugin (`plugin.*` events, `source: "plugin"`, `platform: "opencode"`) so OpenCode appears as a `platform` in the shared plugin dashboard. Events: `plugin.session_start` (with memory count) and `plugin.tool_use` (`add` / `search` / `update` / `delete`). ### Changed diff --git a/mem0-plugin/.opencode-plugin/LICENSE b/integrations/mem0-plugin/.opencode-plugin/LICENSE similarity index 100% rename from mem0-plugin/.opencode-plugin/LICENSE rename to integrations/mem0-plugin/.opencode-plugin/LICENSE diff --git a/mem0-plugin/.opencode-plugin/README.md b/integrations/mem0-plugin/.opencode-plugin/README.md similarity index 97% rename from mem0-plugin/.opencode-plugin/README.md rename to integrations/mem0-plugin/.opencode-plugin/README.md index fac980ad7..680f300f7 100644 --- a/mem0-plugin/.opencode-plugin/README.md +++ b/integrations/mem0-plugin/.opencode-plugin/README.md @@ -17,7 +17,7 @@ opencode plugin @mem0/opencode-plugin **Or let your agent do it** — paste this into OpenCode: ``` -Install @mem0/opencode-plugin by following https://raw.githubusercontent.com/mem0ai/mem0/main/mem0-plugin/.opencode-plugin/README.md +Install @mem0/opencode-plugin by following https://raw.githubusercontent.com/mem0ai/mem0/main/integrations/mem0-plugin/.opencode-plugin/README.md ``` All commands auto-add the plugin and MCP server to your `~/.config/opencode/opencode.json`. No manual config needed. diff --git a/mem0-plugin/.opencode-plugin/bun.lock b/integrations/mem0-plugin/.opencode-plugin/bun.lock similarity index 99% rename from mem0-plugin/.opencode-plugin/bun.lock rename to integrations/mem0-plugin/.opencode-plugin/bun.lock index 2ce24e0eb..1783255c5 100644 --- a/mem0-plugin/.opencode-plugin/bun.lock +++ b/integrations/mem0-plugin/.opencode-plugin/bun.lock @@ -6,7 +6,7 @@ "name": "@mem0/opencode-plugin", "dependencies": { "@opencode-ai/plugin": "^1.0.162", - "mem0ai": "^3.0.5", + "mem0ai": "^3.0.7", }, "devDependencies": { "bun-types": ">=1.3.14", @@ -462,7 +462,7 @@ "md5": ["md5@2.3.0", "", { "dependencies": { "charenc": "0.0.2", "crypt": "0.0.2", "is-buffer": "~1.1.6" } }, "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g=="], - "mem0ai": ["mem0ai@3.0.5", "", { "dependencies": { "axios": "^1.15.2", "openai": "^4.93.0", "uuid": "9.0.1", "zod": "^3.24.1" }, "peerDependencies": { "@anthropic-ai/sdk": "^0.40.1", "@azure/identity": "^4.0.0", "@azure/search-documents": "^12.0.0", "@cloudflare/workers-types": "^4.20250504.0", "@google/genai": "^1.2.0", "@langchain/core": "^1.1.47", "@mistralai/mistralai": "^1.5.2", "@qdrant/js-client-rest": "1.13.0", "@supabase/supabase-js": "^2.49.1", "@types/jest": "29.5.14", "@types/pg": "8.11.0", "better-sqlite3": "^12.6.2", "cloudflare": "^4.2.0", "compromise": "^14.0.0", "groq-sdk": "0.3.0", "natural": "^8.0.1", "ollama": "^0.5.14", "pg": "8.11.3", "redis": "^4.6.13" } }, "sha512-W/R59d5fMpUGHhPEnyoo36GSz5NFJbAs+vS4BxoIvE+t19mIJfoz/2FJSKIw80mT8AkeNYpDfzc/DYRu2IzYIw=="], + "mem0ai": ["mem0ai@3.0.7", "", { "dependencies": { "axios": "^1.16.0", "openai": "^4.93.0", "uuid": "9.0.1", "zod": "^3.24.1" }, "peerDependencies": { "@anthropic-ai/sdk": "^0.40.1", "@azure/identity": "^4.0.0", "@azure/search-documents": "^12.0.0", "@cloudflare/workers-types": "^4.20250504.0", "@google/genai": "^1.40.0", "@langchain/core": "^1.1.47", "@mistralai/mistralai": "^1.5.2", "@qdrant/js-client-rest": "^1.18.0", "@supabase/supabase-js": "^2.49.1", "@types/jest": "29.5.14", "@types/pg": "8.11.0", "better-sqlite3": "^12.6.2", "cloudflare": "^4.2.0", "compromise": "^14.0.0", "groq-sdk": "0.3.0", "natural": "^8.0.1", "ollama": "^0.5.14", "pg": "8.11.3", "redis": "^4.6.13" } }, "sha512-CUHzX7DyeKTHcI3aDsSqY9LXTD7GcFxf988796TuOa4yJgGuF2Xd2NROcBhVFRo3r9y8fVmbo3c5TF9jv1KlKw=="], "memjs": ["memjs@1.3.2", "", {}, "sha512-qUEg2g8vxPe+zPn09KidjIStHPtoBO8Cttm8bgJFWWabbsjQ9Av9Ky+6UcvKx6ue0LLb/LEhtcyQpRyKfzeXcg=="], diff --git a/mem0-plugin/.opencode-plugin/cli.ts b/integrations/mem0-plugin/.opencode-plugin/cli.ts similarity index 100% rename from mem0-plugin/.opencode-plugin/cli.ts rename to integrations/mem0-plugin/.opencode-plugin/cli.ts diff --git a/mem0-plugin/.opencode-plugin/opencode-mem0.ts b/integrations/mem0-plugin/.opencode-plugin/opencode-mem0.ts similarity index 98% rename from mem0-plugin/.opencode-plugin/opencode-mem0.ts rename to integrations/mem0-plugin/.opencode-plugin/opencode-mem0.ts index e91add6a1..e9a4aa10c 100644 --- a/mem0-plugin/.opencode-plugin/opencode-mem0.ts +++ b/integrations/mem0-plugin/.opencode-plugin/opencode-mem0.ts @@ -9,6 +9,7 @@ import { existsSync, readdirSync, cpSync, mkdirSync, readFileSync, writeFileSync import { homedir } from "os"; import { join } from "path"; import { createHash } from "crypto"; +import { captureEvent } from "./telemetry"; async function getUserId(): Promise { if (process.env.MEM0_USER_ID) return process.env.MEM0_USER_ID; @@ -368,6 +369,8 @@ const Mem0Plugin: Plugin = async (ctx) => { }); } catch {} } + + captureEvent("session_start", { memory_count: memoryCount }, apiKey); } if (NUDGE_RE.test(safeText)) { @@ -602,6 +605,17 @@ const Mem0Plugin: Plugin = async (ctx) => { if (MEM0_MCP_RE.test(toolName)) { if (toolName.includes("add_memory")) stats.adds++; if (toolName.includes("search")) stats.searches++; + + const tool = toolName.includes("add_memory") + ? "add_memory" + : toolName.includes("search") + ? "search_memories" + : toolName.includes("delete") + ? "delete_memory" + : toolName.includes("update") + ? "update_memory" + : "other"; + captureEvent("tool_use", { tool }, apiKey); } if (toolName === "bash" && toolOutput.length >= 50) { diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/context-loader/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/context-loader/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/context-loader/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/context-loader/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/dream/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/dream/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/dream/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/dream/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/export/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/export/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/export/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/export/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/forget/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/forget/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/forget/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/forget/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/health/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/health/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/health/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/health/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/import/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/import/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/import/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/import/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/list-projects/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/list-projects/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/list-projects/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/list-projects/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/LICENSE b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/LICENSE similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/LICENSE rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/LICENSE diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/README.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/README.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/README.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/README.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/client/differences.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/client/differences.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/client/differences.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/client/differences.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/client/node.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/client/node.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/client/node.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/client/node.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/client/python.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/client/python.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/client/python.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/client/python.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/api-reference.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/api-reference.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/api-reference.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/api-reference.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/architecture.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/architecture.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/architecture.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/architecture.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/features.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/features.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/features.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/features.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/integration-patterns.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/integration-patterns.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/integration-patterns.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/integration-patterns.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/quickstart.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/quickstart.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/quickstart.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/quickstart.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/sdk-guide.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/sdk-guide.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/sdk-guide.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/sdk-guide.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/use-cases.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/use-cases.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/use-cases.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/references/use-cases.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/mem0/scripts/mem0_doc_search.ts b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/scripts/mem0_doc_search.ts similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/mem0/scripts/mem0_doc_search.ts rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/mem0/scripts/mem0_doc_search.ts diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/memory-reviewer/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/memory-reviewer/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/memory-reviewer/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/memory-reviewer/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/onboard/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/onboard/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/onboard/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/onboard/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/peek/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/peek/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/peek/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/peek/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/pin/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/pin/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/pin/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/pin/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/remember/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/remember/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/remember/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/remember/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/stats/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/stats/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/stats/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/stats/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/switch-project/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/switch-project/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/switch-project/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/switch-project/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode-skills/tour/SKILL.md b/integrations/mem0-plugin/.opencode-plugin/opencode-skills/tour/SKILL.md similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode-skills/tour/SKILL.md rename to integrations/mem0-plugin/.opencode-plugin/opencode-skills/tour/SKILL.md diff --git a/mem0-plugin/.opencode-plugin/opencode.json b/integrations/mem0-plugin/.opencode-plugin/opencode.json similarity index 100% rename from mem0-plugin/.opencode-plugin/opencode.json rename to integrations/mem0-plugin/.opencode-plugin/opencode.json diff --git a/mem0-plugin/.opencode-plugin/package.json b/integrations/mem0-plugin/.opencode-plugin/package.json similarity index 95% rename from mem0-plugin/.opencode-plugin/package.json rename to integrations/mem0-plugin/.opencode-plugin/package.json index 53ebcac6e..721a7f31f 100644 --- a/mem0-plugin/.opencode-plugin/package.json +++ b/integrations/mem0-plugin/.opencode-plugin/package.json @@ -30,7 +30,7 @@ "repository": { "type": "git", "url": "https://github.com/mem0ai/mem0", - "directory": "mem0-plugin/.opencode-plugin" + "directory": "integrations/mem0-plugin/.opencode-plugin" }, "files": [ "dist", @@ -59,7 +59,7 @@ }, "dependencies": { "@opencode-ai/plugin": "^1.0.162", - "mem0ai": "^3.0.5" + "mem0ai": "^3.0.7" }, "devDependencies": { "bun-types": ">=1.3.14", diff --git a/integrations/mem0-plugin/.opencode-plugin/telemetry.test.ts b/integrations/mem0-plugin/.opencode-plugin/telemetry.test.ts new file mode 100644 index 000000000..9ef897a26 --- /dev/null +++ b/integrations/mem0-plugin/.opencode-plugin/telemetry.test.ts @@ -0,0 +1,51 @@ +import { afterEach, describe, expect, test } from "bun:test"; +import { buildEvent, captureEvent, isTelemetryEnabled } from "./telemetry"; + +const KEY = "m0-testkey123"; + +afterEach(() => { + delete process.env.MEM0_TELEMETRY; +}); + +describe("opencode telemetry", () => { + test("buildEvent uses the shared plugin.* schema with platform=opencode", () => { + const payload = buildEvent("session_start", { memory_count: 5 }, KEY); + expect(payload).not.toBeNull(); + const props = payload!.properties as Record; + expect(payload!.event).toBe("plugin.session_start"); + expect(props.source).toBe("plugin"); + expect(props.platform).toBe("opencode"); + expect(props.memory_count).toBe(5); + expect(props.$process_person_profile).toBe(false); + expect(typeof props.plugin_version).toBe("string"); + }); + + test("distinct_id is sha256(apiKey)[:32] — matches the editor plugin", async () => { + const { createHash } = await import("node:crypto"); + const expected = createHash("sha256").update(KEY).digest("hex").slice(0, 32); + expect(buildEvent("session_start", {}, KEY)!.distinct_id).toBe(expected); + }); + + test("system properties win over caller-supplied ones", () => { + const props = buildEvent("x", { platform: "HACK", source: "HACK" }, KEY)! + .properties as Record; + expect(props.platform).toBe("opencode"); + expect(props.source).toBe("plugin"); + }); + + test("returns null without an API key (no anonymous events)", () => { + expect(buildEvent("session_start", {}, undefined)).toBeNull(); + }); + + test("opt-out via MEM0_TELEMETRY disables events", () => { + process.env.MEM0_TELEMETRY = "false"; + expect(isTelemetryEnabled()).toBe(false); + expect(buildEvent("session_start", {}, KEY)).toBeNull(); + }); + + test("captureEvent never throws (and sends nothing when opted out)", () => { + process.env.MEM0_TELEMETRY = "false"; + expect(() => captureEvent("session_start", {}, KEY)).not.toThrow(); + expect(() => captureEvent("session_start", {}, undefined)).not.toThrow(); + }); +}); diff --git a/integrations/mem0-plugin/.opencode-plugin/telemetry.ts b/integrations/mem0-plugin/.opencode-plugin/telemetry.ts new file mode 100644 index 000000000..243a4cc71 --- /dev/null +++ b/integrations/mem0-plugin/.opencode-plugin/telemetry.ts @@ -0,0 +1,104 @@ +/** + * Plugin telemetry for the Mem0 OpenCode plugin — anonymous usage tracking + * via PostHog. + * + * Emits the SAME event schema as the Mem0 editor plugin's telemetry.py + * (event names prefixed `plugin.`, `source: "plugin"`, `platform: "opencode"`, + * `distinct_id = sha256(apiKey)[:32]`) so OpenCode shows up as just another + * `platform` value in the shared plugin dashboard instead of a separate + * event namespace. + * + * Fire-and-forget: never throws, never blocks, failures are swallowed. Only + * fires when an API key is present (same as the editor plugin — anonymous + * installs without a key emit nothing). Disable with MEM0_TELEMETRY=false. + * + * Never sends: memory content, API keys, raw user/project IDs. Only sends: + * event type, platform, plugin version, anonymized hash of the API key. + */ + +import { createHash } from "node:crypto"; +import { readFileSync } from "node:fs"; + +const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX"; +const POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/"; +const REQUEST_TIMEOUT_MS = 2_000; + +function _loadPluginVersion(): string { + // Source context: telemetry.ts sits next to package.json (./). + // Bundled context: dist/index.js sits one level below it (../). + for (const rel of ["./package.json", "../package.json"]) { + try { + const pkg = JSON.parse(readFileSync(new URL(rel, import.meta.url), "utf-8")); + if (pkg?.name === "@mem0/opencode-plugin" && pkg.version) return pkg.version; + } catch { + /* try next candidate */ + } + } + return "unknown"; +} + +const PLUGIN_VERSION = _loadPluginVersion(); + +export function isTelemetryEnabled(): boolean { + const val = process.env.MEM0_TELEMETRY; + if (val === undefined) return true; + const s = val.toLowerCase(); + return s !== "false" && s !== "0" && s !== "no" && s !== "off"; +} + +function distinctId(apiKey: string): string { + // Matches telemetry.py `_distinct_id()` so the same user is one person in + // PostHog whether they use OpenCode or any other Mem0 editor plugin. + return createHash("sha256").update(apiKey).digest("hex").slice(0, 32); +} + +/** + * Build the PostHog event payload, or null when telemetry is disabled or no + * API key is available. Pure (aside from env/version reads) and exported for + * testing. System-controlled properties are applied last so a caller cannot + * override `source`/`platform`/etc. + */ +export function buildEvent( + eventType: string, + properties: Record, + apiKey: string | undefined, +): Record | null { + if (!isTelemetryEnabled() || !apiKey) return null; + return { + api_key: POSTHOG_API_KEY, + distinct_id: distinctId(apiKey), + event: `plugin.${eventType}`, + properties: { + ...properties, + source: "plugin", + platform: "opencode", + plugin_version: PLUGIN_VERSION, + os: process.platform, + sample_rate: 1.0, + $process_person_profile: false, + $lib: "posthog-node", + }, + }; +} + +/** Send a usage event, fire-and-forget. Never throws, never blocks. */ +export function captureEvent( + eventType: string, + properties: Record, + apiKey: string | undefined, +): void { + const payload = buildEvent(eventType, properties, apiKey); + if (!payload) return; + try { + void fetch(POSTHOG_HOST, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(payload), + signal: AbortSignal.timeout(REQUEST_TIMEOUT_MS), + }).catch(() => { + /* fire-and-forget */ + }); + } catch { + /* never throw */ + } +} diff --git a/mem0-plugin/.opencode-plugin/tsconfig.json b/integrations/mem0-plugin/.opencode-plugin/tsconfig.json similarity index 88% rename from mem0-plugin/.opencode-plugin/tsconfig.json rename to integrations/mem0-plugin/.opencode-plugin/tsconfig.json index 97d3c9347..a635ca045 100644 --- a/mem0-plugin/.opencode-plugin/tsconfig.json +++ b/integrations/mem0-plugin/.opencode-plugin/tsconfig.json @@ -16,5 +16,5 @@ "emitDeclarationOnly": true }, "include": ["**/*"], - "exclude": ["node_modules", "dist"] + "exclude": ["node_modules", "dist", "**/*.test.ts"] } diff --git a/mem0-plugin/CHANGELOG.md b/integrations/mem0-plugin/CHANGELOG.md similarity index 96% rename from mem0-plugin/CHANGELOG.md rename to integrations/mem0-plugin/CHANGELOG.md index 4e6bdc907..e8712b798 100644 --- a/mem0-plugin/CHANGELOG.md +++ b/integrations/mem0-plugin/CHANGELOG.md @@ -2,6 +2,21 @@ All notable changes to the Mem0 plugin will be documented in this file. +## 0.2.10 — Accurate per-editor telemetry attribution + +### Fixed + +- **Antigravity counted as Claude Code:** `detect_platform()` (`scripts/telemetry.py`) now checks `ANTIGRAVITY_PLUGIN_ROOT` before the `CLAUDE_PLUGIN_ROOT` branch. Antigravity sets both env vars for compatibility, so every Antigravity session was previously attributed to `claude-code`. Telemetry now reports `platform: "antigravity"`. +- **Codex fell back to the generic `plugin` bucket:** Codex installs standalone hooks with absolute paths via `install_codex_hooks.py`, so `PLUGIN_ROOT` is never set at runtime and platform auto-detection failed. Each command in `hooks/codex-hooks.json` now pins `MEM0_PLATFORM=codex` inline (Codex runs hook commands through a shell). Telemetry now reports `platform: "codex"`. +- **Cursor attribution depended on the host env:** Cursor's `*_cursor.sh` wrappers delegate to the shared hook scripts, whose platform detection relied on Cursor exporting `CURSOR_PLUGIN_ROOT` to the subprocess. All five Cursor wrappers now `export MEM0_PLATFORM=cursor` before delegating. +- **`plugin_version` was identical for every editor:** telemetry read `.claude-plugin/plugin.json` for all bash-hook editors, so Antigravity reported `0.2.10` instead of its real `0.1.2`. `_load_plugin_version()` now reads the manifest matching the detected platform, so each editor reports its own version. + +### Added + +- **`MEM0_PLATFORM` override in `detect_platform()`:** An explicit platform marker that wins over env-var auto-detection, letting each editor label its telemetry reliably. New tests in `tests/test_telemetry.py` cover the override, Antigravity attribution, and the Cursor/Codex platform-pinning contracts. + +> Attribution fixes apply to telemetry emitted after users upgrade to this version; PostHog does not backfill past events. + ## 0.2.9 — File-context injection, session summaries & activity timeline ### Added diff --git a/mem0-plugin/README.md b/integrations/mem0-plugin/README.md similarity index 95% rename from mem0-plugin/README.md rename to integrations/mem0-plugin/README.md index 3093c9ced..090d80e64 100644 --- a/mem0-plugin/README.md +++ b/integrations/mem0-plugin/README.md @@ -90,14 +90,14 @@ git clone https://github.com/mem0ai/mem0.git ~/codex-plugins/mem0-source codex plugin marketplace add ~/codex-plugins/mem0-source ``` -This points Codex at the repo's `.agents/plugins/marketplace.json`, which references `mem0-plugin/` as the local source. Restart Codex, run `/plugins`, and install **Mem0** from the **Mem0 Plugins** marketplace. +This points Codex at the repo's `.agents/plugins/marketplace.json`, which references `integrations/mem0-plugin/` as the local source. Restart Codex, run `/plugins`, and install **Mem0** from the **Mem0 Plugins** marketplace. -> **Don't combine with Option A.** The plugin manifest auto-registers `mem0` as an MCP server via `mem0-plugin/.codex-mcp.json` — adding a manual `[mcp_servers.mem0]` block would duplicate the registration. +> **Don't combine with Option A.** The plugin manifest auto-registers `mem0` as an MCP server via `integrations/mem0-plugin/.codex-mcp.json` — adding a manual `[mcp_servers.mem0]` block would duplicate the registration. **Optional — enable lifecycle hooks.** Codex doesn't auto-wire hooks from plugin manifests; it only reads `~/.codex/hooks.json` (or `/.codex/hooks.json`) ([docs](https://developers.openai.com/codex/hooks)). Run the bundled installer once to merge Mem0's entries: ```bash -python3 ~/codex-plugins/mem0-source/mem0-plugin/scripts/install_codex_hooks.py +python3 ~/codex-plugins/mem0-source/integrations/mem0-plugin/scripts/install_codex_hooks.py ``` This merges three entries into `~/.codex/hooks.json` with absolute paths pointing into your clone: @@ -190,7 +190,7 @@ See [OpenCode integration docs](https://docs.mem0.ai/integrations/opencode) for ```bash # Install the plugin (MCP server, hooks, scripts) -npx degit mem0ai/mem0/mem0-plugin ~/.gemini/config/plugins/mem0 +npx degit mem0ai/mem0/integrations/mem0-plugin ~/.gemini/config/plugins/mem0 ``` This installs the MCP server, lifecycle hooks, and shared scripts. @@ -276,10 +276,10 @@ The background setup is idempotent and runs once per account (cached in `~/.mem0 ```bash # Dry-run -- prints current vs proposed, no changes: -python mem0-plugin/scripts/setup_coding_categories.py +python integrations/mem0-plugin/scripts/setup_coding_categories.py # Write explicitly: -python mem0-plugin/scripts/setup_coding_categories.py --apply +python integrations/mem0-plugin/scripts/setup_coding_categories.py --apply ``` Requires the `mem0ai` Python SDK (`pip install mem0ai`) and `MEM0_API_KEY` set. `project.update(custom_categories=[...])` always replaces the full list. diff --git a/mem0-plugin/hooks.json b/integrations/mem0-plugin/hooks.json similarity index 100% rename from mem0-plugin/hooks.json rename to integrations/mem0-plugin/hooks.json diff --git a/mem0-plugin/hooks/codex-hooks.json b/integrations/mem0-plugin/hooks/codex-hooks.json similarity index 70% rename from mem0-plugin/hooks/codex-hooks.json rename to integrations/mem0-plugin/hooks/codex-hooks.json index 383006e88..7e0cb002f 100644 --- a/mem0-plugin/hooks/codex-hooks.json +++ b/integrations/mem0-plugin/hooks/codex-hooks.json @@ -6,7 +6,7 @@ "hooks": [ { "type": "command", - "command": "${PLUGIN_ROOT}/scripts/block_memory_write.sh", + "command": "MEM0_PLATFORM=codex ${PLUGIN_ROOT}/scripts/block_memory_write.sh", "timeout": 3 } ] @@ -16,7 +16,7 @@ "hooks": [ { "type": "command", - "command": "${PLUGIN_ROOT}/scripts/enforce_metadata_defaults.sh", + "command": "MEM0_PLATFORM=codex ${PLUGIN_ROOT}/scripts/enforce_metadata_defaults.sh", "timeout": 3 } ] @@ -26,7 +26,7 @@ "hooks": [ { "type": "command", - "command": "${PLUGIN_ROOT}/scripts/on_file_read.sh", + "command": "MEM0_PLATFORM=codex ${PLUGIN_ROOT}/scripts/on_file_read.sh", "timeout": 5 } ] @@ -38,7 +38,7 @@ "hooks": [ { "type": "command", - "command": "${PLUGIN_ROOT}/scripts/on_session_start.sh", + "command": "MEM0_PLATFORM=codex ${PLUGIN_ROOT}/scripts/on_session_start.sh", "statusMessage": "Loading mem0 context..." } ] @@ -49,7 +49,7 @@ "hooks": [ { "type": "command", - "command": "${PLUGIN_ROOT}/scripts/on_user_prompt.sh", + "command": "MEM0_PLATFORM=codex ${PLUGIN_ROOT}/scripts/on_user_prompt.sh", "statusMessage": "Checking memory relevance...", "timeout": 12 } @@ -62,7 +62,7 @@ "hooks": [ { "type": "command", - "command": "${PLUGIN_ROOT}/scripts/on_post_tool_use.sh", + "command": "MEM0_PLATFORM=codex ${PLUGIN_ROOT}/scripts/on_post_tool_use.sh", "timeout": 3 } ] @@ -72,7 +72,7 @@ "hooks": [ { "type": "command", - "command": "${PLUGIN_ROOT}/scripts/on_bash_output.sh", + "command": "MEM0_PLATFORM=codex ${PLUGIN_ROOT}/scripts/on_bash_output.sh", "timeout": 12 } ] @@ -83,7 +83,7 @@ "hooks": [ { "type": "command", - "command": "${PLUGIN_ROOT}/scripts/on_stop.sh", + "command": "MEM0_PLATFORM=codex ${PLUGIN_ROOT}/scripts/on_stop.sh", "timeout": 30 } ] @@ -94,7 +94,7 @@ "hooks": [ { "type": "command", - "command": "${PLUGIN_ROOT}/scripts/on_pre_compact.sh", + "command": "MEM0_PLATFORM=codex ${PLUGIN_ROOT}/scripts/on_pre_compact.sh", "statusMessage": "Preparing pre-compaction summary..." } ] diff --git a/mem0-plugin/hooks/cursor-hooks.json b/integrations/mem0-plugin/hooks/cursor-hooks.json similarity index 100% rename from mem0-plugin/hooks/cursor-hooks.json rename to integrations/mem0-plugin/hooks/cursor-hooks.json diff --git a/mem0-plugin/hooks/hooks.json b/integrations/mem0-plugin/hooks/hooks.json similarity index 100% rename from mem0-plugin/hooks/hooks.json rename to integrations/mem0-plugin/hooks/hooks.json diff --git a/mem0-plugin/logo.svg b/integrations/mem0-plugin/logo.svg similarity index 100% rename from mem0-plugin/logo.svg rename to integrations/mem0-plugin/logo.svg diff --git a/mem0-plugin/mcp_config.json b/integrations/mem0-plugin/mcp_config.json similarity index 100% rename from mem0-plugin/mcp_config.json rename to integrations/mem0-plugin/mcp_config.json diff --git a/mem0-plugin/plugin.json b/integrations/mem0-plugin/plugin.json similarity index 96% rename from mem0-plugin/plugin.json rename to integrations/mem0-plugin/plugin.json index 0521b9ad0..ef931be88 100644 --- a/mem0-plugin/plugin.json +++ b/integrations/mem0-plugin/plugin.json @@ -1,7 +1,7 @@ { "id": "mem0", "name": "mem0", - "version": "0.1.1", + "version": "0.1.2", "description": "Persistent semantic memory for Antigravity agents. Cross-session, user-level recall via the Mem0 Platform MCP server. 16 slash commands, lifecycle hooks for auto-capture and metadata enforcement.", "author": { "name": "Mem0", "email": "support@mem0.ai" }, "publisher": "mem0ai", diff --git a/mem0-plugin/requirements.txt b/integrations/mem0-plugin/requirements.txt similarity index 100% rename from mem0-plugin/requirements.txt rename to integrations/mem0-plugin/requirements.txt diff --git a/mem0-plugin/scripts/_chunking.py b/integrations/mem0-plugin/scripts/_chunking.py similarity index 100% rename from mem0-plugin/scripts/_chunking.py rename to integrations/mem0-plugin/scripts/_chunking.py diff --git a/mem0-plugin/scripts/_formatting.py b/integrations/mem0-plugin/scripts/_formatting.py similarity index 100% rename from mem0-plugin/scripts/_formatting.py rename to integrations/mem0-plugin/scripts/_formatting.py diff --git a/mem0-plugin/scripts/_identity.py b/integrations/mem0-plugin/scripts/_identity.py similarity index 100% rename from mem0-plugin/scripts/_identity.py rename to integrations/mem0-plugin/scripts/_identity.py diff --git a/mem0-plugin/scripts/_identity.sh b/integrations/mem0-plugin/scripts/_identity.sh similarity index 100% rename from mem0-plugin/scripts/_identity.sh rename to integrations/mem0-plugin/scripts/_identity.sh diff --git a/mem0-plugin/scripts/_project.py b/integrations/mem0-plugin/scripts/_project.py similarity index 100% rename from mem0-plugin/scripts/_project.py rename to integrations/mem0-plugin/scripts/_project.py diff --git a/mem0-plugin/scripts/_project.sh b/integrations/mem0-plugin/scripts/_project.sh similarity index 100% rename from mem0-plugin/scripts/_project.sh rename to integrations/mem0-plugin/scripts/_project.sh diff --git a/mem0-plugin/scripts/_search.py b/integrations/mem0-plugin/scripts/_search.py similarity index 100% rename from mem0-plugin/scripts/_search.py rename to integrations/mem0-plugin/scripts/_search.py diff --git a/mem0-plugin/scripts/auto_capture.py b/integrations/mem0-plugin/scripts/auto_capture.py similarity index 100% rename from mem0-plugin/scripts/auto_capture.py rename to integrations/mem0-plugin/scripts/auto_capture.py diff --git a/mem0-plugin/scripts/auto_import.py b/integrations/mem0-plugin/scripts/auto_import.py similarity index 100% rename from mem0-plugin/scripts/auto_import.py rename to integrations/mem0-plugin/scripts/auto_import.py diff --git a/mem0-plugin/scripts/auto_setup_categories.py b/integrations/mem0-plugin/scripts/auto_setup_categories.py similarity index 100% rename from mem0-plugin/scripts/auto_setup_categories.py rename to integrations/mem0-plugin/scripts/auto_setup_categories.py diff --git a/mem0-plugin/scripts/block_memory_write.sh b/integrations/mem0-plugin/scripts/block_memory_write.sh similarity index 100% rename from mem0-plugin/scripts/block_memory_write.sh rename to integrations/mem0-plugin/scripts/block_memory_write.sh diff --git a/mem0-plugin/scripts/block_memory_write_cursor.sh b/integrations/mem0-plugin/scripts/block_memory_write_cursor.sh similarity index 100% rename from mem0-plugin/scripts/block_memory_write_cursor.sh rename to integrations/mem0-plugin/scripts/block_memory_write_cursor.sh diff --git a/mem0-plugin/scripts/capture_compact_summary.py b/integrations/mem0-plugin/scripts/capture_compact_summary.py similarity index 100% rename from mem0-plugin/scripts/capture_compact_summary.py rename to integrations/mem0-plugin/scripts/capture_compact_summary.py diff --git a/mem0-plugin/scripts/capture_session_summary.py b/integrations/mem0-plugin/scripts/capture_session_summary.py similarity index 100% rename from mem0-plugin/scripts/capture_session_summary.py rename to integrations/mem0-plugin/scripts/capture_session_summary.py diff --git a/mem0-plugin/scripts/enforce_metadata_defaults.sh b/integrations/mem0-plugin/scripts/enforce_metadata_defaults.sh similarity index 100% rename from mem0-plugin/scripts/enforce_metadata_defaults.sh rename to integrations/mem0-plugin/scripts/enforce_metadata_defaults.sh diff --git a/mem0-plugin/scripts/ensure_deps.sh b/integrations/mem0-plugin/scripts/ensure_deps.sh similarity index 100% rename from mem0-plugin/scripts/ensure_deps.sh rename to integrations/mem0-plugin/scripts/ensure_deps.sh diff --git a/mem0-plugin/scripts/file_context.py b/integrations/mem0-plugin/scripts/file_context.py similarity index 100% rename from mem0-plugin/scripts/file_context.py rename to integrations/mem0-plugin/scripts/file_context.py diff --git a/mem0-plugin/scripts/import_competing_tools.py b/integrations/mem0-plugin/scripts/import_competing_tools.py similarity index 100% rename from mem0-plugin/scripts/import_competing_tools.py rename to integrations/mem0-plugin/scripts/import_competing_tools.py diff --git a/mem0-plugin/scripts/install_codex_hooks.py b/integrations/mem0-plugin/scripts/install_codex_hooks.py similarity index 100% rename from mem0-plugin/scripts/install_codex_hooks.py rename to integrations/mem0-plugin/scripts/install_codex_hooks.py diff --git a/mem0-plugin/scripts/load_settings.py b/integrations/mem0-plugin/scripts/load_settings.py similarity index 100% rename from mem0-plugin/scripts/load_settings.py rename to integrations/mem0-plugin/scripts/load_settings.py diff --git a/mem0-plugin/scripts/on_bash_output.sh b/integrations/mem0-plugin/scripts/on_bash_output.sh similarity index 100% rename from mem0-plugin/scripts/on_bash_output.sh rename to integrations/mem0-plugin/scripts/on_bash_output.sh diff --git a/mem0-plugin/scripts/on_file_read.sh b/integrations/mem0-plugin/scripts/on_file_read.sh similarity index 100% rename from mem0-plugin/scripts/on_file_read.sh rename to integrations/mem0-plugin/scripts/on_file_read.sh diff --git a/mem0-plugin/scripts/on_file_read_cursor.sh b/integrations/mem0-plugin/scripts/on_file_read_cursor.sh similarity index 100% rename from mem0-plugin/scripts/on_file_read_cursor.sh rename to integrations/mem0-plugin/scripts/on_file_read_cursor.sh diff --git a/mem0-plugin/scripts/on_post_tool_use.sh b/integrations/mem0-plugin/scripts/on_post_tool_use.sh similarity index 100% rename from mem0-plugin/scripts/on_post_tool_use.sh rename to integrations/mem0-plugin/scripts/on_post_tool_use.sh diff --git a/mem0-plugin/scripts/on_post_tool_use_cursor.sh b/integrations/mem0-plugin/scripts/on_post_tool_use_cursor.sh similarity index 80% rename from mem0-plugin/scripts/on_post_tool_use_cursor.sh rename to integrations/mem0-plugin/scripts/on_post_tool_use_cursor.sh index b0a5ca4d1..bcfb39ace 100755 --- a/mem0-plugin/scripts/on_post_tool_use_cursor.sh +++ b/integrations/mem0-plugin/scripts/on_post_tool_use_cursor.sh @@ -8,6 +8,9 @@ set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# Pin platform so the shared script's telemetry is attributed to cursor. +export MEM0_PLATFORM=cursor + # Run the shared tracker (output is ignored) "$SCRIPT_DIR/on_post_tool_use.sh" 2>/dev/null || true diff --git a/mem0-plugin/scripts/on_pre_compact.py b/integrations/mem0-plugin/scripts/on_pre_compact.py similarity index 100% rename from mem0-plugin/scripts/on_pre_compact.py rename to integrations/mem0-plugin/scripts/on_pre_compact.py diff --git a/mem0-plugin/scripts/on_pre_compact.sh b/integrations/mem0-plugin/scripts/on_pre_compact.sh similarity index 100% rename from mem0-plugin/scripts/on_pre_compact.sh rename to integrations/mem0-plugin/scripts/on_pre_compact.sh diff --git a/mem0-plugin/scripts/on_pre_compact_cursor.sh b/integrations/mem0-plugin/scripts/on_pre_compact_cursor.sh similarity index 79% rename from mem0-plugin/scripts/on_pre_compact_cursor.sh rename to integrations/mem0-plugin/scripts/on_pre_compact_cursor.sh index 74cf77a14..ca5a5a8e2 100755 --- a/mem0-plugin/scripts/on_pre_compact_cursor.sh +++ b/integrations/mem0-plugin/scripts/on_pre_compact_cursor.sh @@ -8,6 +8,9 @@ set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# Pin platform so the shared script's telemetry is attributed to cursor. +export MEM0_PLATFORM=cursor + TEXT=$("$SCRIPT_DIR/on_pre_compact.sh" 2>/dev/null || echo "") if [ -z "$TEXT" ]; then diff --git a/mem0-plugin/scripts/on_session_start.sh b/integrations/mem0-plugin/scripts/on_session_start.sh similarity index 96% rename from mem0-plugin/scripts/on_session_start.sh rename to integrations/mem0-plugin/scripts/on_session_start.sh index 649d89f5d..9f89e6745 100755 --- a/mem0-plugin/scripts/on_session_start.sh +++ b/integrations/mem0-plugin/scripts/on_session_start.sh @@ -13,23 +13,23 @@ SOURCE=$(echo "$INPUT" | jq -r '.source // "startup"' 2>/dev/null || echo "start if [ "$SOURCE" = "startup" ]; then python3 "$SCRIPT_DIR/session_stats.py" init 2>/dev/null || true - rm -f /tmp/mem0_recent_reads_${USER}_* 2>/dev/null || true + rm -f /tmp/mem0_recent_reads_${USER:-default}_* 2>/dev/null || true fi PYTHONPATH="$SCRIPT_DIR" python3 "$SCRIPT_DIR/load_settings.py" init 2>/dev/null || true -rm -f "/tmp/mem0_rubric_injected_${USER}" 2>/dev/null || true +rm -f "/tmp/mem0_rubric_injected_${USER:-default}" 2>/dev/null || true rm -f /tmp/mem0_rubric_* 2>/dev/null || true rm -f "/tmp/mem0_msg_count_${USER:-default}" 2>/dev/null || true MEM0_SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // ""' 2>/dev/null || echo "") if [ -z "$MEM0_SESSION_ID" ]; then MEM0_SESSION_ID="ses_$(date +%s)_$$" fi -printf '%s' "$MEM0_SESSION_ID" > "/tmp/mem0_session_id_${USER}" +printf '%s' "$MEM0_SESSION_ID" > "/tmp/mem0_session_id_${USER:-default}" export MEM0_SESSION_ID # Persist identity to Claude's env so Bash tool calls, MCP config, and other hooks see them if [ -n "${CLAUDE_ENV_FILE:-}" ]; then echo "export MEM0_SESSION_ID=\"$MEM0_SESSION_ID\"" >> "$CLAUDE_ENV_FILE" - echo "export MEM0_RESOLVED_USER_ID=\"${MEM0_RESOLVED_USER_ID:-$USER}\"" >> "$CLAUDE_ENV_FILE" + echo "export MEM0_RESOLVED_USER_ID=\"${MEM0_RESOLVED_USER_ID:-${USER:-default}}\"" >> "$CLAUDE_ENV_FILE" echo "export MEM0_PROJECT_ID=\"${MEM0_PROJECT_ID:-unknown}\"" >> "$CLAUDE_ENV_FILE" echo "export MEM0_BRANCH=\"${MEM0_BRANCH:-unknown}\"" >> "$CLAUDE_ENV_FILE" if [ -n "${MEM0_API_KEY:-}" ]; then diff --git a/mem0-plugin/scripts/on_session_start_cursor.sh b/integrations/mem0-plugin/scripts/on_session_start_cursor.sh similarity index 80% rename from mem0-plugin/scripts/on_session_start_cursor.sh rename to integrations/mem0-plugin/scripts/on_session_start_cursor.sh index 46ffb8006..905a5fb5b 100755 --- a/mem0-plugin/scripts/on_session_start_cursor.sh +++ b/integrations/mem0-plugin/scripts/on_session_start_cursor.sh @@ -8,6 +8,9 @@ set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# Pin platform so the shared script's telemetry is attributed to cursor. +export MEM0_PLATFORM=cursor + TEXT=$("$SCRIPT_DIR/on_session_start.sh" 2>/dev/null || echo "") if [ -z "$TEXT" ]; then diff --git a/mem0-plugin/scripts/on_stop.sh b/integrations/mem0-plugin/scripts/on_stop.sh similarity index 100% rename from mem0-plugin/scripts/on_stop.sh rename to integrations/mem0-plugin/scripts/on_stop.sh diff --git a/mem0-plugin/scripts/on_stop_cursor.sh b/integrations/mem0-plugin/scripts/on_stop_cursor.sh similarity index 89% rename from mem0-plugin/scripts/on_stop_cursor.sh rename to integrations/mem0-plugin/scripts/on_stop_cursor.sh index ac24c0253..77a8cd0a4 100755 --- a/mem0-plugin/scripts/on_stop_cursor.sh +++ b/integrations/mem0-plugin/scripts/on_stop_cursor.sh @@ -18,6 +18,8 @@ if [ -n "$AGENT_ID" ]; then fi SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# Pin platform so this hook's telemetry is attributed to cursor. +export MEM0_PLATFORM=cursor . "$SCRIPT_DIR/_identity.sh" 2>/dev/null || true if [ -z "${MEM0_API_KEY:-}" ]; then diff --git a/mem0-plugin/scripts/on_user_prompt.sh b/integrations/mem0-plugin/scripts/on_user_prompt.sh similarity index 100% rename from mem0-plugin/scripts/on_user_prompt.sh rename to integrations/mem0-plugin/scripts/on_user_prompt.sh diff --git a/mem0-plugin/scripts/on_user_prompt_cursor.sh b/integrations/mem0-plugin/scripts/on_user_prompt_cursor.sh similarity index 81% rename from mem0-plugin/scripts/on_user_prompt_cursor.sh rename to integrations/mem0-plugin/scripts/on_user_prompt_cursor.sh index bb56c52c6..17a82a7f2 100755 --- a/mem0-plugin/scripts/on_user_prompt_cursor.sh +++ b/integrations/mem0-plugin/scripts/on_user_prompt_cursor.sh @@ -8,6 +8,9 @@ set -uo pipefail SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +# Pin platform so the shared script's telemetry is attributed to cursor. +export MEM0_PLATFORM=cursor + TEXT=$("$SCRIPT_DIR/on_user_prompt.sh" 2>/dev/null || echo "") if [ -z "$TEXT" ]; then diff --git a/mem0-plugin/scripts/parse_export_file.py b/integrations/mem0-plugin/scripts/parse_export_file.py similarity index 100% rename from mem0-plugin/scripts/parse_export_file.py rename to integrations/mem0-plugin/scripts/parse_export_file.py diff --git a/mem0-plugin/scripts/parse_mem0_config.py b/integrations/mem0-plugin/scripts/parse_mem0_config.py similarity index 100% rename from mem0-plugin/scripts/parse_mem0_config.py rename to integrations/mem0-plugin/scripts/parse_mem0_config.py diff --git a/mem0-plugin/scripts/session_stats.py b/integrations/mem0-plugin/scripts/session_stats.py similarity index 100% rename from mem0-plugin/scripts/session_stats.py rename to integrations/mem0-plugin/scripts/session_stats.py diff --git a/mem0-plugin/scripts/session_timeline.py b/integrations/mem0-plugin/scripts/session_timeline.py similarity index 100% rename from mem0-plugin/scripts/session_timeline.py rename to integrations/mem0-plugin/scripts/session_timeline.py diff --git a/mem0-plugin/scripts/setup_coding_categories.py b/integrations/mem0-plugin/scripts/setup_coding_categories.py similarity index 100% rename from mem0-plugin/scripts/setup_coding_categories.py rename to integrations/mem0-plugin/scripts/setup_coding_categories.py diff --git a/mem0-plugin/scripts/telemetry.py b/integrations/mem0-plugin/scripts/telemetry.py similarity index 82% rename from mem0-plugin/scripts/telemetry.py rename to integrations/mem0-plugin/scripts/telemetry.py index 62948c587..afebcb67b 100644 --- a/mem0-plugin/scripts/telemetry.py +++ b/integrations/mem0-plugin/scripts/telemetry.py @@ -26,16 +26,27 @@ import sys import urllib.error import urllib.request +# Each editor surface ships its own manifest with its own version line +# (Antigravity is on 0.1.x while Claude/Cursor/Codex are on 0.2.x), so we read +# the manifest matching the detected platform rather than a single shared one. +_PLATFORM_MANIFESTS = { + "antigravity": ("..", "plugin.json"), + "claude-code": ("..", ".claude-plugin", "plugin.json"), + "cursor": ("..", ".cursor-plugin", "plugin.json"), + "codex": ("..", ".codex-plugin", "plugin.json"), +} +_DEFAULT_MANIFEST = ("..", ".claude-plugin", "plugin.json") -def _load_plugin_version() -> str: + +def _load_plugin_version(platform_name: str = "") -> str: + parts = _PLATFORM_MANIFESTS.get(platform_name, _DEFAULT_MANIFEST) try: - plugin_json = os.path.join(os.path.dirname(__file__), "..", ".claude-plugin", "plugin.json") + plugin_json = os.path.join(os.path.dirname(__file__), *parts) with open(plugin_json) as f: return json.load(f).get("version", "unknown") except (OSError, json.JSONDecodeError, KeyError): return "unknown" -PLUGIN_VERSION = _load_plugin_version() POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX" POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/" @@ -58,6 +69,11 @@ def _distinct_id() -> str: def detect_platform() -> str: + explicit = os.environ.get("MEM0_PLATFORM") + if explicit: + return explicit + if os.environ.get("ANTIGRAVITY_PLUGIN_ROOT"): + return "antigravity" if os.environ.get("PLUGIN_ROOT"): return "codex" if os.environ.get("CLAUDECODE") or os.environ.get("CLAUDE_PLUGIN_ROOT"): @@ -75,6 +91,7 @@ def is_enabled() -> bool: def build_posthog_payload(event_name: str, properties: dict | None = None) -> dict: project_id = os.environ.get("MEM0_PROJECT_ID") or "unknown" + plat = detect_platform() return { "api_key": POSTHOG_API_KEY, "distinct_id": _distinct_id(), @@ -82,8 +99,8 @@ def build_posthog_payload(event_name: str, properties: dict | None = None) -> di "properties": { **(properties or {}), "source": "plugin", - "platform": detect_platform(), - "plugin_version": PLUGIN_VERSION, + "platform": plat, + "plugin_version": _load_plugin_version(plat), "project_hash": _sha256(project_id), "os": sys.platform, "os_version": platform.version(), diff --git a/mem0-plugin/skills/context-loader/SKILL.md b/integrations/mem0-plugin/skills/context-loader/SKILL.md similarity index 100% rename from mem0-plugin/skills/context-loader/SKILL.md rename to integrations/mem0-plugin/skills/context-loader/SKILL.md diff --git a/mem0-plugin/skills/dream/SKILL.md b/integrations/mem0-plugin/skills/dream/SKILL.md similarity index 100% rename from mem0-plugin/skills/dream/SKILL.md rename to integrations/mem0-plugin/skills/dream/SKILL.md diff --git a/mem0-plugin/skills/export/SKILL.md b/integrations/mem0-plugin/skills/export/SKILL.md similarity index 100% rename from mem0-plugin/skills/export/SKILL.md rename to integrations/mem0-plugin/skills/export/SKILL.md diff --git a/mem0-plugin/skills/forget/SKILL.md b/integrations/mem0-plugin/skills/forget/SKILL.md similarity index 100% rename from mem0-plugin/skills/forget/SKILL.md rename to integrations/mem0-plugin/skills/forget/SKILL.md diff --git a/mem0-plugin/skills/health/SKILL.md b/integrations/mem0-plugin/skills/health/SKILL.md similarity index 100% rename from mem0-plugin/skills/health/SKILL.md rename to integrations/mem0-plugin/skills/health/SKILL.md diff --git a/mem0-plugin/skills/import/SKILL.md b/integrations/mem0-plugin/skills/import/SKILL.md similarity index 100% rename from mem0-plugin/skills/import/SKILL.md rename to integrations/mem0-plugin/skills/import/SKILL.md diff --git a/mem0-plugin/skills/list-projects/SKILL.md b/integrations/mem0-plugin/skills/list-projects/SKILL.md similarity index 100% rename from mem0-plugin/skills/list-projects/SKILL.md rename to integrations/mem0-plugin/skills/list-projects/SKILL.md diff --git a/mem0-plugin/skills/mem0/LICENSE b/integrations/mem0-plugin/skills/mem0/LICENSE similarity index 100% rename from mem0-plugin/skills/mem0/LICENSE rename to integrations/mem0-plugin/skills/mem0/LICENSE diff --git a/mem0-plugin/skills/mem0/README.md b/integrations/mem0-plugin/skills/mem0/README.md similarity index 100% rename from mem0-plugin/skills/mem0/README.md rename to integrations/mem0-plugin/skills/mem0/README.md diff --git a/mem0-plugin/skills/mem0/SKILL.md b/integrations/mem0-plugin/skills/mem0/SKILL.md similarity index 100% rename from mem0-plugin/skills/mem0/SKILL.md rename to integrations/mem0-plugin/skills/mem0/SKILL.md diff --git a/mem0-plugin/skills/mem0/client/differences.md b/integrations/mem0-plugin/skills/mem0/client/differences.md similarity index 100% rename from mem0-plugin/skills/mem0/client/differences.md rename to integrations/mem0-plugin/skills/mem0/client/differences.md diff --git a/mem0-plugin/skills/mem0/client/node.md b/integrations/mem0-plugin/skills/mem0/client/node.md similarity index 100% rename from mem0-plugin/skills/mem0/client/node.md rename to integrations/mem0-plugin/skills/mem0/client/node.md diff --git a/mem0-plugin/skills/mem0/client/python.md b/integrations/mem0-plugin/skills/mem0/client/python.md similarity index 100% rename from mem0-plugin/skills/mem0/client/python.md rename to integrations/mem0-plugin/skills/mem0/client/python.md diff --git a/mem0-plugin/skills/mem0/references/api-reference.md b/integrations/mem0-plugin/skills/mem0/references/api-reference.md similarity index 100% rename from mem0-plugin/skills/mem0/references/api-reference.md rename to integrations/mem0-plugin/skills/mem0/references/api-reference.md diff --git a/mem0-plugin/skills/mem0/references/architecture.md b/integrations/mem0-plugin/skills/mem0/references/architecture.md similarity index 100% rename from mem0-plugin/skills/mem0/references/architecture.md rename to integrations/mem0-plugin/skills/mem0/references/architecture.md diff --git a/mem0-plugin/skills/mem0/references/features.md b/integrations/mem0-plugin/skills/mem0/references/features.md similarity index 100% rename from mem0-plugin/skills/mem0/references/features.md rename to integrations/mem0-plugin/skills/mem0/references/features.md diff --git a/mem0-plugin/skills/mem0/references/integration-patterns.md b/integrations/mem0-plugin/skills/mem0/references/integration-patterns.md similarity index 100% rename from mem0-plugin/skills/mem0/references/integration-patterns.md rename to integrations/mem0-plugin/skills/mem0/references/integration-patterns.md diff --git a/mem0-plugin/skills/mem0/references/quickstart.md b/integrations/mem0-plugin/skills/mem0/references/quickstart.md similarity index 100% rename from mem0-plugin/skills/mem0/references/quickstart.md rename to integrations/mem0-plugin/skills/mem0/references/quickstart.md diff --git a/mem0-plugin/skills/mem0/references/sdk-guide.md b/integrations/mem0-plugin/skills/mem0/references/sdk-guide.md similarity index 100% rename from mem0-plugin/skills/mem0/references/sdk-guide.md rename to integrations/mem0-plugin/skills/mem0/references/sdk-guide.md diff --git a/mem0-plugin/skills/mem0/references/use-cases.md b/integrations/mem0-plugin/skills/mem0/references/use-cases.md similarity index 100% rename from mem0-plugin/skills/mem0/references/use-cases.md rename to integrations/mem0-plugin/skills/mem0/references/use-cases.md diff --git a/mem0-plugin/skills/mem0/scripts/mem0_doc_search.py b/integrations/mem0-plugin/skills/mem0/scripts/mem0_doc_search.py similarity index 100% rename from mem0-plugin/skills/mem0/scripts/mem0_doc_search.py rename to integrations/mem0-plugin/skills/mem0/scripts/mem0_doc_search.py diff --git a/mem0-plugin/skills/memory-reviewer/SKILL.md b/integrations/mem0-plugin/skills/memory-reviewer/SKILL.md similarity index 100% rename from mem0-plugin/skills/memory-reviewer/SKILL.md rename to integrations/mem0-plugin/skills/memory-reviewer/SKILL.md diff --git a/mem0-plugin/skills/onboard/SKILL.md b/integrations/mem0-plugin/skills/onboard/SKILL.md similarity index 100% rename from mem0-plugin/skills/onboard/SKILL.md rename to integrations/mem0-plugin/skills/onboard/SKILL.md diff --git a/mem0-plugin/skills/peek/SKILL.md b/integrations/mem0-plugin/skills/peek/SKILL.md similarity index 100% rename from mem0-plugin/skills/peek/SKILL.md rename to integrations/mem0-plugin/skills/peek/SKILL.md diff --git a/mem0-plugin/skills/pin/SKILL.md b/integrations/mem0-plugin/skills/pin/SKILL.md similarity index 100% rename from mem0-plugin/skills/pin/SKILL.md rename to integrations/mem0-plugin/skills/pin/SKILL.md diff --git a/mem0-plugin/skills/remember/SKILL.md b/integrations/mem0-plugin/skills/remember/SKILL.md similarity index 100% rename from mem0-plugin/skills/remember/SKILL.md rename to integrations/mem0-plugin/skills/remember/SKILL.md diff --git a/mem0-plugin/skills/stats/SKILL.md b/integrations/mem0-plugin/skills/stats/SKILL.md similarity index 100% rename from mem0-plugin/skills/stats/SKILL.md rename to integrations/mem0-plugin/skills/stats/SKILL.md diff --git a/mem0-plugin/skills/switch-project/SKILL.md b/integrations/mem0-plugin/skills/switch-project/SKILL.md similarity index 100% rename from mem0-plugin/skills/switch-project/SKILL.md rename to integrations/mem0-plugin/skills/switch-project/SKILL.md diff --git a/mem0-plugin/skills/tour/SKILL.md b/integrations/mem0-plugin/skills/tour/SKILL.md similarity index 100% rename from mem0-plugin/skills/tour/SKILL.md rename to integrations/mem0-plugin/skills/tour/SKILL.md diff --git a/mem0-plugin/tests/conftest.py b/integrations/mem0-plugin/tests/conftest.py similarity index 100% rename from mem0-plugin/tests/conftest.py rename to integrations/mem0-plugin/tests/conftest.py diff --git a/mem0-plugin/tests/test_auto_capture.py b/integrations/mem0-plugin/tests/test_auto_capture.py similarity index 100% rename from mem0-plugin/tests/test_auto_capture.py rename to integrations/mem0-plugin/tests/test_auto_capture.py diff --git a/mem0-plugin/tests/test_auto_setup_categories.py b/integrations/mem0-plugin/tests/test_auto_setup_categories.py similarity index 100% rename from mem0-plugin/tests/test_auto_setup_categories.py rename to integrations/mem0-plugin/tests/test_auto_setup_categories.py diff --git a/mem0-plugin/tests/test_coding_categories.py b/integrations/mem0-plugin/tests/test_coding_categories.py similarity index 100% rename from mem0-plugin/tests/test_coding_categories.py rename to integrations/mem0-plugin/tests/test_coding_categories.py diff --git a/mem0-plugin/tests/test_import_competing_tools.py b/integrations/mem0-plugin/tests/test_import_competing_tools.py similarity index 100% rename from mem0-plugin/tests/test_import_competing_tools.py rename to integrations/mem0-plugin/tests/test_import_competing_tools.py diff --git a/mem0-plugin/tests/test_parse_export_file.py b/integrations/mem0-plugin/tests/test_parse_export_file.py similarity index 100% rename from mem0-plugin/tests/test_parse_export_file.py rename to integrations/mem0-plugin/tests/test_parse_export_file.py diff --git a/mem0-plugin/tests/test_parse_mem0_config.py b/integrations/mem0-plugin/tests/test_parse_mem0_config.py similarity index 100% rename from mem0-plugin/tests/test_parse_mem0_config.py rename to integrations/mem0-plugin/tests/test_parse_mem0_config.py diff --git a/mem0-plugin/tests/test_project.py b/integrations/mem0-plugin/tests/test_project.py similarity index 100% rename from mem0-plugin/tests/test_project.py rename to integrations/mem0-plugin/tests/test_project.py diff --git a/mem0-plugin/tests/test_rubric_dedup.py b/integrations/mem0-plugin/tests/test_rubric_dedup.py similarity index 100% rename from mem0-plugin/tests/test_rubric_dedup.py rename to integrations/mem0-plugin/tests/test_rubric_dedup.py diff --git a/mem0-plugin/tests/test_search.py b/integrations/mem0-plugin/tests/test_search.py similarity index 100% rename from mem0-plugin/tests/test_search.py rename to integrations/mem0-plugin/tests/test_search.py diff --git a/mem0-plugin/tests/test_session_stats.py b/integrations/mem0-plugin/tests/test_session_stats.py similarity index 100% rename from mem0-plugin/tests/test_session_stats.py rename to integrations/mem0-plugin/tests/test_session_stats.py diff --git a/mem0-plugin/tests/test_telemetry.py b/integrations/mem0-plugin/tests/test_telemetry.py similarity index 56% rename from mem0-plugin/tests/test_telemetry.py rename to integrations/mem0-plugin/tests/test_telemetry.py index c0a75a21a..1c15a8158 100644 --- a/mem0-plugin/tests/test_telemetry.py +++ b/integrations/mem0-plugin/tests/test_telemetry.py @@ -89,7 +89,7 @@ def test_system_props_override_caller_props(monkeypatch): # System props must win assert props["source"] == "plugin" assert props["platform"] == telemetry.detect_platform() - assert props["plugin_version"] == telemetry.PLUGIN_VERSION + assert props["plugin_version"] == telemetry._load_plugin_version(telemetry.detect_platform()) # Caller-only props still present assert props["memory_count"] == 42 @@ -125,25 +125,31 @@ def test_hash_deterministic(): def test_platform_claude_code(monkeypatch): import telemetry - monkeypatch.setenv("CLAUDECODE", "1") + monkeypatch.delenv("MEM0_PLATFORM", raising=False) + monkeypatch.delenv("ANTIGRAVITY_PLUGIN_ROOT", raising=False) + monkeypatch.delenv("PLUGIN_ROOT", raising=False) monkeypatch.delenv("CURSOR_PLUGIN_ROOT", raising=False) - monkeypatch.delenv("CODEX_PLUGIN_ROOT", raising=False) + monkeypatch.setenv("CLAUDECODE", "1") assert telemetry.detect_platform() == "claude-code" def test_platform_cursor(monkeypatch): import telemetry + monkeypatch.delenv("MEM0_PLATFORM", raising=False) + monkeypatch.delenv("ANTIGRAVITY_PLUGIN_ROOT", raising=False) + monkeypatch.delenv("PLUGIN_ROOT", raising=False) monkeypatch.delenv("CLAUDECODE", raising=False) monkeypatch.delenv("CLAUDE_PLUGIN_ROOT", raising=False) monkeypatch.setenv("CURSOR_PLUGIN_ROOT", "/path") - monkeypatch.delenv("CODEX_PLUGIN_ROOT", raising=False) assert telemetry.detect_platform() == "cursor" def test_platform_codex(monkeypatch): import telemetry + monkeypatch.delenv("MEM0_PLATFORM", raising=False) + monkeypatch.delenv("ANTIGRAVITY_PLUGIN_ROOT", raising=False) monkeypatch.delenv("CLAUDECODE", raising=False) monkeypatch.delenv("CLAUDE_PLUGIN_ROOT", raising=False) monkeypatch.delenv("CURSOR_PLUGIN_ROOT", raising=False) @@ -151,6 +157,48 @@ def test_platform_codex(monkeypatch): assert telemetry.detect_platform() == "codex" +def test_platform_explicit_override(monkeypatch): + """MEM0_PLATFORM wins over auto-detection so each editor can label + itself reliably even when host env vars are ambiguous or absent.""" + import telemetry + + monkeypatch.setenv("CLAUDE_PLUGIN_ROOT", "/path") # conflicting auto-signal + monkeypatch.setenv("MEM0_PLATFORM", "cursor") + assert telemetry.detect_platform() == "cursor" + + +def test_platform_antigravity(monkeypatch): + """Antigravity sets CLAUDE_PLUGIN_ROOT for compatibility but must be + attributed to its own platform, not claude-code.""" + import telemetry + + monkeypatch.delenv("MEM0_PLATFORM", raising=False) + monkeypatch.setenv("ANTIGRAVITY_PLUGIN_ROOT", "/ext") + monkeypatch.setenv("CLAUDE_PLUGIN_ROOT", "/ext") # antigravity sets both + assert telemetry.detect_platform() == "antigravity" + + +def test_plugin_version_is_per_editor(monkeypatch): + """Each editor reports the version from its OWN manifest. Antigravity is on + a 0.1.x line while Claude/Cursor/Codex are on 0.2.x, so they must not all + report the same shared version.""" + import telemetry + + plugin_dir = os.path.join(os.path.dirname(__file__), "..") + manifests = { + "antigravity": "plugin.json", + "claude-code": os.path.join(".claude-plugin", "plugin.json"), + "cursor": os.path.join(".cursor-plugin", "plugin.json"), + "codex": os.path.join(".codex-plugin", "plugin.json"), + } + for plat, rel in manifests.items(): + monkeypatch.setenv("MEM0_PLATFORM", plat) + with open(os.path.join(plugin_dir, rel)) as f: + expected = json.load(f)["version"] + payload = telemetry.build_posthog_payload("plugin.test") + assert payload["properties"]["plugin_version"] == expected, f"{plat} should report {expected} from {rel}" + + def test_send_fails_silently(monkeypatch): import telemetry @@ -175,3 +223,39 @@ def test_cli_no_args_exits_nonzero(monkeypatch): monkeypatch.delenv("MEM0_TELEMETRY", raising=False) monkeypatch.setattr(sys, "argv", ["telemetry.py"]) assert telemetry.main() == 1 + + +def test_cursor_wrappers_pin_platform(): + """Cursor wrappers delegate to the shared scripts, which auto-detect the + platform from host env vars. Since Cursor may not export CURSOR_PLUGIN_ROOT + to the subprocess, each wrapper must pin MEM0_PLATFORM=cursor so the + delegated telemetry is attributed to cursor, not the 'plugin' fallback.""" + scripts_dir = os.path.join(os.path.dirname(__file__), "..", "scripts") + cursor_wrappers = [ + "on_session_start_cursor.sh", + "on_user_prompt_cursor.sh", + "on_post_tool_use_cursor.sh", + "on_pre_compact_cursor.sh", + "on_stop_cursor.sh", + ] + for name in cursor_wrappers: + with open(os.path.join(scripts_dir, name)) as f: + content = f.read() + assert "export MEM0_PLATFORM=cursor" in content, f"{name} must `export MEM0_PLATFORM=cursor` before delegating" + + +def test_codex_hooks_pin_platform(): + """Codex installs standalone hooks (absolute paths) via install_codex_hooks.py, + so PLUGIN_ROOT is not set at runtime and the platform falls back to 'plugin'. + Codex runs hook commands through a shell, so every command pins + MEM0_PLATFORM=codex inline for correct attribution.""" + hooks_path = os.path.join(os.path.dirname(__file__), "..", "hooks", "codex-hooks.json") + with open(hooks_path) as f: + config = json.load(f) + + commands = [ + h["command"] for entries in config["hooks"].values() for entry in entries for h in entry.get("hooks", []) + ] + assert commands, "expected at least one codex hook command" + for cmd in commands: + assert "MEM0_PLATFORM=codex" in cmd, f"codex hook command missing platform pin: {cmd}" diff --git a/mem0-plugin/tests/test_write_path.py b/integrations/mem0-plugin/tests/test_write_path.py similarity index 100% rename from mem0-plugin/tests/test_write_path.py rename to integrations/mem0-plugin/tests/test_write_path.py diff --git a/openclaw/.gitignore b/integrations/openclaw/.gitignore similarity index 100% rename from openclaw/.gitignore rename to integrations/openclaw/.gitignore diff --git a/openclaw/.npmrc b/integrations/openclaw/.npmrc similarity index 100% rename from openclaw/.npmrc rename to integrations/openclaw/.npmrc diff --git a/openclaw/LICENSE b/integrations/openclaw/LICENSE similarity index 100% rename from openclaw/LICENSE rename to integrations/openclaw/LICENSE diff --git a/openclaw/Makefile b/integrations/openclaw/Makefile similarity index 100% rename from openclaw/Makefile rename to integrations/openclaw/Makefile diff --git a/openclaw/README.md b/integrations/openclaw/README.md similarity index 100% rename from openclaw/README.md rename to integrations/openclaw/README.md diff --git a/openclaw/backend/base.ts b/integrations/openclaw/backend/base.ts similarity index 100% rename from openclaw/backend/base.ts rename to integrations/openclaw/backend/base.ts diff --git a/openclaw/backend/index.ts b/integrations/openclaw/backend/index.ts similarity index 100% rename from openclaw/backend/index.ts rename to integrations/openclaw/backend/index.ts diff --git a/openclaw/backend/platform.ts b/integrations/openclaw/backend/platform.ts similarity index 100% rename from openclaw/backend/platform.ts rename to integrations/openclaw/backend/platform.ts diff --git a/openclaw/cli/commands.ts b/integrations/openclaw/cli/commands.ts similarity index 100% rename from openclaw/cli/commands.ts rename to integrations/openclaw/cli/commands.ts diff --git a/openclaw/cli/config-file.ts b/integrations/openclaw/cli/config-file.ts similarity index 100% rename from openclaw/cli/config-file.ts rename to integrations/openclaw/cli/config-file.ts diff --git a/openclaw/cli/json-helpers.ts b/integrations/openclaw/cli/json-helpers.ts similarity index 100% rename from openclaw/cli/json-helpers.ts rename to integrations/openclaw/cli/json-helpers.ts diff --git a/openclaw/cli/oss-wizard.ts b/integrations/openclaw/cli/oss-wizard.ts similarity index 100% rename from openclaw/cli/oss-wizard.ts rename to integrations/openclaw/cli/oss-wizard.ts diff --git a/openclaw/config.ts b/integrations/openclaw/config.ts similarity index 100% rename from openclaw/config.ts rename to integrations/openclaw/config.ts diff --git a/openclaw/dream-gate.ts b/integrations/openclaw/dream-gate.ts similarity index 100% rename from openclaw/dream-gate.ts rename to integrations/openclaw/dream-gate.ts diff --git a/openclaw/filtering.ts b/integrations/openclaw/filtering.ts similarity index 100% rename from openclaw/filtering.ts rename to integrations/openclaw/filtering.ts diff --git a/openclaw/fs-safe.ts b/integrations/openclaw/fs-safe.ts similarity index 100% rename from openclaw/fs-safe.ts rename to integrations/openclaw/fs-safe.ts diff --git a/openclaw/index.test.ts b/integrations/openclaw/index.test.ts similarity index 94% rename from openclaw/index.test.ts rename to integrations/openclaw/index.test.ts index 3adf1666d..da86c5a7c 100644 --- a/openclaw/index.test.ts +++ b/integrations/openclaw/index.test.ts @@ -2,8 +2,8 @@ * Regression tests for per-agent memory isolation helpers and * message filtering logic. */ -import { describe, it, expect } from "vitest"; -import { +import { describe, it, expect, vi } from "vitest"; +import memoryPlugin, { extractAgentId, effectiveUserId, agentUserId, @@ -16,6 +16,47 @@ import { filterMessagesForExtraction, } from "./index.ts"; +function createPluginApi(registrationMode?: string) { + return { + pluginConfig: { + mode: "platform", + apiKey: "test-api-key", + userId: "alice", + }, + registrationMode, + logger: { + info: vi.fn(), + warn: vi.fn(), + error: vi.fn(), + debug: vi.fn(), + }, + resolvePath: vi.fn((p: string) => p), + registerTool: vi.fn(), + on: vi.fn(), + registerCli: vi.fn(), + registerCommand: vi.fn(), + registerService: vi.fn(), + registerMemoryCapability: vi.fn(), + }; +} + +describe("plugin registration modes", () => { + it("keeps cli-metadata registration free of runtime side effects", () => { + const api = createPluginApi("cli-metadata"); + + memoryPlugin.register(api as any); + + expect(api.registerCli).toHaveBeenCalledTimes(1); + expect(api.registerService).not.toHaveBeenCalled(); + expect(api.registerMemoryCapability).not.toHaveBeenCalled(); + expect(api.registerTool).not.toHaveBeenCalled(); + expect(api.on).not.toHaveBeenCalled(); + expect(api.logger.info).not.toHaveBeenCalledWith( + expect.stringContaining("openclaw-mem0: registered"), + ); + }); +}); + // --------------------------------------------------------------------------- // extractAgentId // --------------------------------------------------------------------------- diff --git a/openclaw/index.ts b/integrations/openclaw/index.ts similarity index 98% rename from openclaw/index.ts rename to integrations/openclaw/index.ts index 4b4c89041..3aa2a3314 100644 --- a/openclaw/index.ts +++ b/integrations/openclaw/index.ts @@ -25,7 +25,11 @@ import type { AddOptions, SearchOptions, } from "./types.ts"; -import { createProvider, providerToBackend } from "./providers.ts"; +import { + createProvider, + customCategoryMapToList, + providerToBackend, +} from "./providers.ts"; import { mem0ConfigSchema } from "./config.ts"; import type { FileConfig } from "./config.ts"; import { createPublicArtifactsProvider } from "./public-artifacts.ts"; @@ -107,6 +111,7 @@ const memoryPlugin = definePluginEntry({ baseUrl: pluginAuth.baseUrl, }; const cfg = mem0ConfigSchema.parse(api.pluginConfig, fileConfig); + const isMetadataRegistration = api.registrationMode === "cli-metadata"; // Telemetry context bound to this plugin instance's config const telemetryCtx = { @@ -122,6 +127,21 @@ const memoryPlugin = definePluginEntry({ } }; + if (isMetadataRegistration) { + registerCliCommands( + api, + null as any, + null as any, + cfg, + () => cfg.userId, + (id: string) => `${cfg.userId}:agent:${id}`, + () => ({ user_id: cfg.userId, top_k: cfg.topK }), + () => undefined, + (cmd: string) => _captureEvent(`openclaw.cli.${cmd}`, { command: cmd }), + ); + return; + } + if (cfg.needsSetup) { api.logger.warn( "openclaw-mem0: API key not configured. Memory features are disabled.\n" + @@ -273,7 +293,8 @@ const memoryPlugin = definePluginEntry({ if (runId) opts.run_id = runId; // Pass customInstructions and customCategories to control what Mem0 extracts if (cfg.customInstructions) opts.custom_instructions = cfg.customInstructions; - if (cfg.customCategories) opts.custom_categories = cfg.customCategories; + const customCategories = customCategoryMapToList(cfg.customCategories); + if (customCategories) opts.custom_categories = customCategories; return opts; } diff --git a/openclaw/isolation.ts b/integrations/openclaw/isolation.ts similarity index 100% rename from openclaw/isolation.ts rename to integrations/openclaw/isolation.ts diff --git a/openclaw/openclaw-plugin-sdk.d.ts b/integrations/openclaw/openclaw-plugin-sdk.d.ts similarity index 97% rename from openclaw/openclaw-plugin-sdk.d.ts rename to integrations/openclaw/openclaw-plugin-sdk.d.ts index 7484e4813..9854a5462 100644 --- a/openclaw/openclaw-plugin-sdk.d.ts +++ b/integrations/openclaw/openclaw-plugin-sdk.d.ts @@ -26,6 +26,7 @@ declare module "openclaw/plugin-sdk" { export interface OpenClawPluginApi { pluginConfig: Record; + registrationMode?: "full" | "cli-metadata" | string; logger: { info(msg: string): void; warn(msg: string): void; diff --git a/openclaw/openclaw.plugin.json b/integrations/openclaw/openclaw.plugin.json similarity index 100% rename from openclaw/openclaw.plugin.json rename to integrations/openclaw/openclaw.plugin.json diff --git a/openclaw/package.json b/integrations/openclaw/package.json similarity index 90% rename from openclaw/package.json rename to integrations/openclaw/package.json index 6764e9e47..62c3a6f51 100644 --- a/openclaw/package.json +++ b/integrations/openclaw/package.json @@ -1,13 +1,13 @@ { "name": "@mem0/openclaw-mem0", - "version": "1.0.12", + "version": "1.0.13", "type": "module", "description": "Mem0 memory backend for OpenClaw — platform or self-hosted open-source", "license": "Apache-2.0", "repository": { "type": "git", "url": "https://github.com/mem0ai/mem0", - "directory": "openclaw" + "directory": "integrations/openclaw" }, "keywords": [ "openclaw", @@ -35,7 +35,7 @@ }, "dependencies": { "@sinclair/typebox": "0.34.47", - "mem0ai": "3.0.6" + "mem0ai": "3.0.7" }, "openclaw": { "extensions": [ @@ -68,7 +68,8 @@ "vite": "^8.0.5", "langsmith@<0.6.0": "^0.6.0", "picomatch@<2.3.2": "^2.3.2", - "@qdrant/js-client-rest": "^1.18.0" + "@qdrant/js-client-rest": "^1.18.0", + "uuid@<11.1.1": ">=11.1.1" } } } diff --git a/openclaw/pnpm-lock.yaml b/integrations/openclaw/pnpm-lock.yaml similarity index 83% rename from openclaw/pnpm-lock.yaml rename to integrations/openclaw/pnpm-lock.yaml index 23f59b1d7..070a08d2c 100644 --- a/openclaw/pnpm-lock.yaml +++ b/integrations/openclaw/pnpm-lock.yaml @@ -10,6 +10,7 @@ overrides: langsmith@<0.6.0: ^0.6.0 picomatch@<2.3.2: ^2.3.2 '@qdrant/js-client-rest': ^1.18.0 + uuid@<11.1.1: '>=11.1.1' importers: @@ -19,18 +20,18 @@ importers: specifier: 0.34.47 version: 0.34.47 mem0ai: - specifier: 3.0.6 - version: 3.0.6(@anthropic-ai/sdk@0.40.1)(@azure/identity@4.13.1)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260528.1)(@google/genai@1.52.0)(@langchain/core@1.1.48(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0))(@mistralai/mistralai@1.15.1)(@qdrant/js-client-rest@1.18.0(typescript@5.9.3))(@supabase/supabase-js@2.106.2)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.10.0)(cloudflare@4.5.0)(compromise@14.15.1)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.11.3)(redis@5.12.1)(ws@8.21.0) + specifier: 3.0.7 + version: 3.0.7(@anthropic-ai/sdk@0.40.1)(@azure/identity@4.13.1)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260610.1)(@google/genai@1.52.0)(@langchain/core@1.1.48(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0))(@mistralai/mistralai@1.15.1)(@qdrant/js-client-rest@1.18.0(typescript@5.9.3))(@supabase/supabase-js@2.108.1)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.10.0)(cloudflare@4.5.0)(compromise@14.15.1)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.21.0)(redis@5.12.1)(ws@8.21.0) devDependencies: '@qdrant/js-client-rest': specifier: ^1.18.0 version: 1.18.0(typescript@5.9.3) '@types/node': specifier: ^22.15.0 - version: 22.19.19 + version: 22.19.20 '@vitest/coverage-v8': specifier: ^4.1.7 - version: 4.1.7(vitest@4.1.7) + version: 4.1.8(vitest@4.1.8) tsup: specifier: ^8.5.0 version: 8.5.1(postcss@8.5.15)(typescript@5.9.3) @@ -39,10 +40,10 @@ importers: version: 5.9.3 vite: specifier: ^8.0.5 - version: 8.0.14(@types/node@22.19.19)(esbuild@0.27.7) + version: 8.0.16(@types/node@22.19.20)(esbuild@0.27.7) vitest: specifier: ^4.1.7 - version: 4.1.7(@types/node@22.19.19)(@vitest/coverage-v8@4.1.7)(vite@8.0.14(@types/node@22.19.19)(esbuild@0.27.7)) + version: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.27.7)) packages: @@ -57,8 +58,8 @@ packages: resolution: {integrity: sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==} engines: {node: '>=20.0.0'} - '@azure/core-client@1.10.1': - resolution: {integrity: sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==} + '@azure/core-client@1.10.2': + resolution: {integrity: sha512-1D2LpsU7y9xrqKjdIbsB7PlrRePw0xsVV8p+AKTlzITrWmscajryfJCdDJB/oGwvDI5HmRo04eMMADB67uwAwQ==} engines: {node: '>=20.0.0'} '@azure/core-http-compat@2.4.0': @@ -72,8 +73,8 @@ packages: resolution: {integrity: sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==} engines: {node: '>=18.0.0'} - '@azure/core-rest-pipeline@1.23.0': - resolution: {integrity: sha512-Evs1INHo+jUjwHi1T6SG6Ua/LHOQBCLuKEEE6efIpt4ZOoNonaT1kP32GoOcdNDbfqsD2445CPri3MubBy5DEQ==} + '@azure/core-rest-pipeline@1.24.0': + resolution: {integrity: sha512-PpLsoDQ3AMmKZ0VU+0GrmqMxgp/sExjlVm4R+nLWngeoEGAzOIPVifaxKGU5gMv+nWELUoHfvrolWD+ZS/nFJg==} engines: {node: '>=20.0.0'} '@azure/core-tracing@1.3.1': @@ -92,16 +93,16 @@ packages: resolution: {integrity: sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==} engines: {node: '>=20.0.0'} - '@azure/msal-browser@5.11.0': - resolution: {integrity: sha512-zkGNYS3TwY8lUpPIafAmsFCYZbgFixY9y/LZB9GUg0IILoHTqpN26j5OrkL1AQThh/YdZsawe4iWXfp85lFVxg==} + '@azure/msal-browser@5.12.0': + resolution: {integrity: sha512-eNf2aqx1C6I0yT1GEu5ukblFrmaBXGfe1bivpmlfqvK7giPZvoXLa404C8EfeHVsy6EIryfQuPRzuW1fPxWlHg==} engines: {node: '>=0.8.0'} - '@azure/msal-common@16.6.2': - resolution: {integrity: sha512-hQjjsekAjB00cM1EmatWJlzhEoK2Qhz7Rj5gvM6tYf8iL7RM3tkxlpU9fG0+ofkulzg9AEEA6dIEnSmDr5ZqUA==} + '@azure/msal-common@16.7.0': + resolution: {integrity: sha512-Jb8Y7pX6KM42SIT7KWP6YbY3+vLbwB5b5m+tpiiOzMU1QeyelQzs9lO8jv1e7/Uj9r7tg7VjPvW4T0KB1jF3UQ==} engines: {node: '>=0.8.0'} - '@azure/msal-node@5.2.2': - resolution: {integrity: sha512-toS+2AePxqyzb0YOKttDOOiSl3jrkK9aiqIvpurpis0O34QcIS5gToqrgT39p04Dpxw3YoUU0lxJKTpSFFfA6Q==} + '@azure/msal-node@5.2.3': + resolution: {integrity: sha512-YYX4TchEVddVBiybKvKhV9QO/q22jgewP+BVxKG7Uh115voPcviGlypbKERDsqQdAiSTJrwi80gcWFjYKdo8+Q==} engines: {node: '>=20'} '@azure/search-documents@12.2.0': @@ -136,8 +137,8 @@ packages: '@cfworker/json-schema@4.1.1': resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} - '@cloudflare/workers-types@4.20260528.1': - resolution: {integrity: sha512-sTNOEN+8DvAOdE7MG3I/TbRGrjZnfbf0aqyTR4wzVi2GEpHTG9DzaK0MSA0z0JS2LmTUSSWimj4mB4cJaYk9rQ==} + '@cloudflare/workers-types@4.20260610.1': + resolution: {integrity: sha512-Mk/f3lUygeIHzQ4HnJjU/JvGg/kllgp9gISty9nylHE/2M2MFeKO+hgAKSgiPpmwUbuhewdYGgqFGgT/ADK0/g==} '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} @@ -348,14 +349,14 @@ packages: '@mongodb-js/saslprep@1.4.11': resolution: {integrity: sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA==} - '@napi-rs/wasm-runtime@1.1.4': - resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 - '@oxc-project/types@0.132.0': - resolution: {integrity: sha512-FESMOxil5Se014ui/Eq8fT5uHJo6nIRwH0PfJrZJXs6Gek3ZVFOrpUv3YIZT20m+extU98Hg1Ym72U58rlsxUQ==} + '@oxc-project/types@0.133.0': + resolution: {integrity: sha512-KzkdCd6Uxqnf6l3HOw1xfatAlUURA0g14cvBYFyJ5SaNOQbOUvBr9PKArcPcrNIeRsBdgcUzOGrhKveVpvOIGA==} '@protobufjs/aspromise@1.1.2': resolution: {integrity: sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==} @@ -433,97 +434,91 @@ packages: peerDependencies: '@redis/client': ^5.12.1 - '@rolldown/binding-android-arm64@1.0.2': - resolution: {integrity: sha512-ZS4D1JPGn/MYQN/SYDWftIE/nVsM8j/AFOYEzAoOE2O3NktQOZru+/vYXGbR/qtdLdIfGCP0lcoJiYVzsEz+iQ==} + '@rolldown/binding-android-arm64@1.0.3': + resolution: {integrity: sha512-454rs7jHngixp/NMxd5srYD57OnzSlZ/eFTETjORQHLwJG1lRtmNOJcBerZlfu4GjKqeq8aCCIQrMdHyhI51Hw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] - '@rolldown/binding-darwin-arm64@1.0.2': - resolution: {integrity: sha512-vdFA9+C/rekyGce7WqHs/xoT0ioZEWaOFyZLIV1mEeNFaFDUQrPIo8Vs2GvJ6eetb3rzDUtUBgzto3ExpXJB3w==} + '@rolldown/binding-darwin-arm64@1.0.3': + resolution: {integrity: sha512-PcAhP+ynjURNyy8SKGl5DQP94aGuB/7JrXJb/t7P+hanXvQVMWzUvRRhBAcg/lNRadBhoUPqSoP4xw5tR/KBEA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] - '@rolldown/binding-darwin-x64@1.0.2': - resolution: {integrity: sha512-BewSOwTHazv77DTYiAZXSqqKZ4KP/KonFisDMVU7PImxoWfB2aepnPhd2E4SWz3zDzYgDNbs6jBmTdgNnF02GA==} + '@rolldown/binding-darwin-x64@1.0.3': + resolution: {integrity: sha512-9YpfeUvSE2RS7wysJ81uOZkXJz7f7Q55H2Gvp3VEw/EsahqDtrphrZ0EwDLK5vvKOzaCrBsjF8JmnMLcUt78Gg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] - '@rolldown/binding-freebsd-x64@1.0.2': - resolution: {integrity: sha512-m41o7M0YWtUdqk61Tb+jnKb2rN++iRdIASlExkUoKfIAH30DOHCB8fVLzSUpbWHHU8esmEioY62PxzexE8MBuA==} + '@rolldown/binding-freebsd-x64@1.0.3': + resolution: {integrity: sha512-yB1IlAsSNHncV6SCTL27/MVGR5htvQsoGxIv5KMGXALp+Ll1wYsn+x98M9MW7qa+NdSbvrrY7ANI4wLJ0n1e6g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] - '@rolldown/binding-linux-arm-gnueabihf@1.0.2': - resolution: {integrity: sha512-jcojB9H7W/jS29pMKWAK1N+fU99vXodHDTatS3b3y/XSOCiHo0kkA74pL3jJmkoQtYpOCxDvaKs1fo2Ij/1X5w==} + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': + resolution: {integrity: sha512-Yi30IVAAfLUCy2MseFjbB1jAMDl1VMCAas5StnYp8da9+CKvMd2H2cbEjWcw5NPaPqzvYkVIaF1nNUG+b7u/sw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] - '@rolldown/binding-linux-arm64-gnu@1.0.2': - resolution: {integrity: sha512-1jn6qDU5iiOgFgygDzKUuKP0maTi0/f1+sBLgvij/76C77Nm3ts6ufz9Bjg5q5dduxiUIxtq86JIoBvo1xQ4Ig==} + '@rolldown/binding-linux-arm64-gnu@1.0.3': + resolution: {integrity: sha512-jsO7R8To+AdlYgUmN5sHSCZbfhtMBkO0WUx8iORQnPcMMdgr7qM2DQmMwgabs3GhNztdmoKkMKQFHD6DTMCIQw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [glibc] - '@rolldown/binding-linux-arm64-musl@1.0.2': - resolution: {integrity: sha512-QVLO/czFMdoMFSqlX3bcswcJNm/23r+qoa/jgtmFc/qEp6/jXmIkDjF/XIo8dPfGaiwy1xfQn8o77L79GeXFgw==} + '@rolldown/binding-linux-arm64-musl@1.0.3': + resolution: {integrity: sha512-VWkUHwWriDciit80wleYwKILoR/KMvxh/IdwS/paX+ZgpuRpCrKLUdadJbc0NpBEiyhpYawsJ73j9aCvOH+f7Q==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] - libc: [musl] - '@rolldown/binding-linux-ppc64-gnu@1.0.2': - resolution: {integrity: sha512-hgO5Abm0w5UL6FEa2iFnZqo2KlK7TQ5QhV5x09hujBf7t5KzHQ1VmfPuTpqRy/rNlSxua3eWH374xxiVrP+lcA==} + '@rolldown/binding-linux-ppc64-gnu@1.0.3': + resolution: {integrity: sha512-5f1laC0SlIR0yDbFCd8acUhvJIag6N3zC5P7oUPN6wX0aOma+uKJ0wBDH5aq7I1PVI2ttTlhJwzwRIBnLiSGEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] - libc: [glibc] - '@rolldown/binding-linux-s390x-gnu@1.0.2': - resolution: {integrity: sha512-fy8rXxuYEu602abC8MUNaPjYLIFzReOaEIEMKMUa0rFEUxNpVXhs15KSSQ4qlqSaM7B6rcj9rDZgADh/IGDzLQ==} + '@rolldown/binding-linux-s390x-gnu@1.0.3': + resolution: {integrity: sha512-Iq4ko0r4XsgbrF/LunNgHtAGLRRVE2kXonAXQ/MV0mC6jQpMOhW1SvtZja2EhC/kd05++bP78dsqBeIQyYJ6Yg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] - libc: [glibc] - '@rolldown/binding-linux-x64-gnu@1.0.2': - resolution: {integrity: sha512-0+bOkiQ779+r1WpoHOWHqncvyySci0vKph+myNDYb+im6meJAzHQXay6oEgnkHuUGouM1LKTZwqKpBow6Kj7CQ==} + '@rolldown/binding-linux-x64-gnu@1.0.3': + resolution: {integrity: sha512-B8m6tD5+/N5FeNQFbKlLA/2yVq9ycQP1SeedyEYYKWBNR3ZQbkvIUcNnDNM03lO1l5F2roiiFJGgvoLLyZXtSg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [glibc] - '@rolldown/binding-linux-x64-musl@1.0.2': - resolution: {integrity: sha512-mjSkrzZK5Qsl0a9d1JgILOiuZOSDTVdKENcSXBoqbzSrspLR/4/IRVDo5wd2GgZjNss/viBFJdeq+j7qH2nypw==} + '@rolldown/binding-linux-x64-musl@1.0.3': + resolution: {integrity: sha512-pSdpdUJHkuCxun9LE7jvgUB9qsRgaiyNNCX7m/AvHTcq67AiT/Yhoxvw5zPfhrM8k/BfP8ce/hMOpthKDpEUow==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] - libc: [musl] - '@rolldown/binding-openharmony-arm64@1.0.2': - resolution: {integrity: sha512-1v5vHasdfQAZoEHakBV72LIFAC9JjnymsiKxp+GEr/ma3+NJCPSaYK+qavInOovJkgwFrs7GccX2d6IgDA3Z5w==} + '@rolldown/binding-openharmony-arm64@1.0.3': + resolution: {integrity: sha512-OXXS3RKJgX2uLwM+gYyuH5omcH8fL1LJs96pZGgtetVCahON57+d4SJHzTgZiOjxgGkSnpXpOsWuPDGAKAigEg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] - '@rolldown/binding-wasm32-wasi@1.0.2': - resolution: {integrity: sha512-mb1VobWn6NheziTk5/WEaR6AKVbrwT5sOi6C7zk3gy/pD1qtJfU1j4PgTo2NJnOtbL9Dl3Aeei8w9jJ7qC2jZQ==} + '@rolldown/binding-wasm32-wasi@1.0.3': + resolution: {integrity: sha512-JTtb8BWFynicNSoPrehsCzBtOKjZ6jhMiPFEmOiuXg1Fl8dn2KHQob+GuPSGR0dryQa1PQJbzjF3dqO/whhjLg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [wasm32] - '@rolldown/binding-win32-arm64-msvc@1.0.2': - resolution: {integrity: sha512-SqKonF56vA/L2yHwHYcEp2P34URpOZ7d1fS635cTkpDnUtEGdUbhI6NzsPdqeSWvAAeGDrxjWjNmibDIdFf9/A==} + '@rolldown/binding-win32-arm64-msvc@1.0.3': + resolution: {integrity: sha512-gEdFFEN70A/jxb2svrWsN3aDL7OUtmvlOy+6fa2jxG8K0wQ1ZbdeLGnidov6Yu5/733dI5ySfzFlQ/cb0bSz1g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] - '@rolldown/binding-win32-x64-msvc@1.0.2': - resolution: {integrity: sha512-v7qRI7gXLRINcOGXt+7YmAZ6iFuyZVMIoXAxhd8oP+DR9dLfL9GfNIx7PLMxmhZdvq8waUJBQiWN9EKNy+TRBQ==} + '@rolldown/binding-win32-x64-msvc@1.0.3': + resolution: {integrity: sha512-eXB7CHuaQdqmJcc3koCNtNPmT/bj2gc999kUFgBxG8Ac0NdgXc4rkCHhqrgrhN3zddvvvrgzj1e90SuSfmyIXA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] @@ -531,141 +526,128 @@ packages: '@rolldown/pluginutils@1.0.1': resolution: {integrity: sha512-2j9bGt5Jh8hj+vPtgzPtl72j0yRxHAyumoo6TNfAjsLB04UtpSvPbPcDcBMxz7n+9CYB0c1GxQFxYRg2jimqGw==} - '@rollup/rollup-android-arm-eabi@4.60.4': - resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} + '@rollup/rollup-android-arm-eabi@4.61.1': + resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.60.4': - resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} + '@rollup/rollup-android-arm64@4.61.1': + resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.60.4': - resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} + '@rollup/rollup-darwin-arm64@4.61.1': + resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.60.4': - resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} + '@rollup/rollup-darwin-x64@4.61.1': + resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.60.4': - resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} + '@rollup/rollup-freebsd-arm64@4.61.1': + resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.4': - resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} + '@rollup/rollup-freebsd-x64@4.61.1': + resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': - resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} + '@rollup/rollup-linux-arm-gnueabihf@4.61.1': + resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==} cpu: [arm] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm-musleabihf@4.60.4': - resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} + '@rollup/rollup-linux-arm-musleabihf@4.61.1': + resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==} cpu: [arm] os: [linux] - libc: [musl] - '@rollup/rollup-linux-arm64-gnu@4.60.4': - resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} + '@rollup/rollup-linux-arm64-gnu@4.61.1': + resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==} cpu: [arm64] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-arm64-musl@4.60.4': - resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} + '@rollup/rollup-linux-arm64-musl@4.61.1': + resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==} cpu: [arm64] os: [linux] - libc: [musl] - '@rollup/rollup-linux-loong64-gnu@4.60.4': - resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} + '@rollup/rollup-linux-loong64-gnu@4.61.1': + resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==} cpu: [loong64] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-loong64-musl@4.60.4': - resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} + '@rollup/rollup-linux-loong64-musl@4.61.1': + resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==} cpu: [loong64] os: [linux] - libc: [musl] - '@rollup/rollup-linux-ppc64-gnu@4.60.4': - resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} + '@rollup/rollup-linux-ppc64-gnu@4.61.1': + resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==} cpu: [ppc64] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-ppc64-musl@4.60.4': - resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} + '@rollup/rollup-linux-ppc64-musl@4.61.1': + resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==} cpu: [ppc64] os: [linux] - libc: [musl] - '@rollup/rollup-linux-riscv64-gnu@4.60.4': - resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} + '@rollup/rollup-linux-riscv64-gnu@4.61.1': + resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==} cpu: [riscv64] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-riscv64-musl@4.60.4': - resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} + '@rollup/rollup-linux-riscv64-musl@4.61.1': + resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==} cpu: [riscv64] os: [linux] - libc: [musl] - '@rollup/rollup-linux-s390x-gnu@4.60.4': - resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} + '@rollup/rollup-linux-s390x-gnu@4.61.1': + resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==} cpu: [s390x] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-x64-gnu@4.60.4': - resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} + '@rollup/rollup-linux-x64-gnu@4.61.1': + resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==} cpu: [x64] os: [linux] - libc: [glibc] - '@rollup/rollup-linux-x64-musl@4.60.4': - resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} + '@rollup/rollup-linux-x64-musl@4.61.1': + resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==} cpu: [x64] os: [linux] - libc: [musl] - '@rollup/rollup-openbsd-x64@4.60.4': - resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} + '@rollup/rollup-openbsd-x64@4.61.1': + resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.60.4': - resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} + '@rollup/rollup-openharmony-arm64@4.61.1': + resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.60.4': - resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} + '@rollup/rollup-win32-arm64-msvc@4.61.1': + resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.4': - resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} + '@rollup/rollup-win32-ia32-msvc@4.61.1': + resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.4': - resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} + '@rollup/rollup-win32-x64-gnu@4.61.1': + resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.4': - resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} + '@rollup/rollup-win32-x64-msvc@4.61.1': + resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==} cpu: [x64] os: [win32] @@ -678,31 +660,31 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@supabase/auth-js@2.106.2': - resolution: {integrity: sha512-VcAjUErkHkhC5Jaf+g/G1qbkQrFh8edaCdHa7pxJmHUjkWKjT7UnYCtPA89XV0N0GIYRkEqJZw5V62CtOxTmBQ==} + '@supabase/auth-js@2.108.1': + resolution: {integrity: sha512-Lle5rKU8f9LF3K5dDd8Or8mkkG+ptzRZZWKPVMm9B9UuovH65Ss2+iFnQqRsCqaGouvJEcTWyl0cj2riNrrDLQ==} engines: {node: '>=20.0.0'} - '@supabase/functions-js@2.106.2': - resolution: {integrity: sha512-oRnr0QrL8H+zTO1YyQ1QjiHZU/957jvubbxSJTUm2XLAgzoGGV9Tahfyd+uvLsBLRVmXLtpU3oyCjdQIvkGMOA==} + '@supabase/functions-js@2.108.1': + resolution: {integrity: sha512-fxBRW/A4IG7ADQztVt0NaEy5ysiO1WJ2pbldsnBchrkHuyepX0Krek9qA9T4gUQBVVTCE9Ea4pdsM5hfn3nc4A==} engines: {node: '>=20.0.0'} '@supabase/phoenix@0.4.2': resolution: {integrity: sha512-YSAGnmDAfuleFCVt3CeurQZAhxRfXWeZIIkwp7NhYzQ1UwW6ePSnzsFAiUm/mbCkfoCf70QQHKW/K6RKh52a4A==} - '@supabase/postgrest-js@2.106.2': - resolution: {integrity: sha512-tDOzyPgp9pIRMR2x6C9+uDSJrnXSzxLtt3d7nC+Lrsy3jnJDHYfdQC/xcRyhJE/TOBJ0heSqRKR3UmejDjZxsw==} + '@supabase/postgrest-js@2.108.1': + resolution: {integrity: sha512-9lj2MCPPMgSTaJ5y+amnhb3TWPtMFVlbDn2hmX/VV91xQU4j0AauwfMaBErHBJ+zzsSwjc0jLU+zLIZFLQzfig==} engines: {node: '>=20.0.0'} - '@supabase/realtime-js@2.106.2': - resolution: {integrity: sha512-LdRGT7DNhyZkPjubUv5bSdAZ0jSEX8wTHvx7htj7+K59TOZRvz4TuQK7tL2RWxyIZVeFMRluL04SzWS61rKnUA==} + '@supabase/realtime-js@2.108.1': + resolution: {integrity: sha512-mHGGqOjwd1XTydcoffUqEMsbFQHUi6A3uhQ0EXr3iqzpLqItxKA9nbN6gIQxrZ7JRRnuUe/iOFPUkYV9Tdc5lg==} engines: {node: '>=20.0.0'} - '@supabase/storage-js@2.106.2': - resolution: {integrity: sha512-xgKCSYuev1YarV+iVqr+zlfgSyremnJtn8T0NCT8L4XmMv1CLtESc0Q6kNp8+mKWdX/8ND0nzm7OMKx08kwNAw==} + '@supabase/storage-js@2.108.1': + resolution: {integrity: sha512-Er0SGGt85iT6ye+SSh98Az6L2CesoZJuyzEZYH2oBOAnIxa9Nn4CtwUC3veGxYggoT56X+3tVuuQeDBP8kR8sg==} engines: {node: '>=20.0.0'} - '@supabase/supabase-js@2.106.2': - resolution: {integrity: sha512-2/RZ/1fmJx/MRSEDG2Xk8+J4JVk5clM9V0uSI6kUTrcS32KA89DtqI5RUOC9r6mzY3WBC9qexLjssIHjbLyVJA==} + '@supabase/supabase-js@2.108.1': + resolution: {integrity: sha512-V/1hRKLSCJ0zEL+9QFRBUtivvePfOsaAYQmC0HhFNSHC2F3xFs4jSF3YhkLmzex6E4V4FGvmBDOP72D/53NnZA==} engines: {node: '>=20.0.0'} '@tybys/wasm-util@0.10.2': @@ -714,9 +696,6 @@ packages: '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} - '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} @@ -738,8 +717,8 @@ packages: '@types/node@18.19.130': resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - '@types/node@22.19.19': - resolution: {integrity: sha512-dyh/xO2Fh5bYrfWaaqGrRQQGkNdmYw6AmaAUvYeUMNTWQtvb796ikLdmTchRmOlOiIJ1TDXfWgVx1QkUlQ6Hew==} + '@types/node@22.19.20': + resolution: {integrity: sha512-6tELRwSDYWW9EdZhbeZmYGZ1/7Djkt+Ah3/ScEYT9cDord7UJzasR/4D3VONg9tQI5CDp+/CZC1AXj2pCFOvpw==} '@types/pg@8.11.0': resolution: {integrity: sha512-sDAlRiBNthGjNFfvt0k6mtotoVYVQ63pA8R4EMWka7crawSR60waVYR0HAgmPRs/e2YaeJTD/43OoZ3PFw80pw==} @@ -762,24 +741,24 @@ packages: '@types/yargs@17.0.35': resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - '@typespec/ts-http-runtime@0.3.5': - resolution: {integrity: sha512-yURCknZhvywvQItHMMmFSo+fq5arCUIyz/CVk7jD89MSai7dkaX8ufjCWp3NttLojoTVbcE72ri+be/TnEbMHw==} + '@typespec/ts-http-runtime@0.3.6': + resolution: {integrity: sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og==} engines: {node: '>=20.0.0'} - '@vitest/coverage-v8@4.1.7': - resolution: {integrity: sha512-qsYPeXc5Q9dFLd1i8Ap+Bx8sQgcp+rFVQo4R0dDsWNBzl26ldVF1qOO+RL24K7FDrR6pA+50XedRLSoSG24bVQ==} + '@vitest/coverage-v8@4.1.8': + resolution: {integrity: sha512-lt3kovsyHwYe00wq4D1ti0Z974fWj4NLp6siqiyEufUpyFwK9Yhi7rBhac9JL5aA0zoMrJqc4vYPZRUnI7l7nw==} peerDependencies: - '@vitest/browser': 4.1.7 - vitest: 4.1.7 + '@vitest/browser': 4.1.8 + vitest: 4.1.8 peerDependenciesMeta: '@vitest/browser': optional: true - '@vitest/expect@4.1.7': - resolution: {integrity: sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w==} + '@vitest/expect@4.1.8': + resolution: {integrity: sha512-h3nDO677RDLEGlBxyQ5CW8RlMThSKSRLUePLOx09gNIWRL40edgA1GCZSZgf1W55MFAG6/Sw14KeaAnqv0NKdQ==} - '@vitest/mocker@4.1.7': - resolution: {integrity: sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA==} + '@vitest/mocker@4.1.8': + resolution: {integrity: sha512-LEiN/xe4OSIbKe9HQIp5OC24agGD9J5CnmMgsLohVVoOPWL9a2sBoR6VBx43jQZb7Kr1l4RCuyCJzcAa0+dojw==} peerDependencies: msw: ^2.4.9 vite: ^8.0.5 @@ -789,20 +768,20 @@ packages: vite: optional: true - '@vitest/pretty-format@4.1.7': - resolution: {integrity: sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw==} + '@vitest/pretty-format@4.1.8': + resolution: {integrity: sha512-9GasEBxpZ1VYIpqHf/0+YGg121uSNwCKOJqIrTwWP/TB7DmFCiaBpNl3aPZzoLWfWkuqhbH8vJIVobZkvdo2cA==} - '@vitest/runner@4.1.7': - resolution: {integrity: sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw==} + '@vitest/runner@4.1.8': + resolution: {integrity: sha512-EmVxeBAfMJvycdjd6Hm+RbFBbA9fKvo0Kx37hNpBYoYeavH3RNsBXWDooR1mgD52dCrxIIuP7UotpfiwOikvcg==} - '@vitest/snapshot@4.1.7': - resolution: {integrity: sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw==} + '@vitest/snapshot@4.1.8': + resolution: {integrity: sha512-acfZboRmAIf05DEKcBQy33VXojFJjtUdLyo7oOmV9kebb2xdU01UknNiPuPZoJZQyO7DF0gZdTGTpeAzET9QPQ==} - '@vitest/spy@4.1.7': - resolution: {integrity: sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q==} + '@vitest/spy@4.1.8': + resolution: {integrity: sha512-6EevtBp6OZOPF7bmz36HrGMeP3txgVSrgebWxHOafDXGkhIzfXK14f8KF6MuFfgXXUeHxmpD3BQxkV00/3s5mA==} - '@vitest/utils@4.1.7': - resolution: {integrity: sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw==} + '@vitest/utils@4.1.8': + resolution: {integrity: sha512-uOJamYALNhfJ6iolExyQM40yIQwDqYnkKtQ5VCiSe17E33H0aQ/u+1GlRuz4LZBk6Mm3sg90G9hEbmEt37C1Zg==} abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} @@ -850,8 +829,8 @@ packages: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} - ast-v8-to-istanbul@1.0.2: - resolution: {integrity: sha512-dKmJxJsGItLmc5CYZKuEjuG6GnBs6PG4gohMhyFOWKaNQoYCuRZJDECaBlHmcG0lv2wc2E0uU8lESmBEumC3DQ==} + ast-v8-to-istanbul@1.0.3: + resolution: {integrity: sha512-jCMQ6ZylLPudp0CDfBmQBZUsrh1/8psbmu9ibeVWKuHWD0YrH9YABwlKu5kVEFoT0GCQQW9Z/SxfuEbbkGQCRg==} asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} @@ -889,10 +868,6 @@ packages: buffer-equal-constant-time@1.0.1: resolution: {integrity: sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==} - buffer-writer@2.0.0: - resolution: {integrity: sha512-a7ZpuTZU1TRtnwyCNW3I5dc0wWNC3VR9S++Ewyk2HHZdrO3CQJqSpd+95Us590V6AL7JqUAH2IwZ/398PmNFgw==} - engines: {node: '>=4'} - buffer@5.7.1: resolution: {integrity: sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==} @@ -1156,8 +1131,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - gaxios@7.1.4: - resolution: {integrity: sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==} + gaxios@7.1.5: + resolution: {integrity: sha512-5FZy72Rh8LhtjmvDrKkI+lVhrsQrVKVsItxMoDm5mNQE+xR0WVIIs+jzPSJgBvKVsLi24fZhXJIsNI0bihDzFg==} engines: {node: '>=18'} gcp-metadata@8.1.2: @@ -1175,8 +1150,8 @@ packages: github-from-package@0.0.0: resolution: {integrity: sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==} - google-auth-library@10.6.2: - resolution: {integrity: sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==} + google-auth-library@10.7.0: + resolution: {integrity: sha512-QpTAbNJ36TliZLx3TTtahR8HG0hN9RllL1e3FymOvQSIKK8JmgV58H924ub2wa2DsS3ANjjP1Aw1N+Ramc8hqQ==} engines: {node: '>=18'} google-logging-utils@1.1.3: @@ -1382,28 +1357,24 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} @@ -1473,8 +1444,8 @@ packages: md5@2.3.0: resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} - mem0ai@3.0.6: - resolution: {integrity: sha512-X1KbCVE5351qpJ6X2KXFhQJiW+dqoHj1otA66DSdXtIEO1uXuIdyeTKeURdAH+wjXtIcFa08ylL2meA1pBk9Iw==} + mem0ai@3.0.7: + resolution: {integrity: sha512-CUHzX7DyeKTHcI3aDsSqY9LXTD7GcFxf988796TuOa4yJgGuF2Xd2NROcBhVFRo3r9y8fVmbo3c5TF9jv1KlKw==} engines: {node: '>=18'} peerDependencies: '@anthropic-ai/sdk': ^0.40.1 @@ -1560,8 +1531,8 @@ packages: socks: optional: true - mongoose@9.6.3: - resolution: {integrity: sha512-vI6dTTlQnfMCyyQ5TrvhG0bCRs4dq5e1uFNPtOOWsOhn0fSg8AoIHjfyyCYr8aybyvPs845dRHGxsC3w/fHcBA==} + mongoose@9.7.0: + resolution: {integrity: sha512-pkrLZ6U41pD4Ai0ju/FYL7o5I5k+rV3RZINQTG937hbhnLGKRuqqYm1Dlt/kTQ+M4FHijzV6JawzsdHKRGt7QA==} engines: {node: '>=20.19.0'} mpath@0.9.0: @@ -1623,8 +1594,9 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - obug@2.1.1: - resolution: {integrity: sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==} + obug@2.1.2: + resolution: {integrity: sha512-AWGB9WFcRXOQs48Z/udjI5ZcZMHXwX8XPByNpOydgcGsDLIzjGizhoMWJyKAWze7AVW/2W1i+/gPX4YtKe5cyg==} + engines: {node: '>=12.20.0'} ollama@0.5.18: resolution: {integrity: sha512-lTFqTf9bo7Cd3hpF6CviBe/DEhewjoZYd9N/uCe7O20qYTvGqrNOFOBDj3lbZgFWHUgDv5EeyusYxsZSLS8nvg==} @@ -1664,9 +1636,6 @@ packages: resolution: {integrity: sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==} engines: {node: '>=8'} - packet-reader@1.0.0: - resolution: {integrity: sha512-HAKu/fG3HpHFO0AA8WE8q2g+gBJaZ9MG7fcKk+IJPLTGAD6Psw4443l+9DGRbOIh3/aXr7Phy0TjilYivJo5XQ==} - pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} @@ -1700,15 +1669,6 @@ packages: resolution: {integrity: sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==} engines: {node: '>=10'} - pg@8.11.3: - resolution: {integrity: sha512-+9iuvG8QfaaUrrph+kpF24cXkH1YOOUeArRNYIxq1viYHZagBxrTno7cecY1Fa44tJeZvaoG+Djpkc3JwehN5g==} - engines: {node: '>= 8.0.0'} - peerDependencies: - pg-native: '>=3.0.1' - peerDependenciesMeta: - pg-native: - optional: true - pg@8.21.0: resolution: {integrity: sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==} engines: {node: '>= 16.0.0'} @@ -1806,8 +1766,8 @@ packages: resolution: {integrity: sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - protobufjs@7.6.1: - resolution: {integrity: sha512-4K0myLaWL5EteuSAro91EGFgcfVgxb64Jx+7oDAY6GOkXD4M69yuSEljNcInGVCA5sOPxmZ/EqDLj2x0Q0+Ygg==} + protobufjs@7.6.3: + resolution: {integrity: sha512-+k0vdJKNdW+Vu+dYe8tZA/VvQb6XKNWexC6URwBFXxNnjLJz9nQJCemGyNgRAWD+B7+nGNc9qMPGwcD7s4nzUw==} engines: {node: '>=12.0.0'} proxy-from-env@2.1.0: @@ -1848,13 +1808,13 @@ packages: resolution: {integrity: sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==} engines: {node: '>= 4'} - rolldown@1.0.2: - resolution: {integrity: sha512-oZx5zVDtVB44AW3eaifgDml1gWRDZGvjcfdxonE4swNPG98PrrXjaO/KrnUjzlMnztCCRVlUueA1kCXhARGk6g==} + rolldown@1.0.3: + resolution: {integrity: sha512-i00lAJ2ks1BYr7rjNjKC7BcqAS7nVfiT3QX1SI5aY+AFHblCmaUf9OE9dbdzDvW6dJxbi2ZCZiy9v3CcwOiX3g==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true - rollup@4.60.4: - resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} + rollup@4.61.1: + resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -1869,8 +1829,8 @@ packages: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} - semver@7.8.1: - resolution: {integrity: sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==} + semver@7.8.4: + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} engines: {node: '>=10'} hasBin: true @@ -1962,12 +1922,12 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyexec@1.2.2: - resolution: {integrity: sha512-M/Q0B2cp4K7kynaT/vnED1j8TlLY+Pp7C6Wl2bl/7u/F0mUVwdyOpwomQb8JpYLitHUssAJRmLZdMCGsrx7i+g==} + tinyexec@1.2.4: + resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} - tinyglobby@0.2.16: - resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} tinyrainbow@3.1.0: @@ -2045,13 +2005,12 @@ packages: resolution: {integrity: sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==} hasBin: true - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + uuid@14.0.0: + resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} hasBin: true - vite@8.0.14: - resolution: {integrity: sha512-s4BJJ+5y1pYL6Otw51FHhVJQhPnuRinKig64g/1+EUNaJsd3gCKdD31IPFvswUgW9/60QT9oFHbZHbQK5imcxw==} + vite@8.0.16: + resolution: {integrity: sha512-h9bXPmJichP5fLmVQo3PyaGSDE2n3aPuomeAlVRm0JLmt4rY6zmPKd59HYI4LNW8oTK7tlTsuC7l/m7awx9Jcw==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: @@ -2093,20 +2052,20 @@ packages: yaml: optional: true - vitest@4.1.7: - resolution: {integrity: sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA==} + vitest@4.1.8: + resolution: {integrity: sha512-flY6ScbCIt9HThs+C5HS7jvGOB560DJtk/Z15IQROTA6zEy49Nh8T/dofWTQL+n3vswqn87sbJNiuqw1SDp5Ig==} 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.7 - '@vitest/browser-preview': 4.1.7 - '@vitest/browser-webdriverio': 4.1.7 - '@vitest/coverage-istanbul': 4.1.7 - '@vitest/coverage-v8': 4.1.7 - '@vitest/ui': 4.1.7 + '@vitest/browser-playwright': 4.1.8 + '@vitest/browser-preview': 4.1.8 + '@vitest/browser-webdriverio': 4.1.8 + '@vitest/coverage-istanbul': 4.1.8 + '@vitest/coverage-v8': 4.1.8 + '@vitest/ui': 4.1.8 happy-dom: '*' jsdom: '*' vite: ^8.0.5 @@ -2228,11 +2187,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@azure/core-client@1.10.1': + '@azure/core-client@1.10.2': dependencies: '@azure/abort-controller': 2.1.2 '@azure/core-auth': 1.10.1 - '@azure/core-rest-pipeline': 1.23.0 + '@azure/core-rest-pipeline': 1.24.0 '@azure/core-tracing': 1.3.1 '@azure/core-util': 1.13.1 '@azure/logger': 1.3.0 @@ -2240,24 +2199,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@azure/core-http-compat@2.4.0(@azure/core-client@1.10.1)(@azure/core-rest-pipeline@1.23.0)': + '@azure/core-http-compat@2.4.0(@azure/core-client@1.10.2)(@azure/core-rest-pipeline@1.24.0)': dependencies: '@azure/abort-controller': 2.1.2 - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.23.0 + '@azure/core-client': 1.10.2 + '@azure/core-rest-pipeline': 1.24.0 '@azure/core-paging@1.6.2': dependencies: tslib: 2.8.1 - '@azure/core-rest-pipeline@1.23.0': + '@azure/core-rest-pipeline@1.24.0': dependencies: '@azure/abort-controller': 2.1.2 '@azure/core-auth': 1.10.1 '@azure/core-tracing': 1.3.1 '@azure/core-util': 1.13.1 '@azure/logger': 1.3.0 - '@typespec/ts-http-runtime': 0.3.5 + '@typespec/ts-http-runtime': 0.3.6 tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -2269,7 +2228,7 @@ snapshots: '@azure/core-util@1.13.1': dependencies: '@azure/abort-controller': 2.1.2 - '@typespec/ts-http-runtime': 0.3.5 + '@typespec/ts-http-runtime': 0.3.6 tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -2278,13 +2237,13 @@ snapshots: dependencies: '@azure/abort-controller': 2.1.2 '@azure/core-auth': 1.10.1 - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.23.0 + '@azure/core-client': 1.10.2 + '@azure/core-rest-pipeline': 1.24.0 '@azure/core-tracing': 1.3.1 '@azure/core-util': 1.13.1 '@azure/logger': 1.3.0 - '@azure/msal-browser': 5.11.0 - '@azure/msal-node': 5.2.2 + '@azure/msal-browser': 5.12.0 + '@azure/msal-node': 5.2.3 open: 10.2.0 tslib: 2.8.1 transitivePeerDependencies: @@ -2292,29 +2251,29 @@ snapshots: '@azure/logger@1.3.0': dependencies: - '@typespec/ts-http-runtime': 0.3.5 + '@typespec/ts-http-runtime': 0.3.6 tslib: 2.8.1 transitivePeerDependencies: - supports-color - '@azure/msal-browser@5.11.0': + '@azure/msal-browser@5.12.0': dependencies: - '@azure/msal-common': 16.6.2 + '@azure/msal-common': 16.7.0 - '@azure/msal-common@16.6.2': {} + '@azure/msal-common@16.7.0': {} - '@azure/msal-node@5.2.2': + '@azure/msal-node@5.2.3': dependencies: - '@azure/msal-common': 16.6.2 + '@azure/msal-common': 16.7.0 jsonwebtoken: 9.0.3 '@azure/search-documents@12.2.0': dependencies: '@azure/core-auth': 1.10.1 - '@azure/core-client': 1.10.1 - '@azure/core-http-compat': 2.4.0(@azure/core-client@1.10.1)(@azure/core-rest-pipeline@1.23.0) + '@azure/core-client': 1.10.2 + '@azure/core-http-compat': 2.4.0(@azure/core-client@1.10.2)(@azure/core-rest-pipeline@1.24.0) '@azure/core-paging': 1.6.2 - '@azure/core-rest-pipeline': 1.23.0 + '@azure/core-rest-pipeline': 1.24.0 '@azure/core-tracing': 1.3.1 '@azure/core-util': 1.13.1 '@azure/logger': 1.3.0 @@ -2346,7 +2305,7 @@ snapshots: '@cfworker/json-schema@4.1.1': {} - '@cloudflare/workers-types@4.20260528.1': {} + '@cloudflare/workers-types@4.20260610.1': {} '@emnapi/core@1.10.0': dependencies: @@ -2444,9 +2403,9 @@ snapshots: '@google/genai@1.52.0': dependencies: - google-auth-library: 10.6.2 + google-auth-library: 10.7.0 p-retry: 4.6.2 - protobufjs: 7.6.1 + protobufjs: 7.6.3 ws: 8.21.0 transitivePeerDependencies: - bufferutil @@ -2466,7 +2425,7 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.19.19 + '@types/node': 22.19.20 '@types/yargs': 17.0.35 chalk: 4.1.2 @@ -2513,14 +2472,14 @@ snapshots: dependencies: sparse-bitfield: 3.0.3 - '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@tybys/wasm-util': 0.10.2 optional: true - '@oxc-project/types@0.132.0': {} + '@oxc-project/types@0.133.0': {} '@protobufjs/aspromise@1.1.2': {} @@ -2572,130 +2531,130 @@ snapshots: dependencies: '@redis/client': 5.12.1 - '@rolldown/binding-android-arm64@1.0.2': + '@rolldown/binding-android-arm64@1.0.3': optional: true - '@rolldown/binding-darwin-arm64@1.0.2': + '@rolldown/binding-darwin-arm64@1.0.3': optional: true - '@rolldown/binding-darwin-x64@1.0.2': + '@rolldown/binding-darwin-x64@1.0.3': optional: true - '@rolldown/binding-freebsd-x64@1.0.2': + '@rolldown/binding-freebsd-x64@1.0.3': optional: true - '@rolldown/binding-linux-arm-gnueabihf@1.0.2': + '@rolldown/binding-linux-arm-gnueabihf@1.0.3': optional: true - '@rolldown/binding-linux-arm64-gnu@1.0.2': + '@rolldown/binding-linux-arm64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-arm64-musl@1.0.2': + '@rolldown/binding-linux-arm64-musl@1.0.3': optional: true - '@rolldown/binding-linux-ppc64-gnu@1.0.2': + '@rolldown/binding-linux-ppc64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-s390x-gnu@1.0.2': + '@rolldown/binding-linux-s390x-gnu@1.0.3': optional: true - '@rolldown/binding-linux-x64-gnu@1.0.2': + '@rolldown/binding-linux-x64-gnu@1.0.3': optional: true - '@rolldown/binding-linux-x64-musl@1.0.2': + '@rolldown/binding-linux-x64-musl@1.0.3': optional: true - '@rolldown/binding-openharmony-arm64@1.0.2': + '@rolldown/binding-openharmony-arm64@1.0.3': optional: true - '@rolldown/binding-wasm32-wasi@1.0.2': + '@rolldown/binding-wasm32-wasi@1.0.3': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@rolldown/binding-win32-arm64-msvc@1.0.2': + '@rolldown/binding-win32-arm64-msvc@1.0.3': optional: true - '@rolldown/binding-win32-x64-msvc@1.0.2': + '@rolldown/binding-win32-x64-msvc@1.0.3': optional: true '@rolldown/pluginutils@1.0.1': {} - '@rollup/rollup-android-arm-eabi@4.60.4': + '@rollup/rollup-android-arm-eabi@4.61.1': optional: true - '@rollup/rollup-android-arm64@4.60.4': + '@rollup/rollup-android-arm64@4.61.1': optional: true - '@rollup/rollup-darwin-arm64@4.60.4': + '@rollup/rollup-darwin-arm64@4.61.1': optional: true - '@rollup/rollup-darwin-x64@4.60.4': + '@rollup/rollup-darwin-x64@4.61.1': optional: true - '@rollup/rollup-freebsd-arm64@4.60.4': + '@rollup/rollup-freebsd-arm64@4.61.1': optional: true - '@rollup/rollup-freebsd-x64@4.60.4': + '@rollup/rollup-freebsd-x64@4.61.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + '@rollup/rollup-linux-arm-gnueabihf@4.61.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.4': + '@rollup/rollup-linux-arm-musleabihf@4.61.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.4': + '@rollup/rollup-linux-arm64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.60.4': + '@rollup/rollup-linux-arm64-musl@4.61.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.4': + '@rollup/rollup-linux-loong64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-loong64-musl@4.60.4': + '@rollup/rollup-linux-loong64-musl@4.61.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.4': + '@rollup/rollup-linux-ppc64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.4': + '@rollup/rollup-linux-ppc64-musl@4.61.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.4': + '@rollup/rollup-linux-riscv64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.4': + '@rollup/rollup-linux-riscv64-musl@4.61.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.4': + '@rollup/rollup-linux-s390x-gnu@4.61.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.60.4': + '@rollup/rollup-linux-x64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-x64-musl@4.60.4': + '@rollup/rollup-linux-x64-musl@4.61.1': optional: true - '@rollup/rollup-openbsd-x64@4.60.4': + '@rollup/rollup-openbsd-x64@4.61.1': optional: true - '@rollup/rollup-openharmony-arm64@4.60.4': + '@rollup/rollup-openharmony-arm64@4.61.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.4': + '@rollup/rollup-win32-arm64-msvc@4.61.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.4': + '@rollup/rollup-win32-ia32-msvc@4.61.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.60.4': + '@rollup/rollup-win32-x64-gnu@4.61.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.60.4': + '@rollup/rollup-win32-x64-msvc@4.61.1': optional: true '@sinclair/typebox@0.27.10': {} @@ -2704,37 +2663,37 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@supabase/auth-js@2.106.2': + '@supabase/auth-js@2.108.1': dependencies: tslib: 2.8.1 - '@supabase/functions-js@2.106.2': + '@supabase/functions-js@2.108.1': dependencies: tslib: 2.8.1 '@supabase/phoenix@0.4.2': {} - '@supabase/postgrest-js@2.106.2': + '@supabase/postgrest-js@2.108.1': dependencies: tslib: 2.8.1 - '@supabase/realtime-js@2.106.2': + '@supabase/realtime-js@2.108.1': dependencies: '@supabase/phoenix': 0.4.2 tslib: 2.8.1 - '@supabase/storage-js@2.106.2': + '@supabase/storage-js@2.108.1': dependencies: iceberg-js: 0.8.1 tslib: 2.8.1 - '@supabase/supabase-js@2.106.2': + '@supabase/supabase-js@2.108.1': dependencies: - '@supabase/auth-js': 2.106.2 - '@supabase/functions-js': 2.106.2 - '@supabase/postgrest-js': 2.106.2 - '@supabase/realtime-js': 2.106.2 - '@supabase/storage-js': 2.106.2 + '@supabase/auth-js': 2.108.1 + '@supabase/functions-js': 2.108.1 + '@supabase/postgrest-js': 2.108.1 + '@supabase/realtime-js': 2.108.1 + '@supabase/storage-js': 2.108.1 '@tybys/wasm-util@0.10.2': dependencies: @@ -2748,8 +2707,6 @@ snapshots: '@types/deep-eql@4.0.2': {} - '@types/estree@1.0.8': {} - '@types/estree@1.0.9': {} '@types/istanbul-lib-coverage@2.0.6': {} @@ -2769,20 +2726,20 @@ snapshots: '@types/node-fetch@2.6.13': dependencies: - '@types/node': 22.19.19 + '@types/node': 22.19.20 form-data: 4.0.5 '@types/node@18.19.130': dependencies: undici-types: 5.26.5 - '@types/node@22.19.19': + '@types/node@22.19.20': dependencies: undici-types: 6.21.0 '@types/pg@8.11.0': dependencies: - '@types/node': 22.19.19 + '@types/node': 22.19.20 pg-protocol: 1.14.0 pg-types: 4.1.0 @@ -2802,7 +2759,7 @@ snapshots: dependencies: '@types/yargs-parser': 21.0.3 - '@typespec/ts-http-runtime@0.3.5': + '@typespec/ts-http-runtime@0.3.6': dependencies: http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -2810,58 +2767,58 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/coverage-v8@4.1.7(vitest@4.1.7)': + '@vitest/coverage-v8@4.1.8(vitest@4.1.8)': dependencies: '@bcoe/v8-coverage': 1.0.2 - '@vitest/utils': 4.1.7 - ast-v8-to-istanbul: 1.0.2 + '@vitest/utils': 4.1.8 + ast-v8-to-istanbul: 1.0.3 istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-reports: 3.2.0 magicast: 0.5.3 - obug: 2.1.1 + obug: 2.1.2 std-env: 4.1.0 tinyrainbow: 3.1.0 - vitest: 4.1.7(@types/node@22.19.19)(@vitest/coverage-v8@4.1.7)(vite@8.0.14(@types/node@22.19.19)(esbuild@0.27.7)) + vitest: 4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.27.7)) - '@vitest/expect@4.1.7': + '@vitest/expect@4.1.8': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 - '@vitest/spy': 4.1.7 - '@vitest/utils': 4.1.7 + '@vitest/spy': 4.1.8 + '@vitest/utils': 4.1.8 chai: 6.2.2 tinyrainbow: 3.1.0 - '@vitest/mocker@4.1.7(vite@8.0.14(@types/node@22.19.19)(esbuild@0.27.7))': + '@vitest/mocker@4.1.8(vite@8.0.16(@types/node@22.19.20)(esbuild@0.27.7))': dependencies: - '@vitest/spy': 4.1.7 + '@vitest/spy': 4.1.8 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.0.14(@types/node@22.19.19)(esbuild@0.27.7) + vite: 8.0.16(@types/node@22.19.20)(esbuild@0.27.7) - '@vitest/pretty-format@4.1.7': + '@vitest/pretty-format@4.1.8': dependencies: tinyrainbow: 3.1.0 - '@vitest/runner@4.1.7': + '@vitest/runner@4.1.8': dependencies: - '@vitest/utils': 4.1.7 + '@vitest/utils': 4.1.8 pathe: 2.0.3 - '@vitest/snapshot@4.1.7': + '@vitest/snapshot@4.1.8': dependencies: - '@vitest/pretty-format': 4.1.7 - '@vitest/utils': 4.1.7 + '@vitest/pretty-format': 4.1.8 + '@vitest/utils': 4.1.8 magic-string: 0.30.21 pathe: 2.0.3 - '@vitest/spy@4.1.7': {} + '@vitest/spy@4.1.8': {} - '@vitest/utils@4.1.7': + '@vitest/utils@4.1.8': dependencies: - '@vitest/pretty-format': 4.1.7 + '@vitest/pretty-format': 4.1.8 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 @@ -2901,7 +2858,7 @@ snapshots: assertion-error@2.0.1: {} - ast-v8-to-istanbul@1.0.2: + ast-v8-to-istanbul@1.0.3: dependencies: '@jridgewell/trace-mapping': 0.3.31 estree-walker: 3.0.3 @@ -2948,8 +2905,6 @@ snapshots: buffer-equal-constant-time@1.0.1: {} - buffer-writer@2.0.0: {} - buffer@5.7.1: dependencies: base64-js: 1.5.1 @@ -3169,7 +3124,7 @@ snapshots: dependencies: magic-string: 0.30.21 mlly: 1.8.2 - rollup: 4.60.4 + rollup: 4.61.1 follow-redirects@1.16.0: {} @@ -3199,7 +3154,7 @@ snapshots: function-bind@1.1.2: {} - gaxios@7.1.4: + gaxios@7.1.5: dependencies: extend: 3.0.2 https-proxy-agent: 7.0.6 @@ -3209,7 +3164,7 @@ snapshots: gcp-metadata@8.1.2: dependencies: - gaxios: 7.1.4 + gaxios: 7.1.5 google-logging-utils: 1.1.3 json-bigint: 1.0.0 transitivePeerDependencies: @@ -3235,11 +3190,11 @@ snapshots: github-from-package@0.0.0: {} - google-auth-library@10.6.2: + google-auth-library@10.7.0: dependencies: base64-js: 1.5.1 ecdsa-sig-formatter: 1.0.11 - gaxios: 7.1.4 + gaxios: 7.1.5 gcp-metadata: 8.1.2 google-logging-utils: 1.1.3 jws: 4.0.1 @@ -3373,7 +3328,7 @@ snapshots: jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.19.19 + '@types/node': 22.19.20 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -3404,7 +3359,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.8.1 + semver: 7.8.4 jwa@2.0.1: dependencies: @@ -3509,7 +3464,7 @@ snapshots: make-dir@4.0.0: dependencies: - semver: 7.8.1 + semver: 7.8.4 math-intrinsics@1.1.0: {} @@ -3519,17 +3474,17 @@ snapshots: crypt: 0.0.2 is-buffer: 1.1.6 - mem0ai@3.0.6(@anthropic-ai/sdk@0.40.1)(@azure/identity@4.13.1)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260528.1)(@google/genai@1.52.0)(@langchain/core@1.1.48(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0))(@mistralai/mistralai@1.15.1)(@qdrant/js-client-rest@1.18.0(typescript@5.9.3))(@supabase/supabase-js@2.106.2)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.10.0)(cloudflare@4.5.0)(compromise@14.15.1)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.11.3)(redis@5.12.1)(ws@8.21.0): + mem0ai@3.0.7(@anthropic-ai/sdk@0.40.1)(@azure/identity@4.13.1)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260610.1)(@google/genai@1.52.0)(@langchain/core@1.1.48(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0))(@mistralai/mistralai@1.15.1)(@qdrant/js-client-rest@1.18.0(typescript@5.9.3))(@supabase/supabase-js@2.108.1)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.10.0)(cloudflare@4.5.0)(compromise@14.15.1)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.21.0)(redis@5.12.1)(ws@8.21.0): dependencies: '@anthropic-ai/sdk': 0.40.1 '@azure/identity': 4.13.1 '@azure/search-documents': 12.2.0 - '@cloudflare/workers-types': 4.20260528.1 + '@cloudflare/workers-types': 4.20260610.1 '@google/genai': 1.52.0 '@langchain/core': 1.1.48(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0) '@mistralai/mistralai': 1.15.1 '@qdrant/js-client-rest': 1.18.0(typescript@5.9.3) - '@supabase/supabase-js': 2.106.2 + '@supabase/supabase-js': 2.108.1 '@types/jest': 29.5.14 '@types/pg': 8.11.0 axios: 1.17.0 @@ -3540,9 +3495,9 @@ snapshots: natural: 8.1.1 ollama: 0.5.18 openai: 4.104.0(ws@8.21.0)(zod@3.25.76) - pg: 8.11.3 + pg: 8.21.0 redis: 5.12.1 - uuid: 9.0.1 + uuid: 14.0.0 zod: 3.25.76 transitivePeerDependencies: - debug @@ -3589,7 +3544,7 @@ snapshots: bson: 7.2.0 mongodb-connection-string-url: 7.0.1 - mongoose@9.6.3: + mongoose@9.7.0: dependencies: kareem: 3.3.0 mongodb: 7.2.0 @@ -3631,7 +3586,7 @@ snapshots: apparatus: 0.0.10 dotenv: 17.4.2 memjs: 1.3.2 - mongoose: 9.6.3 + mongoose: 9.7.0 pg: 8.21.0 redis: 5.12.1 safe-stable-stringify: 2.5.0 @@ -3654,7 +3609,7 @@ snapshots: node-abi@3.92.0: dependencies: - semver: 7.8.1 + semver: 7.8.4 node-domexception@1.0.0: {} @@ -3672,7 +3627,7 @@ snapshots: obuf@1.1.2: {} - obug@2.1.1: {} + obug@2.1.2: {} ollama@0.5.18: dependencies: @@ -3720,8 +3675,6 @@ snapshots: dependencies: p-finally: 1.0.0 - packet-reader@1.0.0: {} - pathe@2.0.3: {} pg-cloudflare@1.4.0: @@ -3733,10 +3686,6 @@ snapshots: pg-numeric@1.0.2: {} - pg-pool@3.14.0(pg@8.11.3): - dependencies: - pg: 8.11.3 - pg-pool@3.14.0(pg@8.21.0): dependencies: pg: 8.21.0 @@ -3761,18 +3710,6 @@ snapshots: postgres-interval: 3.0.0 postgres-range: 1.1.4 - pg@8.11.3: - dependencies: - buffer-writer: 2.0.0 - packet-reader: 1.0.0 - pg-connection-string: 2.13.0 - pg-pool: 3.14.0(pg@8.11.3) - pg-protocol: 1.14.0 - pg-types: 2.2.0 - pgpass: 1.0.5 - optionalDependencies: - pg-cloudflare: 1.4.0 - pg@8.21.0: dependencies: pg-connection-string: 2.13.0 @@ -3856,7 +3793,7 @@ snapshots: ansi-styles: 5.2.0 react-is: 18.3.1 - protobufjs@7.6.1: + protobufjs@7.6.3: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -3868,7 +3805,7 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.1 - '@types/node': 22.19.19 + '@types/node': 22.19.20 long: 5.3.2 proxy-from-env@2.1.0: {} @@ -3912,56 +3849,56 @@ snapshots: retry@0.13.1: {} - rolldown@1.0.2: + rolldown@1.0.3: dependencies: - '@oxc-project/types': 0.132.0 + '@oxc-project/types': 0.133.0 '@rolldown/pluginutils': 1.0.1 optionalDependencies: - '@rolldown/binding-android-arm64': 1.0.2 - '@rolldown/binding-darwin-arm64': 1.0.2 - '@rolldown/binding-darwin-x64': 1.0.2 - '@rolldown/binding-freebsd-x64': 1.0.2 - '@rolldown/binding-linux-arm-gnueabihf': 1.0.2 - '@rolldown/binding-linux-arm64-gnu': 1.0.2 - '@rolldown/binding-linux-arm64-musl': 1.0.2 - '@rolldown/binding-linux-ppc64-gnu': 1.0.2 - '@rolldown/binding-linux-s390x-gnu': 1.0.2 - '@rolldown/binding-linux-x64-gnu': 1.0.2 - '@rolldown/binding-linux-x64-musl': 1.0.2 - '@rolldown/binding-openharmony-arm64': 1.0.2 - '@rolldown/binding-wasm32-wasi': 1.0.2 - '@rolldown/binding-win32-arm64-msvc': 1.0.2 - '@rolldown/binding-win32-x64-msvc': 1.0.2 + '@rolldown/binding-android-arm64': 1.0.3 + '@rolldown/binding-darwin-arm64': 1.0.3 + '@rolldown/binding-darwin-x64': 1.0.3 + '@rolldown/binding-freebsd-x64': 1.0.3 + '@rolldown/binding-linux-arm-gnueabihf': 1.0.3 + '@rolldown/binding-linux-arm64-gnu': 1.0.3 + '@rolldown/binding-linux-arm64-musl': 1.0.3 + '@rolldown/binding-linux-ppc64-gnu': 1.0.3 + '@rolldown/binding-linux-s390x-gnu': 1.0.3 + '@rolldown/binding-linux-x64-gnu': 1.0.3 + '@rolldown/binding-linux-x64-musl': 1.0.3 + '@rolldown/binding-openharmony-arm64': 1.0.3 + '@rolldown/binding-wasm32-wasi': 1.0.3 + '@rolldown/binding-win32-arm64-msvc': 1.0.3 + '@rolldown/binding-win32-x64-msvc': 1.0.3 - rollup@4.60.4: + rollup@4.61.1: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.4 - '@rollup/rollup-android-arm64': 4.60.4 - '@rollup/rollup-darwin-arm64': 4.60.4 - '@rollup/rollup-darwin-x64': 4.60.4 - '@rollup/rollup-freebsd-arm64': 4.60.4 - '@rollup/rollup-freebsd-x64': 4.60.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 - '@rollup/rollup-linux-arm-musleabihf': 4.60.4 - '@rollup/rollup-linux-arm64-gnu': 4.60.4 - '@rollup/rollup-linux-arm64-musl': 4.60.4 - '@rollup/rollup-linux-loong64-gnu': 4.60.4 - '@rollup/rollup-linux-loong64-musl': 4.60.4 - '@rollup/rollup-linux-ppc64-gnu': 4.60.4 - '@rollup/rollup-linux-ppc64-musl': 4.60.4 - '@rollup/rollup-linux-riscv64-gnu': 4.60.4 - '@rollup/rollup-linux-riscv64-musl': 4.60.4 - '@rollup/rollup-linux-s390x-gnu': 4.60.4 - '@rollup/rollup-linux-x64-gnu': 4.60.4 - '@rollup/rollup-linux-x64-musl': 4.60.4 - '@rollup/rollup-openbsd-x64': 4.60.4 - '@rollup/rollup-openharmony-arm64': 4.60.4 - '@rollup/rollup-win32-arm64-msvc': 4.60.4 - '@rollup/rollup-win32-ia32-msvc': 4.60.4 - '@rollup/rollup-win32-x64-gnu': 4.60.4 - '@rollup/rollup-win32-x64-msvc': 4.60.4 + '@rollup/rollup-android-arm-eabi': 4.61.1 + '@rollup/rollup-android-arm64': 4.61.1 + '@rollup/rollup-darwin-arm64': 4.61.1 + '@rollup/rollup-darwin-x64': 4.61.1 + '@rollup/rollup-freebsd-arm64': 4.61.1 + '@rollup/rollup-freebsd-x64': 4.61.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.61.1 + '@rollup/rollup-linux-arm-musleabihf': 4.61.1 + '@rollup/rollup-linux-arm64-gnu': 4.61.1 + '@rollup/rollup-linux-arm64-musl': 4.61.1 + '@rollup/rollup-linux-loong64-gnu': 4.61.1 + '@rollup/rollup-linux-loong64-musl': 4.61.1 + '@rollup/rollup-linux-ppc64-gnu': 4.61.1 + '@rollup/rollup-linux-ppc64-musl': 4.61.1 + '@rollup/rollup-linux-riscv64-gnu': 4.61.1 + '@rollup/rollup-linux-riscv64-musl': 4.61.1 + '@rollup/rollup-linux-s390x-gnu': 4.61.1 + '@rollup/rollup-linux-x64-gnu': 4.61.1 + '@rollup/rollup-linux-x64-musl': 4.61.1 + '@rollup/rollup-openbsd-x64': 4.61.1 + '@rollup/rollup-openharmony-arm64': 4.61.1 + '@rollup/rollup-win32-arm64-msvc': 4.61.1 + '@rollup/rollup-win32-ia32-msvc': 4.61.1 + '@rollup/rollup-win32-x64-gnu': 4.61.1 + '@rollup/rollup-win32-x64-msvc': 4.61.1 fsevents: 2.3.3 run-applescript@7.1.0: {} @@ -3970,7 +3907,7 @@ snapshots: safe-stable-stringify@2.5.0: {} - semver@7.8.1: {} + semver@7.8.4: {} sift@17.1.3: {} @@ -4019,7 +3956,7 @@ snapshots: lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.7 - tinyglobby: 0.2.16 + tinyglobby: 0.2.17 ts-interface-checker: 0.1.13 suffix-thumb@5.0.2: {} @@ -4057,9 +3994,9 @@ snapshots: tinyexec@0.3.2: {} - tinyexec@1.2.2: {} + tinyexec@1.2.4: {} - tinyglobby@0.2.16: + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 @@ -4095,11 +4032,11 @@ snapshots: picocolors: 1.1.1 postcss-load-config: 6.0.1(postcss@8.5.15) resolve-from: 5.0.0 - rollup: 4.60.4 + rollup: 4.61.1 source-map: 0.7.6 sucrase: 3.35.1 tinyexec: 0.3.2 - tinyglobby: 0.2.16 + tinyglobby: 0.2.17 tree-kill: 1.2.2 optionalDependencies: postcss: 8.5.15 @@ -4130,45 +4067,45 @@ snapshots: uuid@13.0.2: {} - uuid@9.0.1: {} + uuid@14.0.0: {} - vite@8.0.14(@types/node@22.19.19)(esbuild@0.27.7): + vite@8.0.16(@types/node@22.19.20)(esbuild@0.27.7): dependencies: lightningcss: 1.32.0 picomatch: 4.0.4 postcss: 8.5.15 - rolldown: 1.0.2 - tinyglobby: 0.2.16 + rolldown: 1.0.3 + tinyglobby: 0.2.17 optionalDependencies: - '@types/node': 22.19.19 + '@types/node': 22.19.20 esbuild: 0.27.7 fsevents: 2.3.3 - vitest@4.1.7(@types/node@22.19.19)(@vitest/coverage-v8@4.1.7)(vite@8.0.14(@types/node@22.19.19)(esbuild@0.27.7)): + vitest@4.1.8(@types/node@22.19.20)(@vitest/coverage-v8@4.1.8)(vite@8.0.16(@types/node@22.19.20)(esbuild@0.27.7)): dependencies: - '@vitest/expect': 4.1.7 - '@vitest/mocker': 4.1.7(vite@8.0.14(@types/node@22.19.19)(esbuild@0.27.7)) - '@vitest/pretty-format': 4.1.7 - '@vitest/runner': 4.1.7 - '@vitest/snapshot': 4.1.7 - '@vitest/spy': 4.1.7 - '@vitest/utils': 4.1.7 + '@vitest/expect': 4.1.8 + '@vitest/mocker': 4.1.8(vite@8.0.16(@types/node@22.19.20)(esbuild@0.27.7)) + '@vitest/pretty-format': 4.1.8 + '@vitest/runner': 4.1.8 + '@vitest/snapshot': 4.1.8 + '@vitest/spy': 4.1.8 + '@vitest/utils': 4.1.8 es-module-lexer: 2.1.0 expect-type: 1.3.0 magic-string: 0.30.21 - obug: 2.1.1 + obug: 2.1.2 pathe: 2.0.3 picomatch: 4.0.4 std-env: 4.1.0 tinybench: 2.9.0 - tinyexec: 1.2.2 - tinyglobby: 0.2.16 + tinyexec: 1.2.4 + tinyglobby: 0.2.17 tinyrainbow: 3.1.0 - vite: 8.0.14(@types/node@22.19.19)(esbuild@0.27.7) + vite: 8.0.16(@types/node@22.19.20)(esbuild@0.27.7) why-is-node-running: 2.3.0 optionalDependencies: - '@types/node': 22.19.19 - '@vitest/coverage-v8': 4.1.7(vitest@4.1.7) + '@types/node': 22.19.20 + '@vitest/coverage-v8': 4.1.8(vitest@4.1.8) transitivePeerDependencies: - msw diff --git a/openclaw/pnpm-workspace.yaml b/integrations/openclaw/pnpm-workspace.yaml similarity index 93% rename from openclaw/pnpm-workspace.yaml rename to integrations/openclaw/pnpm-workspace.yaml index 83f4f6fd4..4396209d7 100644 --- a/openclaw/pnpm-workspace.yaml +++ b/integrations/openclaw/pnpm-workspace.yaml @@ -18,3 +18,4 @@ overrides: "langsmith@<0.6.0": "^0.6.0" "picomatch@<2.3.2": "^2.3.2" "@qdrant/js-client-rest": "^1.18.0" + "uuid@<11.1.1": ">=11.1.1" diff --git a/openclaw/providers.ts b/integrations/openclaw/providers.ts similarity index 98% rename from openclaw/providers.ts rename to integrations/openclaw/providers.ts index 6911adedc..b345aa502 100644 --- a/openclaw/providers.ts +++ b/integrations/openclaw/providers.ts @@ -13,6 +13,16 @@ import type { AddResult, } from "./types.ts"; +export function customCategoryMapToList( + categories?: Record, +): Array> | undefined { + if (!categories || typeof categories !== "object") return undefined; + const items = Object.entries(categories).map(([name, description]) => ({ + [name]: description, + })); + return items.length ? items : undefined; +} + // ============================================================================ // Result Normalizers // ============================================================================ diff --git a/openclaw/public-artifacts.ts b/integrations/openclaw/public-artifacts.ts similarity index 100% rename from openclaw/public-artifacts.ts rename to integrations/openclaw/public-artifacts.ts diff --git a/openclaw/recall.ts b/integrations/openclaw/recall.ts similarity index 100% rename from openclaw/recall.ts rename to integrations/openclaw/recall.ts diff --git a/openclaw/scripts/configure.py b/integrations/openclaw/scripts/configure.py similarity index 100% rename from openclaw/scripts/configure.py rename to integrations/openclaw/scripts/configure.py diff --git a/openclaw/skill-loader.test.ts b/integrations/openclaw/skill-loader.test.ts similarity index 100% rename from openclaw/skill-loader.test.ts rename to integrations/openclaw/skill-loader.test.ts diff --git a/openclaw/skill-loader.ts b/integrations/openclaw/skill-loader.ts similarity index 100% rename from openclaw/skill-loader.ts rename to integrations/openclaw/skill-loader.ts diff --git a/openclaw/skills/memory-dream/SKILL.md b/integrations/openclaw/skills/memory-dream/SKILL.md similarity index 100% rename from openclaw/skills/memory-dream/SKILL.md rename to integrations/openclaw/skills/memory-dream/SKILL.md diff --git a/openclaw/skills/memory-triage/SKILL.md b/integrations/openclaw/skills/memory-triage/SKILL.md similarity index 100% rename from openclaw/skills/memory-triage/SKILL.md rename to integrations/openclaw/skills/memory-triage/SKILL.md diff --git a/openclaw/skills/memory-triage/domains/companion.md b/integrations/openclaw/skills/memory-triage/domains/companion.md similarity index 100% rename from openclaw/skills/memory-triage/domains/companion.md rename to integrations/openclaw/skills/memory-triage/domains/companion.md diff --git a/openclaw/skills/memory-triage/recall-protocol.md b/integrations/openclaw/skills/memory-triage/recall-protocol.md similarity index 100% rename from openclaw/skills/memory-triage/recall-protocol.md rename to integrations/openclaw/skills/memory-triage/recall-protocol.md diff --git a/openclaw/sqlite-resilience.test.ts b/integrations/openclaw/sqlite-resilience.test.ts similarity index 100% rename from openclaw/sqlite-resilience.test.ts rename to integrations/openclaw/sqlite-resilience.test.ts diff --git a/openclaw/telemetry.ts b/integrations/openclaw/telemetry.ts similarity index 100% rename from openclaw/telemetry.ts rename to integrations/openclaw/telemetry.ts diff --git a/openclaw/test-shims/plugin-entry.ts b/integrations/openclaw/test-shims/plugin-entry.ts similarity index 100% rename from openclaw/test-shims/plugin-entry.ts rename to integrations/openclaw/test-shims/plugin-entry.ts diff --git a/openclaw/test-shims/plugin-sdk.ts b/integrations/openclaw/test-shims/plugin-sdk.ts similarity index 100% rename from openclaw/test-shims/plugin-sdk.ts rename to integrations/openclaw/test-shims/plugin-sdk.ts diff --git a/openclaw/tests/backend-platform.test.ts b/integrations/openclaw/tests/backend-platform.test.ts similarity index 100% rename from openclaw/tests/backend-platform.test.ts rename to integrations/openclaw/tests/backend-platform.test.ts diff --git a/openclaw/tests/cli-commands.test.ts b/integrations/openclaw/tests/cli-commands.test.ts similarity index 100% rename from openclaw/tests/cli-commands.test.ts rename to integrations/openclaw/tests/cli-commands.test.ts diff --git a/openclaw/tests/config-file.test.ts b/integrations/openclaw/tests/config-file.test.ts similarity index 100% rename from openclaw/tests/config-file.test.ts rename to integrations/openclaw/tests/config-file.test.ts diff --git a/openclaw/tests/config.test.ts b/integrations/openclaw/tests/config.test.ts similarity index 100% rename from openclaw/tests/config.test.ts rename to integrations/openclaw/tests/config.test.ts diff --git a/openclaw/tests/dream-gate.test.ts b/integrations/openclaw/tests/dream-gate.test.ts similarity index 100% rename from openclaw/tests/dream-gate.test.ts rename to integrations/openclaw/tests/dream-gate.test.ts diff --git a/openclaw/tests/filtering.test.ts b/integrations/openclaw/tests/filtering.test.ts similarity index 100% rename from openclaw/tests/filtering.test.ts rename to integrations/openclaw/tests/filtering.test.ts diff --git a/openclaw/tests/fs-safe.test.ts b/integrations/openclaw/tests/fs-safe.test.ts similarity index 100% rename from openclaw/tests/fs-safe.test.ts rename to integrations/openclaw/tests/fs-safe.test.ts diff --git a/openclaw/tests/json-helpers.test.ts b/integrations/openclaw/tests/json-helpers.test.ts similarity index 100% rename from openclaw/tests/json-helpers.test.ts rename to integrations/openclaw/tests/json-helpers.test.ts diff --git a/openclaw/tests/oss-wizard.test.ts b/integrations/openclaw/tests/oss-wizard.test.ts similarity index 100% rename from openclaw/tests/oss-wizard.test.ts rename to integrations/openclaw/tests/oss-wizard.test.ts diff --git a/openclaw/tests/providers.test.ts b/integrations/openclaw/tests/providers.test.ts similarity index 81% rename from openclaw/tests/providers.test.ts rename to integrations/openclaw/tests/providers.test.ts index 024bf3d91..b48f5ad5b 100644 --- a/openclaw/tests/providers.test.ts +++ b/integrations/openclaw/tests/providers.test.ts @@ -6,7 +6,11 @@ */ import { describe, it, expect, vi, beforeEach } from "vitest"; -import { providerToBackend } from "../providers.ts"; +import { + createProvider, + customCategoryMapToList, + providerToBackend, +} from "../providers.ts"; // --------------------------------------------------------------------------- // Mock provider factory @@ -37,6 +41,77 @@ beforeEach(() => { vi.resetAllMocks(); }); +// --------------------------------------------------------------------------- +// customCategoryMapToList +// --------------------------------------------------------------------------- + +describe("customCategoryMapToList", () => { + it("converts category maps to the Mem0 SDK list shape", () => { + expect( + customCategoryMapToList({ + preference: "User preferences", + work: "Work context", + }), + ).toEqual([ + { preference: "User preferences" }, + { work: "Work context" }, + ]); + }); + + it("returns undefined for empty or missing category maps", () => { + expect(customCategoryMapToList()).toBeUndefined(); + expect(customCategoryMapToList({})).toBeUndefined(); + }); +}); + +// --------------------------------------------------------------------------- +// PlatformProvider +// --------------------------------------------------------------------------- + +describe("PlatformProvider", () => { + it("passes custom_categories as a list to the Mem0 SDK", async () => { + let addOptions: Record | undefined; + + vi.doMock("mem0ai", () => ({ + default: class MockMemoryClient { + async add(_messages: unknown, opts: Record) { + addOptions = opts; + return { results: [] }; + } + }, + })); + + const provider = createProvider( + { + mode: "platform", + apiKey: "test-key", + userId: "test-user", + autoCapture: true, + autoRecall: true, + customInstructions: "Store durable facts.", + customCategories: {}, + searchThreshold: 0.1, + topK: 5, + }, + { resolvePath: (p: string) => p } as any, + ); + + await provider.add([{ role: "user", content: "Remember this" }], { + user_id: "test-user", + custom_categories: customCategoryMapToList({ + preference: "User preferences", + }), + source: "OPENCLAW", + }); + + expect(addOptions?.customCategories).toEqual([ + { preference: "User preferences" }, + ]); + + vi.doUnmock("mem0ai"); + }); +}); + // --------------------------------------------------------------------------- // search // --------------------------------------------------------------------------- diff --git a/openclaw/tests/telemetry.test.ts b/integrations/openclaw/tests/telemetry.test.ts similarity index 100% rename from openclaw/tests/telemetry.test.ts rename to integrations/openclaw/tests/telemetry.test.ts diff --git a/openclaw/tests/tools.test.ts b/integrations/openclaw/tests/tools.test.ts similarity index 100% rename from openclaw/tests/tools.test.ts rename to integrations/openclaw/tests/tools.test.ts diff --git a/openclaw/tools/index.ts b/integrations/openclaw/tools/index.ts similarity index 100% rename from openclaw/tools/index.ts rename to integrations/openclaw/tools/index.ts diff --git a/openclaw/tools/memory-add.ts b/integrations/openclaw/tools/memory-add.ts similarity index 100% rename from openclaw/tools/memory-add.ts rename to integrations/openclaw/tools/memory-add.ts diff --git a/openclaw/tools/memory-delete.ts b/integrations/openclaw/tools/memory-delete.ts similarity index 100% rename from openclaw/tools/memory-delete.ts rename to integrations/openclaw/tools/memory-delete.ts diff --git a/openclaw/tools/memory-event-list.ts b/integrations/openclaw/tools/memory-event-list.ts similarity index 100% rename from openclaw/tools/memory-event-list.ts rename to integrations/openclaw/tools/memory-event-list.ts diff --git a/openclaw/tools/memory-event-status.ts b/integrations/openclaw/tools/memory-event-status.ts similarity index 100% rename from openclaw/tools/memory-event-status.ts rename to integrations/openclaw/tools/memory-event-status.ts diff --git a/openclaw/tools/memory-get.ts b/integrations/openclaw/tools/memory-get.ts similarity index 100% rename from openclaw/tools/memory-get.ts rename to integrations/openclaw/tools/memory-get.ts diff --git a/openclaw/tools/memory-list.ts b/integrations/openclaw/tools/memory-list.ts similarity index 100% rename from openclaw/tools/memory-list.ts rename to integrations/openclaw/tools/memory-list.ts diff --git a/openclaw/tools/memory-search.ts b/integrations/openclaw/tools/memory-search.ts similarity index 100% rename from openclaw/tools/memory-search.ts rename to integrations/openclaw/tools/memory-search.ts diff --git a/openclaw/tools/memory-update.ts b/integrations/openclaw/tools/memory-update.ts similarity index 100% rename from openclaw/tools/memory-update.ts rename to integrations/openclaw/tools/memory-update.ts diff --git a/openclaw/tsconfig.json b/integrations/openclaw/tsconfig.json similarity index 100% rename from openclaw/tsconfig.json rename to integrations/openclaw/tsconfig.json diff --git a/openclaw/tsup.config.ts b/integrations/openclaw/tsup.config.ts similarity index 100% rename from openclaw/tsup.config.ts rename to integrations/openclaw/tsup.config.ts diff --git a/openclaw/types.ts b/integrations/openclaw/types.ts similarity index 98% rename from openclaw/types.ts rename to integrations/openclaw/types.ts index 5c7b1ac88..29d552396 100644 --- a/openclaw/types.ts +++ b/integrations/openclaw/types.ts @@ -36,7 +36,7 @@ export interface AddOptions { user_id: string; run_id?: string; custom_instructions?: string; - custom_categories?: Record; + custom_categories?: Array>; source?: string; // Agentic harness additions infer?: boolean; diff --git a/openclaw/vitest.config.ts b/integrations/openclaw/vitest.config.ts similarity index 100% rename from openclaw/vitest.config.ts rename to integrations/openclaw/vitest.config.ts diff --git a/pi-agent-plugin/CHANGELOG.md b/integrations/pi-agent-plugin/CHANGELOG.md similarity index 59% rename from pi-agent-plugin/CHANGELOG.md rename to integrations/pi-agent-plugin/CHANGELOG.md index aef304a95..7a9656663 100644 --- a/pi-agent-plugin/CHANGELOG.md +++ b/integrations/pi-agent-plugin/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 0.1.2 (2026-06-12) + +### Fixed + +- **Visible command feedback** — `/mem0-remember`, `/mem0-forget`, `/mem0-pin`, and `/mem0-scope` now render their results as persistent message blocks (`pi.sendMessage({ display: true })`) instead of `ctx.ui.notify(..., "info")`. The Pi TUI draws `"info"` notifications as dim, collapsible status text that overwrites the previous line, so command results were easily missed (felt like "no feedback"). Warnings and errors still use `ctx.ui.notify`, which renders prominently. +- **Relevance-filtered search** — `/mem0-search`, `/mem0-forget`, and `/mem0-pin` pass a similarity `threshold` (`searchThreshold`, default `0.3`; configurable in `mem0-config.json`, shown in `/mem0-status`), `top_k`, and `rerank` to the mem0 search API, matching the Claude Code and OpenClaw integrations. mem0 ranks results by similarity with no relevance floor, so without a threshold an unrelated query returns the closest (weak) memories — and `/mem0-forget` would offer them for deletion. The server-side threshold makes a query with no sufficiently similar memory report no match, and reranking orders the genuine matches by deeper relevance. Raise `searchThreshold` to be stricter; lower it if relevant results are missed. + +### Improved + +- **Richer feedback across every command** — results now show what actually happened: an action heading plus the relevant scope, query, match count, and affected memories. + - `/mem0-remember` echoes the stored text and the scope it landed in, instead of a generic success line. + - `/mem0-search` adds an `N matches for ""` header. + - `/mem0-forget` and `/mem0-pin` name the query when nothing matches, list the affected memory, and label the disambiguation dialog with the match count. + - `/mem0-scope` explains where new memories will be saved. +- **Cleaner `/mem0-dream`** — the consolidation protocol is now sent to the agent hidden (`display: false`, still included in LLM context) behind a concise "Dreaming…" status line, instead of dumping the raw protocol into the transcript. + ## 0.1.1 (2026-06-10) Maintenance release — no functional changes. diff --git a/pi-agent-plugin/LICENSE b/integrations/pi-agent-plugin/LICENSE similarity index 100% rename from pi-agent-plugin/LICENSE rename to integrations/pi-agent-plugin/LICENSE diff --git a/pi-agent-plugin/README.md b/integrations/pi-agent-plugin/README.md similarity index 91% rename from pi-agent-plugin/README.md rename to integrations/pi-agent-plugin/README.md index a819c464f..0c88c1b6b 100644 --- a/pi-agent-plugin/README.md +++ b/integrations/pi-agent-plugin/README.md @@ -43,6 +43,7 @@ Or create a config file at `~/.pi/agent/mem0-config.json`: "userId": "your-username", "autoCapture": true, "defaultScope": "project", + "searchThreshold": 0.2, "dream": { "enabled": true, "auto": true, @@ -55,6 +56,8 @@ Or create a config file at `~/.pi/agent/mem0-config.json`: Environment variables (`MEM0_API_KEY`, `MEM0_USER_ID`) override the config file. +`searchThreshold` (default `0.3`) is the minimum similarity score (0–1) a memory must reach to count as a match for `/mem0-search`, `/mem0-forget`, and `/mem0-pin`. It is passed to the mem0 search API (along with reranking for higher-precision ordering), so a query with no sufficiently similar memory reports no match instead of returning the closest unrelated memories. Raise it to be stricter; lower it if relevant results are missed. + ## Commands | Command | Description | diff --git a/pi-agent-plugin/package.json b/integrations/pi-agent-plugin/package.json similarity index 83% rename from pi-agent-plugin/package.json rename to integrations/pi-agent-plugin/package.json index 5c5a470b8..31a3e76e0 100644 --- a/pi-agent-plugin/package.json +++ b/integrations/pi-agent-plugin/package.json @@ -1,13 +1,13 @@ { "name": "@mem0/pi-agent-plugin", - "version": "0.1.1", + "version": "0.1.2", "type": "module", "description": "Mem0 memory extension for Pi Agent persistent, scoped, semantic memory across sessions and projects", "license": "Apache-2.0", "repository": { "type": "git", "url": "https://github.com/mem0ai/mem0", - "directory": "pi-agent-plugin" + "directory": "integrations/pi-agent-plugin" }, "keywords": [ "pi-package", @@ -39,8 +39,12 @@ "LICENSE" ], "pi": { - "extensions": ["./src/entry.ts"], - "skills": ["./skills"] + "extensions": [ + "./src/entry.ts" + ], + "skills": [ + "./skills" + ] }, "scripts": { "build": "tsup", @@ -49,8 +53,8 @@ "typecheck": "tsc --noEmit" }, "peerDependencies": { - "@earendil-works/pi-coding-agent": "*", "@earendil-works/pi-ai": "*", + "@earendil-works/pi-coding-agent": "*", "typebox": "*" }, "devDependencies": { @@ -63,6 +67,11 @@ "vitest": "^4.1.7" }, "dependencies": { - "mem0ai": "^3.0.6" + "mem0ai": "^3.0.7" + }, + "pnpm": { + "overrides": { + "uuid@<11.1.1": ">=11.1.1" + } } } diff --git a/pi-agent-plugin/pnpm-lock.yaml b/integrations/pi-agent-plugin/pnpm-lock.yaml similarity index 94% rename from pi-agent-plugin/pnpm-lock.yaml rename to integrations/pi-agent-plugin/pnpm-lock.yaml index 32b106961..9ce9357d1 100644 --- a/pi-agent-plugin/pnpm-lock.yaml +++ b/integrations/pi-agent-plugin/pnpm-lock.yaml @@ -4,20 +4,23 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + uuid@<11.1.1: '>=11.1.1' + importers: .: dependencies: mem0ai: - specifier: ^3.0.6 - version: 3.0.6(@anthropic-ai/sdk@0.91.1(zod@3.25.76))(@azure/identity@4.13.1)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260609.1)(@google/genai@1.52.0)(@langchain/core@1.1.48(openai@6.26.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0))(@mistralai/mistralai@2.2.1)(@qdrant/js-client-rest@1.18.0(typescript@6.0.3))(@supabase/supabase-js@2.108.0)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.10.0)(cloudflare@4.5.0)(compromise@14.15.1)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.21.0)(redis@5.12.1)(ws@8.21.0) + specifier: ^3.0.7 + version: 3.0.7(@anthropic-ai/sdk@0.91.1(zod@3.25.76))(@azure/identity@4.13.1)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260610.1)(@google/genai@1.52.0)(@langchain/core@1.1.48(openai@6.26.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0))(@mistralai/mistralai@2.2.1)(@qdrant/js-client-rest@1.18.0(typescript@6.0.3))(@supabase/supabase-js@2.108.1)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.10.0)(cloudflare@4.5.0)(compromise@14.15.1)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.21.0)(redis@5.12.1)(ws@8.21.0) devDependencies: '@earendil-works/pi-ai': specifier: ^0.79.0 - version: 0.79.0(ws@8.21.0)(zod@3.25.76) + version: 0.79.1(ws@8.21.0)(zod@3.25.76) '@earendil-works/pi-coding-agent': specifier: ^0.79.0 - version: 0.79.0(ws@8.21.0)(zod@3.25.76) + version: 0.79.1(ws@8.21.0)(zod@3.25.76) '@types/node': specifier: ^25.9.2 version: 25.9.2 @@ -26,7 +29,7 @@ importers: version: 8.5.1(jiti@2.7.0)(postcss@8.5.15)(typescript@6.0.3)(yaml@2.9.0) typebox: specifier: ^1.2.3 - version: 1.2.3 + version: 1.2.8 typescript: specifier: ^6.0.3 version: 6.0.3 @@ -66,40 +69,40 @@ packages: resolution: {integrity: sha512-u+NT61JZEkRFtpL0CAw1N1dwxnaLgwVXQl/zjJxTGgLyS/jTIdg2SdoEoCTHxgDyCnqa1HEi9QOoE9/pYRNpOQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/core@3.974.19': - resolution: {integrity: sha512-SMNfLCU/41xxfFaC5Slwy8V/f1FRhakvyeeMeDeIxqNF0DzhDlXsXnJDELJYke1EtnJbfzfilW7tvulGfxMY6A==} + '@aws-sdk/core@3.974.20': + resolution: {integrity: sha512-7sDi2B2N3mc3nf1nz6FyEx/FCrJ1N1QnBmraHHQNabFaeAh2IaOOLml48/rHOD1bICHgTRkbBgNTvUzEr5Z35g==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-env@3.972.45': - resolution: {integrity: sha512-ZPsnLyrpDRmojKrBbJykASyLLVFkjyD+fWATeSuYgaqablijGOzxPxEKyrwUvNg+bgSQ7PkW2FTu65Xco19Gag==} + '@aws-sdk/credential-provider-env@3.972.46': + resolution: {integrity: sha512-+GPXVS2srMOlH74S+SmC1gVuP2TvUZ0siuC0onKO93q+udP+M72dmY8wJfVQ5CX9z/9X5A1HHwz5yRIGBtskvQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-http@3.972.47': - resolution: {integrity: sha512-1XdgHDIPbARHuzZXM7ouzIbSUZFU9dTi9k+ryMhiZU4QCam4dvwOyUEFjEHNxAZehCYUIOmsSUZ2un6BIgUkWg==} + '@aws-sdk/credential-provider-http@3.972.48': + resolution: {integrity: sha512-fA5loSdlocacRxyUXtpoHSMuk5rsIKRDzQYVMnMxjcmFeZshaJlJ8lymy/hYKji6sne/UmNGj5pxuEs6kq/Qcg==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-ini@3.972.51': - resolution: {integrity: sha512-f8sRTVyM+9BbzQKPlUP9dVVpgNEu65jFckNAAGzRfCrlaSi5AWUbCKEHIMcIYokv8pWblSKEqHkqKYZtwINnhw==} + '@aws-sdk/credential-provider-ini@3.972.52': + resolution: {integrity: sha512-szg1nnebqC+Svv6Vfsdf6P/QK8x5g/ghG2CKa/1WkHifRnq0BBmDELj2Qnqk9nPsUvEu/OEcYic97CPLpKqF9g==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-login@3.972.50': - resolution: {integrity: sha512-NHHsKoMhw6UylSU0XDnDc87+IQW8tRBTIe6vnOX12GSIlBDtoce6bSzONleIglCyu8d3H9bmTSfk+sIN5yh3WA==} + '@aws-sdk/credential-provider-login@3.972.51': + resolution: {integrity: sha512-csHFsH+/VjnI40oqm1l1OqMY4B4kza36DbfcbHcgcbobgjebasqUbTU34xvwUkvtoNGGizbfyMSlMzJWUPv3dQ==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-node@3.972.53': - resolution: {integrity: sha512-z/JJ8Qvf2GiTn4bw+x8k7wQjxmPpNsiwZ7ls/h1cZHikrSpS0+65lB+lafnXZlxv1lqH4k6rQwh+2UsycC662g==} + '@aws-sdk/credential-provider-node@3.972.54': + resolution: {integrity: sha512-vinTSQtziNHxi2nqXF+76jr2sO44q88Ind1qFFVaotNgBaC1rcWDjBug8yoE8n0ov33s21xks9WY5XDHH9SENw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-process@3.972.45': - resolution: {integrity: sha512-QMJXjTGLmHE4Ie03T5H4hHOLfcvMc9DaODO6b5dgte3S8ECf5bBuHUJW4cQREcYZyRkOU8iymqtiBxqF4icxZg==} + '@aws-sdk/credential-provider-process@3.972.46': + resolution: {integrity: sha512-VUoNFBIjWrUN8NbFiQiuxQEgFjvziAlBRPK+ddh27aj65gk0BYu6bLZnrdrNZwpW6vAihtSUtEMQ1PUJ32QRPA==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-sso@3.972.50': - resolution: {integrity: sha512-pQ9ww4G53gwHlon1NMz25JhaBo13E9Jv+VVgjh39C/yzvby+xhSnEOb+VDYShKNCh1TbttMF/5CFCHkZrIqOcA==} + '@aws-sdk/credential-provider-sso@3.972.51': + resolution: {integrity: sha512-60qhpQcSDIKIr0AuBlmJezKX0b5nbJPCINiR49N9yJXrEI5tTRwsXVBr0IdSvvsNJyqgiINyoBd++Ed0yvggbw==} engines: {node: '>=20.0.0'} - '@aws-sdk/credential-provider-web-identity@3.972.50': - resolution: {integrity: sha512-9DbaPaT2aMbz18wtSpq9HVBErjBQwxykqTFgG6n8Bn05GN68mITz+G1869ekYx0mVT/BDjETj5czz/3cPgLwxA==} + '@aws-sdk/credential-provider-web-identity@3.972.51': + resolution: {integrity: sha512-0X5eWsUIp8ItRJeJBBrhQAPzc9AQelDetRTVTsycCAISCCzM17R4hs/vFAPeQ0o0B35sciLiqe/Pwmml909cZA==} engines: {node: '>=20.0.0'} '@aws-sdk/eventstream-handler-node@3.972.21': @@ -110,12 +113,12 @@ packages: resolution: {integrity: sha512-tdbnXbw73ww62ABWP0G0Z/euvFowEEvAoi/zG4NaZo7HJFpfGho/Z65HyVzkJLT1cMsUregr4pTyxljlarT0wA==} engines: {node: '>=20.0.0'} - '@aws-sdk/middleware-websocket@3.972.27': - resolution: {integrity: sha512-V/IgUogQm/NSGlNglDCkREirQXgjyrrq64vPt5qcRTGhEJJwPcUB3RyYE6iZ63WNGp4Ezc+JtVRA4QlSbhDvVQ==} + '@aws-sdk/middleware-websocket@3.972.28': + resolution: {integrity: sha512-SCW06Zjugn86pq7+dxGnFcyWJuEWHT753HTU/Vj/OzVxP+NoShwdAr4ynxAcvWL883OgRVbSqW3ohnjIxwXjjw==} engines: {node: '>= 14.0.0'} - '@aws-sdk/nested-clients@3.997.18': - resolution: {integrity: sha512-xBWrodBvW5SHCZV11UZUJG0pSHkLCEREIBoNbff1C1sacOUCmxJnTCPE80sCGLCtqgXg98I2MQJe2z28tcZSsw==} + '@aws-sdk/nested-clients@3.997.19': + resolution: {integrity: sha512-P2Otgf15GBJMKzG6j5Ddf7w+Kz6z2jvesMy874TD3jlMfDWNK7clJeUd7hgigdeVOotjoUP4emcTWVdS9sfZDw==} engines: {node: '>=20.0.0'} '@aws-sdk/signature-v4-multi-region@3.996.33': @@ -126,8 +129,8 @@ packages: resolution: {integrity: sha512-k0y/GcuesuSfWyUM0WamrGyeZmltRYaPbHO82UDA6mZ/doB+FOHKutikPAtSXMn/hDz970cF+iRuuiYO9VEbAA==} engines: {node: '>=20.0.0'} - '@aws-sdk/token-providers@3.1064.0': - resolution: {integrity: sha512-sjI+iA4JtgeckBgKwPQF7KzWillRoNDmtpiM0TRa0syiAKFHKUSf84kPXSO3+gA7aMMSxrcxzOM2oPSecaJvEA==} + '@aws-sdk/token-providers@3.1065.0': + resolution: {integrity: sha512-qdHQntq82gMqG6Tf8xrgmhJxacaYkxW4PEeDg/ISMVJ84EWe7iD6JyCTgbyox3uNDH6vqEJ8GUiTaXCq307zVw==} engines: {node: '>=20.0.0'} '@aws-sdk/types@3.973.12': @@ -220,25 +223,25 @@ packages: '@cfworker/json-schema@4.1.1': resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} - '@cloudflare/workers-types@4.20260609.1': - resolution: {integrity: sha512-krGHtwSApCFBjTe1NTx/TFQ0P5i/bHGQOqCPnCLssb8rOKaAG4JkPFJZsossr0z/ZTMnpP2Tid5jWju+/i0hCA==} + '@cloudflare/workers-types@4.20260610.1': + resolution: {integrity: sha512-Mk/f3lUygeIHzQ4HnJjU/JvGg/kllgp9gISty9nylHE/2M2MFeKO+hgAKSgiPpmwUbuhewdYGgqFGgT/ADK0/g==} - '@earendil-works/pi-agent-core@0.79.0': - resolution: {integrity: sha512-jQOtYjRGZ7+XC/olw9euLd2V03vkAPO8u0sSnQoLbyOQZz66dEBZrklTESk34Sf3AaeBSua28wjZR48ch1aXJQ==} + '@earendil-works/pi-agent-core@0.79.1': + resolution: {integrity: sha512-PBPjBa2YBm9jauiLtHAKaSfVJ4Dvm3/nK/bR/oHebLjwBCS2tGx3aQDX7MSGAOXi6BejlhzbB/z82BkyAyNjjQ==} engines: {node: '>=22.19.0'} - '@earendil-works/pi-ai@0.79.0': - resolution: {integrity: sha512-D/2aDoe9vcCbqAztALQcKkdqXGuaQcqAzLm8LfUhNaorwoIHkwnaAuDVlo+OkF5clpEwS8Z1bk2o8NiSrwEdsA==} + '@earendil-works/pi-ai@0.79.1': + resolution: {integrity: sha512-UnORwrcsTNLm4StEvoM8iEom0u87Te7BXEWxhec3iNXygWD6eEBosUoq9ddcveqtj/QpUZBMPWUu81cCtZxzkQ==} engines: {node: '>=22.19.0'} hasBin: true - '@earendil-works/pi-coding-agent@0.79.0': - resolution: {integrity: sha512-pZoXk65vFR3dAzzmPNWEX61aHnT6+BaVhTyFDQAs1DyumaMeWpvzRV9ZrGxqlbVLwhrq+0LnXbaqDAFkhe2+MQ==} + '@earendil-works/pi-coding-agent@0.79.1': + resolution: {integrity: sha512-dLnje4U5H3/ZytJpvhjhPINeDT/yvx85e4OH/ziMQRLpPlfNP12/peY9jRQd4W11Xth2+y2xGAFwS+NeVf2ZwA==} engines: {node: '>=22.19.0'} hasBin: true - '@earendil-works/pi-tui@0.79.0': - resolution: {integrity: sha512-qAQWMruW7YKbk2hPcTD4INtXfvIySXifbPQ+mFY5j3J8yf2tfElkh+gGPuBvgPKPT0z9WiAkd7iySCuQq0txuQ==} + '@earendil-works/pi-tui@0.79.1': + resolution: {integrity: sha512-YvZCMfSE0YDSLNklAwMY6LC6SyEgnP0zMOoioTLNnXFNdexrCexMJdee7iDJsNcFlKt7+DVLccomuURtZS1C6g==} engines: {node: '>=22.19.0'} '@emnapi/core@1.10.0': @@ -513,8 +516,8 @@ packages: '@mongodb-js/saslprep@1.4.11': resolution: {integrity: sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA==} - '@napi-rs/wasm-runtime@1.1.4': - resolution: {integrity: sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow==} + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 @@ -867,31 +870,31 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@supabase/auth-js@2.108.0': - resolution: {integrity: sha512-0CzVGVqHfNOhRQVEcAmu58Mex2Ce0zL3aGfyV+iFQjTK6OntLK/hLCLr/VDRX0E8/2CdsiY99L7fZ/8ys/op4w==} + '@supabase/auth-js@2.108.1': + resolution: {integrity: sha512-Lle5rKU8f9LF3K5dDd8Or8mkkG+ptzRZZWKPVMm9B9UuovH65Ss2+iFnQqRsCqaGouvJEcTWyl0cj2riNrrDLQ==} engines: {node: '>=20.0.0'} - '@supabase/functions-js@2.108.0': - resolution: {integrity: sha512-lqEGDzT7QBUuKYzi5lHpV/XecXT9wikzcbXMbFo6krNpSDynD1sHM8wcsfB/BAqa4NkFuy3vF4JCV8MeakV8IQ==} + '@supabase/functions-js@2.108.1': + resolution: {integrity: sha512-fxBRW/A4IG7ADQztVt0NaEy5ysiO1WJ2pbldsnBchrkHuyepX0Krek9qA9T4gUQBVVTCE9Ea4pdsM5hfn3nc4A==} engines: {node: '>=20.0.0'} '@supabase/phoenix@0.4.2': resolution: {integrity: sha512-YSAGnmDAfuleFCVt3CeurQZAhxRfXWeZIIkwp7NhYzQ1UwW6ePSnzsFAiUm/mbCkfoCf70QQHKW/K6RKh52a4A==} - '@supabase/postgrest-js@2.108.0': - resolution: {integrity: sha512-8AwTkPqowDYv/qh016CyXeZ3Ukpw6NHyfqc7DWV4afLR2hAiapf3zRKV2ZLG+//T1LK84HrR6X8VBwfgHWmNyw==} + '@supabase/postgrest-js@2.108.1': + resolution: {integrity: sha512-9lj2MCPPMgSTaJ5y+amnhb3TWPtMFVlbDn2hmX/VV91xQU4j0AauwfMaBErHBJ+zzsSwjc0jLU+zLIZFLQzfig==} engines: {node: '>=20.0.0'} - '@supabase/realtime-js@2.108.0': - resolution: {integrity: sha512-N3xR0u7TNr+c5wuLSU60rcfu/H/8N0WBs7iHWwjI/NxKwY3XWSyLUbpbpU8bzmL0dA/Gk9Mupri8mxKUXBW+iw==} + '@supabase/realtime-js@2.108.1': + resolution: {integrity: sha512-mHGGqOjwd1XTydcoffUqEMsbFQHUi6A3uhQ0EXr3iqzpLqItxKA9nbN6gIQxrZ7JRRnuUe/iOFPUkYV9Tdc5lg==} engines: {node: '>=20.0.0'} - '@supabase/storage-js@2.108.0': - resolution: {integrity: sha512-zMYQmh87CId7d8i/1FIfv4fMDcXPutmIJSpoY58GLXi7M266MNzxWGNabDk4i555Oj1Nqtsu2i3Qo3rpWUXO6A==} + '@supabase/storage-js@2.108.1': + resolution: {integrity: sha512-Er0SGGt85iT6ye+SSh98Az6L2CesoZJuyzEZYH2oBOAnIxa9Nn4CtwUC3veGxYggoT56X+3tVuuQeDBP8kR8sg==} engines: {node: '>=20.0.0'} - '@supabase/supabase-js@2.108.0': - resolution: {integrity: sha512-AjPoimM9MZLZbddnlDBGmpZ/Tas1dNcJvuZy/VD1AfmrjBC8J2RSw6UOqR4ISLLlEioOLca/5t1crFnAxa0wRQ==} + '@supabase/supabase-js@2.108.1': + resolution: {integrity: sha512-V/1hRKLSCJ0zEL+9QFRBUtivvePfOsaAYQmC0HhFNSHC2F3xFs4jSF3YhkLmzex6E4V4FGvmBDOP72D/53NnZA==} engines: {node: '>=20.0.0'} '@tybys/wasm-util@0.10.2': @@ -1019,6 +1022,9 @@ packages: any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} + anynum@1.0.0: + resolution: {integrity: sha512-xjR9/zBVnUOP6ztMIIgShjsxui80nQUQH+5xJnvrYLs+90bF25/KJqaAi8mk+B4RDtX1Nspi6fmp4YTEts8SfA==} + apparatus@0.0.10: resolution: {integrity: sha512-KLy/ugo33KZA7nugtQ7O0E1c8kQ52N3IvD/XgIh4w/Nr28ypfkwDfA67F1ev4N1m5D+BOk1+b2dEJDfpj/VvZg==} engines: {node: '>=0.2.6'} @@ -1683,8 +1689,8 @@ packages: md5@2.3.0: resolution: {integrity: sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==} - mem0ai@3.0.6: - resolution: {integrity: sha512-X1KbCVE5351qpJ6X2KXFhQJiW+dqoHj1otA66DSdXtIEO1uXuIdyeTKeURdAH+wjXtIcFa08ylL2meA1pBk9Iw==} + mem0ai@3.0.7: + resolution: {integrity: sha512-CUHzX7DyeKTHcI3aDsSqY9LXTD7GcFxf988796TuOa4yJgGuF2Xd2NROcBhVFRo3r9y8fVmbo3c5TF9jv1KlKw==} engines: {node: '>=18'} peerDependencies: '@anthropic-ai/sdk': ^0.40.1 @@ -1778,8 +1784,8 @@ packages: socks: optional: true - mongoose@9.6.3: - resolution: {integrity: sha512-vI6dTTlQnfMCyyQ5TrvhG0bCRs4dq5e1uFNPtOOWsOhn0fSg8AoIHjfyyCYr8aybyvPs845dRHGxsC3w/fHcBA==} + mongoose@9.7.0: + resolution: {integrity: sha512-pkrLZ6U41pD4Ai0ju/FYL7o5I5k+rV3RZINQTG937hbhnLGKRuqqYm1Dlt/kTQ+M4FHijzV6JawzsdHKRGt7QA==} engines: {node: '>=20.19.0'} mpath@0.9.0: @@ -2043,8 +2049,8 @@ packages: proper-lockfile@4.1.2: resolution: {integrity: sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==} - protobufjs@7.6.2: - resolution: {integrity: sha512-N9EiLovGEQOJSPF26Ij7qUGvahfEnq0eeYZ02aigIedkmz1qZSwjnP9SBITHJuF/6MYbIW4HDN8zdYjsjqJKXQ==} + protobufjs@7.6.3: + resolution: {integrity: sha512-+k0vdJKNdW+Vu+dYe8tZA/VvQb6XKNWexC6URwBFXxNnjLJz9nQJCemGyNgRAWD+B7+nGNc9qMPGwcD7s4nzUw==} engines: {node: '>=12.0.0'} proxy-from-env@2.1.0: @@ -2110,8 +2116,8 @@ packages: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} - semver@7.8.3: - resolution: {integrity: sha512-wnilbGyMxzbY7dNOl7jpKbLSjcfeweJWU5j4+u5qW+6/wuGD9KzIGOyZnQVSBM9E7DtWaaH3CyHkppYrKYoxwg==} + semver@7.8.4: + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} engines: {node: '>=10'} hasBin: true @@ -2178,8 +2184,8 @@ packages: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} - strnum@2.3.0: - resolution: {integrity: sha512-ums3KNd42PGyx5xaoVTO1mjU1bH3NpY4vsrVlnv9PNGqQj8wd7rJ6nEypLrJ7z5vxK5RP0yMLo6J/Gsm62DI5Q==} + strnum@2.4.0: + resolution: {integrity: sha512-sHrVyWWdq28RbhjuJdZsA1SnGRJV6NiXbk6AXBxDOsgAcA+lmpUZCYjOdLBxkXMwis6RRe7dlZt4VlIWFVzkmg==} sucrase@3.35.1: resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} @@ -2278,8 +2284,8 @@ packages: typebox@1.1.38: resolution: {integrity: sha512-pZ0aQPmMmXoUvSbeuWf/Hzsc+avNw/Zd6VeE8CFgkVGWyuHPJvqeJJDeJqLve+K70LvjYIoleGcoJHPT17cWoA==} - typebox@1.2.3: - resolution: {integrity: sha512-EdFz/9FGvhQK2NmjZjSLri+nd5oMH+t/46/6lueEPbeHmS9WbWOz5IayPyLw0HHIyOWfo+wPrQGcAEQ+GsXc/w==} + typebox@1.2.8: + resolution: {integrity: sha512-rlsKhsd7L3082m9nxhFIB4Gl8jizLd/6YlFAWaz96hdV8+VJRjwYaYmDlT0jlTRwkb48SxCSSirqUtrg/HilNw==} typescript@6.0.3: resolution: {integrity: sha512-y2TvuxSZPDyQakkFRPZHKFm+KKVqIisdg9/CZwm9ftvKXLP8NRWj38/ODjNbr43SsoXqNuAisEf1GdCxqWcdBw==} @@ -2313,9 +2319,8 @@ packages: resolution: {integrity: sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==} hasBin: true - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + uuid@14.0.0: + resolution: {integrity: sha512-Qo+uWgilfSmAhXCMav1uYFynlQO7fMFiMVZsQqZRMIXp0O7rR7qjkj+cPvBHLgBqi960QCoo/PH2/6ZtVqKvrg==} hasBin: true vite@8.0.16: @@ -2528,11 +2533,11 @@ snapshots: dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.19 - '@aws-sdk/credential-provider-node': 3.972.53 + '@aws-sdk/core': 3.974.20 + '@aws-sdk/credential-provider-node': 3.972.54 '@aws-sdk/eventstream-handler-node': 3.972.21 '@aws-sdk/middleware-eventstream': 3.972.17 - '@aws-sdk/middleware-websocket': 3.972.27 + '@aws-sdk/middleware-websocket': 3.972.28 '@aws-sdk/token-providers': 3.1048.0 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 @@ -2541,7 +2546,7 @@ snapshots: '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/core@3.974.19': + '@aws-sdk/core@3.974.20': dependencies: '@aws-sdk/types': 3.973.12 '@aws-sdk/xml-builder': 3.972.29 @@ -2552,17 +2557,17 @@ snapshots: bowser: 2.14.1 tslib: 2.8.1 - '@aws-sdk/credential-provider-env@3.972.45': + '@aws-sdk/credential-provider-env@3.972.46': dependencies: - '@aws-sdk/core': 3.974.19 + '@aws-sdk/core': 3.974.20 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/credential-provider-http@3.972.47': + '@aws-sdk/credential-provider-http@3.972.48': dependencies: - '@aws-sdk/core': 3.974.19 + '@aws-sdk/core': 3.974.20 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 '@smithy/fetch-http-handler': 5.4.6 @@ -2570,67 +2575,67 @@ snapshots: '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/credential-provider-ini@3.972.51': + '@aws-sdk/credential-provider-ini@3.972.52': dependencies: - '@aws-sdk/core': 3.974.19 - '@aws-sdk/credential-provider-env': 3.972.45 - '@aws-sdk/credential-provider-http': 3.972.47 - '@aws-sdk/credential-provider-login': 3.972.50 - '@aws-sdk/credential-provider-process': 3.972.45 - '@aws-sdk/credential-provider-sso': 3.972.50 - '@aws-sdk/credential-provider-web-identity': 3.972.50 - '@aws-sdk/nested-clients': 3.997.18 + '@aws-sdk/core': 3.974.20 + '@aws-sdk/credential-provider-env': 3.972.46 + '@aws-sdk/credential-provider-http': 3.972.48 + '@aws-sdk/credential-provider-login': 3.972.51 + '@aws-sdk/credential-provider-process': 3.972.46 + '@aws-sdk/credential-provider-sso': 3.972.51 + '@aws-sdk/credential-provider-web-identity': 3.972.51 + '@aws-sdk/nested-clients': 3.997.19 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 '@smithy/credential-provider-imds': 4.3.8 '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/credential-provider-login@3.972.50': + '@aws-sdk/credential-provider-login@3.972.51': dependencies: - '@aws-sdk/core': 3.974.19 - '@aws-sdk/nested-clients': 3.997.18 + '@aws-sdk/core': 3.974.20 + '@aws-sdk/nested-clients': 3.997.19 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/credential-provider-node@3.972.53': + '@aws-sdk/credential-provider-node@3.972.54': dependencies: - '@aws-sdk/credential-provider-env': 3.972.45 - '@aws-sdk/credential-provider-http': 3.972.47 - '@aws-sdk/credential-provider-ini': 3.972.51 - '@aws-sdk/credential-provider-process': 3.972.45 - '@aws-sdk/credential-provider-sso': 3.972.50 - '@aws-sdk/credential-provider-web-identity': 3.972.50 + '@aws-sdk/credential-provider-env': 3.972.46 + '@aws-sdk/credential-provider-http': 3.972.48 + '@aws-sdk/credential-provider-ini': 3.972.52 + '@aws-sdk/credential-provider-process': 3.972.46 + '@aws-sdk/credential-provider-sso': 3.972.51 + '@aws-sdk/credential-provider-web-identity': 3.972.51 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 '@smithy/credential-provider-imds': 4.3.8 '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/credential-provider-process@3.972.45': + '@aws-sdk/credential-provider-process@3.972.46': dependencies: - '@aws-sdk/core': 3.974.19 + '@aws-sdk/core': 3.974.20 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/credential-provider-sso@3.972.50': + '@aws-sdk/credential-provider-sso@3.972.51': dependencies: - '@aws-sdk/core': 3.974.19 - '@aws-sdk/nested-clients': 3.997.18 - '@aws-sdk/token-providers': 3.1064.0 + '@aws-sdk/core': 3.974.20 + '@aws-sdk/nested-clients': 3.997.19 + '@aws-sdk/token-providers': 3.1065.0 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/credential-provider-web-identity@3.972.50': + '@aws-sdk/credential-provider-web-identity@3.972.51': dependencies: - '@aws-sdk/core': 3.974.19 - '@aws-sdk/nested-clients': 3.997.18 + '@aws-sdk/core': 3.974.20 + '@aws-sdk/nested-clients': 3.997.19 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 '@smithy/types': 4.14.3 @@ -2650,9 +2655,9 @@ snapshots: '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/middleware-websocket@3.972.27': + '@aws-sdk/middleware-websocket@3.972.28': dependencies: - '@aws-sdk/core': 3.974.19 + '@aws-sdk/core': 3.974.20 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 '@smithy/fetch-http-handler': 5.4.6 @@ -2660,11 +2665,11 @@ snapshots: '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/nested-clients@3.997.18': + '@aws-sdk/nested-clients@3.997.19': dependencies: '@aws-crypto/sha256-browser': 5.2.0 '@aws-crypto/sha256-js': 5.2.0 - '@aws-sdk/core': 3.974.19 + '@aws-sdk/core': 3.974.20 '@aws-sdk/signature-v4-multi-region': 3.996.33 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 @@ -2682,17 +2687,17 @@ snapshots: '@aws-sdk/token-providers@3.1048.0': dependencies: - '@aws-sdk/core': 3.974.19 - '@aws-sdk/nested-clients': 3.997.18 + '@aws-sdk/core': 3.974.20 + '@aws-sdk/nested-clients': 3.997.19 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 '@smithy/types': 4.14.3 tslib: 2.8.1 - '@aws-sdk/token-providers@3.1064.0': + '@aws-sdk/token-providers@3.1065.0': dependencies: - '@aws-sdk/core': 3.974.19 - '@aws-sdk/nested-clients': 3.997.18 + '@aws-sdk/core': 3.974.20 + '@aws-sdk/nested-clients': 3.997.19 '@aws-sdk/types': 3.973.12 '@smithy/core': 3.24.6 '@smithy/types': 4.14.3 @@ -2834,11 +2839,11 @@ snapshots: '@cfworker/json-schema@4.1.1': {} - '@cloudflare/workers-types@4.20260609.1': {} + '@cloudflare/workers-types@4.20260610.1': {} - '@earendil-works/pi-agent-core@0.79.0(ws@8.21.0)(zod@3.25.76)': + '@earendil-works/pi-agent-core@0.79.1(ws@8.21.0)(zod@3.25.76)': dependencies: - '@earendil-works/pi-ai': 0.79.0(ws@8.21.0)(zod@3.25.76) + '@earendil-works/pi-ai': 0.79.1(ws@8.21.0)(zod@3.25.76) ignore: 7.0.5 typebox: 1.1.38 yaml: 2.9.0 @@ -2850,7 +2855,7 @@ snapshots: - ws - zod - '@earendil-works/pi-ai@0.79.0(ws@8.21.0)(zod@3.25.76)': + '@earendil-works/pi-ai@0.79.1(ws@8.21.0)(zod@3.25.76)': dependencies: '@anthropic-ai/sdk': 0.91.1(zod@3.25.76) '@aws-sdk/client-bedrock-runtime': 3.1048.0 @@ -2870,11 +2875,11 @@ snapshots: - ws - zod - '@earendil-works/pi-coding-agent@0.79.0(ws@8.21.0)(zod@3.25.76)': + '@earendil-works/pi-coding-agent@0.79.1(ws@8.21.0)(zod@3.25.76)': dependencies: - '@earendil-works/pi-agent-core': 0.79.0(ws@8.21.0)(zod@3.25.76) - '@earendil-works/pi-ai': 0.79.0(ws@8.21.0)(zod@3.25.76) - '@earendil-works/pi-tui': 0.79.0 + '@earendil-works/pi-agent-core': 0.79.1(ws@8.21.0)(zod@3.25.76) + '@earendil-works/pi-ai': 0.79.1(ws@8.21.0)(zod@3.25.76) + '@earendil-works/pi-tui': 0.79.1 '@silvia-odwyer/photon-node': 0.3.4 chalk: 5.6.2 cross-spawn: 7.0.6 @@ -2899,7 +2904,7 @@ snapshots: - ws - zod - '@earendil-works/pi-tui@0.79.0': + '@earendil-works/pi-tui@0.79.1': dependencies: get-east-asian-width: 1.6.0 marked: 15.0.12 @@ -3002,7 +3007,7 @@ snapshots: dependencies: google-auth-library: 10.7.0 p-retry: 4.6.2 - protobufjs: 7.6.2 + protobufjs: 7.6.3 ws: 8.21.0 transitivePeerDependencies: - bufferutil @@ -3113,7 +3118,7 @@ snapshots: dependencies: sparse-bitfield: 3.0.3 - '@napi-rs/wasm-runtime@1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 @@ -3214,7 +3219,7 @@ snapshots: dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 - '@napi-rs/wasm-runtime': 1.1.4(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@rolldown/binding-win32-arm64-msvc@1.0.3': @@ -3360,37 +3365,37 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@supabase/auth-js@2.108.0': + '@supabase/auth-js@2.108.1': dependencies: tslib: 2.8.1 - '@supabase/functions-js@2.108.0': + '@supabase/functions-js@2.108.1': dependencies: tslib: 2.8.1 '@supabase/phoenix@0.4.2': {} - '@supabase/postgrest-js@2.108.0': + '@supabase/postgrest-js@2.108.1': dependencies: tslib: 2.8.1 - '@supabase/realtime-js@2.108.0': + '@supabase/realtime-js@2.108.1': dependencies: '@supabase/phoenix': 0.4.2 tslib: 2.8.1 - '@supabase/storage-js@2.108.0': + '@supabase/storage-js@2.108.1': dependencies: iceberg-js: 0.8.1 tslib: 2.8.1 - '@supabase/supabase-js@2.108.0': + '@supabase/supabase-js@2.108.1': dependencies: - '@supabase/auth-js': 2.108.0 - '@supabase/functions-js': 2.108.0 - '@supabase/postgrest-js': 2.108.0 - '@supabase/realtime-js': 2.108.0 - '@supabase/storage-js': 2.108.0 + '@supabase/auth-js': 2.108.1 + '@supabase/functions-js': 2.108.1 + '@supabase/postgrest-js': 2.108.1 + '@supabase/realtime-js': 2.108.1 + '@supabase/storage-js': 2.108.1 '@tybys/wasm-util@0.10.2': dependencies: @@ -3535,6 +3540,8 @@ snapshots: any-promise@1.3.0: {} + anynum@1.0.0: {} + apparatus@0.0.10: dependencies: sylvester: 0.0.21 @@ -3810,7 +3817,7 @@ snapshots: '@nodable/entities': 2.1.1 fast-xml-builder: 1.2.0 path-expression-matcher: 1.5.0 - strnum: 2.3.0 + strnum: 2.4.0 fdir@6.5.0(picomatch@4.0.4): optionalDependencies: @@ -4074,7 +4081,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.8.3 + semver: 7.8.4 jwa@2.0.1: dependencies: @@ -4183,17 +4190,17 @@ snapshots: crypt: 0.0.2 is-buffer: 1.1.6 - mem0ai@3.0.6(@anthropic-ai/sdk@0.91.1(zod@3.25.76))(@azure/identity@4.13.1)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260609.1)(@google/genai@1.52.0)(@langchain/core@1.1.48(openai@6.26.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0))(@mistralai/mistralai@2.2.1)(@qdrant/js-client-rest@1.18.0(typescript@6.0.3))(@supabase/supabase-js@2.108.0)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.10.0)(cloudflare@4.5.0)(compromise@14.15.1)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.21.0)(redis@5.12.1)(ws@8.21.0): + mem0ai@3.0.7(@anthropic-ai/sdk@0.91.1(zod@3.25.76))(@azure/identity@4.13.1)(@azure/search-documents@12.2.0)(@cloudflare/workers-types@4.20260610.1)(@google/genai@1.52.0)(@langchain/core@1.1.48(openai@6.26.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0))(@mistralai/mistralai@2.2.1)(@qdrant/js-client-rest@1.18.0(typescript@6.0.3))(@supabase/supabase-js@2.108.1)(@types/jest@29.5.14)(@types/pg@8.11.0)(better-sqlite3@12.10.0)(cloudflare@4.5.0)(compromise@14.15.1)(groq-sdk@0.3.0)(natural@8.1.1)(ollama@0.5.18)(pg@8.21.0)(redis@5.12.1)(ws@8.21.0): dependencies: '@anthropic-ai/sdk': 0.91.1(zod@3.25.76) '@azure/identity': 4.13.1 '@azure/search-documents': 12.2.0 - '@cloudflare/workers-types': 4.20260609.1 + '@cloudflare/workers-types': 4.20260610.1 '@google/genai': 1.52.0 '@langchain/core': 1.1.48(openai@6.26.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0) '@mistralai/mistralai': 2.2.1 '@qdrant/js-client-rest': 1.18.0(typescript@6.0.3) - '@supabase/supabase-js': 2.108.0 + '@supabase/supabase-js': 2.108.1 '@types/jest': 29.5.14 '@types/pg': 8.11.0 axios: 1.17.0 @@ -4206,7 +4213,7 @@ snapshots: openai: 4.104.0(ws@8.21.0)(zod@3.25.76) pg: 8.21.0 redis: 5.12.1 - uuid: 9.0.1 + uuid: 14.0.0 zod: 3.25.76 transitivePeerDependencies: - debug @@ -4259,7 +4266,7 @@ snapshots: bson: 7.2.0 mongodb-connection-string-url: 7.0.1 - mongoose@9.6.3: + mongoose@9.7.0: dependencies: kareem: 3.3.0 mongodb: 7.2.0 @@ -4301,7 +4308,7 @@ snapshots: apparatus: 0.0.10 dotenv: 17.4.2 memjs: 1.3.2 - mongoose: 9.6.3 + mongoose: 9.7.0 pg: 8.21.0 redis: 5.12.1 safe-stable-stringify: 2.5.0 @@ -4324,7 +4331,7 @@ snapshots: node-abi@3.92.0: dependencies: - semver: 7.8.3 + semver: 7.8.4 node-domexception@1.0.0: {} @@ -4532,7 +4539,7 @@ snapshots: retry: 0.12.0 signal-exit: 3.0.7 - protobufjs@7.6.2: + protobufjs@7.6.3: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -4648,7 +4655,7 @@ snapshots: safe-stable-stringify@2.5.0: {} - semver@7.8.3: {} + semver@7.8.4: {} shebang-command@2.0.0: dependencies: @@ -4698,7 +4705,9 @@ snapshots: strip-json-comments@2.0.1: {} - strnum@2.3.0: {} + strnum@2.4.0: + dependencies: + anynum: 1.0.0 sucrase@3.35.1: dependencies: @@ -4806,7 +4815,7 @@ snapshots: typebox@1.1.38: {} - typebox@1.2.3: {} + typebox@1.2.8: {} typescript@6.0.3: {} @@ -4826,7 +4835,7 @@ snapshots: uuid@13.0.2: {} - uuid@9.0.1: {} + uuid@14.0.0: {} vite@8.0.16(@types/node@25.9.2)(esbuild@0.27.7)(jiti@2.7.0)(yaml@2.9.0): dependencies: diff --git a/integrations/pi-agent-plugin/pnpm-workspace.yaml b/integrations/pi-agent-plugin/pnpm-workspace.yaml new file mode 100644 index 000000000..2d5592ee1 --- /dev/null +++ b/integrations/pi-agent-plugin/pnpm-workspace.yaml @@ -0,0 +1,5 @@ +packages: + - '.' + +overrides: + "uuid@<11.1.1": ">=11.1.1" diff --git a/pi-agent-plugin/skills/context-loader/SKILL.md b/integrations/pi-agent-plugin/skills/context-loader/SKILL.md similarity index 100% rename from pi-agent-plugin/skills/context-loader/SKILL.md rename to integrations/pi-agent-plugin/skills/context-loader/SKILL.md diff --git a/pi-agent-plugin/skills/dream/SKILL.md b/integrations/pi-agent-plugin/skills/dream/SKILL.md similarity index 100% rename from pi-agent-plugin/skills/dream/SKILL.md rename to integrations/pi-agent-plugin/skills/dream/SKILL.md diff --git a/pi-agent-plugin/skills/forget/SKILL.md b/integrations/pi-agent-plugin/skills/forget/SKILL.md similarity index 100% rename from pi-agent-plugin/skills/forget/SKILL.md rename to integrations/pi-agent-plugin/skills/forget/SKILL.md diff --git a/pi-agent-plugin/skills/pin/SKILL.md b/integrations/pi-agent-plugin/skills/pin/SKILL.md similarity index 100% rename from pi-agent-plugin/skills/pin/SKILL.md rename to integrations/pi-agent-plugin/skills/pin/SKILL.md diff --git a/pi-agent-plugin/skills/remember/SKILL.md b/integrations/pi-agent-plugin/skills/remember/SKILL.md similarity index 100% rename from pi-agent-plugin/skills/remember/SKILL.md rename to integrations/pi-agent-plugin/skills/remember/SKILL.md diff --git a/pi-agent-plugin/skills/search/SKILL.md b/integrations/pi-agent-plugin/skills/search/SKILL.md similarity index 100% rename from pi-agent-plugin/skills/search/SKILL.md rename to integrations/pi-agent-plugin/skills/search/SKILL.md diff --git a/pi-agent-plugin/skills/status/SKILL.md b/integrations/pi-agent-plugin/skills/status/SKILL.md similarity index 100% rename from pi-agent-plugin/skills/status/SKILL.md rename to integrations/pi-agent-plugin/skills/status/SKILL.md diff --git a/pi-agent-plugin/skills/tour/SKILL.md b/integrations/pi-agent-plugin/skills/tour/SKILL.md similarity index 100% rename from pi-agent-plugin/skills/tour/SKILL.md rename to integrations/pi-agent-plugin/skills/tour/SKILL.md diff --git a/pi-agent-plugin/src/capture/index.ts b/integrations/pi-agent-plugin/src/capture/index.ts similarity index 100% rename from pi-agent-plugin/src/capture/index.ts rename to integrations/pi-agent-plugin/src/capture/index.ts diff --git a/pi-agent-plugin/src/commands.test.ts b/integrations/pi-agent-plugin/src/commands.test.ts similarity index 52% rename from pi-agent-plugin/src/commands.test.ts rename to integrations/pi-agent-plugin/src/commands.test.ts index ac51ac964..1ba46c6be 100644 --- a/pi-agent-plugin/src/commands.test.ts +++ b/integrations/pi-agent-plugin/src/commands.test.ts @@ -55,6 +55,7 @@ const defaultConfig: Mem0Config = { autoCapture: false, defaultScope: "project", contextInjection: false, + searchThreshold: 0.3, dream: { enabled: false, auto: false, minHours: 24, minSessions: 5, minMemories: 20 }, }; @@ -67,6 +68,7 @@ describe("registerCommands", () => { beforeEach(() => { pi = makePi(); mem0 = makeMem0(); + defaultConfig.defaultScope = "project"; registerCommands(pi as any, mem0, defaultConfig, () => scopeCtx); }); @@ -90,11 +92,17 @@ describe("registerCommands", () => { expect(mem0.search).not.toHaveBeenCalled(); }); - it("notifies when no memories match", async () => { + it("sends a visible message naming the query when no memories match", async () => { const ctx = makeCtx(); mem0.search.mockResolvedValue({ results: [] }); await pi._invoke("mem0-forget", "old preference", ctx); - expect(ctx.ui.notify).toHaveBeenCalledWith("No matching memories found.", "info"); + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ + customType: "mem0-forget", + content: expect.stringContaining('No matches for "old preference"'), + display: true, + }), + ); }); it("asks for confirmation before deleting a single match", async () => { @@ -111,6 +119,22 @@ describe("registerCommands", () => { expect(mem0.delete).toHaveBeenCalledWith("abc-123"); }); + it("sends a visible confirmation showing what was forgotten", async () => { + const ctx = makeCtx(true); + mem0.search.mockResolvedValue({ results: [{ id: "abc-123", memory: "test mem" }] }); + mem0.delete.mockResolvedValue({ message: "Deleted" }); + + await pi._invoke("mem0-forget", "test", ctx); + + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ + customType: "mem0-forget", + content: expect.stringContaining("Forgotten"), + display: true, + }), + ); + }); + it("does not delete when user cancels confirmation", async () => { const ctx = makeCtx(false); mem0.search.mockResolvedValue({ results: [{ id: "abc-123", memory: "test mem" }] }); @@ -119,7 +143,9 @@ describe("registerCommands", () => { expect(ctx.ui.confirm).toHaveBeenCalled(); expect(mem0.delete).not.toHaveBeenCalled(); - expect(ctx.ui.notify).toHaveBeenCalledWith("Cancelled.", "info"); + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ content: expect.stringContaining("Cancelled"), display: true }), + ); }); it("uses select UI for multiple matches and deletes chosen memory", async () => { @@ -136,13 +162,20 @@ describe("registerCommands", () => { await pi._invoke("mem0-forget", "test", ctx); expect(ctx.ui.select).toHaveBeenCalledWith( - "Which memory should I delete?", + expect.stringContaining("which should I delete"), expect.arrayContaining([ expect.stringContaining("mem one"), expect.stringContaining("mem two"), ]), ); expect(mem0.delete).toHaveBeenCalledWith("id-2"); + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ + customType: "mem0-forget", + content: expect.stringContaining("Forgotten"), + display: true, + }), + ); }); it("does not delete when user cancels select", async () => { @@ -158,7 +191,9 @@ describe("registerCommands", () => { await pi._invoke("mem0-forget", "test", ctx); expect(mem0.delete).not.toHaveBeenCalled(); - expect(ctx.ui.notify).toHaveBeenCalledWith("Cancelled.", "info"); + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ content: expect.stringContaining("Cancelled"), display: true }), + ); }); }); @@ -179,6 +214,22 @@ describe("registerCommands", () => { expect(mem0.delete).not.toHaveBeenCalled(); }); + it("sends a visible confirmation after pinning", async () => { + const ctx = makeCtx(true); + mem0.search.mockResolvedValue({ results: [{ id: "abc-123", memory: "important fact" }] }); + mem0.update.mockResolvedValue([]); + + await pi._invoke("mem0-pin", "important", ctx); + + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ + customType: "mem0-pin", + content: expect.stringContaining("Pinned"), + display: true, + }), + ); + }); + it("does not pin when user cancels", async () => { const ctx = makeCtx(false); mem0.search.mockResolvedValue({ results: [{ id: "abc-123", memory: "fact" }] }); @@ -188,7 +239,7 @@ describe("registerCommands", () => { expect(mem0.update).not.toHaveBeenCalled(); }); - it("skips already-pinned memories", async () => { + it("skips already-pinned memories with a visible message", async () => { const ctx = makeCtx(); mem0.search.mockResolvedValue({ results: [{ id: "abc-123", memory: "[PINNED] fact" }] }); @@ -196,7 +247,9 @@ describe("registerCommands", () => { expect(ctx.ui.confirm).not.toHaveBeenCalled(); expect(mem0.add).not.toHaveBeenCalled(); - expect(ctx.ui.notify).toHaveBeenCalledWith("Already pinned.", "info"); + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ content: expect.stringContaining("Already pinned"), display: true }), + ); }); it("uses select UI for multiple matches and pins chosen memory", async () => { @@ -213,7 +266,7 @@ describe("registerCommands", () => { await pi._invoke("mem0-pin", "fact", ctx); expect(ctx.ui.select).toHaveBeenCalledWith( - "Which memory should I pin?", + expect.stringContaining("which should I pin"), expect.arrayContaining([ expect.stringContaining("fact one"), expect.stringContaining("fact two"), @@ -235,18 +288,23 @@ describe("registerCommands", () => { await pi._invoke("mem0-pin", "fact", ctx); expect(mem0.update).not.toHaveBeenCalled(); - expect(ctx.ui.notify).toHaveBeenCalledWith("Cancelled.", "info"); + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ content: expect.stringContaining("Cancelled"), display: true }), + ); }); }); describe("/mem0-search", () => { - it("always performs semantic search", async () => { + it("performs server-side semantic search with a relevance threshold", async () => { const ctx = makeCtx(); mem0.search.mockResolvedValue({ results: [{ id: "id-1", memory: "result" }] }); await pi._invoke("mem0-search", "my preferences", ctx); - expect(mem0.search).toHaveBeenCalledWith("my preferences", expect.any(Object)); + expect(mem0.search).toHaveBeenCalledWith( + "my preferences", + expect.objectContaining({ threshold: 0.3, topK: 10, rerank: true }), + ); expect(pi.sendMessage).toHaveBeenCalledWith( expect.objectContaining({ customType: "mem0-search" }), ); @@ -263,16 +321,49 @@ describe("registerCommands", () => { expect(mem0.get).not.toHaveBeenCalled(); }); - it("shows empty results message", async () => { + it("shows a no-matches message naming the query", async () => { const ctx = makeCtx(); mem0.search.mockResolvedValue({ results: [] }); await pi._invoke("mem0-search", "nonexistent", ctx); expect(pi.sendMessage).toHaveBeenCalledWith( - expect.objectContaining({ content: "No memories found." }), + expect.objectContaining({ content: expect.stringContaining("No matches") }), ); }); + + it("shows a result count header when there are matches", async () => { + const ctx = makeCtx(); + mem0.search.mockResolvedValue({ + results: [ + { id: "id-1", memory: "one" }, + { id: "id-2", memory: "two" }, + ], + }); + + await pi._invoke("mem0-search", "stuff", ctx); + + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ content: expect.stringContaining("2 matches") }), + ); + }); + + it("shows all results the API returns (relevance gating is server-side)", async () => { + const ctx = makeCtx(); + mem0.search.mockResolvedValue({ + results: [ + { id: "id-1", memory: "first match", score: 0.62 }, + { id: "id-2", memory: "second match", score: 0.31 }, + ], + }); + + await pi._invoke("mem0-search", "stuff", ctx); + + const call = pi.sendMessage.mock.calls.find(([m]: any[]) => m.customType === "mem0-search"); + expect(call?.[0].content).toContain("first match"); + expect(call?.[0].content).toContain("second match"); + expect(call?.[0].content).toContain("2 matches"); + }); }); describe("/mem0-remember", () => { @@ -288,10 +379,132 @@ describe("registerCommands", () => { ); }); + it("shows the stored text in a visible confirmation (infer:false status response)", async () => { + const ctx = makeCtx(); + mem0.add.mockResolvedValue({ message: "Memories stored successfully" }); + + await pi._invoke("mem0-remember", "I prefer dark mode", ctx); + + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ + customType: "mem0-remember", + content: expect.stringContaining("I prefer dark mode"), + display: true, + }), + ); + }); + + it("lists memory objects returned by the API when present", async () => { + const ctx = makeCtx(); + mem0.add.mockResolvedValue([{ id: "m1", memory: "Uses dark mode", event: "ADD" }]); + + await pi._invoke("mem0-remember", "I prefer dark mode", ctx); + + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ + customType: "mem0-remember", + content: expect.stringContaining("Uses dark mode"), + display: true, + }), + ); + }); + it("shows warning when no text provided", async () => { const ctx = makeCtx(); await pi._invoke("mem0-remember", " ", ctx); expect(ctx.ui.notify).toHaveBeenCalledWith("Usage: /mem0-remember ", "warning"); }); }); + + describe("/mem0-scope", () => { + it("sends a visible message showing the current scope when no arg is given", async () => { + const ctx = makeCtx(); + await pi._invoke("mem0-scope", "", ctx); + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ + customType: "mem0-scope", + content: expect.stringContaining("Current scope:"), + display: true, + }), + ); + }); + + it("sends a visible confirmation after changing scope", async () => { + const ctx = makeCtx(); + await pi._invoke("mem0-scope", "global", ctx); + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ + customType: "mem0-scope", + content: expect.stringContaining("Scope changed to global"), + display: true, + }), + ); + }); + + it("warns on an invalid scope", async () => { + const ctx = makeCtx(); + await pi._invoke("mem0-scope", "bogus", ctx); + expect(ctx.ui.notify).toHaveBeenCalledWith( + expect.stringContaining('Invalid scope "bogus"'), + "warning", + ); + }); + }); + + describe("/mem0-tour", () => { + it("shows an empty-state message when there are no memories", async () => { + const ctx = makeCtx(); + mem0.getAll.mockResolvedValue({ results: [] }); + + await pi._invoke("mem0-tour", "", ctx); + + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ + customType: "mem0-tour", + content: expect.stringContaining("No memories"), + display: true, + }), + ); + }); + + it("groups memories by category with a count header", async () => { + const ctx = makeCtx(); + mem0.getAll.mockResolvedValue({ + results: [ + { id: "id-1", memory: "likes tea", categories: ["preferences"] }, + { id: "id-2", memory: "uses vim", categories: ["technical"] }, + ], + }); + + await pi._invoke("mem0-tour", "", ctx); + + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ + customType: "mem0-tour", + content: expect.stringContaining("Memory tour"), + display: true, + }), + ); + }); + }); + + describe("/mem0-dream", () => { + it("feeds the protocol to the agent and shows a clean status line", async () => { + const ctx = makeCtx(); + + await pi._invoke("mem0-dream", "", ctx); + + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ customType: "mem0-dream", display: false }), + expect.objectContaining({ triggerTurn: true }), + ); + expect(pi.sendMessage).toHaveBeenCalledWith( + expect.objectContaining({ + customType: "mem0-dream", + content: expect.stringContaining("Dreaming"), + display: true, + }), + ); + }); + }); }); diff --git a/pi-agent-plugin/src/commands.ts b/integrations/pi-agent-plugin/src/commands.ts similarity index 59% rename from pi-agent-plugin/src/commands.ts rename to integrations/pi-agent-plugin/src/commands.ts index 196ac0ebf..e5b760043 100644 --- a/pi-agent-plugin/src/commands.ts +++ b/integrations/pi-agent-plugin/src/commands.ts @@ -9,6 +9,8 @@ import { acquireDreamLock } from "./dream/index.ts"; import { CONFIG_DIR } from "./config/index.ts"; import { captureCommandEvent } from "./telemetry.ts"; +const SEARCH_TOP_K = 10; + export function registerCommands( pi: ExtensionAPI, mem0: MemoryClient, @@ -16,7 +18,24 @@ export function registerCommands( getScopeCtx: () => ScopeContext, telemetryCtx?: { apiKey?: string }, ): void { - // ── /mem0-remember ────────────────────────────────────────────────── + const sendFeedback = (customType: string, content: string): void => { + pi.sendMessage({ customType, content, display: true }); + }; + + const pluralize = (n: number, one: string, many: string): string => + `${n} ${n === 1 ? one : many}`; + + const searchMemories = async (query: string, scope: Scope) => { + const filters = resolveSearchFilters(scope, getScopeCtx()); + const result = await mem0.search(query, { + filters, + threshold: config.searchThreshold, + topK: SEARCH_TOP_K, + rerank: true, + }); + return result.results ?? []; + }; + pi.registerCommand("mem0-remember", { description: "Store a memory verbatim (no inference)", handler: async (args, ctx) => { @@ -26,19 +45,24 @@ export function registerCommands( return; } - const scopeCtx = getScopeCtx(); - const addParams = resolveAddParams(config.defaultScope, scopeCtx); + const addParams = resolveAddParams(config.defaultScope, getScopeCtx()); const result = await mem0.add( [{ role: "user", content: text }], { ...addParams, customCategories: DEFAULT_CUSTOM_CATEGORIES, infer: false }, ); - const msg = (result as { message?: string }).message ?? "Memory stored."; captureCommandEvent("mem0-remember", {}, telemetryCtx); - ctx.ui.notify(msg, "info"); + + const storedItems = (Array.isArray(result) ? result : []) + .map((m) => (m as { memory?: string }).memory) + .filter((m): m is string => Boolean(m)); + const items = storedItems.length > 0 ? storedItems : [text]; + sendFeedback( + "mem0-remember", + [`**Stored to ${config.defaultScope} memory**`, ...items.map((m) => `- ${m}`)].join("\n"), + ); }, }); - // ── /mem0-forget ──────────────────────────────────────────────────── pi.registerCommand("mem0-forget", { description: "Delete memories matching a natural language query", handler: async (args, ctx) => { @@ -48,49 +72,51 @@ export function registerCommands( return; } - const scopeCtx = getScopeCtx(); - const filters = resolveSearchFilters(config.defaultScope, scopeCtx); - const result = await mem0.search(query, { filters }); - const memories = result.results ?? []; + const memories = await searchMemories(query, config.defaultScope); if (memories.length === 0) { captureCommandEvent("mem0-forget", { result_count: 0 }, telemetryCtx); - ctx.ui.notify("No matching memories found.", "info"); + sendFeedback("mem0-forget", `**No matches for "${query}"** — nothing to forget.`); return; } + const forgotten = (mem: Parameters[0]) => { + captureCommandEvent("mem0-forget", { deleted_count: 1 }, telemetryCtx); + sendFeedback( + "mem0-forget", + [`**Forgotten from ${config.defaultScope} memory**`, `- ${formatMemoryCompact(mem)}`].join("\n"), + ); + }; + if (memories.length === 1) { const target = memories[0]; - const confirmed = await ctx.ui.confirm( - "Delete this memory?", - formatMemoryCompact(target), - ); + const confirmed = await ctx.ui.confirm("Delete this memory?", formatMemoryCompact(target)); if (!confirmed) { - ctx.ui.notify("Cancelled.", "info"); + sendFeedback("mem0-forget", "**Cancelled** — no memories deleted."); return; } await mem0.delete(target.id); - captureCommandEvent("mem0-forget", { deleted_count: 1 }, telemetryCtx); - ctx.ui.notify(`Deleted: ${formatMemoryCompact(target)}`, "info"); + forgotten(target); return; } const labels = memories.map((m) => formatMemoryCompact(m)); - const selected = await ctx.ui.select("Which memory should I delete?", labels); + const selected = await ctx.ui.select( + `Found ${pluralize(memories.length, "match", "matches")} for "${query}" — which should I delete?`, + labels, + ); if (!selected) { - ctx.ui.notify("Cancelled.", "info"); + sendFeedback("mem0-forget", "**Cancelled** — no memories deleted."); return; } const idx = labels.indexOf(selected); if (idx < 0) return; const target = memories[idx]; await mem0.delete(target.id); - captureCommandEvent("mem0-forget", { deleted_count: 1 }, telemetryCtx); - ctx.ui.notify(`Deleted: ${formatMemoryCompact(target)}`, "info"); + forgotten(target); }, }); - // ── /mem0-search ──────────────────────────────────────────────────── pi.registerCommand("mem0-search", { description: "Semantic search across memories", handler: async (args, ctx) => { @@ -100,21 +126,25 @@ export function registerCommands( return; } - const scopeCtx = getScopeCtx(); - const filters = resolveSearchFilters(config.defaultScope, scopeCtx); - const result = await mem0.search(query, { filters }); - const memories = result.results ?? []; - + const memories = await searchMemories(query, config.defaultScope); captureCommandEvent("mem0-search", { result_count: memories.length }, telemetryCtx); - pi.sendMessage({ - customType: "mem0-search", - content: formatMemoryList(memories), - display: true, - }); + + if (memories.length === 0) { + sendFeedback("mem0-search", `**No matches for "${query}"** · ${config.defaultScope} scope`); + return; + } + + sendFeedback( + "mem0-search", + [ + `**${pluralize(memories.length, "match", "matches")} for "${query}"** · ${config.defaultScope} scope`, + "", + formatMemoryList(memories), + ].join("\n"), + ); }, }); - // ── /mem0-tour ────────────────────────────────────────────────────── pi.registerCommand("mem0-tour", { description: "Browse all memories grouped by category", handler: async (args, ctx) => { @@ -125,19 +155,21 @@ export function registerCommands( return; } const scope: Scope = (raw as Scope) || config.defaultScope; - const scopeCtx = getScopeCtx(); - const filters = resolveSearchFilters(scope, scopeCtx); + const filters = resolveSearchFilters(scope, getScopeCtx()); const result = await mem0.getAll({ filters }); const memories = result.results ?? []; if (memories.length === 0) { captureCommandEvent("mem0-tour", { memory_count: 0, scope }, telemetryCtx); - pi.sendMessage({ customType: "mem0-tour", content: "No memories found.", display: true }); + sendFeedback("mem0-tour", `**No memories in ${scope} scope yet** — store one with \`/mem0-remember\`.`); return; } const groups = groupByCategory(memories); - const lines: string[] = [`**Memory Tour** (${memories.length} total, scope: ${scope})`, ""]; + const lines: string[] = [ + `**Memory tour** · ${pluralize(memories.length, "memory", "memories")} · ${scope} scope`, + "", + ]; for (const [category, items] of groups) { lines.push(`### ${category} (${items.length})`); @@ -148,11 +180,10 @@ export function registerCommands( } captureCommandEvent("mem0-tour", { memory_count: memories.length, scope }, telemetryCtx); - pi.sendMessage({ customType: "mem0-tour", content: lines.join("\n"), display: true }); + sendFeedback("mem0-tour", lines.join("\n")); }, }); - // ── /mem0-dream ───────────────────────────────────────────────────── pi.registerCommand("mem0-dream", { description: "Consolidate memories — merge duplicates, prune stale entries, resolve contradictions", handler: async (_args, ctx) => { @@ -162,12 +193,14 @@ export function registerCommands( } captureCommandEvent("mem0-dream", {}, telemetryCtx); - pi.sendMessage({ customType: "mem0-dream", content: DREAM_PROTOCOL, display: true }, { triggerTurn: true }); - ctx.ui.notify("Dream consolidation started.", "info"); + pi.sendMessage({ customType: "mem0-dream", content: DREAM_PROTOCOL, display: false }, { triggerTurn: true }); + sendFeedback( + "mem0-dream", + "**Dreaming** — reviewing your memories to merge duplicates, resolve contradictions, and prune stale entries. I'll report what changed.", + ); }, }); - // ── /mem0-pin ─────────────────────────────────────────────────────── pi.registerCommand("mem0-pin", { description: "Pin a memory to protect it from dream pruning", handler: async (args, ctx) => { @@ -177,42 +210,49 @@ export function registerCommands( return; } - const scopeCtx = getScopeCtx(); - const filters = resolveSearchFilters(config.defaultScope, scopeCtx); - const result = await mem0.search(query, { filters }); - const memories = result.results ?? []; + const memories = await searchMemories(query, config.defaultScope); if (memories.length === 0) { captureCommandEvent("mem0-pin", { result_count: 0 }, telemetryCtx); - ctx.ui.notify("No matching memories found to pin.", "info"); + sendFeedback("mem0-pin", `**No matches for "${query}"** — nothing to pin.`); return; } + const pinned = (mem: Parameters[0]) => { + captureCommandEvent("mem0-pin", { pinned: true }, telemetryCtx); + sendFeedback( + "mem0-pin", + ["**Pinned** — protected from dream pruning", `- ${formatMemoryCompact(mem)}`].join("\n"), + ); + }; + const alreadyPinned = (mem: Parameters[0]) => { + sendFeedback("mem0-pin", ["**Already pinned**", `- ${formatMemoryCompact(mem)}`].join("\n")); + }; + if (memories.length === 1) { const target = memories[0]; const text = target.memory ?? ""; if (text.startsWith("[PINNED]")) { - ctx.ui.notify("Already pinned.", "info"); + alreadyPinned(target); return; } - const confirmed = await ctx.ui.confirm( - "Pin this memory?", - formatMemoryCompact(target), - ); + const confirmed = await ctx.ui.confirm("Pin this memory?", formatMemoryCompact(target)); if (!confirmed) { - ctx.ui.notify("Cancelled.", "info"); + sendFeedback("mem0-pin", "**Cancelled** — nothing was pinned."); return; } await mem0.update(target.id, { text: `[PINNED] ${text}` }); - captureCommandEvent("mem0-pin", { pinned: true }, telemetryCtx); - ctx.ui.notify(`Pinned: ${formatMemoryCompact(target)}`, "info"); + pinned(target); return; } const labels = memories.map((m) => formatMemoryCompact(m)); - const selected = await ctx.ui.select("Which memory should I pin?", labels); + const selected = await ctx.ui.select( + `Found ${pluralize(memories.length, "match", "matches")} for "${query}" — which should I pin?`, + labels, + ); if (!selected) { - ctx.ui.notify("Cancelled.", "info"); + sendFeedback("mem0-pin", "**Cancelled** — nothing was pinned."); return; } const idx = labels.indexOf(selected); @@ -220,16 +260,14 @@ export function registerCommands( const target = memories[idx]; const selectedText = target.memory ?? ""; if (selectedText.startsWith("[PINNED]")) { - ctx.ui.notify("Already pinned.", "info"); + alreadyPinned(target); return; } await mem0.update(target.id, { text: `[PINNED] ${selectedText}` }); - captureCommandEvent("mem0-pin", { pinned: true }, telemetryCtx); - ctx.ui.notify(`Pinned: ${formatMemoryCompact(target)}`, "info"); + pinned(target); }, }); - // ── /mem0-scope ───────────────────────────────────────────────────── pi.registerCommand("mem0-scope", { description: "Change default memory scope for this session (project, session, global)", handler: async (args, ctx) => { @@ -237,7 +275,13 @@ export function registerCommands( const valid: Scope[] = ["project", "session", "global"]; if (!scope) { - ctx.ui.notify(`Current scope: ${config.defaultScope}. Usage: /mem0-scope <${valid.join("|")}>`, "info"); + sendFeedback( + "mem0-scope", + [ + `**Current scope: ${config.defaultScope}**`, + `New memories save to the **${config.defaultScope}** pool. Switch with \`/mem0-scope <${valid.join(" | ")}>\`.`, + ].join("\n"), + ); return; } @@ -248,11 +292,16 @@ export function registerCommands( config.defaultScope = scope as Scope; captureCommandEvent("mem0-scope", { scope }, telemetryCtx); - ctx.ui.notify(`Default scope changed to "${scope}" for this session.`, "info"); + sendFeedback( + "mem0-scope", + [ + `**Scope changed to ${scope}**`, + `New memories now save to the **${scope}** pool for this session.`, + ].join("\n"), + ); }, }); - // ── /mem0-status ──────────────────────────────────────────────────── pi.registerCommand("mem0-status", { description: "Show connection health, identity, project, and memory count", handler: async (_args, _ctx) => { @@ -270,22 +319,21 @@ export function registerCommands( } const lines = [ - "**Mem0 Status**", + "**Mem0 status**", "", `- Connection: ${connected ? "connected" : "disconnected"}`, `- User: ${scopeCtx.userId}`, `- Project: ${scopeCtx.appId}`, `- Session: ${scopeCtx.runId}`, `- Default scope: ${config.defaultScope}`, + `- Search relevance threshold: ${config.searchThreshold}`, `- Project memories: ${count}`, `- Auto-capture: ${config.autoCapture ? "on" : "off"}`, `- Dream: ${config.dream.enabled ? "enabled" : "disabled"}`, ]; captureCommandEvent("mem0-status", { connected, memory_count: count }, telemetryCtx); - pi.sendMessage({ customType: "mem0-status", content: lines.join("\n"), display: true }); + sendFeedback("mem0-status", lines.join("\n")); }, }); - - } diff --git a/pi-agent-plugin/src/config/index.ts b/integrations/pi-agent-plugin/src/config/index.ts similarity index 98% rename from pi-agent-plugin/src/config/index.ts rename to integrations/pi-agent-plugin/src/config/index.ts index d01a412fa..3031cbb11 100644 --- a/pi-agent-plugin/src/config/index.ts +++ b/integrations/pi-agent-plugin/src/config/index.ts @@ -21,6 +21,7 @@ const DEFAULT_CONFIG: Mem0Config = { autoCapture: true, defaultScope: "project", contextInjection: false, + searchThreshold: 0.3, dream: DEFAULT_DREAM, }; diff --git a/pi-agent-plugin/src/dream/index.ts b/integrations/pi-agent-plugin/src/dream/index.ts similarity index 100% rename from pi-agent-plugin/src/dream/index.ts rename to integrations/pi-agent-plugin/src/dream/index.ts diff --git a/pi-agent-plugin/src/dream/prompt.ts b/integrations/pi-agent-plugin/src/dream/prompt.ts similarity index 100% rename from pi-agent-plugin/src/dream/prompt.ts rename to integrations/pi-agent-plugin/src/dream/prompt.ts diff --git a/pi-agent-plugin/src/entry.test.ts b/integrations/pi-agent-plugin/src/entry.test.ts similarity index 100% rename from pi-agent-plugin/src/entry.test.ts rename to integrations/pi-agent-plugin/src/entry.test.ts diff --git a/pi-agent-plugin/src/entry.ts b/integrations/pi-agent-plugin/src/entry.ts similarity index 100% rename from pi-agent-plugin/src/entry.ts rename to integrations/pi-agent-plugin/src/entry.ts diff --git a/pi-agent-plugin/src/index.ts b/integrations/pi-agent-plugin/src/index.ts similarity index 100% rename from pi-agent-plugin/src/index.ts rename to integrations/pi-agent-plugin/src/index.ts diff --git a/pi-agent-plugin/src/memory/formatting.ts b/integrations/pi-agent-plugin/src/memory/formatting.ts similarity index 100% rename from pi-agent-plugin/src/memory/formatting.ts rename to integrations/pi-agent-plugin/src/memory/formatting.ts diff --git a/pi-agent-plugin/src/memory/scoping.test.ts b/integrations/pi-agent-plugin/src/memory/scoping.test.ts similarity index 100% rename from pi-agent-plugin/src/memory/scoping.test.ts rename to integrations/pi-agent-plugin/src/memory/scoping.test.ts diff --git a/pi-agent-plugin/src/memory/scoping.ts b/integrations/pi-agent-plugin/src/memory/scoping.ts similarity index 100% rename from pi-agent-plugin/src/memory/scoping.ts rename to integrations/pi-agent-plugin/src/memory/scoping.ts diff --git a/pi-agent-plugin/src/memory/tools.ts b/integrations/pi-agent-plugin/src/memory/tools.ts similarity index 100% rename from pi-agent-plugin/src/memory/tools.ts rename to integrations/pi-agent-plugin/src/memory/tools.ts diff --git a/pi-agent-plugin/src/prompt.ts b/integrations/pi-agent-plugin/src/prompt.ts similarity index 100% rename from pi-agent-plugin/src/prompt.ts rename to integrations/pi-agent-plugin/src/prompt.ts diff --git a/integrations/pi-agent-plugin/src/telemetry.test.ts b/integrations/pi-agent-plugin/src/telemetry.test.ts new file mode 100644 index 000000000..514dfbf74 --- /dev/null +++ b/integrations/pi-agent-plugin/src/telemetry.test.ts @@ -0,0 +1,56 @@ +import { afterEach, beforeEach, describe, expect, it } from "vitest"; +import { + _getEventQueue, + _resetForTesting, + captureCommandEvent, + captureEvent, + captureToolEvent, +} from "./telemetry.ts"; + +const CTX = { apiKey: "m0-testkey123" }; + +function findEvent(name: string): Record | undefined { + return _getEventQueue().find((e) => (e as Record).event === name) as + | Record + | undefined; +} + +beforeEach(() => { + delete process.env.MEM0_TELEMETRY; + _resetForTesting(); +}); + +afterEach(() => { + delete process.env.MEM0_TELEMETRY; + _resetForTesting(); +}); + +describe("pi-agent telemetry", () => { + it.each(["add", "search", "update", "delete"])( + "captureToolEvent tracks the %s operation as pi.tool.mem0_memory", + (action) => { + captureToolEvent(action, { success: true }, CTX); + const ev = findEvent("pi.tool.mem0_memory"); + expect(ev).toBeDefined(); + const props = ev!.properties as Record; + expect(props.action).toBe(action); + expect(props.success).toBe(true); + expect(props.source).toBe("PI_AGENT_PLUGIN"); + expect(props.$process_person_profile).toBe(false); + }, + ); + + it("captureCommandEvent emits a namespaced pi.command.* event", () => { + captureCommandEvent("mem0-search", { result_count: 3 }, CTX); + const ev = findEvent("pi.command.mem0-search"); + expect(ev).toBeDefined(); + expect((ev!.properties as Record).result_count).toBe(3); + }); + + it("respects the MEM0_TELEMETRY opt-out", () => { + process.env.MEM0_TELEMETRY = "false"; + captureEvent("pi.session.start", {}, CTX); + captureToolEvent("add", {}, CTX); + expect(_getEventQueue()).toHaveLength(0); + }); +}); diff --git a/pi-agent-plugin/src/telemetry.ts b/integrations/pi-agent-plugin/src/telemetry.ts similarity index 100% rename from pi-agent-plugin/src/telemetry.ts rename to integrations/pi-agent-plugin/src/telemetry.ts diff --git a/pi-agent-plugin/src/types.ts b/integrations/pi-agent-plugin/src/types.ts similarity index 98% rename from pi-agent-plugin/src/types.ts rename to integrations/pi-agent-plugin/src/types.ts index 482028c55..e23e80e4c 100644 --- a/pi-agent-plugin/src/types.ts +++ b/integrations/pi-agent-plugin/src/types.ts @@ -14,6 +14,7 @@ export interface Mem0Config { autoCapture: boolean; defaultScope: Scope; contextInjection: boolean; + searchThreshold: number; dream: DreamConfig; } diff --git a/pi-agent-plugin/tests/capture.test.ts b/integrations/pi-agent-plugin/tests/capture.test.ts similarity index 100% rename from pi-agent-plugin/tests/capture.test.ts rename to integrations/pi-agent-plugin/tests/capture.test.ts diff --git a/pi-agent-plugin/tests/config.test.ts b/integrations/pi-agent-plugin/tests/config.test.ts similarity index 100% rename from pi-agent-plugin/tests/config.test.ts rename to integrations/pi-agent-plugin/tests/config.test.ts diff --git a/pi-agent-plugin/tests/dream.test.ts b/integrations/pi-agent-plugin/tests/dream.test.ts similarity index 100% rename from pi-agent-plugin/tests/dream.test.ts rename to integrations/pi-agent-plugin/tests/dream.test.ts diff --git a/pi-agent-plugin/tests/formatting.test.ts b/integrations/pi-agent-plugin/tests/formatting.test.ts similarity index 100% rename from pi-agent-plugin/tests/formatting.test.ts rename to integrations/pi-agent-plugin/tests/formatting.test.ts diff --git a/pi-agent-plugin/tests/scoping.test.ts b/integrations/pi-agent-plugin/tests/scoping.test.ts similarity index 100% rename from pi-agent-plugin/tests/scoping.test.ts rename to integrations/pi-agent-plugin/tests/scoping.test.ts diff --git a/pi-agent-plugin/tests/telemetry.test.ts b/integrations/pi-agent-plugin/tests/telemetry.test.ts similarity index 100% rename from pi-agent-plugin/tests/telemetry.test.ts rename to integrations/pi-agent-plugin/tests/telemetry.test.ts diff --git a/pi-agent-plugin/tests/tools.test.ts b/integrations/pi-agent-plugin/tests/tools.test.ts similarity index 100% rename from pi-agent-plugin/tests/tools.test.ts rename to integrations/pi-agent-plugin/tests/tools.test.ts diff --git a/pi-agent-plugin/tsconfig.json b/integrations/pi-agent-plugin/tsconfig.json similarity index 100% rename from pi-agent-plugin/tsconfig.json rename to integrations/pi-agent-plugin/tsconfig.json diff --git a/pi-agent-plugin/tsup.config.ts b/integrations/pi-agent-plugin/tsup.config.ts similarity index 100% rename from pi-agent-plugin/tsup.config.ts rename to integrations/pi-agent-plugin/tsup.config.ts diff --git a/pi-agent-plugin/vitest.config.ts b/integrations/pi-agent-plugin/vitest.config.ts similarity index 100% rename from pi-agent-plugin/vitest.config.ts rename to integrations/pi-agent-plugin/vitest.config.ts diff --git a/vercel-ai-sdk/.gitattributes b/integrations/vercel-ai-sdk/.gitattributes similarity index 100% rename from vercel-ai-sdk/.gitattributes rename to integrations/vercel-ai-sdk/.gitattributes diff --git a/vercel-ai-sdk/.gitignore b/integrations/vercel-ai-sdk/.gitignore similarity index 100% rename from vercel-ai-sdk/.gitignore rename to integrations/vercel-ai-sdk/.gitignore diff --git a/vercel-ai-sdk/README.md b/integrations/vercel-ai-sdk/README.md similarity index 100% rename from vercel-ai-sdk/README.md rename to integrations/vercel-ai-sdk/README.md diff --git a/vercel-ai-sdk/config/test-config.ts b/integrations/vercel-ai-sdk/config/test-config.ts similarity index 100% rename from vercel-ai-sdk/config/test-config.ts rename to integrations/vercel-ai-sdk/config/test-config.ts diff --git a/vercel-ai-sdk/jest.config.js b/integrations/vercel-ai-sdk/jest.config.js similarity index 100% rename from vercel-ai-sdk/jest.config.js rename to integrations/vercel-ai-sdk/jest.config.js diff --git a/vercel-ai-sdk/nodemon.json b/integrations/vercel-ai-sdk/nodemon.json similarity index 100% rename from vercel-ai-sdk/nodemon.json rename to integrations/vercel-ai-sdk/nodemon.json diff --git a/vercel-ai-sdk/package.json b/integrations/vercel-ai-sdk/package.json similarity index 98% rename from vercel-ai-sdk/package.json rename to integrations/vercel-ai-sdk/package.json index 896c3ff1c..35739fe39 100644 --- a/vercel-ai-sdk/package.json +++ b/integrations/vercel-ai-sdk/package.json @@ -22,7 +22,7 @@ "repository": { "type": "git", "url": "https://github.com/mem0ai/mem0", - "directory": "vercel-ai-sdk" + "directory": "integrations/vercel-ai-sdk" }, "keywords": [ "ai", diff --git a/vercel-ai-sdk/pnpm-lock.yaml b/integrations/vercel-ai-sdk/pnpm-lock.yaml similarity index 100% rename from vercel-ai-sdk/pnpm-lock.yaml rename to integrations/vercel-ai-sdk/pnpm-lock.yaml diff --git a/vercel-ai-sdk/pnpm-workspace.yaml b/integrations/vercel-ai-sdk/pnpm-workspace.yaml similarity index 100% rename from vercel-ai-sdk/pnpm-workspace.yaml rename to integrations/vercel-ai-sdk/pnpm-workspace.yaml diff --git a/vercel-ai-sdk/src/index.ts b/integrations/vercel-ai-sdk/src/index.ts similarity index 100% rename from vercel-ai-sdk/src/index.ts rename to integrations/vercel-ai-sdk/src/index.ts diff --git a/vercel-ai-sdk/src/mem0-facade.ts b/integrations/vercel-ai-sdk/src/mem0-facade.ts similarity index 100% rename from vercel-ai-sdk/src/mem0-facade.ts rename to integrations/vercel-ai-sdk/src/mem0-facade.ts diff --git a/vercel-ai-sdk/src/mem0-generic-language-model.ts b/integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts similarity index 100% rename from vercel-ai-sdk/src/mem0-generic-language-model.ts rename to integrations/vercel-ai-sdk/src/mem0-generic-language-model.ts diff --git a/vercel-ai-sdk/src/mem0-provider-selector.ts b/integrations/vercel-ai-sdk/src/mem0-provider-selector.ts similarity index 100% rename from vercel-ai-sdk/src/mem0-provider-selector.ts rename to integrations/vercel-ai-sdk/src/mem0-provider-selector.ts diff --git a/vercel-ai-sdk/src/mem0-provider.ts b/integrations/vercel-ai-sdk/src/mem0-provider.ts similarity index 100% rename from vercel-ai-sdk/src/mem0-provider.ts rename to integrations/vercel-ai-sdk/src/mem0-provider.ts diff --git a/vercel-ai-sdk/src/mem0-types.ts b/integrations/vercel-ai-sdk/src/mem0-types.ts similarity index 100% rename from vercel-ai-sdk/src/mem0-types.ts rename to integrations/vercel-ai-sdk/src/mem0-types.ts diff --git a/vercel-ai-sdk/src/mem0-utils.ts b/integrations/vercel-ai-sdk/src/mem0-utils.ts similarity index 100% rename from vercel-ai-sdk/src/mem0-utils.ts rename to integrations/vercel-ai-sdk/src/mem0-utils.ts diff --git a/vercel-ai-sdk/src/provider-response-provider.ts b/integrations/vercel-ai-sdk/src/provider-response-provider.ts similarity index 100% rename from vercel-ai-sdk/src/provider-response-provider.ts rename to integrations/vercel-ai-sdk/src/provider-response-provider.ts diff --git a/vercel-ai-sdk/src/stream-utils.ts b/integrations/vercel-ai-sdk/src/stream-utils.ts similarity index 100% rename from vercel-ai-sdk/src/stream-utils.ts rename to integrations/vercel-ai-sdk/src/stream-utils.ts diff --git a/vercel-ai-sdk/teardown.ts b/integrations/vercel-ai-sdk/teardown.ts similarity index 100% rename from vercel-ai-sdk/teardown.ts rename to integrations/vercel-ai-sdk/teardown.ts diff --git a/vercel-ai-sdk/tests/generate-output.test.ts b/integrations/vercel-ai-sdk/tests/generate-output.test.ts similarity index 100% rename from vercel-ai-sdk/tests/generate-output.test.ts rename to integrations/vercel-ai-sdk/tests/generate-output.test.ts diff --git a/vercel-ai-sdk/tests/mem0-provider-tests/mem0-cohere.test.ts b/integrations/vercel-ai-sdk/tests/mem0-provider-tests/mem0-cohere.test.ts similarity index 100% rename from vercel-ai-sdk/tests/mem0-provider-tests/mem0-cohere.test.ts rename to integrations/vercel-ai-sdk/tests/mem0-provider-tests/mem0-cohere.test.ts diff --git a/vercel-ai-sdk/tests/mem0-provider-tests/mem0-google.test.ts b/integrations/vercel-ai-sdk/tests/mem0-provider-tests/mem0-google.test.ts similarity index 100% rename from vercel-ai-sdk/tests/mem0-provider-tests/mem0-google.test.ts rename to integrations/vercel-ai-sdk/tests/mem0-provider-tests/mem0-google.test.ts diff --git a/vercel-ai-sdk/tests/mem0-provider-tests/mem0-groq.test.ts b/integrations/vercel-ai-sdk/tests/mem0-provider-tests/mem0-groq.test.ts similarity index 100% rename from vercel-ai-sdk/tests/mem0-provider-tests/mem0-groq.test.ts rename to integrations/vercel-ai-sdk/tests/mem0-provider-tests/mem0-groq.test.ts diff --git a/vercel-ai-sdk/tests/mem0-provider-tests/mem0-openai-structured-ouput.test.ts b/integrations/vercel-ai-sdk/tests/mem0-provider-tests/mem0-openai-structured-ouput.test.ts similarity index 100% rename from vercel-ai-sdk/tests/mem0-provider-tests/mem0-openai-structured-ouput.test.ts rename to integrations/vercel-ai-sdk/tests/mem0-provider-tests/mem0-openai-structured-ouput.test.ts diff --git a/vercel-ai-sdk/tests/mem0-provider-tests/mem0-openai.test.ts b/integrations/vercel-ai-sdk/tests/mem0-provider-tests/mem0-openai.test.ts similarity index 100% rename from vercel-ai-sdk/tests/mem0-provider-tests/mem0-openai.test.ts rename to integrations/vercel-ai-sdk/tests/mem0-provider-tests/mem0-openai.test.ts diff --git a/vercel-ai-sdk/tests/mem0-provider-tests/mem0_anthropic.test.ts b/integrations/vercel-ai-sdk/tests/mem0-provider-tests/mem0_anthropic.test.ts similarity index 100% rename from vercel-ai-sdk/tests/mem0-provider-tests/mem0_anthropic.test.ts rename to integrations/vercel-ai-sdk/tests/mem0-provider-tests/mem0_anthropic.test.ts diff --git a/vercel-ai-sdk/tests/mem0-toolcalls.test.ts b/integrations/vercel-ai-sdk/tests/mem0-toolcalls.test.ts similarity index 100% rename from vercel-ai-sdk/tests/mem0-toolcalls.test.ts rename to integrations/vercel-ai-sdk/tests/mem0-toolcalls.test.ts diff --git a/vercel-ai-sdk/tests/memory-core.test.ts b/integrations/vercel-ai-sdk/tests/memory-core.test.ts similarity index 100% rename from vercel-ai-sdk/tests/memory-core.test.ts rename to integrations/vercel-ai-sdk/tests/memory-core.test.ts diff --git a/vercel-ai-sdk/tests/text-properties.test.ts b/integrations/vercel-ai-sdk/tests/text-properties.test.ts similarity index 100% rename from vercel-ai-sdk/tests/text-properties.test.ts rename to integrations/vercel-ai-sdk/tests/text-properties.test.ts diff --git a/vercel-ai-sdk/tests/utils-test/anthropic-integration.test.ts b/integrations/vercel-ai-sdk/tests/utils-test/anthropic-integration.test.ts similarity index 100% rename from vercel-ai-sdk/tests/utils-test/anthropic-integration.test.ts rename to integrations/vercel-ai-sdk/tests/utils-test/anthropic-integration.test.ts diff --git a/vercel-ai-sdk/tests/utils-test/cohere-integration.test.ts b/integrations/vercel-ai-sdk/tests/utils-test/cohere-integration.test.ts similarity index 100% rename from vercel-ai-sdk/tests/utils-test/cohere-integration.test.ts rename to integrations/vercel-ai-sdk/tests/utils-test/cohere-integration.test.ts diff --git a/vercel-ai-sdk/tests/utils-test/google-integration.test.ts b/integrations/vercel-ai-sdk/tests/utils-test/google-integration.test.ts similarity index 100% rename from vercel-ai-sdk/tests/utils-test/google-integration.test.ts rename to integrations/vercel-ai-sdk/tests/utils-test/google-integration.test.ts diff --git a/vercel-ai-sdk/tests/utils-test/groq-integration.test.ts b/integrations/vercel-ai-sdk/tests/utils-test/groq-integration.test.ts similarity index 100% rename from vercel-ai-sdk/tests/utils-test/groq-integration.test.ts rename to integrations/vercel-ai-sdk/tests/utils-test/groq-integration.test.ts diff --git a/vercel-ai-sdk/tests/utils-test/openai-integration.test.ts b/integrations/vercel-ai-sdk/tests/utils-test/openai-integration.test.ts similarity index 100% rename from vercel-ai-sdk/tests/utils-test/openai-integration.test.ts rename to integrations/vercel-ai-sdk/tests/utils-test/openai-integration.test.ts diff --git a/vercel-ai-sdk/tests/v3-provider-contract.test.ts b/integrations/vercel-ai-sdk/tests/v3-provider-contract.test.ts similarity index 100% rename from vercel-ai-sdk/tests/v3-provider-contract.test.ts rename to integrations/vercel-ai-sdk/tests/v3-provider-contract.test.ts diff --git a/vercel-ai-sdk/tsconfig.json b/integrations/vercel-ai-sdk/tsconfig.json similarity index 100% rename from vercel-ai-sdk/tsconfig.json rename to integrations/vercel-ai-sdk/tsconfig.json diff --git a/vercel-ai-sdk/tsup.config.ts b/integrations/vercel-ai-sdk/tsup.config.ts similarity index 100% rename from vercel-ai-sdk/tsup.config.ts rename to integrations/vercel-ai-sdk/tsup.config.ts diff --git a/marketplace.json b/marketplace.json index 6e0e240a1..b09335c80 100644 --- a/marketplace.json +++ b/marketplace.json @@ -8,7 +8,7 @@ "name": "mem0", "source": { "source": "local", - "path": "./mem0-plugin" + "path": "./integrations/mem0-plugin" }, "policy": { "installation": "AVAILABLE", diff --git a/mem0-ts/package.json b/mem0-ts/package.json index f8de2b674..8e81b1630 100644 --- a/mem0-ts/package.json +++ b/mem0-ts/package.json @@ -1,6 +1,6 @@ { "name": "mem0ai", - "version": "3.0.7", + "version": "3.0.8", "description": "The Memory Layer For Your AI Apps", "main": "./dist/index.js", "module": "./dist/index.mjs", @@ -102,7 +102,7 @@ "dependencies": { "axios": "^1.16.0", "openai": "^4.93.0", - "uuid": "9.0.1", + "uuid": "^11.1.1", "zod": "^3.24.1" }, "peerDependencies": { @@ -140,12 +140,17 @@ ], "overrides": { "picomatch@<2.3.2": "^2.3.2", + "picomatch@>=4.0.0 <4.0.4": "^4.0.4", "jws@4.0.0": "4.0.1", + "js-yaml@<3.14.2": ">=3.14.2", "langsmith@<0.6.0": "^0.6.0", "minimatch@<3.1.3": "^3.1.3", "minimatch@>=5.0.0 <5.1.8": "^5.1.8", "minimatch@>=9.0.0 <9.0.7": "^9.0.7", "path-to-regexp@>=8.0.0 <8.4.0": "^8.4.0", + "postcss@<8.5.10": ">=8.5.10", + "uuid@<11.1.1": ">=11.1.1", + "ws@>=8.0.0 <8.20.1": ">=8.20.1", "rollup@>=4.0.0 <4.59.0": "^4.59.0", "tar-fs@>=2.0.0 <2.1.4": "^2.1.4", "glob@>=10.2.0 <10.5.0": "^10.5.0", diff --git a/mem0-ts/pnpm-lock.yaml b/mem0-ts/pnpm-lock.yaml index c812ef531..b8b11f6a1 100644 --- a/mem0-ts/pnpm-lock.yaml +++ b/mem0-ts/pnpm-lock.yaml @@ -6,12 +6,17 @@ settings: overrides: picomatch@<2.3.2: ^2.3.2 + picomatch@>=4.0.0 <4.0.4: ^4.0.4 jws@4.0.0: 4.0.1 + js-yaml@<3.14.2: '>=3.14.2' langsmith@<0.6.0: ^0.6.0 minimatch@<3.1.3: ^3.1.3 minimatch@>=5.0.0 <5.1.8: ^5.1.8 minimatch@>=9.0.0 <9.0.7: ^9.0.7 path-to-regexp@>=8.0.0 <8.4.0: ^8.4.0 + postcss@<8.5.10: '>=8.5.10' + uuid@<11.1.1: '>=11.1.1' + ws@>=8.0.0 <8.20.1: '>=8.20.1' rollup@>=4.0.0 <4.59.0: ^4.59.0 tar-fs@>=2.0.0 <2.1.4: ^2.1.4 glob@>=10.2.0 <10.5.0: ^10.5.0 @@ -23,31 +28,31 @@ importers: dependencies: '@anthropic-ai/sdk': specifier: ^0.40.1 - version: 0.40.1(encoding@0.1.13) + version: 0.40.1 '@azure/identity': specifier: ^4.0.0 - version: 4.13.0 + version: 4.13.1 '@azure/search-documents': specifier: ^12.0.0 version: 12.2.0 '@cloudflare/workers-types': specifier: ^4.20250504.0 - version: 4.20250606.0 + version: 4.20260611.1 '@google/genai': specifier: ^1.40.0 version: 1.52.0 '@langchain/core': specifier: ^1.1.47 - version: 1.1.47(openai@4.93.0(encoding@0.1.13)(ws@8.18.1)(zod@3.24.2))(ws@8.18.1) + version: 1.1.48(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0) '@mistralai/mistralai': specifier: ^1.5.2 - version: 1.5.2(zod@3.24.2) + version: 1.15.1 '@qdrant/js-client-rest': specifier: ^1.18.0 version: 1.18.0(typescript@5.5.4) '@supabase/supabase-js': specifier: ^2.49.1 - version: 2.49.1 + version: 2.108.1 '@types/jest': specifier: 29.5.14 version: 29.5.14 @@ -56,87 +61,83 @@ importers: version: 8.11.0 axios: specifier: ^1.16.0 - version: 1.16.1 + version: 1.17.0 better-sqlite3: specifier: ^12.6.2 - version: 12.6.2 + version: 12.10.0 cloudflare: specifier: ^4.2.0 - version: 4.3.0(encoding@0.1.13) + version: 4.5.0 compromise: specifier: ^14.0.0 - version: 14.15.0 + version: 14.15.1 groq-sdk: specifier: 0.3.0 - version: 0.3.0(encoding@0.1.13) + version: 0.3.0 natural: specifier: ^8.0.1 version: 8.1.1 ollama: specifier: ^0.5.14 - version: 0.5.14 + version: 0.5.18 openai: specifier: ^4.93.0 - version: 4.93.0(encoding@0.1.13)(ws@8.18.1)(zod@3.24.2) + version: 4.104.0(ws@8.21.0)(zod@3.25.76) pg: specifier: 8.11.3 version: 8.11.3 redis: specifier: ^4.6.13 - version: 4.7.0 + version: 4.7.1 uuid: - specifier: 9.0.1 - version: 9.0.1 + specifier: ^11.1.1 + version: 11.1.1 zod: specifier: ^3.24.1 - version: 3.24.2 + version: 3.25.76 devDependencies: '@types/better-sqlite3': specifier: ^7.6.13 version: 7.6.13 '@types/node': specifier: ^22.7.6 - version: 22.13.5 + version: 22.19.21 '@types/uuid': specifier: ^9.0.8 version: 9.0.8 dotenv: specifier: ^16.4.5 - version: 16.4.7 + version: 16.6.1 fix-tsup-cjs: specifier: ^1.2.0 version: 1.2.0 jest: specifier: ^29.7.0 - version: 29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)) + version: 29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)) nodemon: specifier: ^3.0.1 - version: 3.1.9 + version: 3.1.14 prettier: specifier: ^3.5.2 - version: 3.5.2 + version: 3.8.4 rimraf: specifier: ^5.0.5 version: 5.0.10 ts-jest: specifier: ^29.2.6 - version: 29.2.6(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.9))(esbuild@0.25.1)(jest@29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)))(typescript@5.5.4) + version: 29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(esbuild@0.27.7)(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)))(typescript@5.5.4) ts-node: specifier: ^10.9.2 - version: 10.9.2(@types/node@22.13.5)(typescript@5.5.4) + version: 10.9.2(@types/node@22.19.21)(typescript@5.5.4) tsup: specifier: ^8.3.0 - version: 8.4.0(postcss@8.5.3)(typescript@5.5.4) + version: 8.5.1(typescript@5.5.4) typescript: specifier: 5.5.4 version: 5.5.4 packages: - '@ampproject/remapping@2.3.0': - resolution: {integrity: sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==} - engines: {node: '>=6.0.0'} - '@anthropic-ai/sdk@0.40.1': resolution: {integrity: sha512-DJMWm8lTEM9Lk/MSFL+V+ugF7jKOn0M2Ujvb5fN8r2nY14aHbGPZ1k6sgjL+tpJ3VuOGJNG+4R83jEpOuYPv8w==} @@ -148,20 +149,23 @@ packages: resolution: {integrity: sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==} engines: {node: '>=20.0.0'} - '@azure/core-client@1.10.1': - resolution: {integrity: sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==} + '@azure/core-client@1.10.2': + resolution: {integrity: sha512-1D2LpsU7y9xrqKjdIbsB7PlrRePw0xsVV8p+AKTlzITrWmscajryfJCdDJB/oGwvDI5HmRo04eMMADB67uwAwQ==} engines: {node: '>=20.0.0'} - '@azure/core-http-compat@2.3.1': - resolution: {integrity: sha512-az9BkXND3/d5VgdRRQVkiJb2gOmDU8Qcq4GvjtBmDICNiQ9udFmDk4ZpSB5Qq1OmtDJGlQAfBaS4palFsazQ5g==} + '@azure/core-http-compat@2.4.0': + resolution: {integrity: sha512-f1P96IB399YiN2ARYHP7EpZi3Bf3wH4SN2lGzrw7JVwm7bbsVYtf2iKSBwTywD2P62NOPZGHFSZi+6jjb75JuA==} engines: {node: '>=20.0.0'} + peerDependencies: + '@azure/core-client': ^1.10.0 + '@azure/core-rest-pipeline': ^1.22.0 '@azure/core-paging@1.6.2': resolution: {integrity: sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==} engines: {node: '>=18.0.0'} - '@azure/core-rest-pipeline@1.22.2': - resolution: {integrity: sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==} + '@azure/core-rest-pipeline@1.24.0': + resolution: {integrity: sha512-PpLsoDQ3AMmKZ0VU+0GrmqMxgp/sExjlVm4R+nLWngeoEGAzOIPVifaxKGU5gMv+nWELUoHfvrolWD+ZS/nFJg==} engines: {node: '>=20.0.0'} '@azure/core-tracing@1.3.1': @@ -172,82 +176,86 @@ packages: resolution: {integrity: sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==} engines: {node: '>=20.0.0'} - '@azure/identity@4.13.0': - resolution: {integrity: sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==} + '@azure/identity@4.13.1': + resolution: {integrity: sha512-5C/2WD5Vb1lHnZS16dNQRPMjN6oV/Upba+C9nBIs15PmOi6A3ZGs4Lr2u60zw4S04gi+u3cEXiqTVP7M4Pz3kw==} engines: {node: '>=20.0.0'} '@azure/logger@1.3.0': resolution: {integrity: sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==} engines: {node: '>=20.0.0'} - '@azure/msal-browser@4.27.0': - resolution: {integrity: sha512-bZ8Pta6YAbdd0o0PEaL1/geBsPrLEnyY/RDWqvF1PP9RUH8EMLvUMGoZFYS6jSlUan6KZ9IMTLCnwpWWpQRK/w==} + '@azure/msal-browser@5.13.0': + resolution: {integrity: sha512-Ea23x0U8XNFY+qJ9T44zO2BbY+AHdb+WdjmYnx36OhJ/KO+PGU5pmsNHf1DCElYX+6wyVRJz1HFeCPC/cHbRug==} engines: {node: '>=0.8.0'} - '@azure/msal-common@15.13.3': - resolution: {integrity: sha512-shSDU7Ioecya+Aob5xliW9IGq1Ui8y4EVSdWGyI1Gbm4Vg61WpP95LuzcY214/wEjSn6w4PZYD4/iVldErHayQ==} + '@azure/msal-common@16.8.0': + resolution: {integrity: sha512-5S4RHOcInL2Nu2U217tDZbWGI6StMfcWCrA7TWvWdJmXQ+cYrrIqr84AsN62fGh2MDBysiBJPt6CfWceJfloEA==} engines: {node: '>=0.8.0'} - '@azure/msal-node@3.8.4': - resolution: {integrity: sha512-lvuAwsDpPDE/jSuVQOBMpLbXuVuLsPNRwWCyK3/6bPlBk0fGWegqoZ0qjZclMWyQ2JNvIY3vHY7hoFmFmFQcOw==} - engines: {node: '>=16'} + '@azure/msal-node@5.2.4': + resolution: {integrity: sha512-rpBUg9dA8UpC2WiFt3KeDKVQmmmVrfxdRnW+F1ebgou/jX/0tAvYuonaq5RUo8OaqzOrj4x/HaI8DmY56RXZ2Q==} + engines: {node: '>=20'} '@azure/search-documents@12.2.0': resolution: {integrity: sha512-4+Qw+qaGqnkdUCq/vEFzk/bkROogTvdbPb1fmI8poxNfDDN1q2WHxBmhI7CYwesrBj1yXC4i5E0aISBxZqZi0g==} engines: {node: '>=20.0.0'} - '@babel/code-frame@7.26.2': - resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} + '@babel/code-frame@7.29.7': + resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} - '@babel/compat-data@7.26.8': - resolution: {integrity: sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==} + '@babel/compat-data@7.29.7': + resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} - '@babel/core@7.26.9': - resolution: {integrity: sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==} + '@babel/core@7.29.7': + resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} - '@babel/generator@7.26.9': - resolution: {integrity: sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==} + '@babel/generator@7.29.7': + resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} - '@babel/helper-compilation-targets@7.26.5': - resolution: {integrity: sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==} + '@babel/helper-compilation-targets@7.29.7': + resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} - '@babel/helper-module-imports@7.25.9': - resolution: {integrity: sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==} + '@babel/helper-globals@7.29.7': + resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} - '@babel/helper-module-transforms@7.26.0': - resolution: {integrity: sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==} + '@babel/helper-module-imports@7.29.7': + resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} + engines: {node: '>=6.9.0'} + + '@babel/helper-module-transforms@7.29.7': + resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 - '@babel/helper-plugin-utils@7.26.5': - resolution: {integrity: sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==} + '@babel/helper-plugin-utils@7.29.7': + resolution: {integrity: sha512-G7sHYigPY17oO5SYWnfD/0MTBwVR781S/JI643e/JhUYgVgWE/61SoW3NH9KWUKyKq5LVh3npif99Wkt6j86Jw==} engines: {node: '>=6.9.0'} - '@babel/helper-string-parser@7.25.9': - resolution: {integrity: sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==} + '@babel/helper-string-parser@7.29.7': + resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-identifier@7.25.9': - resolution: {integrity: sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==} + '@babel/helper-validator-identifier@7.29.7': + resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} - '@babel/helper-validator-option@7.25.9': - resolution: {integrity: sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==} + '@babel/helper-validator-option@7.29.7': + resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} - '@babel/helpers@7.26.9': - resolution: {integrity: sha512-Mz/4+y8udxBKdmzt/UjPACs4G3j5SshJJEFFKxlCGPydG4JAHXxjWjAwjd09tf6oINvl1VfMJo+nB7H2YKQ0dA==} + '@babel/helpers@7.29.7': + resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} - '@babel/parser@7.26.9': - resolution: {integrity: sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==} + '@babel/parser@7.29.7': + resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true @@ -272,8 +280,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-import-attributes@7.26.0': - resolution: {integrity: sha512-e2dttdsJ1ZTpi3B9UYGLw41hifAubg19AtCu/2I/F1QNVclOBr1dYpTdmdyZ84Xiz43BS/tCUkMAZNLv12Pi+A==} + '@babel/plugin-syntax-import-attributes@7.29.7': + resolution: {integrity: sha512-zGYcYfq/WmZ4V+kBIXQon9dSSc8ircGZqw9ZaNhhGj9nZkeBu1jHLBDQqYYi5WA9uawvA2sIMbry2nCFhf5Djg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -288,8 +296,8 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-jsx@7.25.9': - resolution: {integrity: sha512-ld6oezHQMZsZfp6pWtbjaNDF2tiiCYYDqQszHt5VV437lewP9aSi2Of99CK0D0XB21k7FLgnLcmQKyKzynfeAA==} + '@babel/plugin-syntax-jsx@7.29.7': + resolution: {integrity: sha512-TSu8+mHCoEaaCDEZ0I3+6mvTBYR4PCxQwf2z9/r5Tbztv6NaLR3B9thGTTxX2WGuGHJqRiAbKPeGTJ5XWXVg6A==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 @@ -336,22 +344,22 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/plugin-syntax-typescript@7.25.9': - resolution: {integrity: sha512-hjMgRy5hb8uJJjUcdWunWVcoi9bGpJp8p5Ol1229PoN6aytsLwNMgmdftO23wnCLMfVmTwZDWMPNq/D1SY60JQ==} + '@babel/plugin-syntax-typescript@7.29.7': + resolution: {integrity: sha512-ngr+82Sh0xMz25TPCZi+nC2iTzjfCdWS2ONXTp/PtSCHCgaCNBpdMqgvJ2ccdLlClVZ7sisIgB914j/JFe+RZA==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0-0 - '@babel/template@7.26.9': - resolution: {integrity: sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==} + '@babel/template@7.29.7': + resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} - '@babel/traverse@7.26.9': - resolution: {integrity: sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==} + '@babel/traverse@7.29.7': + resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} - '@babel/types@7.26.9': - resolution: {integrity: sha512-Y3IR1cRnOxOCDvMmNiym7XpXQ93iGDDPHx+Zj+NM+rg0fBaShfQLkg+hKPaZCEvg5N/LeCo4+Rj/i3FuJsIQaw==} + '@babel/types@7.29.7': + resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} '@bcoe/v8-coverage@0.2.3': @@ -360,159 +368,165 @@ packages: '@cfworker/json-schema@4.1.1': resolution: {integrity: sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og==} - '@cloudflare/workers-types@4.20250606.0': - resolution: {integrity: sha512-9T/Y/Mxe57UVzqgfjJKheiMplnStj/3CmCHlgoZNLU8JW2waRbXvpY3EEeliiYAJfeHZTjeAaKO2pCabxAoyCw==} + '@cloudflare/workers-types@4.20260611.1': + resolution: {integrity: sha512-DLiz8Ol1OIWLigJ+dGvuQ5Nm66D/CHNPasl8YnPiz6fGo10ggYSIVuEDMlFk6oho+piAHstNmZMl088w8xqW6g==} '@cspotcode/source-map-support@0.8.1': resolution: {integrity: sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==} engines: {node: '>=12'} - '@esbuild/aix-ppc64@0.25.1': - resolution: {integrity: sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==} + '@esbuild/aix-ppc64@0.27.7': + resolution: {integrity: sha512-EKX3Qwmhz1eMdEJokhALr0YiD0lhQNwDqkPYyPhiSwKrh7/4KRjQc04sZ8db+5DVVnZ1LmbNDI1uAMPEUBnQPg==} engines: {node: '>=18'} cpu: [ppc64] os: [aix] - '@esbuild/android-arm64@0.25.1': - resolution: {integrity: sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==} + '@esbuild/android-arm64@0.27.7': + resolution: {integrity: sha512-62dPZHpIXzvChfvfLJow3q5dDtiNMkwiRzPylSCfriLvZeq0a1bWChrGx/BbUbPwOrsWKMn8idSllklzBy+dgQ==} engines: {node: '>=18'} cpu: [arm64] os: [android] - '@esbuild/android-arm@0.25.1': - resolution: {integrity: sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==} + '@esbuild/android-arm@0.27.7': + resolution: {integrity: sha512-jbPXvB4Yj2yBV7HUfE2KHe4GJX51QplCN1pGbYjvsyCZbQmies29EoJbkEc+vYuU5o45AfQn37vZlyXy4YJ8RQ==} engines: {node: '>=18'} cpu: [arm] os: [android] - '@esbuild/android-x64@0.25.1': - resolution: {integrity: sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==} + '@esbuild/android-x64@0.27.7': + resolution: {integrity: sha512-x5VpMODneVDb70PYV2VQOmIUUiBtY3D3mPBG8NxVk5CogneYhkR7MmM3yR/uMdITLrC1ml/NV1rj4bMJuy9MCg==} engines: {node: '>=18'} cpu: [x64] os: [android] - '@esbuild/darwin-arm64@0.25.1': - resolution: {integrity: sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==} + '@esbuild/darwin-arm64@0.27.7': + resolution: {integrity: sha512-5lckdqeuBPlKUwvoCXIgI2D9/ABmPq3Rdp7IfL70393YgaASt7tbju3Ac+ePVi3KDH6N2RqePfHnXkaDtY9fkw==} engines: {node: '>=18'} cpu: [arm64] os: [darwin] - '@esbuild/darwin-x64@0.25.1': - resolution: {integrity: sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==} + '@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.25.1': - resolution: {integrity: sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==} + '@esbuild/freebsd-arm64@0.27.7': + resolution: {integrity: sha512-B48PqeCsEgOtzME2GbNM2roU29AMTuOIN91dsMO30t+Ydis3z/3Ngoj5hhnsOSSwNzS+6JppqWsuhTp6E82l2w==} engines: {node: '>=18'} cpu: [arm64] os: [freebsd] - '@esbuild/freebsd-x64@0.25.1': - resolution: {integrity: sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==} + '@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.25.1': - resolution: {integrity: sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==} + '@esbuild/linux-arm64@0.27.7': + resolution: {integrity: sha512-RZPHBoxXuNnPQO9rvjh5jdkRmVizktkT7TCDkDmQ0W2SwHInKCAV95GRuvdSvA7w4VMwfCjUiPwDi0ZO6Nfe9A==} engines: {node: '>=18'} cpu: [arm64] os: [linux] - '@esbuild/linux-arm@0.25.1': - resolution: {integrity: sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==} + '@esbuild/linux-arm@0.27.7': + resolution: {integrity: sha512-RkT/YXYBTSULo3+af8Ib0ykH8u2MBh57o7q/DAs3lTJlyVQkgQvlrPTnjIzzRPQyavxtPtfg0EopvDyIt0j1rA==} engines: {node: '>=18'} cpu: [arm] os: [linux] - '@esbuild/linux-ia32@0.25.1': - resolution: {integrity: sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==} + '@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.25.1': - resolution: {integrity: sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==} + '@esbuild/linux-loong64@0.27.7': + resolution: {integrity: sha512-a4POruNM2oWsD4WKvBSEKGIiWQF8fZOAsycHOt6JBpZ+JN2n2JH9WAv56SOyu9X5IqAjqSIPTaJkqN8F7XOQ5Q==} engines: {node: '>=18'} cpu: [loong64] os: [linux] - '@esbuild/linux-mips64el@0.25.1': - resolution: {integrity: sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==} + '@esbuild/linux-mips64el@0.27.7': + resolution: {integrity: sha512-KabT5I6StirGfIz0FMgl1I+R1H73Gp0ofL9A3nG3i/cYFJzKHhouBV5VWK1CSgKvVaG4q1RNpCTR2LuTVB3fIw==} engines: {node: '>=18'} cpu: [mips64el] os: [linux] - '@esbuild/linux-ppc64@0.25.1': - resolution: {integrity: sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==} + '@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.25.1': - resolution: {integrity: sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==} + '@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.25.1': - resolution: {integrity: sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==} + '@esbuild/linux-s390x@0.27.7': + resolution: {integrity: sha512-2k8go8Ycu1Kb46vEelhu1vqEP+UeRVj2zY1pSuPdgvbd5ykAw82Lrro28vXUrRmzEsUV0NzCf54yARIK8r0fdw==} engines: {node: '>=18'} cpu: [s390x] os: [linux] - '@esbuild/linux-x64@0.25.1': - resolution: {integrity: sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==} + '@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.25.1': - resolution: {integrity: sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==} + '@esbuild/netbsd-arm64@0.27.7': + resolution: {integrity: sha512-b6pqtrQdigZBwZxAn1UpazEisvwaIDvdbMbmrly7cDTMFnw/+3lVxxCTGOrkPVnsYIosJJXAsILG9XcQS+Yu6w==} engines: {node: '>=18'} cpu: [arm64] os: [netbsd] - '@esbuild/netbsd-x64@0.25.1': - resolution: {integrity: sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==} + '@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.25.1': - resolution: {integrity: sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==} + '@esbuild/openbsd-arm64@0.27.7': + resolution: {integrity: sha512-AFuojMQTxAz75Fo8idVcqoQWEHIXFRbOc1TrVcFSgCZtQfSdc1RXgB3tjOn/krRHENUB4j00bfGjyl2mJrU37A==} engines: {node: '>=18'} cpu: [arm64] os: [openbsd] - '@esbuild/openbsd-x64@0.25.1': - resolution: {integrity: sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==} + '@esbuild/openbsd-x64@0.27.7': + resolution: {integrity: sha512-+A1NJmfM8WNDv5CLVQYJ5PshuRm/4cI6WMZRg1by1GwPIQPCTs1GLEUHwiiQGT5zDdyLiRM/l1G0Pv54gvtKIg==} engines: {node: '>=18'} cpu: [x64] os: [openbsd] - '@esbuild/sunos-x64@0.25.1': - resolution: {integrity: sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==} + '@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.25.1': - resolution: {integrity: sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==} + '@esbuild/win32-arm64@0.27.7': + resolution: {integrity: sha512-7yRhbHvPqSpRUV7Q20VuDwbjW5kIMwTHpptuUzV+AA46kiPze5Z7qgt6CLCK3pWFrHeNfDd1VKgyP4O+ng17CA==} engines: {node: '>=18'} cpu: [arm64] os: [win32] - '@esbuild/win32-ia32@0.25.1': - resolution: {integrity: sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==} + '@esbuild/win32-ia32@0.27.7': + resolution: {integrity: sha512-SmwKXe6VHIyZYbBLJrhOoCJRB/Z1tckzmgTLfFYOfpMAx63BJEaL9ExI8x7v0oAO3Zh6D/Oi1gVxEYr5oUCFhw==} engines: {node: '>=18'} cpu: [ia32] os: [win32] - '@esbuild/win32-x64@0.25.1': - resolution: {integrity: sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==} + '@esbuild/win32-x64@0.27.7': + resolution: {integrity: sha512-56hiAJPhwQ1R4i+21FVF7V8kSD5zZTdHcVuRFMW0hn753vVfQN8xlx4uOPT4xoGH0Z/oVATuR82AiqSTDIpaHg==} engines: {node: '>=18'} cpu: [x64] os: [win32] @@ -534,8 +548,8 @@ packages: resolution: {integrity: sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==} engines: {node: '>=8'} - '@istanbuljs/schema@0.1.3': - resolution: {integrity: sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==} + '@istanbuljs/schema@0.1.6': + resolution: {integrity: sha512-+Sg6GCR/wy1oSmQDFq4LQDAhm3ETKnorxN+y5nbLULOR3P0c14f2Wurzj3/xqPXtasLFfHd5iRFQ7AJt4KH2cw==} engines: {node: '>=8'} '@jest/console@29.7.0': @@ -604,38 +618,34 @@ packages: resolution: {integrity: sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} + '@jridgewell/gen-mapping@0.3.13': + resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} + + '@jridgewell/remapping@2.3.5': + resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} - '@jridgewell/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@jridgewell/trace-mapping@0.3.9': resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} - '@langchain/core@1.1.47': - resolution: {integrity: sha512-+fiPu6ZFnJMrZyKeM77OIVPoMPAY6OKWacnPlojHtXTbMMzb2cEOKAJV0U07cDl86NHSCIYYa0i4CyKZzXbHQQ==} + '@langchain/core@1.1.48': + resolution: {integrity: sha512-fQU6Guyb1pwc2fEplmA8FPbKfOMAofjnyJzExevro0FxEiuGHE18Ov/ZHmT9trWCDTZRI9eW1VIc6aChxV8pAQ==} engines: {node: '>=20'} - '@mistralai/mistralai@1.5.2': - resolution: {integrity: sha512-mBTIDQmuAX9RowMYteZFHJIYlEwDcHzzaxgXzrFtlvH9CkKXK7R1VnZ1sZSe+uLMg0dIXUVdPRUh1SwyFeSqXw==} - peerDependencies: - zod: '>= 3' + '@mistralai/mistralai@1.15.1': + resolution: {integrity: sha512-fb995eiz3r0KsBGtRjFV+/iLbX+UpfalxpF+YitT3R6ukrPD4PN+FGwwmYcRFhNAzVzDUtTVxQYnjQWEnwV5nw==} - '@mongodb-js/saslprep@1.4.6': - resolution: {integrity: sha512-y+x3H1xBZd38n10NZF/rEBlvDOOMQ6LKUTHqr8R9VkJ+mmQOYtJFxIlkkK8fZrtOiL6VixbOBWMbZGBdal3Z1g==} + '@mongodb-js/saslprep@1.4.11': + resolution: {integrity: sha512-o9rAHc0IpIjuPSxRutWpE1F62x7n+4mVS4rCNHkzhIUMQcc18bb6xEq5wd2NdN0WjepIyXIppRshYI2kQDOZVA==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} @@ -698,24 +708,27 @@ packages: peerDependencies: '@redis/client': ^1.0.0 - '@redis/bloom@5.11.0': - resolution: {integrity: sha512-KYiVilAhAFN3057afUb/tfYJpsEyTkQB+tQcn5gVVA7DgcNOAj8lLxe4j8ov8BF6I9C1Fe/kwlbuAICcTMX8Lw==} - engines: {node: '>= 18'} + '@redis/bloom@5.12.1': + resolution: {integrity: sha512-PUUfv+ms7jgPSBVoo/DN4AkPHj4D5TZSd6SbJX7egzBplkYUcKmHRE8RKia7UtZ8bSQbLguLvxVO+asKtQfZWA==} + engines: {node: '>= 18.19.0'} peerDependencies: - '@redis/client': ^5.11.0 + '@redis/client': ^5.12.1 - '@redis/client@1.6.0': - resolution: {integrity: sha512-aR0uffYI700OEEH4gYnitAnv3vzVGXCFvYfdpu/CJKvk4pHfLPEy/JSZyrpQ+15WhXe1yJRXLtfQ84s4mEXnPg==} + '@redis/client@1.6.1': + resolution: {integrity: sha512-/KCsg3xSlR+nCK8/8ZYSknYxvXHwubJrU82F3Lm1Fp6789VQ0/3RJKfsmRXjqfaTA++23CvC3hqmqe/2GEt6Kw==} engines: {node: '>=14'} - '@redis/client@5.11.0': - resolution: {integrity: sha512-GHoprlNQD51Xq2Ztd94HHV94MdFZQ3CVrpA04Fz8MVoHM0B7SlbmPEVIjwTbcv58z8QyjnrOuikS0rWF03k5dQ==} - engines: {node: '>= 18'} + '@redis/client@5.12.1': + resolution: {integrity: sha512-7aPGWeqA3uFm43o19umzdl16CEjK/JQGtSXVPevplTaOU3VJA/rseBC1QvYUz9lLDIMBimc4SW/zrW4S89BaCA==} + engines: {node: '>= 18.19.0'} peerDependencies: '@node-rs/xxhash': ^1.1.0 + '@opentelemetry/api': '>=1 <2' peerDependenciesMeta: '@node-rs/xxhash': optional: true + '@opentelemetry/api': + optional: true '@redis/graph@1.1.1': resolution: {integrity: sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==} @@ -727,161 +740,161 @@ packages: peerDependencies: '@redis/client': ^1.0.0 - '@redis/json@5.11.0': - resolution: {integrity: sha512-1iAy9kAtcD0quB21RbPTbUqqy+T2Uu2JxucwE+B4A+VaDbIRvpZR6DMqV8Iqaws2YxJYB3GC5JVNzPYio2ErUg==} - engines: {node: '>= 18'} + '@redis/json@5.12.1': + resolution: {integrity: sha512-eOze75esLve4vfqDel7aMX08CNaiLLQS2fV8mpRN9NxPe1rVR4vQyYiW/OgtGUysF6QOr9ANhfxABKNOJfXdKg==} + engines: {node: '>= 18.19.0'} peerDependencies: - '@redis/client': ^5.11.0 + '@redis/client': ^5.12.1 '@redis/search@1.2.0': resolution: {integrity: sha512-tYoDBbtqOVigEDMAcTGsRlMycIIjwMCgD8eR2t0NANeQmgK/lvxNAvYyb6bZDD4frHRhIHkJu2TBRvB0ERkOmw==} peerDependencies: '@redis/client': ^1.0.0 - '@redis/search@5.11.0': - resolution: {integrity: sha512-g1l7f3Rnyk/xI99oGHIgWHSKFl45Re5YTIcO8j/JE8olz389yUFyz2+A6nqVy/Zi031VgPDWscbbgOk8hlhZ3g==} - engines: {node: '>= 18'} + '@redis/search@5.12.1': + resolution: {integrity: sha512-ItlxbxC9cKI6IU1TLWoczwJCRb6TdmkEpWv05UrPawqaAnWGRu3rcIqsc5vN483T2fSociuyV1UkWIL5I4//2w==} + engines: {node: '>= 18.19.0'} peerDependencies: - '@redis/client': ^5.11.0 + '@redis/client': ^5.12.1 '@redis/time-series@1.1.0': resolution: {integrity: sha512-c1Q99M5ljsIuc4YdaCwfUEXsofakb9c8+Zse2qxTadu8TalLXuAESzLvFAvNVbkmSlvlzIQOLpBCmWI9wTOt+g==} peerDependencies: '@redis/client': ^1.0.0 - '@redis/time-series@5.11.0': - resolution: {integrity: sha512-TWFeOcU4xkj0DkndnOyhtxvX1KWD+78UHT3XX3x3XRBUGWeQrKo3jqzDsZwxbggUgf9yLJr/akFHXru66X5UQA==} - engines: {node: '>= 18'} + '@redis/time-series@5.12.1': + resolution: {integrity: sha512-c6JL6E3EcZJuNqKFz+KM+l9l5mpcQiKvTwgA3blt5glWJ8hjDk0yeHN3beE/MpqYIQ8UEX44ItQzgkE/gCBELQ==} + engines: {node: '>= 18.19.0'} peerDependencies: - '@redis/client': ^5.11.0 + '@redis/client': ^5.12.1 - '@rollup/rollup-android-arm-eabi@4.60.4': - resolution: {integrity: sha512-F5QXMSiFebS9hKZj02XhWLLnRpJ3B3AROP0tWbFBSj+6kCbg5m9j5JoHKd4mmSVy5mS/IMQloYgYxCuJC0fxEQ==} + '@rollup/rollup-android-arm-eabi@4.61.1': + resolution: {integrity: sha512-JnBB8MdXj45cajvTuO5FmPlvFVJRQgvrz1uSEl3NwqFnReAPGwb8EanbGi4z2nRaqLzjJSv5/JmycoTKlRZxHA==} cpu: [arm] os: [android] - '@rollup/rollup-android-arm64@4.60.4': - resolution: {integrity: sha512-GxxTKApUpzRhof7poWvCJHRF51C67u1R7D6DiluBE8wKU1u5GWE8t+v81JvJYtbawoBFX1hLv5Ei4eVjkWokaw==} + '@rollup/rollup-android-arm64@4.61.1': + resolution: {integrity: sha512-Jx2g7iSjw4AOT0HDPHM9RV3GNjRXwybWtSFZiZAYUTjUwjVrYIwq3kBf+LnhqJlzXFAqTAh2F7IGI+O568exPw==} cpu: [arm64] os: [android] - '@rollup/rollup-darwin-arm64@4.60.4': - resolution: {integrity: sha512-tua0TaJxMOB1R0V0RS1jFZ/RpURFDJIOR2A6jWwQeawuFyS4gBW+rntLRaQd0EQ4bd6Vp44Z2rXW+YYDBsj6IA==} + '@rollup/rollup-darwin-arm64@4.61.1': + resolution: {integrity: sha512-0F1L/Z3Eqv8mT2n3dCpeO8GcTvHvVqkP5/t6DMsn0KzhYVcg+s7Ncl5DS8qjKYEeio6Az0Gt6nyBORay5qIlCA==} cpu: [arm64] os: [darwin] - '@rollup/rollup-darwin-x64@4.60.4': - resolution: {integrity: sha512-CSKq7MsP+5PFIcydhAiR1K0UhEI1A2jWXVKHPCBZ151yOutENwvnPocgVHkivu2kviURtCEB6zUQw0vs8RrhMg==} + '@rollup/rollup-darwin-x64@4.61.1': + resolution: {integrity: sha512-qLttcH871ujY4YcVfUSShhOw+CsoTatYz8gRbHO7Bb92QH059/P0y5do1KMs41fY0BpD2x4AJH/gID0zFiqVKQ==} cpu: [x64] os: [darwin] - '@rollup/rollup-freebsd-arm64@4.60.4': - resolution: {integrity: sha512-+O8OkVdyvXMtJEciu2wS/pzm1IxntEEQx3z5TAVy4l32G0etZn+RsA48ARRrFm6Ri8fvqPQfgrvNxSjKAbnd3g==} + '@rollup/rollup-freebsd-arm64@4.61.1': + resolution: {integrity: sha512-fUI4RapGE0Oh3mb8mgfvC1O2nU1RpDZUKnDQm3xB1Ipg7C2wTs5Kstz7G2uWK99a8S2yTMq8/P4uycwNa0nJyw==} cpu: [arm64] os: [freebsd] - '@rollup/rollup-freebsd-x64@4.60.4': - resolution: {integrity: sha512-Iw3oMskH3AfNuhU0MSN7vNbdi4me/NiYo2azqPz/Le16zHSa+3RRmliCMWWQmh4lcndccU40xcJuTYJZxNo/lw==} + '@rollup/rollup-freebsd-x64@4.61.1': + resolution: {integrity: sha512-H5YrdvJaDtI/U9/emrD4b++xkvp3y/JvOe4rizHbxvkyMfRS/CiRYdji+Pl8D0brEaNFWUh1drQxgAGIl6Xudw==} cpu: [x64] os: [freebsd] - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': - resolution: {integrity: sha512-EIPRXTVQpHyF8WOo219AD2yEltPehLTcTMz2fn6JsatLYSzQf00hj3rulF+yauOlF9/FtM2WpkT/hJh/KJFGhA==} + '@rollup/rollup-linux-arm-gnueabihf@4.61.1': + resolution: {integrity: sha512-Q8CBCCQtDFrYtXoeUXSrnFXKOnyUhx6bz+SkL6A0E7V8kAiCJ5pamq1WtbfpVGhR5TSpXY6ak3avmDc5fHTyJA==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm-musleabihf@4.60.4': - resolution: {integrity: sha512-J3Yh9PzzF1Ovah2At+lHiGQdsYgArxBbXv/zHfSyaiFQEqvNv7DcW98pCrmdjCZBrqBiKrKKe2V+aaSGWuBe/w==} + '@rollup/rollup-linux-arm-musleabihf@4.61.1': + resolution: {integrity: sha512-nwnhk1581l0FBVellGcVCAT0Oi06onEA3WB53sf01VO3I0UPBkMH9sXONYME2K0ovXcNayJfNtHfm6mpJElatQ==} cpu: [arm] os: [linux] - '@rollup/rollup-linux-arm64-gnu@4.60.4': - resolution: {integrity: sha512-BFDEZMYfUvLn37ONE1yMBojPxnMlTFsdyNoqncT0qFq1mAfllL+ATMMJd8TeuVMiX84s1KbcxcZbXInmcO2mRg==} + '@rollup/rollup-linux-arm64-gnu@4.61.1': + resolution: {integrity: sha512-x5Xr49hwt3hdW75UOZm3395YwwzPyauktslv29KpWL/T+vVAzoT3azLcTWv0eMciBNrx+DYjH4paehHoLpPvpg==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-arm64-musl@4.60.4': - resolution: {integrity: sha512-pc9EYOSlOgdQ2uPl1o9PF6/kLSgaUosia7gOuS8mB69IxJvlclko1MECXysjs5ryez1/5zjYqx3+xYU0TU6R1A==} + '@rollup/rollup-linux-arm64-musl@4.61.1': + resolution: {integrity: sha512-unMS3H73DpaoPyyEVPjGKleM/s0mkmsauTENpw4INQY8y4+IuLNjkueQ5QCtC0D3N38Y38yhAU8OoZ20S2Tm6w==} cpu: [arm64] os: [linux] - '@rollup/rollup-linux-loong64-gnu@4.60.4': - resolution: {integrity: sha512-NxnomyxYerDh5n4iLrNa+sH+Z+U4BMEE46V2PgQ/hoB909i8gV1M5wPojWg9fk1jWpO3IQnOs20K4wyZuFLEFQ==} + '@rollup/rollup-linux-loong64-gnu@4.61.1': + resolution: {integrity: sha512-zNZzGRnAhwjFEYmvphJRV5XaQGjs62cCmeYYHUT//NbvEnHauw+I85nGG+SiVg5ld4GX8D1IbKIX+ozITQnhMQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-loong64-musl@4.60.4': - resolution: {integrity: sha512-nbJnQ8a3z1mtmrwImCYhc6BGpThAyYVRQxw9uKSKG4wR6aAYno9sVjJ0zaZcW9BPJX1GbrDPf+SvdWjgTuDmnw==} + '@rollup/rollup-linux-loong64-musl@4.61.1': + resolution: {integrity: sha512-LdpWGL8X209B2SIvWjqlc8VZgM6PKfontSerGepuldQmHYrAOtnMCXeJkxXGbC+PPZVOuu5czJo7fNV6aeW8rQ==} cpu: [loong64] os: [linux] - '@rollup/rollup-linux-ppc64-gnu@4.60.4': - resolution: {integrity: sha512-2EU6acNrQLd8tYvo/LXW535wupT3m6fo7HKo6lr7ktQoItxTyOL1ZCR/GfGCuXl2vR+zmfI6eRXkSemafv+iVg==} + '@rollup/rollup-linux-ppc64-gnu@4.61.1': + resolution: {integrity: sha512-EC5kTtNaNGOmbMGqar8dvJy6y/hg99GAwjfBz++pxZhQATXGcRjd6c5en5wcbru0vkRmiMGsQKdMJOOf6sza4g==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-ppc64-musl@4.60.4': - resolution: {integrity: sha512-WeBtoMuaMxiiIrO2IYP3xs6GMWkJP2C0EoT8beTLkUPmzV1i/UcOSVw1d5r9KBODtHKilG5yFxsGRnBbK3wJ4A==} + '@rollup/rollup-linux-ppc64-musl@4.61.1': + resolution: {integrity: sha512-8hiwp6D4acEcNK78I4rP0/XtS1sknWIAMJBPdR4l6zUtyTm5KiTDr5bXmWt4foY7nAN7AThDHgkLIEZOWKbzWw==} cpu: [ppc64] os: [linux] - '@rollup/rollup-linux-riscv64-gnu@4.60.4': - resolution: {integrity: sha512-FJHFfqpKUI3A10WrWKiFbBZ7yVbGT4q4B5o1qKFFojqpaYoh9LrQgqWCmmcxQzVSXYtyB5bzkXrYzlHTs21MYA==} + '@rollup/rollup-linux-riscv64-gnu@4.61.1': + resolution: {integrity: sha512-10dh/h/BqA7DuMPWSxkR8uks18FRwnwOEqr5zOTEl+NOwP/OMzKX8OFR/Of9xxDA7D5qef1Nzar5WDD2kCCr1g==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-riscv64-musl@4.60.4': - resolution: {integrity: sha512-mcEl6CUT5IAUmQf1m9FYSmVqCJlpQ8r8eyftFUHG8i9OhY7BkBXSUdnLH5DOf0wCOjcP9v/QO93zpmF1SptCCw==} + '@rollup/rollup-linux-riscv64-musl@4.61.1': + resolution: {integrity: sha512-YKJ5lg35DP17gcAOggnihe+APw9HLyj1Xn7gsmGumBJAUDa6NGXNixJzmkWLhcK9TOuuyQjdamzvJefkO7qHZQ==} cpu: [riscv64] os: [linux] - '@rollup/rollup-linux-s390x-gnu@4.60.4': - resolution: {integrity: sha512-ynt3JxVd2w2buzoKDWIyiV1pJW93xlQic1THVLXilz429oijRpSHivZAgp65KBu+cMcgf1eVVjdnTLvPxgCuoQ==} + '@rollup/rollup-linux-s390x-gnu@4.61.1': + resolution: {integrity: sha512-Mlil5G2Jj6a7B3LWGctg+XPL9vdXYuzCtNXfxOQ0nPjc2m6ueUktocPGH9bnAM0bNRKb/bAWTujUU7IJQdQA+g==} cpu: [s390x] os: [linux] - '@rollup/rollup-linux-x64-gnu@4.60.4': - resolution: {integrity: sha512-Boiz5+MsaROEWDf+GGEwF8VMHGhlUoQMtIPjOgA5fv4osupqTVnJteQNKJwUcnUog2G55jYXH7KZFFiJe0TEzQ==} + '@rollup/rollup-linux-x64-gnu@4.61.1': + resolution: {integrity: sha512-bVWIOIk6pV01p4CdUbPP7CJ/434z+OooYjDuFcR+44N35YvKUC66G8MGnvcWx5mWKW3g61J+t74l3Kj15Kwn2Q==} cpu: [x64] os: [linux] - '@rollup/rollup-linux-x64-musl@4.60.4': - resolution: {integrity: sha512-+qfSY27qIrFfI/Hom04KYFw3GKZSGU4lXus51wsb5EuySfFlWRwjkKWoE9emgRw/ukoT4Udsj4W/+xxG8VbPKg==} + '@rollup/rollup-linux-x64-musl@4.61.1': + resolution: {integrity: sha512-qy5pBvZbqNFheBz61R1rzsezjm0J7O2oNGoWtGoY89SZYLUfxAJTBAqDChqAIdB4rCiIbi9nF7yZ83GnNiLwSw==} cpu: [x64] os: [linux] - '@rollup/rollup-openbsd-x64@4.60.4': - resolution: {integrity: sha512-VpTfOPHgVXEBeeR8hZ2O0F3aSso+JDWqTWmTmzcQKted54IAdUVbxE+j/MVxUsKa8L20HJhv3vUezVPoquqWjA==} + '@rollup/rollup-openbsd-x64@4.61.1': + resolution: {integrity: sha512-E83TXjI4zm0+5f2qO+UOudaCYIhYwpJ5jq6YCZNIZ+6CbfhKrkAGezeiASBL9ElxAxFsRS9ZhESv8mfnj6TKeg==} cpu: [x64] os: [openbsd] - '@rollup/rollup-openharmony-arm64@4.60.4': - resolution: {integrity: sha512-IPOsh5aRYuLv/nkU51X10Bf75Bsf6+gZdx1X+QP5QM6lIJFHHqbHLG0uJn/hWthzo13UAc2umiUorqZy3axoZg==} + '@rollup/rollup-openharmony-arm64@4.61.1': + resolution: {integrity: sha512-fbWnKqVkjrJN38vNe3ahkbk6iejS/3b0Nt7EEtPpE6RBacZcGXNKbzfHN3GUUlXOPghUg0j6XUGrtjX9z1sIvA==} cpu: [arm64] os: [openharmony] - '@rollup/rollup-win32-arm64-msvc@4.60.4': - resolution: {integrity: sha512-4QzE9E81OohJ/HKzHhsqU+zcYYojVOXlFMs1DdyMT6qXl/niOH7AVElmmEdUNHHS/oRkc++d5k6Vy85zFs0DEw==} + '@rollup/rollup-win32-arm64-msvc@4.61.1': + resolution: {integrity: sha512-ArMl38iVAbk0New1ogihQNY6iphLi4ZaRsa037gUzv5yeKPY8TD3Dmy4x2RNC1VztU/uqm+G+/RwFrSka3Oy2g==} cpu: [arm64] os: [win32] - '@rollup/rollup-win32-ia32-msvc@4.60.4': - resolution: {integrity: sha512-zTPgT1YuHHcd+Tmx7h8aml0FWFVelV5N54oHow9SLj+GfoDy/huQ+UV396N/C7KpMDMiPspRktzM1/0r1usYEA==} + '@rollup/rollup-win32-ia32-msvc@4.61.1': + resolution: {integrity: sha512-0mYtjHS9ucAbcATycCNK9IGBk/cCe/ma7EmSLGZdsxnOA8cjRIyU04wDpVAD9NiOfLUR9KTxdiO53uOkherqjQ==} cpu: [ia32] os: [win32] - '@rollup/rollup-win32-x64-gnu@4.60.4': - resolution: {integrity: sha512-DRS4G7mi9lJxqEDezIkKCaUIKCrLUUDCUaCsTPCi/rtqaC6D/jjwslMQyiDU50Ka0JKpeXeRBFBAXwArY52vBw==} + '@rollup/rollup-win32-x64-gnu@4.61.1': + resolution: {integrity: sha512-gK1iCEPfpoSG9wfBihXxvBMi8ZfcWffYkEsC/Eih+iFENTaewvNcrEQ69lIOWYO5pePHKLHHO7nq5AILGO/HQQ==} cpu: [x64] os: [win32] - '@rollup/rollup-win32-x64-msvc@4.60.4': - resolution: {integrity: sha512-QVTUovf40zgTqlFVrKA1uXMVvU2QWEFWfAH8Wdc48IxLvrJMQVMBRjuQyUpzZCDkakImib9eVazbWlC6ksWtJw==} + '@rollup/rollup-win32-x64-msvc@4.61.1': + resolution: {integrity: sha512-X+zaP2x+j4RXGfbp/seSoRHWnPxzApilDszisZxbYH5C/jTxFhCtDNdPGZb9lJyYPs24wGxruPF7Y+sIXt9Gzw==} cpu: [x64] os: [win32] - '@sinclair/typebox@0.27.8': - resolution: {integrity: sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==} + '@sinclair/typebox@0.27.10': + resolution: {integrity: sha512-MTBk/3jGLNB2tVxv6uLlFh1iu64iYOQ2PbdOSK3NW8JZsmlaOh2q6sdtKowBhfw8QFLmYNzTW4/oK4uATIi6ZA==} '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} @@ -892,30 +905,35 @@ packages: '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} - '@supabase/auth-js@2.68.0': - resolution: {integrity: sha512-odG7nb7aOmZPUXk6SwL2JchSsn36Ppx11i2yWMIc/meUO2B2HK9YwZHPK06utD9Ql9ke7JKDbwGin/8prHKxxQ==} + '@supabase/auth-js@2.108.1': + resolution: {integrity: sha512-Lle5rKU8f9LF3K5dDd8Or8mkkG+ptzRZZWKPVMm9B9UuovH65Ss2+iFnQqRsCqaGouvJEcTWyl0cj2riNrrDLQ==} + engines: {node: '>=20.0.0'} - '@supabase/functions-js@2.4.4': - resolution: {integrity: sha512-WL2p6r4AXNGwop7iwvul2BvOtuJ1YQy8EbOd0dhG1oN1q8el/BIRSFCFnWAMM/vJJlHWLi4ad22sKbKr9mvjoA==} + '@supabase/functions-js@2.108.1': + resolution: {integrity: sha512-fxBRW/A4IG7ADQztVt0NaEy5ysiO1WJ2pbldsnBchrkHuyepX0Krek9qA9T4gUQBVVTCE9Ea4pdsM5hfn3nc4A==} + engines: {node: '>=20.0.0'} - '@supabase/node-fetch@2.6.15': - resolution: {integrity: sha512-1ibVeYUacxWYi9i0cf5efil6adJ9WRyZBLivgjs+AUpewx1F3xPi7gLgaASI2SmIQxPoCEjAsLAzKPgMJVgOUQ==} - engines: {node: 4.x || >=6.0.0} + '@supabase/phoenix@0.4.2': + resolution: {integrity: sha512-YSAGnmDAfuleFCVt3CeurQZAhxRfXWeZIIkwp7NhYzQ1UwW6ePSnzsFAiUm/mbCkfoCf70QQHKW/K6RKh52a4A==} - '@supabase/postgrest-js@1.19.2': - resolution: {integrity: sha512-MXRbk4wpwhWl9IN6rIY1mR8uZCCG4MZAEji942ve6nMwIqnBgBnZhZlON6zTTs6fgveMnoCILpZv1+K91jN+ow==} + '@supabase/postgrest-js@2.108.1': + resolution: {integrity: sha512-9lj2MCPPMgSTaJ5y+amnhb3TWPtMFVlbDn2hmX/VV91xQU4j0AauwfMaBErHBJ+zzsSwjc0jLU+zLIZFLQzfig==} + engines: {node: '>=20.0.0'} - '@supabase/realtime-js@2.11.2': - resolution: {integrity: sha512-u/XeuL2Y0QEhXSoIPZZwR6wMXgB+RQbJzG9VErA3VghVt7uRfSVsjeqd7m5GhX3JR6dM/WRmLbVR8URpDWG4+w==} + '@supabase/realtime-js@2.108.1': + resolution: {integrity: sha512-mHGGqOjwd1XTydcoffUqEMsbFQHUi6A3uhQ0EXr3iqzpLqItxKA9nbN6gIQxrZ7JRRnuUe/iOFPUkYV9Tdc5lg==} + engines: {node: '>=20.0.0'} - '@supabase/storage-js@2.7.1': - resolution: {integrity: sha512-asYHcyDR1fKqrMpytAS1zjyEfvxuOIp1CIXX7ji4lHHcJKqyk+sLl/Vxgm4sN6u8zvuUtae9e4kDxQP2qrwWBA==} + '@supabase/storage-js@2.108.1': + resolution: {integrity: sha512-Er0SGGt85iT6ye+SSh98Az6L2CesoZJuyzEZYH2oBOAnIxa9Nn4CtwUC3veGxYggoT56X+3tVuuQeDBP8kR8sg==} + engines: {node: '>=20.0.0'} - '@supabase/supabase-js@2.49.1': - resolution: {integrity: sha512-lKaptKQB5/juEF5+jzmBeZlz69MdHZuxf+0f50NwhL+IE//m4ZnOeWlsKRjjsM0fVayZiQKqLvYdBn0RLkhGiQ==} + '@supabase/supabase-js@2.108.1': + resolution: {integrity: sha512-V/1hRKLSCJ0zEL+9QFRBUtivvePfOsaAYQmC0HhFNSHC2F3xFs4jSF3YhkLmzex6E4V4FGvmBDOP72D/53NnZA==} + engines: {node: '>=20.0.0'} - '@tsconfig/node10@1.0.11': - resolution: {integrity: sha512-DcRjDCujK/kCk/cUe8Xz8ZSpm8mS3mNNpta+jGCA6USEDfktlNvm1+IuZ9eTcDbNk41BHwpHHeW+N1lKCz4zOw==} + '@tsconfig/node10@1.0.12': + resolution: {integrity: sha512-UCYBaeFvM11aU2y3YPZ//O5Rhj+xKyzy7mvcIoAjASbigy8mHMryP5cK7dgjlz2hWxh1g5pLw084E0a/wlUSFQ==} '@tsconfig/node12@1.0.11': resolution: {integrity: sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==} @@ -929,20 +947,20 @@ packages: '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} - '@types/babel__generator@7.6.8': - resolution: {integrity: sha512-ASsj+tpEDsEiFr1arWrlN6V3mdfjRMZt6LtK/Vp/kreFLnr5QH5+DhvD5nINYZXzwJvXeGq+05iUXcAzVrqWtw==} + '@types/babel__generator@7.27.0': + resolution: {integrity: sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==} '@types/babel__template@7.4.4': resolution: {integrity: sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==} - '@types/babel__traverse@7.20.6': - resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/babel__traverse@7.28.0': + resolution: {integrity: sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==} '@types/better-sqlite3@7.6.13': resolution: {integrity: sha512-NMv9ASNARoKksWtsq/SHakpYAYnhBrQgGD8zkLYk/jaK8jUGn08CfEdTRgYhMypUQAfzSP8W6gNLe0q19/t4VA==} - '@types/estree@1.0.8': - resolution: {integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==} + '@types/estree@1.0.9': + resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} '@types/graceful-fs@4.1.9': resolution: {integrity: sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==} @@ -959,14 +977,14 @@ packages: '@types/jest@29.5.14': resolution: {integrity: sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==} - '@types/node-fetch@2.6.12': - resolution: {integrity: sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA==} + '@types/node-fetch@2.6.13': + resolution: {integrity: sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==} - '@types/node@18.19.76': - resolution: {integrity: sha512-yvR7Q9LdPz2vGpmpJX5LolrgRdWvB67MJKDPSgIIzpFbaf9a1j/f5DnLp5VDyHGMR0QZHlTr1afsD87QCXFHKw==} + '@types/node@18.19.130': + resolution: {integrity: sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==} - '@types/node@22.13.5': - resolution: {integrity: sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg==} + '@types/node@22.19.21': + resolution: {integrity: sha512-VMeFBSCKQKmm2swI2kW51SFusDqekC6q9trBCvJ/JliDchFSuoYYKN7yVNjPthP1HKZcx3U1gI/wTcEBjEFKTA==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} @@ -974,9 +992,6 @@ packages: '@types/pg@8.11.0': resolution: {integrity: sha512-sDAlRiBNthGjNFfvt0k6mtotoVYVQ63pA8R4EMWka7crawSR60waVYR0HAgmPRs/e2YaeJTD/43OoZ3PFw80pw==} - '@types/phoenix@1.6.6': - resolution: {integrity: sha512-PIzZZlEppgrpoT2QgbnDU+MMzuR6BbCjllj0bM70lWoejMeNJAxCchxnv7J3XFkI8MpygtRpzXrIlmWUBclP5A==} - '@types/retry@0.12.0': resolution: {integrity: sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==} @@ -992,29 +1007,26 @@ packages: '@types/whatwg-url@13.0.0': resolution: {integrity: sha512-N8WXpbE6Wgri7KUSvrmQcqrMllKZ9uxkYWMt+mCSGwNc0Hsw9VQTW7ApqI4XNrx6/SaM2QQJCzMPDEXE058s+Q==} - '@types/ws@8.18.0': - resolution: {integrity: sha512-8svvI3hMyvN0kKCJMvTJP/x6Y/EoQbepff882wL+Sn5QsXb3etnamgrJq4isrBxSJj5L2AuXcI0+bgkoAXGUJw==} - '@types/yargs-parser@21.0.3': resolution: {integrity: sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==} - '@types/yargs@17.0.33': - resolution: {integrity: sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==} + '@types/yargs@17.0.35': + resolution: {integrity: sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==} - '@typespec/ts-http-runtime@0.3.2': - resolution: {integrity: sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg==} + '@typespec/ts-http-runtime@0.3.6': + resolution: {integrity: sha512-jIXhD0eWQ1JA6ln/5Dltyx22UxWNrw0hZmhy2rlv6m6KgF7kplHx3g0fzi09lNmTJQRR91OlemYp3xFnvDK9og==} engines: {node: '>=20.0.0'} abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} - acorn-walk@8.3.4: - resolution: {integrity: sha512-ueEepnujpqee2o5aIYnvHU6C0A42MNdsIDeqy5BydrkuC5R1ZuUFnm27EeFJGoEHJQgn3uleRvmTXaJgfXbt4g==} + acorn-walk@8.3.5: + resolution: {integrity: sha512-HEHNfbars9v4pgpW6SO1KSPkfoS0xVOM/9UzkJltjlsHZmJasxg8aXkuZa7SMf8vKGIBhpUsPluQSqhJFCqebw==} engines: {node: '>=0.4.0'} - acorn@8.14.0: - resolution: {integrity: sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA==} + acorn@8.16.0: + resolution: {integrity: sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==} engines: {node: '>=0.4.0'} hasBin: true @@ -1028,8 +1040,8 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - agent-base@7.1.3: - resolution: {integrity: sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw==} + agent-base@7.1.4: + resolution: {integrity: sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==} engines: {node: '>= 14'} agentkeepalive@4.6.0: @@ -1044,8 +1056,8 @@ packages: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} + ansi-regex@6.2.2: + resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@4.3.0: @@ -1056,8 +1068,8 @@ packages: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} + ansi-styles@6.2.3: + resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} any-promise@1.3.0: @@ -1074,17 +1086,14 @@ packages: arg@4.1.3: resolution: {integrity: sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==} - argparse@1.0.10: - resolution: {integrity: sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==} - - async@3.2.6: - resolution: {integrity: sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==} + argparse@2.0.1: + resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - axios@1.16.1: - resolution: {integrity: sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==} + axios@1.17.0: + resolution: {integrity: sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==} babel-jest@29.7.0: resolution: {integrity: sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==} @@ -1100,10 +1109,10 @@ packages: resolution: {integrity: sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - babel-preset-current-node-syntax@1.1.0: - resolution: {integrity: sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==} + babel-preset-current-node-syntax@1.2.0: + resolution: {integrity: sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==} peerDependencies: - '@babel/core': ^7.0.0 + '@babel/core': ^7.0.0 || ^8.0.0-0 babel-preset-jest@29.6.3: resolution: {integrity: sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==} @@ -1114,19 +1123,27 @@ packages: balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + balanced-match@4.0.4: + resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} + engines: {node: 18 || 20 || >=22} + base-64@0.1.0: resolution: {integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==} base64-js@1.5.1: resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==} - better-sqlite3@12.6.2: - resolution: {integrity: sha512-8VYKM3MjCa9WcaSAI3hzwhmyHVlH8tiGFwf0RlTsZPWJ1I5MkzjiudCo4KC4DxOaL/53A5B1sI/IbldNFDbsKA==} - engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x} + baseline-browser-mapping@2.10.35: + resolution: {integrity: sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==} + engines: {node: '>=6.0.0'} + hasBin: true - bignumber.js@9.2.0: - resolution: {integrity: sha512-JocpCSOixzy5XFJi2ub6IMmV/G9i8Lrm2lZvwBv9xPdglmZM0ufDVBbjbrfU/zuLvBfD7Bv2eYxz9i+OHTgkew==} - deprecated: pkg version number incorrect + better-sqlite3@12.10.0: + resolution: {integrity: sha512-CyzaZRQKyHkB2ZInfTTl2nvT33EbDpjkLEbE8/Zck3Ll6O0qqvuGdrJ45HgtH+HykRg88ITY3AdreBGN70aBSQ==} + engines: {node: 20.x || 22.x || 23.x || 24.x || 25.x || 26.x} + + bignumber.js@9.3.1: + resolution: {integrity: sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==} binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} @@ -1138,21 +1155,22 @@ packages: bl@4.1.0: resolution: {integrity: sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==} - brace-expansion@1.1.11: - resolution: {integrity: sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==} - - brace-expansion@2.0.1: - resolution: {integrity: sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==} + brace-expansion@1.1.15: + resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} brace-expansion@2.1.1: resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} + engines: {node: 18 || 20 || >=22} + braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.4: - resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1210,8 +1228,8 @@ packages: resolution: {integrity: sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==} engines: {node: '>=10'} - caniuse-lite@1.0.30001701: - resolution: {integrity: sha512-faRs/AW3jA9nTwmJBSO1PQ6L/EOgsB5HMQQq4iCu5zhPgVVgO/pZRHlmatwijZKetFw8/Pr4q6dEN8sJuq8qTw==} + caniuse-lite@1.0.30001799: + resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1246,8 +1264,8 @@ packages: resolution: {integrity: sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==} engines: {node: '>=12'} - cloudflare@4.3.0: - resolution: {integrity: sha512-C+4Jhsl/OY4V5sykRB1yJxComDld5BkKW1xd3s0MDJ1yYamT2sFAoC2FEUQg5zipyxMaaGU4N7hZ6il+gfJxZg==} + cloudflare@4.5.0: + resolution: {integrity: sha512-fPcbPKx4zF45jBvQ0z7PCdgejVAPBBCZxwqk1k7krQNfpM07Cfj97/Q6wBzvYqlWXx/zt1S9+m8vnfCe06umbQ==} cluster-key-slot@1.1.2: resolution: {integrity: sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==} @@ -1257,8 +1275,8 @@ packages: resolution: {integrity: sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==} engines: {iojs: '>= 1.0.0', node: '>= 0.12.0'} - collect-v8-coverage@1.0.2: - resolution: {integrity: sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==} + collect-v8-coverage@1.0.3: + resolution: {integrity: sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==} color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} @@ -1275,13 +1293,16 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} - compromise@14.15.0: - resolution: {integrity: sha512-YEMv5JGWyqRJw5hdZqDVQF3MMlHA6TRiXreR8IYffk6xB7GA5p/8DeDzvg0Jy2tHNGpD+qJGl0+oJwA+5R/sVA==} + compromise@14.15.1: + resolution: {integrity: sha512-9F3UkUaEU1PPz2fgStkE/TI4tk++0wHxS8xfWq9PQWL/v28dy8bEcPVVSLh3dISIRD7PEhJ8YTzHRKF8y9tnLA==} engines: {node: '>=12.0.0'} concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + 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} @@ -1308,8 +1329,8 @@ packages: resolution: {integrity: sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==} engines: {node: '>= 12'} - debug@4.4.0: - resolution: {integrity: sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==} + debug@4.4.3: + resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' @@ -1321,8 +1342,8 @@ packages: resolution: {integrity: sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==} engines: {node: '>=10'} - dedent@1.5.3: - resolution: {integrity: sha512-NHQtfOOW68WD8lgypbLA5oT+Bt0xXJhiYvoR6SmmNXZfpzOGXwdKWmcwG8N7PwVVWV3eF/68nmD9BaJSsTBhyQ==} + dedent@1.7.2: + resolution: {integrity: sha512-WzMx3mW98SN+zn3hgemf4OzdmyNhhhKz5Ay0pUfQiMQ3e1g+xmTJWp/pKdwKVXhdSkAEGIIzqeuWrL3mV/AXbA==} peerDependencies: babel-plugin-macros: ^3.1.0 peerDependenciesMeta: @@ -1341,8 +1362,8 @@ packages: resolution: {integrity: sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==} engines: {node: '>=18'} - default-browser@5.4.0: - resolution: {integrity: sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==} + default-browser@5.5.0: + resolution: {integrity: sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==} engines: {node: '>=18'} define-lazy-prop@3.0.0: @@ -1353,8 +1374,8 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - detect-libc@2.0.3: - resolution: {integrity: sha512-bwy0MGW55bG41VqxxypOsdSdGqLwXPI/focwgTYCFMbdUiBAxLg9CFzG08sz2aqzknwiX7Hkl0bQENjg8iLByw==} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} detect-newline@3.1.0: @@ -1365,15 +1386,15 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@4.0.2: - resolution: {integrity: sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==} + diff@4.0.4: + resolution: {integrity: sha512-X07nttJQkwkfKfvTPG/KSnE2OMdcUCao6+eXF3wmnIQRn2aPAHH3VxDbDOdegkd6JbPsXqShpvEOHfAT+nCNwQ==} engines: {node: '>=0.3.1'} digest-fetch@1.3.0: resolution: {integrity: sha512-CGJuv6iKNM7QyZlM2T3sPAdZWd/p9zQiRNS9G+9COUCwzWFTs0Xp8NF5iePx7wtvhDykReiRRrSeNb4oMmB8lA==} - dotenv@16.4.7: - resolution: {integrity: sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==} + dotenv@16.6.1: + resolution: {integrity: sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==} engines: {node: '>=12'} dotenv@17.4.2: @@ -1394,13 +1415,8 @@ packages: resolution: {integrity: sha512-/RInbCy1d4P6Zdfa+TMVsf/ufZVotat5hCw3QXmWtjU+3pFEOvOQ7ibo3aIxyCJw2leIeAMjmPj+1SLJiCpdrQ==} engines: {node: '>=12.0.0'} - ejs@3.1.10: - resolution: {integrity: sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==} - engines: {node: '>=0.10.0'} - hasBin: true - - electron-to-chromium@1.5.109: - resolution: {integrity: sha512-AidaH9JETVRr9DIPGfp1kAarm/W6hRJTPuCnkF+2MqhF4KaAgRIcBc8nvjk+YMXZhwfISof/7WG29eS4iGxQLQ==} + electron-to-chromium@1.5.371: + resolution: {integrity: sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w==} emittery@0.13.1: resolution: {integrity: sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==} @@ -1412,14 +1428,11 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - encoding@0.1.13: - resolution: {integrity: sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==} + end-of-stream@1.4.5: + resolution: {integrity: sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==} - end-of-stream@1.4.4: - resolution: {integrity: sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==} - - error-ex@1.3.2: - resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + error-ex@1.3.4: + resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} @@ -1429,16 +1442,16 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + 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.25.1: - resolution: {integrity: sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==} + esbuild@0.27.7: + resolution: {integrity: sha512-IxpibTjyVnmrIQo5aqNpCgoACA/dTKLTlhMHihVHhdkxKyPO1uBBthumT0rdHmcsk9uMonIWS0m4FljWzILh3w==} engines: {node: '>=18'} hasBin: true @@ -1450,11 +1463,6 @@ packages: resolution: {integrity: sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==} engines: {node: '>=8'} - esprima@4.0.1: - resolution: {integrity: sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==} - engines: {node: '>=4'} - hasBin: true - event-target-shim@5.0.1: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} @@ -1492,16 +1500,17 @@ packages: fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fb-watchman@2.0.2: resolution: {integrity: sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==} - fdir@6.4.3: - resolution: {integrity: sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==} + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} peerDependencies: - picomatch: ^3 || ^4 + picomatch: ^4.0.4 peerDependenciesMeta: picomatch: optional: true @@ -1513,9 +1522,6 @@ packages: file-uri-to-path@1.0.0: resolution: {integrity: sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==} - filelist@1.0.4: - resolution: {integrity: sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==} - fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} @@ -1524,6 +1530,9 @@ packages: resolution: {integrity: sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==} engines: {node: '>=8'} + fix-dts-default-cjs-exports@1.0.1: + resolution: {integrity: sha512-pVIECanWFC61Hzl2+oOCtoJ3F17kglZC/6N94eRWycFgBH35hHx0Li604ZIzhseh97mf2p0cv7vVrOZGoqhlEg==} + fix-tsup-cjs@1.2.0: resolution: {integrity: sha512-5z2nZxrnKxk+jLq5TyD0xbPXI2I18FF+knIZVG55e0CXWgXF/F4SpCBsiW7JTBPwghqXsC66T2yctnVT/sMO0g==} hasBin: true @@ -1570,8 +1579,8 @@ packages: function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} - gaxios@7.1.4: - resolution: {integrity: sha512-bTIgTsM2bWn3XklZISBTQX7ZSddGW+IO3bMdGaemHZ3tbqExMENHLx6kKZ/KlejgrMtj8q7wBItt51yegqalrA==} + gaxios@7.1.5: + resolution: {integrity: sha512-5FZy72Rh8LhtjmvDrKkI+lVhrsQrVKVsItxMoDm5mNQE+xR0WVIIs+jzPSJgBvKVsLi24fZhXJIsNI0bihDzFg==} engines: {node: '>=18'} gcp-metadata@8.1.2: @@ -1622,12 +1631,8 @@ packages: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - globals@11.12.0: - resolution: {integrity: sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==} - engines: {node: '>=4'} - - google-auth-library@10.6.2: - resolution: {integrity: sha512-e27Z6EThmVNNvtYASwQxose/G57rkRuaRbQyxM2bvYLLX/GqWZ5chWq2EBoUchJbCc57eC9ArzO5wMsEmWftCw==} + google-auth-library@10.7.0: + resolution: {integrity: sha512-QpTAbNJ36TliZLx3TTtahR8HG0hN9RllL1e3FymOvQSIKK8JmgV58H924ub2wa2DsS3ANjjP1Aw1N+Ramc8hqQ==} engines: {node: '>=18'} google-logging-utils@1.1.3: @@ -1648,6 +1653,11 @@ packages: groq-sdk@0.3.0: resolution: {integrity: sha512-Cdgjh4YoSBE2X4S9sxPGXaAy1dlN4bRtAaDZ3cnq+XsxhhN9WSBeHF64l7LWwuD5ntmw7YC5Vf4Ff1oHCg1LOg==} + handlebars@4.7.9: + resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} + engines: {node: '>=0.4.7'} + hasBin: true + has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} @@ -1664,8 +1674,8 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} hosted-git-info@7.0.2: @@ -1694,9 +1704,9 @@ packages: humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} - iconv-lite@0.6.3: - resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} - engines: {node: '>=0.10.0'} + iceberg-js@0.8.1: + resolution: {integrity: sha512-1dhVQZXhcHje7798IVM+xoo/1ZdVfzOMIc8/rgVSijRK38EDqOJoGula9N/8ZI5RD8QTxNQtK/Gozpr+qUqRRA==} + engines: {node: '>=20.0.0'} ieee754@1.2.1: resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==} @@ -1733,8 +1743,8 @@ packages: is-buffer@1.1.6: resolution: {integrity: sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==} - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} engines: {node: '>= 0.4'} is-docker@3.0.0: @@ -1771,8 +1781,8 @@ packages: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} - is-wsl@3.1.0: - resolution: {integrity: sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==} + is-wsl@3.1.1: + resolution: {integrity: sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==} engines: {node: '>=16'} isexe@2.0.0: @@ -1798,18 +1808,13 @@ packages: resolution: {integrity: sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==} engines: {node: '>=10'} - istanbul-reports@3.1.7: - resolution: {integrity: sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==} + istanbul-reports@3.2.0: + resolution: {integrity: sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==} engines: {node: '>=8'} jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jake@10.9.2: - resolution: {integrity: sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==} - engines: {node: '>=10'} - hasBin: true - jest-changed-files@29.7.0: resolution: {integrity: sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -1943,14 +1948,14 @@ packages: resolution: {integrity: sha512-34wB/Y7MW7bzjKRjUKTa46I2Z7eV62Rkhva+KkopW7Qvv/OSWBqvkSY7vusOPrNuZcUG3tApvdVgNB8POj3SPw==} engines: {node: '>=10'} - js-tiktoken@1.0.19: - resolution: {integrity: sha512-XC63YQeEcS47Y53gg950xiZ4IWmkfMe4p2V9OSaBt26q+p47WHn18izuXzSclCI73B7yGqtfRsT6jcZQI0y08g==} + js-tiktoken@1.0.21: + resolution: {integrity: sha512-biOj/6M5qdgx5TKjDnFT1ymSpM5tbd3ylwDtrQvFQSu0Z7bBYko2dF+W/aUkXUPuk6IVpRxk/3Q2sHOzGlS36g==} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@3.14.1: - resolution: {integrity: sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==} + js-yaml@4.2.0: + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} hasBin: true jsesc@3.1.0: @@ -1983,8 +1988,8 @@ packages: jws@4.0.1: resolution: {integrity: sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==} - kareem@3.2.0: - resolution: {integrity: sha512-VS8MWZz/cT+SqBCpVfNN4zoVz5VskR3N4+sTmUXme55e9avQHntpwpNq0yjnosISXqwJ3AQVjlbI4Dyzv//JtA==} + kareem@3.3.0: + resolution: {integrity: sha512-kpSuLD3/7RenBnjnJdOHXCKC8dTd1JzeOiJhN0necWWci6cC+qX+VuwPnMVgb+a4+KNJSfgqahpnfWaeDXCimw==} engines: {node: '>=18.0.0'} kleur@3.0.3: @@ -2001,7 +2006,7 @@ packages: '@opentelemetry/exporter-trace-otlp-proto': '*' '@opentelemetry/sdk-trace-base': '*' openai: '*' - ws: '>=7' + ws: '>=8.20.1' peerDependenciesMeta: '@opentelemetry/api': optional: true @@ -2061,9 +2066,6 @@ packages: lodash.once@4.1.1: resolution: {integrity: sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==} - lodash.sortby@4.7.0: - resolution: {integrity: sha512-HDWXG8isMntAyRF5vZ7xKuEvOhT4AhlRt/3czTSjvGUxjYCBVRQY48ViDHyfYz9VIoBkW4TMGQNapx+l3RUwdA==} - long@5.3.2: resolution: {integrity: sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==} @@ -2073,6 +2075,9 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} + magic-string@0.30.21: + resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} + make-dir@4.0.0: resolution: {integrity: sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==} engines: {node: '>=10'} @@ -2124,13 +2129,13 @@ packages: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} + minimatch@10.2.5: + resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} + engines: {node: 18 || 20 || >=22} + minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} - minimatch@5.1.9: - resolution: {integrity: sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==} - engines: {node: '>=10'} - minimatch@9.0.9: resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} engines: {node: '>=16 || 14 >=14.17'} @@ -2138,19 +2143,22 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} + minipass@7.1.3: + resolution: {integrity: sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==} engines: {node: '>=16 || 14 >=14.17'} mkdirp-classic@0.5.3: resolution: {integrity: sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==} + mlly@1.8.2: + resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} + mongodb-connection-string-url@7.0.1: resolution: {integrity: sha512-h0AZ9A7IDVwwHyMxmdMXKy+9oNlF0zFoahHiX3vQ8e3KFcSP3VmsmfvtRSuLPxmyv2vjIDxqty8smTgie/SNRQ==} engines: {node: '>=20.19.0'} - mongodb@7.1.1: - resolution: {integrity: sha512-067DXiMjcpYQl6bGjWQoTUEE9UoRViTtKFcoqX7z08I+iDZv/emH1g8XEFiO3qiDfXAheT5ozl1VffDTKhIW/w==} + mongodb@7.2.0: + resolution: {integrity: sha512-F/2+BMZtLVhY30ioZp0dAmZ+IRZMBqI+nrv6t5+9/1AIwCa8sMRC3jBf81lpxMhnZgqq8CoUD503Z1oZWq1/sw==} engines: {node: '>=20.19.0'} peerDependencies: '@aws-sdk/credential-providers': ^3.806.0 @@ -2176,8 +2184,8 @@ packages: socks: optional: true - mongoose@9.4.1: - resolution: {integrity: sha512-4rFBWa+/wdBQSfvnOPJBpiSG6UCEbhSQh865dEdaH9Y8WfHBUC+I2XT28dp0IBIGrEwmh+gzrgZgea5PbmrHWA==} + mongoose@9.7.0: + resolution: {integrity: sha512-pkrLZ6U41pD4Ai0ju/FYL7o5I5k+rV3RZINQTG937hbhnLGKRuqqYm1Dlt/kTQ+M4FHijzV6JawzsdHKRGt7QA==} engines: {node: '>=20.19.0'} mpath@0.9.0: @@ -2198,11 +2206,6 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.8: - resolution: {integrity: sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w==} - engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} - hasBin: true - napi-build-utils@2.0.0: resolution: {integrity: sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==} @@ -2213,8 +2216,11 @@ packages: resolution: {integrity: sha512-Ucb+lsUcGxUqu3rn8cwHjT6gJQosO63nIX/aBQXB3+IDkNbFV7PuviysO+Rzz3aKn7PZhPj3bNF4PS9gDVjYCQ==} engines: {node: '>=0.4.10'} - node-abi@3.74.0: - resolution: {integrity: sha512-c5XK0MjkGBrQPGYG24GBADZud0NCbznxNx0ZkS+ebUTrmV1qTDxPxSL8zEAPURXSbLRWVexxmP4986BziahL5w==} + neo-async@2.6.2: + resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} + + node-abi@3.92.0: + resolution: {integrity: sha512-KdHvFWZjEKDf0cakgFjebl371GPsISX2oZHcuyKqM7DtogIsHrqKeLTo8wBHxaXRAQlY2PsPlZmfo+9ZCxEREQ==} engines: {node: '>=10'} node-domexception@1.0.0: @@ -2238,11 +2244,12 @@ packages: node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.47: + resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} + engines: {node: '>=18'} - nodemon@3.1.9: - resolution: {integrity: sha512-hdr1oIb2p6ZSxu3PB2JWWYS7ZQ0qvaZsc3hK8DR8f02kRzc8rjYmxAIvdz+aYC+8F2IjNaB7HMcSDg8nQpJxyg==} + nodemon@3.1.14: + resolution: {integrity: sha512-jakjZi93UtB3jHMWsXL68FXSAosbLfY0In5gtKq3niLSkrWznrVBzXFNOEMJUfc9+Ke7SHWoAZsiMkNP3vq6Jw==} engines: {node: '>=10'} hasBin: true @@ -2265,8 +2272,8 @@ packages: obuf@1.1.2: resolution: {integrity: sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==} - ollama@0.5.14: - resolution: {integrity: sha512-pvOuEYa2WkkAumxzJP0RdEYHkbZ64AYyyUszXVX7ruLvk5L+EiO2G71da2GqEQ4IAk4j6eLoUbGk5arzFT1wJA==} + ollama@0.5.18: + resolution: {integrity: sha512-lTFqTf9bo7Cd3hpF6CviBe/DEhewjoZYd9N/uCe7O20qYTvGqrNOFOBDj3lbZgFWHUgDv5EeyusYxsZSLS8nvg==} once@1.4.0: resolution: {integrity: sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==} @@ -2279,11 +2286,11 @@ packages: resolution: {integrity: sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==} engines: {node: '>=18'} - openai@4.93.0: - resolution: {integrity: sha512-2kONcISbThKLfm7T9paVzg+QCE1FOZtNMMUfXyXckUAoXRRS/mTP89JSDHPMp8uM5s0bz28RISbvQjArD6mgUQ==} + openai@4.104.0: + resolution: {integrity: sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA==} hasBin: true peerDependencies: - ws: ^8.18.0 + ws: '>=8.20.1' zod: ^3.23.8 peerDependenciesMeta: ws: @@ -2356,17 +2363,14 @@ packages: resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} engines: {node: '>=16 || 14 >=14.18'} - pg-cloudflare@1.1.1: - resolution: {integrity: sha512-xWPagP/4B6BgFO+EKz3JONXv3YDgvkbVrGw2mTo3D6tVDQRh1e7cqVGvyR3BE+eQgAvx1XhW/iEASj4/jCWl3Q==} + pathe@2.0.3: + resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} - pg-cloudflare@1.3.0: - resolution: {integrity: sha512-6lswVVSztmHiRtD6I8hw4qP/nDm1EJbKMRhf3HCYaqud7frGysPv7FYJ5noZQdhQtN2xJnimfMtvQq21pdbzyQ==} + pg-cloudflare@1.4.0: + resolution: {integrity: sha512-Vo7z/6rrQYxpNRylp4Tlob2elzbh+N/MOQbxFVWCxS7oEx6jF53GTJFxK2WWpKuBRkmiin4Mt+xofFDjx09R0A==} - pg-connection-string@2.12.0: - resolution: {integrity: sha512-U7qg+bpswf3Cs5xLzRqbXbQl85ng0mfSV/J0nnA31MCLgvEaAo7CIhmeyrmJpOr7o+zm0rXK+hNnT5l9RHkCkQ==} - - pg-connection-string@2.7.0: - resolution: {integrity: sha512-PI2W9mv53rXJQEOb8xNR8lH7Hr+EKa6oJa38zsK0S/ky2er16ios1wLKhZyxzD7jUReiWokc9WK5nxSnC7W1TA==} + pg-connection-string@2.13.0: + resolution: {integrity: sha512-EMnU9E2fSULdsbErBbMaXJvFeD9B4+nPcM3f+4lsiCR0BHLPrLVjv3DbyM2hgQQviKJaTWIRRTjKjWlHg3p2ig==} pg-int8@1.0.1: resolution: {integrity: sha512-WCtabS6t3c8SkpDBUlb1kjOs7l66xsGdKpIPZsg4wR+B3+u9UAum2odSsF9tnvxg80h4ZxLWMy4pRjOsFIqQpw==} @@ -2376,28 +2380,20 @@ packages: resolution: {integrity: sha512-BM/Thnrw5jm2kKLE5uJkXqqExRUY/toLHda65XgFTBTFYZyopbKjBe29Ii3RbkvlsMoFwD+tHeGaCjjv0gHlyw==} engines: {node: '>=4'} - pg-pool@3.13.0: - resolution: {integrity: sha512-gB+R+Xud1gLFuRD/QgOIgGOBE2KCQPaPwkzBBGC9oG69pHTkhQeIuejVIk3/cnDyX39av2AxomQiyPT13WKHQA==} + pg-pool@3.14.0: + resolution: {integrity: sha512-gKtPkFdQPU3DksooVLi9LsjZxrsBUZIpa+7aVx+LV5pNh0KzP4Zleud2po+ConrxbuXGBJ6Hfer6hdgpIBpBaw==} peerDependencies: pg: '>=8.0' - pg-pool@3.7.1: - resolution: {integrity: sha512-xIOsFoh7Vdhojas6q3596mXFsR8nwBQBXX5JiV7p9buEVAGqYL4yFzclON5P9vFrpu1u7Zwl2oriyDa89n0wbw==} - peerDependencies: - pg: '>=8.0' - - pg-protocol@1.13.0: - resolution: {integrity: sha512-zzdvXfS6v89r6v7OcFCHfHlyG/wvry1ALxZo4LqgUoy7W9xhBDMaqOuMiF3qEV45VqsN6rdlcehHrfDtlCPc8w==} - - pg-protocol@1.7.1: - resolution: {integrity: sha512-gjTHWGYWsEgy9MsY0Gp6ZJxV24IjDqdpTW7Eh0x+WfJLFsm/TJx1MzL6T0D88mBvkpxotCQ6TwW6N+Kko7lhgQ==} + pg-protocol@1.14.0: + resolution: {integrity: sha512-n5taZ1kO3s9ngDTVxsEznOqCyToTgz0FLuPq0B33COy5pPpuWJpY3/2oRBVETuOgzdqRXfWpM9HIhp2LBBT1BA==} pg-types@2.2.0: resolution: {integrity: sha512-qTAAlrEsl8s4OiEQY69wDvcMIdQN6wdz5ojQiOy6YRMuynxenON0O5oCpJI6lshc6scgAY8qvJ2On/p+CXY0GA==} engines: {node: '>=4'} - pg-types@4.0.2: - resolution: {integrity: sha512-cRL3JpS3lKMGsKaWndugWQoLOCoP+Cic8oseVcbr0qhPzYD5DWXK+RZ9LY9wxRf7RQia4SCwQlXk0q6FCPrVng==} + pg-types@4.1.0: + resolution: {integrity: sha512-o2XFanIMy/3+mThw69O8d4n1E5zsLhdO+OPqswezu7Z5ekP4hYDqlDjlmOpYMbzY2Br0ufCwJLdDIXeNVwcWFg==} engines: {node: '>=10'} pg@8.11.3: @@ -2409,8 +2405,8 @@ packages: pg-native: optional: true - pg@8.20.0: - resolution: {integrity: sha512-ldhMxz2r8fl/6QkXnBD3CR9/xg694oT6DZQ2s6c/RI28OjtSOpxnPrUCGOBJ46RCUxcWdx3p6kw/xnDHjKvaRA==} + pg@8.21.0: + resolution: {integrity: sha512-AUP1EYJuHraQGsVoCQVIcM7TEJVGtDzxWtGFZd8rds9d+CCXlU5Js1rYgfLNvxy9iJrpHjGrRjoi/3BT9fRyiA==} engines: {node: '>= 16.0.0'} peerDependencies: pg-native: '>=3.0.1' @@ -2428,24 +2424,27 @@ packages: resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} - picomatch@4.0.2: - resolution: {integrity: sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} engines: {node: '>=12'} - pirates@4.0.6: - resolution: {integrity: sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==} + pirates@4.0.7: + resolution: {integrity: sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==} engines: {node: '>= 6'} pkg-dir@4.2.0: resolution: {integrity: sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==} engines: {node: '>=8'} + 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' + postcss: '>=8.5.10' tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: @@ -2458,20 +2457,16 @@ packages: yaml: optional: true - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} - engines: {node: ^10 || ^12 || >=14} - postgres-array@2.0.0: resolution: {integrity: sha512-VpZrUqU5A69eQyW2c5CA1jtLecCsN2U/bD6VilrFDWq5+5UIEVO7nazS3TEcHf1zuPYO/sqGvUvW62g86RXZuA==} engines: {node: '>=4'} - postgres-array@3.0.2: - resolution: {integrity: sha512-6faShkdFugNQCLwucjPcY5ARoW1SlbnrZjmGl0IrrqewpvxvhSLHimCVzqeuULCbG0fQv7Dtk1yDbG3xv7Veog==} + postgres-array@3.0.4: + resolution: {integrity: sha512-nAUSGfSDGOaOAEGwqsRY27GPOea7CNipJPOA7lPbdEpx5Kg3qzdP0AaWC5MlhTWV9s4hFX39nomVZ+C4tnGOJQ==} engines: {node: '>=12'} - postgres-bytea@1.0.0: - resolution: {integrity: sha512-xy3pmLuQqRBZBXDULy7KbaitYqLcmxigw14Q5sj8QBVLqEwXfeybIKVWiqAXTlcvdvb0+xkOtDbfQMOf4lST1w==} + postgres-bytea@1.0.1: + resolution: {integrity: sha512-5+5HqXnsZPE65IJZSMkZtURARZelel2oXUEO8rH83VS/hxH5vv1uHquPg5wZs8yMAfdv971IU+kcPUczi7NVBQ==} engines: {node: '>=0.10.0'} postgres-bytea@3.0.0: @@ -2503,8 +2498,8 @@ packages: deprecated: No longer maintained. Please contact the author of the relevant native addon; alternatives are available. hasBin: true - prettier@3.5.2: - resolution: {integrity: sha512-lc6npv5PH7hVqozBR7lkBNOGXV9vMwROAPlumdBkX0wTbbzPu/U1hk5yL8p2pt4Xoc+2mkT8t/sow2YrV/M5qg==} + prettier@3.8.4: + resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==} engines: {node: '>=14'} hasBin: true @@ -2516,8 +2511,8 @@ packages: resolution: {integrity: sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==} engines: {node: '>= 6'} - protobufjs@7.6.1: - resolution: {integrity: sha512-4K0myLaWL5EteuSAro91EGFgcfVgxb64Jx+7oDAY6GOkXD4M69yuSEljNcInGVCA5sOPxmZ/EqDLj2x0Q0+Ygg==} + protobufjs@7.6.3: + resolution: {integrity: sha512-+k0vdJKNdW+Vu+dYe8tZA/VvQb6XKNWexC6URwBFXxNnjLJz9nQJCemGyNgRAWD+B7+nGNc9qMPGwcD7s4nzUw==} engines: {node: '>=12.0.0'} proxy-from-env@2.1.0: @@ -2527,8 +2522,8 @@ packages: pstree.remy@1.1.8: resolution: {integrity: sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==} - pump@3.0.2: - resolution: {integrity: sha512-tUPXtzlGM8FE3P0ZL6DVs/3P58k9nk8/jZeQCurTJylQA8qFYzHFfhBJkuqyE0FifOsQ0uKWekiZ5g8wtr28cw==} + pump@3.0.4: + resolution: {integrity: sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==} punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} @@ -2563,12 +2558,12 @@ packages: resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} engines: {node: '>= 14.18.0'} - redis@4.7.0: - resolution: {integrity: sha512-zvmkHEAdGMn+hMRXuMBtu4Vo5P6rHQjLoHftu+lBqq8ZTA3RCVC/WzD790bkKKiNFp7d5/9PcSD19fJyyRvOdQ==} + redis@4.7.1: + resolution: {integrity: sha512-S1bJDnqLftzHXHP8JsT5II/CtHWQrASX5K96REjWjlmWKrviSOLWmM7QnRLstAWsu1VBBV1ffV6DzCvxNP0UJQ==} - redis@5.11.0: - resolution: {integrity: sha512-YwXjATVDT+AuxcyfOwZn046aml9jMlQPvU1VXIlLDVAExe0u93aTfPYSeRgG4p9Q/Jlkj+LXJ1XEoFV+j2JKcQ==} - engines: {node: '>= 18'} + redis@5.12.1: + resolution: {integrity: sha512-LDsoVvb/CpoV9EN3FXvgvSHNJWuCIzl9MiO3ppOevuGLpSGJhwfQjpEwfFJcQvNSddHADDdZaWx0HnmMxRXG7g==} + engines: {node: '>= 18.19.0'} require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} @@ -2586,8 +2581,8 @@ packages: resolution: {integrity: sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==} engines: {node: '>=10'} - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true @@ -2603,8 +2598,8 @@ packages: resolution: {integrity: sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==} hasBin: true - rollup@4.60.4: - resolution: {integrity: sha512-WHeFSbZYsPu3+bLoNRUuAO+wavNlocOPf3wSHTP7hcFKVnJeWsYlCDbr3mTS14FCizf9ccIxXA8sGL8zKeQN3g==} + rollup@4.61.1: + resolution: {integrity: sha512-I4KW6iuRpuu2uHBLraZ1wNZe0DP7lnRha+VJ9tNaYVaVgKhW0aI3h4RYnoRPeql0flHm/Co55b7snEDcOfOJrA==} engines: {node: '>=18.0.0', npm: '>=8.0.0'} hasBin: true @@ -2622,15 +2617,12 @@ packages: resolution: {integrity: sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==} engines: {node: '>=10'} - safer-buffer@2.1.2: - resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} - semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.1: - resolution: {integrity: sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==} + semver@7.8.4: + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} engines: {node: '>=10'} hasBin: true @@ -2669,10 +2661,6 @@ packages: resolution: {integrity: sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==} engines: {node: '>=8'} - source-map-js@1.2.1: - resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} - engines: {node: '>=0.10.0'} - source-map-support@0.5.13: resolution: {integrity: sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==} @@ -2680,10 +2668,9 @@ packages: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} - source-map@0.8.0-beta.0: - resolution: {integrity: sha512-2ymg6oRBpebeZi9UUNsgQ89bhx01TcTkmNTGnNO88imTmbSgy4nfujrgVEFKWpMTEGA11EDkTt7mqObTPdigIA==} - engines: {node: '>= 8'} - deprecated: The work that was done in this beta branch won't be included in future versions + source-map@0.7.6: + resolution: {integrity: sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==} + engines: {node: '>= 12'} sparse-bitfield@3.0.3: resolution: {integrity: sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==} @@ -2697,16 +2684,13 @@ packages: spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} - spdx-license-ids@3.0.21: - resolution: {integrity: sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==} + spdx-license-ids@3.0.23: + resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} split2@4.2.0: resolution: {integrity: sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==} engines: {node: '>= 10.x'} - sprintf-js@1.0.3: - resolution: {integrity: sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==} - stack-utils@2.0.6: resolution: {integrity: sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==} engines: {node: '>=10'} @@ -2734,8 +2718,8 @@ packages: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} + strip-ansi@7.2.0: + resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} strip-bom@4.0.0: @@ -2754,8 +2738,8 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true @@ -2803,8 +2787,8 @@ packages: tinyexec@0.3.2: resolution: {integrity: sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==} - tinyglobby@0.2.12: - resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} tmpl@1.0.5: @@ -2821,9 +2805,6 @@ packages: tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - tr46@1.0.1: - resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} - tr46@5.1.1: resolution: {integrity: sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw==} engines: {node: '>=18'} @@ -2835,18 +2816,19 @@ packages: ts-interface-checker@0.1.13: resolution: {integrity: sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==} - ts-jest@29.2.6: - resolution: {integrity: sha512-yTNZVZqc8lSixm+QGVFcPe6+yj7+TWZwIesuOWvfcn4B9bz5x4NDzVCQQjOs7Hfouu36aEqfEbo9Qpo+gq8dDg==} + ts-jest@29.4.11: + resolution: {integrity: sha512-IrFl7l9AuB/qrNw5quqvAv/hmKMb8dhWOH4jQOGo0Oq8tCeo1O86/iTFG1FaRimgUkF13l4PcepO8ATFT6Ns4g==} engines: {node: ^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0} hasBin: true peerDependencies: '@babel/core': '>=7.0.0-beta.0 <8' - '@jest/transform': ^29.0.0 - '@jest/types': ^29.0.0 - babel-jest: ^29.0.0 + '@jest/transform': ^29.0.0 || ^30.0.0 + '@jest/types': ^29.0.0 || ^30.0.0 + babel-jest: ^29.0.0 || ^30.0.0 esbuild: '*' - jest: ^29.0.0 - typescript: '>=4.3 <6' + jest: ^29.0.0 || ^30.0.0 + jest-util: ^29.0.0 || ^30.0.0 + typescript: '>=4.3 <7' peerDependenciesMeta: '@babel/core': optional: true @@ -2858,6 +2840,8 @@ packages: optional: true esbuild: optional: true + jest-util: + optional: true ts-node@10.9.2: resolution: {integrity: sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==} @@ -2876,14 +2860,14 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - tsup@8.4.0: - resolution: {integrity: sha512-b+eZbPCjz10fRryaAA7C8xlIHnf8VnsaRqydheLIqwG/Mcpfk8Z5zp3HayX7GaTygkigHl5cBUs+IhcySiIexQ==} + 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 + postcss: '>=8.5.10' typescript: '>=4.5.0' peerDependenciesMeta: '@microsoft/api-extractor': @@ -2910,8 +2894,8 @@ packages: resolution: {integrity: sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==} engines: {node: '>=14.16'} - type-fest@4.35.0: - resolution: {integrity: sha512-2/AwEFQDFEy30iOLjrvHDIH7e4HEWH+f1Yl1bI5XMqzuoCUqwYCdxachgsgv0og/JdVZUhbfjcJAoHj5L1753A==} + type-fest@4.41.0: + resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} typescript@5.5.4: @@ -2919,6 +2903,14 @@ packages: engines: {node: '>=14.17'} hasBin: true + ufo@1.6.4: + resolution: {integrity: sha512-JFNbkD1Svwe0KvGi8GOeLcP4kAWQ609twvCdcHxq1oSL8svv39ZuSvajcD8B+5D0eL4+s1Is2D/O6KN3qcTeRA==} + + uglify-js@3.19.3: + resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} + engines: {node: '>=0.8.0'} + hasBin: true + undefsafe@2.0.5: resolution: {integrity: sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==} @@ -2928,15 +2920,15 @@ packages: undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} - undici-types@6.20.0: - resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} undici@6.26.0: resolution: {integrity: sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A==} engines: {node: '>=18.17'} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -2944,18 +2936,12 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@13.0.0: - resolution: {integrity: sha512-XQegIaBTVUjSHliKqcnFqYypAd4S+WCYt5NIeRs6w/UAry7z8Y9j5ZwRRL4kzq9U3sD6v+85er9FvkEaBpji2w==} + uuid@11.1.1: + resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} hasBin: true - uuid@8.3.2: - resolution: {integrity: sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==} - deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). - hasBin: true - - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + uuid@13.0.2: + resolution: {integrity: sha512-vzi9uRZ926x4XV73S/4qQaTwPXM2JBj6/6lI/byHH1jOpCzb0zDbfytgA9LcN/hzb2l7WQSQnxITOVx5un/wGw==} hasBin: true v8-compile-cache-lib@3.0.1: @@ -2982,9 +2968,6 @@ packages: webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - webidl-conversions@4.0.2: - resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} - webidl-conversions@7.0.0: resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} engines: {node: '>=12'} @@ -2999,9 +2982,6 @@ packages: whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} - whatwg-url@7.1.0: - resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==} - which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} @@ -3011,6 +2991,9 @@ packages: resolution: {integrity: sha512-zVyFsvE+mq9MCmwXUWHIcpfbrHHClZWZiVOzKSxNJruIcFn2RbY55zkhiAMMxM8zCVSmtNiViq8FsAZSFpMYag==} engines: {node: '>=0.6.0'} + wordwrap@1.0.0: + resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} + wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} @@ -3026,8 +3009,8 @@ packages: resolution: {integrity: sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==} engines: {node: ^12.13.0 || ^14.15.0 || >=16.0.0} - ws@8.18.1: - resolution: {integrity: sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w==} + ws@8.21.0: + resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 @@ -3072,33 +3055,25 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} - zod-to-json-schema@3.24.5: - resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} + zod-to-json-schema@3.25.2: + resolution: {integrity: sha512-O/PgfnpT1xKSDeQYSCfRI5Gy3hPf91mKVDuYLUHZJMiDFptvP41MSnWofm8dnCm0256ZNfZIM7DSzuSMAFnjHA==} peerDependencies: - zod: ^3.24.1 - - zod@3.24.2: - resolution: {integrity: sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ==} + zod: ^3.25.28 || ^4 zod@3.25.76: resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} snapshots: - '@ampproject/remapping@2.3.0': + '@anthropic-ai/sdk@0.40.1': dependencies: - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 - - '@anthropic-ai/sdk@0.40.1(encoding@0.1.13)': - dependencies: - '@types/node': 18.19.76 - '@types/node-fetch': 2.6.12 + '@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(encoding@0.1.13) + node-fetch: 2.7.0 transitivePeerDependencies: - encoding @@ -3114,11 +3089,11 @@ snapshots: transitivePeerDependencies: - supports-color - '@azure/core-client@1.10.1': + '@azure/core-client@1.10.2': dependencies: '@azure/abort-controller': 2.1.2 '@azure/core-auth': 1.10.1 - '@azure/core-rest-pipeline': 1.22.2 + '@azure/core-rest-pipeline': 1.24.0 '@azure/core-tracing': 1.3.1 '@azure/core-util': 1.13.1 '@azure/logger': 1.3.0 @@ -3126,26 +3101,24 @@ snapshots: transitivePeerDependencies: - supports-color - '@azure/core-http-compat@2.3.1': + '@azure/core-http-compat@2.4.0(@azure/core-client@1.10.2)(@azure/core-rest-pipeline@1.24.0)': dependencies: '@azure/abort-controller': 2.1.2 - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.22.2 - transitivePeerDependencies: - - supports-color + '@azure/core-client': 1.10.2 + '@azure/core-rest-pipeline': 1.24.0 '@azure/core-paging@1.6.2': dependencies: tslib: 2.8.1 - '@azure/core-rest-pipeline@1.22.2': + '@azure/core-rest-pipeline@1.24.0': dependencies: '@azure/abort-controller': 2.1.2 '@azure/core-auth': 1.10.1 '@azure/core-tracing': 1.3.1 '@azure/core-util': 1.13.1 '@azure/logger': 1.3.0 - '@typespec/ts-http-runtime': 0.3.2 + '@typespec/ts-http-runtime': 0.3.6 tslib: 2.8.1 transitivePeerDependencies: - supports-color @@ -3157,22 +3130,22 @@ snapshots: '@azure/core-util@1.13.1': dependencies: '@azure/abort-controller': 2.1.2 - '@typespec/ts-http-runtime': 0.3.2 + '@typespec/ts-http-runtime': 0.3.6 tslib: 2.8.1 transitivePeerDependencies: - supports-color - '@azure/identity@4.13.0': + '@azure/identity@4.13.1': dependencies: '@azure/abort-controller': 2.1.2 '@azure/core-auth': 1.10.1 - '@azure/core-client': 1.10.1 - '@azure/core-rest-pipeline': 1.22.2 + '@azure/core-client': 1.10.2 + '@azure/core-rest-pipeline': 1.24.0 '@azure/core-tracing': 1.3.1 '@azure/core-util': 1.13.1 '@azure/logger': 1.3.0 - '@azure/msal-browser': 4.27.0 - '@azure/msal-node': 3.8.4 + '@azure/msal-browser': 5.13.0 + '@azure/msal-node': 5.2.4 open: 10.2.0 tslib: 2.8.1 transitivePeerDependencies: @@ -3180,30 +3153,29 @@ snapshots: '@azure/logger@1.3.0': dependencies: - '@typespec/ts-http-runtime': 0.3.2 + '@typespec/ts-http-runtime': 0.3.6 tslib: 2.8.1 transitivePeerDependencies: - supports-color - '@azure/msal-browser@4.27.0': + '@azure/msal-browser@5.13.0': dependencies: - '@azure/msal-common': 15.13.3 + '@azure/msal-common': 16.8.0 - '@azure/msal-common@15.13.3': {} + '@azure/msal-common@16.8.0': {} - '@azure/msal-node@3.8.4': + '@azure/msal-node@5.2.4': dependencies: - '@azure/msal-common': 15.13.3 + '@azure/msal-common': 16.8.0 jsonwebtoken: 9.0.3 - uuid: 8.3.2 '@azure/search-documents@12.2.0': dependencies: '@azure/core-auth': 1.10.1 - '@azure/core-client': 1.10.1 - '@azure/core-http-compat': 2.3.1 + '@azure/core-client': 1.10.2 + '@azure/core-http-compat': 2.4.0(@azure/core-client@1.10.2)(@azure/core-rest-pipeline@1.24.0) '@azure/core-paging': 1.6.2 - '@azure/core-rest-pipeline': 1.22.2 + '@azure/core-rest-pipeline': 1.24.0 '@azure/core-tracing': 1.3.1 '@azure/core-util': 1.13.1 '@azure/logger': 1.3.0 @@ -3212,282 +3184,287 @@ snapshots: transitivePeerDependencies: - supports-color - '@babel/code-frame@7.26.2': + '@babel/code-frame@7.29.7': dependencies: - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 - '@babel/compat-data@7.26.8': {} + '@babel/compat-data@7.29.7': {} - '@babel/core@7.26.9': + '@babel/core@7.29.7': dependencies: - '@ampproject/remapping': 2.3.0 - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.9 - '@babel/helper-compilation-targets': 7.26.5 - '@babel/helper-module-transforms': 7.26.0(@babel/core@7.26.9) - '@babel/helpers': 7.26.9 - '@babel/parser': 7.26.9 - '@babel/template': 7.26.9 - '@babel/traverse': 7.26.9 - '@babel/types': 7.26.9 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-compilation-targets': 7.29.7 + '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) + '@babel/helpers': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color - '@babel/generator@7.26.9': + '@babel/generator@7.29.7': dependencies: - '@babel/parser': 7.26.9 - '@babel/types': 7.26.9 - '@jridgewell/gen-mapping': 0.3.8 - '@jridgewell/trace-mapping': 0.3.25 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 - '@babel/helper-compilation-targets@7.26.5': + '@babel/helper-compilation-targets@7.29.7': dependencies: - '@babel/compat-data': 7.26.8 - '@babel/helper-validator-option': 7.25.9 - browserslist: 4.24.4 + '@babel/compat-data': 7.29.7 + '@babel/helper-validator-option': 7.29.7 + browserslist: 4.28.2 lru-cache: 5.1.1 semver: 6.3.1 - '@babel/helper-module-imports@7.25.9': + '@babel/helper-globals@7.29.7': {} + + '@babel/helper-module-imports@7.29.7': dependencies: - '@babel/traverse': 7.26.9 - '@babel/types': 7.26.9 + '@babel/traverse': 7.29.7 + '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-module-transforms@7.26.0(@babel/core@7.26.9)': + '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-module-imports': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 - '@babel/traverse': 7.26.9 + '@babel/core': 7.29.7 + '@babel/helper-module-imports': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 + '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color - '@babel/helper-plugin-utils@7.26.5': {} + '@babel/helper-plugin-utils@7.29.7': {} - '@babel/helper-string-parser@7.25.9': {} + '@babel/helper-string-parser@7.29.7': {} - '@babel/helper-validator-identifier@7.25.9': {} + '@babel/helper-validator-identifier@7.29.7': {} - '@babel/helper-validator-option@7.25.9': {} + '@babel/helper-validator-option@7.29.7': {} - '@babel/helpers@7.26.9': + '@babel/helpers@7.29.7': dependencies: - '@babel/template': 7.26.9 - '@babel/types': 7.26.9 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 - '@babel/parser@7.26.9': + '@babel/parser@7.29.7': dependencies: - '@babel/types': 7.26.9 + '@babel/types': 7.29.7 - '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.26.9)': + '@babel/plugin-syntax-async-generators@7.8.4(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.26.9)': + '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.26.9)': + '@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.26.9)': + '@babel/plugin-syntax-class-static-block@7.14.5(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-import-attributes@7.26.0(@babel/core@7.26.9)': + '@babel/plugin-syntax-import-attributes@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.26.9)': + '@babel/plugin-syntax-import-meta@7.10.4(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.26.9)': + '@babel/plugin-syntax-json-strings@7.8.3(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-jsx@7.25.9(@babel/core@7.26.9)': + '@babel/plugin-syntax-jsx@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.26.9)': + '@babel/plugin-syntax-logical-assignment-operators@7.10.4(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.26.9)': + '@babel/plugin-syntax-nullish-coalescing-operator@7.8.3(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.26.9)': + '@babel/plugin-syntax-numeric-separator@7.10.4(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.26.9)': + '@babel/plugin-syntax-object-rest-spread@7.8.3(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.26.9)': + '@babel/plugin-syntax-optional-catch-binding@7.8.3(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.26.9)': + '@babel/plugin-syntax-optional-chaining@7.8.3(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.26.9)': + '@babel/plugin-syntax-private-property-in-object@7.14.5(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.26.9)': + '@babel/plugin-syntax-top-level-await@7.14.5(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/plugin-syntax-typescript@7.25.9(@babel/core@7.26.9)': + '@babel/plugin-syntax-typescript@7.29.7(@babel/core@7.29.7)': dependencies: - '@babel/core': 7.26.9 - '@babel/helper-plugin-utils': 7.26.5 + '@babel/core': 7.29.7 + '@babel/helper-plugin-utils': 7.29.7 - '@babel/template@7.26.9': + '@babel/template@7.29.7': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/parser': 7.26.9 - '@babel/types': 7.26.9 + '@babel/code-frame': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 - '@babel/traverse@7.26.9': + '@babel/traverse@7.29.7': dependencies: - '@babel/code-frame': 7.26.2 - '@babel/generator': 7.26.9 - '@babel/parser': 7.26.9 - '@babel/template': 7.26.9 - '@babel/types': 7.26.9 - debug: 4.4.0(supports-color@5.5.0) - globals: 11.12.0 + '@babel/code-frame': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/helper-globals': 7.29.7 + '@babel/parser': 7.29.7 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color - '@babel/types@7.26.9': + '@babel/types@7.29.7': dependencies: - '@babel/helper-string-parser': 7.25.9 - '@babel/helper-validator-identifier': 7.25.9 + '@babel/helper-string-parser': 7.29.7 + '@babel/helper-validator-identifier': 7.29.7 '@bcoe/v8-coverage@0.2.3': {} '@cfworker/json-schema@4.1.1': {} - '@cloudflare/workers-types@4.20250606.0': {} + '@cloudflare/workers-types@4.20260611.1': {} '@cspotcode/source-map-support@0.8.1': dependencies: '@jridgewell/trace-mapping': 0.3.9 - '@esbuild/aix-ppc64@0.25.1': + '@esbuild/aix-ppc64@0.27.7': optional: true - '@esbuild/android-arm64@0.25.1': + '@esbuild/android-arm64@0.27.7': optional: true - '@esbuild/android-arm@0.25.1': + '@esbuild/android-arm@0.27.7': optional: true - '@esbuild/android-x64@0.25.1': + '@esbuild/android-x64@0.27.7': optional: true - '@esbuild/darwin-arm64@0.25.1': + '@esbuild/darwin-arm64@0.27.7': optional: true - '@esbuild/darwin-x64@0.25.1': + '@esbuild/darwin-x64@0.27.7': optional: true - '@esbuild/freebsd-arm64@0.25.1': + '@esbuild/freebsd-arm64@0.27.7': optional: true - '@esbuild/freebsd-x64@0.25.1': + '@esbuild/freebsd-x64@0.27.7': optional: true - '@esbuild/linux-arm64@0.25.1': + '@esbuild/linux-arm64@0.27.7': optional: true - '@esbuild/linux-arm@0.25.1': + '@esbuild/linux-arm@0.27.7': optional: true - '@esbuild/linux-ia32@0.25.1': + '@esbuild/linux-ia32@0.27.7': optional: true - '@esbuild/linux-loong64@0.25.1': + '@esbuild/linux-loong64@0.27.7': optional: true - '@esbuild/linux-mips64el@0.25.1': + '@esbuild/linux-mips64el@0.27.7': optional: true - '@esbuild/linux-ppc64@0.25.1': + '@esbuild/linux-ppc64@0.27.7': optional: true - '@esbuild/linux-riscv64@0.25.1': + '@esbuild/linux-riscv64@0.27.7': optional: true - '@esbuild/linux-s390x@0.25.1': + '@esbuild/linux-s390x@0.27.7': optional: true - '@esbuild/linux-x64@0.25.1': + '@esbuild/linux-x64@0.27.7': optional: true - '@esbuild/netbsd-arm64@0.25.1': + '@esbuild/netbsd-arm64@0.27.7': optional: true - '@esbuild/netbsd-x64@0.25.1': + '@esbuild/netbsd-x64@0.27.7': optional: true - '@esbuild/openbsd-arm64@0.25.1': + '@esbuild/openbsd-arm64@0.27.7': optional: true - '@esbuild/openbsd-x64@0.25.1': + '@esbuild/openbsd-x64@0.27.7': optional: true - '@esbuild/sunos-x64@0.25.1': + '@esbuild/openharmony-arm64@0.27.7': optional: true - '@esbuild/win32-arm64@0.25.1': + '@esbuild/sunos-x64@0.27.7': optional: true - '@esbuild/win32-ia32@0.25.1': + '@esbuild/win32-arm64@0.27.7': optional: true - '@esbuild/win32-x64@0.25.1': + '@esbuild/win32-ia32@0.27.7': + optional: true + + '@esbuild/win32-x64@0.27.7': optional: true '@google/genai@1.52.0': dependencies: - google-auth-library: 10.6.2 + google-auth-library: 10.7.0 p-retry: 4.6.2 - protobufjs: 7.6.1 - ws: 8.18.1 + protobufjs: 7.6.3 + ws: 8.21.0 transitivePeerDependencies: - bufferutil - supports-color @@ -3497,7 +3474,7 @@ snapshots: dependencies: string-width: 5.1.2 string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 + strip-ansi: 7.2.0 strip-ansi-cjs: strip-ansi@6.0.1 wrap-ansi: 8.1.0 wrap-ansi-cjs: wrap-ansi@7.0.0 @@ -3507,35 +3484,35 @@ snapshots: camelcase: 5.3.1 find-up: 4.1.0 get-package-type: 0.1.0 - js-yaml: 3.14.1 + js-yaml: 4.2.0 resolve-from: 5.0.0 - '@istanbuljs/schema@0.1.3': {} + '@istanbuljs/schema@0.1.6': {} '@jest/console@29.7.0': dependencies: '@jest/types': 29.6.3 - '@types/node': 22.13.5 + '@types/node': 22.19.21 chalk: 4.1.2 jest-message-util: 29.7.0 jest-util: 29.7.0 slash: 3.0.0 - '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4))': + '@jest/core@29.7.0(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4))': dependencies: '@jest/console': 29.7.0 '@jest/reporters': 29.7.0 '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.5 + '@types/node': 22.19.21 ansi-escapes: 4.3.2 chalk: 4.1.2 ci-info: 3.9.0 exit: 0.1.2 graceful-fs: 4.2.11 jest-changed-files: 29.7.0 - jest-config: 29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)) + jest-config: 29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)) jest-haste-map: 29.7.0 jest-message-util: 29.7.0 jest-regex-util: 29.6.3 @@ -3560,7 +3537,7 @@ snapshots: dependencies: '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.5 + '@types/node': 22.19.21 jest-mock: 29.7.0 '@jest/expect-utils@29.7.0': @@ -3578,7 +3555,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@sinonjs/fake-timers': 10.3.0 - '@types/node': 22.13.5 + '@types/node': 22.19.21 jest-message-util: 29.7.0 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -3599,10 +3576,10 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 - '@types/node': 22.13.5 + '@jridgewell/trace-mapping': 0.3.31 + '@types/node': 22.19.21 chalk: 4.1.2 - collect-v8-coverage: 1.0.2 + collect-v8-coverage: 1.0.3 exit: 0.1.2 glob: 7.2.3 graceful-fs: 4.2.11 @@ -3610,7 +3587,7 @@ snapshots: istanbul-lib-instrument: 6.0.3 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.7 + istanbul-reports: 3.2.0 jest-message-util: 29.7.0 jest-util: 29.7.0 jest-worker: 29.7.0 @@ -3623,11 +3600,11 @@ snapshots: '@jest/schemas@29.6.3': dependencies: - '@sinclair/typebox': 0.27.8 + '@sinclair/typebox': 0.27.10 '@jest/source-map@29.6.3': dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.31 callsites: 3.1.0 graceful-fs: 4.2.11 @@ -3636,7 +3613,7 @@ snapshots: '@jest/console': 29.7.0 '@jest/types': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 - collect-v8-coverage: 1.0.2 + collect-v8-coverage: 1.0.3 '@jest/test-sequencer@29.7.0': dependencies: @@ -3647,9 +3624,9 @@ snapshots: '@jest/transform@29.7.0': dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.29.7 '@jest/types': 29.6.3 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.31 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 convert-source-map: 2.0.0 @@ -3659,7 +3636,7 @@ snapshots: jest-regex-util: 29.6.3 jest-util: 29.7.0 micromatch: 4.0.8 - pirates: 4.0.6 + pirates: 4.0.7 slash: 3.0.0 write-file-atomic: 4.0.2 transitivePeerDependencies: @@ -3670,38 +3647,40 @@ snapshots: '@jest/schemas': 29.6.3 '@types/istanbul-lib-coverage': 2.0.6 '@types/istanbul-reports': 3.0.4 - '@types/node': 22.13.5 - '@types/yargs': 17.0.33 + '@types/node': 22.19.21 + '@types/yargs': 17.0.35 chalk: 4.1.2 - '@jridgewell/gen-mapping@0.3.8': + '@jridgewell/gen-mapping@0.3.13': dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 + + '@jridgewell/remapping@2.3.5': + dependencies: + '@jridgewell/gen-mapping': 0.3.13 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/set-array@1.2.1': {} + '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/sourcemap-codec@1.5.0': {} - - '@jridgewell/trace-mapping@0.3.25': + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping@0.3.9': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 - '@langchain/core@1.1.47(openai@4.93.0(encoding@0.1.13)(ws@8.18.1)(zod@3.24.2))(ws@8.18.1)': + '@langchain/core@1.1.48(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0)': dependencies: '@cfworker/json-schema': 4.1.1 '@standard-schema/spec': 1.1.0 - js-tiktoken: 1.0.19 - langsmith: 0.6.3(openai@4.93.0(encoding@0.1.13)(ws@8.18.1)(zod@3.24.2))(ws@8.18.1) + js-tiktoken: 1.0.21 + langsmith: 0.6.3(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0) mustache: 4.2.0 p-queue: 6.6.2 zod: 3.25.76 @@ -3712,12 +3691,16 @@ snapshots: - openai - ws - '@mistralai/mistralai@1.5.2(zod@3.24.2)': + '@mistralai/mistralai@1.15.1': dependencies: - zod: 3.24.2 - zod-to-json-schema: 3.24.5(zod@3.24.2) + ws: 8.21.0 + zod: 3.25.76 + zod-to-json-schema: 3.25.2(zod@3.25.76) + transitivePeerDependencies: + - bufferutil + - utf-8-validate - '@mongodb-js/saslprep@1.4.6': + '@mongodb-js/saslprep@1.4.11': dependencies: sparse-bitfield: 3.0.3 @@ -3731,7 +3714,7 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + fastq: 1.20.1 '@pkgjs/parseargs@0.11.0': optional: true @@ -3766,128 +3749,128 @@ snapshots: '@qdrant/openapi-typescript-fetch@1.2.6': {} - '@redis/bloom@1.2.0(@redis/client@1.6.0)': + '@redis/bloom@1.2.0(@redis/client@1.6.1)': dependencies: - '@redis/client': 1.6.0 + '@redis/client': 1.6.1 - '@redis/bloom@5.11.0(@redis/client@5.11.0)': + '@redis/bloom@5.12.1(@redis/client@5.12.1)': dependencies: - '@redis/client': 5.11.0 + '@redis/client': 5.12.1 - '@redis/client@1.6.0': + '@redis/client@1.6.1': dependencies: cluster-key-slot: 1.1.2 generic-pool: 3.9.0 yallist: 4.0.0 - '@redis/client@5.11.0': + '@redis/client@5.12.1': dependencies: cluster-key-slot: 1.1.2 - '@redis/graph@1.1.1(@redis/client@1.6.0)': + '@redis/graph@1.1.1(@redis/client@1.6.1)': dependencies: - '@redis/client': 1.6.0 + '@redis/client': 1.6.1 - '@redis/json@1.0.7(@redis/client@1.6.0)': + '@redis/json@1.0.7(@redis/client@1.6.1)': dependencies: - '@redis/client': 1.6.0 + '@redis/client': 1.6.1 - '@redis/json@5.11.0(@redis/client@5.11.0)': + '@redis/json@5.12.1(@redis/client@5.12.1)': dependencies: - '@redis/client': 5.11.0 + '@redis/client': 5.12.1 - '@redis/search@1.2.0(@redis/client@1.6.0)': + '@redis/search@1.2.0(@redis/client@1.6.1)': dependencies: - '@redis/client': 1.6.0 + '@redis/client': 1.6.1 - '@redis/search@5.11.0(@redis/client@5.11.0)': + '@redis/search@5.12.1(@redis/client@5.12.1)': dependencies: - '@redis/client': 5.11.0 + '@redis/client': 5.12.1 - '@redis/time-series@1.1.0(@redis/client@1.6.0)': + '@redis/time-series@1.1.0(@redis/client@1.6.1)': dependencies: - '@redis/client': 1.6.0 + '@redis/client': 1.6.1 - '@redis/time-series@5.11.0(@redis/client@5.11.0)': + '@redis/time-series@5.12.1(@redis/client@5.12.1)': dependencies: - '@redis/client': 5.11.0 + '@redis/client': 5.12.1 - '@rollup/rollup-android-arm-eabi@4.60.4': + '@rollup/rollup-android-arm-eabi@4.61.1': optional: true - '@rollup/rollup-android-arm64@4.60.4': + '@rollup/rollup-android-arm64@4.61.1': optional: true - '@rollup/rollup-darwin-arm64@4.60.4': + '@rollup/rollup-darwin-arm64@4.61.1': optional: true - '@rollup/rollup-darwin-x64@4.60.4': + '@rollup/rollup-darwin-x64@4.61.1': optional: true - '@rollup/rollup-freebsd-arm64@4.60.4': + '@rollup/rollup-freebsd-arm64@4.61.1': optional: true - '@rollup/rollup-freebsd-x64@4.60.4': + '@rollup/rollup-freebsd-x64@4.61.1': optional: true - '@rollup/rollup-linux-arm-gnueabihf@4.60.4': + '@rollup/rollup-linux-arm-gnueabihf@4.61.1': optional: true - '@rollup/rollup-linux-arm-musleabihf@4.60.4': + '@rollup/rollup-linux-arm-musleabihf@4.61.1': optional: true - '@rollup/rollup-linux-arm64-gnu@4.60.4': + '@rollup/rollup-linux-arm64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-arm64-musl@4.60.4': + '@rollup/rollup-linux-arm64-musl@4.61.1': optional: true - '@rollup/rollup-linux-loong64-gnu@4.60.4': + '@rollup/rollup-linux-loong64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-loong64-musl@4.60.4': + '@rollup/rollup-linux-loong64-musl@4.61.1': optional: true - '@rollup/rollup-linux-ppc64-gnu@4.60.4': + '@rollup/rollup-linux-ppc64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-ppc64-musl@4.60.4': + '@rollup/rollup-linux-ppc64-musl@4.61.1': optional: true - '@rollup/rollup-linux-riscv64-gnu@4.60.4': + '@rollup/rollup-linux-riscv64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-riscv64-musl@4.60.4': + '@rollup/rollup-linux-riscv64-musl@4.61.1': optional: true - '@rollup/rollup-linux-s390x-gnu@4.60.4': + '@rollup/rollup-linux-s390x-gnu@4.61.1': optional: true - '@rollup/rollup-linux-x64-gnu@4.60.4': + '@rollup/rollup-linux-x64-gnu@4.61.1': optional: true - '@rollup/rollup-linux-x64-musl@4.60.4': + '@rollup/rollup-linux-x64-musl@4.61.1': optional: true - '@rollup/rollup-openbsd-x64@4.60.4': + '@rollup/rollup-openbsd-x64@4.61.1': optional: true - '@rollup/rollup-openharmony-arm64@4.60.4': + '@rollup/rollup-openharmony-arm64@4.61.1': optional: true - '@rollup/rollup-win32-arm64-msvc@4.60.4': + '@rollup/rollup-win32-arm64-msvc@4.61.1': optional: true - '@rollup/rollup-win32-ia32-msvc@4.60.4': + '@rollup/rollup-win32-ia32-msvc@4.61.1': optional: true - '@rollup/rollup-win32-x64-gnu@4.60.4': + '@rollup/rollup-win32-x64-gnu@4.61.1': optional: true - '@rollup/rollup-win32-x64-msvc@4.60.4': + '@rollup/rollup-win32-x64-msvc@4.61.1': optional: true - '@sinclair/typebox@0.27.8': {} + '@sinclair/typebox@0.27.10': {} '@sinonjs/commons@3.0.1': dependencies: @@ -3899,49 +3882,39 @@ snapshots: '@standard-schema/spec@1.1.0': {} - '@supabase/auth-js@2.68.0': + '@supabase/auth-js@2.108.1': dependencies: - '@supabase/node-fetch': 2.6.15 + tslib: 2.8.1 - '@supabase/functions-js@2.4.4': + '@supabase/functions-js@2.108.1': dependencies: - '@supabase/node-fetch': 2.6.15 + tslib: 2.8.1 - '@supabase/node-fetch@2.6.15': + '@supabase/phoenix@0.4.2': {} + + '@supabase/postgrest-js@2.108.1': dependencies: - whatwg-url: 5.0.0 + tslib: 2.8.1 - '@supabase/postgrest-js@1.19.2': + '@supabase/realtime-js@2.108.1': dependencies: - '@supabase/node-fetch': 2.6.15 + '@supabase/phoenix': 0.4.2 + tslib: 2.8.1 - '@supabase/realtime-js@2.11.2': + '@supabase/storage-js@2.108.1': dependencies: - '@supabase/node-fetch': 2.6.15 - '@types/phoenix': 1.6.6 - '@types/ws': 8.18.0 - ws: 8.18.1 - transitivePeerDependencies: - - bufferutil - - utf-8-validate + iceberg-js: 0.8.1 + tslib: 2.8.1 - '@supabase/storage-js@2.7.1': + '@supabase/supabase-js@2.108.1': dependencies: - '@supabase/node-fetch': 2.6.15 + '@supabase/auth-js': 2.108.1 + '@supabase/functions-js': 2.108.1 + '@supabase/postgrest-js': 2.108.1 + '@supabase/realtime-js': 2.108.1 + '@supabase/storage-js': 2.108.1 - '@supabase/supabase-js@2.49.1': - dependencies: - '@supabase/auth-js': 2.68.0 - '@supabase/functions-js': 2.4.4 - '@supabase/node-fetch': 2.6.15 - '@supabase/postgrest-js': 1.19.2 - '@supabase/realtime-js': 2.11.2 - '@supabase/storage-js': 2.7.1 - transitivePeerDependencies: - - bufferutil - - utf-8-validate - - '@tsconfig/node10@1.0.11': {} + '@tsconfig/node10@1.0.12': {} '@tsconfig/node12@1.0.11': {} @@ -3951,34 +3924,34 @@ snapshots: '@types/babel__core@7.20.5': dependencies: - '@babel/parser': 7.26.9 - '@babel/types': 7.26.9 - '@types/babel__generator': 7.6.8 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 + '@types/babel__generator': 7.27.0 '@types/babel__template': 7.4.4 - '@types/babel__traverse': 7.20.6 + '@types/babel__traverse': 7.28.0 - '@types/babel__generator@7.6.8': + '@types/babel__generator@7.27.0': dependencies: - '@babel/types': 7.26.9 + '@babel/types': 7.29.7 '@types/babel__template@7.4.4': dependencies: - '@babel/parser': 7.26.9 - '@babel/types': 7.26.9 + '@babel/parser': 7.29.7 + '@babel/types': 7.29.7 - '@types/babel__traverse@7.20.6': + '@types/babel__traverse@7.28.0': dependencies: - '@babel/types': 7.26.9 + '@babel/types': 7.29.7 '@types/better-sqlite3@7.6.13': dependencies: - '@types/node': 22.13.5 + '@types/node': 22.19.21 - '@types/estree@1.0.8': {} + '@types/estree@1.0.9': {} '@types/graceful-fs@4.1.9': dependencies: - '@types/node': 22.13.5 + '@types/node': 22.19.21 '@types/istanbul-lib-coverage@2.0.6': {} @@ -3995,28 +3968,26 @@ snapshots: expect: 29.7.0 pretty-format: 29.7.0 - '@types/node-fetch@2.6.12': + '@types/node-fetch@2.6.13': dependencies: - '@types/node': 22.13.5 + '@types/node': 22.19.21 form-data: 4.0.5 - '@types/node@18.19.76': + '@types/node@18.19.130': dependencies: undici-types: 5.26.5 - '@types/node@22.13.5': + '@types/node@22.19.21': dependencies: - undici-types: 6.20.0 + undici-types: 6.21.0 '@types/normalize-package-data@2.4.4': {} '@types/pg@8.11.0': dependencies: - '@types/node': 22.13.5 - pg-protocol: 1.7.1 - pg-types: 4.0.2 - - '@types/phoenix@1.6.6': {} + '@types/node': 22.19.21 + pg-protocol: 1.14.0 + pg-types: 4.1.0 '@types/retry@0.12.0': {} @@ -4030,17 +4001,13 @@ snapshots: dependencies: '@types/webidl-conversions': 7.0.3 - '@types/ws@8.18.0': - dependencies: - '@types/node': 22.13.5 - '@types/yargs-parser@21.0.3': {} - '@types/yargs@17.0.33': + '@types/yargs@17.0.35': dependencies: '@types/yargs-parser': 21.0.3 - '@typespec/ts-http-runtime@0.3.2': + '@typespec/ts-http-runtime@0.3.6': dependencies: http-proxy-agent: 7.0.2 https-proxy-agent: 7.0.6 @@ -4052,11 +4019,11 @@ snapshots: dependencies: event-target-shim: 5.0.1 - acorn-walk@8.3.4: + acorn-walk@8.3.5: dependencies: - acorn: 8.14.0 + acorn: 8.16.0 - acorn@8.14.0: {} + acorn@8.16.0: {} afinn-165-financialmarketnews@3.0.0: {} @@ -4064,11 +4031,11 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color - agent-base@7.1.3: {} + agent-base@7.1.4: {} agentkeepalive@4.6.0: dependencies: @@ -4080,7 +4047,7 @@ snapshots: ansi-regex@5.0.1: {} - ansi-regex@6.1.0: {} + ansi-regex@6.2.2: {} ansi-styles@4.3.0: dependencies: @@ -4088,7 +4055,7 @@ snapshots: ansi-styles@5.2.0: {} - ansi-styles@6.2.1: {} + ansi-styles@6.2.3: {} any-promise@1.3.0: {} @@ -4103,15 +4070,11 @@ snapshots: arg@4.1.3: {} - argparse@1.0.10: - dependencies: - sprintf-js: 1.0.3 - - async@3.2.6: {} + argparse@2.0.1: {} asynckit@0.4.0: {} - axios@1.16.1: + axios@1.17.0: dependencies: follow-redirects: 1.16.0 form-data: 4.0.5 @@ -4121,13 +4084,13 @@ snapshots: - debug - supports-color - babel-jest@29.7.0(@babel/core@7.26.9): + babel-jest@29.7.0(@babel/core@7.29.7): dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.29.7 '@jest/transform': 29.7.0 '@types/babel__core': 7.20.5 babel-plugin-istanbul: 6.1.1 - babel-preset-jest: 29.6.3(@babel/core@7.26.9) + babel-preset-jest: 29.6.3(@babel/core@7.29.7) chalk: 4.1.2 graceful-fs: 4.2.11 slash: 3.0.0 @@ -4136,9 +4099,9 @@ snapshots: babel-plugin-istanbul@6.1.1: dependencies: - '@babel/helper-plugin-utils': 7.26.5 + '@babel/helper-plugin-utils': 7.29.7 '@istanbuljs/load-nyc-config': 1.1.0 - '@istanbuljs/schema': 0.1.3 + '@istanbuljs/schema': 0.1.6 istanbul-lib-instrument: 5.2.1 test-exclude: 6.0.0 transitivePeerDependencies: @@ -4146,48 +4109,52 @@ snapshots: babel-plugin-jest-hoist@29.6.3: dependencies: - '@babel/template': 7.26.9 - '@babel/types': 7.26.9 + '@babel/template': 7.29.7 + '@babel/types': 7.29.7 '@types/babel__core': 7.20.5 - '@types/babel__traverse': 7.20.6 + '@types/babel__traverse': 7.28.0 - babel-preset-current-node-syntax@1.1.0(@babel/core@7.26.9): + babel-preset-current-node-syntax@1.2.0(@babel/core@7.29.7): dependencies: - '@babel/core': 7.26.9 - '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.26.9) - '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.26.9) - '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.26.9) - '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.26.9) - '@babel/plugin-syntax-import-attributes': 7.26.0(@babel/core@7.26.9) - '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.26.9) - '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.26.9) - '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.26.9) - '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.26.9) - '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.26.9) - '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.26.9) - '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.26.9) - '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.26.9) - '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.26.9) - '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.26.9) + '@babel/core': 7.29.7 + '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.29.7) + '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.29.7) + '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.29.7) + '@babel/plugin-syntax-class-static-block': 7.14.5(@babel/core@7.29.7) + '@babel/plugin-syntax-import-attributes': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.29.7) + '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.29.7) + '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.29.7) + '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.29.7) + '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.29.7) + '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.29.7) + '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.29.7) + '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.29.7) + '@babel/plugin-syntax-private-property-in-object': 7.14.5(@babel/core@7.29.7) + '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.29.7) - babel-preset-jest@29.6.3(@babel/core@7.26.9): + babel-preset-jest@29.6.3(@babel/core@7.29.7): dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.29.7 babel-plugin-jest-hoist: 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.9) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7) balanced-match@1.0.2: {} + balanced-match@4.0.4: {} + base-64@0.1.0: {} base64-js@1.5.1: {} - better-sqlite3@12.6.2: + baseline-browser-mapping@2.10.35: {} + + better-sqlite3@12.10.0: dependencies: bindings: 1.5.0 prebuild-install: 7.1.3 - bignumber.js@9.2.0: {} + bignumber.js@9.3.1: {} binary-extensions@2.3.0: {} @@ -4201,29 +4168,30 @@ snapshots: inherits: 2.0.4 readable-stream: 3.6.2 - brace-expansion@1.1.11: + brace-expansion@1.1.15: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@2.0.1: - dependencies: - balanced-match: 1.0.2 - brace-expansion@2.1.1: dependencies: balanced-match: 1.0.2 + brace-expansion@5.0.6: + dependencies: + balanced-match: 4.0.4 + braces@3.0.3: dependencies: fill-range: 7.1.1 - browserslist@4.24.4: + browserslist@4.28.2: dependencies: - caniuse-lite: 1.0.30001701 - electron-to-chromium: 1.5.109 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.24.4) + baseline-browser-mapping: 2.10.35 + caniuse-lite: 1.0.30001799 + electron-to-chromium: 1.5.371 + node-releases: 2.0.47 + update-browserslist-db: 1.2.3(browserslist@4.28.2) bs-logger@0.2.6: dependencies: @@ -4250,9 +4218,9 @@ snapshots: dependencies: run-applescript: 7.1.0 - bundle-require@5.1.0(esbuild@0.25.1): + bundle-require@5.1.0(esbuild@0.27.7): dependencies: - esbuild: 0.25.1 + esbuild: 0.27.7 load-tsconfig: 0.2.5 cac@6.7.14: {} @@ -4268,7 +4236,7 @@ snapshots: camelcase@6.3.0: {} - caniuse-lite@1.0.30001701: {} + caniuse-lite@1.0.30001799: {} chalk@4.1.2: dependencies: @@ -4307,15 +4275,15 @@ snapshots: strip-ansi: 6.0.1 wrap-ansi: 7.0.0 - cloudflare@4.3.0(encoding@0.1.13): + cloudflare@4.5.0: dependencies: - '@types/node': 18.19.76 - '@types/node-fetch': 2.6.12 + '@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(encoding@0.1.13) + node-fetch: 2.7.0 transitivePeerDependencies: - encoding @@ -4323,7 +4291,7 @@ snapshots: co@4.6.0: {} - collect-v8-coverage@1.0.2: {} + collect-v8-coverage@1.0.3: {} color-convert@2.0.1: dependencies: @@ -4337,7 +4305,7 @@ snapshots: commander@4.1.1: {} - compromise@14.15.0: + compromise@14.15.1: dependencies: efrt: 2.7.0 grad-school: 0.0.5 @@ -4345,17 +4313,19 @@ snapshots: concat-map@0.0.1: {} + confbox@0.1.8: {} + consola@3.4.2: {} convert-source-map@2.0.0: {} - create-jest@29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)): + create-jest@29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)): dependencies: '@jest/types': 29.6.3 chalk: 4.1.2 exit: 0.1.2 graceful-fs: 4.2.11 - jest-config: 29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)) + jest-config: 29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)) jest-util: 29.7.0 prompts: 2.4.2 transitivePeerDependencies: @@ -4376,7 +4346,7 @@ snapshots: data-uri-to-buffer@4.0.1: {} - debug@4.4.0(supports-color@5.5.0): + debug@4.4.3(supports-color@5.5.0): dependencies: ms: 2.1.3 optionalDependencies: @@ -4386,7 +4356,7 @@ snapshots: dependencies: mimic-response: 3.1.0 - dedent@1.5.3: {} + dedent@1.7.2: {} deep-extend@0.6.0: {} @@ -4394,7 +4364,7 @@ snapshots: default-browser-id@5.0.1: {} - default-browser@5.4.0: + default-browser@5.5.0: dependencies: bundle-name: 4.1.0 default-browser-id: 5.0.1 @@ -4403,20 +4373,20 @@ snapshots: delayed-stream@1.0.0: {} - detect-libc@2.0.3: {} + detect-libc@2.1.2: {} detect-newline@3.1.0: {} diff-sequences@29.6.3: {} - diff@4.0.2: {} + diff@4.0.4: {} digest-fetch@1.3.0: dependencies: base-64: 0.1.0 md5: 2.3.0 - dotenv@16.4.7: {} + dotenv@16.6.1: {} dotenv@17.4.2: {} @@ -4434,11 +4404,7 @@ snapshots: efrt@2.7.0: {} - ejs@3.1.10: - dependencies: - jake: 10.9.2 - - electron-to-chromium@1.5.109: {} + electron-to-chromium@1.5.371: {} emittery@0.13.1: {} @@ -4446,16 +4412,11 @@ snapshots: emoji-regex@9.2.2: {} - encoding@0.1.13: - dependencies: - iconv-lite: 0.6.3 - optional: true - - end-of-stream@1.4.4: + end-of-stream@1.4.5: dependencies: once: 1.4.0 - error-ex@1.3.2: + error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 @@ -4463,7 +4424,7 @@ snapshots: es-errors@1.3.0: {} - es-object-atoms@1.1.1: + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 @@ -4472,42 +4433,41 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 - esbuild@0.25.1: + esbuild@0.27.7: optionalDependencies: - '@esbuild/aix-ppc64': 0.25.1 - '@esbuild/android-arm': 0.25.1 - '@esbuild/android-arm64': 0.25.1 - '@esbuild/android-x64': 0.25.1 - '@esbuild/darwin-arm64': 0.25.1 - '@esbuild/darwin-x64': 0.25.1 - '@esbuild/freebsd-arm64': 0.25.1 - '@esbuild/freebsd-x64': 0.25.1 - '@esbuild/linux-arm': 0.25.1 - '@esbuild/linux-arm64': 0.25.1 - '@esbuild/linux-ia32': 0.25.1 - '@esbuild/linux-loong64': 0.25.1 - '@esbuild/linux-mips64el': 0.25.1 - '@esbuild/linux-ppc64': 0.25.1 - '@esbuild/linux-riscv64': 0.25.1 - '@esbuild/linux-s390x': 0.25.1 - '@esbuild/linux-x64': 0.25.1 - '@esbuild/netbsd-arm64': 0.25.1 - '@esbuild/netbsd-x64': 0.25.1 - '@esbuild/openbsd-arm64': 0.25.1 - '@esbuild/openbsd-x64': 0.25.1 - '@esbuild/sunos-x64': 0.25.1 - '@esbuild/win32-arm64': 0.25.1 - '@esbuild/win32-ia32': 0.25.1 - '@esbuild/win32-x64': 0.25.1 + '@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 escalade@3.2.0: {} escape-string-regexp@2.0.0: {} - esprima@4.0.1: {} - event-target-shim@5.0.1: {} eventemitter3@4.0.7: {} @@ -4550,7 +4510,7 @@ snapshots: fast-json-stable-stringify@2.1.0: {} - fastq@1.19.1: + fastq@1.20.1: dependencies: reusify: 1.1.0 @@ -4558,9 +4518,9 @@ snapshots: dependencies: bser: 2.1.1 - fdir@6.4.3(picomatch@4.0.2): + fdir@6.5.0(picomatch@4.0.4): optionalDependencies: - picomatch: 4.0.2 + picomatch: 4.0.4 fetch-blob@3.2.0: dependencies: @@ -4569,10 +4529,6 @@ snapshots: file-uri-to-path@1.0.0: {} - filelist@1.0.4: - dependencies: - minimatch: 5.1.9 - fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 @@ -4582,6 +4538,12 @@ snapshots: locate-path: 5.0.0 path-exists: 4.0.0 + fix-dts-default-cjs-exports@1.0.1: + dependencies: + magic-string: 0.30.21 + mlly: 1.8.2 + rollup: 4.61.1 + fix-tsup-cjs@1.2.0: dependencies: cac: 6.7.14 @@ -4603,7 +4565,7 @@ snapshots: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 - hasown: 2.0.2 + hasown: 2.0.4 mime-types: 2.1.35 formdata-node@4.4.1: @@ -4624,7 +4586,7 @@ snapshots: function-bind@1.1.2: {} - gaxios@7.1.4: + gaxios@7.1.5: dependencies: extend: 3.0.2 https-proxy-agent: 7.0.6 @@ -4634,7 +4596,7 @@ snapshots: gcp-metadata@8.1.2: dependencies: - gaxios: 7.1.4 + gaxios: 7.1.5 google-logging-utils: 1.1.3 json-bigint: 1.0.0 transitivePeerDependencies: @@ -4651,12 +4613,12 @@ snapshots: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + 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.2 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-package-type@0.1.0: {} @@ -4664,7 +4626,7 @@ snapshots: get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-stream@6.0.1: {} @@ -4679,7 +4641,7 @@ snapshots: foreground-child: 3.3.1 jackspeak: 3.4.3 minimatch: 9.0.9 - minipass: 7.1.2 + minipass: 7.1.3 package-json-from-dist: 1.0.1 path-scurry: 1.11.1 @@ -4692,13 +4654,11 @@ snapshots: once: 1.4.0 path-is-absolute: 1.0.1 - globals@11.12.0: {} - - google-auth-library@10.6.2: + google-auth-library@10.7.0: dependencies: base64-js: 1.5.1 ecdsa-sig-formatter: 1.0.11 - gaxios: 7.1.4 + gaxios: 7.1.5 gcp-metadata: 8.1.2 google-logging-utils: 1.1.3 jws: 4.0.1 @@ -4713,20 +4673,29 @@ snapshots: grad-school@0.0.5: {} - groq-sdk@0.3.0(encoding@0.1.13): + groq-sdk@0.3.0: dependencies: - '@types/node': 18.19.76 - '@types/node-fetch': 2.6.12 + '@types/node': 18.19.130 + '@types/node-fetch': 2.6.13 abort-controller: 3.0.0 agentkeepalive: 4.6.0 digest-fetch: 1.3.0 form-data-encoder: 1.7.2 formdata-node: 4.4.1 - node-fetch: 2.7.0(encoding@0.1.13) + node-fetch: 2.7.0 web-streams-polyfill: 3.3.3 transitivePeerDependencies: - encoding + handlebars@4.7.9: + dependencies: + minimist: 1.2.8 + neo-async: 2.6.2 + source-map: 0.6.1 + wordwrap: 1.0.0 + optionalDependencies: + uglify-js: 3.19.3 + has-flag@3.0.0: {} has-flag@4.0.0: {} @@ -4737,7 +4706,7 @@ snapshots: dependencies: has-symbols: 1.1.0 - hasown@2.0.2: + hasown@2.0.4: dependencies: function-bind: 1.1.2 @@ -4749,22 +4718,22 @@ snapshots: http-proxy-agent@7.0.2: dependencies: - agent-base: 7.1.3 - debug: 4.4.0(supports-color@5.5.0) + agent-base: 7.1.4 + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color https-proxy-agent@7.0.6: dependencies: - agent-base: 7.1.3 - debug: 4.4.0(supports-color@5.5.0) + agent-base: 7.1.4 + debug: 4.4.3(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -4774,10 +4743,7 @@ snapshots: dependencies: ms: 2.1.3 - iconv-lite@0.6.3: - dependencies: - safer-buffer: 2.1.2 - optional: true + iceberg-js@0.8.1: {} ieee754@1.2.1: {} @@ -4807,9 +4773,9 @@ snapshots: is-buffer@1.1.6: {} - is-core-module@2.16.1: + is-core-module@2.16.2: dependencies: - hasown: 2.0.2 + hasown: 2.0.4 is-docker@3.0.0: {} @@ -4831,7 +4797,7 @@ snapshots: is-stream@2.0.1: {} - is-wsl@3.1.0: + is-wsl@3.1.1: dependencies: is-inside-container: 1.0.0 @@ -4841,9 +4807,9 @@ snapshots: istanbul-lib-instrument@5.2.1: dependencies: - '@babel/core': 7.26.9 - '@babel/parser': 7.26.9 - '@istanbuljs/schema': 0.1.3 + '@babel/core': 7.29.7 + '@babel/parser': 7.29.7 + '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 semver: 6.3.1 transitivePeerDependencies: @@ -4851,11 +4817,11 @@ snapshots: istanbul-lib-instrument@6.0.3: dependencies: - '@babel/core': 7.26.9 - '@babel/parser': 7.26.9 - '@istanbuljs/schema': 0.1.3 + '@babel/core': 7.29.7 + '@babel/parser': 7.29.7 + '@istanbuljs/schema': 0.1.6 istanbul-lib-coverage: 3.2.2 - semver: 7.7.1 + semver: 7.8.4 transitivePeerDependencies: - supports-color @@ -4867,13 +4833,13 @@ snapshots: istanbul-lib-source-maps@4.0.1: dependencies: - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) istanbul-lib-coverage: 3.2.2 source-map: 0.6.1 transitivePeerDependencies: - supports-color - istanbul-reports@3.1.7: + istanbul-reports@3.2.0: dependencies: html-escaper: 2.0.2 istanbul-lib-report: 3.0.1 @@ -4884,13 +4850,6 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 - jake@10.9.2: - dependencies: - async: 3.2.6 - chalk: 4.1.2 - filelist: 1.0.4 - minimatch: 3.1.5 - jest-changed-files@29.7.0: dependencies: execa: 5.1.1 @@ -4903,10 +4862,10 @@ snapshots: '@jest/expect': 29.7.0 '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.5 + '@types/node': 22.19.21 chalk: 4.1.2 co: 4.6.0 - dedent: 1.5.3 + dedent: 1.7.2 is-generator-fn: 2.1.0 jest-each: 29.7.0 jest-matcher-utils: 29.7.0 @@ -4923,16 +4882,16 @@ snapshots: - babel-plugin-macros - supports-color - jest-cli@29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)): + jest-cli@29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)) '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 chalk: 4.1.2 - create-jest: 29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)) + create-jest: 29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)) exit: 0.1.2 import-local: 3.2.0 - jest-config: 29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)) + jest-config: 29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)) jest-util: 29.7.0 jest-validate: 29.7.0 yargs: 17.7.2 @@ -4942,12 +4901,12 @@ snapshots: - supports-color - ts-node - jest-config@29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)): + jest-config@29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)): dependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.29.7 '@jest/test-sequencer': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.9) + babel-jest: 29.7.0(@babel/core@7.29.7) chalk: 4.1.2 ci-info: 3.9.0 deepmerge: 4.3.1 @@ -4967,8 +4926,8 @@ snapshots: slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: - '@types/node': 22.13.5 - ts-node: 10.9.2(@types/node@22.13.5)(typescript@5.5.4) + '@types/node': 22.19.21 + ts-node: 10.9.2(@types/node@22.19.21)(typescript@5.5.4) transitivePeerDependencies: - babel-plugin-macros - supports-color @@ -4997,7 +4956,7 @@ snapshots: '@jest/environment': 29.7.0 '@jest/fake-timers': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.5 + '@types/node': 22.19.21 jest-mock: 29.7.0 jest-util: 29.7.0 @@ -5007,7 +4966,7 @@ snapshots: dependencies: '@jest/types': 29.6.3 '@types/graceful-fs': 4.1.9 - '@types/node': 22.13.5 + '@types/node': 22.19.21 anymatch: 3.1.3 fb-watchman: 2.0.2 graceful-fs: 4.2.11 @@ -5033,7 +4992,7 @@ snapshots: jest-message-util@29.7.0: dependencies: - '@babel/code-frame': 7.26.2 + '@babel/code-frame': 7.29.7 '@jest/types': 29.6.3 '@types/stack-utils': 2.0.3 chalk: 4.1.2 @@ -5046,7 +5005,7 @@ snapshots: jest-mock@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.13.5 + '@types/node': 22.19.21 jest-util: 29.7.0 jest-pnp-resolver@1.2.3(jest-resolve@29.7.0): @@ -5070,7 +5029,7 @@ snapshots: jest-pnp-resolver: 1.2.3(jest-resolve@29.7.0) jest-util: 29.7.0 jest-validate: 29.7.0 - resolve: 1.22.10 + resolve: 1.22.12 resolve.exports: 2.0.3 slash: 3.0.0 @@ -5081,7 +5040,7 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.5 + '@types/node': 22.19.21 chalk: 4.1.2 emittery: 0.13.1 graceful-fs: 4.2.11 @@ -5109,10 +5068,10 @@ snapshots: '@jest/test-result': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.5 + '@types/node': 22.19.21 chalk: 4.1.2 cjs-module-lexer: 1.4.3 - collect-v8-coverage: 1.0.2 + collect-v8-coverage: 1.0.3 glob: 7.2.3 graceful-fs: 4.2.11 jest-haste-map: 29.7.0 @@ -5129,15 +5088,15 @@ snapshots: jest-snapshot@29.7.0: dependencies: - '@babel/core': 7.26.9 - '@babel/generator': 7.26.9 - '@babel/plugin-syntax-jsx': 7.25.9(@babel/core@7.26.9) - '@babel/plugin-syntax-typescript': 7.25.9(@babel/core@7.26.9) - '@babel/types': 7.26.9 + '@babel/core': 7.29.7 + '@babel/generator': 7.29.7 + '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) + '@babel/plugin-syntax-typescript': 7.29.7(@babel/core@7.29.7) + '@babel/types': 7.29.7 '@jest/expect-utils': 29.7.0 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-preset-current-node-syntax: 1.1.0(@babel/core@7.26.9) + babel-preset-current-node-syntax: 1.2.0(@babel/core@7.29.7) chalk: 4.1.2 expect: 29.7.0 graceful-fs: 4.2.11 @@ -5148,14 +5107,14 @@ snapshots: jest-util: 29.7.0 natural-compare: 1.4.0 pretty-format: 29.7.0 - semver: 7.7.1 + semver: 7.8.4 transitivePeerDependencies: - supports-color jest-util@29.7.0: dependencies: '@jest/types': 29.6.3 - '@types/node': 22.13.5 + '@types/node': 22.19.21 chalk: 4.1.2 ci-info: 3.9.0 graceful-fs: 4.2.11 @@ -5174,7 +5133,7 @@ snapshots: dependencies: '@jest/test-result': 29.7.0 '@jest/types': 29.6.3 - '@types/node': 22.13.5 + '@types/node': 22.19.21 ansi-escapes: 4.3.2 chalk: 4.1.2 emittery: 0.13.1 @@ -5183,17 +5142,17 @@ snapshots: jest-worker@29.7.0: dependencies: - '@types/node': 22.13.5 + '@types/node': 22.19.21 jest-util: 29.7.0 merge-stream: 2.0.0 supports-color: 8.1.1 - jest@29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)): + jest@29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)): dependencies: - '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)) + '@jest/core': 29.7.0(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)) '@jest/types': 29.6.3 import-local: 3.2.0 - jest-cli: 29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)) + jest-cli: 29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)) transitivePeerDependencies: - '@types/node' - babel-plugin-macros @@ -5202,22 +5161,21 @@ snapshots: joycon@3.1.1: {} - js-tiktoken@1.0.19: + js-tiktoken@1.0.21: dependencies: base64-js: 1.5.1 js-tokens@4.0.0: {} - js-yaml@3.14.1: + js-yaml@4.2.0: dependencies: - argparse: 1.0.10 - esprima: 4.0.1 + argparse: 2.0.1 jsesc@3.1.0: {} json-bigint@1.0.0: dependencies: - bignumber.js: 9.2.0 + bignumber.js: 9.3.1 json-parse-even-better-errors@2.3.1: {} @@ -5236,7 +5194,7 @@ snapshots: lodash.isstring: 4.0.1 lodash.once: 4.1.1 ms: 2.1.3 - semver: 7.7.1 + semver: 7.8.4 jwa@2.0.1: dependencies: @@ -5249,18 +5207,18 @@ snapshots: jwa: 2.0.1 safe-buffer: 5.2.1 - kareem@3.2.0: {} + kareem@3.3.0: {} kleur@3.0.3: {} kolorist@1.8.0: {} - langsmith@0.6.3(openai@4.93.0(encoding@0.1.13)(ws@8.18.1)(zod@3.24.2))(ws@8.18.1): + langsmith@0.6.3(openai@4.104.0(ws@8.21.0)(zod@3.25.76))(ws@8.21.0): dependencies: p-queue: 6.6.2 optionalDependencies: - openai: 4.93.0(encoding@0.1.13)(ws@8.18.1)(zod@3.24.2) - ws: 8.18.1 + openai: 4.104.0(ws@8.21.0)(zod@3.25.76) + ws: 8.21.0 leven@3.1.0: {} @@ -5292,8 +5250,6 @@ snapshots: lodash.once@4.1.1: {} - lodash.sortby@4.7.0: {} - long@5.3.2: {} lru-cache@10.4.3: {} @@ -5302,9 +5258,13 @@ snapshots: dependencies: yallist: 3.1.1 + magic-string@0.30.21: + dependencies: + '@jridgewell/sourcemap-codec': 1.5.5 + make-dir@4.0.0: dependencies: - semver: 7.7.1 + semver: 7.8.4 make-error@1.3.6: {} @@ -5343,13 +5303,13 @@ snapshots: mimic-response@3.1.0: {} + minimatch@10.2.5: + dependencies: + brace-expansion: 5.0.6 + minimatch@3.1.5: dependencies: - brace-expansion: 1.1.11 - - minimatch@5.1.9: - dependencies: - brace-expansion: 2.0.1 + brace-expansion: 1.1.15 minimatch@9.0.9: dependencies: @@ -5357,25 +5317,32 @@ snapshots: minimist@1.2.8: {} - minipass@7.1.2: {} + minipass@7.1.3: {} mkdirp-classic@0.5.3: {} + mlly@1.8.2: + dependencies: + acorn: 8.16.0 + pathe: 2.0.3 + pkg-types: 1.3.1 + ufo: 1.6.4 + mongodb-connection-string-url@7.0.1: dependencies: '@types/whatwg-url': 13.0.0 whatwg-url: 14.2.0 - mongodb@7.1.1: + mongodb@7.2.0: dependencies: - '@mongodb-js/saslprep': 1.4.6 + '@mongodb-js/saslprep': 1.4.11 bson: 7.2.0 mongodb-connection-string-url: 7.0.1 - mongoose@9.4.1: + mongoose@9.7.0: dependencies: - kareem: 3.2.0 - mongodb: 7.1.1 + kareem: 3.3.0 + mongodb: 7.2.0 mpath: 0.9.0 mquery: 6.0.0 ms: 2.1.3 @@ -5403,9 +5370,6 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.8: - optional: true - napi-build-utils@2.0.0: {} natural-compare@1.4.0: {} @@ -5417,19 +5381,20 @@ snapshots: apparatus: 0.0.10 dotenv: 17.4.2 memjs: 1.3.2 - mongoose: 9.4.1 - pg: 8.20.0 - redis: 5.11.0 + mongoose: 9.7.0 + pg: 8.21.0 + redis: 5.12.1 safe-stable-stringify: 2.5.0 stopwords-iso: 1.1.0 sylvester: 0.0.21 underscore: 1.13.8 - uuid: 13.0.0 + uuid: 13.0.2 wordnet-db: 3.1.14 transitivePeerDependencies: - '@aws-sdk/credential-providers' - '@mongodb-js/zstd' - '@node-rs/xxhash' + - '@opentelemetry/api' - gcp-metadata - kerberos - mongodb-client-encryption @@ -5437,17 +5402,17 @@ snapshots: - snappy - socks - node-abi@3.74.0: + neo-async@2.6.2: {} + + node-abi@3.92.0: dependencies: - semver: 7.7.1 + semver: 7.8.4 node-domexception@1.0.0: {} - node-fetch@2.7.0(encoding@0.1.13): + node-fetch@2.7.0: dependencies: whatwg-url: 5.0.0 - optionalDependencies: - encoding: 0.1.13 node-fetch@3.3.2: dependencies: @@ -5457,16 +5422,16 @@ snapshots: node-int64@0.4.0: {} - node-releases@2.0.19: {} + node-releases@2.0.47: {} - nodemon@3.1.9: + nodemon@3.1.14: dependencies: chokidar: 3.6.0 - debug: 4.4.0(supports-color@5.5.0) + debug: 4.4.3(supports-color@5.5.0) ignore-by-default: 1.0.1 - minimatch: 3.1.5 + minimatch: 10.2.5 pstree.remy: 1.1.8 - semver: 7.7.1 + semver: 7.8.4 simple-update-notifier: 2.0.0 supports-color: 5.5.0 touch: 3.1.1 @@ -5475,7 +5440,7 @@ snapshots: normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 - semver: 7.7.1 + semver: 7.8.4 validate-npm-package-license: 3.0.4 normalize-path@3.0.0: {} @@ -5488,7 +5453,7 @@ snapshots: obuf@1.1.2: {} - ollama@0.5.14: + ollama@0.5.18: dependencies: whatwg-fetch: 3.6.20 @@ -5502,23 +5467,23 @@ snapshots: open@10.2.0: dependencies: - default-browser: 5.4.0 + default-browser: 5.5.0 define-lazy-prop: 3.0.0 is-inside-container: 1.0.0 wsl-utils: 0.1.0 - openai@4.93.0(encoding@0.1.13)(ws@8.18.1)(zod@3.24.2): + openai@4.104.0(ws@8.21.0)(zod@3.25.76): dependencies: - '@types/node': 18.19.76 - '@types/node-fetch': 2.6.12 + '@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(encoding@0.1.13) + node-fetch: 2.7.0 optionalDependencies: - ws: 8.18.1 - zod: 3.24.2 + ws: 8.21.0 + zod: 3.25.76 transitivePeerDependencies: - encoding @@ -5558,15 +5523,15 @@ snapshots: parse-json@5.2.0: dependencies: - '@babel/code-frame': 7.26.2 - error-ex: 1.3.2 + '@babel/code-frame': 7.29.7 + error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 parse-json@7.1.1: dependencies: - '@babel/code-frame': 7.26.2 - error-ex: 1.3.2 + '@babel/code-frame': 7.29.7 + error-ex: 1.3.4 json-parse-even-better-errors: 3.0.2 lines-and-columns: 2.0.4 type-fest: 3.13.1 @@ -5582,47 +5547,42 @@ snapshots: path-scurry@1.11.1: dependencies: lru-cache: 10.4.3 - minipass: 7.1.2 + minipass: 7.1.3 - pg-cloudflare@1.1.1: + pathe@2.0.3: {} + + pg-cloudflare@1.4.0: optional: true - pg-cloudflare@1.3.0: - optional: true - - pg-connection-string@2.12.0: {} - - pg-connection-string@2.7.0: {} + pg-connection-string@2.13.0: {} pg-int8@1.0.1: {} pg-numeric@1.0.2: {} - pg-pool@3.13.0(pg@8.20.0): - dependencies: - pg: 8.20.0 - - pg-pool@3.7.1(pg@8.11.3): + pg-pool@3.14.0(pg@8.11.3): dependencies: pg: 8.11.3 - pg-protocol@1.13.0: {} + pg-pool@3.14.0(pg@8.21.0): + dependencies: + pg: 8.21.0 - pg-protocol@1.7.1: {} + pg-protocol@1.14.0: {} pg-types@2.2.0: dependencies: pg-int8: 1.0.1 postgres-array: 2.0.0 - postgres-bytea: 1.0.0 + postgres-bytea: 1.0.1 postgres-date: 1.0.7 postgres-interval: 1.2.0 - pg-types@4.0.2: + pg-types@4.1.0: dependencies: pg-int8: 1.0.1 pg-numeric: 1.0.2 - postgres-array: 3.0.2 + postgres-array: 3.0.4 postgres-bytea: 3.0.0 postgres-date: 2.1.0 postgres-interval: 3.0.0 @@ -5632,23 +5592,23 @@ snapshots: dependencies: buffer-writer: 2.0.0 packet-reader: 1.0.0 - pg-connection-string: 2.7.0 - pg-pool: 3.7.1(pg@8.11.3) - pg-protocol: 1.7.1 + pg-connection-string: 2.13.0 + pg-pool: 3.14.0(pg@8.11.3) + pg-protocol: 1.14.0 pg-types: 2.2.0 pgpass: 1.0.5 optionalDependencies: - pg-cloudflare: 1.1.1 + pg-cloudflare: 1.4.0 - pg@8.20.0: + pg@8.21.0: dependencies: - pg-connection-string: 2.12.0 - pg-pool: 3.13.0(pg@8.20.0) - pg-protocol: 1.13.0 + pg-connection-string: 2.13.0 + pg-pool: 3.14.0(pg@8.21.0) + pg-protocol: 1.14.0 pg-types: 2.2.0 pgpass: 1.0.5 optionalDependencies: - pg-cloudflare: 1.3.0 + pg-cloudflare: 1.4.0 pgpass@1.0.5: dependencies: @@ -5658,32 +5618,29 @@ snapshots: picomatch@2.3.2: {} - picomatch@4.0.2: {} + picomatch@4.0.4: {} - pirates@4.0.6: {} + pirates@4.0.7: {} pkg-dir@4.2.0: dependencies: find-up: 4.1.0 - postcss-load-config@6.0.1(postcss@8.5.3): + pkg-types@1.3.1: + dependencies: + confbox: 0.1.8 + mlly: 1.8.2 + pathe: 2.0.3 + + postcss-load-config@6.0.1: dependencies: lilconfig: 3.1.3 - optionalDependencies: - postcss: 8.5.3 - - postcss@8.5.3: - dependencies: - nanoid: 3.3.8 - picocolors: 1.1.1 - source-map-js: 1.2.1 - optional: true postgres-array@2.0.0: {} - postgres-array@3.0.2: {} + postgres-array@3.0.4: {} - postgres-bytea@1.0.0: {} + postgres-bytea@1.0.1: {} postgres-bytea@3.0.0: dependencies: @@ -5703,20 +5660,20 @@ snapshots: prebuild-install@7.1.3: dependencies: - detect-libc: 2.0.3 + detect-libc: 2.1.2 expand-template: 2.0.3 github-from-package: 0.0.0 minimist: 1.2.8 mkdirp-classic: 0.5.3 napi-build-utils: 2.0.0 - node-abi: 3.74.0 - pump: 3.0.2 + node-abi: 3.92.0 + pump: 3.0.4 rc: 1.2.8 simple-get: 4.0.1 tar-fs: 2.1.4 tunnel-agent: 0.6.0 - prettier@3.5.2: {} + prettier@3.8.4: {} pretty-format@29.7.0: dependencies: @@ -5729,7 +5686,7 @@ snapshots: kleur: 3.0.3 sisteransi: 1.0.5 - protobufjs@7.6.1: + protobufjs@7.6.3: dependencies: '@protobufjs/aspromise': 1.1.2 '@protobufjs/base64': 1.1.2 @@ -5741,16 +5698,16 @@ snapshots: '@protobufjs/path': 1.1.2 '@protobufjs/pool': 1.1.0 '@protobufjs/utf8': 1.1.1 - '@types/node': 22.13.5 + '@types/node': 22.19.21 long: 5.3.2 proxy-from-env@2.1.0: {} pstree.remy@1.1.8: {} - pump@3.0.2: + pump@3.0.4: dependencies: - end-of-stream: 1.4.4 + end-of-stream: 1.4.5 once: 1.4.0 punycode@2.3.1: {} @@ -5773,7 +5730,7 @@ snapshots: '@types/normalize-package-data': 2.4.4 normalize-package-data: 6.0.2 parse-json: 7.1.1 - type-fest: 4.35.0 + type-fest: 4.41.0 readable-stream@3.6.2: dependencies: @@ -5787,24 +5744,25 @@ snapshots: readdirp@4.1.2: {} - redis@4.7.0: + redis@4.7.1: dependencies: - '@redis/bloom': 1.2.0(@redis/client@1.6.0) - '@redis/client': 1.6.0 - '@redis/graph': 1.1.1(@redis/client@1.6.0) - '@redis/json': 1.0.7(@redis/client@1.6.0) - '@redis/search': 1.2.0(@redis/client@1.6.0) - '@redis/time-series': 1.1.0(@redis/client@1.6.0) + '@redis/bloom': 1.2.0(@redis/client@1.6.1) + '@redis/client': 1.6.1 + '@redis/graph': 1.1.1(@redis/client@1.6.1) + '@redis/json': 1.0.7(@redis/client@1.6.1) + '@redis/search': 1.2.0(@redis/client@1.6.1) + '@redis/time-series': 1.1.0(@redis/client@1.6.1) - redis@5.11.0: + redis@5.12.1: dependencies: - '@redis/bloom': 5.11.0(@redis/client@5.11.0) - '@redis/client': 5.11.0 - '@redis/json': 5.11.0(@redis/client@5.11.0) - '@redis/search': 5.11.0(@redis/client@5.11.0) - '@redis/time-series': 5.11.0(@redis/client@5.11.0) + '@redis/bloom': 5.12.1(@redis/client@5.12.1) + '@redis/client': 5.12.1 + '@redis/json': 5.12.1(@redis/client@5.12.1) + '@redis/search': 5.12.1(@redis/client@5.12.1) + '@redis/time-series': 5.12.1(@redis/client@5.12.1) transitivePeerDependencies: - '@node-rs/xxhash' + - '@opentelemetry/api' require-directory@2.1.1: {} @@ -5816,9 +5774,10 @@ snapshots: resolve.exports@2.0.3: {} - resolve@1.22.10: + resolve@1.22.12: dependencies: - is-core-module: 2.16.1 + es-errors: 1.3.0 + is-core-module: 2.16.2 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -5830,35 +5789,35 @@ snapshots: dependencies: glob: 10.5.0 - rollup@4.60.4: + rollup@4.61.1: dependencies: - '@types/estree': 1.0.8 + '@types/estree': 1.0.9 optionalDependencies: - '@rollup/rollup-android-arm-eabi': 4.60.4 - '@rollup/rollup-android-arm64': 4.60.4 - '@rollup/rollup-darwin-arm64': 4.60.4 - '@rollup/rollup-darwin-x64': 4.60.4 - '@rollup/rollup-freebsd-arm64': 4.60.4 - '@rollup/rollup-freebsd-x64': 4.60.4 - '@rollup/rollup-linux-arm-gnueabihf': 4.60.4 - '@rollup/rollup-linux-arm-musleabihf': 4.60.4 - '@rollup/rollup-linux-arm64-gnu': 4.60.4 - '@rollup/rollup-linux-arm64-musl': 4.60.4 - '@rollup/rollup-linux-loong64-gnu': 4.60.4 - '@rollup/rollup-linux-loong64-musl': 4.60.4 - '@rollup/rollup-linux-ppc64-gnu': 4.60.4 - '@rollup/rollup-linux-ppc64-musl': 4.60.4 - '@rollup/rollup-linux-riscv64-gnu': 4.60.4 - '@rollup/rollup-linux-riscv64-musl': 4.60.4 - '@rollup/rollup-linux-s390x-gnu': 4.60.4 - '@rollup/rollup-linux-x64-gnu': 4.60.4 - '@rollup/rollup-linux-x64-musl': 4.60.4 - '@rollup/rollup-openbsd-x64': 4.60.4 - '@rollup/rollup-openharmony-arm64': 4.60.4 - '@rollup/rollup-win32-arm64-msvc': 4.60.4 - '@rollup/rollup-win32-ia32-msvc': 4.60.4 - '@rollup/rollup-win32-x64-gnu': 4.60.4 - '@rollup/rollup-win32-x64-msvc': 4.60.4 + '@rollup/rollup-android-arm-eabi': 4.61.1 + '@rollup/rollup-android-arm64': 4.61.1 + '@rollup/rollup-darwin-arm64': 4.61.1 + '@rollup/rollup-darwin-x64': 4.61.1 + '@rollup/rollup-freebsd-arm64': 4.61.1 + '@rollup/rollup-freebsd-x64': 4.61.1 + '@rollup/rollup-linux-arm-gnueabihf': 4.61.1 + '@rollup/rollup-linux-arm-musleabihf': 4.61.1 + '@rollup/rollup-linux-arm64-gnu': 4.61.1 + '@rollup/rollup-linux-arm64-musl': 4.61.1 + '@rollup/rollup-linux-loong64-gnu': 4.61.1 + '@rollup/rollup-linux-loong64-musl': 4.61.1 + '@rollup/rollup-linux-ppc64-gnu': 4.61.1 + '@rollup/rollup-linux-ppc64-musl': 4.61.1 + '@rollup/rollup-linux-riscv64-gnu': 4.61.1 + '@rollup/rollup-linux-riscv64-musl': 4.61.1 + '@rollup/rollup-linux-s390x-gnu': 4.61.1 + '@rollup/rollup-linux-x64-gnu': 4.61.1 + '@rollup/rollup-linux-x64-musl': 4.61.1 + '@rollup/rollup-openbsd-x64': 4.61.1 + '@rollup/rollup-openharmony-arm64': 4.61.1 + '@rollup/rollup-win32-arm64-msvc': 4.61.1 + '@rollup/rollup-win32-ia32-msvc': 4.61.1 + '@rollup/rollup-win32-x64-gnu': 4.61.1 + '@rollup/rollup-win32-x64-msvc': 4.61.1 fsevents: 2.3.3 run-applescript@7.1.0: {} @@ -5871,12 +5830,9 @@ snapshots: safe-stable-stringify@2.5.0: {} - safer-buffer@2.1.2: - optional: true - semver@6.3.1: {} - semver@7.7.1: {} + semver@7.8.4: {} shebang-command@2.0.0: dependencies: @@ -5900,15 +5856,12 @@ snapshots: simple-update-notifier@2.0.0: dependencies: - semver: 7.7.1 + semver: 7.8.4 sisteransi@1.0.5: {} slash@3.0.0: {} - source-map-js@1.2.1: - optional: true - source-map-support@0.5.13: dependencies: buffer-from: 1.1.2 @@ -5916,9 +5869,7 @@ snapshots: source-map@0.6.1: {} - source-map@0.8.0-beta.0: - dependencies: - whatwg-url: 7.1.0 + source-map@0.7.6: {} sparse-bitfield@3.0.3: dependencies: @@ -5927,21 +5878,19 @@ snapshots: spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 - spdx-license-ids: 3.0.21 + spdx-license-ids: 3.0.23 spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 - spdx-license-ids: 3.0.21 + spdx-license-ids: 3.0.23 - spdx-license-ids@3.0.21: {} + spdx-license-ids@3.0.23: {} split2@4.2.0: {} - sprintf-js@1.0.3: {} - stack-utils@2.0.6: dependencies: escape-string-regexp: 2.0.0 @@ -5963,7 +5912,7 @@ snapshots: dependencies: eastasianwidth: 0.2.0 emoji-regex: 9.2.2 - strip-ansi: 7.1.0 + strip-ansi: 7.2.0 string_decoder@1.3.0: dependencies: @@ -5973,9 +5922,9 @@ snapshots: dependencies: ansi-regex: 5.0.1 - strip-ansi@7.1.0: + strip-ansi@7.2.0: dependencies: - ansi-regex: 6.1.0 + ansi-regex: 6.2.2 strip-bom@4.0.0: {} @@ -5985,14 +5934,14 @@ snapshots: strip-json-comments@3.1.1: {} - sucrase@3.35.0: + sucrase@3.35.1: dependencies: - '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 - glob: 10.5.0 lines-and-columns: 1.2.4 mz: 2.7.0 - pirates: 4.0.6 + pirates: 4.0.7 + tinyglobby: 0.2.17 ts-interface-checker: 0.1.13 suffix-thumb@5.0.2: {} @@ -6017,20 +5966,20 @@ snapshots: dependencies: chownr: 1.1.4 mkdirp-classic: 0.5.3 - pump: 3.0.2 + pump: 3.0.4 tar-stream: 2.2.0 tar-stream@2.2.0: dependencies: bl: 4.1.0 - end-of-stream: 1.4.4 + end-of-stream: 1.4.5 fs-constants: 1.0.0 inherits: 2.0.4 readable-stream: 3.6.2 test-exclude@6.0.0: dependencies: - '@istanbuljs/schema': 0.1.3 + '@istanbuljs/schema': 0.1.6 glob: 7.2.3 minimatch: 3.1.5 @@ -6044,10 +5993,10 @@ snapshots: tinyexec@0.3.2: {} - tinyglobby@0.2.12: + tinyglobby@0.2.17: dependencies: - fdir: 6.4.3(picomatch@4.0.2) - picomatch: 4.0.2 + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 tmpl@1.0.5: {} @@ -6059,10 +6008,6 @@ snapshots: tr46@0.0.3: {} - tr46@1.0.1: - dependencies: - punycode: 2.3.1 - tr46@5.1.1: dependencies: punycode: 2.3.1 @@ -6071,39 +6016,40 @@ snapshots: ts-interface-checker@0.1.13: {} - ts-jest@29.2.6(@babel/core@7.26.9)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.26.9))(esbuild@0.25.1)(jest@29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)))(typescript@5.5.4): + ts-jest@29.4.11(@babel/core@7.29.7)(@jest/transform@29.7.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.29.7))(esbuild@0.27.7)(jest-util@29.7.0)(jest@29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)))(typescript@5.5.4): dependencies: bs-logger: 0.2.6 - ejs: 3.1.10 fast-json-stable-stringify: 2.1.0 - jest: 29.7.0(@types/node@22.13.5)(ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4)) - jest-util: 29.7.0 + handlebars: 4.7.9 + jest: 29.7.0(@types/node@22.19.21)(ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4)) json5: 2.2.3 lodash.memoize: 4.1.2 make-error: 1.3.6 - semver: 7.7.1 + semver: 7.8.4 + type-fest: 4.41.0 typescript: 5.5.4 yargs-parser: 21.1.1 optionalDependencies: - '@babel/core': 7.26.9 + '@babel/core': 7.29.7 '@jest/transform': 29.7.0 '@jest/types': 29.6.3 - babel-jest: 29.7.0(@babel/core@7.26.9) - esbuild: 0.25.1 + babel-jest: 29.7.0(@babel/core@7.29.7) + esbuild: 0.27.7 + jest-util: 29.7.0 - ts-node@10.9.2(@types/node@22.13.5)(typescript@5.5.4): + ts-node@10.9.2(@types/node@22.19.21)(typescript@5.5.4): dependencies: '@cspotcode/source-map-support': 0.8.1 - '@tsconfig/node10': 1.0.11 + '@tsconfig/node10': 1.0.12 '@tsconfig/node12': 1.0.11 '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.4 - '@types/node': 22.13.5 - acorn: 8.14.0 - acorn-walk: 8.3.4 + '@types/node': 22.19.21 + acorn: 8.16.0 + acorn-walk: 8.3.5 arg: 4.1.3 create-require: 1.1.1 - diff: 4.0.2 + diff: 4.0.4 make-error: 1.3.6 typescript: 5.5.4 v8-compile-cache-lib: 3.0.1 @@ -6111,26 +6057,26 @@ snapshots: tslib@2.8.1: {} - tsup@8.4.0(postcss@8.5.3)(typescript@5.5.4): + tsup@8.5.1(typescript@5.5.4): dependencies: - bundle-require: 5.1.0(esbuild@0.25.1) + bundle-require: 5.1.0(esbuild@0.27.7) cac: 6.7.14 chokidar: 4.0.3 consola: 3.4.2 - debug: 4.4.0(supports-color@5.5.0) - esbuild: 0.25.1 + debug: 4.4.3(supports-color@5.5.0) + 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.3) + postcss-load-config: 6.0.1 resolve-from: 5.0.0 - rollup: 4.60.4 - source-map: 0.8.0-beta.0 - sucrase: 3.35.0 + rollup: 4.61.1 + source-map: 0.7.6 + sucrase: 3.35.1 tinyexec: 0.3.2 - tinyglobby: 0.2.12 + tinyglobby: 0.2.17 tree-kill: 1.2.2 optionalDependencies: - postcss: 8.5.3 typescript: 5.5.4 transitivePeerDependencies: - jiti @@ -6148,39 +6094,42 @@ snapshots: type-fest@3.13.1: {} - type-fest@4.35.0: {} + type-fest@4.41.0: {} typescript@5.5.4: {} + ufo@1.6.4: {} + + uglify-js@3.19.3: + optional: true + undefsafe@2.0.5: {} underscore@1.13.8: {} undici-types@5.26.5: {} - undici-types@6.20.0: {} + undici-types@6.21.0: {} undici@6.26.0: {} - update-browserslist-db@1.1.3(browserslist@4.24.4): + update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: - browserslist: 4.24.4 + browserslist: 4.28.2 escalade: 3.2.0 picocolors: 1.1.1 util-deprecate@1.0.2: {} - uuid@13.0.0: {} + uuid@11.1.1: {} - uuid@8.3.2: {} - - uuid@9.0.1: {} + uuid@13.0.2: {} v8-compile-cache-lib@3.0.1: {} v8-to-istanbul@9.3.0: dependencies: - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/trace-mapping': 0.3.31 '@types/istanbul-lib-coverage': 2.0.6 convert-source-map: 2.0.0 @@ -6199,8 +6148,6 @@ snapshots: webidl-conversions@3.0.1: {} - webidl-conversions@4.0.2: {} - webidl-conversions@7.0.0: {} whatwg-fetch@3.6.20: {} @@ -6215,18 +6162,14 @@ snapshots: tr46: 0.0.3 webidl-conversions: 3.0.1 - whatwg-url@7.1.0: - dependencies: - lodash.sortby: 4.7.0 - tr46: 1.0.1 - webidl-conversions: 4.0.2 - which@2.0.2: dependencies: isexe: 2.0.0 wordnet-db@3.1.14: {} + wordwrap@1.0.0: {} + wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 @@ -6235,9 +6178,9 @@ snapshots: wrap-ansi@8.1.0: dependencies: - ansi-styles: 6.2.1 + ansi-styles: 6.2.3 string-width: 5.1.2 - strip-ansi: 7.1.0 + strip-ansi: 7.2.0 wrappy@1.0.2: {} @@ -6246,11 +6189,11 @@ snapshots: imurmurhash: 0.1.4 signal-exit: 3.0.7 - ws@8.18.1: {} + ws@8.21.0: {} wsl-utils@0.1.0: dependencies: - is-wsl: 3.1.0 + is-wsl: 3.1.1 xtend@4.0.2: {} @@ -6276,10 +6219,8 @@ snapshots: yocto-queue@0.1.0: {} - zod-to-json-schema@3.24.5(zod@3.24.2): + zod-to-json-schema@3.25.2(zod@3.25.76): dependencies: - zod: 3.24.2 - - zod@3.24.2: {} + zod: 3.25.76 zod@3.25.76: {} diff --git a/mem0-ts/pnpm-workspace.yaml b/mem0-ts/pnpm-workspace.yaml index 7cba39285..73429a951 100644 --- a/mem0-ts/pnpm-workspace.yaml +++ b/mem0-ts/pnpm-workspace.yaml @@ -7,12 +7,17 @@ onlyBuiltDependencies: overrides: "picomatch@<2.3.2": "^2.3.2" + "picomatch@>=4.0.0 <4.0.4": "^4.0.4" "jws@4.0.0": "4.0.1" + "js-yaml@<3.14.2": ">=3.14.2" "langsmith@<0.6.0": "^0.6.0" "minimatch@<3.1.3": "^3.1.3" "minimatch@>=5.0.0 <5.1.8": "^5.1.8" "minimatch@>=9.0.0 <9.0.7": "^9.0.7" "path-to-regexp@>=8.0.0 <8.4.0": "^8.4.0" + "postcss@<8.5.10": ">=8.5.10" + "uuid@<11.1.1": ">=11.1.1" + "ws@>=8.0.0 <8.20.1": ">=8.20.1" "rollup@>=4.0.0 <4.59.0": "^4.59.0" "tar-fs@>=2.0.0 <2.1.4": "^2.1.4" "glob@>=10.2.0 <10.5.0": "^10.5.0" diff --git a/mem0-ts/src/community/package.json b/mem0-ts/src/community/package.json index 36c5ca8b2..6f16ae23c 100644 --- a/mem0-ts/src/community/package.json +++ b/mem0-ts/src/community/package.json @@ -75,11 +75,11 @@ "typescript": "5.5.4" }, "dependencies": { - "@langchain/community": "^0.3.36", - "@langchain/core": "^0.3.42", + "@langchain/community": "^1.1.18", + "@langchain/core": "^1.1.27", "axios": "^1.16.0", "mem0ai": "^2.1.8", - "uuid": "9.0.1", + "uuid": "^11.1.1", "zod": "3.22.4" }, "engines": { diff --git a/mem0-ts/src/oss/package.json b/mem0-ts/src/oss/package.json index 19d856cea..3ea60e431 100644 --- a/mem0-ts/src/oss/package.json +++ b/mem0-ts/src/oss/package.json @@ -27,7 +27,7 @@ "pg": "^8.11.3", "redis": "^4.7.0", "better-sqlite3": "^12.6.2", - "uuid": "^9.0.1", + "uuid": "^11.1.1", "zod": "^3.22.4" }, "devDependencies": { diff --git a/mem0-ts/src/oss/src/memory/index.ts b/mem0-ts/src/oss/src/memory/index.ts index 05591e615..318548029 100644 --- a/mem0-ts/src/oss/src/memory/index.ts +++ b/mem0-ts/src/oss/src/memory/index.ts @@ -36,10 +36,29 @@ import { SearchMemoryOptions, DeleteAllMemoryOptions, GetAllMemoryOptions, + UpdateProjectOptions, } from "./memory.types"; import { parse_vision_messages } from "../utils/memory"; import { HistoryManager } from "../storage/base"; import { captureClientEvent, isTelemetryEnabled } from "../utils/telemetry"; +import { + detectScaleThresholdFromAddResult, + detectScaleThresholdFromTopK, + detectPerformanceSlowQuery, + detectTemporalUsageFromMetadata, + detectTemporalUsageFromSearch, + displayDecayUsageNotice, + displayFirstRunNotice, + displayPerformanceSlowQueryNotice, + displayScaleThresholdNotice, + displayTemporalUsageNotice, + getDecayFeatureErrorMessage, + getDecayUsageDeleteCountAfterSuccess, + getTemporalFeatureErrorMessage, + isDecayUsageDeleteEligible, + PerformanceSlowQueryTrigger, + ScaleThresholdTrigger, +} from "../utils/notices"; import { lemmatizeForBm25 } from "../utils/lemmatization"; import { extractEntities, @@ -500,6 +519,73 @@ export class Memory { } } + private async _displayFirstRunNotice(triggerFunction: string) { + try { + await this._getTelemetryId(); + await displayFirstRunNotice(this, triggerFunction); + } catch {} + } + + private async _displayDecayUsageNotice(trigger: { + triggerFunction: "delete" | "delete_all"; + triggerSource: "delete_count" | "delete_all"; + triggerReason: "repeated_deletes" | "bulk_delete"; + deleteCount?: number; + deletedCount?: number; + }) { + try { + await this._getTelemetryId(); + await displayDecayUsageNotice(this, trigger); + } catch {} + } + + private async _displayTemporalUsageNotice(trigger: { + triggerFunction: "add" | "search"; + triggerSource: "metadata" | "query" | "filter"; + triggerReason: + | "date_like_metadata" + | "relative_phrase" + | "date_like_query" + | "date_range_filter"; + }) { + try { + await this._getTelemetryId(); + await displayTemporalUsageNotice(this, trigger); + } catch {} + } + + private async _displayScaleThresholdNotice(trigger: ScaleThresholdTrigger) { + try { + await this._getTelemetryId(); + await displayScaleThresholdNotice(this, trigger); + } catch {} + } + + private async _displayPerformanceSlowQueryNotice( + trigger: PerformanceSlowQueryTrigger, + ) { + try { + await this._getTelemetryId(); + await displayPerformanceSlowQueryNotice(this, trigger); + } catch {} + } + + private async _getNoticeTelemetryId() { + try { + if ( + !this.telemetryId || + this.telemetryId === "anonymous" || + this.telemetryId === "anonymous-supabase" + ) { + this.telemetryId = (await getOrCreateMem0UserId()) || "anonymous"; + } + return this.telemetryId; + } catch { + this.telemetryId = "anonymous"; + return this.telemetryId; + } + } + static fromConfig(configDict: Record): Memory { try { const config = MemoryConfigSchema.parse(configDict); @@ -510,10 +596,29 @@ export class Memory { } } + async updateProject(options: UpdateProjectOptions = {}): Promise { + if (options?.decay === true) { + await this._getNoticeTelemetryId(); + throw new Error(await getDecayFeatureErrorMessage(this)); + } + + throw new Error("Project updates are not supported by the OSS Memory SDK."); + } + async add( messages: string | Message[], config: AddMemoryOptions, ): Promise { + if (config?.timestamp !== undefined) { + await this._getNoticeTelemetryId(); + throw new Error( + await getTemporalFeatureErrorMessage(this, { + triggerFunction: "add", + triggerParameter: "timestamp", + }), + ); + } + // Validate messages input if (messages === undefined || messages === null) { throw new Error( @@ -521,6 +626,10 @@ export class Memory { ); } + const temporalUsageNotice = detectTemporalUsageFromMetadata( + config?.metadata, + ); + await this._ensureInitialized(); await this._captureEvent("add", { message_count: Array.isArray(messages) ? messages.length : 1, @@ -560,6 +669,27 @@ export class Memory { infer, ); + if (temporalUsageNotice) { + await this._displayTemporalUsageNotice({ + triggerFunction: "add", + triggerSource: temporalUsageNotice.triggerSource, + triggerReason: temporalUsageNotice.triggerReason, + }); + } else { + const scaleThresholdNotice = await detectScaleThresholdFromAddResult( + this, + vectorStoreResult, + ); + if (scaleThresholdNotice) { + await this._displayScaleThresholdNotice({ + triggerFunction: "add", + ...scaleThresholdNotice, + }); + } else { + await this._displayFirstRunNotice("add"); + } + } + return { results: vectorStoreResult, }; @@ -1002,7 +1132,10 @@ export class Memory { async get(memoryId: string): Promise { await this._ensureInitialized(); const memory = await this.vectorStore.get(memoryId); - if (!memory) return null; + if (!memory) { + await this._displayFirstRunNotice("get"); + return null; + } const filters = { ...(memory.payload.user_id && { user_id: memory.payload.user_id }), @@ -1037,13 +1170,30 @@ export class Memory { } } - return { ...memoryItem, ...filters }; + const result = { ...memoryItem, ...filters }; + await this._displayFirstRunNotice("get"); + return result; } async search( query: string, config: SearchMemoryOptions, ): Promise { + if (config?.referenceDate !== undefined) { + await this._getNoticeTelemetryId(); + throw new Error( + await getTemporalFeatureErrorMessage(this, { + triggerFunction: "search", + triggerParameter: "referenceDate", + }), + ); + } + + const temporalUsageNotice = detectTemporalUsageFromSearch( + query, + config?.filters, + ); + // Reject top-level entity params - must use filters instead rejectTopLevelEntityParams(config as Record, "search"); @@ -1111,6 +1261,8 @@ export class Memory { ); } + const searchStartMs = Date.now(); + // Step 1: Preprocess query const queryLemmatized = lemmatizeForBm25(query); const queryEntities = extractEntities(query); @@ -1286,9 +1438,41 @@ export class Memory { }; }); - return { + const result = { results, }; + const searchElapsedMs = Date.now() - searchStartMs; + if (temporalUsageNotice) { + await this._displayTemporalUsageNotice({ + triggerFunction: "search", + triggerSource: temporalUsageNotice.triggerSource, + triggerReason: temporalUsageNotice.triggerReason, + }); + } else { + const scaleThresholdNotice = detectScaleThresholdFromTopK(topK); + if (scaleThresholdNotice) { + await this._displayScaleThresholdNotice({ + triggerFunction: "search", + ...scaleThresholdNotice, + }); + } else { + const performanceSlowQueryNotice = detectPerformanceSlowQuery( + searchElapsedMs, + topK, + results.length, + ); + if (performanceSlowQueryNotice) { + await this._displayPerformanceSlowQueryNotice({ + triggerFunction: "search", + triggerReason: "slow_query", + ...performanceSlowQueryNotice, + }); + } else { + await this._displayFirstRunNotice("search"); + } + } + } + return result; } async update(memoryId: string, data: string): Promise<{ message: string }> { @@ -1296,14 +1480,28 @@ export class Memory { await this._captureEvent("update", { memory_id: memoryId }); const embedding = await this.embedder.embed(data); await this.updateMemory(memoryId, data, { [data]: embedding }); - return { message: "Memory updated successfully!" }; + const result = { message: "Memory updated successfully!" }; + await this._displayFirstRunNotice("update"); + return result; } async delete(memoryId: string): Promise<{ message: string }> { await this._ensureInitialized(); await this._captureEvent("delete", { memory_id: memoryId }); await this.deleteMemory(memoryId); - return { message: "Memory deleted successfully!" }; + const result = { message: "Memory deleted successfully!" }; + const deleteCount = getDecayUsageDeleteCountAfterSuccess(); + if (isDecayUsageDeleteEligible(deleteCount)) { + await this._displayDecayUsageNotice({ + triggerFunction: "delete", + triggerSource: "delete_count", + triggerReason: "repeated_deletes", + deleteCount, + }); + } else { + await this._displayFirstRunNotice("delete"); + } + return result; } async deleteAll( @@ -1334,12 +1532,25 @@ export class Memory { await this.deleteMemory(memory.id); } - return { message: "Memories deleted successfully!" }; + const result = { message: "Memories deleted successfully!" }; + if (memories.length > 0) { + await this._displayDecayUsageNotice({ + triggerFunction: "delete_all", + triggerSource: "delete_all", + triggerReason: "bulk_delete", + deletedCount: memories.length, + }); + } else { + await this._displayFirstRunNotice("delete_all"); + } + return result; } async history(memoryId: string): Promise { await this._ensureInitialized(); - return this.db.getHistory(memoryId); + const result = await this.db.getHistory(memoryId); + await this._displayFirstRunNotice("history"); + return result; } async reset(): Promise { @@ -1391,6 +1602,7 @@ export class Memory { console.error(this._initError); }); await this._initPromise; + await this._displayFirstRunNotice("reset"); } async getAll(config: GetAllMemoryOptions): Promise { @@ -1458,7 +1670,17 @@ export class Memory { ...(mem.payload.run_id && { run_id: mem.payload.run_id }), })); - return { results }; + const result = { results }; + const scaleThresholdNotice = detectScaleThresholdFromTopK(topK); + if (scaleThresholdNotice) { + await this._displayScaleThresholdNotice({ + triggerFunction: "get_all", + ...scaleThresholdNotice, + }); + } else { + await this._displayFirstRunNotice("get_all"); + } + return result; } private async createMemory( diff --git a/mem0-ts/src/oss/src/memory/memory.types.ts b/mem0-ts/src/oss/src/memory/memory.types.ts index 023b9d829..c125b61b3 100644 --- a/mem0-ts/src/oss/src/memory/memory.types.ts +++ b/mem0-ts/src/oss/src/memory/memory.types.ts @@ -11,6 +11,7 @@ export interface AddMemoryOptions extends Entity { metadata?: Record; filters?: SearchFilters; infer?: boolean; + timestamp?: number | string | Date | null; } export interface SearchMemoryOptions { @@ -18,6 +19,7 @@ export interface SearchMemoryOptions { filters?: SearchFilters; threshold?: number; explain?: boolean; + referenceDate?: number | string | Date | null; } export interface GetAllMemoryOptions { @@ -26,3 +28,8 @@ export interface GetAllMemoryOptions { } export interface DeleteAllMemoryOptions extends Entity {} + +export interface UpdateProjectOptions { + decay?: boolean; + [key: string]: any; +} diff --git a/mem0-ts/src/oss/src/utils/notices.ts b/mem0-ts/src/oss/src/utils/notices.ts new file mode 100644 index 000000000..868370e74 --- /dev/null +++ b/mem0-ts/src/oss/src/utils/notices.ts @@ -0,0 +1,1434 @@ +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; +import { + POSTHOG_API_KEY, + captureNoticeEvent, + isTelemetryEnabled, +} from "./telemetry"; +import type { TelemetryInstance } from "./telemetry.types"; + +export const NOTICE_FLAG_KEY = "mem0-oss-notices"; +export const NOTICE_EVENT_NAME = "mem0.notice_displayed"; +export const NOTICE_STATE_SECTION = "notice_state"; +export const FIRST_RUN_NOTICE_ID = "first_run"; +export const TEMPORAL_FEATURE_NOTICE_ID = "temporal_stub"; +export const TEMPORAL_USAGE_NOTICE_ID = "temporal_usage"; +export const DECAY_FEATURE_NOTICE_ID = "decay_stub"; +export const DECAY_USAGE_NOTICE_ID = "decay_usage"; +export const SCALE_THRESHOLD_NOTICE_ID = "scale_threshold"; +export const PERFORMANCE_SLOW_QUERY_NOTICE_ID = "performance_slow_query"; +export const NOTICE_CAP_LIMIT = 10; +export const NOTICE_CAP_WINDOW_MS = 7 * 24 * 60 * 60 * 1000; +export const NOTICE_FLAG_TIMEOUT_MS = 500; +export const POSTHOG_FLAGS_URL = "https://us.i.posthog.com/flags?v=2"; +const DISPLAYED_VARIANT = "displayed"; +const HOLDOUT_VARIANT = "holdout"; +const LOG_LINE_NOTICE_TYPE = "log_line"; +const ERROR_NOTICE_TYPE = "error"; +const TEMPORAL_TIMESTAMP_PLAIN_ERROR = + "The timestamp parameter is not supported by the OSS Memory SDK."; +const TEMPORAL_REFERENCE_DATE_PLAIN_ERROR = + "The referenceDate parameter is not supported by the OSS Memory SDK."; +const DECAY_FEATURE_PLAIN_ERROR = + "The decay parameter is not supported by the OSS Memory SDK."; +const DECAY_USAGE_DELETE_THRESHOLD = 5; +export const SCALE_MEMORY_COUNT_THRESHOLD = 2000; +export const SCALE_MEMORY_COUNT_CHECK_INTERVAL = 100; +export const SCALE_TOP_K_THRESHOLD = 50; +export const PERFORMANCE_SLOW_QUERY_THRESHOLD_MS = 2000; +const MAX_TEMPORAL_DETECTION_DEPTH = 32; +const ISO_DATE_RE = + /\b\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)?\b/; +const RELATIVE_TIME_RE = + /\b(today|yesterday|tomorrow|last\s+(?:night|week|month|year)|this\s+(?:week|month|year)|next\s+(?:week|month|year)|(?:past|last)\s+\d+\s+(?:day|days|week|weeks|month|months|year|years)|(?:since|before|after|until)\s+(?:today|yesterday|tomorrow|\d{4}-\d{2}-\d{2}|last\s+(?:week|month|year)))\b/i; +const RANGE_OPERATORS = new Set(["gt", "gte", "lt", "lte"]); + +let firstRunConsumedInProcess = false; +let firstRunClaimInProgress = false; +let decayUsageSuccessfulDeleteCount = 0; +const noticeCapacityReachedInProcess = new Set(); +let scaleMemoryCountAddsSinceCheck = 0; +let scaleMemoryCountCheckedInProcess = false; +let scaleMemoryCountThresholdEvaluatedInProcess = false; + +export interface NoticePayloadConfig { + enabled?: boolean; + notice_type?: string; + copy?: string; + [key: string]: any; +} + +export interface NoticeFlagEvaluation { + variant: string; + payload?: Record; + flag?: Record; +} + +export interface NoticeCapEvent { + evaluated_at?: string; + [key: string]: any; +} + +interface NoticeDisplayDecision { + displayed: boolean; + noticeConfigFound: boolean; + copy?: string; + bypassReason?: string; + disabledReason?: string; +} + +export interface DecayUsageTrigger { + triggerFunction: "delete" | "delete_all"; + triggerSource: "delete_count" | "delete_all"; + triggerReason: "repeated_deletes" | "bulk_delete"; + deleteCount?: number; + deletedCount?: number; +} + +export interface TemporalFeatureErrorTrigger { + triggerFunction: "add" | "search"; + triggerParameter: "timestamp" | "referenceDate"; +} + +export interface TemporalUsageTrigger { + triggerFunction: "add" | "search"; + triggerSource: "metadata" | "query" | "filter"; + triggerReason: + | "date_like_metadata" + | "relative_phrase" + | "date_like_query" + | "date_range_filter"; +} + +export interface ScaleThresholdTrigger { + triggerFunction: "add" | "search" | "get_all"; + triggerSource: "top_k" | "memory_count"; + triggerReason: "high_top_k" | "memory_count_threshold"; + topK?: number; + memoryCount?: number; + threshold: number; +} + +export interface PerformanceSlowQueryTrigger { + triggerFunction: "search"; + triggerReason: "slow_query"; + elapsedMs: number; + thresholdMs: number; + topK: number; + resultCount: number; +} + +export function getMem0Dir(): string { + return process.env.MEM0_DIR || path.join(os.homedir(), ".mem0"); +} + +export function getMem0ConfigPath(): string { + return path.join(getMem0Dir(), "config.json"); +} + +export function loadMem0Config(): Record { + try { + const configPath = getMem0ConfigPath(); + if (!fs.existsSync(configPath)) return {}; + const parsed = JSON.parse(fs.readFileSync(configPath, "utf8")); + return parsed && typeof parsed === "object" && !Array.isArray(parsed) + ? parsed + : {}; + } catch { + return {}; + } +} + +export function writeMem0ConfigAtomic(config: Record): boolean { + const configPath = getMem0ConfigPath(); + const dir = path.dirname(configPath); + const tempPath = path.join( + dir, + `.config.${process.pid}.${Date.now()}.${Math.random() + .toString(36) + .slice(2)}.tmp`, + ); + + try { + fs.mkdirSync(dir, { recursive: true }); + fs.writeFileSync(tempPath, JSON.stringify(config, null, 4)); + fs.renameSync(tempPath, configPath); + return true; + } catch { + try { + if (fs.existsSync(tempPath)) fs.unlinkSync(tempPath); + } catch {} + return false; + } +} + +export function getNoticeState( + config: Record, + noticeId: string, +): Record { + const stateSection = + config[NOTICE_STATE_SECTION] && + typeof config[NOTICE_STATE_SECTION] === "object" && + !Array.isArray(config[NOTICE_STATE_SECTION]) + ? config[NOTICE_STATE_SECTION] + : {}; + const noticeState = stateSection[noticeId]; + return noticeState && + typeof noticeState === "object" && + !Array.isArray(noticeState) + ? noticeState + : {}; +} + +export function setNoticeState( + config: Record, + noticeId: string, + state: Record, +): Record { + const stateSection = + config[NOTICE_STATE_SECTION] && + typeof config[NOTICE_STATE_SECTION] === "object" && + !Array.isArray(config[NOTICE_STATE_SECTION]) + ? { ...config[NOTICE_STATE_SECTION] } + : {}; + return { + ...config, + [NOTICE_STATE_SECTION]: { + ...stateSection, + [noticeId]: state, + }, + }; +} + +function parsePayload(payload: unknown): Record | undefined { + try { + if (typeof payload === "string") { + const parsed = JSON.parse(payload); + return parsed && typeof parsed === "object" && !Array.isArray(parsed) + ? parsed + : undefined; + } + return payload && typeof payload === "object" && !Array.isArray(payload) + ? (payload as Record) + : undefined; + } catch { + return undefined; + } +} + +export function getNoticeConfigFromPayload( + payload: unknown, + noticeId: string, +): { + found: boolean; + config?: NoticePayloadConfig; + payload?: Record; +} { + const parsedPayload = parsePayload(payload); + const notices = parsedPayload?.notices; + if (!notices || typeof notices !== "object" || Array.isArray(notices)) { + return { found: false, payload: parsedPayload }; + } + + const noticeConfig = (notices as Record)[noticeId]; + if ( + !noticeConfig || + typeof noticeConfig !== "object" || + Array.isArray(noticeConfig) + ) { + return { found: false, payload: parsedPayload }; + } + + return { + found: true, + config: noticeConfig as NoticePayloadConfig, + payload: parsedPayload, + }; +} + +export async function evaluateNoticeFlag( + distinctId: string, + options: { timeoutMs?: number; fetchImpl?: typeof fetch } = {}, +): Promise { + if (!isTelemetryEnabled()) return null; + + const timeoutMs = options.timeoutMs ?? NOTICE_FLAG_TIMEOUT_MS; + const fetchImpl = options.fetchImpl ?? fetch; + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), timeoutMs); + + try { + const response = await fetchImpl(POSTHOG_FLAGS_URL, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ + api_key: POSTHOG_API_KEY, + distinct_id: distinctId, + }), + signal: controller.signal, + }); + + if (!response.ok) return null; + const data: any = await response.json(); + const flag = data?.flags?.[NOTICE_FLAG_KEY]; + if (!flag || flag.enabled === false) return null; + + const variant = typeof flag.variant === "string" ? flag.variant : null; + if (!variant) return null; + + return { + variant, + payload: parsePayload(flag.metadata?.payload), + flag, + }; + } catch { + return null; + } finally { + clearTimeout(timeout); + } +} + +function eventsInWindow( + events: unknown, + now: Date, + windowMs: number, +): NoticeCapEvent[] { + if (!Array.isArray(events)) return []; + const cutoff = now.getTime() - windowMs; + return events.filter((event): event is NoticeCapEvent => { + if (!event || typeof event !== "object" || Array.isArray(event)) { + return false; + } + const evaluatedAt = (event as NoticeCapEvent).evaluated_at; + if (typeof evaluatedAt !== "string") return false; + const timestamp = Date.parse(evaluatedAt); + return Number.isFinite(timestamp) && timestamp >= cutoff; + }); +} + +export function hasNoticeCapRoom( + state: Record, + options: { now?: Date; limit?: number; windowMs?: number } = {}, +): boolean { + const now = options.now ?? new Date(); + const limit = options.limit ?? NOTICE_CAP_LIMIT; + const windowMs = options.windowMs ?? NOTICE_CAP_WINDOW_MS; + return eventsInWindow(state.events, now, windowMs).length < limit; +} + +function isNoticeCapacityReachedInProcess(noticeId: string): boolean { + return noticeCapacityReachedInProcess.has(noticeId); +} + +function markNoticeCapacityReachedInProcess(noticeId: string): void { + noticeCapacityReachedInProcess.add(noticeId); +} + +function hasNoticeCapRoomForNotice( + noticeId: string, + state: Record, + options: { now?: Date; limit?: number; windowMs?: number } = {}, +): boolean { + const hasRoom = hasNoticeCapRoom(state, options); + if (!hasRoom) markNoticeCapacityReachedInProcess(noticeId); + return hasRoom; +} + +export function appendNoticeCapEvent( + state: Record, + event: NoticeCapEvent, + options: { now?: Date; limit?: number; windowMs?: number } = {}, +): Record | null { + const now = options.now ?? new Date(); + const limit = options.limit ?? NOTICE_CAP_LIMIT; + const windowMs = options.windowMs ?? NOTICE_CAP_WINDOW_MS; + const events = eventsInWindow(state.events, now, windowMs); + if (events.length >= limit) return null; + + return { + ...state, + events: [ + ...events, + { + evaluated_at: now.toISOString(), + ...event, + }, + ], + }; +} + +export function recordNoticeOpportunity( + noticeId: string, + event: NoticeCapEvent, + options: { now?: Date; limit?: number; windowMs?: number } = {}, +): boolean { + if (!isTelemetryEnabled()) return false; + if (isNoticeCapacityReachedInProcess(noticeId)) return false; + + const config = loadMem0Config(); + const state = getNoticeState(config, noticeId); + const nextState = appendNoticeCapEvent(state, event, options); + if (!nextState) { + markNoticeCapacityReachedInProcess(noticeId); + return false; + } + + const written = writeMem0ConfigAtomic( + setNoticeState(config, noticeId, nextState), + ); + if (written && !hasNoticeCapRoom(nextState, options)) { + markNoticeCapacityReachedInProcess(noticeId); + } + return written; +} + +function isFirstRunConsumed(config: Record): boolean { + return getNoticeState(config, FIRST_RUN_NOTICE_ID).consumed === true; +} + +function markFirstRunConsumed( + triggerFunction: string, + variant: string, +): boolean { + const config = loadMem0Config(); + const state = getNoticeState(config, FIRST_RUN_NOTICE_ID); + const nextState = { + ...state, + consumed: true, + consumed_at: new Date().toISOString(), + trigger_function: triggerFunction, + variant, + }; + return writeMem0ConfigAtomic( + setNoticeState(config, FIRST_RUN_NOTICE_ID, nextState), + ); +} + +function getDisplayDecision( + noticeId: string, + expectedNoticeType: string, + variant: string, + payload: unknown, +): NoticeDisplayDecision { + const parsed = getNoticeConfigFromPayload(payload, noticeId); + const copy = + typeof parsed.config?.copy === "string" ? parsed.config.copy : undefined; + + if (!parsed.found || !parsed.config) { + return { + displayed: false, + noticeConfigFound: false, + bypassReason: "missing_notice_config", + }; + } + + const noticeConfig = parsed.config; + if (noticeConfig.enabled === false) { + return { + displayed: false, + noticeConfigFound: true, + copy, + bypassReason: "payload_disabled", + disabledReason: "payload_disabled", + }; + } + + if (noticeConfig.notice_type !== expectedNoticeType) { + return { + displayed: false, + noticeConfigFound: true, + copy, + bypassReason: "invalid_notice_type", + }; + } + + if (!copy || copy.trim() === "") { + return { + displayed: false, + noticeConfigFound: true, + bypassReason: "missing_copy", + }; + } + + if (variant !== DISPLAYED_VARIANT) { + return { + displayed: false, + noticeConfigFound: true, + copy, + bypassReason: "holdout", + }; + } + + return { + displayed: true, + noticeConfigFound: true, + copy, + }; +} + +function renderScaleCopy( + template: unknown, + trigger: Pick, +): string | undefined { + if (typeof template !== "string" || template.trim() === "") return undefined; + return template + .replace(/\{top_k\}/g, String(trigger.topK ?? "")) + .replace(/\{topK\}/g, String(trigger.topK ?? "")) + .replace(/\{memory_count\}/g, String(trigger.memoryCount ?? "")); +} + +function getScaleDisplayDecision( + variant: string, + payload: unknown, + trigger: ScaleThresholdTrigger, +): NoticeDisplayDecision { + const parsed = getNoticeConfigFromPayload(payload, SCALE_THRESHOLD_NOTICE_ID); + const copies = + parsed.config?.copies && + typeof parsed.config.copies === "object" && + !Array.isArray(parsed.config.copies) + ? (parsed.config.copies as Record) + : {}; + const copyKey = + trigger.triggerSource === "memory_count" ? "memory_count" : "top_k"; + const copy = renderScaleCopy(copies[copyKey], trigger); + + if (!parsed.found || !parsed.config) { + return { + displayed: false, + noticeConfigFound: false, + bypassReason: "missing_notice_config", + }; + } + + const noticeConfig = parsed.config; + if (noticeConfig.enabled === false) { + return { + displayed: false, + noticeConfigFound: true, + copy, + bypassReason: "payload_disabled", + disabledReason: "payload_disabled", + }; + } + + if (noticeConfig.notice_type !== LOG_LINE_NOTICE_TYPE) { + return { + displayed: false, + noticeConfigFound: true, + copy, + bypassReason: "invalid_notice_type", + }; + } + + if (!copy || copy.trim() === "") { + return { + displayed: false, + noticeConfigFound: true, + bypassReason: "missing_copy", + }; + } + + if (variant !== DISPLAYED_VARIANT) { + return { + displayed: false, + noticeConfigFound: true, + copy, + bypassReason: "holdout", + }; + } + + return { + displayed: true, + noticeConfigFound: true, + copy, + }; +} + +function getFeatureErrorDecision( + noticeId: string, + expectedNoticeType: string, + variant: string, + payload: unknown, +): NoticeDisplayDecision { + const parsed = getNoticeConfigFromPayload(payload, noticeId); + const copy = + typeof parsed.config?.copy === "string" ? parsed.config.copy : undefined; + + if (!parsed.found || !parsed.config) { + return { + displayed: false, + noticeConfigFound: false, + bypassReason: "missing_notice_config", + }; + } + + const noticeConfig = parsed.config; + if (noticeConfig.enabled === false) { + return { + displayed: false, + noticeConfigFound: true, + copy, + bypassReason: "payload_disabled", + disabledReason: "payload_disabled", + }; + } + + if (noticeConfig.notice_type !== expectedNoticeType) { + return { + displayed: false, + noticeConfigFound: true, + copy, + bypassReason: "invalid_notice_type", + }; + } + + if (!copy || copy.trim() === "") { + return { + displayed: false, + noticeConfigFound: true, + bypassReason: "missing_copy", + }; + } + + if (variant !== DISPLAYED_VARIANT && variant !== HOLDOUT_VARIANT) { + return { + displayed: false, + noticeConfigFound: true, + copy, + bypassReason: "not_displayed", + }; + } + + return { + displayed: true, + noticeConfigFound: true, + copy, + }; +} + +export async function getDecayFeatureErrorMessage( + instance: TelemetryInstance, +): Promise { + if (!isTelemetryEnabled()) return DECAY_FEATURE_PLAIN_ERROR; + + try { + const flagEvaluation = await evaluateNoticeFlag(instance.telemetryId); + if (!flagEvaluation) return DECAY_FEATURE_PLAIN_ERROR; + + const decision = getFeatureErrorDecision( + DECAY_FEATURE_NOTICE_ID, + ERROR_NOTICE_TYPE, + flagEvaluation.variant, + flagEvaluation.payload, + ); + + await emitNoticeDisplayed(instance, { + notice_id: DECAY_FEATURE_NOTICE_ID, + notice_type: ERROR_NOTICE_TYPE, + flag_key: NOTICE_FLAG_KEY, + variant: flagEvaluation.variant, + displayed: decision.displayed, + payload: decision.copy, + bypass_reason: decision.bypassReason, + disabled_reason: decision.disabledReason, + notice_config_found: decision.noticeConfigFound, + sync_type: "async", + trigger_function: "update_project", + trigger_parameter: "decay", + }); + + if (decision.displayed && decision.copy) { + return decision.copy; + } + } catch {} + + return DECAY_FEATURE_PLAIN_ERROR; +} + +export async function getTemporalFeatureErrorMessage( + instance: TelemetryInstance, + trigger: TemporalFeatureErrorTrigger, +): Promise { + const plainError = + trigger.triggerParameter === "timestamp" + ? TEMPORAL_TIMESTAMP_PLAIN_ERROR + : TEMPORAL_REFERENCE_DATE_PLAIN_ERROR; + + if (!isTelemetryEnabled()) return plainError; + + try { + const flagEvaluation = await evaluateNoticeFlag(instance.telemetryId); + if (!flagEvaluation) return plainError; + + const decision = getFeatureErrorDecision( + TEMPORAL_FEATURE_NOTICE_ID, + ERROR_NOTICE_TYPE, + flagEvaluation.variant, + flagEvaluation.payload, + ); + + await emitNoticeDisplayed(instance, { + notice_id: TEMPORAL_FEATURE_NOTICE_ID, + notice_type: ERROR_NOTICE_TYPE, + flag_key: NOTICE_FLAG_KEY, + variant: flagEvaluation.variant, + displayed: decision.displayed, + payload: decision.copy, + bypass_reason: decision.bypassReason, + disabled_reason: decision.disabledReason, + notice_config_found: decision.noticeConfigFound, + sync_type: "async", + trigger_function: trigger.triggerFunction, + trigger_parameter: trigger.triggerParameter, + }); + + if (decision.displayed && decision.copy) { + return decision.copy; + } + } catch {} + + return plainError; +} + +export function getDecayUsageDeleteCountAfterSuccess(): number { + if (!isTelemetryEnabled()) return 0; + decayUsageSuccessfulDeleteCount += 1; + return decayUsageSuccessfulDeleteCount; +} + +export function isDecayUsageDeleteEligible(deleteCount: number): boolean { + return deleteCount >= DECAY_USAGE_DELETE_THRESHOLD; +} + +function isRecord(value: unknown): value is Record { + return ( + value !== null && + typeof value === "object" && + !Array.isArray(value) && + !(value instanceof Date) + ); +} + +function isTemporalKey(key: unknown): boolean { + const keyText = String(key).toLowerCase(); + return ( + [ + "date", + "time", + "timestamp", + "datetime", + "event_date", + "reference_date", + "referencedate", + "created_at", + "createdat", + "updated_at", + "updatedat", + "started_at", + "startedat", + "ended_at", + "endedat", + "expires_at", + "expiresat", + ].includes(keyText) || + keyText.endsWith("_date") || + keyText.endsWith("_time") || + keyText.endsWith("_at") || + keyText.includes("timestamp") + ); +} + +function looksTemporalValue(value: unknown, allowEpoch: boolean): boolean { + if (value instanceof Date) return !Number.isNaN(value.getTime()); + if (typeof value === "string") { + return ISO_DATE_RE.test(value) || RELATIVE_TIME_RE.test(value); + } + if (allowEpoch && typeof value === "number" && Number.isFinite(value)) { + return ( + (value >= 946684800 && value <= 4102444800) || + (value >= 946684800000 && value <= 4102444800000) + ); + } + return false; +} + +export function detectTemporalUsageFromMetadata( + metadata: unknown, +): Pick | null { + try { + if (!isRecord(metadata)) return null; + + const visited = new WeakSet(); + const stack: Array<{ value: unknown; parentKey?: string; depth: number }> = + [{ value: metadata, depth: 0 }]; + + while (stack.length > 0) { + const current = stack.pop()!; + if (current.depth > MAX_TEMPORAL_DETECTION_DEPTH) continue; + + if (Array.isArray(current.value)) { + for (const child of current.value) { + if (looksTemporalValue(child, false)) { + return { + triggerSource: "metadata", + triggerReason: "date_like_metadata", + }; + } + stack.push({ + value: child, + parentKey: current.parentKey, + depth: current.depth + 1, + }); + } + continue; + } + + if (!isRecord(current.value)) continue; + if (visited.has(current.value)) continue; + visited.add(current.value); + + for (const [key, value] of Object.entries(current.value)) { + const temporalKey = isTemporalKey(key); + if ( + (temporalKey && looksTemporalValue(value, true)) || + looksTemporalValue(value, false) + ) { + return { + triggerSource: "metadata", + triggerReason: "date_like_metadata", + }; + } + if (isRecord(value) || Array.isArray(value)) { + stack.push({ value, parentKey: key, depth: current.depth + 1 }); + } + } + } + } catch {} + + return null; +} + +function hasTemporalFilter(filters: unknown): boolean { + try { + if (!isRecord(filters)) return false; + + const visited = new WeakSet(); + const stack: Array<{ value: unknown; depth: number }> = [ + { value: filters, depth: 0 }, + ]; + + while (stack.length > 0) { + const current = stack.pop()!; + if (current.depth > MAX_TEMPORAL_DETECTION_DEPTH) continue; + + if (Array.isArray(current.value)) { + for (const child of current.value) { + stack.push({ value: child, depth: current.depth + 1 }); + } + continue; + } + + if (!isRecord(current.value)) continue; + if (visited.has(current.value)) continue; + visited.add(current.value); + + for (const [key, value] of Object.entries(current.value)) { + if (["AND", "OR", "NOT", "$and", "$or", "$not"].includes(key)) { + if (isRecord(value) || Array.isArray(value)) { + stack.push({ value, depth: current.depth + 1 }); + } + continue; + } + + const temporalKey = isTemporalKey(key); + if (isRecord(value)) { + const rangeValues = Object.entries(value) + .filter(([operator]) => RANGE_OPERATORS.has(operator)) + .map(([, rangeValue]) => rangeValue); + if ( + rangeValues.length > 0 && + (temporalKey || + rangeValues.some((rangeValue) => + looksTemporalValue(rangeValue, temporalKey), + )) + ) { + return true; + } + stack.push({ value, depth: current.depth + 1 }); + } else if (temporalKey && looksTemporalValue(value, true)) { + return true; + } + } + } + } catch {} + + return false; +} + +export function detectTemporalUsageFromSearch( + query: unknown, + filters: unknown, +): Pick | null { + try { + if (typeof query === "string") { + if (RELATIVE_TIME_RE.test(query)) { + return { triggerSource: "query", triggerReason: "relative_phrase" }; + } + if (ISO_DATE_RE.test(query)) { + return { triggerSource: "query", triggerReason: "date_like_query" }; + } + } + + if (hasTemporalFilter(filters)) { + return { triggerSource: "filter", triggerReason: "date_range_filter" }; + } + } catch {} + + return null; +} + +function coerceNonnegativeInteger(value: unknown): number | null { + if (typeof value === "boolean") return null; + const parsed = Number(value); + if (!Number.isFinite(parsed) || !Number.isInteger(parsed) || parsed < 0) { + return null; + } + return parsed; +} + +function countAddedMemories(addResult: unknown): number { + const results = + isRecord(addResult) && Array.isArray(addResult.results) + ? addResult.results + : addResult; + + if (!Array.isArray(results)) return 0; + + return results.filter((item) => { + if (!isRecord(item)) return false; + const metadata = item.metadata; + return isRecord(metadata) && metadata.event === "ADD"; + }).length; +} + +function extractProviderCount(info: unknown): number | null { + if (!info) return null; + if (typeof info === "number") return coerceNonnegativeInteger(info); + + if (isRecord(info)) { + for (const key of [ + "count", + "points_count", + "vectors_count", + "indexed_vectors_count", + ]) { + const value = coerceNonnegativeInteger(info[key]); + if (value !== null) return value; + } + + const result = extractProviderCount(info.result); + if (result !== null) return result; + } + + return null; +} + +async function getProviderMemoryCount( + memoryInstance: unknown, +): Promise { + try { + const vectorStore = (memoryInstance as any)?.vectorStore; + if (!vectorStore) return null; + + if (typeof vectorStore.count === "function") { + const value = extractProviderCount(await vectorStore.count()); + if (value !== null) return value; + } + + const collectionName = vectorStore.collectionName; + const client = vectorStore.client; + + if (client && collectionName && typeof client.count === "function") { + const value = extractProviderCount( + await client.count(collectionName, { exact: true }), + ); + if (value !== null) return value; + } + + if ( + client && + collectionName && + typeof client.getCollection === "function" + ) { + const value = extractProviderCount( + await client.getCollection(collectionName), + ); + if (value !== null) return value; + } + } catch {} + + return null; +} + +function markScaleMemoryCountThresholdEvaluated(): boolean { + try { + const config = loadMem0Config(); + const state = getNoticeState(config, SCALE_THRESHOLD_NOTICE_ID); + if (state.memory_count_threshold_evaluated === true) { + scaleMemoryCountThresholdEvaluatedInProcess = true; + return false; + } + + const nextState = { + ...state, + memory_count_threshold_evaluated: true, + }; + const written = writeMem0ConfigAtomic( + setNoticeState(config, SCALE_THRESHOLD_NOTICE_ID, nextState), + ); + if (written) scaleMemoryCountThresholdEvaluatedInProcess = true; + return written; + } catch { + return false; + } +} + +export function detectScaleThresholdFromTopK( + topK: unknown, +): Omit | null { + const topKValue = coerceNonnegativeInteger(topK); + if (topKValue === null || topKValue < SCALE_TOP_K_THRESHOLD) return null; + + return { + triggerSource: "top_k", + triggerReason: "high_top_k", + topK: topKValue, + threshold: SCALE_TOP_K_THRESHOLD, + }; +} + +export async function detectScaleThresholdFromAddResult( + memoryInstance: unknown, + addResult: unknown, +): Promise | null> { + if (!isTelemetryEnabled()) return null; + + const addedCount = countAddedMemories(addResult); + if (addedCount === 0) return null; + + try { + if (scaleMemoryCountThresholdEvaluatedInProcess) return null; + + scaleMemoryCountAddsSinceCheck += addedCount; + const shouldCheck = + !scaleMemoryCountCheckedInProcess || + scaleMemoryCountAddsSinceCheck >= SCALE_MEMORY_COUNT_CHECK_INTERVAL; + if (!shouldCheck) return null; + + scaleMemoryCountCheckedInProcess = true; + scaleMemoryCountAddsSinceCheck = 0; + + const config = loadMem0Config(); + const state = getNoticeState(config, SCALE_THRESHOLD_NOTICE_ID); + if (state.memory_count_threshold_evaluated === true) { + scaleMemoryCountThresholdEvaluatedInProcess = true; + return null; + } + + if (!hasNoticeCapRoom(state)) return null; + } catch { + return null; + } + + const providerCount = await getProviderMemoryCount(memoryInstance); + if (providerCount === null || providerCount < SCALE_MEMORY_COUNT_THRESHOLD) { + return null; + } + + if (!markScaleMemoryCountThresholdEvaluated()) return null; + + return { + triggerSource: "memory_count", + triggerReason: "memory_count_threshold", + memoryCount: providerCount, + threshold: SCALE_MEMORY_COUNT_THRESHOLD, + }; +} + +export function detectPerformanceSlowQuery( + elapsedMs: unknown, + topK: unknown, + resultCount: unknown, +): Omit< + PerformanceSlowQueryTrigger, + "triggerFunction" | "triggerReason" +> | null { + const elapsedMsValue = coerceNonnegativeInteger( + Math.round(Number(elapsedMs)), + ); + const topKValue = coerceNonnegativeInteger(topK); + const resultCountValue = coerceNonnegativeInteger(resultCount); + + if ( + elapsedMsValue === null || + topKValue === null || + resultCountValue === null || + elapsedMsValue <= PERFORMANCE_SLOW_QUERY_THRESHOLD_MS + ) { + return null; + } + + return { + elapsedMs: elapsedMsValue, + thresholdMs: PERFORMANCE_SLOW_QUERY_THRESHOLD_MS, + topK: topKValue, + resultCount: resultCountValue, + }; +} + +export async function displayScaleThresholdNotice( + instance: TelemetryInstance, + trigger: ScaleThresholdTrigger, +): Promise { + if (!isTelemetryEnabled()) return; + if (isNoticeCapacityReachedInProcess(SCALE_THRESHOLD_NOTICE_ID)) return; + + try { + const config = loadMem0Config(); + const state = getNoticeState(config, SCALE_THRESHOLD_NOTICE_ID); + if (!hasNoticeCapRoomForNotice(SCALE_THRESHOLD_NOTICE_ID, state)) return; + + const flagEvaluation = await evaluateNoticeFlag(instance.telemetryId); + if (!flagEvaluation) return; + + const decision = getScaleDisplayDecision( + flagEvaluation.variant, + flagEvaluation.payload, + trigger, + ); + + const opportunity = { + variant: flagEvaluation.variant, + sync_type: "async", + trigger_function: trigger.triggerFunction, + trigger_source: trigger.triggerSource, + trigger_reason: trigger.triggerReason, + ...(trigger.topK !== undefined && { top_k: trigger.topK }), + ...(trigger.memoryCount !== undefined && { + memory_count: trigger.memoryCount, + }), + threshold: trigger.threshold, + }; + + if (!recordNoticeOpportunity(SCALE_THRESHOLD_NOTICE_ID, opportunity)) { + return; + } + + await emitNoticeDisplayed(instance, { + notice_id: SCALE_THRESHOLD_NOTICE_ID, + notice_type: LOG_LINE_NOTICE_TYPE, + flag_key: NOTICE_FLAG_KEY, + variant: flagEvaluation.variant, + displayed: decision.displayed, + payload: decision.copy, + bypass_reason: decision.bypassReason, + disabled_reason: decision.disabledReason, + notice_config_found: decision.noticeConfigFound, + sync_type: "async", + trigger_function: trigger.triggerFunction, + trigger_source: trigger.triggerSource, + trigger_reason: trigger.triggerReason, + top_k: trigger.topK, + memory_count: trigger.memoryCount, + threshold: trigger.threshold, + }); + + if (decision.displayed && decision.copy) { + process.stderr.write(`${decision.copy}\n`); + } + } catch {} +} + +export async function displayPerformanceSlowQueryNotice( + instance: TelemetryInstance, + trigger: PerformanceSlowQueryTrigger, +): Promise { + if (!isTelemetryEnabled()) return; + if (isNoticeCapacityReachedInProcess(PERFORMANCE_SLOW_QUERY_NOTICE_ID)) { + return; + } + + try { + const config = loadMem0Config(); + const state = getNoticeState(config, PERFORMANCE_SLOW_QUERY_NOTICE_ID); + if (!hasNoticeCapRoomForNotice(PERFORMANCE_SLOW_QUERY_NOTICE_ID, state)) { + return; + } + + const flagEvaluation = await evaluateNoticeFlag(instance.telemetryId); + if (!flagEvaluation) return; + + const decision = getDisplayDecision( + PERFORMANCE_SLOW_QUERY_NOTICE_ID, + LOG_LINE_NOTICE_TYPE, + flagEvaluation.variant, + flagEvaluation.payload, + ); + + const opportunity = { + variant: flagEvaluation.variant, + sync_type: "async", + trigger_function: trigger.triggerFunction, + trigger_reason: trigger.triggerReason, + }; + + if ( + !recordNoticeOpportunity(PERFORMANCE_SLOW_QUERY_NOTICE_ID, opportunity) + ) { + return; + } + + await emitNoticeDisplayed(instance, { + notice_id: PERFORMANCE_SLOW_QUERY_NOTICE_ID, + notice_type: LOG_LINE_NOTICE_TYPE, + flag_key: NOTICE_FLAG_KEY, + variant: flagEvaluation.variant, + displayed: decision.displayed, + payload: decision.copy, + bypass_reason: decision.bypassReason, + disabled_reason: decision.disabledReason, + notice_config_found: decision.noticeConfigFound, + sync_type: "async", + trigger_function: trigger.triggerFunction, + trigger_reason: trigger.triggerReason, + elapsed_ms: trigger.elapsedMs, + threshold_ms: trigger.thresholdMs, + top_k: trigger.topK, + result_count: trigger.resultCount, + }); + + if (decision.displayed && decision.copy) { + process.stderr.write(`${decision.copy}\n`); + } + } catch {} +} + +export async function displayTemporalUsageNotice( + instance: TelemetryInstance, + trigger: TemporalUsageTrigger, +): Promise { + if (!isTelemetryEnabled()) return; + if (isNoticeCapacityReachedInProcess(TEMPORAL_USAGE_NOTICE_ID)) return; + + try { + const config = loadMem0Config(); + const state = getNoticeState(config, TEMPORAL_USAGE_NOTICE_ID); + if (!hasNoticeCapRoomForNotice(TEMPORAL_USAGE_NOTICE_ID, state)) return; + + const flagEvaluation = await evaluateNoticeFlag(instance.telemetryId); + if (!flagEvaluation) return; + + const decision = getDisplayDecision( + TEMPORAL_USAGE_NOTICE_ID, + LOG_LINE_NOTICE_TYPE, + flagEvaluation.variant, + flagEvaluation.payload, + ); + + const opportunity = { + variant: flagEvaluation.variant, + sync_type: "async", + trigger_function: trigger.triggerFunction, + trigger_source: trigger.triggerSource, + trigger_reason: trigger.triggerReason, + }; + + if (!recordNoticeOpportunity(TEMPORAL_USAGE_NOTICE_ID, opportunity)) { + return; + } + + await emitNoticeDisplayed(instance, { + notice_id: TEMPORAL_USAGE_NOTICE_ID, + notice_type: LOG_LINE_NOTICE_TYPE, + flag_key: NOTICE_FLAG_KEY, + variant: flagEvaluation.variant, + displayed: decision.displayed, + payload: decision.copy, + bypass_reason: decision.bypassReason, + disabled_reason: decision.disabledReason, + notice_config_found: decision.noticeConfigFound, + sync_type: "async", + trigger_function: trigger.triggerFunction, + trigger_source: trigger.triggerSource, + trigger_reason: trigger.triggerReason, + }); + + if (decision.displayed && decision.copy) { + process.stderr.write(`${decision.copy}\n`); + } + } catch {} +} + +export async function displayDecayUsageNotice( + instance: TelemetryInstance, + trigger: DecayUsageTrigger, +): Promise { + if (!isTelemetryEnabled()) return; + if (isNoticeCapacityReachedInProcess(DECAY_USAGE_NOTICE_ID)) return; + + try { + const config = loadMem0Config(); + const state = getNoticeState(config, DECAY_USAGE_NOTICE_ID); + if (!hasNoticeCapRoomForNotice(DECAY_USAGE_NOTICE_ID, state)) return; + + const flagEvaluation = await evaluateNoticeFlag(instance.telemetryId); + if (!flagEvaluation) return; + + const decision = getDisplayDecision( + DECAY_USAGE_NOTICE_ID, + LOG_LINE_NOTICE_TYPE, + flagEvaluation.variant, + flagEvaluation.payload, + ); + + const opportunity = { + variant: flagEvaluation.variant, + sync_type: "async", + trigger_function: trigger.triggerFunction, + trigger_source: trigger.triggerSource, + trigger_reason: trigger.triggerReason, + ...(trigger.deleteCount !== undefined && { + delete_count: trigger.deleteCount, + }), + ...(trigger.deletedCount !== undefined && { + deleted_count: trigger.deletedCount, + }), + }; + + if (!recordNoticeOpportunity(DECAY_USAGE_NOTICE_ID, opportunity)) { + return; + } + + await emitNoticeDisplayed(instance, { + notice_id: DECAY_USAGE_NOTICE_ID, + notice_type: LOG_LINE_NOTICE_TYPE, + flag_key: NOTICE_FLAG_KEY, + variant: flagEvaluation.variant, + displayed: decision.displayed, + payload: decision.copy, + bypass_reason: decision.bypassReason, + disabled_reason: decision.disabledReason, + notice_config_found: decision.noticeConfigFound, + sync_type: "async", + trigger_function: trigger.triggerFunction, + trigger_source: trigger.triggerSource, + trigger_reason: trigger.triggerReason, + ...(trigger.deleteCount !== undefined && { + delete_count: trigger.deleteCount, + }), + ...(trigger.deletedCount !== undefined && { + deleted_count: trigger.deletedCount, + }), + }); + + if (decision.displayed && decision.copy) { + process.stderr.write(`${decision.copy}\n`); + } + } catch {} +} + +export async function displayFirstRunNotice( + instance: TelemetryInstance, + triggerFunction: string, +): Promise { + if (!isTelemetryEnabled()) return; + if (firstRunConsumedInProcess || firstRunClaimInProgress) return; + + const config = loadMem0Config(); + if (isFirstRunConsumed(config)) { + firstRunConsumedInProcess = true; + return; + } + + firstRunClaimInProgress = true; + try { + const flagEvaluation = await evaluateNoticeFlag(instance.telemetryId); + if (!flagEvaluation) { + firstRunClaimInProgress = false; + return; + } + + const decision = getDisplayDecision( + FIRST_RUN_NOTICE_ID, + LOG_LINE_NOTICE_TYPE, + flagEvaluation.variant, + flagEvaluation.payload, + ); + + firstRunConsumedInProcess = true; + markFirstRunConsumed(triggerFunction, flagEvaluation.variant); + + await emitNoticeDisplayed(instance, { + notice_id: FIRST_RUN_NOTICE_ID, + notice_type: LOG_LINE_NOTICE_TYPE, + flag_key: NOTICE_FLAG_KEY, + variant: flagEvaluation.variant, + displayed: decision.displayed, + payload: decision.copy, + bypass_reason: decision.bypassReason, + disabled_reason: decision.disabledReason, + notice_config_found: decision.noticeConfigFound, + sync_type: "async", + trigger_function: triggerFunction, + }); + + if (decision.displayed && decision.copy) { + process.stderr.write(`${decision.copy}\n`); + } + } catch { + if (!firstRunConsumedInProcess) firstRunClaimInProgress = false; + } finally { + if (firstRunConsumedInProcess) firstRunClaimInProgress = false; + } +} + +export async function emitNoticeDisplayed( + instance: TelemetryInstance, + properties: Record, +): Promise { + if (!isTelemetryEnabled()) return; + try { + await captureNoticeEvent(instance, properties); + } catch {} +} + +export const __noticeTestHooks = { + appendNoticeCapEvent, + emitNoticeDisplayed, + evaluateNoticeFlag, + displayFirstRunNotice, + displayDecayUsageNotice, + displayTemporalUsageNotice, + displayScaleThresholdNotice, + displayPerformanceSlowQueryNotice, + detectPerformanceSlowQuery, + detectScaleThresholdFromAddResult, + detectScaleThresholdFromTopK, + detectTemporalUsageFromMetadata, + detectTemporalUsageFromSearch, + getDecayFeatureErrorMessage, + getTemporalFeatureErrorMessage, + getDecayUsageDeleteCountAfterSuccess, + getMem0ConfigPath, + getMem0Dir, + getNoticeConfigFromPayload, + getNoticeState, + hasNoticeCapRoom, + isDecayUsageDeleteEligible, + loadMem0Config, + recordNoticeOpportunity, + setNoticeState, + writeMem0ConfigAtomic, +}; diff --git a/mem0-ts/src/oss/src/utils/telemetry.ts b/mem0-ts/src/oss/src/utils/telemetry.ts index 2f34416f7..0be4869cb 100644 --- a/mem0-ts/src/oss/src/utils/telemetry.ts +++ b/mem0-ts/src/oss/src/utils/telemetry.ts @@ -13,10 +13,12 @@ let version = // Safely check for process.env in different environments let MEM0_TELEMETRY = true; try { - MEM0_TELEMETRY = process?.env?.MEM0_TELEMETRY === "false" ? false : true; + MEM0_TELEMETRY = + process?.env?.MEM0_TELEMETRY?.toLowerCase() === "false" ? false : true; } catch (error) {} const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX"; const POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/"; +const NOTICE_EVENT_NAME = "mem0.notice_displayed"; // Default sampling rate for hot-path OSS events. Lifecycle events always fire at 100%. // Override via MEM0_TELEMETRY_SAMPLE_RATE env var. Mirrors mem0/memory/telemetry.py. @@ -35,7 +37,11 @@ const MEM0_TELEMETRY_SAMPLE_RATE: number = ((): number => { })(); // Events that bypass sampling. Keep in sync with _captureEvent call sites in memory/index.ts. -const LIFECYCLE_EVENTS: ReadonlySet = new Set(["init", "reset"]); +const ALWAYS_SEND_EVENTS: ReadonlySet = new Set([ + "init", + "reset", + "notice_displayed", +]); class UnifiedTelemetry implements TelemetryClient { private apiKey: string; @@ -91,6 +97,10 @@ class UnifiedTelemetry implements TelemetryClient { const telemetry = new UnifiedTelemetry(POSTHOG_API_KEY, POSTHOG_HOST); +function isTelemetryEnabled(): boolean { + return MEM0_TELEMETRY; +} + async function captureClientEvent( eventName: string, instance: TelemetryInstance, @@ -102,8 +112,8 @@ async function captureClientEvent( } // >= so that rate=0 drops everything and rate=1 keeps everything (Math.random() ∈ [0, 1)). - const isLifecycle = LIFECYCLE_EVENTS.has(eventName); - if (!isLifecycle && Math.random() >= MEM0_TELEMETRY_SAMPLE_RATE) { + const alwaysSend = ALWAYS_SEND_EVENTS.has(eventName); + if (!alwaysSend && Math.random() >= MEM0_TELEMETRY_SAMPLE_RATE) { return; } @@ -116,7 +126,7 @@ async function captureClientEvent( client_source: "nodejs", ...additionalData, // sample_rate set AFTER the spread so callers can never override it - sample_rate: isLifecycle ? 1.0 : MEM0_TELEMETRY_SAMPLE_RATE, + sample_rate: alwaysSend ? 1.0 : MEM0_TELEMETRY_SAMPLE_RATE, }; await telemetry.captureEvent( @@ -126,8 +136,36 @@ async function captureClientEvent( ); } -function isTelemetryEnabled(): boolean { - return MEM0_TELEMETRY; +async function captureNoticeEvent( + instance: TelemetryInstance, + properties: Record = {}, +) { + if (!instance.telemetryId) return; + + const eventData: TelemetryEventData = { + function: `${instance.constructor.name}`, + method: "notice_displayed", + api_host: instance.host, + timestamp: new Date().toISOString(), + client_version: version, + client_source: "nodejs", + ...properties, + sample_rate: 1.0, + }; + + await telemetry.captureEvent( + instance.telemetryId, + NOTICE_EVENT_NAME, + eventData, + ); } -export { telemetry, captureClientEvent, isTelemetryEnabled }; +export { + POSTHOG_API_KEY, + POSTHOG_HOST, + NOTICE_EVENT_NAME, + telemetry, + captureClientEvent, + captureNoticeEvent, + isTelemetryEnabled, +}; diff --git a/mem0-ts/src/oss/tests/config-manager.test.ts b/mem0-ts/src/oss/tests/config-manager.test.ts index 035f44733..76bd780ad 100644 --- a/mem0-ts/src/oss/tests/config-manager.test.ts +++ b/mem0-ts/src/oss/tests/config-manager.test.ts @@ -418,6 +418,7 @@ describe("Memory – LM Studio end-to-end flow", () => { })); jest.doMock("../src/utils/telemetry", () => ({ captureClientEvent: jest.fn().mockResolvedValue(undefined), + isTelemetryEnabled: jest.fn(() => false), })); MemoryClass = require("../src/memory").Memory; diff --git a/mem0-ts/src/oss/tests/dimension-autodetect.test.ts b/mem0-ts/src/oss/tests/dimension-autodetect.test.ts index 55398eae3..ad37d1297 100644 --- a/mem0-ts/src/oss/tests/dimension-autodetect.test.ts +++ b/mem0-ts/src/oss/tests/dimension-autodetect.test.ts @@ -290,6 +290,7 @@ describe("Memory – auto-initialization", () => { jest.doMock("../src/utils/telemetry", () => ({ captureClientEvent: jest.fn().mockResolvedValue(undefined), + isTelemetryEnabled: jest.fn(() => false), })); MemoryClass = require("../src/memory").Memory; diff --git a/mem0-ts/src/oss/tests/notices.decay-feature.test.ts b/mem0-ts/src/oss/tests/notices.decay-feature.test.ts new file mode 100644 index 000000000..111f2bad8 --- /dev/null +++ b/mem0-ts/src/oss/tests/notices.decay-feature.test.ts @@ -0,0 +1,387 @@ +/// +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; + +jest.setTimeout(15000); + +jest.mock("../src/embeddings/google", () => ({ + GoogleEmbedder: jest.fn(), +})); +jest.mock("../src/llms/google", () => ({ + GoogleLLM: jest.fn(), +})); + +const mockEmbedding = new Array(1536).fill(0.1); +jest.mock("../src/embeddings/openai", () => ({ + OpenAIEmbedder: jest.fn().mockImplementation(() => ({ + embed: jest.fn().mockResolvedValue(mockEmbedding), + embedBatch: jest + .fn() + .mockImplementation((texts: string[]) => + Promise.resolve(texts.map(() => mockEmbedding)), + ), + embeddingDims: 1536, + })), +})); + +jest.mock("../src/llms/openai", () => ({ + OpenAILLM: jest.fn().mockImplementation(() => ({ + generateResponse: jest.fn(), + })), +})); + +const DECAY_COPY = + 'Memory decay requires Mem0 Platform. Get a free API key at https://app.mem0.ai?utm_source=oss_sdk&utm_medium=in_product&utm_campaign=decay_stub&utm_content=node_error and use `import MemoryClient from "mem0ai"`.'; +const PLAIN_DECAY_ERROR = + "The decay parameter is not supported by the OSS Memory SDK."; +const PROJECT_UPDATE_ERROR = + "Project updates are not supported by the OSS Memory SDK."; + +function makeTempMem0Dir(): string { + return fs.mkdtempSync(path.join(os.tmpdir(), "mem0-node-decay-feature-")); +} + +function decayPayload(overrides: Record = {}) { + return { + notices: { + decay_stub: { + enabled: true, + notice_type: "error", + copy: DECAY_COPY, + ...overrides, + }, + }, + }; +} + +function createFetchMock(options: { + variant?: string; + payload?: unknown; + failFlags?: boolean; + flagEnabled?: boolean; +}) { + const calls: any[] = []; + const fetchMock = jest.fn(async (url: string | URL, init?: RequestInit) => { + const target = String(url); + + if (target.includes("/flags")) { + if (options.failFlags) { + throw new Error("flag evaluation failed"); + } + return { + ok: true, + json: jest.fn().mockResolvedValue({ + flags: { + "mem0-oss-notices": { + key: "mem0-oss-notices", + enabled: options.flagEnabled ?? true, + variant: options.variant ?? "displayed", + metadata: { + payload: + options.payload === undefined + ? JSON.stringify(decayPayload()) + : options.payload, + }, + }, + }, + }), + }; + } + + if (target.includes("/i/v0/e/")) { + calls.push(JSON.parse(String(init?.body))); + return { + ok: true, + text: jest.fn().mockResolvedValue(""), + }; + } + + return { + ok: true, + json: jest.fn().mockResolvedValue({}), + text: jest.fn().mockResolvedValue(""), + }; + }); + + return { fetchMock, calls }; +} + +function noticeEvents(calls: any[]) { + return calls.filter((call) => call.event === "mem0.notice_displayed"); +} + +async function createMemory() { + const { Memory } = await import("../src/memory"); + return new Memory({ + version: "v1.1", + embedder: { + provider: "openai", + config: { apiKey: "test-key", model: "text-embedding-3-small" }, + }, + vectorStore: { + provider: "memory", + config: { + collectionName: `test-decay-feature-${Date.now()}-${Math.random()}`, + dimension: 1536, + dbPath: ":memory:", + }, + }, + llm: { + provider: "openai", + config: { apiKey: "test-key", model: "gpt-5-mini" }, + }, + historyDbPath: ":memory:", + }); +} + +describe("Node OSS decay feature error notice", () => { + let originalMem0Dir: string | undefined; + let originalTelemetry: string | undefined; + let originalSampleRate: string | undefined; + let originalFetch: typeof global.fetch; + + beforeEach(() => { + originalMem0Dir = process.env.MEM0_DIR; + originalTelemetry = process.env.MEM0_TELEMETRY; + originalSampleRate = process.env.MEM0_TELEMETRY_SAMPLE_RATE; + originalFetch = global.fetch; + + process.env.MEM0_DIR = makeTempMem0Dir(); + process.env.MEM0_TELEMETRY = "true"; + process.env.MEM0_TELEMETRY_SAMPLE_RATE = "1"; + jest.resetModules(); + }); + + afterEach(() => { + if (originalMem0Dir === undefined) delete process.env.MEM0_DIR; + else process.env.MEM0_DIR = originalMem0Dir; + + if (originalTelemetry === undefined) delete process.env.MEM0_TELEMETRY; + else process.env.MEM0_TELEMETRY = originalTelemetry; + + if (originalSampleRate === undefined) { + delete process.env.MEM0_TELEMETRY_SAMPLE_RATE; + } else { + process.env.MEM0_TELEMETRY_SAMPLE_RATE = originalSampleRate; + } + + global.fetch = originalFetch; + jest.restoreAllMocks(); + jest.resetModules(); + }); + + it("raises CTA copy for displayed and emits displayed=true", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.updateProject({ decay: true })).rejects.toThrow( + DECAY_COPY, + ); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "decay_stub", + notice_type: "error", + flag_key: "mem0-oss-notices", + variant: "displayed", + displayed: true, + payload: DECAY_COPY, + notice_config_found: true, + sync_type: "async", + trigger_function: "update_project", + trigger_parameter: "decay", + sample_rate: 1, + }), + ); + }); + + it("raises CTA copy for holdout and emits displayed=true", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "holdout" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.updateProject({ decay: true })).rejects.toThrow( + DECAY_COPY, + ); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "decay_stub", + variant: "holdout", + displayed: true, + trigger_function: "update_project", + trigger_parameter: "decay", + }), + ); + expect(notices[0].properties.bypass_reason).toBeUndefined(); + }); + + it("uses plain error for unknown future variants and emits not_displayed", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "silent" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.updateProject({ decay: true })).rejects.toThrow( + PLAIN_DECAY_ERROR, + ); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "decay_stub", + variant: "silent", + displayed: false, + bypass_reason: "not_displayed", + payload: DECAY_COPY, + }), + ); + }); + + it("uses plain error for disabled payload and emits payload_disabled", async () => { + const { fetchMock, calls } = createFetchMock({ + variant: "holdout", + payload: JSON.stringify(decayPayload({ enabled: false })), + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.updateProject({ decay: true })).rejects.toThrow( + PLAIN_DECAY_ERROR, + ); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "decay_stub", + displayed: false, + bypass_reason: "payload_disabled", + disabled_reason: "payload_disabled", + notice_config_found: true, + }), + ); + }); + + it.each([ + [ + "missing config", + JSON.stringify({ notices: {} }), + "missing_notice_config", + ], + [ + "missing copy", + JSON.stringify(decayPayload({ copy: "" })), + "missing_copy", + ], + ["malformed payload", "{not-json", "missing_notice_config"], + ])("uses plain error for %s", async (_label, payload, bypassReason) => { + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + payload, + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.updateProject({ decay: true })).rejects.toThrow( + PLAIN_DECAY_ERROR, + ); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "decay_stub", + displayed: false, + bypass_reason: bypassReason, + }), + ); + }); + + it("uses plain error and emits no event when the blunt flag is disabled", async () => { + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + flagEnabled: false, + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.updateProject({ decay: true })).rejects.toThrow( + PLAIN_DECAY_ERROR, + ); + + expect(noticeEvents(calls)).toHaveLength(0); + }); + + it("uses plain error and emits no event when PostHog fails", async () => { + const { fetchMock, calls } = createFetchMock({ failFlags: true }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.updateProject({ decay: true })).rejects.toThrow( + PLAIN_DECAY_ERROR, + ); + + expect(noticeEvents(calls)).toHaveLength(0); + }); + + it("uses plain error and skips flag evaluation when telemetry is off", async () => { + process.env.MEM0_TELEMETRY = "False"; + jest.resetModules(); + + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.updateProject({ decay: true })).rejects.toThrow( + PLAIN_DECAY_ERROR, + ); + + expect(fetchMock).not.toHaveBeenCalled(); + expect(noticeEvents(calls)).toHaveLength(0); + }); + + it.each([ + ["empty options", {}], + ["decay false", { decay: false }], + ["non-decay options", { customInstructions: "Updated" }], + ])("does not emit notice telemetry for %s", async (_label, options) => { + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.updateProject(options)).rejects.toThrow( + PROJECT_UPDATE_ERROR, + ); + + expect( + fetchMock.mock.calls.filter(([url]) => String(url).includes("/flags")), + ).toHaveLength(0); + expect(noticeEvents(calls)).toHaveLength(0); + }); + + it("does not trigger first-run notice or update_project telemetry", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.updateProject({ decay: true })).rejects.toThrow( + DECAY_COPY, + ); + + const eventNames = calls.map((call) => call.event); + expect(eventNames).toContain("mem0.notice_displayed"); + expect(eventNames).not.toContain("mem0.update_project"); + expect( + noticeEvents(calls).some( + (call) => call.properties.notice_id === "first_run", + ), + ).toBe(false); + }); +}); diff --git a/mem0-ts/src/oss/tests/notices.decay-usage.test.ts b/mem0-ts/src/oss/tests/notices.decay-usage.test.ts new file mode 100644 index 000000000..a60770135 --- /dev/null +++ b/mem0-ts/src/oss/tests/notices.decay-usage.test.ts @@ -0,0 +1,526 @@ +/// +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; + +jest.setTimeout(15000); + +jest.mock("../src/embeddings/google", () => ({ + GoogleEmbedder: jest.fn(), +})); +jest.mock("../src/llms/google", () => ({ + GoogleLLM: jest.fn(), +})); + +const mockEmbedding = new Array(1536).fill(0.1); +jest.mock("../src/embeddings/openai", () => ({ + OpenAIEmbedder: jest.fn().mockImplementation(() => ({ + embed: jest.fn().mockResolvedValue(mockEmbedding), + embedBatch: jest + .fn() + .mockImplementation((texts: string[]) => + Promise.resolve(texts.map(() => mockEmbedding)), + ), + embeddingDims: 1536, + })), +})); + +jest.mock("../src/llms/openai", () => ({ + OpenAILLM: jest.fn().mockImplementation(() => ({ + generateResponse: jest.fn(), + })), +})); + +const DECAY_USAGE_COPY = + "Tip: Python fallback copy with memory.project.update(decay=True)."; + +function makeTempMem0Dir(): string { + return fs.mkdtempSync(path.join(os.tmpdir(), "mem0-node-decay-usage-")); +} + +function configPath(): string { + return path.join(process.env.MEM0_DIR as string, "config.json"); +} + +function readConfig(): Record { + const file = configPath(); + if (!fs.existsSync(file)) return {}; + return JSON.parse(fs.readFileSync(file, "utf8")); +} + +function writeConfig(config: Record) { + fs.mkdirSync(path.dirname(configPath()), { recursive: true }); + fs.writeFileSync(configPath(), JSON.stringify(config, null, 4)); +} + +function consumeFirstRun() { + writeConfig({ + user_id: "node-decay-usage-test-user", + notice_state: { + first_run: { + consumed: true, + trigger_function: "test_setup", + variant: "test", + }, + }, + }); +} + +function decayUsagePayload(overrides: Record = {}) { + return { + notices: { + decay_usage: { + enabled: true, + notice_type: "log_line", + copy: DECAY_USAGE_COPY, + ...overrides, + }, + }, + }; +} + +function createFetchMock(options: { + variant?: string; + payload?: unknown; + failFlags?: boolean; + flagEnabled?: boolean; +}) { + const calls: any[] = []; + const fetchMock = jest.fn(async (url: string | URL, init?: RequestInit) => { + const target = String(url); + + if (target.includes("/flags")) { + if (options.failFlags) { + throw new Error("flag evaluation failed"); + } + const payload = + options.payload === undefined + ? JSON.stringify(decayUsagePayload()) + : options.payload; + return { + ok: true, + json: jest.fn().mockResolvedValue({ + flags: { + "mem0-oss-notices": { + key: "mem0-oss-notices", + enabled: options.flagEnabled ?? true, + variant: options.variant ?? "displayed", + metadata: { payload }, + }, + }, + }), + }; + } + + if (target.includes("/i/v0/e/")) { + calls.push(JSON.parse(String(init?.body))); + return { + ok: true, + text: jest.fn().mockResolvedValue(""), + }; + } + + return { + ok: true, + json: jest.fn().mockResolvedValue({}), + text: jest.fn().mockResolvedValue(""), + }; + }); + + return { fetchMock, calls }; +} + +function noticeEvents(calls: any[]) { + return calls.filter((call) => call.event === "mem0.notice_displayed"); +} + +function flagRequestCount(fetchMock: jest.Mock) { + return fetchMock.mock.calls.filter(([url]) => String(url).includes("/flags")) + .length; +} + +async function createMemory() { + const { Memory } = await import("../src/memory"); + return new Memory({ + version: "v1.1", + embedder: { + provider: "openai", + config: { apiKey: "test-key", model: "text-embedding-3-small" }, + }, + vectorStore: { + provider: "memory", + config: { + collectionName: `test-decay-usage-${Date.now()}-${Math.random()}`, + dimension: 1536, + dbPath: ":memory:", + }, + }, + llm: { + provider: "openai", + config: { apiKey: "test-key", model: "gpt-5-mini" }, + }, + historyDbPath: ":memory:", + }); +} + +async function addMemories(memory: any, userId: string, count: number) { + const ids: string[] = []; + for (let index = 0; index < count; index++) { + const result = await memory.add(`Decay usage memory ${index}`, { + userId, + infer: false, + }); + ids.push(result.results[0].id); + } + return ids; +} + +describe("Node OSS decay usage notice", () => { + let originalMem0Dir: string | undefined; + let originalTelemetry: string | undefined; + let originalSampleRate: string | undefined; + let originalFetch: typeof global.fetch; + let stderrSpy: jest.SpyInstance; + + beforeEach(() => { + originalMem0Dir = process.env.MEM0_DIR; + originalTelemetry = process.env.MEM0_TELEMETRY; + originalSampleRate = process.env.MEM0_TELEMETRY_SAMPLE_RATE; + originalFetch = global.fetch; + + process.env.MEM0_DIR = makeTempMem0Dir(); + process.env.MEM0_TELEMETRY = "true"; + process.env.MEM0_TELEMETRY_SAMPLE_RATE = "1"; + stderrSpy = jest + .spyOn(process.stderr, "write") + .mockImplementation(() => true); + jest.resetModules(); + }); + + afterEach(() => { + if (originalMem0Dir === undefined) delete process.env.MEM0_DIR; + else process.env.MEM0_DIR = originalMem0Dir; + + if (originalTelemetry === undefined) delete process.env.MEM0_TELEMETRY; + else process.env.MEM0_TELEMETRY = originalTelemetry; + + if (originalSampleRate === undefined) { + delete process.env.MEM0_TELEMETRY_SAMPLE_RATE; + } else { + process.env.MEM0_TELEMETRY_SAMPLE_RATE = originalSampleRate; + } + + global.fetch = originalFetch; + jest.restoreAllMocks(); + jest.resetModules(); + }); + + it("does not evaluate or write decay state before the 5th successful delete", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + const ids = await addMemories(memory, "decay-delete-user", 4); + + for (const id of ids) { + await memory.delete(id); + } + + expect(flagRequestCount(fetchMock)).toBe(0); + expect(noticeEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.decay_usage).toBeUndefined(); + expect(stderrSpy).not.toHaveBeenCalledWith( + expect.stringContaining(DECAY_USAGE_COPY), + ); + }); + + it("evaluates on the 5th successful delete and records delete_count", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + const ids = await addMemories(memory, "decay-delete-user", 5); + + for (const id of ids) { + await memory.delete(id); + } + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "decay_usage", + notice_type: "log_line", + flag_key: "mem0-oss-notices", + variant: "displayed", + displayed: true, + payload: DECAY_USAGE_COPY, + notice_config_found: true, + sync_type: "async", + trigger_function: "delete", + trigger_source: "delete_count", + trigger_reason: "repeated_deletes", + delete_count: 5, + sample_rate: 1, + }), + ); + expect(stderrSpy.mock.calls.flat().join("")).toContain(DECAY_USAGE_COPY); + expect(readConfig().notice_state.decay_usage.events).toHaveLength(1); + }); + + it("does not count or emit when delete fails", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + jest + .spyOn(memory as any, "deleteMemory") + .mockRejectedValue(new Error("delete failed")); + + await expect(memory.delete("memory-id")).rejects.toThrow("delete failed"); + + expect(flagRequestCount(fetchMock)).toBe(0); + expect(noticeEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.decay_usage).toBeUndefined(); + }); + + it("evaluates deleteAll after deleting at least one memory", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addMemories(memory, "decay-delete-all-user", 3); + + await memory.deleteAll({ userId: "decay-delete-all-user" }); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "decay_usage", + variant: "displayed", + displayed: true, + payload: DECAY_USAGE_COPY, + trigger_function: "delete_all", + trigger_source: "delete_all", + trigger_reason: "bulk_delete", + deleted_count: 3, + }), + ); + }); + + it("does not evaluate deleteAll when no memories are deleted", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.deleteAll({ userId: "empty-delete-all-user" }); + + expect(flagRequestCount(fetchMock)).toBe(0); + expect(noticeEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.decay_usage).toBeUndefined(); + }); + + it("does not evaluate when deleteAll fails", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.deleteAll({} as any)).rejects.toThrow( + "At least one filter is required", + ); + + expect(flagRequestCount(fetchMock)).toBe(0); + expect(noticeEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.decay_usage).toBeUndefined(); + }); + + it("is silent for holdout and emits displayed=false", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "holdout" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addMemories(memory, "decay-holdout-user", 3); + + await memory.deleteAll({ userId: "decay-holdout-user" }); + + expect(stderrSpy).not.toHaveBeenCalledWith( + expect.stringContaining(DECAY_USAGE_COPY), + ); + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "decay_usage", + variant: "holdout", + displayed: false, + bypass_reason: "holdout", + trigger_function: "delete_all", + }), + ); + }); + + it.each([ + [ + "disabled payload", + JSON.stringify(decayUsagePayload({ enabled: false })), + "payload_disabled", + "payload_disabled", + ], + [ + "missing config", + JSON.stringify({ notices: {} }), + "missing_notice_config", + undefined, + ], + [ + "missing copy", + JSON.stringify({ + notices: { + decay_usage: { enabled: true, notice_type: "log_line" }, + }, + }), + "missing_copy", + undefined, + ], + ["malformed payload", "{not-json", "missing_notice_config", undefined], + ])( + "stays silent and emits safe bypass for %s", + async (_label, payload, bypassReason, disabledReason) => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + payload, + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addMemories(memory, "decay-bypass-user", 3); + + await memory.deleteAll({ userId: "decay-bypass-user" }); + + expect(stderrSpy).not.toHaveBeenCalledWith( + expect.stringContaining(DECAY_USAGE_COPY), + ); + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "decay_usage", + displayed: false, + bypass_reason: bypassReason, + ...(disabledReason && { disabled_reason: disabledReason }), + }), + ); + }, + ); + + it("does not emit or consume cap when the blunt flag is disabled", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + flagEnabled: false, + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addMemories(memory, "decay-flag-disabled-user", 3); + + await memory.deleteAll({ userId: "decay-flag-disabled-user" }); + + expect(noticeEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.decay_usage).toBeUndefined(); + }); + + it("does not emit or consume cap when PostHog fails", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ failFlags: true }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addMemories(memory, "decay-posthog-failure-user", 3); + + await memory.deleteAll({ userId: "decay-posthog-failure-user" }); + + expect(noticeEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.decay_usage).toBeUndefined(); + }); + + it("skips flag evaluation, event emission, and state writes when telemetry is off", async () => { + process.env.MEM0_TELEMETRY = "False"; + jest.resetModules(); + + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addMemories(memory, "decay-telemetry-off-user", 3); + + await memory.deleteAll({ userId: "decay-telemetry-off-user" }); + + expect(fetchMock).not.toHaveBeenCalled(); + expect(noticeEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.decay_usage).toBeUndefined(); + }); + + it("blocks the 11th evaluated opportunity before flag evaluation", async () => { + consumeFirstRun(); + const now = new Date(); + writeConfig({ + ...readConfig(), + notice_state: { + ...readConfig().notice_state, + first_run: readConfig().notice_state.first_run, + decay_usage: { + events: Array.from({ length: 10 }, (_, index) => ({ + evaluated_at: new Date(now.getTime() - index * 1000).toISOString(), + variant: "displayed", + })), + }, + }, + }); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addMemories(memory, "decay-cap-user", 1); + + await memory.deleteAll({ userId: "decay-cap-user" }); + + expect(flagRequestCount(fetchMock)).toBe(0); + expect(noticeEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state.decay_usage.events).toHaveLength(10); + }); + + it("does not consume first-run on a qualifying decay usage call", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + payload: JSON.stringify({ + notices: { + first_run: { + enabled: true, + notice_type: "log_line", + copy: "First-run should not appear", + }, + decay_usage: { + enabled: true, + notice_type: "log_line", + copy: DECAY_USAGE_COPY, + }, + }, + }), + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addMemories(memory, "decay-priority-user", 3); + const config = readConfig(); + delete config.notice_state.first_run; + writeConfig(config); + + await memory.deleteAll({ userId: "decay-priority-user" }); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties.notice_id).toBe("decay_usage"); + expect(readConfig().notice_state.first_run).toBeUndefined(); + expect(readConfig().notice_state.decay_usage.events).toHaveLength(1); + }); +}); diff --git a/mem0-ts/src/oss/tests/notices.first-run.test.ts b/mem0-ts/src/oss/tests/notices.first-run.test.ts new file mode 100644 index 000000000..2c849e69d --- /dev/null +++ b/mem0-ts/src/oss/tests/notices.first-run.test.ts @@ -0,0 +1,409 @@ +/// +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; + +jest.setTimeout(15000); + +jest.mock("../src/embeddings/google", () => ({ + GoogleEmbedder: jest.fn(), +})); +jest.mock("../src/llms/google", () => ({ + GoogleLLM: jest.fn(), +})); + +jest.mock("../src/llms/openai", () => ({ + OpenAILLM: jest.fn().mockImplementation(() => ({ + generateResponse: jest.fn().mockResolvedValue( + JSON.stringify({ + memory: [{ id: "0", text: "stored fact", attributed_to: "user" }], + }), + ), + })), +})); + +const mockEmbedding = new Array(1536).fill(0.1); +jest.mock("../src/embeddings/openai", () => ({ + OpenAIEmbedder: jest.fn().mockImplementation(() => ({ + embed: jest.fn().mockResolvedValue(mockEmbedding), + embedBatch: jest + .fn() + .mockImplementation((texts: string[]) => + Promise.resolve(texts.map(() => mockEmbedding)), + ), + embeddingDims: 1536, + })), +})); + +const FIRST_RUN_COPY = "First-run CTA from PostHog"; + +function makeTempMem0Dir(): string { + return fs.mkdtempSync(path.join(os.tmpdir(), "mem0-node-first-run-")); +} + +function firstRunPayload(overrides: Record = {}) { + return { + notices: { + first_run: { + enabled: true, + notice_type: "log_line", + copy: FIRST_RUN_COPY, + ...overrides, + }, + }, + }; +} + +function createFetchMock(options: { + variant?: string; + payload?: Record; + failFlags?: boolean; +}) { + const calls: any[] = []; + const fetchMock = jest.fn(async (url: string | URL, init?: RequestInit) => { + const target = String(url); + + if (target.includes("/flags")) { + if (options.failFlags) { + throw new Error("flag evaluation failed"); + } + return { + ok: true, + json: jest.fn().mockResolvedValue({ + flags: { + "mem0-oss-notices": { + key: "mem0-oss-notices", + enabled: true, + variant: options.variant ?? "displayed", + metadata: { + payload: JSON.stringify(options.payload ?? firstRunPayload()), + }, + }, + }, + }), + }; + } + + if (target.includes("/i/v0/e/")) { + calls.push(JSON.parse(String(init?.body))); + return { + ok: true, + text: jest.fn().mockResolvedValue(""), + }; + } + + return { + ok: true, + json: jest.fn().mockResolvedValue({}), + text: jest.fn().mockResolvedValue(""), + }; + }); + + return { fetchMock, calls }; +} + +function noticeEvents(calls: any[]) { + return calls.filter((call) => call.event === "mem0.notice_displayed"); +} + +async function createMemory() { + const { Memory } = await import("../src/memory"); + return new Memory({ + version: "v1.1", + embedder: { + provider: "openai", + config: { apiKey: "test-key", model: "text-embedding-3-small" }, + }, + vectorStore: { + provider: "memory", + config: { + collectionName: `test-first-run-${Date.now()}-${Math.random()}`, + dimension: 1536, + dbPath: ":memory:", + }, + }, + llm: { + provider: "openai", + config: { apiKey: "test-key", model: "gpt-5-mini" }, + }, + historyDbPath: ":memory:", + }); +} + +describe("Node OSS first-run notice", () => { + let originalMem0Dir: string | undefined; + let originalTelemetry: string | undefined; + let originalSampleRate: string | undefined; + let originalFetch: typeof global.fetch; + let stderrSpy: jest.SpyInstance; + + beforeEach(() => { + originalMem0Dir = process.env.MEM0_DIR; + originalTelemetry = process.env.MEM0_TELEMETRY; + originalSampleRate = process.env.MEM0_TELEMETRY_SAMPLE_RATE; + originalFetch = global.fetch; + + process.env.MEM0_DIR = makeTempMem0Dir(); + process.env.MEM0_TELEMETRY = "true"; + process.env.MEM0_TELEMETRY_SAMPLE_RATE = "1"; + stderrSpy = jest + .spyOn(process.stderr, "write") + .mockImplementation(() => true); + jest.resetModules(); + }); + + afterEach(() => { + if (originalMem0Dir === undefined) delete process.env.MEM0_DIR; + else process.env.MEM0_DIR = originalMem0Dir; + + if (originalTelemetry === undefined) delete process.env.MEM0_TELEMETRY; + else process.env.MEM0_TELEMETRY = originalTelemetry; + + if (originalSampleRate === undefined) { + delete process.env.MEM0_TELEMETRY_SAMPLE_RATE; + } else { + process.env.MEM0_TELEMETRY_SAMPLE_RATE = originalSampleRate; + } + + global.fetch = originalFetch; + jest.restoreAllMocks(); + jest.resetModules(); + }); + + it("shows the displayed first-run copy once after a successful public call", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Direct storage content", { + userId: "first-run-user", + infer: false, + }); + await memory.add("Second direct storage content", { + userId: "first-run-user", + infer: false, + }); + + const stderrOutput = stderrSpy.mock.calls.flat().join(""); + expect(stderrOutput.match(new RegExp(FIRST_RUN_COPY, "g"))).toHaveLength(1); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "first_run", + notice_type: "log_line", + flag_key: "mem0-oss-notices", + variant: "displayed", + displayed: true, + payload: FIRST_RUN_COPY, + notice_config_found: true, + sync_type: "async", + trigger_function: "add", + sample_rate: 1, + }), + ); + + const { __noticeTestHooks } = await import("../src/utils/notices"); + const config = __noticeTestHooks.loadMem0Config(); + expect(config.notice_state.first_run).toEqual( + expect.objectContaining({ + consumed: true, + trigger_function: "add", + variant: "displayed", + }), + ); + }); + + it("treats missing enabled as enabled, matching Python payload semantics", async () => { + const payload = firstRunPayload(); + delete (payload.notices.first_run as Record).enabled; + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + payload, + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Missing enabled payload content", { + userId: "first-run-missing-enabled", + infer: false, + }); + + const stderrOutput = stderrSpy.mock.calls.flat().join(""); + expect(stderrOutput).toContain(FIRST_RUN_COPY); + expect(noticeEvents(calls)[0].properties).toEqual( + expect.objectContaining({ + notice_id: "first_run", + displayed: true, + payload: FIRST_RUN_COPY, + }), + ); + }); + + it("is silent for holdout but still emits and consumes the opportunity", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "holdout" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Holdout content", { + userId: "first-run-holdout", + infer: false, + }); + + expect(stderrSpy).not.toHaveBeenCalledWith( + expect.stringContaining(FIRST_RUN_COPY), + ); + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "first_run", + variant: "holdout", + displayed: false, + bypass_reason: "holdout", + notice_config_found: true, + }), + ); + }); + + it("is silent for disabled payloads and records a safe bypass", async () => { + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + payload: firstRunPayload({ enabled: false }), + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Disabled payload content", { + userId: "first-run-disabled", + infer: false, + }); + + const stderrOutput = stderrSpy.mock.calls.flat().join(""); + expect(stderrOutput).not.toContain(FIRST_RUN_COPY); + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "first_run", + displayed: false, + bypass_reason: "payload_disabled", + disabled_reason: "payload_disabled", + notice_config_found: true, + }), + ); + }); + + it("lets the payload disabled kill switch take precedence over holdout", async () => { + const { fetchMock, calls } = createFetchMock({ + variant: "holdout", + payload: firstRunPayload({ enabled: false }), + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Disabled holdout payload content", { + userId: "first-run-disabled-holdout", + infer: false, + }); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "first_run", + variant: "holdout", + displayed: false, + bypass_reason: "payload_disabled", + disabled_reason: "payload_disabled", + notice_config_found: true, + }), + ); + }); + + it("does not evaluate flags, emit, or write state when telemetry is off", async () => { + process.env.MEM0_TELEMETRY = "false"; + jest.resetModules(); + + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Telemetry off content", { + userId: "first-run-off", + infer: false, + }); + + expect(fetchMock).not.toHaveBeenCalled(); + expect(noticeEvents(calls)).toHaveLength(0); + const { __noticeTestHooks } = await import("../src/utils/notices"); + expect(__noticeTestHooks.loadMem0Config().notice_state).toBeUndefined(); + }); + + it("does not export internal notice test hooks from the public OSS entrypoint", async () => { + const publicOssEntry = await import("../src"); + + expect(publicOssEntry).not.toHaveProperty("__noticeTestHooks"); + }); + + it("treats uppercase False as telemetry off", async () => { + process.env.MEM0_TELEMETRY = "False"; + jest.resetModules(); + + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Uppercase telemetry off content", { + userId: "first-run-off-uppercase", + infer: false, + }); + + expect(fetchMock).not.toHaveBeenCalled(); + expect(noticeEvents(calls)).toHaveLength(0); + const { __noticeTestHooks } = await import("../src/utils/notices"); + expect(__noticeTestHooks.loadMem0Config().notice_state).toBeUndefined(); + }); + + it("does not run after a failed public call", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.add("Missing owner", {} as any)).rejects.toThrow( + "One of the filters: userId, agentId or runId is required!", + ); + + expect( + fetchMock.mock.calls.filter(([url]) => String(url).includes("/flags")), + ).toHaveLength(0); + expect(noticeEvents(calls)).toHaveLength(0); + }); + + it("does not break the successful operation when flag evaluation fails", async () => { + const { fetchMock, calls } = createFetchMock({ failFlags: true }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + const result = await memory.add("Flag failure content", { + userId: "first-run-failure", + infer: false, + }); + + expect(result.results).toHaveLength(1); + expect(noticeEvents(calls)).toHaveLength(0); + }); + + it("can trigger from get() when get is the first successful public call", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect(memory.get("missing-memory-id")).resolves.toBeNull(); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties.trigger_function).toBe("get"); + }); +}); diff --git a/mem0-ts/src/oss/tests/notices.foundation.test.ts b/mem0-ts/src/oss/tests/notices.foundation.test.ts new file mode 100644 index 000000000..70b2adff7 --- /dev/null +++ b/mem0-ts/src/oss/tests/notices.foundation.test.ts @@ -0,0 +1,227 @@ +/// +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; +import type { TelemetryInstance } from "../src/utils/telemetry.types"; + +function makeTempMem0Dir(): string { + return fs.mkdtempSync(path.join(os.tmpdir(), "mem0-node-notices-")); +} + +function makeInstance( + overrides: Partial = {}, +): TelemetryInstance { + return { + telemetryId: "notice-test-user", + constructor: { name: "Memory" }, + host: "https://test.example.com", + ...overrides, + }; +} + +describe("Node OSS notice foundation", () => { + let originalMem0Dir: string | undefined; + let originalTelemetry: string | undefined; + let originalFetch: typeof global.fetch; + + beforeEach(() => { + originalMem0Dir = process.env.MEM0_DIR; + originalTelemetry = process.env.MEM0_TELEMETRY; + originalFetch = global.fetch; + process.env.MEM0_DIR = makeTempMem0Dir(); + process.env.MEM0_TELEMETRY = "true"; + jest.resetModules(); + }); + + afterEach(() => { + if (originalMem0Dir === undefined) delete process.env.MEM0_DIR; + else process.env.MEM0_DIR = originalMem0Dir; + + if (originalTelemetry === undefined) delete process.env.MEM0_TELEMETRY; + else process.env.MEM0_TELEMETRY = originalTelemetry; + + global.fetch = originalFetch; + jest.restoreAllMocks(); + jest.resetModules(); + }); + + it("writes notice state into isolated MEM0_DIR config and preserves user_id", async () => { + const notices = await import("../src/utils/notices"); + const configPath = notices.getMem0ConfigPath(); + + fs.mkdirSync(path.dirname(configPath), { recursive: true }); + fs.writeFileSync(configPath, JSON.stringify({ user_id: "existing-user" })); + + expect( + notices.recordNoticeOpportunity("foundation_notice", { + variant: "displayed", + }), + ).toBe(true); + + const config = JSON.parse(fs.readFileSync(configPath, "utf8")); + expect(config.user_id).toBe("existing-user"); + expect(config.notice_state.foundation_notice.events).toHaveLength(1); + }); + + it("writes config through a temp-file path and leaves no temp file behind", async () => { + const notices = await import("../src/utils/notices"); + + expect(notices.writeMem0ConfigAtomic({ user_id: "atomic-user" })).toBe( + true, + ); + + const configDir = path.dirname(notices.getMem0ConfigPath()); + const tempFiles = fs + .readdirSync(configDir) + .filter((name) => name.endsWith(".tmp")); + expect(tempFiles).toHaveLength(0); + expect(notices.loadMem0Config().user_id).toBe("atomic-user"); + }); + + it("allows 10 evaluated opportunities in a rolling window and blocks the 11th", async () => { + const notices = await import("../src/utils/notices"); + const now = new Date("2026-06-11T12:00:00.000Z"); + let state: Record = {}; + + for (let i = 0; i < 10; i++) { + const nextState = notices.appendNoticeCapEvent( + state, + { variant: "displayed", index: i }, + { now: new Date(now.getTime() + i) }, + ); + expect(nextState).not.toBeNull(); + state = nextState!; + } + + expect(notices.hasNoticeCapRoom(state, { now })).toBe(false); + expect( + notices.appendNoticeCapEvent(state, { variant: "displayed" }, { now }), + ).toBeNull(); + }); + + it("drops old cap events outside the rolling window", async () => { + const notices = await import("../src/utils/notices"); + const oldEvent = { + evaluated_at: "2026-06-01T00:00:00.000Z", + variant: "displayed", + }; + const now = new Date("2026-06-11T12:00:00.000Z"); + + expect(notices.hasNoticeCapRoom({ events: [oldEvent] }, { now })).toBe( + true, + ); + const nextState = notices.appendNoticeCapEvent( + { events: [oldEvent] }, + { variant: "displayed" }, + { now }, + ); + expect(nextState?.events).toHaveLength(1); + }); + + it("does not evaluate flags or write notice state when telemetry is off", async () => { + process.env.MEM0_TELEMETRY = "false"; + jest.resetModules(); + + const fetchMock = jest.fn(); + global.fetch = fetchMock as any; + const notices = await import("../src/utils/notices"); + + await expect( + notices.evaluateNoticeFlag("notice-test-user", { fetchImpl: fetchMock }), + ).resolves.toBeNull(); + expect(fetchMock).not.toHaveBeenCalled(); + expect( + notices.recordNoticeOpportunity("foundation_notice", { + variant: "displayed", + }), + ).toBe(false); + expect(fs.existsSync(notices.getMem0ConfigPath())).toBe(false); + }); + + it("returns null when PostHog flag evaluation fails", async () => { + const notices = await import("../src/utils/notices"); + const fetchMock = jest.fn().mockRejectedValue(new Error("network down")); + + await expect( + notices.evaluateNoticeFlag("notice-test-user", { fetchImpl: fetchMock }), + ).resolves.toBeNull(); + }); + + it("returns null when PostHog flag evaluation times out", async () => { + const notices = await import("../src/utils/notices"); + const fetchMock = jest.fn( + (_url: string | URL | Request, init?: RequestInit) => + new Promise((_resolve, reject) => { + init?.signal?.addEventListener("abort", () => { + reject(new Error("aborted")); + }); + }), + ); + + await expect( + notices.evaluateNoticeFlag("notice-test-user", { + fetchImpl: fetchMock as any, + timeoutMs: 1, + }), + ).resolves.toBeNull(); + }); + + it("parses displayed variant and JSON payload from PostHog flags response", async () => { + const notices = await import("../src/utils/notices"); + const payload = { + notices: { + foundation_notice: { + enabled: true, + notice_type: "log_line", + copy: "Foundation notice", + }, + }, + }; + const fetchMock = jest.fn().mockResolvedValue({ + ok: true, + json: jest.fn().mockResolvedValue({ + flags: { + "mem0-oss-notices": { + key: "mem0-oss-notices", + enabled: true, + variant: "displayed", + metadata: { payload: JSON.stringify(payload) }, + }, + }, + }), + }); + + const result = await notices.evaluateNoticeFlag("notice-test-user", { + fetchImpl: fetchMock, + }); + expect(result?.variant).toBe("displayed"); + const parsed = notices.getNoticeConfigFromPayload( + result?.payload, + "foundation_notice", + ); + expect(parsed.found).toBe(true); + expect(parsed.config?.copy).toBe("Foundation notice"); + }); + + it("emits mem0.notice_displayed with sample_rate=1", async () => { + const fetchMock = jest.fn().mockResolvedValue({ + ok: true, + text: jest.fn().mockResolvedValue(""), + }); + global.fetch = fetchMock as any; + const notices = await import("../src/utils/notices"); + + await notices.emitNoticeDisplayed(makeInstance(), { + notice_id: "foundation_notice", + notice_type: "log_line", + flag_key: "mem0-oss-notices", + variant: "displayed", + displayed: true, + }); + + const body = JSON.parse(fetchMock.mock.calls[0][1].body); + expect(body.event).toBe("mem0.notice_displayed"); + expect(body.properties.sample_rate).toBe(1); + expect(body.properties.notice_id).toBe("foundation_notice"); + }); +}); diff --git a/mem0-ts/src/oss/tests/notices.performance-slow-query.test.ts b/mem0-ts/src/oss/tests/notices.performance-slow-query.test.ts new file mode 100644 index 000000000..a0a267535 --- /dev/null +++ b/mem0-ts/src/oss/tests/notices.performance-slow-query.test.ts @@ -0,0 +1,492 @@ +/// +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; + +jest.setTimeout(15000); + +jest.mock("../src/embeddings/google", () => ({ + GoogleEmbedder: jest.fn(), +})); +jest.mock("../src/llms/google", () => ({ + GoogleLLM: jest.fn(), +})); + +const mockEmbedding = new Array(1536).fill(0.1); +jest.mock("../src/embeddings/openai", () => ({ + OpenAIEmbedder: jest.fn().mockImplementation(() => ({ + embed: jest.fn().mockResolvedValue(mockEmbedding), + embedBatch: jest + .fn() + .mockImplementation((texts: string[]) => + Promise.resolve(texts.map(() => mockEmbedding)), + ), + embeddingDims: 1536, + })), +})); + +jest.mock("../src/llms/openai", () => ({ + OpenAILLM: jest.fn().mockImplementation(() => ({ + generateResponse: jest.fn(), + })), +})); + +const PERFORMANCE_COPY = + "Mem0 Platform is optimized for this type of workload. Its retrieval benchmarked at ~0.8-1.09s p50 across LoCoMo, LongMemEval, and BEAM 1M/10M workloads; you can use it for free by getting an API key at: https://app.mem0.ai"; + +function makeTempMem0Dir(): string { + return fs.mkdtempSync(path.join(os.tmpdir(), "mem0-node-performance-")); +} + +function configPath(): string { + return path.join(process.env.MEM0_DIR as string, "config.json"); +} + +function readConfig(): Record { + const file = configPath(); + if (!fs.existsSync(file)) return {}; + return JSON.parse(fs.readFileSync(file, "utf8")); +} + +function writeConfig(config: Record) { + fs.mkdirSync(path.dirname(configPath()), { recursive: true }); + fs.writeFileSync(configPath(), JSON.stringify(config, null, 4)); +} + +function consumeFirstRun() { + writeConfig({ + user_id: "node-performance-test-user", + notice_state: { + first_run: { + consumed: true, + trigger_function: "test_setup", + variant: "test", + }, + }, + }); +} + +function performancePayload(overrides: Record = {}) { + return { + notices: { + performance_slow_query: { + enabled: true, + notice_type: "log_line", + copy: PERFORMANCE_COPY, + ...overrides, + }, + }, + }; +} + +function createFetchMock(options: { + variant?: string; + payload?: unknown; + failFlags?: boolean; + flagEnabled?: boolean; +}) { + const calls: any[] = []; + const fetchMock = jest.fn(async (url: string | URL, init?: RequestInit) => { + const target = String(url); + + if (target.includes("/flags")) { + if (options.failFlags) { + throw new Error("flag evaluation failed"); + } + const payload = + options.payload === undefined + ? JSON.stringify(performancePayload()) + : options.payload; + return { + ok: true, + json: jest.fn().mockResolvedValue({ + flags: { + "mem0-oss-notices": { + key: "mem0-oss-notices", + enabled: options.flagEnabled ?? true, + variant: options.variant ?? "displayed", + metadata: { payload }, + }, + }, + }), + }; + } + + if (target.includes("/i/v0/e/")) { + calls.push(JSON.parse(String(init?.body))); + return { + ok: true, + text: jest.fn().mockResolvedValue(""), + }; + } + + return { + ok: true, + json: jest.fn().mockResolvedValue({}), + text: jest.fn().mockResolvedValue(""), + }; + }); + + return { fetchMock, calls }; +} + +function noticeEvents(calls: any[]) { + return calls.filter((call) => call.event === "mem0.notice_displayed"); +} + +function performanceEvents(calls: any[]) { + return noticeEvents(calls).filter( + (call) => call.properties.notice_id === "performance_slow_query", + ); +} + +function flagRequestCount(fetchMock: jest.Mock) { + return fetchMock.mock.calls.filter(([url]) => String(url).includes("/flags")) + .length; +} + +function mockSearchElapsed(elapsedMs: number) { + let now = 1000; + jest.spyOn(Date, "now").mockImplementation(() => { + const current = now; + now += elapsedMs; + return current; + }); +} + +async function createMemory() { + const { Memory } = await import("../src/memory"); + return new Memory({ + version: "v1.1", + embedder: { + provider: "openai", + config: { apiKey: "test-key", model: "text-embedding-3-small" }, + }, + vectorStore: { + provider: "memory", + config: { + collectionName: `test-performance-${Date.now()}-${Math.random()}`, + dimension: 1536, + dbPath: ":memory:", + }, + }, + llm: { + provider: "openai", + config: { apiKey: "test-key", model: "gpt-5-mini" }, + }, + historyDbPath: ":memory:", + }); +} + +async function addSeed(memory: any, userId = "performance-user") { + await memory.add("The user's favorite drink is green tea.", { + userId, + infer: false, + }); +} + +describe("Node OSS performance slow query notice", () => { + let originalMem0Dir: string | undefined; + let originalTelemetry: string | undefined; + let originalSampleRate: string | undefined; + let originalFetch: typeof global.fetch; + let stderrSpy: jest.SpyInstance; + + beforeEach(() => { + originalMem0Dir = process.env.MEM0_DIR; + originalTelemetry = process.env.MEM0_TELEMETRY; + originalSampleRate = process.env.MEM0_TELEMETRY_SAMPLE_RATE; + originalFetch = global.fetch; + + process.env.MEM0_DIR = makeTempMem0Dir(); + process.env.MEM0_TELEMETRY = "true"; + process.env.MEM0_TELEMETRY_SAMPLE_RATE = "1"; + stderrSpy = jest + .spyOn(process.stderr, "write") + .mockImplementation(() => true); + jest.resetModules(); + }); + + afterEach(() => { + if (originalMem0Dir === undefined) delete process.env.MEM0_DIR; + else process.env.MEM0_DIR = originalMem0Dir; + + if (originalTelemetry === undefined) delete process.env.MEM0_TELEMETRY; + else process.env.MEM0_TELEMETRY = originalTelemetry; + + if (originalSampleRate === undefined) { + delete process.env.MEM0_TELEMETRY_SAMPLE_RATE; + } else { + process.env.MEM0_TELEMETRY_SAMPLE_RATE = originalSampleRate; + } + + global.fetch = originalFetch; + jest.restoreAllMocks(); + jest.resetModules(); + }); + + it("displays and emits safe fields after a slow successful search", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSeed(memory); + stderrSpy.mockClear(); + mockSearchElapsed(2345); + + const result = await memory.search("favorite drink private text", { + filters: { user_id: "performance-user" }, + topK: 3, + }); + + expect(result.results.length).toBeGreaterThan(0); + expect(stderrSpy).toHaveBeenCalledWith(`${PERFORMANCE_COPY}\n`); + const event = performanceEvents(calls)[0]; + expect(event.properties).toEqual( + expect.objectContaining({ + notice_id: "performance_slow_query", + notice_type: "log_line", + flag_key: "mem0-oss-notices", + variant: "displayed", + displayed: true, + payload: PERFORMANCE_COPY, + notice_config_found: true, + sync_type: "async", + trigger_function: "search", + trigger_reason: "slow_query", + elapsed_ms: 2345, + threshold_ms: 2000, + top_k: 3, + result_count: result.results.length, + sample_rate: 1, + }), + ); + const serialized = JSON.stringify(event.properties); + expect(serialized).not.toContain("private text"); + expect(serialized).not.toContain("performance-user"); + }); + + it("is silent for holdout and emits displayed=false", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "holdout" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSeed(memory); + stderrSpy.mockClear(); + mockSearchElapsed(2345); + + await memory.search("favorite drink", { + filters: { user_id: "performance-user" }, + topK: 3, + }); + + expect(stderrSpy).not.toHaveBeenCalled(); + expect(performanceEvents(calls)[0].properties).toEqual( + expect.objectContaining({ + notice_id: "performance_slow_query", + variant: "holdout", + displayed: false, + bypass_reason: "holdout", + }), + ); + }); + + it.each([ + [ + "disabled payload", + JSON.stringify(performancePayload({ enabled: false })), + "payload_disabled", + ], + [ + "missing config", + JSON.stringify({ notices: {} }), + "missing_notice_config", + ], + [ + "missing copy", + JSON.stringify(performancePayload({ copy: "" })), + "missing_copy", + ], + ["malformed payload", "{not-json", "missing_notice_config"], + ])("is silent and safe for %s", async (_label, payload, bypassReason) => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + payload, + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSeed(memory); + stderrSpy.mockClear(); + mockSearchElapsed(2345); + + await memory.search("favorite drink", { + filters: { user_id: "performance-user" }, + topK: 3, + }); + + expect(stderrSpy).not.toHaveBeenCalled(); + expect(performanceEvents(calls)[0].properties).toEqual( + expect.objectContaining({ + displayed: false, + bypass_reason: bypassReason, + }), + ); + }); + + it("does not evaluate fast searches", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSeed(memory); + mockSearchElapsed(100); + + await memory.search("favorite drink", { + filters: { user_id: "performance-user" }, + topK: 3, + }); + + expect(flagRequestCount(fetchMock)).toBe(0); + expect(performanceEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.performance_slow_query).toBeUndefined(); + }); + + it("does not evaluate failed searches", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + mockSearchElapsed(2345); + + await expect( + memory.search("private slow query", { + filters: {}, + topK: 3, + }), + ).rejects.toThrow("filters must contain"); + + expect(flagRequestCount(fetchMock)).toBe(0); + expect(performanceEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.performance_slow_query).toBeUndefined(); + }); + + it("does not consume cap or emit when the blunt flag is disabled", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + flagEnabled: false, + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSeed(memory); + mockSearchElapsed(2345); + + await memory.search("favorite drink", { + filters: { user_id: "performance-user" }, + topK: 3, + }); + + expect(performanceEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.performance_slow_query).toBeUndefined(); + }); + + it("does not consume cap or emit when PostHog fails", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ failFlags: true }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSeed(memory); + mockSearchElapsed(2345); + + await memory.search("favorite drink", { + filters: { user_id: "performance-user" }, + topK: 3, + }); + + expect(performanceEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.performance_slow_query).toBeUndefined(); + }); + + it("does nothing when telemetry is off", async () => { + process.env.MEM0_TELEMETRY = "false"; + jest.resetModules(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSeed(memory); + mockSearchElapsed(2345); + + await memory.search("favorite drink", { + filters: { user_id: "performance-user" }, + topK: 3, + }); + + expect(fetchMock).not.toHaveBeenCalled(); + expect(performanceEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state).toBeUndefined(); + }); + + it("caps evaluated opportunities at 10 per rolling week", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSeed(memory); + stderrSpy.mockClear(); + mockSearchElapsed(2345); + + for (let index = 1; index <= 11; index++) { + await memory.search(`favorite drink ${index}`, { + filters: { user_id: "performance-user" }, + topK: 3, + }); + } + + expect(performanceEvents(calls)).toHaveLength(10); + expect(flagRequestCount(fetchMock)).toBe(10); + expect(stderrSpy).toHaveBeenCalledTimes(10); + expect( + readConfig().notice_state.performance_slow_query.events, + ).toHaveLength(10); + }); + + it("lets temporal usage and scale threshold take priority", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSeed(memory); + mockSearchElapsed(2345); + + await memory.search("what happened last week?", { + filters: { user_id: "performance-user" }, + topK: 3, + }); + await memory.search("favorite drink", { + filters: { user_id: "performance-user" }, + topK: 50, + }); + + expect(performanceEvents(calls)).toHaveLength(0); + const noticeIds = noticeEvents(calls).map( + (call) => call.properties.notice_id, + ); + expect(noticeIds).toEqual(["temporal_usage", "scale_threshold"]); + }); + + it("does not consume first-run on the same qualifying performance call", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + mockSearchElapsed(2345); + + await memory.search("favorite drink", { + filters: { user_id: "performance-user" }, + topK: 3, + }); + + expect(performanceEvents(calls)).toHaveLength(1); + expect(readConfig().notice_state.first_run).toBeUndefined(); + }); +}); diff --git a/mem0-ts/src/oss/tests/notices.scale-threshold.test.ts b/mem0-ts/src/oss/tests/notices.scale-threshold.test.ts new file mode 100644 index 000000000..48f2c0a76 --- /dev/null +++ b/mem0-ts/src/oss/tests/notices.scale-threshold.test.ts @@ -0,0 +1,463 @@ +/// +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; + +jest.setTimeout(15000); + +jest.mock("../src/embeddings/google", () => ({ + GoogleEmbedder: jest.fn(), +})); +jest.mock("../src/llms/google", () => ({ + GoogleLLM: jest.fn(), +})); + +const mockEmbedding = new Array(1536).fill(0.1); +jest.mock("../src/embeddings/openai", () => ({ + OpenAIEmbedder: jest.fn().mockImplementation(() => ({ + embed: jest.fn().mockResolvedValue(mockEmbedding), + embedBatch: jest + .fn() + .mockImplementation((texts: string[]) => + Promise.resolve(texts.map(() => mockEmbedding)), + ), + embeddingDims: 1536, + })), +})); + +jest.mock("../src/llms/openai", () => ({ + OpenAILLM: jest.fn().mockImplementation(() => ({ + generateResponse: jest.fn(), + })), +})); + +const SCALE_TOP_K_COPY = "Scale top {top_k}"; +const SCALE_MEMORY_COUNT_COPY = "Scale count {memory_count}"; + +function makeTempMem0Dir(): string { + return fs.mkdtempSync(path.join(os.tmpdir(), "mem0-node-scale-")); +} + +function configPath(): string { + return path.join(process.env.MEM0_DIR as string, "config.json"); +} + +function readConfig(): Record { + const file = configPath(); + if (!fs.existsSync(file)) return {}; + return JSON.parse(fs.readFileSync(file, "utf8")); +} + +function writeConfig(config: Record) { + fs.mkdirSync(path.dirname(configPath()), { recursive: true }); + fs.writeFileSync(configPath(), JSON.stringify(config, null, 4)); +} + +function consumeFirstRun(scaleState: Record = {}) { + writeConfig({ + user_id: "node-scale-test-user", + notice_state: { + first_run: { + consumed: true, + trigger_function: "test_setup", + variant: "test", + }, + ...(Object.keys(scaleState).length > 0 && { + scale_threshold: scaleState, + }), + }, + }); +} + +function scalePayload(overrides: Record = {}) { + return { + notices: { + scale_threshold: { + enabled: true, + notice_type: "log_line", + copies: { + top_k: SCALE_TOP_K_COPY, + memory_count: SCALE_MEMORY_COUNT_COPY, + }, + ...overrides, + }, + }, + }; +} + +function createFetchMock(options: { + variant?: string; + payload?: unknown; + failFlags?: boolean; + flagEnabled?: boolean; +}) { + const calls: any[] = []; + const fetchMock = jest.fn(async (url: string | URL, init?: RequestInit) => { + const target = String(url); + + if (target.includes("/flags")) { + if (options.failFlags) { + throw new Error("flag evaluation failed"); + } + const payload = + options.payload === undefined + ? JSON.stringify(scalePayload()) + : options.payload; + return { + ok: true, + json: jest.fn().mockResolvedValue({ + flags: { + "mem0-oss-notices": { + key: "mem0-oss-notices", + enabled: options.flagEnabled ?? true, + variant: options.variant ?? "displayed", + metadata: { payload }, + }, + }, + }), + }; + } + + if (target.includes("/i/v0/e/")) { + calls.push(JSON.parse(String(init?.body))); + return { + ok: true, + text: jest.fn().mockResolvedValue(""), + }; + } + + return { + ok: true, + json: jest.fn().mockResolvedValue({}), + text: jest.fn().mockResolvedValue(""), + }; + }); + + return { fetchMock, calls }; +} + +function noticeEvents(calls: any[]) { + return calls.filter((call) => call.event === "mem0.notice_displayed"); +} + +function scaleEvents(calls: any[]) { + return noticeEvents(calls).filter( + (call) => call.properties.notice_id === "scale_threshold", + ); +} + +function flagRequestCount(fetchMock: jest.Mock) { + return fetchMock.mock.calls.filter(([url]) => String(url).includes("/flags")) + .length; +} + +async function createMemory() { + const { Memory } = await import("../src/memory"); + return new Memory({ + version: "v1.1", + embedder: { + provider: "openai", + config: { apiKey: "test-key", model: "text-embedding-3-small" }, + }, + vectorStore: { + provider: "memory", + config: { + collectionName: `test-scale-${Date.now()}-${Math.random()}`, + dimension: 1536, + dbPath: ":memory:", + }, + }, + llm: { + provider: "openai", + config: { apiKey: "test-key", model: "gpt-5-mini" }, + }, + historyDbPath: ":memory:", + }); +} + +async function addSeed(memory: any, userId = "scale-user") { + await memory.add("The user's favorite drink is green tea.", { + userId, + infer: false, + }); +} + +describe("Node OSS scale threshold notice", () => { + let originalMem0Dir: string | undefined; + let originalTelemetry: string | undefined; + let originalSampleRate: string | undefined; + let originalFetch: typeof global.fetch; + let stderrSpy: jest.SpyInstance; + + beforeEach(() => { + originalMem0Dir = process.env.MEM0_DIR; + originalTelemetry = process.env.MEM0_TELEMETRY; + originalSampleRate = process.env.MEM0_TELEMETRY_SAMPLE_RATE; + originalFetch = global.fetch; + + process.env.MEM0_DIR = makeTempMem0Dir(); + process.env.MEM0_TELEMETRY = "true"; + process.env.MEM0_TELEMETRY_SAMPLE_RATE = "1"; + stderrSpy = jest + .spyOn(process.stderr, "write") + .mockImplementation(() => true); + jest.resetModules(); + }); + + afterEach(() => { + if (originalMem0Dir === undefined) delete process.env.MEM0_DIR; + else process.env.MEM0_DIR = originalMem0Dir; + + if (originalTelemetry === undefined) delete process.env.MEM0_TELEMETRY; + else process.env.MEM0_TELEMETRY = originalTelemetry; + + if (originalSampleRate === undefined) { + delete process.env.MEM0_TELEMETRY_SAMPLE_RATE; + } else { + process.env.MEM0_TELEMETRY_SAMPLE_RATE = originalSampleRate; + } + + global.fetch = originalFetch; + jest.restoreAllMocks(); + jest.resetModules(); + }); + + it("detects high topK after successful search", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await addSeed(memory); + stderrSpy.mockClear(); + const result = await memory.search("favorite drink private text", { + filters: { user_id: "scale-user" }, + topK: 50, + }); + + expect(result.results.length).toBeGreaterThan(0); + expect(stderrSpy).toHaveBeenCalledWith("Scale top 50\n"); + const event = scaleEvents(calls)[0]; + expect(event.properties).toMatchObject({ + notice_id: "scale_threshold", + displayed: true, + trigger_function: "search", + trigger_source: "top_k", + trigger_reason: "high_top_k", + top_k: 50, + threshold: 50, + sample_rate: 1, + }); + expect(JSON.stringify(event.properties)).not.toContain("private text"); + }); + + it("detects high topK after successful getAll", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await addSeed(memory); + stderrSpy.mockClear(); + const result = await memory.getAll({ + filters: { user_id: "scale-user" }, + topK: 50, + }); + + expect(result.results.length).toBeGreaterThan(0); + expect(stderrSpy).toHaveBeenCalledWith("Scale top 50\n"); + const event = scaleEvents(calls)[0]; + expect(event.properties).toMatchObject({ + notice_id: "scale_threshold", + displayed: true, + trigger_function: "get_all", + trigger_source: "top_k", + trigger_reason: "high_top_k", + top_k: 50, + threshold: 50, + }); + }); + + it("does not evaluate topK below the threshold", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await addSeed(memory); + await memory.search("favorite drink", { + filters: { user_id: "scale-user" }, + topK: 49, + }); + + expect(flagRequestCount(fetchMock)).toBe(0); + expect(scaleEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.scale_threshold).toBeUndefined(); + }); + + it("marks memory-count threshold evaluated before PostHog display", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ failFlags: true }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + (memory as any).vectorStore.count = jest + .fn() + .mockResolvedValue({ count: 2000 }); + + await memory.add("Scale count threshold fixture.", { + userId: "scale-user", + infer: false, + }); + + expect((memory as any).vectorStore.count).toHaveBeenCalledTimes(1); + expect( + readConfig().notice_state.scale_threshold + .memory_count_threshold_evaluated, + ).toBe(true); + expect(scaleEvents(calls)).toHaveLength(0); + }); + + it("does not count provider memories once threshold was evaluated", async () => { + consumeFirstRun({ memory_count_threshold_evaluated: true }); + const { fetchMock } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + (memory as any).vectorStore.count = jest + .fn() + .mockResolvedValue({ count: 2000 }); + + await memory.add("Scale count already evaluated fixture.", { + userId: "scale-user", + infer: false, + }); + + expect((memory as any).vectorStore.count).not.toHaveBeenCalled(); + expect(flagRequestCount(fetchMock)).toBe(0); + }); + + it("throttles under-threshold provider counts", async () => { + consumeFirstRun(); + const { fetchMock } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + (memory as any).vectorStore.count = jest + .fn() + .mockResolvedValue({ count: 1999 }); + + await memory.add("Scale count under threshold one.", { + userId: "scale-user", + infer: false, + }); + await memory.add("Scale count under threshold two.", { + userId: "scale-user", + infer: false, + }); + + expect((memory as any).vectorStore.count).toHaveBeenCalledTimes(1); + expect(flagRequestCount(fetchMock)).toBe(0); + }); + + it("records holdout and disabled variants silently", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ + variant: "holdout", + payload: JSON.stringify(scalePayload({ enabled: false })), + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await addSeed(memory); + stderrSpy.mockClear(); + await memory.search("favorite drink", { + filters: { user_id: "scale-user" }, + topK: 50, + }); + + expect(stderrSpy).not.toHaveBeenCalled(); + const event = scaleEvents(calls)[0]; + expect(event.properties).toMatchObject({ + displayed: false, + bypass_reason: "payload_disabled", + disabled_reason: "payload_disabled", + variant: "holdout", + }); + }); + + it("treats missing enabled as enabled for scale payloads", async () => { + consumeFirstRun(); + const payload = scalePayload(); + delete (payload.notices.scale_threshold as Record).enabled; + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + payload: JSON.stringify(payload), + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await addSeed(memory); + stderrSpy.mockClear(); + await memory.search("favorite drink", { + filters: { user_id: "scale-user" }, + topK: 50, + }); + + expect(stderrSpy.mock.calls.flat().join("")).toContain("Scale top 50"); + expect(scaleEvents(calls)[0].properties).toMatchObject({ + notice_id: "scale_threshold", + displayed: true, + payload: "Scale top 50", + }); + }); + + it("caps evaluated scale opportunities at 10 per week", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await addSeed(memory); + stderrSpy.mockClear(); + for (let index = 0; index < 11; index++) { + await memory.search(`favorite drink ${index}`, { + filters: { user_id: "scale-user" }, + topK: 50, + }); + } + + expect(scaleEvents(calls)).toHaveLength(10); + expect(flagRequestCount(fetchMock)).toBe(10); + expect(stderrSpy).toHaveBeenCalledTimes(10); + expect(readConfig().notice_state.scale_threshold.events).toHaveLength(10); + }); + + it("does not consume first-run on the same qualifying scale call", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.getAll({ + filters: { user_id: "scale-user" }, + topK: 50, + }); + + expect(scaleEvents(calls)).toHaveLength(1); + expect(readConfig().notice_state?.first_run).toBeUndefined(); + }); + + it("telemetry off skips flag evaluation, event emission, and state writes", async () => { + process.env.MEM0_TELEMETRY = "false"; + jest.resetModules(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.getAll({ + filters: { user_id: "scale-user" }, + topK: 50, + }); + + expect(flagRequestCount(fetchMock)).toBe(0); + expect(scaleEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state).toBeUndefined(); + }); +}); diff --git a/mem0-ts/src/oss/tests/notices.temporal-feature.test.ts b/mem0-ts/src/oss/tests/notices.temporal-feature.test.ts new file mode 100644 index 000000000..66608c0ad --- /dev/null +++ b/mem0-ts/src/oss/tests/notices.temporal-feature.test.ts @@ -0,0 +1,516 @@ +/// +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; + +jest.setTimeout(15000); + +jest.mock("../src/embeddings/google", () => ({ + GoogleEmbedder: jest.fn(), +})); +jest.mock("../src/llms/google", () => ({ + GoogleLLM: jest.fn(), +})); + +const mockEmbedding = new Array(1536).fill(0.1); +const mockEmbed = jest.fn().mockResolvedValue(mockEmbedding); +const mockEmbedBatch = jest + .fn() + .mockImplementation((texts: string[]) => + Promise.resolve(texts.map(() => mockEmbedding)), + ); + +jest.mock("../src/embeddings/openai", () => ({ + OpenAIEmbedder: jest.fn().mockImplementation(() => ({ + embed: mockEmbed, + embedBatch: mockEmbedBatch, + embeddingDims: 1536, + })), +})); + +const mockGenerateResponse = jest.fn(); +jest.mock("../src/llms/openai", () => ({ + OpenAILLM: jest.fn().mockImplementation(() => ({ + generateResponse: mockGenerateResponse, + })), +})); + +const TEMPORAL_COPY = + "Temporal reasoning requires a Mem0 API key. Get one for free at https://app.mem0.ai"; +const PLAIN_TIMESTAMP_ERROR = + "The timestamp parameter is not supported by the OSS Memory SDK."; +const PLAIN_REFERENCE_DATE_ERROR = + "The referenceDate parameter is not supported by the OSS Memory SDK."; + +function makeTempMem0Dir(): string { + return fs.mkdtempSync(path.join(os.tmpdir(), "mem0-node-temporal-feature-")); +} + +function temporalPayload(overrides: Record = {}) { + return { + notices: { + temporal_stub: { + enabled: true, + notice_type: "error", + copy: TEMPORAL_COPY, + ...overrides, + }, + }, + }; +} + +function createFetchMock(options: { + variant?: string; + payload?: unknown; + failFlags?: boolean; + flagEnabled?: boolean; +}) { + const calls: any[] = []; + const fetchMock = jest.fn(async (url: string | URL, init?: RequestInit) => { + const target = String(url); + + if (target.includes("/flags")) { + if (options.failFlags) { + throw new Error("flag evaluation failed"); + } + return { + ok: true, + json: jest.fn().mockResolvedValue({ + flags: { + "mem0-oss-notices": { + key: "mem0-oss-notices", + enabled: options.flagEnabled ?? true, + variant: options.variant ?? "displayed", + metadata: { + payload: + options.payload === undefined + ? JSON.stringify(temporalPayload()) + : options.payload, + }, + }, + }, + }), + }; + } + + if (target.includes("/i/v0/e/")) { + calls.push(JSON.parse(String(init?.body))); + return { + ok: true, + text: jest.fn().mockResolvedValue(""), + }; + } + + return { + ok: true, + json: jest.fn().mockResolvedValue({}), + text: jest.fn().mockResolvedValue(""), + }; + }); + + return { fetchMock, calls }; +} + +function noticeEvents(calls: any[]) { + return calls.filter((call) => call.event === "mem0.notice_displayed"); +} + +async function createMemory() { + const { Memory } = await import("../src/memory"); + return new Memory({ + version: "v1.1", + embedder: { + provider: "openai", + config: { apiKey: "test-key", model: "text-embedding-3-small" }, + }, + vectorStore: { + provider: "memory", + config: { + collectionName: `test-temporal-feature-${Date.now()}-${Math.random()}`, + dimension: 1536, + dbPath: ":memory:", + }, + }, + llm: { + provider: "openai", + config: { apiKey: "test-key", model: "gpt-5-mini" }, + }, + historyDbPath: ":memory:", + }); +} + +describe("Node OSS temporal feature error notice", () => { + let originalMem0Dir: string | undefined; + let originalTelemetry: string | undefined; + let originalSampleRate: string | undefined; + let originalFetch: typeof global.fetch; + + beforeEach(() => { + originalMem0Dir = process.env.MEM0_DIR; + originalTelemetry = process.env.MEM0_TELEMETRY; + originalSampleRate = process.env.MEM0_TELEMETRY_SAMPLE_RATE; + originalFetch = global.fetch; + + process.env.MEM0_DIR = makeTempMem0Dir(); + process.env.MEM0_TELEMETRY = "true"; + process.env.MEM0_TELEMETRY_SAMPLE_RATE = "1"; + mockEmbed.mockClear(); + mockEmbedBatch.mockClear(); + mockGenerateResponse.mockClear(); + jest.resetModules(); + }); + + afterEach(() => { + if (originalMem0Dir === undefined) delete process.env.MEM0_DIR; + else process.env.MEM0_DIR = originalMem0Dir; + + if (originalTelemetry === undefined) delete process.env.MEM0_TELEMETRY; + else process.env.MEM0_TELEMETRY = originalTelemetry; + + if (originalSampleRate === undefined) { + delete process.env.MEM0_TELEMETRY_SAMPLE_RATE; + } else { + process.env.MEM0_TELEMETRY_SAMPLE_RATE = originalSampleRate; + } + + global.fetch = originalFetch; + jest.restoreAllMocks(); + jest.resetModules(); + }); + + it.each([ + ["add", "timestamp"], + ["search", "referenceDate"], + ])( + "raises CTA copy for displayed %s(%s) and emits displayed=true", + async (triggerFunction, triggerParameter) => { + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + if (triggerFunction === "add") { + await expect( + memory.add("Temporal add", { + userId: "temporal-user", + timestamp: 1778112000, + }), + ).rejects.toThrow(TEMPORAL_COPY); + } else { + await expect( + memory.search("Temporal search", { + filters: { user_id: "temporal-user" }, + referenceDate: "2026-05-06", + }), + ).rejects.toThrow(TEMPORAL_COPY); + } + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "temporal_stub", + notice_type: "error", + flag_key: "mem0-oss-notices", + variant: "displayed", + displayed: true, + payload: TEMPORAL_COPY, + notice_config_found: true, + sync_type: "async", + trigger_function: triggerFunction, + trigger_parameter: triggerParameter, + sample_rate: 1, + }), + ); + }, + ); + + it.each([ + ["add", "timestamp"], + ["search", "referenceDate"], + ])( + "raises CTA copy for holdout %s(%s) and emits displayed=true", + async (triggerFunction, triggerParameter) => { + const { fetchMock, calls } = createFetchMock({ variant: "holdout" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + if (triggerFunction === "add") { + await expect( + memory.add("Temporal add", { + userId: "temporal-user", + timestamp: null, + }), + ).rejects.toThrow(TEMPORAL_COPY); + } else { + await expect( + memory.search("Temporal search", { + filters: { user_id: "temporal-user" }, + referenceDate: null, + }), + ).rejects.toThrow(TEMPORAL_COPY); + } + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "temporal_stub", + variant: "holdout", + displayed: true, + trigger_function: triggerFunction, + trigger_parameter: triggerParameter, + }), + ); + expect(notices[0].properties.bypass_reason).toBeUndefined(); + }, + ); + + it("uses plain error for unknown future variants and emits not_displayed", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "silent" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect( + memory.add("Temporal add", { + userId: "temporal-user", + timestamp: 1778112000, + }), + ).rejects.toThrow(PLAIN_TIMESTAMP_ERROR); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "temporal_stub", + variant: "silent", + displayed: false, + bypass_reason: "not_displayed", + payload: TEMPORAL_COPY, + }), + ); + }); + + it("treats missing enabled as enabled for feature-error payloads", async () => { + const payload = temporalPayload(); + delete (payload.notices.temporal_stub as Record).enabled; + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + payload: JSON.stringify(payload), + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect( + memory.add("Temporal add", { + userId: "temporal-user", + timestamp: 1778112000, + }), + ).rejects.toThrow(TEMPORAL_COPY); + + expect(noticeEvents(calls)[0].properties).toEqual( + expect.objectContaining({ + notice_id: "temporal_stub", + displayed: true, + payload: TEMPORAL_COPY, + }), + ); + }); + + it("uses timestamp plain error for disabled payload and emits payload_disabled", async () => { + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + payload: JSON.stringify(temporalPayload({ enabled: false })), + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect( + memory.add("Temporal add", { + userId: "temporal-user", + timestamp: 1778112000, + }), + ).rejects.toThrow(PLAIN_TIMESTAMP_ERROR); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "temporal_stub", + displayed: false, + bypass_reason: "payload_disabled", + disabled_reason: "payload_disabled", + notice_config_found: true, + trigger_function: "add", + trigger_parameter: "timestamp", + }), + ); + }); + + it.each([ + [ + "missing config", + JSON.stringify({ notices: {} }), + "missing_notice_config", + ], + [ + "missing copy", + JSON.stringify(temporalPayload({ copy: "" })), + "missing_copy", + ], + ["malformed payload", "{not-json", "missing_notice_config"], + ])( + "uses referenceDate plain error for %s", + async (_label, payload, bypassReason) => { + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + payload, + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect( + memory.search("Temporal search", { + filters: { user_id: "temporal-user" }, + referenceDate: "2026-05-06", + }), + ).rejects.toThrow(PLAIN_REFERENCE_DATE_ERROR); + + const notices = noticeEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "temporal_stub", + displayed: false, + bypass_reason: bypassReason, + trigger_function: "search", + trigger_parameter: "referenceDate", + }), + ); + }, + ); + + it("uses plain error and emits no event when the blunt flag is disabled", async () => { + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + flagEnabled: false, + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect( + memory.add("Temporal add", { + userId: "temporal-user", + timestamp: 1778112000, + }), + ).rejects.toThrow(PLAIN_TIMESTAMP_ERROR); + + expect(noticeEvents(calls)).toHaveLength(0); + }); + + it("uses plain error and emits no event when PostHog fails", async () => { + const { fetchMock, calls } = createFetchMock({ failFlags: true }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect( + memory.search("Temporal search", { + filters: { user_id: "temporal-user" }, + referenceDate: "2026-05-06", + }), + ).rejects.toThrow(PLAIN_REFERENCE_DATE_ERROR); + + expect(noticeEvents(calls)).toHaveLength(0); + }); + + it("uses plain error and skips flag evaluation when telemetry is off", async () => { + process.env.MEM0_TELEMETRY = "False"; + jest.resetModules(); + + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect( + memory.add("Temporal add", { + userId: "temporal-user", + timestamp: 1778112000, + }), + ).rejects.toThrow(PLAIN_TIMESTAMP_ERROR); + + expect(fetchMock).not.toHaveBeenCalled(); + expect(noticeEvents(calls)).toHaveLength(0); + }); + + it("throws before add validation, normal telemetry, embeddings, and first-run", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + const addToVectorStoreSpy = jest.spyOn(memory as any, "addToVectorStore"); + + await expect( + memory.add( + undefined as any, + { + timestamp: 1778112000, + } as any, + ), + ).rejects.toThrow(TEMPORAL_COPY); + + expect(mockEmbed).not.toHaveBeenCalled(); + expect(addToVectorStoreSpy).not.toHaveBeenCalled(); + expect(calls.map((call) => call.event)).not.toContain("mem0.add"); + expect( + noticeEvents(calls).some( + (call) => call.properties.notice_id === "first_run", + ), + ).toBe(false); + }); + + it("throws before search validation, normal telemetry, embeddings, and first-run", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect( + memory.search("Temporal search", { + userId: "invalid-top-level-user", + referenceDate: "2026-05-06", + } as any), + ).rejects.toThrow(TEMPORAL_COPY); + + expect(mockEmbed).not.toHaveBeenCalled(); + expect(calls.map((call) => call.event)).not.toContain("mem0.search"); + expect( + noticeEvents(calls).some( + (call) => call.properties.notice_id === "first_run", + ), + ).toBe(false); + }); + + it("leaves normal add/search calls unchanged when temporal options are omitted", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "holdout" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + const addResult = await memory.add("Normal add", { + userId: "normal-user", + infer: false, + }); + expect(addResult.results).toHaveLength(1); + + const searchResult = await memory.search("Normal add", { + filters: { user_id: "normal-user" }, + topK: 3, + }); + expect(searchResult.results.length).toBeGreaterThanOrEqual(0); + + const eventNames = calls.map((call) => call.event); + expect(eventNames).toContain("mem0.add"); + expect(eventNames).toContain("mem0.search"); + expect( + noticeEvents(calls).some( + (call) => call.properties.notice_id === "temporal_stub", + ), + ).toBe(false); + }); +}); diff --git a/mem0-ts/src/oss/tests/notices.temporal-usage.test.ts b/mem0-ts/src/oss/tests/notices.temporal-usage.test.ts new file mode 100644 index 000000000..1ca63c7e2 --- /dev/null +++ b/mem0-ts/src/oss/tests/notices.temporal-usage.test.ts @@ -0,0 +1,594 @@ +/// +import * as fs from "fs"; +import * as os from "os"; +import * as path from "path"; + +jest.setTimeout(15000); + +jest.mock("../src/embeddings/google", () => ({ + GoogleEmbedder: jest.fn(), +})); +jest.mock("../src/llms/google", () => ({ + GoogleLLM: jest.fn(), +})); + +const mockEmbedding = new Array(1536).fill(0.1); +jest.mock("../src/embeddings/openai", () => ({ + OpenAIEmbedder: jest.fn().mockImplementation(() => ({ + embed: jest.fn().mockResolvedValue(mockEmbedding), + embedBatch: jest + .fn() + .mockImplementation((texts: string[]) => + Promise.resolve(texts.map(() => mockEmbedding)), + ), + embeddingDims: 1536, + })), +})); + +jest.mock("../src/llms/openai", () => ({ + OpenAILLM: jest.fn().mockImplementation(() => ({ + generateResponse: jest.fn(), + })), +})); + +const TEMPORAL_USAGE_COPY = + "This looks like a time-aware memory workflow. Mem0 Platform has temporal reasoning built in. Use `timestamp` when adding memories and `reference_date` when searching."; + +function makeTempMem0Dir(): string { + return fs.mkdtempSync(path.join(os.tmpdir(), "mem0-node-temporal-usage-")); +} + +function configPath(): string { + return path.join(process.env.MEM0_DIR as string, "config.json"); +} + +function readConfig(): Record { + const file = configPath(); + if (!fs.existsSync(file)) return {}; + return JSON.parse(fs.readFileSync(file, "utf8")); +} + +function writeConfig(config: Record) { + fs.mkdirSync(path.dirname(configPath()), { recursive: true }); + fs.writeFileSync(configPath(), JSON.stringify(config, null, 4)); +} + +function consumeFirstRun() { + writeConfig({ + user_id: "node-temporal-usage-test-user", + notice_state: { + first_run: { + consumed: true, + trigger_function: "test_setup", + variant: "test", + }, + }, + }); +} + +function temporalUsagePayload(overrides: Record = {}) { + return { + notices: { + temporal_usage: { + enabled: true, + notice_type: "log_line", + copy: TEMPORAL_USAGE_COPY, + ...overrides, + }, + }, + }; +} + +function createFetchMock(options: { + variant?: string; + payload?: unknown; + failFlags?: boolean; + flagEnabled?: boolean; +}) { + const calls: any[] = []; + const fetchMock = jest.fn(async (url: string | URL, init?: RequestInit) => { + const target = String(url); + + if (target.includes("/flags")) { + if (options.failFlags) { + throw new Error("flag evaluation failed"); + } + const payload = + options.payload === undefined + ? JSON.stringify(temporalUsagePayload()) + : options.payload; + return { + ok: true, + json: jest.fn().mockResolvedValue({ + flags: { + "mem0-oss-notices": { + key: "mem0-oss-notices", + enabled: options.flagEnabled ?? true, + variant: options.variant ?? "displayed", + metadata: { payload }, + }, + }, + }), + }; + } + + if (target.includes("/i/v0/e/")) { + calls.push(JSON.parse(String(init?.body))); + return { + ok: true, + text: jest.fn().mockResolvedValue(""), + }; + } + + return { + ok: true, + json: jest.fn().mockResolvedValue({}), + text: jest.fn().mockResolvedValue(""), + }; + }); + + return { fetchMock, calls }; +} + +function noticeEvents(calls: any[]) { + return calls.filter((call) => call.event === "mem0.notice_displayed"); +} + +function temporalUsageEvents(calls: any[]) { + return noticeEvents(calls).filter( + (call) => call.properties.notice_id === "temporal_usage", + ); +} + +function flagRequestCount(fetchMock: jest.Mock) { + return fetchMock.mock.calls.filter(([url]) => String(url).includes("/flags")) + .length; +} + +async function createMemory() { + const { Memory } = await import("../src/memory"); + return new Memory({ + version: "v1.1", + embedder: { + provider: "openai", + config: { apiKey: "test-key", model: "text-embedding-3-small" }, + }, + vectorStore: { + provider: "memory", + config: { + collectionName: `test-temporal-usage-${Date.now()}-${Math.random()}`, + dimension: 1536, + dbPath: ":memory:", + }, + }, + llm: { + provider: "openai", + config: { apiKey: "test-key", model: "gpt-5-mini" }, + }, + historyDbPath: ":memory:", + }); +} + +async function addSearchSeed(memory: any, userId = "temporal-user") { + await memory.add("The user's favorite drink is green tea.", { + userId, + infer: false, + }); +} + +describe("Node OSS temporal usage notice", () => { + let originalMem0Dir: string | undefined; + let originalTelemetry: string | undefined; + let originalSampleRate: string | undefined; + let originalFetch: typeof global.fetch; + let stderrSpy: jest.SpyInstance; + + beforeEach(() => { + originalMem0Dir = process.env.MEM0_DIR; + originalTelemetry = process.env.MEM0_TELEMETRY; + originalSampleRate = process.env.MEM0_TELEMETRY_SAMPLE_RATE; + originalFetch = global.fetch; + + process.env.MEM0_DIR = makeTempMem0Dir(); + process.env.MEM0_TELEMETRY = "true"; + process.env.MEM0_TELEMETRY_SAMPLE_RATE = "1"; + stderrSpy = jest + .spyOn(process.stderr, "write") + .mockImplementation(() => true); + jest.resetModules(); + }); + + afterEach(() => { + if (originalMem0Dir === undefined) delete process.env.MEM0_DIR; + else process.env.MEM0_DIR = originalMem0Dir; + + if (originalTelemetry === undefined) delete process.env.MEM0_TELEMETRY; + else process.env.MEM0_TELEMETRY = originalTelemetry; + + if (originalSampleRate === undefined) { + delete process.env.MEM0_TELEMETRY_SAMPLE_RATE; + } else { + process.env.MEM0_TELEMETRY_SAMPLE_RATE = originalSampleRate; + } + + global.fetch = originalFetch; + jest.restoreAllMocks(); + jest.resetModules(); + }); + + it("detects timestamp-like metadata after successful add", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + const result = await memory.add("Temporal metadata memory", { + userId: "temporal-user", + infer: false, + metadata: { event_date: "2025-04-09" }, + }); + + expect(result.results).toHaveLength(1); + expect(stderrSpy).toHaveBeenCalledWith(`${TEMPORAL_USAGE_COPY}\n`); + const notices = temporalUsageEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "temporal_usage", + notice_type: "log_line", + flag_key: "mem0-oss-notices", + variant: "displayed", + displayed: true, + payload: TEMPORAL_USAGE_COPY, + notice_config_found: true, + sync_type: "async", + trigger_function: "add", + trigger_source: "metadata", + trigger_reason: "date_like_metadata", + sample_rate: 1, + }), + ); + expect(readConfig().notice_state.temporal_usage.events).toHaveLength(1); + }); + + it("emits query trigger fields after successful search", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSearchSeed(memory); + + await memory.search("what happened last week?", { + filters: { user_id: "temporal-user" }, + topK: 3, + }); + + const notices = temporalUsageEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "temporal_usage", + displayed: true, + trigger_function: "search", + trigger_source: "query", + trigger_reason: "relative_phrase", + }), + ); + }); + + it("detects temporal range filters after successful search", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSearchSeed(memory); + + await memory.search("favorite drink", { + filters: { + user_id: "temporal-user", + AND: [{ created_at: { gte: "2025-04-01" } }], + }, + topK: 3, + }); + + const notices = temporalUsageEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "temporal_usage", + displayed: true, + trigger_function: "search", + trigger_source: "filter", + trigger_reason: "date_range_filter", + }), + ); + }); + + it("does not evaluate for normal non-temporal add/search calls", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Normal memory", { + userId: "normal-user", + infer: false, + metadata: { category: "planning" }, + }); + await memory.search("favorite drink", { + filters: { user_id: "normal-user" }, + topK: 3, + }); + + expect(flagRequestCount(fetchMock)).toBe(0); + expect(temporalUsageEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.temporal_usage).toBeUndefined(); + }); + + it("does not evaluate when add/search fail", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await expect( + memory.add("Invalid temporal metadata add", { + infer: false, + metadata: { event_date: "2025-04-09" }, + } as any), + ).rejects.toThrow("One of the filters"); + await expect( + memory.search("what happened last week?", { + filters: {}, + }), + ).rejects.toThrow("filters must contain"); + + expect(temporalUsageEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.temporal_usage).toBeUndefined(); + }); + + it("is silent for holdout and emits displayed=false", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "holdout" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Temporal metadata memory", { + userId: "temporal-user", + infer: false, + metadata: { event_date: "2025-04-09" }, + }); + + expect(stderrSpy).not.toHaveBeenCalled(); + const notices = temporalUsageEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "temporal_usage", + variant: "holdout", + displayed: false, + bypass_reason: "holdout", + trigger_source: "metadata", + trigger_reason: "date_like_metadata", + }), + ); + }); + + it.each([ + [ + "disabled payload", + JSON.stringify(temporalUsagePayload({ enabled: false })), + "payload_disabled", + ], + [ + "missing config", + JSON.stringify({ notices: {} }), + "missing_notice_config", + ], + [ + "missing copy", + JSON.stringify(temporalUsagePayload({ copy: "" })), + "missing_copy", + ], + ["malformed payload", "{not-json", "missing_notice_config"], + ])("is silent and safe for %s", async (_label, payload, bypassReason) => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + payload, + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Temporal metadata memory", { + userId: "temporal-user", + infer: false, + metadata: { event_date: "2025-04-09" }, + }); + + expect(stderrSpy).not.toHaveBeenCalled(); + const notices = temporalUsageEvents(calls); + expect(notices).toHaveLength(1); + expect(notices[0].properties).toEqual( + expect.objectContaining({ + notice_id: "temporal_usage", + displayed: false, + bypass_reason: bypassReason, + }), + ); + }); + + it("does not consume cap or emit when the blunt flag is disabled", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ + variant: "displayed", + flagEnabled: false, + }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Temporal metadata memory", { + userId: "temporal-user", + infer: false, + metadata: { event_date: "2025-04-09" }, + }); + + expect(temporalUsageEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.temporal_usage).toBeUndefined(); + }); + + it("does not consume cap or emit when PostHog fails", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ failFlags: true }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Temporal metadata memory", { + userId: "temporal-user", + infer: false, + metadata: { event_date: "2025-04-09" }, + }); + + expect(temporalUsageEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state?.temporal_usage).toBeUndefined(); + }); + + it("does nothing when telemetry is off", async () => { + process.env.MEM0_TELEMETRY = "False"; + jest.resetModules(); + + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Temporal metadata memory", { + userId: "temporal-user", + infer: false, + metadata: { event_date: "2025-04-09" }, + }); + + expect(fetchMock).not.toHaveBeenCalled(); + expect(temporalUsageEvents(calls)).toHaveLength(0); + expect(readConfig().notice_state).toBeUndefined(); + }); + + it("caps evaluated opportunities at 10 per rolling week", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSearchSeed(memory); + + for (let index = 1; index <= 11; index++) { + await memory.search(`what happened last week ${index}?`, { + filters: { user_id: "temporal-user" }, + topK: 3, + }); + } + + expect(temporalUsageEvents(calls)).toHaveLength(10); + expect(flagRequestCount(fetchMock)).toBe(10); + expect(readConfig().notice_state.temporal_usage.events).toHaveLength(10); + expect(stderrSpy).toHaveBeenCalledTimes(10); + }); + + it("does not consume first-run on the same qualifying temporal usage call", async () => { + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + + await memory.add("Temporal metadata memory", { + userId: "temporal-user", + infer: false, + metadata: { event_date: "2025-04-09" }, + }); + + expect(readConfig().notice_state.first_run).toBeUndefined(); + await memory.add("Normal follow-up memory", { + userId: "temporal-user", + infer: false, + }); + + const notices = noticeEvents(calls); + expect(notices.map((call) => call.properties.notice_id)).toEqual([ + "temporal_usage", + "first_run", + ]); + expect(readConfig().notice_state.first_run.consumed).toBe(true); + }); + + it("does not include raw query, metadata, or filter values in telemetry props", async () => { + consumeFirstRun(); + const { fetchMock, calls } = createFetchMock({ variant: "displayed" }); + global.fetch = fetchMock as any; + const memory = await createMemory(); + await addSearchSeed(memory); + + await memory.search("private trip since 2025-04-09", { + filters: { + user_id: "temporal-user", + created_at: { gte: "2025-04-09" }, + }, + topK: 3, + }); + + const props = temporalUsageEvents(calls)[0].properties; + const serialized = JSON.stringify(props); + expect(serialized).not.toContain("private trip"); + expect(serialized).not.toContain("2025-04-09"); + expect(serialized).not.toContain("temporal-user"); + }); + + it("detects expected query, metadata, and filter cases conservatively", async () => { + const { __noticeTestHooks } = await import("../src/utils/notices"); + + expect( + __noticeTestHooks.detectTemporalUsageFromSearch("notes from today", null), + ).toEqual({ triggerSource: "query", triggerReason: "relative_phrase" }); + expect( + __noticeTestHooks.detectTemporalUsageFromSearch( + "notes from 2025-04-09", + null, + ), + ).toEqual({ triggerSource: "query", triggerReason: "date_like_query" }); + expect( + __noticeTestHooks.detectTemporalUsageFromMetadata({ + event_date: "2025-04-09", + }), + ).toEqual({ + triggerSource: "metadata", + triggerReason: "date_like_metadata", + }); + expect( + __noticeTestHooks.detectTemporalUsageFromSearch("favorite drink", { + AND: [{ created_at: { gte: "2025-04-01" } }], + }), + ).toEqual({ triggerSource: "filter", triggerReason: "date_range_filter" }); + expect( + __noticeTestHooks.detectTemporalUsageFromSearch("favorite drink", null), + ).toBeNull(); + expect( + __noticeTestHooks.detectTemporalUsageFromMetadata({ + category: "planning", + }), + ).toBeNull(); + }); + + it("detectors never raise for cyclic metadata or filters", async () => { + const { __noticeTestHooks } = await import("../src/utils/notices"); + const metadata: Record = {}; + metadata.self = metadata; + const filters: Record = {}; + filters.AND = [filters]; + + expect( + __noticeTestHooks.detectTemporalUsageFromMetadata(metadata), + ).toBeNull(); + expect( + __noticeTestHooks.detectTemporalUsageFromSearch( + "favorite drink", + filters, + ), + ).toBeNull(); + }); +}); diff --git a/mem0-ts/src/oss/tests/telemetry-sampling.test.ts b/mem0-ts/src/oss/tests/telemetry-sampling.test.ts index 7b5f6c5b3..14e68c2f4 100644 --- a/mem0-ts/src/oss/tests/telemetry-sampling.test.ts +++ b/mem0-ts/src/oss/tests/telemetry-sampling.test.ts @@ -61,6 +61,13 @@ describe("telemetry sampling", () => { expect(fetchMock).toHaveBeenCalledTimes(1); }); + it("notice_displayed event fires even at the highest random value", async () => { + randomSpy.mockReturnValue(0.999); + const { captureClientEvent } = await import("../src/utils/telemetry"); + await captureClientEvent("notice_displayed", makeInstance()); + expect(fetchMock).toHaveBeenCalledTimes(1); + }); + it("init event payload has sample_rate: 1.0", async () => { randomSpy.mockReturnValue(0.999); const { captureClientEvent } = await import("../src/utils/telemetry"); diff --git a/mem0-ts/src/oss/tests/vector-stores-compat.test.ts b/mem0-ts/src/oss/tests/vector-stores-compat.test.ts index 8a71049e0..a9ecda1b1 100644 --- a/mem0-ts/src/oss/tests/vector-stores-compat.test.ts +++ b/mem0-ts/src/oss/tests/vector-stores-compat.test.ts @@ -923,6 +923,7 @@ describe("Memory class – backward compat with all providers", () => { jest.doMock("../src/utils/telemetry", () => ({ captureClientEvent: jest.fn().mockResolvedValue(undefined), + isTelemetryEnabled: jest.fn(() => false), })); MemoryClass = require("../src/memory").Memory; diff --git a/mem0/configs/vector_stores/qdrant.py b/mem0/configs/vector_stores/qdrant.py index a61088388..7d9afa86d 100644 --- a/mem0/configs/vector_stores/qdrant.py +++ b/mem0/configs/vector_stores/qdrant.py @@ -16,6 +16,10 @@ class QdrantConfig(BaseModel): path: Optional[str] = Field("/tmp/qdrant", description="Path for local Qdrant database") url: Optional[str] = Field(None, description="Full URL for Qdrant server") api_key: Optional[str] = Field(None, description="API key for Qdrant server") + https: Optional[bool] = Field( + None, + description="Whether to force HTTPS on or off. Explicit schemes in url take precedence.", + ) on_disk: Optional[bool] = Field(False,description="Enables persistent storage. Vectors are kept on disk (True) or in memory (False). Does not delete the local database path.") @model_validator(mode="before") diff --git a/mem0/memory/main.py b/mem0/memory/main.py index 751cc00ae..89d22a205 100644 --- a/mem0/memory/main.py +++ b/mem0/memory/main.py @@ -5,6 +5,7 @@ import hashlib import json import logging import os +import time import uuid import warnings from copy import deepcopy @@ -26,6 +27,29 @@ from mem0.memory.base import MemoryBase from mem0.memory.setup import mark_oss_used, mem0_dir, setup_config from mem0.memory.storage import SQLiteManager from mem0.memory.telemetry import MEM0_TELEMETRY, capture_event +from mem0.memory.notices import ( + PERFORMANCE_SLOW_QUERY_THRESHOLD_SECONDS, + detect_scale_threshold_from_add_result, + detect_scale_threshold_from_top_k, + detect_decay_usage_from_delete, + detect_decay_usage_from_delete_all, + detect_temporal_usage_from_metadata, + detect_temporal_usage_from_search, + display_decay_usage_notice, + display_decay_usage_notice_async, + display_first_run_notice, + display_first_run_notice_async, + display_performance_slow_query_notice, + display_performance_slow_query_notice_async, + display_scale_threshold_notice, + display_scale_threshold_notice_async, + display_temporal_usage_notice, + display_temporal_usage_notice_async, + get_decay_feature_error_message, + get_decay_feature_error_message_async, + get_temporal_feature_error_message, + get_temporal_feature_error_message_async, +) from mem0.memory.utils import ( extract_json, parse_messages, @@ -341,8 +365,43 @@ def _build_session_scope(filters): return "&".join(parts) +def _entity_collection_name(provider: str, collection_name: str) -> str: + separator = "-" if provider == "s3_vectors" else "_" + return f"{collection_name}{separator}entities" + + logger = logging.getLogger(__name__) +_PROJECT_UPDATE_UNSUPPORTED_ERROR = "Project updates are not supported by the OSS Memory SDK." + + +class _OSSProject: + def update( + self, + custom_instructions: Optional[str] = None, + custom_categories: Optional[list] = None, + retrieval_criteria: Optional[list] = None, + multilingual: Optional[bool] = None, + decay: Optional[bool] = None, + ): + if decay is True: + raise ValueError(get_decay_feature_error_message("sync", "project.update", "decay")) + raise ValueError(_PROJECT_UPDATE_UNSUPPORTED_ERROR) + + +class _AsyncOSSProject: + async def update( + self, + custom_instructions: Optional[str] = None, + custom_categories: Optional[list] = None, + retrieval_criteria: Optional[list] = None, + multilingual: Optional[bool] = None, + decay: Optional[bool] = None, + ): + if decay is True: + raise ValueError(await get_decay_feature_error_message_async("async", "project.update", "decay")) + raise ValueError(_PROJECT_UPDATE_UNSUPPORTED_ERROR) + class Memory(MemoryBase): def __init__(self, config: MemoryConfig = MemoryConfig()): @@ -418,12 +477,16 @@ class Memory(MemoryBase): mark_oss_used() capture_event("mem0.init", self, {"sync_type": "sync"}) + @property + def project(self): + return _OSSProject() + @property def entity_store(self): """Lazily initialize entity store on first use.""" if self._entity_store is None: entity_config = _safe_deepcopy_config(self.config.vector_store.config) - entity_collection = f"{self.collection_name}_entities" + entity_collection = _entity_collection_name(self.config.vector_store.provider, self.collection_name) # Set collection name on the cloned config if hasattr(entity_config, 'collection_name'): entity_config.collection_name = entity_collection @@ -601,6 +664,7 @@ class Memory(MemoryBase): agent_id: Optional[str] = None, run_id: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, + timestamp: Optional[Any] = None, infer: bool = True, memory_type: Optional[str] = None, prompt: Optional[str] = None, @@ -618,6 +682,7 @@ class Memory(MemoryBase): agent_id (str, optional): ID of the agent creating the memory. Defaults to None. run_id (str, optional): ID of the run creating the memory. Defaults to None. metadata (dict, optional): Metadata to store with the memory. Defaults to None. + timestamp (Any, optional): Platform-only temporal parameter. Not supported in OSS. infer (bool, optional): If True (default), an LLM is used to extract key facts from 'messages' and decide whether to add, update, or delete related memories. If False, 'messages' are added as raw memories directly. @@ -640,7 +705,10 @@ class Memory(MemoryBase): LLMError: If LLM operations fail. DatabaseError: If database operations fail. """ + if timestamp is not None: + raise ValueError(get_temporal_feature_error_message("sync", "add", "timestamp")) + temporal_usage_notice = detect_temporal_usage_from_metadata(metadata) processed_metadata, effective_filters = _build_filters_and_metadata( user_id=user_id, agent_id=agent_id, @@ -672,6 +740,13 @@ class Memory(MemoryBase): if agent_id is not None and memory_type == MemoryType.PROCEDURAL.value: results = self._create_procedural_memory(messages, metadata=processed_metadata, prompt=prompt) + scale_threshold_notice = detect_scale_threshold_from_add_result(self, results) + if temporal_usage_notice: + display_temporal_usage_notice(self, "sync", "add", *temporal_usage_notice) + elif scale_threshold_notice: + display_scale_threshold_notice(self, "sync", "add", *scale_threshold_notice) + else: + display_first_run_notice(self, "sync", "add") return results if self.config.llm.config.get("enable_vision"): @@ -680,6 +755,13 @@ class Memory(MemoryBase): messages = parse_vision_messages(messages) vector_store_result = self._add_to_vector_store(messages, processed_metadata, effective_filters, infer, prompt=prompt) + scale_threshold_notice = detect_scale_threshold_from_add_result(self, vector_store_result) + if temporal_usage_notice: + display_temporal_usage_notice(self, "sync", "add", *temporal_usage_notice) + elif scale_threshold_notice: + display_scale_threshold_notice(self, "sync", "add", *scale_threshold_notice) + else: + display_first_run_notice(self, "sync", "add") return {"results": vector_store_result} def _add_to_vector_store(self, messages, metadata, filters, infer, prompt=None): @@ -1006,6 +1088,7 @@ class Memory(MemoryBase): capture_event("mem0.get", self, {"memory_id": memory_id, "sync_type": "sync"}) memory = self.vector_store.get(vector_id=memory_id) if not memory: + display_first_run_notice(self, "sync", "get") return None promoted_payload_keys = [ @@ -1034,6 +1117,7 @@ class Memory(MemoryBase): if additional_metadata: result_item["metadata"] = additional_metadata + display_first_run_notice(self, "sync", "get") return result_item def get_all( @@ -1089,6 +1173,7 @@ class Memory(MemoryBase): ) limit = top_k + scale_threshold_notice = detect_scale_threshold_from_top_k(top_k) keys, encoded_ids = process_telemetry_filters(effective_filters) capture_event( @@ -1097,6 +1182,10 @@ class Memory(MemoryBase): all_memories_result = self._get_all_from_vector_store(effective_filters, limit) + if scale_threshold_notice: + display_scale_threshold_notice(self, "sync", "get_all", *scale_threshold_notice) + else: + display_first_run_notice(self, "sync", "get_all") return {"results": all_memories_result} def _get_all_from_vector_store(self, filters, limit): @@ -1155,6 +1244,7 @@ class Memory(MemoryBase): threshold: float = 0.1, rerank: bool = False, explain: bool = False, + reference_date: Optional[Any] = None, **kwargs, ): """ @@ -1186,6 +1276,7 @@ class Memory(MemoryBase): threshold (float, optional): Minimum score for a memory to be included. Defaults to 0.1. rerank (bool, optional): Whether to rerank results. Defaults to False. explain (bool, optional): Whether to include score_details for each result. Defaults to False. + reference_date (Any, optional): Platform-only temporal parameter. Not supported in OSS. Returns: dict: A dictionary containing the search results under a "results" key. @@ -1195,12 +1286,16 @@ class Memory(MemoryBase): ValueError: If filters doesn't contain at least one of user_id, agent_id, run_id, or if threshold/top_k values are invalid. """ + if reference_date is not None: + raise ValueError(get_temporal_feature_error_message("sync", "search", "reference_date")) + # Reject top-level entity params - must use filters instead _reject_top_level_entity_params(kwargs, "search") # Validate search parameters (before applying defaults) _validate_search_params(threshold=threshold, top_k=top_k) query = _validate_and_trim_search_query(query) + temporal_usage_notice = detect_temporal_usage_from_search(query, filters) # Validate and trim entity IDs in filters effective_filters = filters.copy() if filters else {} @@ -1223,6 +1318,7 @@ class Memory(MemoryBase): ) limit = top_k + scale_threshold_notice = detect_scale_threshold_from_top_k(top_k) # Apply enhanced metadata filtering if advanced operators are detected if self._has_advanced_operators(effective_filters): @@ -1251,7 +1347,9 @@ class Memory(MemoryBase): }, ) + search_start = time.perf_counter() original_memories = self._search_vector_store(query, effective_filters, limit, threshold, explain=explain) + search_elapsed_seconds = time.perf_counter() - search_start # Apply reranking if enabled and reranker is available if rerank and self.reranker and original_memories: @@ -1261,6 +1359,21 @@ class Memory(MemoryBase): except Exception as e: logger.warning(f"Reranking failed, using original results: {e}") + if temporal_usage_notice: + display_temporal_usage_notice(self, "sync", "search", *temporal_usage_notice) + elif scale_threshold_notice: + display_scale_threshold_notice(self, "sync", "search", *scale_threshold_notice) + elif search_elapsed_seconds > PERFORMANCE_SLOW_QUERY_THRESHOLD_SECONDS: + display_performance_slow_query_notice( + self, + "sync", + "search", + search_elapsed_seconds, + top_k, + len(original_memories), + ) + else: + display_first_run_notice(self, "sync", "search") return {"results": original_memories} def _process_metadata_filters(self, metadata_filters: Dict[str, Any]) -> Dict[str, Any]: @@ -1570,6 +1683,7 @@ class Memory(MemoryBase): existing_embeddings = {data: self.embedding_model.embed(data, "update")} self._update_memory(memory_id, data, existing_embeddings, metadata) + display_first_run_notice(self, "sync", "update") return {"message": "Memory updated successfully!"} def delete(self, memory_id): @@ -1586,6 +1700,11 @@ class Memory(MemoryBase): raise ValueError(f"Memory with id {memory_id} not found") self._delete_memory(memory_id, existing_memory) + decay_usage_notice = detect_decay_usage_from_delete() + if decay_usage_notice: + display_decay_usage_notice(self, "sync", "delete", *decay_usage_notice) + else: + display_first_run_notice(self, "sync", "delete") return {"message": "Memory deleted successfully!"} def delete_all(self, user_id: Optional[str] = None, agent_id: Optional[str] = None, run_id: Optional[str] = None): @@ -1619,6 +1738,11 @@ class Memory(MemoryBase): logger.info(f"Deleted {len(memories)} memories") + decay_usage_notice = detect_decay_usage_from_delete_all(len(memories)) + if decay_usage_notice: + display_decay_usage_notice(self, "sync", "delete_all", *decay_usage_notice) + else: + display_first_run_notice(self, "sync", "delete_all") return {"message": "Memories deleted successfully!"} def history(self, memory_id): @@ -1632,7 +1756,9 @@ class Memory(MemoryBase): list: List of changes for the memory. """ capture_event("mem0.history", self, {"memory_id": memory_id, "sync_type": "sync"}) - return self.db.get_history(memory_id) + history = self.db.get_history(memory_id) + display_first_run_notice(self, "sync", "history") + return history def _create_memory(self, data, existing_embeddings, metadata=None): logger.debug(f"Creating memory with {data=}") @@ -1832,6 +1958,7 @@ class Memory(MemoryBase): self._entity_store = None capture_event("mem0.reset", self, {"sync_type": "sync"}) + display_first_run_notice(self, "sync", "reset") def close(self): """Release resources held by this Memory instance (SQLite connections, etc.).""" @@ -1893,12 +2020,16 @@ class AsyncMemory(MemoryBase): mark_oss_used() capture_event("mem0.init", self, {"sync_type": "async"}) + @property + def project(self): + return _AsyncOSSProject() + @property def entity_store(self): """Lazily initialize entity store on first use.""" if self._entity_store is None: entity_config = _safe_deepcopy_config(self.config.vector_store.config) - entity_collection = f"{self.collection_name}_entities" + entity_collection = _entity_collection_name(self.config.vector_store.provider, self.collection_name) if hasattr(entity_config, 'collection_name'): entity_config.collection_name = entity_collection elif isinstance(entity_config, dict): @@ -2062,6 +2193,7 @@ class AsyncMemory(MemoryBase): agent_id: Optional[str] = None, run_id: Optional[str] = None, metadata: Optional[Dict[str, Any]] = None, + timestamp: Optional[Any] = None, infer: bool = True, memory_type: Optional[str] = None, prompt: Optional[str] = None, @@ -2076,6 +2208,7 @@ class AsyncMemory(MemoryBase): agent_id (str, optional): ID of the agent creating the memory. Defaults to None. run_id (str, optional): ID of the run creating the memory. Defaults to None. metadata (dict, optional): Metadata to store with the memory. Defaults to None. + timestamp (Any, optional): Platform-only temporal parameter. Not supported in OSS. infer (bool, optional): Whether to infer the memories. Defaults to True. memory_type (str, optional): Type of memory to create. Defaults to None. Pass "procedural_memory" to create procedural memories. @@ -2084,6 +2217,10 @@ class AsyncMemory(MemoryBase): Returns: dict: A dictionary containing the result of the memory addition operation. """ + if timestamp is not None: + raise ValueError(await get_temporal_feature_error_message_async("async", "add", "timestamp")) + + temporal_usage_notice = detect_temporal_usage_from_metadata(metadata) processed_metadata, effective_filters = _build_filters_and_metadata( user_id=user_id, agent_id=agent_id, run_id=run_id, input_metadata=metadata ) @@ -2111,6 +2248,13 @@ class AsyncMemory(MemoryBase): results = await self._create_procedural_memory( messages, metadata=processed_metadata, prompt=prompt, llm=llm ) + scale_threshold_notice = await asyncio.to_thread(detect_scale_threshold_from_add_result, self, results) + if temporal_usage_notice: + await display_temporal_usage_notice_async(self, "async", "add", *temporal_usage_notice) + elif scale_threshold_notice: + await display_scale_threshold_notice_async(self, "async", "add", *scale_threshold_notice) + else: + await display_first_run_notice_async(self, "async", "add") return results if self.config.llm.config.get("enable_vision"): @@ -2119,6 +2263,13 @@ class AsyncMemory(MemoryBase): messages = parse_vision_messages(messages) vector_store_result = await self._add_to_vector_store(messages, processed_metadata, effective_filters, infer, prompt=prompt) + scale_threshold_notice = await asyncio.to_thread(detect_scale_threshold_from_add_result, self, vector_store_result) + if temporal_usage_notice: + await display_temporal_usage_notice_async(self, "async", "add", *temporal_usage_notice) + elif scale_threshold_notice: + await display_scale_threshold_notice_async(self, "async", "add", *scale_threshold_notice) + else: + await display_first_run_notice_async(self, "async", "add") return {"results": vector_store_result} async def _add_to_vector_store( @@ -2452,6 +2603,7 @@ class AsyncMemory(MemoryBase): capture_event("mem0.get", self, {"memory_id": memory_id, "sync_type": "async"}) memory = await asyncio.to_thread(self.vector_store.get, vector_id=memory_id) if not memory: + await display_first_run_notice_async(self, "async", "get") return None promoted_payload_keys = [ @@ -2480,6 +2632,7 @@ class AsyncMemory(MemoryBase): if additional_metadata: result_item["metadata"] = additional_metadata + await display_first_run_notice_async(self, "async", "get") return result_item async def get_all( @@ -2535,6 +2688,7 @@ class AsyncMemory(MemoryBase): ) limit = top_k + scale_threshold_notice = detect_scale_threshold_from_top_k(top_k) keys, encoded_ids = process_telemetry_filters(effective_filters) capture_event( @@ -2543,6 +2697,10 @@ class AsyncMemory(MemoryBase): all_memories_result = await self._get_all_from_vector_store(effective_filters, limit) + if scale_threshold_notice: + await display_scale_threshold_notice_async(self, "async", "get_all", *scale_threshold_notice) + else: + await display_first_run_notice_async(self, "async", "get_all") return {"results": all_memories_result} async def _get_all_from_vector_store(self, filters, limit): @@ -2601,6 +2759,7 @@ class AsyncMemory(MemoryBase): threshold: float = 0.1, rerank: bool = False, explain: bool = False, + reference_date: Optional[Any] = None, **kwargs, ): """ @@ -2632,6 +2791,7 @@ class AsyncMemory(MemoryBase): threshold (float, optional): Minimum score for a memory to be included. Defaults to 0.1. rerank (bool, optional): Whether to rerank results. Defaults to False. explain (bool, optional): Whether to include score_details for each result. Defaults to False. + reference_date (Any, optional): Platform-only temporal parameter. Not supported in OSS. Returns: dict: A dictionary containing the search results under a "results" key. @@ -2641,12 +2801,18 @@ class AsyncMemory(MemoryBase): ValueError: If filters doesn't contain at least one of user_id, agent_id, run_id, or if threshold/top_k values are invalid. """ + if reference_date is not None: + raise ValueError( + await get_temporal_feature_error_message_async("async", "search", "reference_date") + ) + # Reject top-level entity params - must use filters instead _reject_top_level_entity_params(kwargs, "search") # Validate search parameters (before applying defaults) _validate_search_params(threshold=threshold, top_k=top_k) query = _validate_and_trim_search_query(query) + temporal_usage_notice = detect_temporal_usage_from_search(query, filters) # Validate and trim entity IDs in filters effective_filters = filters.copy() if filters else {} @@ -2671,6 +2837,7 @@ class AsyncMemory(MemoryBase): ) limit = top_k + scale_threshold_notice = detect_scale_threshold_from_top_k(top_k) # Apply enhanced metadata filtering if advanced operators are detected if self._has_advanced_operators(effective_filters): @@ -2699,7 +2866,9 @@ class AsyncMemory(MemoryBase): }, ) + search_start = time.perf_counter() original_memories = await self._search_vector_store(query, effective_filters, limit, threshold, explain=explain) + search_elapsed_seconds = time.perf_counter() - search_start # Apply reranking if enabled and reranker is available if rerank and self.reranker and original_memories: @@ -2712,6 +2881,21 @@ class AsyncMemory(MemoryBase): except Exception as e: logger.warning(f"Reranking failed, using original results: {e}") + if temporal_usage_notice: + await display_temporal_usage_notice_async(self, "async", "search", *temporal_usage_notice) + elif scale_threshold_notice: + await display_scale_threshold_notice_async(self, "async", "search", *scale_threshold_notice) + elif search_elapsed_seconds > PERFORMANCE_SLOW_QUERY_THRESHOLD_SECONDS: + await display_performance_slow_query_notice_async( + self, + "async", + "search", + search_elapsed_seconds, + top_k, + len(original_memories), + ) + else: + await display_first_run_notice_async(self, "async", "search") return {"results": original_memories} def _process_metadata_filters(self, metadata_filters: Dict[str, Any]) -> Dict[str, Any]: @@ -3012,6 +3196,7 @@ class AsyncMemory(MemoryBase): existing_embeddings = {data: embeddings} await self._update_memory(memory_id, data, existing_embeddings, metadata) + await display_first_run_notice_async(self, "async", "update") return {"message": "Memory updated successfully!"} async def delete(self, memory_id): @@ -3028,6 +3213,11 @@ class AsyncMemory(MemoryBase): raise ValueError(f"Memory with id {memory_id} not found") await self._delete_memory(memory_id, existing_memory) + decay_usage_notice = detect_decay_usage_from_delete() + if decay_usage_notice: + await display_decay_usage_notice_async(self, "async", "delete", *decay_usage_notice) + else: + await display_first_run_notice_async(self, "async", "delete") return {"message": "Memory deleted successfully!"} async def delete_all(self, user_id=None, agent_id=None, run_id=None): @@ -3064,6 +3254,11 @@ class AsyncMemory(MemoryBase): logger.info(f"Deleted {len(memories[0])} memories") + decay_usage_notice = detect_decay_usage_from_delete_all(len(memories[0])) + if decay_usage_notice: + await display_decay_usage_notice_async(self, "async", "delete_all", *decay_usage_notice) + else: + await display_first_run_notice_async(self, "async", "delete_all") return {"message": "Memories deleted successfully!"} async def history(self, memory_id): @@ -3077,7 +3272,9 @@ class AsyncMemory(MemoryBase): list: List of changes for the memory. """ capture_event("mem0.history", self, {"memory_id": memory_id, "sync_type": "async"}) - return await asyncio.to_thread(self.db.get_history, memory_id) + history = await asyncio.to_thread(self.db.get_history, memory_id) + await display_first_run_notice_async(self, "async", "history") + return history async def _create_memory(self, data, existing_embeddings, metadata=None): logger.debug(f"Creating memory with {data=}") @@ -3295,6 +3492,7 @@ class AsyncMemory(MemoryBase): ) capture_event("mem0.reset", self, {"sync_type": "async"}) + await display_first_run_notice_async(self, "async", "reset") def close(self): """Release resources held by this AsyncMemory instance.""" diff --git a/mem0/memory/notices.py b/mem0/memory/notices.py new file mode 100644 index 000000000..f2f769d65 --- /dev/null +++ b/mem0/memory/notices.py @@ -0,0 +1,1559 @@ +import asyncio +import json +import re +import sys +import threading +from datetime import date, datetime, timedelta, timezone +from typing import Any, Dict, Optional, Tuple + +from mem0.memory import telemetry as telemetry_module +from mem0.memory.setup import _load_config, _write_config + + +FLAG_KEY = "mem0-oss-notices" +NOTICE_ID = "first_run" +TEMPORAL_FEATURE_NOTICE_ID = "temporal_stub" +TEMPORAL_USAGE_NOTICE_ID = "temporal_usage" +DECAY_FEATURE_NOTICE_ID = "decay_stub" +DECAY_USAGE_NOTICE_ID = "decay_usage" +SCALE_THRESHOLD_NOTICE_ID = "scale_threshold" +PERFORMANCE_SLOW_QUERY_NOTICE_ID = "performance_slow_query" +NOTICE_EVENT = "mem0.notice_displayed" +DISPLAYED_VARIANT = "displayed" +HOLDOUT_VARIANT = "holdout" +STATE_SECTION = "notice_state" +STATE_KEY = "first_run" +TEMPORAL_USAGE_STATE_KEY = "temporal_usage" +TEMPORAL_USAGE_CAP = 10 +TEMPORAL_USAGE_WINDOW = timedelta(days=7) +DECAY_USAGE_STATE_KEY = "decay_usage" +DECAY_USAGE_CAP = 10 +DECAY_USAGE_WINDOW = timedelta(days=7) +DECAY_USAGE_DELETE_THRESHOLD = 5 +SCALE_THRESHOLD_STATE_KEY = "scale_threshold" +SCALE_THRESHOLD_CAP = 10 +SCALE_THRESHOLD_WINDOW = timedelta(days=7) +SCALE_MEMORY_COUNT_THRESHOLD = 2000 +SCALE_MEMORY_COUNT_CHECK_INTERVAL = 100 +SCALE_TOP_K_THRESHOLD = 50 +PERFORMANCE_SLOW_QUERY_STATE_KEY = "performance_slow_query" +PERFORMANCE_SLOW_QUERY_CAP = 10 +PERFORMANCE_SLOW_QUERY_WINDOW = timedelta(days=7) +PERFORMANCE_SLOW_QUERY_THRESHOLD_SECONDS = 2.0 +FEATURE_ERROR_CAP = 10 +FEATURE_ERROR_WINDOW = timedelta(days=7) +TEMPORAL_FEATURE_ERROR_MESSAGES = { + "timestamp": "The timestamp parameter is not supported by the OSS Memory SDK.", + "reference_date": "The reference_date parameter is not supported by the OSS Memory SDK.", +} +DECAY_FEATURE_ERROR_MESSAGE = "The decay parameter is not supported by the OSS Memory SDK." + +_ISO_DATE_RE = re.compile( + r"\b\d{4}-\d{2}-\d{2}(?:[T\s]\d{2}:\d{2}(?::\d{2})?(?:\.\d+)?(?:Z|[+-]\d{2}:?\d{2})?)?\b" +) +_RELATIVE_TIME_RE = re.compile( + r"\b(" + r"today|yesterday|tomorrow|" + r"last\s+(?:night|week|month|year)|" + r"this\s+(?:week|month|year)|" + r"next\s+(?:week|month|year)|" + r"(?:past|last)\s+\d+\s+(?:day|days|week|weeks|month|months|year|years)|" + r"(?:since|before|after|until)\s+(?:today|yesterday|tomorrow|\d{4}-\d{2}-\d{2}|last\s+(?:week|month|year))" + r")\b", + re.IGNORECASE, +) +_RANGE_OPERATORS = {"gt", "gte", "lt", "lte"} + +_state_lock = threading.Lock() +_first_run_claimed_in_process = False +_decay_usage_successful_delete_count_in_process = 0 +_temporal_usage_capacity_reached_in_process = False +_decay_usage_capacity_reached_in_process = False +_scale_threshold_capacity_reached_in_process = False +_performance_slow_query_capacity_reached_in_process = False +_feature_error_capacity_reached_in_process = set() +_scale_memory_count_adds_since_check = 0 +_scale_memory_count_checked_in_process = False +_scale_memory_count_threshold_evaluated_in_process = False + + +def display_first_run_notice(memory_instance, sync_type: str, trigger_function: str) -> None: + """Best-effort first-run notice check. Never raises or writes unless displayed.""" + if not telemetry_module.MEM0_TELEMETRY: + return + + if not _claim_first_run_notice(trigger_function): + return + + variant = None + try: + telemetry = telemetry_module._get_oss_telemetry() + if telemetry is None or telemetry.posthog is None or not telemetry.user_id: + return + + flags = telemetry.posthog.evaluate_flags(telemetry.user_id, flag_keys=[FLAG_KEY]) + variant = flags.get_flag(FLAG_KEY) + _update_first_run_variant(variant) + + if variant in (None, False): + return + + payload = _coerce_mapping(flags.get_flag_payload(FLAG_KEY)) + notices = payload.get("notices", {}) + notice_config = _coerce_mapping(notices.get(NOTICE_ID) if isinstance(notices, dict) else {}) + notice_config_found = bool(notice_config) + + copy = notice_config.get("copy") + enabled = notice_config.get("enabled", True) if notice_config_found else False + notice_type = notice_config.get("notice_type", "log_line") + + disabled_reason = None + bypass_reason = None + if not notice_config_found: + bypass_reason = "missing_notice_config" + elif not enabled: + disabled_reason = "payload_disabled" + bypass_reason = disabled_reason + elif not copy: + bypass_reason = "missing_copy" + elif variant != DISPLAYED_VARIANT: + bypass_reason = "holdout" if variant == HOLDOUT_VARIANT else "not_displayed" + + displayed = variant == DISPLAYED_VARIANT and enabled and bool(copy) + + telemetry.capture_event( + NOTICE_EVENT, + { + "notice_id": NOTICE_ID, + "notice_type": notice_type, + "flag_key": FLAG_KEY, + "variant": variant, + "displayed": displayed, + "payload": copy, + "bypass_reason": bypass_reason, + "disabled_reason": disabled_reason, + "notice_config_found": notice_config_found, + "sync_type": sync_type, + "trigger_function": trigger_function, + }, + flags=flags, + ) + + if displayed: + print(copy, file=sys.stderr) + except Exception: + if variant is not None: + _update_first_run_variant(variant) + + +async def display_first_run_notice_async(memory_instance, sync_type: str, trigger_function: str) -> None: + if not telemetry_module.MEM0_TELEMETRY or _first_run_claimed_in_process: + return + await asyncio.to_thread(display_first_run_notice, memory_instance, sync_type, trigger_function) + + +def display_temporal_usage_notice( + memory_instance, + sync_type: str, + trigger_function: str, + trigger_source: str, + trigger_reason: str, +) -> None: + """Best-effort temporal usage notice. Never raises or writes unless displayed.""" + if not telemetry_module.MEM0_TELEMETRY: + return + + if _temporal_usage_at_capacity(): + return + + try: + telemetry = telemetry_module._get_oss_telemetry() + if telemetry is None or telemetry.posthog is None or not telemetry.user_id: + return + + flags = telemetry.posthog.evaluate_flags(telemetry.user_id, flag_keys=[FLAG_KEY]) + variant = flags.get_flag(FLAG_KEY) + if variant in (None, False): + return + + payload = _coerce_mapping(flags.get_flag_payload(FLAG_KEY)) + notices = payload.get("notices", {}) + notice_config = _coerce_mapping( + notices.get(TEMPORAL_USAGE_NOTICE_ID) if isinstance(notices, dict) else {} + ) + notice_config_found = bool(notice_config) + + copy = notice_config.get("copy") + enabled = notice_config.get("enabled", True) if notice_config_found else False + notice_type = notice_config.get("notice_type", "log_line") + + disabled_reason = None + bypass_reason = None + if not notice_config_found: + bypass_reason = "missing_notice_config" + elif not enabled: + disabled_reason = "payload_disabled" + bypass_reason = disabled_reason + elif not copy: + bypass_reason = "missing_copy" + elif variant != DISPLAYED_VARIANT: + bypass_reason = "holdout" if variant == HOLDOUT_VARIANT else "not_displayed" + + displayed = variant == DISPLAYED_VARIANT and enabled and bool(copy) + + if not _record_temporal_usage_opportunity( + variant=variant, + sync_type=sync_type, + trigger_function=trigger_function, + trigger_source=trigger_source, + trigger_reason=trigger_reason, + ): + return + + telemetry.capture_event( + NOTICE_EVENT, + { + "notice_id": TEMPORAL_USAGE_NOTICE_ID, + "notice_type": notice_type, + "flag_key": FLAG_KEY, + "variant": variant, + "displayed": displayed, + "payload": copy, + "bypass_reason": bypass_reason, + "disabled_reason": disabled_reason, + "notice_config_found": notice_config_found, + "sync_type": sync_type, + "trigger_function": trigger_function, + "trigger_source": trigger_source, + "trigger_reason": trigger_reason, + }, + flags=flags, + ) + + if displayed: + print(copy, file=sys.stderr) + except Exception: + return + + +async def display_temporal_usage_notice_async( + memory_instance, + sync_type: str, + trigger_function: str, + trigger_source: str, + trigger_reason: str, +) -> None: + await asyncio.to_thread( + display_temporal_usage_notice, + memory_instance, + sync_type, + trigger_function, + trigger_source, + trigger_reason, + ) + + +def detect_decay_usage_from_delete() -> Optional[Tuple[str, str, Optional[int], Optional[int]]]: + if not telemetry_module.MEM0_TELEMETRY: + return None + + global _decay_usage_successful_delete_count_in_process + try: + with _state_lock: + if _decay_usage_capacity_reached_in_process: + return None + _decay_usage_successful_delete_count_in_process += 1 + delete_count = _decay_usage_successful_delete_count_in_process + + if delete_count >= DECAY_USAGE_DELETE_THRESHOLD and not _decay_usage_at_capacity(): + return ("delete_count", "repeated_deletes", delete_count, None) + except Exception: + return None + + return None + + +def detect_decay_usage_from_delete_all(deleted_count: Any) -> Optional[Tuple[str, str, Optional[int], Optional[int]]]: + if not telemetry_module.MEM0_TELEMETRY: + return None + + deleted_count_value = _coerce_nonnegative_int(deleted_count, 0) + if deleted_count_value <= 0: + return None + + return ("delete_all", "bulk_delete", None, deleted_count_value) + + +def display_decay_usage_notice( + memory_instance, + sync_type: str, + trigger_function: str, + trigger_source: str, + trigger_reason: str, + delete_count: Optional[int] = None, + deleted_count: Optional[int] = None, +) -> None: + """Best-effort decay usage notice. Never raises or writes unless displayed.""" + if not telemetry_module.MEM0_TELEMETRY: + return + + if _decay_usage_at_capacity(): + return + + try: + telemetry = telemetry_module._get_oss_telemetry() + if telemetry is None or telemetry.posthog is None or not telemetry.user_id: + return + + flags = telemetry.posthog.evaluate_flags(telemetry.user_id, flag_keys=[FLAG_KEY]) + variant = flags.get_flag(FLAG_KEY) + if variant in (None, False): + return + + payload = _coerce_mapping(flags.get_flag_payload(FLAG_KEY)) + notices = payload.get("notices", {}) + notice_config = _coerce_mapping( + notices.get(DECAY_USAGE_NOTICE_ID) if isinstance(notices, dict) else {} + ) + notice_config_found = bool(notice_config) + + copy = notice_config.get("copy") + enabled = notice_config.get("enabled", True) if notice_config_found else False + notice_type = notice_config.get("notice_type", "log_line") + + disabled_reason = None + bypass_reason = None + if not notice_config_found: + bypass_reason = "missing_notice_config" + elif not enabled: + disabled_reason = "payload_disabled" + bypass_reason = disabled_reason + elif not copy: + bypass_reason = "missing_copy" + elif variant != DISPLAYED_VARIANT: + bypass_reason = "holdout" if variant == HOLDOUT_VARIANT else "not_displayed" + + displayed = variant == DISPLAYED_VARIANT and enabled and bool(copy) + + if not _record_decay_usage_opportunity( + variant=variant, + sync_type=sync_type, + trigger_function=trigger_function, + trigger_source=trigger_source, + trigger_reason=trigger_reason, + delete_count=delete_count, + deleted_count=deleted_count, + ): + return + + telemetry.capture_event( + NOTICE_EVENT, + { + "notice_id": DECAY_USAGE_NOTICE_ID, + "notice_type": notice_type, + "flag_key": FLAG_KEY, + "variant": variant, + "displayed": displayed, + "payload": copy, + "bypass_reason": bypass_reason, + "disabled_reason": disabled_reason, + "notice_config_found": notice_config_found, + "sync_type": sync_type, + "trigger_function": trigger_function, + "trigger_source": trigger_source, + "trigger_reason": trigger_reason, + "delete_count": delete_count, + "deleted_count": deleted_count, + }, + flags=flags, + ) + + if displayed: + print(copy, file=sys.stderr) + except Exception: + return + + +async def display_decay_usage_notice_async( + memory_instance, + sync_type: str, + trigger_function: str, + trigger_source: str, + trigger_reason: str, + delete_count: Optional[int] = None, + deleted_count: Optional[int] = None, +) -> None: + await asyncio.to_thread( + display_decay_usage_notice, + memory_instance, + sync_type, + trigger_function, + trigger_source, + trigger_reason, + delete_count, + deleted_count, + ) + + +def detect_scale_threshold_from_top_k(top_k: Any) -> Optional[Tuple[str, str, Optional[int], Optional[int], int]]: + try: + top_k_value = int(top_k) + except (TypeError, ValueError): + return None + + if top_k_value < SCALE_TOP_K_THRESHOLD: + return None + + return ("top_k", "high_top_k", top_k_value, None, SCALE_TOP_K_THRESHOLD) + + +def detect_scale_threshold_from_add_result( + memory_instance, + add_result: Any, +) -> Optional[Tuple[str, str, Optional[int], Optional[int], int]]: + if not telemetry_module.MEM0_TELEMETRY: + return None + + added_count = _count_added_memories(add_result) + if added_count == 0: + return None + + global _scale_memory_count_adds_since_check + global _scale_memory_count_checked_in_process + global _scale_memory_count_threshold_evaluated_in_process + try: + with _state_lock: + if _scale_memory_count_threshold_evaluated_in_process: + return None + + _scale_memory_count_adds_since_check += added_count + should_check = ( + not _scale_memory_count_checked_in_process + or _scale_memory_count_adds_since_check >= SCALE_MEMORY_COUNT_CHECK_INTERVAL + ) + if not should_check: + return None + + _scale_memory_count_checked_in_process = True + _scale_memory_count_adds_since_check = 0 + + config = _load_config() + scale_state = _get_notice_state(config, SCALE_THRESHOLD_STATE_KEY) + if scale_state.get("memory_count_threshold_evaluated"): + _scale_memory_count_threshold_evaluated_in_process = True + return None + except Exception: + return None + + provider_count = _get_provider_memory_count(memory_instance) + if provider_count is None or provider_count < SCALE_MEMORY_COUNT_THRESHOLD: + return None + + if not _mark_scale_memory_count_threshold_evaluated(): + return None + + return ( + "memory_count", + "memory_count_threshold", + None, + provider_count, + SCALE_MEMORY_COUNT_THRESHOLD, + ) + + +def display_scale_threshold_notice( + memory_instance, + sync_type: str, + trigger_function: str, + trigger_source: str, + trigger_reason: str, + top_k: Optional[int] = None, + memory_count: Optional[int] = None, + threshold: Optional[int] = None, +) -> None: + """Best-effort scale notice. Never raises or writes unless displayed.""" + if not telemetry_module.MEM0_TELEMETRY: + return + + if _scale_threshold_at_capacity(): + return + + try: + telemetry = telemetry_module._get_oss_telemetry() + if telemetry is None or telemetry.posthog is None or not telemetry.user_id: + return + + flags = telemetry.posthog.evaluate_flags(telemetry.user_id, flag_keys=[FLAG_KEY]) + variant = flags.get_flag(FLAG_KEY) + if variant in (None, False): + return + + payload = _coerce_mapping(flags.get_flag_payload(FLAG_KEY)) + notices = payload.get("notices", {}) + notice_config = _coerce_mapping( + notices.get(SCALE_THRESHOLD_NOTICE_ID) if isinstance(notices, dict) else {} + ) + notice_config_found = bool(notice_config) + + copies = _coerce_mapping(notice_config.get("copies")) if notice_config_found else {} + copy_key = "memory_count" if trigger_source == "memory_count" else "top_k" + copy = _render_scale_copy(copies.get(copy_key), top_k=top_k, memory_count=memory_count) + enabled = notice_config.get("enabled", True) if notice_config_found else False + notice_type = notice_config.get("notice_type", "log_line") + + disabled_reason = None + bypass_reason = None + if not notice_config_found: + bypass_reason = "missing_notice_config" + elif not enabled: + disabled_reason = "payload_disabled" + bypass_reason = disabled_reason + elif not copy: + bypass_reason = "missing_copy" + elif variant != DISPLAYED_VARIANT: + bypass_reason = "holdout" if variant == HOLDOUT_VARIANT else "not_displayed" + + displayed = variant == DISPLAYED_VARIANT and enabled and bool(copy) + + if not _record_scale_threshold_opportunity( + variant=variant, + sync_type=sync_type, + trigger_function=trigger_function, + trigger_source=trigger_source, + trigger_reason=trigger_reason, + top_k=top_k, + memory_count=memory_count, + threshold=threshold, + ): + return + + telemetry.capture_event( + NOTICE_EVENT, + { + "notice_id": SCALE_THRESHOLD_NOTICE_ID, + "notice_type": notice_type, + "flag_key": FLAG_KEY, + "variant": variant, + "displayed": displayed, + "payload": copy, + "bypass_reason": bypass_reason, + "disabled_reason": disabled_reason, + "notice_config_found": notice_config_found, + "sync_type": sync_type, + "trigger_function": trigger_function, + "trigger_source": trigger_source, + "trigger_reason": trigger_reason, + "top_k": top_k, + "memory_count": memory_count, + "threshold": threshold, + }, + flags=flags, + ) + + if displayed: + print(copy, file=sys.stderr) + except Exception: + return + + +async def display_scale_threshold_notice_async( + memory_instance, + sync_type: str, + trigger_function: str, + trigger_source: str, + trigger_reason: str, + top_k: Optional[int] = None, + memory_count: Optional[int] = None, + threshold: Optional[int] = None, +) -> None: + await asyncio.to_thread( + display_scale_threshold_notice, + memory_instance, + sync_type, + trigger_function, + trigger_source, + trigger_reason, + top_k, + memory_count, + threshold, + ) + + +def display_performance_slow_query_notice( + memory_instance, + sync_type: str, + trigger_function: str, + elapsed_seconds: float, + top_k: int, + result_count: int, +) -> None: + """Best-effort slow-query notice. Never raises or writes unless displayed.""" + if not telemetry_module.MEM0_TELEMETRY: + return + + if _performance_slow_query_at_capacity(): + return + + try: + telemetry = telemetry_module._get_oss_telemetry() + if telemetry is None or telemetry.posthog is None or not telemetry.user_id: + return + + flags = telemetry.posthog.evaluate_flags(telemetry.user_id, flag_keys=[FLAG_KEY]) + variant = flags.get_flag(FLAG_KEY) + if variant in (None, False): + return + + payload = _coerce_mapping(flags.get_flag_payload(FLAG_KEY)) + notices = payload.get("notices", {}) + notice_config = _coerce_mapping( + notices.get(PERFORMANCE_SLOW_QUERY_NOTICE_ID) if isinstance(notices, dict) else {} + ) + notice_config_found = bool(notice_config) + + copy = notice_config.get("copy") + enabled = notice_config.get("enabled", True) if notice_config_found else False + notice_type = notice_config.get("notice_type", "log_line") + + disabled_reason = None + bypass_reason = None + if not notice_config_found: + bypass_reason = "missing_notice_config" + elif not enabled: + disabled_reason = "payload_disabled" + bypass_reason = disabled_reason + elif not copy: + bypass_reason = "missing_copy" + elif variant != DISPLAYED_VARIANT: + bypass_reason = "holdout" if variant == HOLDOUT_VARIANT else "not_displayed" + + displayed = variant == DISPLAYED_VARIANT and enabled and bool(copy) + trigger_reason = "slow_query" + + if not _record_performance_slow_query_opportunity( + variant=variant, + sync_type=sync_type, + trigger_function=trigger_function, + trigger_reason=trigger_reason, + ): + return + + telemetry.capture_event( + NOTICE_EVENT, + { + "notice_id": PERFORMANCE_SLOW_QUERY_NOTICE_ID, + "notice_type": notice_type, + "flag_key": FLAG_KEY, + "variant": variant, + "displayed": displayed, + "payload": copy, + "bypass_reason": bypass_reason, + "disabled_reason": disabled_reason, + "notice_config_found": notice_config_found, + "sync_type": sync_type, + "trigger_function": trigger_function, + "trigger_reason": trigger_reason, + "elapsed_ms": round(elapsed_seconds * 1000), + "threshold_ms": round(PERFORMANCE_SLOW_QUERY_THRESHOLD_SECONDS * 1000), + "top_k": top_k, + "result_count": result_count, + }, + flags=flags, + ) + + if displayed: + print(copy, file=sys.stderr) + except Exception: + return + + +async def display_performance_slow_query_notice_async( + memory_instance, + sync_type: str, + trigger_function: str, + elapsed_seconds: float, + top_k: int, + result_count: int, +) -> None: + await asyncio.to_thread( + display_performance_slow_query_notice, + memory_instance, + sync_type, + trigger_function, + elapsed_seconds, + top_k, + result_count, + ) + + +def get_temporal_feature_error_message(sync_type: str, trigger_function: str, trigger_parameter: str) -> str: + """Return the temporal feature error copy and capture event when available.""" + return _get_feature_error_message( + TEMPORAL_FEATURE_NOTICE_ID, + TEMPORAL_FEATURE_ERROR_MESSAGES[trigger_parameter], + sync_type, + trigger_function, + trigger_parameter, + ) + + +async def get_temporal_feature_error_message_async( + sync_type: str, + trigger_function: str, + trigger_parameter: str, +) -> str: + return await asyncio.to_thread( + get_temporal_feature_error_message, + sync_type, + trigger_function, + trigger_parameter, + ) + + +def get_decay_feature_error_message(sync_type: str, trigger_function: str, trigger_parameter: str) -> str: + """Return the decay feature error copy and capture event when available.""" + return _get_feature_error_message( + DECAY_FEATURE_NOTICE_ID, + DECAY_FEATURE_ERROR_MESSAGE, + sync_type, + trigger_function, + trigger_parameter, + ) + + +async def get_decay_feature_error_message_async( + sync_type: str, + trigger_function: str, + trigger_parameter: str, +) -> str: + return await asyncio.to_thread( + get_decay_feature_error_message, + sync_type, + trigger_function, + trigger_parameter, + ) + + +def _get_feature_error_message( + notice_id: str, + plain_error: str, + sync_type: str, + trigger_function: str, + trigger_parameter: str, +) -> str: + if not telemetry_module.MEM0_TELEMETRY: + return plain_error + + if _feature_error_at_capacity(notice_id): + return plain_error + + try: + telemetry = telemetry_module._get_oss_telemetry() + if telemetry is None or telemetry.posthog is None or not telemetry.user_id: + return plain_error + + flags = telemetry.posthog.evaluate_flags(telemetry.user_id, flag_keys=[FLAG_KEY]) + variant = flags.get_flag(FLAG_KEY) + if variant in (None, False): + return plain_error + + payload = _coerce_mapping(flags.get_flag_payload(FLAG_KEY)) + notices = payload.get("notices", {}) + notice_config = _coerce_mapping( + notices.get(notice_id) if isinstance(notices, dict) else {} + ) + notice_config_found = bool(notice_config) + + copy = notice_config.get("copy") + enabled = notice_config.get("enabled", True) if notice_config_found else False + notice_type = notice_config.get("notice_type", "error") + + disabled_reason = None + bypass_reason = None + if not notice_config_found: + bypass_reason = "missing_notice_config" + elif not enabled: + disabled_reason = "payload_disabled" + bypass_reason = disabled_reason + elif not copy: + bypass_reason = "missing_copy" + elif variant not in (DISPLAYED_VARIANT, HOLDOUT_VARIANT): + bypass_reason = "not_displayed" + + displayed = variant in (DISPLAYED_VARIANT, HOLDOUT_VARIANT) and enabled and bool(copy) + + if not _record_feature_error_opportunity( + notice_id=notice_id, + variant=variant, + sync_type=sync_type, + trigger_function=trigger_function, + trigger_parameter=trigger_parameter, + ): + return plain_error + + telemetry.capture_event( + NOTICE_EVENT, + { + "notice_id": notice_id, + "notice_type": notice_type, + "flag_key": FLAG_KEY, + "variant": variant, + "displayed": displayed, + "payload": copy, + "bypass_reason": bypass_reason, + "disabled_reason": disabled_reason, + "notice_config_found": notice_config_found, + "sync_type": sync_type, + "trigger_function": trigger_function, + "trigger_parameter": trigger_parameter, + }, + flags=flags, + ) + + if displayed: + return copy + except Exception: + return plain_error + + return plain_error + + +def detect_temporal_usage_from_metadata(metadata: Optional[Dict[str, Any]]) -> Optional[Tuple[str, str]]: + try: + if not isinstance(metadata, dict): + return None + + for key, value in _walk_mapping(metadata): + temporal_key = _is_temporal_key(key) + if temporal_key and _looks_temporal_value(value, allow_epoch=True): + return ("metadata", "date_like_metadata") + except Exception: + return None + return None + + +def detect_temporal_usage_from_search( + query: Any, + filters: Optional[Dict[str, Any]], +) -> Optional[Tuple[str, str]]: + try: + if isinstance(query, str): + if _RELATIVE_TIME_RE.search(query): + return ("query", "relative_phrase") + if _ISO_DATE_RE.search(query): + return ("query", "date_like_query") + + if _has_temporal_filter(filters): + return ("filter", "date_range_filter") + except Exception: + return None + return None + + +def _claim_first_run_notice(trigger_function: str) -> bool: + global _first_run_claimed_in_process + + with _state_lock: + if _first_run_claimed_in_process: + return False + + config = _load_config() + state = config.get(STATE_SECTION) + if isinstance(state, dict): + first_run = state.get(STATE_KEY) + if isinstance(first_run, dict) and first_run.get("consumed"): + _first_run_claimed_in_process = True + return False + + if not isinstance(state, dict): + state = {} + + state[STATE_KEY] = { + "consumed": True, + "consumed_at": datetime.now(timezone.utc).isoformat(), + "trigger_function": trigger_function, + "variant": None, + } + config[STATE_SECTION] = state + _write_config(config) + _first_run_claimed_in_process = True + return True + + +def _update_first_run_variant(variant) -> None: + try: + with _state_lock: + config = _load_config() + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + state = {} + first_run = state.get(STATE_KEY) + if not isinstance(first_run, dict): + first_run = {"consumed": True} + first_run["variant"] = variant + state[STATE_KEY] = first_run + config[STATE_SECTION] = state + _write_config(config) + except Exception: + return + + +def _feature_error_at_capacity(notice_id: str) -> bool: + if notice_id in _feature_error_capacity_reached_in_process: + return True + + try: + with _state_lock: + config = _load_config() + entries = _recent_feature_error_entries(config, notice_id, datetime.now(timezone.utc)) + at_capacity = len(entries) >= FEATURE_ERROR_CAP + if at_capacity: + _feature_error_capacity_reached_in_process.add(notice_id) + return at_capacity + except Exception: + return True + + +def _record_feature_error_opportunity( + *, + notice_id: str, + variant: str, + sync_type: str, + trigger_function: str, + trigger_parameter: str, +) -> bool: + try: + with _state_lock: + now = datetime.now(timezone.utc) + config = _load_config() + entries = _recent_feature_error_entries(config, notice_id, now) + if len(entries) >= FEATURE_ERROR_CAP: + _feature_error_capacity_reached_in_process.add(notice_id) + return False + + entries.append( + { + "evaluated_at": now.isoformat(), + "variant": variant, + "sync_type": sync_type, + "trigger_function": trigger_function, + "trigger_parameter": trigger_parameter, + } + ) + + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + state = {} + feature_state = state.get(notice_id) + if not isinstance(feature_state, dict): + feature_state = {} + feature_state["events"] = entries + state[notice_id] = feature_state + config[STATE_SECTION] = state + _write_config(config) + if len(entries) >= FEATURE_ERROR_CAP: + _feature_error_capacity_reached_in_process.add(notice_id) + return True + except Exception: + return False + + +def _recent_feature_error_entries(config: Dict[str, Any], notice_id: str, now: datetime): + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + return [] + + feature_state = state.get(notice_id) + if not isinstance(feature_state, dict): + return [] + + entries = feature_state.get("events") + if not isinstance(entries, list): + return [] + + cutoff = now - FEATURE_ERROR_WINDOW + recent = [] + for entry in entries: + if not isinstance(entry, dict): + continue + evaluated_at = _parse_datetime(entry.get("evaluated_at")) + if evaluated_at is not None and evaluated_at >= cutoff: + recent.append(entry) + return recent + + +def _temporal_usage_at_capacity() -> bool: + global _temporal_usage_capacity_reached_in_process + if _temporal_usage_capacity_reached_in_process: + return True + + try: + with _state_lock: + config = _load_config() + entries = _recent_temporal_usage_entries(config, datetime.now(timezone.utc)) + at_capacity = len(entries) >= TEMPORAL_USAGE_CAP + if at_capacity: + _temporal_usage_capacity_reached_in_process = True + return at_capacity + except Exception: + return True + + +def _record_temporal_usage_opportunity( + *, + variant: str, + sync_type: str, + trigger_function: str, + trigger_source: str, + trigger_reason: str, +) -> bool: + global _temporal_usage_capacity_reached_in_process + try: + with _state_lock: + now = datetime.now(timezone.utc) + config = _load_config() + entries = _recent_temporal_usage_entries(config, now) + if len(entries) >= TEMPORAL_USAGE_CAP: + _temporal_usage_capacity_reached_in_process = True + return False + + entries.append( + { + "evaluated_at": now.isoformat(), + "variant": variant, + "sync_type": sync_type, + "trigger_function": trigger_function, + "trigger_source": trigger_source, + "trigger_reason": trigger_reason, + } + ) + + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + state = {} + temporal_state = state.get(TEMPORAL_USAGE_STATE_KEY) + if not isinstance(temporal_state, dict): + temporal_state = {} + temporal_state["events"] = entries + state[TEMPORAL_USAGE_STATE_KEY] = temporal_state + config[STATE_SECTION] = state + _write_config(config) + if len(entries) >= TEMPORAL_USAGE_CAP: + _temporal_usage_capacity_reached_in_process = True + return True + except Exception: + return False + + +def _recent_temporal_usage_entries(config: Dict[str, Any], now: datetime): + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + return [] + + temporal_state = state.get(TEMPORAL_USAGE_STATE_KEY) + if not isinstance(temporal_state, dict): + return [] + + entries = temporal_state.get("events") + if not isinstance(entries, list): + return [] + + cutoff = now - TEMPORAL_USAGE_WINDOW + recent = [] + for entry in entries: + if not isinstance(entry, dict): + continue + evaluated_at = _parse_datetime(entry.get("evaluated_at")) + if evaluated_at is not None and evaluated_at >= cutoff: + recent.append(entry) + return recent + + +def _decay_usage_at_capacity() -> bool: + global _decay_usage_capacity_reached_in_process + if _decay_usage_capacity_reached_in_process: + return True + + try: + with _state_lock: + config = _load_config() + entries = _recent_decay_usage_entries(config, datetime.now(timezone.utc)) + at_capacity = len(entries) >= DECAY_USAGE_CAP + if at_capacity: + _decay_usage_capacity_reached_in_process = True + return at_capacity + except Exception: + return True + + +def _record_decay_usage_opportunity( + *, + variant: str, + sync_type: str, + trigger_function: str, + trigger_source: str, + trigger_reason: str, + delete_count: Optional[int], + deleted_count: Optional[int], +) -> bool: + global _decay_usage_capacity_reached_in_process + try: + with _state_lock: + now = datetime.now(timezone.utc) + config = _load_config() + entries = _recent_decay_usage_entries(config, now) + if len(entries) >= DECAY_USAGE_CAP: + _decay_usage_capacity_reached_in_process = True + return False + + entry = { + "evaluated_at": now.isoformat(), + "variant": variant, + "sync_type": sync_type, + "trigger_function": trigger_function, + "trigger_source": trigger_source, + "trigger_reason": trigger_reason, + } + if delete_count is not None: + entry["delete_count"] = delete_count + if deleted_count is not None: + entry["deleted_count"] = deleted_count + entries.append(entry) + + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + state = {} + decay_state = state.get(DECAY_USAGE_STATE_KEY) + if not isinstance(decay_state, dict): + decay_state = {} + decay_state["events"] = entries + state[DECAY_USAGE_STATE_KEY] = decay_state + config[STATE_SECTION] = state + _write_config(config) + if len(entries) >= DECAY_USAGE_CAP: + _decay_usage_capacity_reached_in_process = True + return True + except Exception: + return False + + +def _recent_decay_usage_entries(config: Dict[str, Any], now: datetime): + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + return [] + + decay_state = state.get(DECAY_USAGE_STATE_KEY) + if not isinstance(decay_state, dict): + return [] + + entries = decay_state.get("events") + if not isinstance(entries, list): + return [] + + cutoff = now - DECAY_USAGE_WINDOW + recent = [] + for entry in entries: + if not isinstance(entry, dict): + continue + evaluated_at = _parse_datetime(entry.get("evaluated_at")) + if evaluated_at is not None and evaluated_at >= cutoff: + recent.append(entry) + return recent + + +def _scale_threshold_at_capacity() -> bool: + global _scale_threshold_capacity_reached_in_process + if _scale_threshold_capacity_reached_in_process: + return True + + try: + with _state_lock: + config = _load_config() + entries = _recent_scale_threshold_entries(config, datetime.now(timezone.utc)) + at_capacity = len(entries) >= SCALE_THRESHOLD_CAP + if at_capacity: + _scale_threshold_capacity_reached_in_process = True + return at_capacity + except Exception: + return True + + +def _record_scale_threshold_opportunity( + *, + variant: str, + sync_type: str, + trigger_function: str, + trigger_source: str, + trigger_reason: str, + top_k: Optional[int], + memory_count: Optional[int], + threshold: Optional[int], +) -> bool: + global _scale_threshold_capacity_reached_in_process + try: + with _state_lock: + now = datetime.now(timezone.utc) + config = _load_config() + entries = _recent_scale_threshold_entries(config, now) + if len(entries) >= SCALE_THRESHOLD_CAP: + _scale_threshold_capacity_reached_in_process = True + return False + + entry = { + "evaluated_at": now.isoformat(), + "variant": variant, + "sync_type": sync_type, + "trigger_function": trigger_function, + "trigger_source": trigger_source, + "trigger_reason": trigger_reason, + } + if top_k is not None: + entry["top_k"] = top_k + if memory_count is not None: + entry["memory_count"] = memory_count + if threshold is not None: + entry["threshold"] = threshold + entries.append(entry) + + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + state = {} + scale_state = state.get(SCALE_THRESHOLD_STATE_KEY) + if not isinstance(scale_state, dict): + scale_state = {} + scale_state["events"] = entries + if trigger_source == "memory_count": + scale_state["memory_count_threshold_evaluated"] = True + state[SCALE_THRESHOLD_STATE_KEY] = scale_state + config[STATE_SECTION] = state + _write_config(config) + if len(entries) >= SCALE_THRESHOLD_CAP: + _scale_threshold_capacity_reached_in_process = True + return True + except Exception: + return False + + +def _mark_scale_memory_count_threshold_evaluated() -> bool: + global _scale_memory_count_threshold_evaluated_in_process + try: + with _state_lock: + config = _load_config() + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + state = {} + scale_state = state.get(SCALE_THRESHOLD_STATE_KEY) + if not isinstance(scale_state, dict): + scale_state = {} + if scale_state.get("memory_count_threshold_evaluated"): + _scale_memory_count_threshold_evaluated_in_process = True + return False + + scale_state["memory_count_threshold_evaluated"] = True + state[SCALE_THRESHOLD_STATE_KEY] = scale_state + config[STATE_SECTION] = state + _write_config(config) + _scale_memory_count_threshold_evaluated_in_process = True + return True + except Exception: + return False + + +def _recent_scale_threshold_entries(config: Dict[str, Any], now: datetime): + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + return [] + + scale_state = state.get(SCALE_THRESHOLD_STATE_KEY) + if not isinstance(scale_state, dict): + return [] + + entries = scale_state.get("events") + if not isinstance(entries, list): + return [] + + cutoff = now - SCALE_THRESHOLD_WINDOW + recent = [] + for entry in entries: + if not isinstance(entry, dict): + continue + evaluated_at = _parse_datetime(entry.get("evaluated_at")) + if evaluated_at is not None and evaluated_at >= cutoff: + recent.append(entry) + return recent + + +def _performance_slow_query_at_capacity() -> bool: + global _performance_slow_query_capacity_reached_in_process + if _performance_slow_query_capacity_reached_in_process: + return True + + try: + with _state_lock: + config = _load_config() + entries = _recent_performance_slow_query_entries(config, datetime.now(timezone.utc)) + at_capacity = len(entries) >= PERFORMANCE_SLOW_QUERY_CAP + if at_capacity: + _performance_slow_query_capacity_reached_in_process = True + return at_capacity + except Exception: + return True + + +def _record_performance_slow_query_opportunity( + *, + variant: str, + sync_type: str, + trigger_function: str, + trigger_reason: str, +) -> bool: + global _performance_slow_query_capacity_reached_in_process + try: + with _state_lock: + now = datetime.now(timezone.utc) + config = _load_config() + entries = _recent_performance_slow_query_entries(config, now) + if len(entries) >= PERFORMANCE_SLOW_QUERY_CAP: + _performance_slow_query_capacity_reached_in_process = True + return False + + entries.append( + { + "evaluated_at": now.isoformat(), + "variant": variant, + "sync_type": sync_type, + "trigger_function": trigger_function, + "trigger_reason": trigger_reason, + } + ) + + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + state = {} + performance_state = state.get(PERFORMANCE_SLOW_QUERY_STATE_KEY) + if not isinstance(performance_state, dict): + performance_state = {} + performance_state["events"] = entries + state[PERFORMANCE_SLOW_QUERY_STATE_KEY] = performance_state + config[STATE_SECTION] = state + _write_config(config) + if len(entries) >= PERFORMANCE_SLOW_QUERY_CAP: + _performance_slow_query_capacity_reached_in_process = True + return True + except Exception: + return False + + +def _recent_performance_slow_query_entries(config: Dict[str, Any], now: datetime): + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + return [] + + performance_state = state.get(PERFORMANCE_SLOW_QUERY_STATE_KEY) + if not isinstance(performance_state, dict): + return [] + + entries = performance_state.get("events") + if not isinstance(entries, list): + return [] + + cutoff = now - PERFORMANCE_SLOW_QUERY_WINDOW + recent = [] + for entry in entries: + if not isinstance(entry, dict): + continue + evaluated_at = _parse_datetime(entry.get("evaluated_at")) + if evaluated_at is not None and evaluated_at >= cutoff: + recent.append(entry) + return recent + + +def _parse_datetime(value: Any): + if not isinstance(value, str): + return None + try: + parsed = datetime.fromisoformat(value.replace("Z", "+00:00")) + except ValueError: + return None + if parsed.tzinfo is None: + parsed = parsed.replace(tzinfo=timezone.utc) + return parsed.astimezone(timezone.utc) + + +def _get_notice_state(config: Dict[str, Any], state_key: str) -> Dict[str, Any]: + state = config.get(STATE_SECTION) + if not isinstance(state, dict): + return {} + + notice_state = state.get(state_key) + return notice_state if isinstance(notice_state, dict) else {} + + +def _count_added_memories(add_result: Any) -> int: + results = add_result.get("results") if isinstance(add_result, dict) else add_result + if not isinstance(results, list): + return 0 + + count = 0 + for item in results: + if isinstance(item, dict) and item.get("event") == "ADD": + count += 1 + return count + + +def _get_provider_memory_count(memory_instance) -> Optional[int]: + vector_store = getattr(memory_instance, "vector_store", None) + if vector_store is None: + return None + + try: + count = getattr(vector_store, "count", None) + if callable(count): + value = _coerce_nonnegative_int(count(), None) + if value is not None: + return value + except Exception: + pass + + try: + col_info = getattr(vector_store, "col_info", None) + if callable(col_info): + return _extract_count(col_info()) + except Exception: + return None + + return None + + +def _extract_count(info: Any) -> Optional[int]: + if info is None: + return None + + if isinstance(info, dict): + for key in ("count", "points_count", "vectors_count", "indexed_vectors_count"): + value = _coerce_nonnegative_int(info.get(key), None) + if value is not None: + return value + return None + + model_dump = getattr(info, "model_dump", None) + if callable(model_dump): + try: + value = _extract_count(model_dump()) + if value is not None: + return value + except Exception: + return None + + for attr in ("count", "points_count", "vectors_count", "indexed_vectors_count"): + value = _coerce_nonnegative_int(getattr(info, attr, None), None) + if value is not None: + return value + + return None + + +def _render_scale_copy(copy_template: Any, *, top_k: Optional[int], memory_count: Optional[int]) -> Optional[str]: + if not isinstance(copy_template, str) or not copy_template.strip(): + return None + try: + return copy_template.format(top_k=top_k, memory_count=memory_count) + except Exception: + return copy_template + + +def _coerce_nonnegative_int(value: Any, default: Optional[int]) -> Optional[int]: + if isinstance(value, bool): + return default + try: + parsed = int(value) + except (TypeError, ValueError): + return default + return parsed if parsed >= 0 else default + + +def _coerce_mapping(payload: Any) -> Dict[str, Any]: + if isinstance(payload, dict): + return payload + if isinstance(payload, str): + try: + parsed = json.loads(payload) + except Exception: + return {} + return parsed if isinstance(parsed, dict) else {} + return {} + + +def _walk_mapping(value: Any, parent_key: str = ""): + if isinstance(value, dict): + for key, child in value.items(): + key_text = str(key) + yield key_text, child + yield from _walk_mapping(child, key_text) + elif isinstance(value, (list, tuple, set)): + for child in value: + yield from _walk_mapping(child, parent_key) + + +def _is_temporal_key(key: Any) -> bool: + key_text = str(key).lower() + exact_keys = { + "date", + "time", + "timestamp", + "datetime", + "event_date", + "reference_date", + "created_at", + "updated_at", + "started_at", + "ended_at", + "expires_at", + } + return ( + key_text in exact_keys + or key_text.endswith("_date") + or key_text.endswith("_time") + or key_text.endswith("_at") + or "timestamp" in key_text + ) + + +def _looks_temporal_value(value: Any, allow_epoch: bool) -> bool: + if isinstance(value, datetime): + return True + if isinstance(value, date): + return True + if isinstance(value, str): + return bool(_ISO_DATE_RE.search(value) or _RELATIVE_TIME_RE.search(value)) + if allow_epoch and isinstance(value, (int, float)) and not isinstance(value, bool): + return 946684800 <= value <= 4102444800 or 946684800000 <= value <= 4102444800000 + return False + + +def _has_temporal_filter(filters: Any) -> bool: + if not isinstance(filters, dict): + return False + + for key, value in filters.items(): + if key in {"AND", "OR", "NOT", "$and", "$or", "$not"}: + if isinstance(value, list) and any(_has_temporal_filter(item) for item in value): + return True + if isinstance(value, dict) and _has_temporal_filter(value): + return True + continue + + temporal_key = _is_temporal_key(key) + if isinstance(value, dict): + range_values = [item for op, item in value.items() if op in _RANGE_OPERATORS] + if range_values and ( + temporal_key + or any(_looks_temporal_value(item, allow_epoch=temporal_key) for item in range_values) + ): + return True + if _has_temporal_filter(value): + return True + elif temporal_key and _looks_temporal_value(value, allow_epoch=True): + return True + + return False diff --git a/mem0/memory/setup.py b/mem0/memory/setup.py index 369fcb036..ed835fb8f 100644 --- a/mem0/memory/setup.py +++ b/mem0/memory/setup.py @@ -1,6 +1,7 @@ import json import logging import os +import tempfile import uuid from datetime import datetime, timezone from hashlib import sha256 @@ -35,10 +36,21 @@ def _load_config(): def _write_config(config): """Best-effort write of ~/.mem0/config.json. Never raises.""" path = _config_path() + temp_path = None try: - with open(path, "w") as f: + os.makedirs(os.path.dirname(path), exist_ok=True) + with tempfile.NamedTemporaryFile("w", dir=os.path.dirname(path), delete=False) as f: + temp_path = f.name json.dump(config, f, indent=4) + f.flush() + os.fsync(f.fileno()) + os.replace(temp_path, path) except Exception as e: + if temp_path: + try: + os.unlink(temp_path) + except OSError: + pass _logger.debug("Failed to write mem0 config %s: %s", path, e) diff --git a/mem0/memory/telemetry.py b/mem0/memory/telemetry.py index 428f9e694..123f69212 100644 --- a/mem0/memory/telemetry.py +++ b/mem0/memory/telemetry.py @@ -14,6 +14,7 @@ from mem0.memory.setup import get_or_create_user_id MEM0_TELEMETRY = os.environ.get("MEM0_TELEMETRY", "True") PROJECT_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX" HOST = "https://us.i.posthog.com" +FEATURE_FLAGS_REQUEST_TIMEOUT_SECONDS = 0.5 if isinstance(MEM0_TELEMETRY, str): MEM0_TELEMETRY = MEM0_TELEMETRY.lower() in ("true", "1", "yes") @@ -49,7 +50,9 @@ MEM0_TELEMETRY_SAMPLE_RATE = _parse_sample_rate(os.environ.get("MEM0_TELEMETRY_S # Events that bypass sampling and always fire. Keep this set in sync with the # event names passed to capture_event() in mem0/memory/main.py. # $identify is included so PostHog person-merging is never lost to sampling. -_LIFECYCLE_EVENTS = frozenset({"mem0.init", "mem0.reset", "mem0._create_procedural_memory", "$identify"}) +_LIFECYCLE_EVENTS = frozenset( + {"mem0.init", "mem0.reset", "mem0._create_procedural_memory", "mem0.notice_displayed", "$identify"} +) def _sampling_before_send(msg): @@ -77,15 +80,15 @@ class AnonymousTelemetry: self.user_id = None return - try: - self.posthog = Posthog(project_api_key=PROJECT_API_KEY, host=HOST, before_send=before_send) - except TypeError: - # posthog <4.5.0 does not accept before_send; fall back without sampling. - _logger.debug("posthog.Posthog does not accept before_send; upgrade to >=4.5.0 for sampling") - self.posthog = Posthog(project_api_key=PROJECT_API_KEY, host=HOST) + self.posthog = Posthog( + project_api_key=PROJECT_API_KEY, + host=HOST, + before_send=before_send, + feature_flags_request_timeout_seconds=FEATURE_FLAGS_REQUEST_TIMEOUT_SECONDS, + ) self.user_id = get_or_create_user_id(vector_store) - def capture_event(self, event_name, properties=None, user_email=None): + def capture_event(self, event_name, properties=None, user_email=None, flags=None): if self.posthog is None: return @@ -109,7 +112,10 @@ class AnonymousTelemetry: **properties, } try: - self.posthog.capture(distinct_id=distinct_id, event=event_name, properties=properties) + capture_kwargs = {"distinct_id": distinct_id, "properties": properties} + if flags is not None: + capture_kwargs["flags"] = flags + self.posthog.capture(event_name, **capture_kwargs) except Exception as e: _logger.debug("Failed to capture telemetry event %r: %s", event_name, e) diff --git a/mem0/memory/utils.py b/mem0/memory/utils.py index f03341efe..253f6b4b4 100644 --- a/mem0/memory/utils.py +++ b/mem0/memory/utils.py @@ -179,11 +179,20 @@ def parse_vision_messages(messages, llm=None, vision_details="auto"): # Handle message content if isinstance(msg["content"], list): - # Multiple image URLs in content - description = get_image_description(msg, llm, vision_details) - returned_messages.append({"role": msg["role"], "content": description}) + if llm is None: + text_parts = [ + part["text"] for part in msg["content"] + if isinstance(part, dict) and part.get("type") == "text" + ] + if not text_parts: + continue + returned_messages.append({"role": msg["role"], "content": " ".join(text_parts)}) + else: + description = get_image_description(msg, llm, vision_details) + returned_messages.append({"role": msg["role"], "content": description}) elif isinstance(msg["content"], dict) and msg["content"].get("type") == "image_url": - # Single image content + if llm is None: + continue image_url = msg["content"]["image_url"]["url"] try: description = get_image_description(image_url, llm, vision_details) diff --git a/mem0/vector_stores/qdrant.py b/mem0/vector_stores/qdrant.py index a42962782..25a688058 100644 --- a/mem0/vector_stores/qdrant.py +++ b/mem0/vector_stores/qdrant.py @@ -37,6 +37,7 @@ class Qdrant(VectorStoreBase): path: str = None, url: str = None, api_key: str = None, + https: bool | None = None, on_disk: bool = False, ): """ @@ -51,6 +52,8 @@ class Qdrant(VectorStoreBase): path (str, optional): Path for local Qdrant database. Defaults to None. url (str, optional): Full URL for Qdrant server. Defaults to None. api_key (str, optional): API key for Qdrant server. Defaults to None. + https (bool, optional): Whether to force HTTPS on or off. Explicit schemes in url take precedence. + Defaults to None. on_disk (bool, optional): Enables persistent storage. Vectors are stored on disk (True) or in memory (False). Does not delete the local database path. Defaults to False. """ @@ -66,6 +69,8 @@ class Qdrant(VectorStoreBase): if host and port: params["host"] = host params["port"] = port + if https is not None: + params["https"] = https if not params: params["path"] = path diff --git a/openmemory/ui/package.json b/openmemory/ui/package.json index fded949c6..15f7ed5aa 100644 --- a/openmemory/ui/package.json +++ b/openmemory/ui/package.json @@ -84,7 +84,9 @@ "immutable@>=5.0.0 <5.1.5": "^5.1.5", "picomatch@<2.3.2": "^2.3.2", "minimatch@>=9.0.0 <9.0.7": "^9.0.7", - "glob@>=10.2.0 <10.5.0": "^10.5.0" + "glob@>=10.2.0 <10.5.0": "^10.5.0", + "postcss@<8.5.10": ">=8.5.10", + "yaml@>=2.0.0 <2.8.3": ">=2.8.3" } } } diff --git a/openmemory/ui/pnpm-lock.yaml b/openmemory/ui/pnpm-lock.yaml index 25ebcb8ba..ca7fa1323 100644 --- a/openmemory/ui/pnpm-lock.yaml +++ b/openmemory/ui/pnpm-lock.yaml @@ -9,6 +9,8 @@ overrides: picomatch@<2.3.2: ^2.3.2 minimatch@>=9.0.0 <9.0.7: ^9.0.7 glob@>=10.2.0 <10.5.0: ^10.5.0 + postcss@<8.5.10: '>=8.5.10' + yaml@>=2.0.0 <2.8.3: '>=2.8.3' importers: @@ -16,97 +18,97 @@ importers: dependencies: '@hookform/resolvers': specifier: ^3.9.1 - version: 3.9.1(react-hook-form@7.54.1(react@19.0.0)) + version: 3.10.0(react-hook-form@7.78.0(react@19.2.7)) '@radix-ui/react-accordion': specifier: ^1.2.2 - version: 1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.2.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-alert-dialog': specifier: ^1.1.4 - version: 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-aspect-ratio': specifier: ^1.1.1 - version: 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-avatar': specifier: ^1.1.2 - version: 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-checkbox': specifier: ^1.1.3 - version: 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.3.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-collapsible': specifier: ^1.1.2 - version: 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-context-menu': specifier: ^2.2.4 - version: 2.2.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-dialog': specifier: ^1.1.4 - version: 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-dropdown-menu': specifier: ^2.1.4 - version: 2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-hover-card': specifier: ^1.1.4 - version: 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-label': specifier: ^2.1.1 - version: 2.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 2.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-menubar': specifier: ^1.1.4 - version: 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-navigation-menu': specifier: ^1.2.3 - version: 1.2.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-popover': specifier: ^1.1.4 - version: 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-progress': specifier: ^1.1.1 - version: 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-radio-group': specifier: ^1.2.2 - version: 1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.4.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-scroll-area': specifier: ^1.2.2 - version: 1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-select': specifier: ^2.1.4 - version: 2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-separator': specifier: ^1.1.1 - version: 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slider': specifier: ^1.2.2 - version: 1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.4.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': specifier: ^1.1.1 - version: 1.1.1(@types/react@19.0.0)(react@19.0.0) + version: 1.2.5(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-switch': specifier: ^1.1.2 - version: 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-tabs': specifier: ^1.1.2 - version: 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-toast': specifier: ^1.2.4 - version: 1.2.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-toggle': specifier: ^1.1.1 - version: 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-toggle-group': specifier: ^1.1.1 - version: 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-tooltip': specifier: ^1.1.6 - version: 1.1.6(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.2.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@reduxjs/toolkit': specifier: ^2.7.0 - version: 2.7.0(react-redux@9.2.0(@types/react@19.0.0)(react@19.0.0)(redux@5.0.1))(react@19.0.0) + version: 2.12.0(react-redux@9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1))(react@19.2.7) autoprefixer: specifier: ^10.4.20 - version: 10.4.20(postcss@8.0.0) + version: 10.5.0(postcss@8.5.15) axios: specifier: ^1.16.0 - version: 1.16.1 + version: 1.17.0 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -115,92 +117,92 @@ importers: version: 2.1.1 cmdk: specifier: 1.0.4 - version: 1.0.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.0.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) date-fns: specifier: 4.1.0 version: 4.1.0 embla-carousel-react: specifier: 8.5.1 - version: 8.5.1(react@19.0.0) + version: 8.5.1(react@19.2.7) input-otp: specifier: 1.4.1 - version: 1.4.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.4.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7) lodash: specifier: ^4.18.1 version: 4.18.1 lucide-react: specifier: ^0.454.0 - version: 0.454.0(react@19.0.0) + version: 0.454.0(react@19.2.7) next: specifier: 15.5.18 - version: 15.5.18(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3) + version: 15.5.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0) next-themes: specifier: ^0.4.4 - version: 0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: specifier: ^19 - version: 19.0.0 + version: 19.2.7 react-day-picker: specifier: 8.10.1 - version: 8.10.1(date-fns@4.1.0)(react@19.0.0) + version: 8.10.1(date-fns@4.1.0)(react@19.2.7) react-dom: specifier: ^19 - version: 19.0.0(react@19.0.0) + version: 19.2.7(react@19.2.7) react-hook-form: specifier: ^7.54.1 - version: 7.54.1(react@19.0.0) + version: 7.78.0(react@19.2.7) react-icons: specifier: ^5.5.0 - version: 5.5.0(react@19.0.0) + version: 5.6.0(react@19.2.7) react-redux: specifier: ^9.2.0 - version: 9.2.0(@types/react@19.0.0)(react@19.0.0)(redux@5.0.1) + version: 9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1) react-resizable-panels: specifier: ^2.1.7 - version: 2.1.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7) recharts: specifier: 2.15.0 - version: 2.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 2.15.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) sass: specifier: ^1.86.3 - version: 1.86.3 + version: 1.100.0 sonner: specifier: ^1.7.1 - version: 1.7.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 1.7.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7) tailwind-merge: specifier: ^2.5.5 - version: 2.5.5 + version: 2.6.1 tailwindcss-animate: specifier: ^1.0.7 - version: 1.0.7(tailwindcss@3.4.17) + version: 1.0.7(tailwindcss@3.4.19) vaul: specifier: ^0.9.6 - version: 0.9.6(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + version: 0.9.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) zod: specifier: ^3.24.1 - version: 3.24.1 + version: 3.25.76 devDependencies: '@types/lodash': specifier: ^4.17.16 - version: 4.17.16 + version: 4.17.24 '@types/node': specifier: ^22 - version: 22.0.0 + version: 22.19.21 '@types/react': specifier: ^19 - version: 19.0.0 + version: 19.2.17 '@types/react-dom': specifier: ^19 - version: 19.0.0 + version: 19.2.3(@types/react@19.2.17) postcss: - specifier: ^8 - version: 8.0.0 + specifier: '>=8.5.10' + version: 8.5.15 tailwindcss: specifier: ^3.4.17 - version: 3.4.17 + version: 3.4.19 typescript: specifier: ^5 - version: 5.0.2 + version: 5.9.3 packages: @@ -208,30 +210,30 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@babel/runtime@7.27.0': - resolution: {integrity: sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==} + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} - '@emnapi/runtime@1.10.0': - resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + '@emnapi/runtime@1.11.0': + resolution: {integrity: sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==} - '@floating-ui/core@1.6.9': - resolution: {integrity: sha512-uMXCuQ3BItDUbAMhIXw7UPXRfAlOAvZzdK9BWpE60MCn+Svt3aLn9jsPTi/WNGlRUu2uI0v5S7JiIUsbsvh3fw==} + '@floating-ui/core@1.7.5': + resolution: {integrity: sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==} - '@floating-ui/dom@1.6.13': - resolution: {integrity: sha512-umqzocjDgNRGTuO7Q8CU32dkHkECqI8ZdMZ5Swb6QAM0t5rnlrN3lGo1hdpscRd3WS8T6DKYK4ephgIH9iRh3w==} + '@floating-ui/dom@1.7.6': + resolution: {integrity: sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==} - '@floating-ui/react-dom@2.1.2': - resolution: {integrity: sha512-06okr5cgPzMNBy+Ycse2A6udMi4bqwW/zgBF/rwjcNqWkyr82Mcg8b0vjX8OJpZFy/FKjJmw6wV7t44kK6kW7A==} + '@floating-ui/react-dom@2.1.8': + resolution: {integrity: sha512-cC52bHwM/n/CxS87FH0yWdngEZrjdtLW/qVruo68qg+prK7ZQ4YGdut2GyDVpoGeAYe/h899rVeOVm6Oi40k2A==} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' - '@floating-ui/utils@0.2.9': - resolution: {integrity: sha512-MDWhGtE+eHw5JW7lq4qhc5yRLS11ERl1c7Z6Xd0a58DozHES6EnNNwUWbMiG4J9Cgj053Bhk8zvlhFYKVhULwg==} + '@floating-ui/utils@0.2.11': + resolution: {integrity: sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==} - '@hookform/resolvers@3.9.1': - resolution: {integrity: sha512-ud2HqmGBM0P0IABqoskKWI6PEf6ZDDBZkFqe2Vnl+mTHCEHzr3ISjjZyCwTjC/qpL25JC9aIDkloQejvMeq0ug==} + '@hookform/resolvers@3.10.0': + resolution: {integrity: sha512-79Dv+3mDF7i+2ajj7SkypSKHhl1cbln1OGavqrsF7p6mbUv11xpqpacPsGDCTRvCSjEEIez2ef1NveSVL3b0Ag==} peerDependencies: react-hook-form: ^7.0.0 @@ -372,27 +374,18 @@ packages: cpu: [x64] os: [win32] - '@isaacs/cliui@8.0.2': - resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} - engines: {node: '>=12'} - - '@jridgewell/gen-mapping@0.3.8': - resolution: {integrity: sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==} - engines: {node: '>=6.0.0'} + '@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/set-array@1.2.1': - resolution: {integrity: sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==} - engines: {node: '>=6.0.0'} + '@jridgewell/sourcemap-codec@1.5.5': + resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} - '@jridgewell/sourcemap-codec@1.5.0': - resolution: {integrity: sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==} - - '@jridgewell/trace-mapping@0.3.25': - resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@jridgewell/trace-mapping@0.3.31': + resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@next/env@15.5.18': resolution: {integrity: sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g==} @@ -457,100 +450,96 @@ packages: resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} - '@parcel/watcher-android-arm64@2.5.1': - resolution: {integrity: sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==} + '@parcel/watcher-android-arm64@2.5.6': + resolution: {integrity: sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [android] - '@parcel/watcher-darwin-arm64@2.5.1': - resolution: {integrity: sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==} + '@parcel/watcher-darwin-arm64@2.5.6': + resolution: {integrity: sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [darwin] - '@parcel/watcher-darwin-x64@2.5.1': - resolution: {integrity: sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==} + '@parcel/watcher-darwin-x64@2.5.6': + resolution: {integrity: sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [darwin] - '@parcel/watcher-freebsd-x64@2.5.1': - resolution: {integrity: sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==} + '@parcel/watcher-freebsd-x64@2.5.6': + resolution: {integrity: sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [freebsd] - '@parcel/watcher-linux-arm-glibc@2.5.1': - resolution: {integrity: sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==} + '@parcel/watcher-linux-arm-glibc@2.5.6': + resolution: {integrity: sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - '@parcel/watcher-linux-arm-musl@2.5.1': - resolution: {integrity: sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==} + '@parcel/watcher-linux-arm-musl@2.5.6': + resolution: {integrity: sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==} engines: {node: '>= 10.0.0'} cpu: [arm] os: [linux] - '@parcel/watcher-linux-arm64-glibc@2.5.1': - resolution: {integrity: sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==} + '@parcel/watcher-linux-arm64-glibc@2.5.6': + resolution: {integrity: sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - '@parcel/watcher-linux-arm64-musl@2.5.1': - resolution: {integrity: sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==} + '@parcel/watcher-linux-arm64-musl@2.5.6': + resolution: {integrity: sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [linux] - '@parcel/watcher-linux-x64-glibc@2.5.1': - resolution: {integrity: sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==} + '@parcel/watcher-linux-x64-glibc@2.5.6': + resolution: {integrity: sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - '@parcel/watcher-linux-x64-musl@2.5.1': - resolution: {integrity: sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==} + '@parcel/watcher-linux-x64-musl@2.5.6': + resolution: {integrity: sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [linux] - '@parcel/watcher-win32-arm64@2.5.1': - resolution: {integrity: sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==} + '@parcel/watcher-win32-arm64@2.5.6': + resolution: {integrity: sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==} engines: {node: '>= 10.0.0'} cpu: [arm64] os: [win32] - '@parcel/watcher-win32-ia32@2.5.1': - resolution: {integrity: sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==} + '@parcel/watcher-win32-ia32@2.5.6': + resolution: {integrity: sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==} engines: {node: '>= 10.0.0'} cpu: [ia32] os: [win32] - '@parcel/watcher-win32-x64@2.5.1': - resolution: {integrity: sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==} + '@parcel/watcher-win32-x64@2.5.6': + resolution: {integrity: sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==} engines: {node: '>= 10.0.0'} cpu: [x64] os: [win32] - '@parcel/watcher@2.5.1': - resolution: {integrity: sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==} + '@parcel/watcher@2.5.6': + resolution: {integrity: sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==} engines: {node: '>= 10.0.0'} - '@pkgjs/parseargs@0.11.0': - resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==} - engines: {node: '>=14'} + '@radix-ui/number@1.1.2': + resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==} - '@radix-ui/number@1.1.0': - resolution: {integrity: sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ==} + '@radix-ui/primitive@1.1.4': + resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} - '@radix-ui/primitive@1.1.1': - resolution: {integrity: sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==} - - '@radix-ui/react-accordion@1.2.2': - resolution: {integrity: sha512-b1oh54x4DMCdGsB4/7ahiSrViXxaBwRPotiZNnYXjLha9vfuURSAZErki6qjDoSIV0eXx5v57XnTGVtGwnfp2g==} + '@radix-ui/react-accordion@1.2.13': + resolution: {integrity: sha512-xITxBB2p5m5tAe7M0F95kb4uAh7jSIKGlExMEm93HlW+XxZHV2eXFbPWLktd4JhRiwcnXNbO7iekcrbZy6ZCvA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -562,8 +551,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-alert-dialog@1.1.4': - resolution: {integrity: sha512-A6Kh23qZDLy3PSU4bh2UJZznOrUdHImIXqF8YtUa6CN73f8EOO9XlXSCd9IHyPvIquTaa/kwaSWzZTtUvgXVGw==} + '@radix-ui/react-alert-dialog@1.1.16': + resolution: {integrity: sha512-vPaIgo0mxYlvcFaM9jB2Uot9TjGXMuAPEvrc6BOLeV+I5U8s1dkIoouYaa6lmSfc5SPMo5x5djOTOTvaigdGMQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -575,8 +564,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-arrow@1.1.1': - resolution: {integrity: sha512-NaVpZfmv8SKeZbn4ijN2V3jlHA9ngBG16VnIIm22nUR0Yk8KUALyBxT3KYEUnNuch9sTE8UTsS3whzBgKOL30w==} + '@radix-ui/react-arrow@1.1.9': + resolution: {integrity: sha512-yqHW5WQ/cTpU/un7dqqIKNy2iRU8BC0JB78PEzTfCCYvZu1U6W9KwObAniMk9nhSfyotKPQTYaUD/HB0f5muig==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -588,8 +577,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-aspect-ratio@1.1.1': - resolution: {integrity: sha512-kNU4FIpcFMBLkOUcgeIteH06/8JLBcYY6Le1iKenDGCYNYFX3TQqCZjzkOsz37h7r94/99GTb7YhEr98ZBJibw==} + '@radix-ui/react-aspect-ratio@1.1.9': + resolution: {integrity: sha512-Xy+Dpxt/5n9rVTdPrNFmf8GwG1NlT1pzCF/z1MgOGZMLZWdWl+km+ZRWGQAPEhbkzSwYEsfYmTca8NhUtVxqnw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -601,8 +590,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-avatar@1.1.2': - resolution: {integrity: sha512-GaC7bXQZ5VgZvVvsJ5mu/AEbjYLnhhkoidOboC50Z6FFlLA03wG2ianUoH+zgDQ31/9gCF59bE4+2bBgTyMiig==} + '@radix-ui/react-avatar@1.1.12': + resolution: {integrity: sha512-NQCQyWC7QrDPhjMn8hUqFeU0lUrprIgm1AyMgLbzuQJibNnatdc3SSMo3/UGFu/eUkJUU1cEcKCnyhXTQzq6tA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -614,8 +603,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-checkbox@1.1.3': - resolution: {integrity: sha512-HD7/ocp8f1B3e6OHygH0n7ZKjONkhciy1Nh0yuBgObqThc3oyx+vuMfFHKAknXRHHWVE9XvXStxJFyjUmB8PIw==} + '@radix-ui/react-checkbox@1.3.4': + resolution: {integrity: sha512-m3JmIOAX5ZzZ6VPjxEU2dbTOhoHi0nT5riwcDwe8idocsWf4a5DXJLDtZ6LfJwMBx7W+A2b7kp2TgPEKtaiF6A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -627,8 +616,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collapsible@1.1.2': - resolution: {integrity: sha512-PliMB63vxz7vggcyq0IxNYk8vGDrLXVWw4+W4B8YnwI1s18x7YZYqlG9PLX7XxAJUi0g2DxP4XKJMFHh/iVh9A==} + '@radix-ui/react-collapsible@1.1.13': + resolution: {integrity: sha512-F0s8+p2XNpfc3k02zBfB0jPWbkHVG162+p7BdUMyJ2308QMqZ+oaclX+FAzKFovgL5OqRU+Rvy6f/vbdlJVaqA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -640,8 +629,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.1': - resolution: {integrity: sha512-LwT3pSho9Dljg+wY2KN2mrrh6y3qELfftINERIzBUO9e0N+t0oMTyn3k9iv+ZqgrwGkRnLpNJrsMv9BZlt2yuA==} + '@radix-ui/react-collection@1.1.9': + resolution: {integrity: sha512-zuSVi7ziP7uQRqc+yGxsKJfNkdyHv3ZKDaHe0gzg4dRgws96TPKWIiz84tVHP4GEcEl8bC0mdt17NkcxaJHmaQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -653,8 +642,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-compose-refs@1.1.1': - resolution: {integrity: sha512-Y9VzoRDSJtgFMUCoiZBDVo084VQ5hfpXxVE+NgkdNsjiDBByiImMZKKhxMwCbdHvhlENG6a833CbFkOQvTricw==} + '@radix-ui/react-compose-refs@1.1.3': + resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -662,17 +651,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-context-menu@2.2.4': - resolution: {integrity: sha512-ap4wdGwK52rJxGkwukU1NrnEodsUFQIooANKu+ey7d6raQ2biTcEf8za1zr0mgFHieevRTB2nK4dJeN8pTAZGQ==} + '@radix-ui/react-context-menu@2.3.0': + resolution: {integrity: sha512-d7CouXhAW+CGmFOqmB+IEvd3E9GcaqfgvfjCc3hfulp2pkaUCEVEGa0SN5nNWYA+IvQ6g1Pt+S5dpNn1AoY9hg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -684,8 +664,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-context@1.1.1': - resolution: {integrity: sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q==} + '@radix-ui/react-context@1.1.4': + resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -693,8 +673,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dialog@1.1.4': - resolution: {integrity: sha512-Ur7EV1IwQGCyaAuyDRiOLA5JIUZxELJljF+MbM/2NC0BYwfuRrbpS30BiQBJrVruscgUkieKkqXYDOoByaxIoA==} + '@radix-ui/react-dialog@1.1.16': + resolution: {integrity: sha512-l9ok83YBclEZhbjgzt76Hw733e6cvRKPNgO6GJ/IETlufXG9p+fRu2wlvpImQvR6xdJ8h7J8J2DBvsPEiEsKMw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -706,8 +686,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-direction@1.1.0': - resolution: {integrity: sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg==} + '@radix-ui/react-direction@1.1.2': + resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -715,8 +695,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dismissable-layer@1.1.3': - resolution: {integrity: sha512-onrWn/72lQoEucDmJnr8uczSNTujT0vJnA/X5+3AkChVPowr8n1yvIKIabhWyMQeMvvmdpsvcyDqx3X1LEXCPg==} + '@radix-ui/react-dismissable-layer@1.1.12': + resolution: {integrity: sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -728,8 +708,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.4': - resolution: {integrity: sha512-iXU1Ab5ecM+yEepGAWK8ZhMyKX4ubFdCNtol4sT9D0OVErG9PNElfx3TQhjw7n7BC5nFVz68/5//clWy+8TXzA==} + '@radix-ui/react-dropdown-menu@2.1.17': + resolution: {integrity: sha512-S6b3Jm57sY5EdDyOMLkacbB0qMnKhy1RCKZCt795ZkmtUOAvojYIZ5p7dXHIh5Cyr3jCLLI5/g64V3FKLudZmw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -741,8 +721,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-focus-guards@1.1.1': - resolution: {integrity: sha512-pSIwfrT1a6sIoDASCSpFwOasEwKTZWDw/iBdtnqKO7v6FeOzYJ7U53cPzYFVR3geGGXgVHaH+CdngrrAzqUGxg==} + '@radix-ui/react-focus-guards@1.1.4': + resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -750,8 +730,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-focus-scope@1.1.1': - resolution: {integrity: sha512-01omzJAYRxXdG2/he/+xy+c8a8gCydoQ1yOxnWNcRhrrBW5W+RQJ22EK1SaO8tb3WoUsuEw7mJjBozPzihDFjA==} + '@radix-ui/react-focus-scope@1.1.9': + resolution: {integrity: sha512-9Se8t+Zry+1rEOL7Y6l/4ANYU/TOtAtf8O2fKdwLltcaMcm6kOqYGbzO4tMFQ0bvzO920pRAoHpFZ4W85S3keQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -763,8 +743,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-hover-card@1.1.4': - resolution: {integrity: sha512-QSUUnRA3PQ2UhvoCv3eYvMnCAgGQW+sTu86QPuNb+ZMi+ZENd6UWpiXbcWDQ4AEaKF9KKpCHBeaJz9Rw6lRlaQ==} + '@radix-ui/react-hover-card@1.1.16': + resolution: {integrity: sha512-hAileDBtd6CX7nlZOarOnISQ6PP4q0e16BX51ulzdZ+7IzjL0sDTVpFdmSYrIjw6zVNsfQBao5gG6AWr3qwfvA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -776,8 +756,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-id@1.1.0': - resolution: {integrity: sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA==} + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -785,17 +765,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-id@1.1.1': - resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-label@2.1.1': - resolution: {integrity: sha512-UUw5E4e/2+4kFMH7+YxORXGWggtY6sM8WIwh5RZchhLuUg2H1hc98Py+pr8HMz6rdaYrK2t296ZEjYLOCO5uUw==} + '@radix-ui/react-label@2.1.9': + resolution: {integrity: sha512-rDoTeMbCwRVcnmo7NGT9IlPo1yXmEI+xc1URP3oeewwZEV4mdTp1dYUhYbQdo4D1q2SjKVvv4N1gNY77QAQtjA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -807,8 +778,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menu@2.1.4': - resolution: {integrity: sha512-BnOgVoL6YYdHAG6DtXONaR29Eq4nvbi8rutrV/xlr3RQCMMb3yqP85Qiw/3NReozrSW+4dfLkK+rc1hb4wPU/A==} + '@radix-ui/react-menu@2.1.17': + resolution: {integrity: sha512-fmbNnFyf+JYCN0DhhWnEdUTDnZD1mXaPQWivdsPIb8oOSbARfD3LIQJbLCG8a8QLCwoMxiJ7GVPIFcC8Dw8v2Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -820,8 +791,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menubar@1.1.4': - resolution: {integrity: sha512-+KMpi7VAZuB46+1LD7a30zb5IxyzLgC8m8j42gk3N4TUCcViNQdX8FhoH1HDvYiA8quuqcek4R4bYpPn/SY1GA==} + '@radix-ui/react-menubar@1.1.17': + resolution: {integrity: sha512-AKtZ4O782yO7qwIyq73WpulYt1IHhQ0htDb6wNcxzxnSDCcSWMVBiU9ycpcA90XzQO4IVIxIErtak6Kg/Vt0rQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -833,8 +804,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-navigation-menu@1.2.3': - resolution: {integrity: sha512-IQWAsQ7dsLIYDrn0WqPU+cdM7MONTv9nqrLVYoie3BPiabSfUVDe6Fr+oEt0Cofsr9ONDcDe9xhmJbL1Uq1yKg==} + '@radix-ui/react-navigation-menu@1.2.15': + resolution: {integrity: sha512-/fS8hKCcRt4DwCGa5QIB3juRXmfYSOk4a2AEe/BDIyy7Hm+eje2Y13oUx5zejl+wFt1owrM7E8NWlbaEl5EGpg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -846,8 +817,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popover@1.1.4': - resolution: {integrity: sha512-aUACAkXx8LaFymDma+HQVji7WhvEhpFJ7+qPz17Nf4lLZqtreGOFRiNQWQmhzp7kEWg9cOyyQJpdIMUMPc/CPw==} + '@radix-ui/react-popover@1.1.16': + resolution: {integrity: sha512-8brVpAU5Uq7Bh0c8EFc4ZTf2JJTYn0o+1L+CUJB3UYIOkTjKGMgoHvduylrahdmNlr3DfH0rFq2DrbNZXgaspw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -859,8 +830,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popper@1.2.1': - resolution: {integrity: sha512-3kn5Me69L+jv82EKRuQCXdYyf1DqHwD2U/sxoNgBGCB7K9TRc3bQamQ+5EPM9EvyPdli0W41sROd+ZU1dTCztw==} + '@radix-ui/react-popper@1.3.0': + resolution: {integrity: sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -872,8 +843,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-portal@1.1.3': - resolution: {integrity: sha512-NciRqhXnGojhT93RPyDaMPfLH3ZSl4jjIFbZQ1b/vxvZEdHsBZ49wP9w8L3HzUQwep01LcWtkUvm0OVB5JAHTw==} + '@radix-ui/react-portal@1.1.11': + resolution: {integrity: sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -885,8 +856,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.2': - resolution: {integrity: sha512-18TFr80t5EVgL9x1SwF/YGtfG+l0BS0PRAlCWBDoBEiDQjeKgnNZRVJp/oVBl24sr3Gbfwc/Qpj4OcWTQMsAEg==} + '@radix-ui/react-presence@1.1.6': + resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -898,8 +869,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.0.1': - resolution: {integrity: sha512-sHCWTtxwNn3L3fH8qAfnF3WbUZycW93SM1j3NFDzXBiz8D6F5UTTy8G1+WFEaiCdvCVRJWj6N2R4Xq6HdiHmDg==} + '@radix-ui/react-primitive@2.1.5': + resolution: {integrity: sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -911,8 +882,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.0.3': - resolution: {integrity: sha512-Pf/t/GkndH7CQ8wE2hbkXA+WyZ83fhQQn5DDmwDiDo6AwN/fhaH8oqZ0jRjMrO2iaMhDi6P1HRx6AZwyMinY1g==} + '@radix-ui/react-progress@1.1.9': + resolution: {integrity: sha512-+EOkvg1Zn1vI1+fRDfRSAiJ7BWfcDAo5ASMmbqrcLZ4s4USk2FGkoHgeb2X+CkUgo2zJMiyObwf1k44CrRWsyw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -924,8 +895,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-progress@1.1.1': - resolution: {integrity: sha512-6diOawA84f/eMxFHcWut0aE1C2kyE9dOyCTQOMRR2C/qPiXz/X0SaiA/RLbapQaXUCmy0/hLMf9meSccD1N0pA==} + '@radix-ui/react-radio-group@1.4.0': + resolution: {integrity: sha512-eHdV5bLx9sH+tBnbDjkIBdvQEH/c6MEtQYhTbxkaDK9qsIFFLtmJYEQFVdwhnruWotLfQmIuWEL/J+L3utE8rQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -937,8 +908,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-radio-group@1.2.2': - resolution: {integrity: sha512-E0MLLGfOP0l8P/NxgVzfXJ8w3Ch8cdO6UDzJfDChu4EJDy+/WdO5LqpdY8PYnCErkmZH3gZhDL1K7kQ41fAHuQ==} + '@radix-ui/react-roving-focus@1.1.12': + resolution: {integrity: sha512-FvgPt1bRmg8Xt2QpF7NUZW3dE0ZQHGm41dAdgT2J2GJPoIXz+9Em3NobAxf4fupcxhgHu03E5CRiU2MWvObXyg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -950,8 +921,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.1': - resolution: {integrity: sha512-QE1RoxPGJ/Nm8Qmk0PxP8ojmoaS67i0s7hVssS7KuI2FQoc/uzVlZsqKfQvxPE6D8hICCPHJ4D88zNhT3OOmkw==} + '@radix-ui/react-scroll-area@1.2.11': + resolution: {integrity: sha512-DS39ziOgea75U/TrXKU2/oKp0be2jrDHnzFLvahg/0iNAT1Zq16e4Uw0WXwyXvsK+mG3BRyMb7A3NRZMDuEXtQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -963,8 +934,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-scroll-area@1.2.2': - resolution: {integrity: sha512-EFI1N/S3YxZEW/lJ/H1jY3njlvTd8tBmgKEn4GHi51+aMm94i6NmAJstsm5cu3yJwYqYc93gpCPm21FeAbFk6g==} + '@radix-ui/react-select@2.3.0': + resolution: {integrity: sha512-mENc7WpJvJcW8hlMpzfFcHcEhTvYS5JMBmi9HVC1Q00uhBwML086MHYUV8QQdQv6lcu0Wg8dzd1RB8AFADcG/g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -976,8 +947,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-select@2.1.4': - resolution: {integrity: sha512-pOkb2u8KgO47j/h7AylCj7dJsm69BXcjkrvTqMptFqsE2i0p8lHkfgneXKjAgPzBMivnoMyt8o4KiV4wYzDdyQ==} + '@radix-ui/react-separator@1.1.9': + resolution: {integrity: sha512-gvgW+JV/Mbjj6darztTetnmElpQEzZrXpJvfj+dOxNAxiyHEAyUvEjjl4zxblvmjmKmi3jfPoy7ZdxzCuUBJSA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -989,8 +960,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-separator@1.1.1': - resolution: {integrity: sha512-RRiNRSrD8iUiXriq/Y5n4/3iE8HzqgLHsusUSg5jVpU2+3tqcUFPJXHDymwEypunc2sWxDUS3UC+rkZRlHedsw==} + '@radix-ui/react-slider@1.4.0': + resolution: {integrity: sha512-RHcPlLOThRJM51DSIC33ZnpDEBYhyEFroVWkd2P54PGGjkmAt14RboYUU9E1MFst666zFHM0tGtWvMjSOtU1pw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1002,8 +973,17 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slider@1.2.2': - resolution: {integrity: sha512-sNlU06ii1/ZcbHf8I9En54ZPW0Vil/yPVg4vQMcFNjrIx51jsHbFl1HYHQvCIWJSr1q0ZmA+iIs/ZTv8h7HHSA==} + '@radix-ui/react-slot@1.2.5': + resolution: {integrity: sha512-rCMO3QsIVKv5JTY5CVbo2MvO77SpEqqYc8AvRE7OWqRDOIqAKjsp+DrmnY9uc8NPdxB5E2z47HTYGeE2+NTptg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.3.0': + resolution: {integrity: sha512-GP1EZwhoZO/GGnhM1P5/2Vpm8iN8EnngyU0oezn2l78kN8tj25pyrvjIaT7azBhK615KSt+P2w39y57YV5jVkA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1015,26 +995,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slot@1.1.1': - resolution: {integrity: sha512-RApLLOcINYJA+dMVbOju7MYv1Mb2EBp2nH4HdDzXTSyaR5optlm6Otrz1euW3HbdOR8UmmFK06TD+A9frYWv+g==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-slot@1.2.0': - resolution: {integrity: sha512-ujc+V6r0HNDviYqIK3rW4ffgYiZ8g5DEHrGJVk4x7kTlLXRDILnKX9vAUYeIsLOoDpDJ0ujpqMkjH4w2ofuo6w==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-switch@1.1.2': - resolution: {integrity: sha512-zGukiWHjEdBCRyXvKR6iXAQG6qXm2esuAD6kDOi9Cn+1X6ev3ASo4+CsYaD6Fov9r/AQFekqnD/7+V0Cs6/98g==} + '@radix-ui/react-tabs@1.1.14': + resolution: {integrity: sha512-D5jwp9JNuwDeCw3CYD2Fz+sSHo0droQjC8u75dJHe4aWr5q6yBiXZU+hurXnKudRgEpUkD5TsI6bjHPo5ThUxA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1046,8 +1008,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tabs@1.1.2': - resolution: {integrity: sha512-9u/tQJMcC2aGq7KXpGivMm1mgq7oRJKXphDwdypPd/j21j/2znamPU8WkXgnhUaTrSFNIt8XhOyCAupg8/GbwQ==} + '@radix-ui/react-toast@1.2.16': + resolution: {integrity: sha512-WUymDDiN2DpoGudRN1aW4wF5O3BNQjZZO/5nngPoNiEVqjyOzirvZZNO0R6dC1ifucSINVaSv8JX1aq47VGgiA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1059,8 +1021,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toast@1.2.4': - resolution: {integrity: sha512-Sch9idFJHJTMH9YNpxxESqABcAFweJG4tKv+0zo0m5XBvUSL8FM5xKcJLFLXononpePs8IclyX1KieL5SDUNgA==} + '@radix-ui/react-toggle-group@1.1.12': + resolution: {integrity: sha512-TEgECgJaWGAHJJZGzNNEYTNBdIXqX7LchANycpyP7DkfjmuiSN7ISt1k/ZRGVJgVJonsgP4vwaiKMn5utrcwWQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1072,8 +1034,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toggle-group@1.1.1': - resolution: {integrity: sha512-OgDLZEA30Ylyz8YSXvnGqIHtERqnUt1KUYTKdw/y8u7Ci6zGiJfXc02jahmcSNK3YcErqioj/9flWC9S1ihfwg==} + '@radix-ui/react-toggle@1.1.11': + resolution: {integrity: sha512-FikrKJemoBGZQ6uRID0HJqSPBP6D7OppdD2OhLl0ZYLlAyPXI7MezoYGmumwNkrAoRm35xXkb4C8JPfJZZzcaw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1085,8 +1047,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toggle@1.1.1': - resolution: {integrity: sha512-i77tcgObYr743IonC1hrsnnPmszDRn8p+EGUsUt+5a/JFn28fxaM88Py6V2mc8J5kELMWishI0rLnuGLFD/nnQ==} + '@radix-ui/react-tooltip@1.2.9': + resolution: {integrity: sha512-u6F9MmTtBSLkiXNVDrtB/yPCZarM9smNswC24YYLV/M+bth6J3Gs3vlJezEoFwKZvPvxhCpUYdUnOsNG/0XOlA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1098,8 +1060,89 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tooltip@1.1.6': - resolution: {integrity: sha512-TLB5D8QLExS1uDn7+wH/bjEmRurNMTzNrtq7IjaS4kjion9NtzsTGkvR5+i7yc9q01Pi2KMM2cN3f8UG4IvvXA==} + '@radix-ui/react-use-callback-ref@1.1.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.2': + resolution: {integrity: sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-is-hydrated@0.1.1': + resolution: {integrity: sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.2': + resolution: {integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.5': + resolution: {integrity: sha512-tPcHNI3FajdDBFpl/Ez1m2WL0ufJqBKyHxMDBvKitopamK36WwBGOMicuMEZKkM5Wce41QxUyv6BsiqfrWBiGg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1111,96 +1154,11 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-use-callback-ref@1.1.0': - resolution: {integrity: sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true + '@radix-ui/rect@1.1.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} - '@radix-ui/react-use-controllable-state@1.1.0': - resolution: {integrity: sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-escape-keydown@1.1.0': - resolution: {integrity: sha512-L7vwWlR1kTTQ3oh7g1O0CBF3YCyyTj8NmhLR+phShpyA50HCfBFKVJTpshm9PzLiKmehsrQzTYTpX9HvmC9rhw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.1.0': - resolution: {integrity: sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-previous@1.1.0': - resolution: {integrity: sha512-Z/e78qg2YFnnXcW88A4JmTtm4ADckLno6F7OXotmkQfeuCVaKuYzqAATPhVzl3delXE7CxIV8shofPn3jPc5Og==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-rect@1.1.0': - resolution: {integrity: sha512-0Fmkebhr6PiseyZlYAOtLS+nb7jLmpqTrJyv61Pe68MKYW6OWdRE2kI70TaYY27u7H0lajqM3hSMMLFq18Z7nQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-size@1.1.0': - resolution: {integrity: sha512-XW3/vWuIXHa+2Uwcc2ABSfcCledmXhhQPlGbfcRXbiUQI5Icjcg19BGCZVKKInYbvUCut/ufbbLLPFC5cbb1hw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-visually-hidden@1.1.1': - resolution: {integrity: sha512-vVfA2IZ9q/J+gEamvj761Oq1FpWgCDaNOOIfbPVp2MVPLEomUr5+Vf7kJGwQ24YxZSlQVar7Bes8kyTo5Dshpg==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/rect@1.1.0': - resolution: {integrity: sha512-A9+lCBZoaMJlVKcRBz2YByCG+Cp2t6nAnMnNba+XiWxnj6r4JUFqfsgwocMBZU9LPtdxC6wB56ySYpc7LQIoJg==} - - '@reduxjs/toolkit@2.7.0': - resolution: {integrity: sha512-XVwolG6eTqwV0N8z/oDlN93ITCIGIop6leXlGJI/4EKy+0POYkR+ABHRSdGXY+0MQvJBP8yAzh+EYFxTuvmBiQ==} + '@reduxjs/toolkit@2.12.0': + resolution: {integrity: sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==} peerDependencies: react: ^16.9.0 || ^17.0.0 || ^18 || ^19 react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 @@ -1210,8 +1168,8 @@ packages: react-redux: optional: true - '@standard-schema/spec@1.0.0': - resolution: {integrity: sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA==} + '@standard-schema/spec@1.1.0': + resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} '@standard-schema/utils@0.3.0': resolution: {integrity: sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==} @@ -1219,8 +1177,8 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@types/d3-array@3.2.1': - resolution: {integrity: sha512-Y2Jn2idRrLzUfAKV2LyRImR+y4oa2AntrgID95SHJxuMUrkNXmanDSed71sRNZysveJVt1hLLemQZIady0FpEg==} + '@types/d3-array@3.2.2': + resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} '@types/d3-color@3.1.3': resolution: {integrity: sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==} @@ -1237,8 +1195,8 @@ packages: '@types/d3-scale@4.0.9': resolution: {integrity: sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==} - '@types/d3-shape@3.1.7': - resolution: {integrity: sha512-VLvUQ33C+3J+8p+Daf+nYSOsjB4GXp19/S/aGo60m9h1v6XaxjiT82lKVWJCfzhtuZ3yD7i/TPeC/fuKLLOSmg==} + '@types/d3-shape@3.1.8': + resolution: {integrity: sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==} '@types/d3-time@3.0.4': resolution: {integrity: sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==} @@ -1246,17 +1204,19 @@ packages: '@types/d3-timer@3.0.2': resolution: {integrity: sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==} - '@types/lodash@4.17.16': - resolution: {integrity: sha512-HX7Em5NYQAXKW+1T+FiuG27NGwzJfCX3s1GjOa7ujxZa52kjJLOr4FUxT+giF6Tgxv1e+/czV/iTtBw27WTU9g==} + '@types/lodash@4.17.24': + resolution: {integrity: sha512-gIW7lQLZbue7lRSWEFql49QJJWThrTFFeIMJdp3eH4tKoxm1OvEPg02rm4wCCSHS0cL3/Fizimb35b7k8atwsQ==} - '@types/node@22.0.0': - resolution: {integrity: sha512-VT7KSYudcPOzP5Q0wfbowyNLaVR8QWUdw+088uFWwfvpY6uCWaXpqV6ieLAu9WBcnTa7H4Z5RLK8I5t2FuOcqw==} + '@types/node@22.19.21': + resolution: {integrity: sha512-VMeFBSCKQKmm2swI2kW51SFusDqekC6q9trBCvJ/JliDchFSuoYYKN7yVNjPthP1HKZcx3U1gI/wTcEBjEFKTA==} - '@types/react-dom@19.0.0': - resolution: {integrity: sha512-1KfiQKsH1o00p9m5ag12axHQSb3FOU9H20UTrujVSkNhuCrRHiQWFqgEnTNK5ZNfnzZv8UWrnXVqCmCF9fgY3w==} + '@types/react-dom@19.2.3': + resolution: {integrity: sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==} + peerDependencies: + '@types/react': ^19.2.0 - '@types/react@19.0.0': - resolution: {integrity: sha512-MY3oPudxvMYyesqs/kW1Bh8y9VqSmf+tzqw3ae8a9DZW68pUe3zAdHeI1jc6iAysuRdACnVknHP8AhwD4/dxtg==} + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} '@types/use-sync-external-store@0.0.6': resolution: {integrity: sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==} @@ -1265,22 +1225,6 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - ansi-regex@5.0.1: - resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} - engines: {node: '>=8'} - - ansi-regex@6.1.0: - resolution: {integrity: sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==} - engines: {node: '>=12'} - - ansi-styles@4.3.0: - resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} - engines: {node: '>=8'} - - ansi-styles@6.2.1: - resolution: {integrity: sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==} - engines: {node: '>=12'} - any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} @@ -1291,39 +1235,38 @@ packages: arg@5.0.2: resolution: {integrity: sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==} - aria-hidden@1.2.4: - resolution: {integrity: sha512-y+CcFFwelSXpLZk/7fMB2mUbGtX9lKycf1MWJ7CaTIERyitVlyQx6C+sxcROU2BAJ24OiZyK+8wj2i8AlBoS3A==} + aria-hidden@1.2.6: + resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==} engines: {node: '>=10'} asynckit@0.4.0: resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==} - autoprefixer@10.4.20: - resolution: {integrity: sha512-XY25y5xSv/wEoqzDyXXME4AFfkZI0P23z6Fs3YgymDnKJkCGOnkL0iTxCa85UTqaSgfcqyf3UA6+c7wUvx/16g==} + autoprefixer@10.5.0: + resolution: {integrity: sha512-FMhOoZV4+qR6aTUALKX2rEqGG+oyATvwBt9IIzVR5rMa2HRWPkxf+P+PAJLD1I/H5/II+HuZcBJYEFBpq39ong==} engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: - postcss: ^8.1.0 + postcss: '>=8.5.10' - axios@1.16.1: - resolution: {integrity: sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==} + axios@1.17.0: + resolution: {integrity: sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==} - balanced-match@1.0.2: - resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} + baseline-browser-mapping@2.10.35: + resolution: {integrity: sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==} + engines: {node: '>=6.0.0'} + hasBin: true binary-extensions@2.3.0: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - brace-expansion@2.1.1: - resolution: {integrity: sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==} - braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} - browserslist@4.24.4: - resolution: {integrity: sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==} + browserslist@4.28.2: + resolution: {integrity: sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true @@ -1335,16 +1278,16 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001714: - resolution: {integrity: sha512-mtgapdwDLSSBnCI3JokHM7oEQBLxiJKVRtg10AxM1AyeiKcM96f0Mkbqeq+1AbiCtvMcHRulAAEMu693JrSWqg==} + caniuse-lite@1.0.30001799: + resolution: {integrity: sha512-hG1bReV+OUU+MOqK4t/ZWI0tZOyz3rqS9XuhOUz1cIcbwBKjOyJEJuw9ER5JuNyqxNk8u/JUVbGibBOL1yrjFw==} chokidar@3.6.0: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} - chokidar@4.0.3: - resolution: {integrity: sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==} - engines: {node: '>= 14.16.0'} + chokidar@5.0.0: + resolution: {integrity: sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==} + engines: {node: '>= 20.19.0'} class-variance-authority@0.7.1: resolution: {integrity: sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg==} @@ -1362,16 +1305,6 @@ packages: react: ^18 || ^19 || ^19.0.0-rc react-dom: ^18 || ^19 || ^19.0.0-rc - color-convert@2.0.1: - resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} - engines: {node: '>=7.0.0'} - - color-name@1.1.4: - resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} - - colorette@1.4.0: - resolution: {integrity: sha512-Y2oEozpomLn7Q3HFP7dpww7AtMJplbM9lGZP6RDfHqmbeRjiwRg4n6VM6j4KLmRke85uWEI7JqF17f3pqdRA0g==} - combined-stream@1.0.8: resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==} engines: {node: '>= 0.8'} @@ -1380,17 +1313,13 @@ packages: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} - cross-spawn@7.0.6: - resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} - engines: {node: '>= 8'} - cssesc@3.0.0: resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==} engines: {node: '>=4'} hasBin: true - csstype@3.1.3: - resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + csstype@3.2.3: + resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} d3-array@3.2.4: resolution: {integrity: sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==} @@ -1404,8 +1333,8 @@ packages: resolution: {integrity: sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==} engines: {node: '>=12'} - d3-format@3.1.0: - resolution: {integrity: sha512-YyUI6AEuY/Wpt8KWLgZHsIU86atmikuoOmCfommt0LYHiQSPjvX2AcFc38PX0CBpr2RCyZhjex+NS/LPOv6YqA==} + d3-format@3.1.2: + resolution: {integrity: sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==} engines: {node: '>=12'} d3-interpolate@3.0.1: @@ -1455,11 +1384,6 @@ packages: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} - detect-libc@1.0.3: - resolution: {integrity: sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==} - engines: {node: '>=0.10'} - hasBin: true - detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} @@ -1480,11 +1404,8 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - eastasianwidth@0.2.0: - resolution: {integrity: sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==} - - electron-to-chromium@1.5.137: - resolution: {integrity: sha512-/QSJaU2JyIuTbbABAo/crOs+SuAZLS+fVVS10PVrIT9hrRkmZl8Hb0xPSkKRUUWHQtYzXHpQUW3Dy5hwMzGZkA==} + electron-to-chromium@1.5.371: + resolution: {integrity: sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w==} embla-carousel-react@8.5.1: resolution: {integrity: sha512-z9Y0K84BJvhChXgqn2CFYbfEi6AwEr+FFVVKm/MqbTQ2zIzO1VQri6w67LcfpVF0AjbhwVMywDZqY4alYkjW5w==} @@ -1499,12 +1420,6 @@ packages: embla-carousel@8.5.1: resolution: {integrity: sha512-JUb5+FOHobSiWQ2EJNaueCNT/cQU9L6XWBbWmorWPQT9bkbk+fhsuLr8wWrzXKagO3oWszBO7MSx+GfaRk4E6A==} - emoji-regex@8.0.0: - resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} - - emoji-regex@9.2.2: - resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} - es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} @@ -1513,8 +1428,8 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: @@ -1528,16 +1443,25 @@ packages: eventemitter3@4.0.7: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} - fast-equals@5.2.2: - resolution: {integrity: sha512-V7/RktU11J3I36Nwq2JnZEM7tNm17eBJz+u25qdxBZeCKiX6BkVSZQjwWIr+IobgnZy+ag73tTZgZi7tr0LrBw==} + fast-equals@5.4.0: + resolution: {integrity: sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==} engines: {node: '>=6.0.0'} fast-glob@3.3.3: resolution: {integrity: sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==} engines: {node: '>=8.6.0'} - fastq@1.19.1: - resolution: {integrity: sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==} + fastq@1.20.1: + resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} + + fdir@6.5.0: + resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} + engines: {node: '>=12.0.0'} + peerDependencies: + picomatch: ^3 || ^4 + peerDependenciesMeta: + picomatch: + optional: true fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} @@ -1552,16 +1476,12 @@ packages: debug: optional: true - foreground-child@3.3.1: - resolution: {integrity: sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==} - engines: {node: '>=14'} - form-data@4.0.5: resolution: {integrity: sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==} engines: {node: '>= 6'} - fraction.js@4.3.7: - resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==} + fraction.js@5.3.4: + resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} @@ -1591,11 +1511,6 @@ packages: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} - glob@10.5.0: - resolution: {integrity: sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==} - deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me - hasBin: true - gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} @@ -1608,19 +1523,19 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + 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'} - immer@10.1.1: - resolution: {integrity: sha512-s2MPrmjovJcoMaHtx6K11Ra7oD05NT97w1IC5zpMkT6Atjr7H8LjaDd81iIxUYpMKSRRNMJE703M1Fhr/TctHw==} + immer@11.1.8: + resolution: {integrity: sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA==} - immutable@5.1.5: - resolution: {integrity: sha512-t7xcm2siw+hlUM68I+UEOK+z84RzmN59as9DZ7P1l0994DKUWV7UXBMQZVxaoMSRQ+PBZbHCOoBt7a2wxOMt+A==} + immutable@5.1.6: + resolution: {integrity: sha512-q1swsS8K7L8usSHuOqF2TAoCCkonYz0SG38wLAggaa4Wml70zixIvt2ql4coQ2C2B3hTjltJry4r6bULwgAXLQ==} input-otp@1.4.1: resolution: {integrity: sha512-+yvpmKYKHi9jIGngxagY9oWiiblPB7+nEO75F2l2o4vs+6vpPZZmUl4tBNYuTCvQjhvEIbdNeJu70bhfYP2nbw==} @@ -1636,18 +1551,14 @@ packages: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} engines: {node: '>= 0.4'} is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} - is-fullwidth-code-point@3.0.0: - resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} - engines: {node: '>=8'} - is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} @@ -1656,19 +1567,6 @@ packages: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} - isarray@1.0.0: - resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} - - isexe@2.0.0: - resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} - - isobject@2.1.0: - resolution: {integrity: sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==} - engines: {node: '>=0.10.0'} - - jackspeak@3.4.3: - resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} - jiti@1.21.7: resolution: {integrity: sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==} hasBin: true @@ -1680,9 +1578,6 @@ packages: resolution: {integrity: sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==} engines: {node: '>=14'} - line-column@1.0.2: - resolution: {integrity: sha512-Ktrjk5noGYlHsVnYWh62FLVs4hTb8A3e+vucNZMgPeAOITdshMSgv4cCZQeRDjm7+goqmo6+liZwTXo+U3sVww==} - lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} @@ -1693,9 +1588,6 @@ packages: resolution: {integrity: sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==} hasBin: true - lru-cache@10.4.3: - resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} - lucide-react@0.454.0: resolution: {integrity: sha512-hw7zMDwykCLnEzgncEEjHeA6+45aeEzRYuKHuyRSOPkhko+J3ySGjGIzu+mmMfDFG1vazHepMaYFYHbTFAZAAQ==} peerDependencies: @@ -1721,27 +1613,19 @@ packages: resolution: {integrity: sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==} engines: {node: '>= 0.6'} - minimatch@9.0.9: - resolution: {integrity: sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==} - engines: {node: '>=16 || 14 >=14.17'} - - minipass@7.1.2: - resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==} - engines: {node: '>=16 || 14 >=14.17'} - 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.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true - next-themes@0.4.4: - resolution: {integrity: sha512-LDQ2qIOJF0VnuVrrMSMLrWGjRMkq+0mpgl6e0juCLqdJ+oo8Q84JRWT6Wh11VDQKkMMe+dVzDKLWs5n87T+PkQ==} + next-themes@0.4.6: + resolution: {integrity: sha512-pZvgD5L0IEvX5/9GWyHMf3m8BKiVQwsCMHfoFosXtXBMnaS0ZnIJ9ST4b4NqLVKDEm8QBxoNNGNaBv2JNF6XNA==} peerDependencies: react: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc react-dom: ^16.8 || ^17 || ^18 || ^19 || ^19.0.0-rc @@ -1770,17 +1654,14 @@ packages: node-addon-api@7.1.1: resolution: {integrity: sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==} - node-releases@2.0.19: - resolution: {integrity: sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==} + node-releases@2.0.47: + resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} + engines: {node: '>=18'} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} engines: {node: '>=0.10.0'} - normalize-range@0.1.2: - resolution: {integrity: sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==} - engines: {node: '>=0.10.0'} - object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} @@ -1789,20 +1670,9 @@ packages: resolution: {integrity: sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==} engines: {node: '>= 6'} - package-json-from-dist@1.0.1: - resolution: {integrity: sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==} - - path-key@3.1.1: - resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} - engines: {node: '>=8'} - path-parse@1.0.7: resolution: {integrity: sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==} - path-scurry@1.11.1: - resolution: {integrity: sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==} - engines: {node: '>=16 || 14 >=14.18'} - picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} @@ -1810,6 +1680,10 @@ packages: resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} + picomatch@4.0.4: + resolution: {integrity: sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==} + engines: {node: '>=12'} + pify@2.3.0: resolution: {integrity: sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==} engines: {node: '>=0.10.0'} @@ -1822,49 +1696,47 @@ packages: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} peerDependencies: - postcss: ^8.0.0 + postcss: '>=8.5.10' - postcss-js@4.0.1: - resolution: {integrity: sha512-dDLF8pEO191hJMtlHFPRa8xsizHaM82MLfNkUHdUtVEV3tgTp5oj+8qbEqYM57SLfc74KSbw//4SeJma2LRVIw==} + postcss-js@4.1.0: + resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: - postcss: ^8.4.21 + postcss: '>=8.5.10' - postcss-load-config@4.0.2: - resolution: {integrity: sha512-bSVhyJGL00wMVoPUzAVAnbEoWyqRxkjv64tUl427SKnPrENtq6hJwUojroMz2VB+Q1edmi4IfrAPpami5VVgMQ==} - engines: {node: '>= 14'} + postcss-load-config@6.0.1: + resolution: {integrity: sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==} + engines: {node: '>= 18'} peerDependencies: - postcss: '>=8.0.9' - ts-node: '>=9.0.0' + jiti: '>=1.21.0' + postcss: '>=8.5.10' + tsx: ^4.8.1 + yaml: '>=2.8.3' peerDependenciesMeta: + jiti: + optional: true postcss: optional: true - ts-node: + tsx: + optional: true + yaml: optional: true postcss-nested@6.2.0: resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} engines: {node: '>=12.0'} peerDependencies: - postcss: ^8.2.14 + postcss: '>=8.5.10' - postcss-selector-parser@6.1.2: - resolution: {integrity: sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==} + postcss-selector-parser@6.1.3: + resolution: {integrity: sha512-cDoO18VWCIWRsSaws3C23b0HXRxlUknttfdNcbXnf3IGHAPRNLlXPHc6dYiatOkxW0W4uZh524Plaaw5a7WEtQ==} engines: {node: '>=4'} postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.0.0: - resolution: {integrity: sha512-BriaW5AeZHfyuuKhK3Z6yRDKI6NR2TdRWyZcj3+Pk2nczQsMBqavggAzTledsbyexPthW3nFA6XfgCWjZqmVPA==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.5.3: - resolution: {integrity: sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==} + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} prop-types@15.8.1: @@ -1883,19 +1755,19 @@ packages: date-fns: ^2.28.0 || ^3.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom@19.0.0: - resolution: {integrity: sha512-4GV5sHFG0e/0AD4X+ySy6UJd3jVl1iNsNHdpad0qhABJ11twS3TTBnseqsKurKcsNqCEFeGL3uLpVChpIO3QfQ==} + react-dom@19.2.7: + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} peerDependencies: - react: ^19.0.0 + react: ^19.2.7 - react-hook-form@7.54.1: - resolution: {integrity: sha512-PUNzFwQeQ5oHiiTUO7GO/EJXGEtuun2Y1A59rLnZBBj+vNEOWt/3ERTiG1/zt7dVeJEM+4vDX/7XQ/qanuvPMg==} + react-hook-form@7.78.0: + resolution: {integrity: sha512-EEZqc+N23moyzTlz61Pj+JvcXo76ICkpfOZo8JZw+sM4+wLQGh6nI2Ms+PdMOYNluFu0ghlM7B8mCzhRYtJCnA==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 - react-icons@5.5.0: - resolution: {integrity: sha512-MEFcXdkP3dLo8uumGI5xN3lDFNsRtrjbOEKDLD7yv76v4wpnEq2Lt2qeHaQOr34I/wPN3s3+N08WkQ+CW37Xiw==} + react-icons@5.6.0: + resolution: {integrity: sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==} peerDependencies: react: '*' @@ -1905,8 +1777,8 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-redux@9.2.0: - resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + react-redux@9.3.0: + resolution: {integrity: sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==} peerDependencies: '@types/react': ^18.2.25 || ^19 react: ^18.0 || ^19 @@ -1927,8 +1799,8 @@ packages: '@types/react': optional: true - react-remove-scroll@2.6.3: - resolution: {integrity: sha512-pnAi91oOk8g8ABQKGF5/M9qxmmOPxaAnopyTHYfqYEwJhyFrbbBtHuSgtKEoH0jpcxx5o3hXqH1mNd9/Oi+8iQ==} + react-remove-scroll@2.7.2: + resolution: {integrity: sha512-Iqb9NjCCTt6Hf+vOdNIZGdTiH1QSqr27H/Ek9sv/a97gfueI/5h1s3yRi1nngzMUaOOToin5dI1dXKdXiF+u0Q==} engines: {node: '>=10'} peerDependencies: '@types/react': '*' @@ -1937,8 +1809,8 @@ packages: '@types/react': optional: true - react-resizable-panels@2.1.7: - resolution: {integrity: sha512-JtT6gI+nURzhMYQYsx8DKkx6bSoOGFp7A3CwMrOb8y5jFHFyqwo9m68UhmXRw57fRVJksFn1TSlm3ywEQ9vMgA==} + react-resizable-panels@2.1.9: + resolution: {integrity: sha512-z77+X08YDIrgAes4jl8xhnUu1LNIRp4+E7cv4xHmLOxxUPO/ML7PSrE813b90vj7xvQ1lcf7g2uA9GeMZonjhQ==} peerDependencies: react: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^16.14.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -1965,8 +1837,8 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' - react@19.0.0: - resolution: {integrity: sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ==} + react@19.2.7: + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} read-cache@1.0.0: @@ -1976,9 +1848,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} - readdirp@4.1.2: - resolution: {integrity: sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==} - engines: {node: '>= 14.18.0'} + readdirp@5.0.0: + resolution: {integrity: sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==} + engines: {node: '>= 20.19.0'} recharts-scale@0.4.5: resolution: {integrity: sha512-kivNFO+0OcUNu7jQquLXAxz1FIwZj8nrj+YkOKc5694NbjCvcT6aSZiIzNzd2Kul4o4rTto8QVR9lMNtxD4G1w==} @@ -1999,14 +1871,11 @@ packages: redux@5.0.1: resolution: {integrity: sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==} - regenerator-runtime@0.14.1: - resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + reselect@5.2.0: + resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==} - reselect@5.1.1: - resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} - - resolve@1.22.10: - resolution: {integrity: sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==} + resolve@1.22.12: + resolution: {integrity: sha512-TyeJ1zif53BPfHootBGwPRYT1RUt6oGWsaQr8UyZW/eAm9bKoijtvruSDEmZHm92CwS9nj7/fWttqPCgzep8CA==} engines: {node: '>= 0.4'} hasBin: true @@ -2017,16 +1886,16 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - sass@1.86.3: - resolution: {integrity: sha512-iGtg8kus4GrsGLRDLRBRHY9dNVA78ZaS7xr01cWnS7PEMQyFtTqBiyCrfpTYTZXRWM94akzckYjh8oADfFNTzw==} - engines: {node: '>=14.0.0'} + sass@1.100.0: + resolution: {integrity: sha512-B5j0rYMlinhhOo9tjQebMVVn0TfyXAF+wB3b2ggZUuJ/is/Y+7+JGjirAMxHZ9Z3hIP98NPfamlAkBHa1lAaXQ==} + engines: {node: '>=20.19.0'} hasBin: true - scheduler@0.25.0: - resolution: {integrity: sha512-xFVuu11jh+xcO7JOAGJNOXld8/TcEHK/4CituBUeUb5hqxJLj9YuemAEuvm9gQ/+pgXYfbQuqAkiYu+u7YEsNA==} + scheduler@0.27.0: + resolution: {integrity: sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==} - semver@7.8.0: - resolution: {integrity: sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA==} + semver@7.8.4: + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} engines: {node: '>=10'} hasBin: true @@ -2034,20 +1903,8 @@ packages: resolution: {integrity: sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} - shebang-command@2.0.0: - resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} - engines: {node: '>=8'} - - shebang-regex@3.0.0: - resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} - engines: {node: '>=8'} - - signal-exit@4.1.0: - resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} - engines: {node: '>=14'} - - sonner@1.7.1: - resolution: {integrity: sha512-b6LHBfH32SoVasRFECrdY8p8s7hXPDn3OHUFbZZbiB1ctLS9Gdh6rpX2dVrpQA0kiL5jcRzDDldwwLkSKk3+QQ==} + sonner@1.7.4: + resolution: {integrity: sha512-DIS8z4PfJRbIyfVFDVnK9rO3eYDtse4Omcm6bt0oEr5/jtLgysmjuBl1frJ9E/EQZrFmKx2A8m/s5s9CRXIzhw==} peerDependencies: react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc @@ -2056,26 +1913,6 @@ packages: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} - source-map@0.6.1: - resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} - engines: {node: '>=0.10.0'} - - string-width@4.2.3: - resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} - engines: {node: '>=8'} - - string-width@5.1.2: - resolution: {integrity: sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==} - engines: {node: '>=12'} - - strip-ansi@6.0.1: - resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} - engines: {node: '>=8'} - - strip-ansi@7.1.0: - resolution: {integrity: sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==} - engines: {node: '>=12'} - styled-jsx@5.1.6: resolution: {integrity: sha512-qSVyDTeMotdvQYoHWLNGwRFJHC+i+ZvdBRYosOFgC+Wg1vx4frN2/RG/NA7SYqqvKNLf39P2LSRA2pu6n0XYZA==} engines: {node: '>= 12.0.0'} @@ -2089,8 +1926,8 @@ packages: babel-plugin-macros: optional: true - sucrase@3.35.0: - resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} + sucrase@3.35.1: + resolution: {integrity: sha512-DhuTmvZWux4H1UOnWMB3sk0sbaCVOoQZjv8u1rDoTV0HTdGem9hkAZtl4JZy8P2z4Bg0nT+YMeOFyVr4zcG5Tw==} engines: {node: '>=16 || 14 >=14.17'} hasBin: true @@ -2098,16 +1935,16 @@ packages: resolution: {integrity: sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==} engines: {node: '>= 0.4'} - tailwind-merge@2.5.5: - resolution: {integrity: sha512-0LXunzzAZzo0tEPxV3I297ffKZPlKDrjj7NXphC8V5ak9yHC5zRmxnOe2m/Rd/7ivsOMJe3JZ2JVocoDdQTRBA==} + tailwind-merge@2.6.1: + resolution: {integrity: sha512-Oo6tHdpZsGpkKG88HJ8RR1rg/RdnEkQEfMoEk2x1XRI3F1AxeU+ijRXpiVUF4UbLfcxxRGw6TbUINKYdWVsQTQ==} tailwindcss-animate@1.0.7: resolution: {integrity: sha512-bl6mpH3T7I3UFxuvDEXLxy/VuFxBk5bbzplh7tXI68mwMokNYd1t9qPBHlnyTwfa4JGC4zP516I1hYYtQ/vspA==} peerDependencies: tailwindcss: '>=3.0.0 || insiders' - tailwindcss@3.4.17: - resolution: {integrity: sha512-w33E2aCvSDP0tW9RZuNXadXlkHXqFzSkQew/aIa2i/Sj8fThxwovwlXHSPXTbAHwEIhBFXAedUhP2tueAKP8Og==} + tailwindcss@3.4.19: + resolution: {integrity: sha512-3ofp+LL8E+pK/JuPLPggVAIaEuhvIz4qNcf3nA1Xn2o/7fb7s/TYpHhwGDv1ZU3PkBluUVaF8PyCHcm48cKLWQ==} engines: {node: '>=14.0.0'} hasBin: true @@ -2121,6 +1958,10 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} + engines: {node: '>=12.0.0'} + to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} @@ -2131,16 +1972,16 @@ packages: tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} - typescript@5.0.2: - resolution: {integrity: sha512-wVORMBGO/FAs/++blGNeAVdbNKtIh1rbBL2EyQ1+J9lClJ93KiiKe8PmFIVdXhHcyv44SL9oglmfeSsndo0jRw==} - engines: {node: '>=12.20'} + typescript@5.9.3: + resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==} + engines: {node: '>=14.17'} hasBin: true - undici-types@6.11.1: - resolution: {integrity: sha512-mIDEX2ek50x0OlRgxryxsenE5XaQD4on5U2inY7RApK3SOJpofyw7uW2AyfMKkhAxXIceo2DeWGVGwyvng1GNQ==} + undici-types@6.21.0: + resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==} - update-browserslist-db@1.1.3: - resolution: {integrity: sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==} + update-browserslist-db@1.2.3: + resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' @@ -2165,16 +2006,16 @@ packages: '@types/react': optional: true - use-sync-external-store@1.5.0: - resolution: {integrity: sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==} + use-sync-external-store@1.6.0: + resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==} peerDependencies: react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - vaul@0.9.6: - resolution: {integrity: sha512-Ykk5FSu4ibeD6qfKQH/CkBRdSGWkxi35KMNei0z59kTPAlgzpE/Qf1gTx2sxih8Q05KBO/aFhcF/UkBW5iI1Ww==} + vaul@0.9.9: + resolution: {integrity: sha512-7afKg48srluhZwIkaU+lgGtFCUsYBSGOl8vcc8N/M3YQlZFlynHD15AE+pwrYdc826o7nrIND4lL9Y6b9WWZZQ==} peerDependencies: react: ^16.8 || ^17.0 || ^18.0 react-dom: ^16.8 || ^17.0 || ^18.0 @@ -2182,60 +2023,40 @@ packages: victory-vendor@36.9.2: resolution: {integrity: sha512-PnpQQMuxlwYdocC8fIJqVXvkeViHYzotI+NJrCuav0ZYFoq912ZHBk3mCeuj+5/VpodOjPe1z0Fk2ihgzlXqjQ==} - which@2.0.2: - resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} - engines: {node: '>= 8'} - hasBin: true - - wrap-ansi@7.0.0: - resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} - engines: {node: '>=10'} - - wrap-ansi@8.1.0: - resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==} - engines: {node: '>=12'} - - yaml@2.7.1: - resolution: {integrity: sha512-10ULxpnOCQXxJvBgxsn9ptjq6uviG/htZKk9veJGhlqn3w/DxQ631zFF+nlQXLwmImeS5amR2dl2U8sg6U9jsQ==} - engines: {node: '>= 14'} - hasBin: true - - zod@3.24.1: - resolution: {integrity: sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==} + zod@3.25.76: + resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} snapshots: '@alloc/quick-lru@5.2.0': {} - '@babel/runtime@7.27.0': - dependencies: - regenerator-runtime: 0.14.1 + '@babel/runtime@7.29.7': {} - '@emnapi/runtime@1.10.0': + '@emnapi/runtime@1.11.0': dependencies: tslib: 2.8.1 optional: true - '@floating-ui/core@1.6.9': + '@floating-ui/core@1.7.5': dependencies: - '@floating-ui/utils': 0.2.9 + '@floating-ui/utils': 0.2.11 - '@floating-ui/dom@1.6.13': + '@floating-ui/dom@1.7.6': dependencies: - '@floating-ui/core': 1.6.9 - '@floating-ui/utils': 0.2.9 + '@floating-ui/core': 1.7.5 + '@floating-ui/utils': 0.2.11 - '@floating-ui/react-dom@2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@floating-ui/dom': 1.6.13 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@floating-ui/dom': 1.7.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) - '@floating-ui/utils@0.2.9': {} + '@floating-ui/utils@0.2.11': {} - '@hookform/resolvers@3.9.1(react-hook-form@7.54.1(react@19.0.0))': + '@hookform/resolvers@3.10.0(react-hook-form@7.78.0(react@19.2.7))': dependencies: - react-hook-form: 7.54.1(react@19.0.0) + react-hook-form: 7.78.0(react@19.2.7) '@img/colour@1.1.0': optional: true @@ -2322,7 +2143,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.10.0 + '@emnapi/runtime': 1.11.0 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -2334,31 +2155,19 @@ snapshots: '@img/sharp-win32-x64@0.34.5': optional: true - '@isaacs/cliui@8.0.2': + '@jridgewell/gen-mapping@0.3.13': dependencies: - string-width: 5.1.2 - string-width-cjs: string-width@4.2.3 - strip-ansi: 7.1.0 - strip-ansi-cjs: strip-ansi@6.0.1 - wrap-ansi: 8.1.0 - wrap-ansi-cjs: wrap-ansi@7.0.0 - - '@jridgewell/gen-mapping@0.3.8': - dependencies: - '@jridgewell/set-array': 1.2.1 - '@jridgewell/sourcemap-codec': 1.5.0 - '@jridgewell/trace-mapping': 0.3.25 + '@jridgewell/sourcemap-codec': 1.5.5 + '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} - '@jridgewell/set-array@1.2.1': {} + '@jridgewell/sourcemap-codec@1.5.5': {} - '@jridgewell/sourcemap-codec@1.5.0': {} - - '@jridgewell/trace-mapping@0.3.25': + '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 - '@jridgewell/sourcemap-codec': 1.5.0 + '@jridgewell/sourcemap-codec': 1.5.5 '@next/env@15.5.18': {} @@ -2396,777 +2205,753 @@ snapshots: '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 - fastq: 1.19.1 + fastq: 1.20.1 - '@parcel/watcher-android-arm64@2.5.1': + '@parcel/watcher-android-arm64@2.5.6': optional: true - '@parcel/watcher-darwin-arm64@2.5.1': + '@parcel/watcher-darwin-arm64@2.5.6': optional: true - '@parcel/watcher-darwin-x64@2.5.1': + '@parcel/watcher-darwin-x64@2.5.6': optional: true - '@parcel/watcher-freebsd-x64@2.5.1': + '@parcel/watcher-freebsd-x64@2.5.6': optional: true - '@parcel/watcher-linux-arm-glibc@2.5.1': + '@parcel/watcher-linux-arm-glibc@2.5.6': optional: true - '@parcel/watcher-linux-arm-musl@2.5.1': + '@parcel/watcher-linux-arm-musl@2.5.6': optional: true - '@parcel/watcher-linux-arm64-glibc@2.5.1': + '@parcel/watcher-linux-arm64-glibc@2.5.6': optional: true - '@parcel/watcher-linux-arm64-musl@2.5.1': + '@parcel/watcher-linux-arm64-musl@2.5.6': optional: true - '@parcel/watcher-linux-x64-glibc@2.5.1': + '@parcel/watcher-linux-x64-glibc@2.5.6': optional: true - '@parcel/watcher-linux-x64-musl@2.5.1': + '@parcel/watcher-linux-x64-musl@2.5.6': optional: true - '@parcel/watcher-win32-arm64@2.5.1': + '@parcel/watcher-win32-arm64@2.5.6': optional: true - '@parcel/watcher-win32-ia32@2.5.1': + '@parcel/watcher-win32-ia32@2.5.6': optional: true - '@parcel/watcher-win32-x64@2.5.1': + '@parcel/watcher-win32-x64@2.5.6': optional: true - '@parcel/watcher@2.5.1': + '@parcel/watcher@2.5.6': dependencies: - detect-libc: 1.0.3 + detect-libc: 2.1.2 is-glob: 4.0.3 - micromatch: 4.0.8 node-addon-api: 7.1.1 + picomatch: 4.0.4 optionalDependencies: - '@parcel/watcher-android-arm64': 2.5.1 - '@parcel/watcher-darwin-arm64': 2.5.1 - '@parcel/watcher-darwin-x64': 2.5.1 - '@parcel/watcher-freebsd-x64': 2.5.1 - '@parcel/watcher-linux-arm-glibc': 2.5.1 - '@parcel/watcher-linux-arm-musl': 2.5.1 - '@parcel/watcher-linux-arm64-glibc': 2.5.1 - '@parcel/watcher-linux-arm64-musl': 2.5.1 - '@parcel/watcher-linux-x64-glibc': 2.5.1 - '@parcel/watcher-linux-x64-musl': 2.5.1 - '@parcel/watcher-win32-arm64': 2.5.1 - '@parcel/watcher-win32-ia32': 2.5.1 - '@parcel/watcher-win32-x64': 2.5.1 + '@parcel/watcher-android-arm64': 2.5.6 + '@parcel/watcher-darwin-arm64': 2.5.6 + '@parcel/watcher-darwin-x64': 2.5.6 + '@parcel/watcher-freebsd-x64': 2.5.6 + '@parcel/watcher-linux-arm-glibc': 2.5.6 + '@parcel/watcher-linux-arm-musl': 2.5.6 + '@parcel/watcher-linux-arm64-glibc': 2.5.6 + '@parcel/watcher-linux-arm64-musl': 2.5.6 + '@parcel/watcher-linux-x64-glibc': 2.5.6 + '@parcel/watcher-linux-x64-musl': 2.5.6 + '@parcel/watcher-win32-arm64': 2.5.6 + '@parcel/watcher-win32-ia32': 2.5.6 + '@parcel/watcher-win32-x64': 2.5.6 optional: true - '@pkgjs/parseargs@0.11.0': - optional: true + '@radix-ui/number@1.1.2': {} - '@radix-ui/number@1.1.0': {} + '@radix-ui/primitive@1.1.4': {} - '@radix-ui/primitive@1.1.1': {} - - '@radix-ui/react-accordion@1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-accordion@1.2.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collapsible': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collapsible': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-alert-dialog@1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-alert-dialog@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dialog': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-arrow@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-arrow@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-aspect-ratio@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-aspect-ratio@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-avatar@1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-avatar@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-checkbox@1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-checkbox@1.3.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collapsible@1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-collapsible@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collection@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-collection@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-compose-refs@1.1.1(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.0.0 + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-context-menu@2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - react: 19.0.0 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-context-menu@2.2.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-menu': 2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-context@1.1.1(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-dialog@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - react: 19.0.0 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-dialog@1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - aria-hidden: 1.2.4 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.3(@types/react@19.0.0)(react@19.0.0) + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-direction@1.1.0(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-dismissable-layer@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - react: 19.0.0 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-dismissable-layer@1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-dropdown-menu@2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-escape-keydown': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-dropdown-menu@2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-menu': 2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-focus-guards@1.1.1(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-focus-scope@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - react: 19.0.0 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-focus-scope@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-hover-card@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-hover-card@1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-id@1.1.0(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-label@2.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-id@1.1.1(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-menu@2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-label@2.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-menubar@1.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-menu@2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-navigation-menu@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - aria-hidden: 1.2.4 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.3(@types/react@19.0.0)(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-menubar@1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-popover@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-menu': 2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-navigation-menu@1.2.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-popper@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-popover@1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-portal@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - aria-hidden: 1.2.4 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.3(@types/react@19.0.0)(react@19.0.0) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-popper@1.2.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@floating-ui/react-dom': 2.1.2(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-arrow': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-rect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/rect': 1.1.0 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-portal@1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-primitive@2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-presence@1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-progress@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-primitive@2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-radio-group@1.4.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-primitive@2.0.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-roving-focus@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-slot': 1.2.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-progress@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-scroll-area@1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-radio-group@1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-select@2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + aria-hidden: 1.2.6 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-roving-focus@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-separator@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-scroll-area@1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-slider@1.4.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/number': 1.1.0 - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-select@2.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-slot@1.2.5(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/number': 1.1.0 - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-focus-guards': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-focus-scope': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - aria-hidden: 1.2.4 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-remove-scroll: 2.6.3(@types/react@19.0.0)(react@19.0.0) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-separator@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-switch@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-slider@1.2.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-tabs@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/number': 1.1.0 - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-slot@1.1.1(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-toast@1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-slot@1.2.0(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-toggle-group@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-toggle': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-switch@1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-toggle@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-previous': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-size': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-tabs@1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-tooltip@1.2.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toast@1.2.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-collection': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-toggle-group@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-direction': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-roving-focus': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-toggle': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-toggle@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-tooltip@1.1.6(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': + '@radix-ui/react-use-escape-keydown@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.1 - '@radix-ui/react-compose-refs': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-context': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-dismissable-layer': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-popper': 1.2.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-portal': 1.1.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-presence': 1.1.2(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-slot': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-use-controllable-state': 1.1.0(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-visually-hidden': 1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-use-callback-ref@1.1.0(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-use-is-hydrated@0.1.1(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.0.0 + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-use-controllable-state@1.1.0(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-use-escape-keydown@1.1.0(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-use-previous@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-use-layout-effect@1.1.0(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.0.0 + '@radix-ui/rect': 1.1.2 + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.0.0 + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 - '@radix-ui/react-use-previous@1.1.0(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/react-visually-hidden@1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - react: 19.0.0 + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-use-rect@1.1.0(@types/react@19.0.0)(react@19.0.0)': + '@radix-ui/rect@1.1.2': {} + + '@reduxjs/toolkit@2.12.0(react-redux@9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1))(react@19.2.7)': dependencies: - '@radix-ui/rect': 1.1.0 - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.0 - - '@radix-ui/react-use-size@1.1.0(@types/react@19.0.0)(react@19.0.0)': - dependencies: - '@radix-ui/react-use-layout-effect': 1.1.0(@types/react@19.0.0)(react@19.0.0) - react: 19.0.0 - optionalDependencies: - '@types/react': 19.0.0 - - '@radix-ui/react-visually-hidden@1.1.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)': - dependencies: - '@radix-ui/react-primitive': 2.0.1(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - optionalDependencies: - '@types/react': 19.0.0 - '@types/react-dom': 19.0.0 - - '@radix-ui/rect@1.1.0': {} - - '@reduxjs/toolkit@2.7.0(react-redux@9.2.0(@types/react@19.0.0)(react@19.0.0)(redux@5.0.1))(react@19.0.0)': - dependencies: - '@standard-schema/spec': 1.0.0 + '@standard-schema/spec': 1.1.0 '@standard-schema/utils': 0.3.0 - immer: 10.1.1 + immer: 11.1.8 redux: 5.0.1 redux-thunk: 3.1.0(redux@5.0.1) - reselect: 5.1.1 + reselect: 5.2.0 optionalDependencies: - react: 19.0.0 - react-redux: 9.2.0(@types/react@19.0.0)(react@19.0.0)(redux@5.0.1) + react: 19.2.7 + react-redux: 9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1) - '@standard-schema/spec@1.0.0': {} + '@standard-schema/spec@1.1.0': {} '@standard-schema/utils@0.3.0': {} @@ -3174,7 +2959,7 @@ snapshots: dependencies: tslib: 2.8.1 - '@types/d3-array@3.2.1': {} + '@types/d3-array@3.2.2': {} '@types/d3-color@3.1.3': {} @@ -3190,7 +2975,7 @@ snapshots: dependencies: '@types/d3-time': 3.0.4 - '@types/d3-shape@3.1.7': + '@types/d3-shape@3.1.8': dependencies: '@types/d3-path': 3.1.1 @@ -3198,19 +2983,19 @@ snapshots: '@types/d3-timer@3.0.2': {} - '@types/lodash@4.17.16': {} + '@types/lodash@4.17.24': {} - '@types/node@22.0.0': + '@types/node@22.19.21': dependencies: - undici-types: 6.11.1 + undici-types: 6.21.0 - '@types/react-dom@19.0.0': + '@types/react-dom@19.2.3(@types/react@19.2.17)': dependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 - '@types/react@19.0.0': + '@types/react@19.2.17': dependencies: - csstype: 3.1.3 + csstype: 3.2.3 '@types/use-sync-external-store@0.0.6': {} @@ -3220,16 +3005,6 @@ snapshots: transitivePeerDependencies: - supports-color - ansi-regex@5.0.1: {} - - ansi-regex@6.1.0: {} - - ansi-styles@4.3.0: - dependencies: - color-convert: 2.0.1 - - ansi-styles@6.2.1: {} - any-promise@1.3.0: {} anymatch@3.1.3: @@ -3239,23 +3014,22 @@ snapshots: arg@5.0.2: {} - aria-hidden@1.2.4: + aria-hidden@1.2.6: dependencies: tslib: 2.8.1 asynckit@0.4.0: {} - autoprefixer@10.4.20(postcss@8.0.0): + autoprefixer@10.5.0(postcss@8.5.15): dependencies: - browserslist: 4.24.4 - caniuse-lite: 1.0.30001714 - fraction.js: 4.3.7 - normalize-range: 0.1.2 + browserslist: 4.28.2 + caniuse-lite: 1.0.30001799 + fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.0.0 + postcss: 8.5.15 postcss-value-parser: 4.2.0 - axios@1.16.1: + axios@1.17.0: dependencies: follow-redirects: 1.16.0 form-data: 4.0.5 @@ -3265,24 +3039,21 @@ snapshots: - debug - supports-color - balanced-match@1.0.2: {} + baseline-browser-mapping@2.10.35: {} binary-extensions@2.3.0: {} - brace-expansion@2.1.1: - dependencies: - balanced-match: 1.0.2 - braces@3.0.3: dependencies: fill-range: 7.1.1 - browserslist@4.24.4: + browserslist@4.28.2: dependencies: - caniuse-lite: 1.0.30001714 - electron-to-chromium: 1.5.137 - node-releases: 2.0.19 - update-browserslist-db: 1.1.3(browserslist@4.24.4) + baseline-browser-mapping: 2.10.35 + caniuse-lite: 1.0.30001799 + electron-to-chromium: 1.5.371 + node-releases: 2.0.47 + update-browserslist-db: 1.2.3(browserslist@4.28.2) call-bind-apply-helpers@1.0.2: dependencies: @@ -3291,7 +3062,7 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001714: {} + caniuse-lite@1.0.30001799: {} chokidar@3.6.0: dependencies: @@ -3305,9 +3076,9 @@ snapshots: optionalDependencies: fsevents: 2.3.3 - chokidar@4.0.3: + chokidar@5.0.0: dependencies: - readdirp: 4.1.2 + readdirp: 5.0.0 class-variance-authority@0.7.1: dependencies: @@ -3317,41 +3088,27 @@ snapshots: clsx@2.1.1: {} - cmdk@1.0.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + cmdk@1.0.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - '@radix-ui/react-id': 1.1.1(@types/react@19.0.0)(react@19.0.0) - '@radix-ui/react-primitive': 2.0.3(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - use-sync-external-store: 1.5.0(react@19.0.0) + '@radix-ui/react-dialog': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + use-sync-external-store: 1.6.0(react@19.2.7) transitivePeerDependencies: - '@types/react' - '@types/react-dom' - color-convert@2.0.1: - dependencies: - color-name: 1.1.4 - - color-name@1.1.4: {} - - colorette@1.4.0: {} - combined-stream@1.0.8: dependencies: delayed-stream: 1.0.0 commander@4.1.1: {} - cross-spawn@7.0.6: - dependencies: - path-key: 3.1.1 - shebang-command: 2.0.0 - which: 2.0.2 - cssesc@3.0.0: {} - csstype@3.1.3: {} + csstype@3.2.3: {} d3-array@3.2.4: dependencies: @@ -3361,7 +3118,7 @@ snapshots: d3-ease@3.0.1: {} - d3-format@3.1.0: {} + d3-format@3.1.2: {} d3-interpolate@3.0.1: dependencies: @@ -3372,7 +3129,7 @@ snapshots: d3-scale@4.0.2: dependencies: d3-array: 3.2.4 - d3-format: 3.1.0 + d3-format: 3.1.2 d3-interpolate: 3.0.1 d3-time: 3.1.0 d3-time-format: 4.1.0 @@ -3401,9 +3158,6 @@ snapshots: delayed-stream@1.0.0: {} - detect-libc@1.0.3: - optional: true - detect-libc@2.1.2: optional: true @@ -3415,8 +3169,8 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.27.0 - csstype: 3.1.3 + '@babel/runtime': 7.29.7 + csstype: 3.2.3 dunder-proto@1.0.1: dependencies: @@ -3424,15 +3178,13 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - eastasianwidth@0.2.0: {} + electron-to-chromium@1.5.371: {} - electron-to-chromium@1.5.137: {} - - embla-carousel-react@8.5.1(react@19.0.0): + embla-carousel-react@8.5.1(react@19.2.7): dependencies: embla-carousel: 8.5.1 embla-carousel-reactive-utils: 8.5.1(embla-carousel@8.5.1) - react: 19.0.0 + react: 19.2.7 embla-carousel-reactive-utils@8.5.1(embla-carousel@8.5.1): dependencies: @@ -3440,15 +3192,11 @@ snapshots: embla-carousel@8.5.1: {} - emoji-regex@8.0.0: {} - - emoji-regex@9.2.2: {} - es-define-property@1.0.1: {} es-errors@1.3.0: {} - es-object-atoms@1.1.1: + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 @@ -3457,13 +3205,13 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 escalade@3.2.0: {} eventemitter3@4.0.7: {} - fast-equals@5.2.2: {} + fast-equals@5.4.0: {} fast-glob@3.3.3: dependencies: @@ -3473,30 +3221,29 @@ snapshots: merge2: 1.4.1 micromatch: 4.0.8 - fastq@1.19.1: + fastq@1.20.1: dependencies: reusify: 1.1.0 + fdir@6.5.0(picomatch@4.0.4): + optionalDependencies: + picomatch: 4.0.4 + fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 follow-redirects@1.16.0: {} - foreground-child@3.3.1: - dependencies: - cross-spawn: 7.0.6 - signal-exit: 4.1.0 - form-data@4.0.5: dependencies: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 - hasown: 2.0.2 + hasown: 2.0.4 mime-types: 2.1.35 - fraction.js@4.3.7: {} + fraction.js@5.3.4: {} fsevents@2.3.3: optional: true @@ -3508,12 +3255,12 @@ snapshots: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + 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.2 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-nonce@1.0.1: {} @@ -3521,7 +3268,7 @@ snapshots: get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 glob-parent@5.1.2: dependencies: @@ -3531,15 +3278,6 @@ snapshots: dependencies: is-glob: 4.0.3 - glob@10.5.0: - dependencies: - foreground-child: 3.3.1 - jackspeak: 3.4.3 - minimatch: 9.0.9 - minipass: 7.1.2 - package-json-from-dist: 1.0.1 - path-scurry: 1.11.1 - gopd@1.2.0: {} has-symbols@1.1.0: {} @@ -3548,7 +3286,7 @@ snapshots: dependencies: has-symbols: 1.1.0 - hasown@2.0.2: + hasown@2.0.4: dependencies: function-bind: 1.1.2 @@ -3559,14 +3297,14 @@ snapshots: transitivePeerDependencies: - supports-color - immer@10.1.1: {} + immer@11.1.8: {} - immutable@5.1.5: {} + immutable@5.1.6: {} - input-otp@1.4.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + input-otp@1.4.1(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) internmap@2.0.3: {} @@ -3574,45 +3312,24 @@ snapshots: dependencies: binary-extensions: 2.3.0 - is-core-module@2.16.1: + is-core-module@2.16.2: dependencies: - hasown: 2.0.2 + hasown: 2.0.4 is-extglob@2.1.1: {} - is-fullwidth-code-point@3.0.0: {} - is-glob@4.0.3: dependencies: is-extglob: 2.1.1 is-number@7.0.0: {} - isarray@1.0.0: {} - - isexe@2.0.0: {} - - isobject@2.1.0: - dependencies: - isarray: 1.0.0 - - jackspeak@3.4.3: - dependencies: - '@isaacs/cliui': 8.0.2 - optionalDependencies: - '@pkgjs/parseargs': 0.11.0 - jiti@1.21.7: {} js-tokens@4.0.0: {} lilconfig@3.1.3: {} - line-column@1.0.2: - dependencies: - isarray: 1.0.0 - isobject: 2.1.0 - lines-and-columns@1.2.4: {} lodash@4.18.1: {} @@ -3621,11 +3338,9 @@ snapshots: dependencies: js-tokens: 4.0.0 - lru-cache@10.4.3: {} - - lucide-react@0.454.0(react@19.0.0): + lucide-react@0.454.0(react@19.2.7): dependencies: - react: 19.0.0 + react: 19.2.7 math-intrinsics@1.1.0: {} @@ -3642,12 +3357,6 @@ snapshots: dependencies: mime-db: 1.52.0 - minimatch@9.0.9: - dependencies: - brace-expansion: 2.1.1 - - minipass@7.1.2: {} - ms@2.1.3: {} mz@2.7.0: @@ -3656,22 +3365,22 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.11: {} + nanoid@3.3.12: {} - next-themes@0.4.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + next-themes@0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) - next@15.5.18(react-dom@19.0.0(react@19.0.0))(react@19.0.0)(sass@1.86.3): + next@15.5.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7)(sass@1.100.0): dependencies: '@next/env': 15.5.18 '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001714 - postcss: 8.4.31 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - styled-jsx: 5.1.6(react@19.0.0) + caniuse-lite: 1.0.30001799 + postcss: 8.5.15 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + styled-jsx: 5.1.6(react@19.2.7) optionalDependencies: '@next/swc-darwin-arm64': 15.5.18 '@next/swc-darwin-x64': 15.5.18 @@ -3681,7 +3390,7 @@ snapshots: '@next/swc-linux-x64-musl': 15.5.18 '@next/swc-win32-arm64-msvc': 15.5.18 '@next/swc-win32-x64-msvc': 15.5.18 - sass: 1.86.3 + sass: 1.100.0 sharp: 0.34.5 transitivePeerDependencies: - '@babel/core' @@ -3690,82 +3399,60 @@ snapshots: node-addon-api@7.1.1: optional: true - node-releases@2.0.19: {} + node-releases@2.0.47: {} normalize-path@3.0.0: {} - normalize-range@0.1.2: {} - object-assign@4.1.1: {} object-hash@3.0.0: {} - package-json-from-dist@1.0.1: {} - - path-key@3.1.1: {} - path-parse@1.0.7: {} - path-scurry@1.11.1: - dependencies: - lru-cache: 10.4.3 - minipass: 7.1.2 - picocolors@1.1.1: {} picomatch@2.3.2: {} + picomatch@4.0.4: {} + pify@2.3.0: {} pirates@4.0.7: {} - postcss-import@15.1.0(postcss@8.5.3): + postcss-import@15.1.0(postcss@8.5.15): dependencies: - postcss: 8.5.3 + postcss: 8.5.15 postcss-value-parser: 4.2.0 read-cache: 1.0.0 - resolve: 1.22.10 + resolve: 1.22.12 - postcss-js@4.0.1(postcss@8.5.3): + postcss-js@4.1.0(postcss@8.5.15): dependencies: camelcase-css: 2.0.1 - postcss: 8.5.3 + postcss: 8.5.15 - postcss-load-config@4.0.2(postcss@8.5.3): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.15): dependencies: lilconfig: 3.1.3 - yaml: 2.7.1 optionalDependencies: - postcss: 8.5.3 + jiti: 1.21.7 + postcss: 8.5.15 - postcss-nested@6.2.0(postcss@8.5.3): + postcss-nested@6.2.0(postcss@8.5.15): dependencies: - postcss: 8.5.3 - postcss-selector-parser: 6.1.2 + postcss: 8.5.15 + postcss-selector-parser: 6.1.3 - postcss-selector-parser@6.1.2: + postcss-selector-parser@6.1.3: dependencies: cssesc: 3.0.0 util-deprecate: 1.0.2 postcss-value-parser@4.2.0: {} - postcss@8.0.0: + postcss@8.5.15: dependencies: - colorette: 1.4.0 - line-column: 1.0.2 - nanoid: 3.3.11 - source-map: 0.6.1 - - postcss@8.4.31: - dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postcss@8.5.3: - dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 @@ -3779,87 +3466,87 @@ snapshots: queue-microtask@1.2.3: {} - react-day-picker@8.10.1(date-fns@4.1.0)(react@19.0.0): + react-day-picker@8.10.1(date-fns@4.1.0)(react@19.2.7): dependencies: date-fns: 4.1.0 - react: 19.0.0 + react: 19.2.7 - react-dom@19.0.0(react@19.0.0): + react-dom@19.2.7(react@19.2.7): dependencies: - react: 19.0.0 - scheduler: 0.25.0 + react: 19.2.7 + scheduler: 0.27.0 - react-hook-form@7.54.1(react@19.0.0): + react-hook-form@7.78.0(react@19.2.7): dependencies: - react: 19.0.0 + react: 19.2.7 - react-icons@5.5.0(react@19.0.0): + react-icons@5.6.0(react@19.2.7): dependencies: - react: 19.0.0 + react: 19.2.7 react-is@16.13.1: {} react-is@18.3.1: {} - react-redux@9.2.0(@types/react@19.0.0)(react@19.0.0)(redux@5.0.1): + react-redux@9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1): dependencies: '@types/use-sync-external-store': 0.0.6 - react: 19.0.0 - use-sync-external-store: 1.5.0(react@19.0.0) + react: 19.2.7 + use-sync-external-store: 1.6.0(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 redux: 5.0.1 - react-remove-scroll-bar@2.3.8(@types/react@19.0.0)(react@19.0.0): + react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.7): dependencies: - react: 19.0.0 - react-style-singleton: 2.2.3(@types/react@19.0.0)(react@19.0.0) + react: 19.2.7 + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 - react-remove-scroll@2.6.3(@types/react@19.0.0)(react@19.0.0): + react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.7): dependencies: - react: 19.0.0 - react-remove-scroll-bar: 2.3.8(@types/react@19.0.0)(react@19.0.0) - react-style-singleton: 2.2.3(@types/react@19.0.0)(react@19.0.0) + react: 19.2.7 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.7) + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.0.0)(react@19.0.0) - use-sidecar: 1.1.3(@types/react@19.0.0)(react@19.0.0) + use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.7) + use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 - react-resizable-panels@2.1.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + react-resizable-panels@2.1.9(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) - react-smooth@4.0.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + react-smooth@4.0.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - fast-equals: 5.2.2 + fast-equals: 5.4.0 prop-types: 15.8.1 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) - react-transition-group: 4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-transition-group: 4.4.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react-style-singleton@2.2.3(@types/react@19.0.0)(react@19.0.0): + react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.7): dependencies: get-nonce: 1.0.1 - react: 19.0.0 + react: 19.2.7 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 - react-transition-group@4.4.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + react-transition-group@4.4.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@babel/runtime': 7.27.0 + '@babel/runtime': 7.29.7 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) - react@19.0.0: {} + react@19.2.7: {} read-cache@1.0.0: dependencies: @@ -3869,21 +3556,21 @@ snapshots: dependencies: picomatch: 2.3.2 - readdirp@4.1.2: {} + readdirp@5.0.0: {} recharts-scale@0.4.5: dependencies: decimal.js-light: 2.5.1 - recharts@2.15.0(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + recharts@2.15.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: clsx: 2.1.1 eventemitter3: 4.0.7 lodash: 4.18.1 - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) react-is: 18.3.1 - react-smooth: 4.0.4(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + react-smooth: 4.0.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7) recharts-scale: 0.4.5 tiny-invariant: 1.3.3 victory-vendor: 36.9.2 @@ -3894,13 +3581,12 @@ snapshots: redux@5.0.1: {} - regenerator-runtime@0.14.1: {} + reselect@5.2.0: {} - reselect@5.1.1: {} - - resolve@1.22.10: + resolve@1.22.12: dependencies: - is-core-module: 2.16.1 + es-errors: 1.3.0 + is-core-module: 2.16.2 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 @@ -3910,24 +3596,24 @@ snapshots: dependencies: queue-microtask: 1.2.3 - sass@1.86.3: + sass@1.100.0: dependencies: - chokidar: 4.0.3 - immutable: 5.1.5 + chokidar: 5.0.0 + immutable: 5.1.6 source-map-js: 1.2.1 optionalDependencies: - '@parcel/watcher': 2.5.1 + '@parcel/watcher': 2.5.6 - scheduler@0.25.0: {} + scheduler@0.27.0: {} - semver@7.8.0: + semver@7.8.4: optional: true sharp@0.34.5: dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 - semver: 7.8.0 + semver: 7.8.4 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.5 '@img/sharp-darwin-x64': 0.34.5 @@ -3955,67 +3641,37 @@ snapshots: '@img/sharp-win32-x64': 0.34.5 optional: true - shebang-command@2.0.0: + sonner@1.7.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - shebang-regex: 3.0.0 - - shebang-regex@3.0.0: {} - - signal-exit@4.1.0: {} - - sonner@1.7.1(react-dom@19.0.0(react@19.0.0))(react@19.0.0): - dependencies: - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) source-map-js@1.2.1: {} - source-map@0.6.1: {} - - string-width@4.2.3: - dependencies: - emoji-regex: 8.0.0 - is-fullwidth-code-point: 3.0.0 - strip-ansi: 6.0.1 - - string-width@5.1.2: - dependencies: - eastasianwidth: 0.2.0 - emoji-regex: 9.2.2 - strip-ansi: 7.1.0 - - strip-ansi@6.0.1: - dependencies: - ansi-regex: 5.0.1 - - strip-ansi@7.1.0: - dependencies: - ansi-regex: 6.1.0 - - styled-jsx@5.1.6(react@19.0.0): + styled-jsx@5.1.6(react@19.2.7): dependencies: client-only: 0.0.1 - react: 19.0.0 + react: 19.2.7 - sucrase@3.35.0: + sucrase@3.35.1: dependencies: - '@jridgewell/gen-mapping': 0.3.8 + '@jridgewell/gen-mapping': 0.3.13 commander: 4.1.1 - glob: 10.5.0 lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.7 + tinyglobby: 0.2.17 ts-interface-checker: 0.1.13 supports-preserve-symlinks-flag@1.0.0: {} - tailwind-merge@2.5.5: {} + tailwind-merge@2.6.1: {} - tailwindcss-animate@1.0.7(tailwindcss@3.4.17): + tailwindcss-animate@1.0.7(tailwindcss@3.4.19): dependencies: - tailwindcss: 3.4.17 + tailwindcss: 3.4.19 - tailwindcss@3.4.17: + tailwindcss@3.4.19: dependencies: '@alloc/quick-lru': 5.2.0 arg: 5.0.2 @@ -4031,16 +3687,17 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.3 - postcss-import: 15.1.0(postcss@8.5.3) - postcss-js: 4.0.1(postcss@8.5.3) - postcss-load-config: 4.0.2(postcss@8.5.3) - postcss-nested: 6.2.0(postcss@8.5.3) - postcss-selector-parser: 6.1.2 - resolve: 1.22.10 - sucrase: 3.35.0 + postcss: 8.5.15 + postcss-import: 15.1.0(postcss@8.5.15) + postcss-js: 4.1.0(postcss@8.5.15) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.15) + postcss-nested: 6.2.0(postcss@8.5.15) + postcss-selector-parser: 6.1.3 + resolve: 1.22.12 + sucrase: 3.35.1 transitivePeerDependencies: - - ts-node + - tsx + - yaml thenify-all@1.6.0: dependencies: @@ -4052,6 +3709,11 @@ snapshots: tiny-invariant@1.3.3: {} + tinyglobby@0.2.17: + dependencies: + fdir: 6.5.0(picomatch@4.0.4) + picomatch: 4.0.4 + to-regex-range@5.0.1: dependencies: is-number: 7.0.0 @@ -4060,53 +3722,53 @@ snapshots: tslib@2.8.1: {} - typescript@5.0.2: {} + typescript@5.9.3: {} - undici-types@6.11.1: {} + undici-types@6.21.0: {} - update-browserslist-db@1.1.3(browserslist@4.24.4): + update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: - browserslist: 4.24.4 + browserslist: 4.28.2 escalade: 3.2.0 picocolors: 1.1.1 - use-callback-ref@1.3.3(@types/react@19.0.0)(react@19.0.0): + use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.7): dependencies: - react: 19.0.0 + react: 19.2.7 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 - use-sidecar@1.1.3(@types/react@19.0.0)(react@19.0.0): + use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.7): dependencies: detect-node-es: 1.1.0 - react: 19.0.0 + react: 19.2.7 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.0.0 + '@types/react': 19.2.17 - use-sync-external-store@1.5.0(react@19.0.0): + use-sync-external-store@1.6.0(react@19.2.7): dependencies: - react: 19.0.0 + react: 19.2.7 util-deprecate@1.0.2: {} - vaul@0.9.6(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0): + vaul@0.9.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@radix-ui/react-dialog': 1.1.4(@types/react-dom@19.0.0)(@types/react@19.0.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0) - react: 19.0.0 - react-dom: 19.0.0(react@19.0.0) + '@radix-ui/react-dialog': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) transitivePeerDependencies: - '@types/react' - '@types/react-dom' victory-vendor@36.9.2: dependencies: - '@types/d3-array': 3.2.1 + '@types/d3-array': 3.2.2 '@types/d3-ease': 3.0.2 '@types/d3-interpolate': 3.0.4 '@types/d3-scale': 4.0.9 - '@types/d3-shape': 3.1.7 + '@types/d3-shape': 3.1.8 '@types/d3-time': 3.0.4 '@types/d3-timer': 3.0.2 d3-array: 3.2.4 @@ -4117,22 +3779,4 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - which@2.0.2: - dependencies: - isexe: 2.0.0 - - wrap-ansi@7.0.0: - dependencies: - ansi-styles: 4.3.0 - string-width: 4.2.3 - strip-ansi: 6.0.1 - - wrap-ansi@8.1.0: - dependencies: - ansi-styles: 6.2.1 - string-width: 5.1.2 - strip-ansi: 7.1.0 - - yaml@2.7.1: {} - - zod@3.24.1: {} + zod@3.25.76: {} diff --git a/openmemory/ui/pnpm-workspace.yaml b/openmemory/ui/pnpm-workspace.yaml index cc2df9840..de1829360 100644 --- a/openmemory/ui/pnpm-workspace.yaml +++ b/openmemory/ui/pnpm-workspace.yaml @@ -10,3 +10,5 @@ overrides: "picomatch@<2.3.2": "^2.3.2" "minimatch@>=9.0.0 <9.0.7": "^9.0.7" "glob@>=10.2.0 <10.5.0": "^10.5.0" + "postcss@<8.5.10": ">=8.5.10" + "yaml@>=2.0.0 <2.8.3": ">=2.8.3" diff --git a/pyproject.toml b/pyproject.toml index a90ba3fb2..faad07cdc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "mem0ai" -version = "2.0.5" +version = "2.0.6" description = "Long-term memory for AI Agents" authors = [ { name = "Mem0", email = "support@mem0.ai" } @@ -17,7 +17,7 @@ dependencies = [ "qdrant-client>=1.12.0", "pydantic>=2.7.3", "openai>=1.90.0", - "posthog>=4.5.0", + "posthog>=7.14.0", "pytz>=2024.1", "sqlalchemy>=2.0.31", "protobuf>=5.29.6,<7.0.0", diff --git a/server/dashboard/package.json b/server/dashboard/package.json index 7b212b7d7..38468936a 100644 --- a/server/dashboard/package.json +++ b/server/dashboard/package.json @@ -60,7 +60,7 @@ "tailwind-merge": "^2.2.0", "tailwindcss-animate": "^1.0.7", "use-debounce": "^10.0.4", - "uuid": "^9.0.1", + "uuid": "^11.1.1", "vaul": "^0.9.1", "zod": "^3.23.8" }, diff --git a/server/dashboard/pnpm-lock.yaml b/server/dashboard/pnpm-lock.yaml index a77323829..a16b67ed2 100644 --- a/server/dashboard/pnpm-lock.yaml +++ b/server/dashboard/pnpm-lock.yaml @@ -4,88 +4,93 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +overrides: + postcss@<8.5.10: '>=8.5.10' + prismjs@<1.30.0: '>=1.30.0' + uuid@<11.1.1: '>=11.1.1' + importers: .: dependencies: '@hookform/resolvers': specifier: ^3.6.0 - version: 3.10.0(react-hook-form@7.72.1(react@19.2.5)) + version: 3.10.0(react-hook-form@7.78.0(react@19.2.7)) '@radix-ui/react-accordion': specifier: ^1.2.0 - version: 1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.2.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-alert-dialog': specifier: ^1.1.4 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-avatar': specifier: ^1.0.4 - version: 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-checkbox': specifier: ^1.0.4 - version: 1.3.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.3.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-collapsible': specifier: ^1.1.3 - version: 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-dialog': specifier: ^1.1.1 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-dropdown-menu': specifier: ^2.0.6 - version: 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-hover-card': specifier: ^1.1.1 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-icons': specifier: ^1.3.0 - version: 1.3.2(react@19.2.5) + version: 1.3.2(react@19.2.7) '@radix-ui/react-label': specifier: ^2.1.0 - version: 2.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 2.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-popover': specifier: ^1.0.7 - version: 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-progress': specifier: ^1.1.0 - version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-radio-group': specifier: ^1.2.0 - version: 1.3.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.4.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-scroll-area': specifier: ^1.0.5 - version: 1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-select': specifier: ^2.1.1 - version: 2.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-separator': specifier: ^1.1.0 - version: 1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slider': specifier: ^1.2.2 - version: 1.3.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.4.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-slot': specifier: ^1.2.3 - version: 1.2.4(@types/react@19.2.14)(react@19.2.5) + version: 1.2.5(@types/react@19.2.17)(react@19.2.7) '@radix-ui/react-switch': specifier: ^1.1.2 - version: 1.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-tabs': specifier: ^1.1.0 - version: 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-toast': specifier: ^1.2.1 - version: 1.2.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@radix-ui/react-tooltip': specifier: ^1.0.7 - version: 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.2.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) '@reduxjs/toolkit': specifier: ^2.3.0 - version: 2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1))(react@19.2.5) + version: 2.12.0(react-redux@9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1))(react@19.2.7) '@tanstack/react-table': specifier: ^8.17.3 - version: 8.21.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 8.21.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7) axios: specifier: ^1.16.0 - version: 1.16.1 + version: 1.17.0 class-variance-authority: specifier: ^0.7.1 version: 0.7.1 @@ -94,55 +99,55 @@ importers: version: 2.1.1 cmdk: specifier: ^1.0.0 - version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) date-fns: specifier: ^4.1.0 - version: 4.1.0 + version: 4.4.0 framer-motion: specifier: ^12.23.12 - version: 12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 12.40.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7) lodash: specifier: ^4.18.1 version: 4.18.1 lucide-react: specifier: ^0.542.0 - version: 0.542.0(react@19.2.5) + version: 0.542.0(react@19.2.7) next: specifier: 15.5.18 - version: 15.5.18(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 15.5.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7) next-themes: specifier: ^0.4.4 - version: 0.4.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7) react: specifier: ^19.0.0 - version: 19.2.5 + version: 19.2.7 react-copy-to-clipboard: specifier: ^5.1.0 - version: 5.1.1(react@19.2.5) + version: 5.1.1(react@19.2.7) react-day-picker: specifier: 8.10.1 - version: 8.10.1(date-fns@4.1.0)(react@19.2.5) + version: 8.10.1(date-fns@4.4.0)(react@19.2.7) react-dom: specifier: ^19.0.0 - version: 19.2.5(react@19.2.5) + version: 19.2.7(react@19.2.7) react-hook-form: specifier: ^7.52.0 - version: 7.72.1(react@19.2.5) + version: 7.78.0(react@19.2.7) react-redux: specifier: ^9.1.2 - version: 9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1) + version: 9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1) react-syntax-highlighter: specifier: ^15.5.0 - version: 15.6.6(react@19.2.5) + version: 15.6.6(react@19.2.7) recharts: specifier: ^2.15.0 - version: 2.15.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 2.15.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7) redux: specifier: ^5.0.1 version: 5.0.1 sonner: specifier: ^1.4.41 - version: 1.7.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 1.7.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7) tailwind-merge: specifier: ^2.2.0 version: 2.6.1 @@ -151,20 +156,20 @@ importers: version: 1.0.7(tailwindcss@3.4.19) use-debounce: specifier: ^10.0.4 - version: 10.1.1(react@19.2.5) + version: 10.1.1(react@19.2.7) uuid: - specifier: ^9.0.1 - version: 9.0.1 + specifier: ^11.1.1 + version: 11.1.1 vaul: specifier: ^0.9.1 - version: 0.9.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + version: 0.9.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) zod: specifier: ^3.23.8 version: 3.25.76 devDependencies: '@tailwindcss/typography': specifier: ^0.5.13 - version: 0.5.19(tailwindcss@3.4.19) + version: 0.5.20(tailwindcss@3.4.19) '@types/lodash': specifier: ^4.17.7 version: 4.17.24 @@ -173,13 +178,13 @@ importers: version: 22.5.4 '@types/react': specifier: ^19.0.0 - version: 19.2.14 + version: 19.2.17 '@types/react-copy-to-clipboard': specifier: ^5.0.7 version: 5.0.7 '@types/react-dom': specifier: ^19.0.0 - version: 19.2.3(@types/react@19.2.14) + version: 19.2.3(@types/react@19.2.17) '@types/react-syntax-highlighter': specifier: ^15.5.13 version: 15.5.13 @@ -188,7 +193,7 @@ importers: version: 9.0.8 autoprefixer: specifier: ^10.0.1 - version: 10.5.0(postcss@8.5.10) + version: 10.5.0(postcss@8.5.15) eslint: specifier: ^8 version: 8.57.1 @@ -196,11 +201,11 @@ importers: specifier: 15.5.18 version: 15.5.18(eslint@8.57.1)(typescript@5.9.3) postcss: - specifier: ^8 - version: 8.5.10 + specifier: '>=8.5.10' + version: 8.5.15 prettier: specifier: ^3.5.2 - version: 3.8.3 + version: 3.8.4 tailwindcss: specifier: ^3.3.0 version: 3.4.19 @@ -214,15 +219,18 @@ packages: resolution: {integrity: sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==} engines: {node: '>=10'} - '@babel/runtime@7.29.2': - resolution: {integrity: sha512-JiDShH45zKHWyGe4ZNVRrCjBz8Nh9TMmZG1kh4QTK8hCBTWBi8Da+i7s1fJw7/lYpM4ccepSNfqzZ/QvABBi5g==} + '@babel/runtime@7.29.7': + resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} - '@emnapi/core@1.9.2': - resolution: {integrity: sha512-UC+ZhH3XtczQYfOlu3lNEkdW/p4dsJ1r/bP7H8+rhao3TTTMO1ATq/4DdIi23XuGoFY+Cz0JmCbdVl0hz9jZcA==} + '@emnapi/core@1.10.0': + resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} - '@emnapi/runtime@1.9.2': - resolution: {integrity: sha512-3U4+MIWHImeyu1wnmVygh5WlgfYDtyf0k8AbLhMFxOipihf6nrWC4syIm/SwEeec0mNSafiiNnMJwbza/Is6Lw==} + '@emnapi/runtime@1.10.0': + resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} + + '@emnapi/runtime@1.11.0': + resolution: {integrity: sha512-55coeOFKHv1ywEcUXJtWU5f+Jr/W5tZDvZig8DLKSwUN1JpROQ4rk/SNOQiFWmaR/VKF4zuFyW1B8JduOSv6Pg==} '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} @@ -444,8 +452,11 @@ packages: '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} - '@napi-rs/wasm-runtime@0.2.12': - resolution: {integrity: sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ==} + '@napi-rs/wasm-runtime@1.1.5': + resolution: {integrity: sha512-AWPoBRJ9tsnVhor4sjO7rkni+7p+2IAEFj6cx06UgP10jkQHqay/36uRV/bFkgrh18D9vb4cr8Q0Pthskgzy+Q==} + peerDependencies: + '@emnapi/core': ^1.7.1 + '@emnapi/runtime': ^1.7.1 '@next/env@15.5.18': resolution: {integrity: sha512-hAV85Ckd9QR6RvH04MEKwsfLTksvFpO47j9xwtoIuvuPnlwecpSi+uZTtm8HirVbtlI2Fnz//xpcSTjFdyJk+g==} @@ -521,14 +532,14 @@ packages: resolution: {integrity: sha512-nn5ozdjYQpUCZlWGuxcJY/KpxkWQs4DcbMCmKojjyrYDEAGy4Ce19NN4v5MduafTwJlbKc99UA8YhSVqq9yPZA==} engines: {node: '>=12.4.0'} - '@radix-ui/number@1.1.1': - resolution: {integrity: sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g==} + '@radix-ui/number@1.1.2': + resolution: {integrity: sha512-ceTwaxc4I5IOi97DgCotl3pqiyRGvffcc0oOsE2dQYaJOFIDsDt4VWG6xEbg1QePv9QWausCEIppud/tJ1wNig==} - '@radix-ui/primitive@1.1.3': - resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==} + '@radix-ui/primitive@1.1.4': + resolution: {integrity: sha512-7AdCK9PQyiljKoBDbN8OuctCbd/esdwZPQ8RtOE3SsyQtUpiPb+ND75q0jEhC1m1ecBI0MFNeLJvwIh9iKHRcQ==} - '@radix-ui/react-accordion@1.2.12': - resolution: {integrity: sha512-T4nygeh9YE9dLRPhAHSeOZi7HBXo+0kYIPJXayZfvWOWA0+n3dESrZbjfDPUABkUNym6Hd+f2IR113To8D2GPA==} + '@radix-ui/react-accordion@1.2.13': + resolution: {integrity: sha512-xITxBB2p5m5tAe7M0F95kb4uAh7jSIKGlExMEm93HlW+XxZHV2eXFbPWLktd4JhRiwcnXNbO7iekcrbZy6ZCvA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -540,8 +551,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-alert-dialog@1.1.15': - resolution: {integrity: sha512-oTVLkEw5GpdRe29BqJ0LSDFWI3qu0vR1M0mUkOQWDIUnY/QIkLpgDMWuKxP94c2NAC2LGcgVhG1ImF3jkZ5wXw==} + '@radix-ui/react-alert-dialog@1.1.16': + resolution: {integrity: sha512-vPaIgo0mxYlvcFaM9jB2Uot9TjGXMuAPEvrc6BOLeV+I5U8s1dkIoouYaa6lmSfc5SPMo5x5djOTOTvaigdGMQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -553,8 +564,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-arrow@1.1.7': - resolution: {integrity: sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w==} + '@radix-ui/react-arrow@1.1.9': + resolution: {integrity: sha512-yqHW5WQ/cTpU/un7dqqIKNy2iRU8BC0JB78PEzTfCCYvZu1U6W9KwObAniMk9nhSfyotKPQTYaUD/HB0f5muig==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -566,8 +577,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-avatar@1.1.11': - resolution: {integrity: sha512-0Qk603AHGV28BOBO34p7IgD5m+V5Sg/YovfayABkoDDBM5d3NCx0Mp4gGrjzLGes1jV5eNOE1r3itqOR33VC6Q==} + '@radix-ui/react-avatar@1.1.12': + resolution: {integrity: sha512-NQCQyWC7QrDPhjMn8hUqFeU0lUrprIgm1AyMgLbzuQJibNnatdc3SSMo3/UGFu/eUkJUU1cEcKCnyhXTQzq6tA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -579,8 +590,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-checkbox@1.3.3': - resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==} + '@radix-ui/react-checkbox@1.3.4': + resolution: {integrity: sha512-m3JmIOAX5ZzZ6VPjxEU2dbTOhoHi0nT5riwcDwe8idocsWf4a5DXJLDtZ6LfJwMBx7W+A2b7kp2TgPEKtaiF6A==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -592,8 +603,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collapsible@1.1.12': - resolution: {integrity: sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA==} + '@radix-ui/react-collapsible@1.1.13': + resolution: {integrity: sha512-F0s8+p2XNpfc3k02zBfB0jPWbkHVG162+p7BdUMyJ2308QMqZ+oaclX+FAzKFovgL5OqRU+Rvy6f/vbdlJVaqA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -605,8 +616,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-collection@1.1.7': - resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==} + '@radix-ui/react-collection@1.1.9': + resolution: {integrity: sha512-zuSVi7ziP7uQRqc+yGxsKJfNkdyHv3ZKDaHe0gzg4dRgws96TPKWIiz84tVHP4GEcEl8bC0mdt17NkcxaJHmaQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -618,8 +629,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-compose-refs@1.1.2': - resolution: {integrity: sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg==} + '@radix-ui/react-compose-refs@1.1.3': + resolution: {integrity: sha512-rYOP8OMnuuPMQF1uhPVlGNcCDlkokKqGFE3JcxFViIkAXP7EvFWUliJAstrapypaBLJNHbZL6jGhbVDGTwmVhA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -627,8 +638,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-context@1.1.2': - resolution: {integrity: sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA==} + '@radix-ui/react-context@1.1.4': + resolution: {integrity: sha512-QwH4PO5urrbO+FaGd5Aglg+YJgWTyyuZ3g/6mKvsqraLkglDdckw9JafgL5McL5VEJ6EPNduPaT3ZE9BttDAqg==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -636,17 +647,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-context@1.1.3': - resolution: {integrity: sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-dialog@1.1.15': - resolution: {integrity: sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw==} + '@radix-ui/react-dialog@1.1.16': + resolution: {integrity: sha512-l9ok83YBclEZhbjgzt76Hw733e6cvRKPNgO6GJ/IETlufXG9p+fRu2wlvpImQvR6xdJ8h7J8J2DBvsPEiEsKMw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -658,8 +660,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-direction@1.1.1': - resolution: {integrity: sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw==} + '@radix-ui/react-direction@1.1.2': + resolution: {integrity: sha512-C3vFhbyi4SW3PmbAi6Awpu4OzJtd0MxGurvSsYtr7p7nM8RNB3VAF3CUmnp2j50knpkrRcB7+ycVXzgLgF6yNA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -667,8 +669,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-dismissable-layer@1.1.11': - resolution: {integrity: sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg==} + '@radix-ui/react-dismissable-layer@1.1.12': + resolution: {integrity: sha512-MhoruH6xEzsbvOmo4TNgMfmtvRGyDZw4MDSdf4ybMHfezjqwzv6hyd4lsMzBp8K9Sn6sGzCF62x1I7BYUECXOg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -680,8 +682,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-dropdown-menu@2.1.16': - resolution: {integrity: sha512-1PLGQEynI/3OX/ftV54COn+3Sud/Mn8vALg2rWnBLnRaGtJDduNW/22XjlGgPdpcIbiQxjKtb7BkcjP00nqfJw==} + '@radix-ui/react-dropdown-menu@2.1.17': + resolution: {integrity: sha512-S6b3Jm57sY5EdDyOMLkacbB0qMnKhy1RCKZCt795ZkmtUOAvojYIZ5p7dXHIh5Cyr3jCLLI5/g64V3FKLudZmw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -693,8 +695,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-focus-guards@1.1.3': - resolution: {integrity: sha512-0rFg/Rj2Q62NCm62jZw0QX7a3sz6QCQU0LpZdNrJX8byRGaGVTqbrW9jAoIAHyMQqsNpeZ81YgSizOt5WXq0Pw==} + '@radix-ui/react-focus-guards@1.1.4': + resolution: {integrity: sha512-cot/aB/mOm0IYVYTTmQcEEK1M48lZWi8FlYe5nDPQQ8NYZUlXEFgncJ9p2Kzer3RKSrY7cTTpEMLZKNo9QoP5Q==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -702,8 +704,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-focus-scope@1.1.7': - resolution: {integrity: sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw==} + '@radix-ui/react-focus-scope@1.1.9': + resolution: {integrity: sha512-9Se8t+Zry+1rEOL7Y6l/4ANYU/TOtAtf8O2fKdwLltcaMcm6kOqYGbzO4tMFQ0bvzO920pRAoHpFZ4W85S3keQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -715,8 +717,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-hover-card@1.1.15': - resolution: {integrity: sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg==} + '@radix-ui/react-hover-card@1.1.16': + resolution: {integrity: sha512-hAileDBtd6CX7nlZOarOnISQ6PP4q0e16BX51ulzdZ+7IzjL0sDTVpFdmSYrIjw6zVNsfQBao5gG6AWr3qwfvA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -733,8 +735,8 @@ packages: peerDependencies: react: ^16.x || ^17.x || ^18.x || ^19.0.0 || ^19.0.0-rc - '@radix-ui/react-id@1.1.1': - resolution: {integrity: sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg==} + '@radix-ui/react-id@1.1.2': + resolution: {integrity: sha512-orBC88futVpqCmhX1p4cvquNHsELQ+w+vBJnuj3ftETI5bJb0bZn3Tqu3SWN2IOcPycTnMGnhwoermvISt72sA==} peerDependencies: '@types/react': '*' react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc @@ -742,8 +744,8 @@ packages: '@types/react': optional: true - '@radix-ui/react-label@2.1.8': - resolution: {integrity: sha512-FmXs37I6hSBVDlO4y764TNz1rLgKwjJMQ0EGte6F3Cb3f4bIuHB/iLa/8I9VKkmOy+gNHq8rql3j686ACVV21A==} + '@radix-ui/react-label@2.1.9': + resolution: {integrity: sha512-rDoTeMbCwRVcnmo7NGT9IlPo1yXmEI+xc1URP3oeewwZEV4mdTp1dYUhYbQdo4D1q2SjKVvv4N1gNY77QAQtjA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -755,8 +757,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-menu@2.1.16': - resolution: {integrity: sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg==} + '@radix-ui/react-menu@2.1.17': + resolution: {integrity: sha512-fmbNnFyf+JYCN0DhhWnEdUTDnZD1mXaPQWivdsPIb8oOSbARfD3LIQJbLCG8a8QLCwoMxiJ7GVPIFcC8Dw8v2Q==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -768,8 +770,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popover@1.1.15': - resolution: {integrity: sha512-kr0X2+6Yy/vJzLYJUPCZEc8SfQcf+1COFoAqauJm74umQhta9M7lNJHP7QQS3vkvcGLQUbWpMzwrXYwrYztHKA==} + '@radix-ui/react-popover@1.1.16': + resolution: {integrity: sha512-8brVpAU5Uq7Bh0c8EFc4ZTf2JJTYn0o+1L+CUJB3UYIOkTjKGMgoHvduylrahdmNlr3DfH0rFq2DrbNZXgaspw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -781,8 +783,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-popper@1.2.8': - resolution: {integrity: sha512-0NJQ4LFFUuWkE7Oxf0htBKS6zLkkjBH+hM1uk7Ng705ReR8m/uelduy1DBo0PyBXPKVnBA6YBlU94MBGXrSBCw==} + '@radix-ui/react-popper@1.3.0': + resolution: {integrity: sha512-9PB589e1aWZbrlFUHdz6WiPCL+xLZHQFX7oibqG/6Q0SwOkxDyQX9W/cyPa+sAPPKuC8cpLCpRczE5a/1DiwVQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -794,8 +796,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-portal@1.1.9': - resolution: {integrity: sha512-bpIxvq03if6UNwXZ+HTK71JLh4APvnXntDc6XOX8UVq4XQOVl7lwok0AvIl+b8zgCw3fSaVTZMpAPPagXbKmHQ==} + '@radix-ui/react-portal@1.1.11': + resolution: {integrity: sha512-UEytdjgEh2tJGgD/gZK4FUx6t1rNIlM3U0DENhSrG7I75FGm1DnaDuVUWF1pWAWUwGmn1sCJ1VGHn8LhN1aTOw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -807,8 +809,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-presence@1.1.5': - resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==} + '@radix-ui/react-presence@1.1.6': + resolution: {integrity: sha512-zdTk4PlUO0E18HnZ3wYbW0KkJJxWCdiNYp6g6X1PtONFhxVkg01vliTJAmwIszU6mHiyBOoW9P0rAugl5/hULQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -820,8 +822,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.1.3': - resolution: {integrity: sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ==} + '@radix-ui/react-primitive@2.1.5': + resolution: {integrity: sha512-zifXeB8Y88qCYx8PLZ5oQb32KwZub+s925mMoZsBBq9KUQqWKkREubTfs6ASjRPPBe7Jt9O8OHH89+95VG+grA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -833,8 +835,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-primitive@2.1.4': - resolution: {integrity: sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg==} + '@radix-ui/react-progress@1.1.9': + resolution: {integrity: sha512-+EOkvg1Zn1vI1+fRDfRSAiJ7BWfcDAo5ASMmbqrcLZ4s4USk2FGkoHgeb2X+CkUgo2zJMiyObwf1k44CrRWsyw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -846,8 +848,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-progress@1.1.8': - resolution: {integrity: sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA==} + '@radix-ui/react-radio-group@1.4.0': + resolution: {integrity: sha512-eHdV5bLx9sH+tBnbDjkIBdvQEH/c6MEtQYhTbxkaDK9qsIFFLtmJYEQFVdwhnruWotLfQmIuWEL/J+L3utE8rQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -859,8 +861,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-radio-group@1.3.8': - resolution: {integrity: sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==} + '@radix-ui/react-roving-focus@1.1.12': + resolution: {integrity: sha512-FvgPt1bRmg8Xt2QpF7NUZW3dE0ZQHGm41dAdgT2J2GJPoIXz+9Em3NobAxf4fupcxhgHu03E5CRiU2MWvObXyg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -872,8 +874,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-roving-focus@1.1.11': - resolution: {integrity: sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA==} + '@radix-ui/react-scroll-area@1.2.11': + resolution: {integrity: sha512-DS39ziOgea75U/TrXKU2/oKp0be2jrDHnzFLvahg/0iNAT1Zq16e4Uw0WXwyXvsK+mG3BRyMb7A3NRZMDuEXtQ==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -885,8 +887,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-scroll-area@1.2.10': - resolution: {integrity: sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A==} + '@radix-ui/react-select@2.3.0': + resolution: {integrity: sha512-mENc7WpJvJcW8hlMpzfFcHcEhTvYS5JMBmi9HVC1Q00uhBwML086MHYUV8QQdQv6lcu0Wg8dzd1RB8AFADcG/g==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -898,8 +900,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-select@2.2.6': - resolution: {integrity: sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ==} + '@radix-ui/react-separator@1.1.9': + resolution: {integrity: sha512-gvgW+JV/Mbjj6darztTetnmElpQEzZrXpJvfj+dOxNAxiyHEAyUvEjjl4zxblvmjmKmi3jfPoy7ZdxzCuUBJSA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -911,8 +913,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-separator@1.1.8': - resolution: {integrity: sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g==} + '@radix-ui/react-slider@1.4.0': + resolution: {integrity: sha512-RHcPlLOThRJM51DSIC33ZnpDEBYhyEFroVWkd2P54PGGjkmAt14RboYUU9E1MFst666zFHM0tGtWvMjSOtU1pw==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -924,8 +926,17 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slider@1.3.6': - resolution: {integrity: sha512-JPYb1GuM1bxfjMRlNLE+BcmBC8onfCi60Blk7OBqi2MLTFdS+8401U4uFjnwkOr49BLmXxLC6JHkvAsx5OJvHw==} + '@radix-ui/react-slot@1.2.5': + resolution: {integrity: sha512-rCMO3QsIVKv5JTY5CVbo2MvO77SpEqqYc8AvRE7OWqRDOIqAKjsp+DrmnY9uc8NPdxB5E2z47HTYGeE2+NTptg==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-switch@1.3.0': + resolution: {integrity: sha512-GP1EZwhoZO/GGnhM1P5/2Vpm8iN8EnngyU0oezn2l78kN8tj25pyrvjIaT7azBhK615KSt+P2w39y57YV5jVkA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -937,26 +948,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-slot@1.2.3': - resolution: {integrity: sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-slot@1.2.4': - resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-switch@1.2.6': - resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==} + '@radix-ui/react-tabs@1.1.14': + resolution: {integrity: sha512-D5jwp9JNuwDeCw3CYD2Fz+sSHo0droQjC8u75dJHe4aWr5q6yBiXZU+hurXnKudRgEpUkD5TsI6bjHPo5ThUxA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -968,8 +961,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tabs@1.1.13': - resolution: {integrity: sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A==} + '@radix-ui/react-toast@1.2.16': + resolution: {integrity: sha512-WUymDDiN2DpoGudRN1aW4wF5O3BNQjZZO/5nngPoNiEVqjyOzirvZZNO0R6dC1ifucSINVaSv8JX1aq47VGgiA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -981,8 +974,8 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-toast@1.2.15': - resolution: {integrity: sha512-3OSz3TacUWy4WtOXV38DggwxoqJK4+eDkNMl5Z/MJZaoUPaP4/9lf81xXMe1I2ReTAptverZUpbPY4wWwWyL5g==} + '@radix-ui/react-tooltip@1.2.9': + resolution: {integrity: sha512-u6F9MmTtBSLkiXNVDrtB/yPCZarM9smNswC24YYLV/M+bth6J3Gs3vlJezEoFwKZvPvxhCpUYdUnOsNG/0XOlA==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -994,8 +987,89 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-tooltip@1.2.8': - resolution: {integrity: sha512-tY7sVt1yL9ozIxvmbtN5qtmH2krXcBCfjEiCgKGLqunJHvgvZG2Pcl2oQ3kbcZARb1BGEHdkLzcYGO8ynVlieg==} + '@radix-ui/react-use-callback-ref@1.1.2': + resolution: {integrity: sha512-xCso9j1/u8sEgP1RNHjFrXJLApL8LiqOkI1R4ywuN00rxWdYg4oQXuwKLS3i0j5NWLromUD27/4nlxj2UFVvIw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-controllable-state@1.2.3': + resolution: {integrity: sha512-PLzC90MS+ReootmjC597dvopoelpZ8Q61HJkDXZSExitIq7PL55vHNnesAHwguHK0aPfBnpdNzQtv1uliaqQrA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-effect-event@0.0.3': + resolution: {integrity: sha512-6c8ZqvPTWILEKnyVkP53EGRCcpnJiKTC21sS/6R1GF5xKyHJJWQEPfkqlcgUkdRQivd6tb23abUwe4ngWmY0JA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-escape-keydown@1.1.2': + resolution: {integrity: sha512-2uVLvLjgO7NZCWw01/FdqRwmA42J0BcjPMUCA+koFEOAb+zjqIP7SiFz/7zWPrKnVmSqr76Omq2ALyCuX4dhLw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-is-hydrated@0.1.1': + resolution: {integrity: sha512-qwOiz4Tjo8CNnrOLAYUMXeZwDzXgXpvK4TKQPmWLECM9XoWvA6+0Z2/7Ag3A4ivjS4ovbLJPbskkxioFyBhr8A==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-layout-effect@1.1.2': + resolution: {integrity: sha512-jrBWOxZITuGcnjRCM2t2U5ZPkCLxD+Ym6DjfssS5haTj2iiak/DOb64JeN6OdLfLgptb6/e2kKR+ZuTrGoZTPA==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-previous@1.1.2': + resolution: {integrity: sha512-IGBQPtRFdhN6MQ8dbegVmBq1LVZluya3F1jWY+puIcQC3MHctRwTDSBWCkL/3ZcnMJLTMJ++Z+ktmvg0F89iCw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-rect@1.1.2': + resolution: {integrity: sha512-d8a+bBY/FxikNPlgJJoaBHZX+zKVbWHYJGTLnLvveQgFSTntkGdEKv3JDtHrMS0DNYpllz2nRsTLGLKYttbpmw==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-use-size@1.1.2': + resolution: {integrity: sha512-giWQp+4mxjBPt4KZ0MmyuykFNWfbDxKt4x+fPkRYmgRFJSbCZFzUglvMb/Kjn38tm10YP4ufiQZDx3zna4LU6w==} + peerDependencies: + '@types/react': '*' + react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true + + '@radix-ui/react-visually-hidden@1.2.5': + resolution: {integrity: sha512-tPcHNI3FajdDBFpl/Ez1m2WL0ufJqBKyHxMDBvKitopamK36WwBGOMicuMEZKkM5Wce41QxUyv6BsiqfrWBiGg==} peerDependencies: '@types/react': '*' '@types/react-dom': '*' @@ -1007,105 +1081,11 @@ packages: '@types/react-dom': optional: true - '@radix-ui/react-use-callback-ref@1.1.1': - resolution: {integrity: sha512-FkBMwD+qbGQeMu1cOHnuGB6x4yzPjho8ap5WtbEJ26umhgqVXbhekKUQO+hZEL1vU92a3wHwdp0HAcqAUF5iDg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true + '@radix-ui/rect@1.1.2': + resolution: {integrity: sha512-xnXE7wG13PI+cxieVssYXlQJuYVRhH9NBoxt3KNwzghDIA69GMm7d4wXRouHIYjE+KvS6U/MsMO73NdS2MH9ZA==} - '@radix-ui/react-use-controllable-state@1.2.2': - resolution: {integrity: sha512-BjasUjixPFdS+NKkypcyyN5Pmg83Olst0+c6vGov0diwTEo6mgdqVR6hxcEgFuh4QrAs7Rc+9KuGJ9TVCj0Zzg==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-effect-event@0.0.2': - resolution: {integrity: sha512-Qp8WbZOBe+blgpuUT+lw2xheLP8q0oatc9UpmiemEICxGvFLYmHm9QowVZGHtJlGbS6A6yJ3iViad/2cVjnOiA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-escape-keydown@1.1.1': - resolution: {integrity: sha512-Il0+boE7w/XebUHyBjroE+DbByORGR9KKmITzbR7MyQ4akpORYP/ZmbhAr0DG7RmmBqoOnZdy2QlvajJ2QA59g==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-is-hydrated@0.1.0': - resolution: {integrity: sha512-U+UORVEq+cTnRIaostJv9AGdV3G6Y+zbVd+12e18jQ5A3c0xL03IhnHuiU4UV69wolOQp5GfR58NW/EgdQhwOA==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-layout-effect@1.1.1': - resolution: {integrity: sha512-RbJRS4UWQFkzHTTwVymMTUv8EqYhOp8dOOviLj2ugtTiXRaRQS7GLGxZTLL1jWhMeoSCf5zmcZkqTl9IiYfXcQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-previous@1.1.1': - resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-rect@1.1.1': - resolution: {integrity: sha512-QTYuDesS0VtuHNNvMh+CjlKJ4LJickCMUAqjlE3+j8w+RlRpwyX3apEQKGFzbZGdo7XNG1tXa+bQqIE7HIXT2w==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-use-size@1.1.1': - resolution: {integrity: sha512-ewrXRDTAqAXlkl6t/fkXWNAhFX9I+CkKlw6zjEwk86RSPKwZr3xpBRso655aqYafwtnbpHLj6toFzmd6xdVptQ==} - peerDependencies: - '@types/react': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - - '@radix-ui/react-visually-hidden@1.2.3': - resolution: {integrity: sha512-pzJq12tEaaIhqjbzpCuv/OypJY/BPavOofm+dbab+MHLajy277+1lLm6JFcGgF5eskJ6mquGirhXY2GD/8u8Ug==} - peerDependencies: - '@types/react': '*' - '@types/react-dom': '*' - react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc - peerDependenciesMeta: - '@types/react': - optional: true - '@types/react-dom': - optional: true - - '@radix-ui/rect@1.1.1': - resolution: {integrity: sha512-HPwpGIzkl28mWyZqG52jiqDJ12waP11Pa1lGoiyUkIEuMLBP0oeK/C89esbXrxsky5we7dfd8U58nm0SgAWpVw==} - - '@reduxjs/toolkit@2.11.2': - resolution: {integrity: sha512-Kd6kAHTA6/nUpp8mySPqj3en3dm0tdMIgbttnQ1xFMVpufoj+ADi8pXLBsd4xzTRHQa7t/Jv8W5UnCuW4kuWMQ==} + '@reduxjs/toolkit@2.12.0': + resolution: {integrity: sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==} peerDependencies: react: ^16.9.0 || ^17.0.0 || ^18 || ^19 react-redux: ^7.2.1 || ^8.1.3 || ^9.0.0 @@ -1130,10 +1110,10 @@ packages: '@swc/helpers@0.5.15': resolution: {integrity: sha512-JQ5TuMi45Owi4/BIMAJBoSQoOJu12oOk/gADqlcUL9JEdHB8vyjUSsxqeNXnmXHjYKMi2WcYtezGEEhqUI/E2g==} - '@tailwindcss/typography@0.5.19': - resolution: {integrity: sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==} + '@tailwindcss/typography@0.5.20': + resolution: {integrity: sha512-hwbzQuNUfcPvbegQFatVPl/MY/tcM9KLl963hQ5laJKPh81TEZ1+dNG9PirGvcaDBkp+BCshExAyKVPW91dozw==} peerDependencies: - tailwindcss: '>=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1' + tailwindcss: '>=3.0.0 || >=4.0.0 || insiders' '@tanstack/react-table@8.21.3': resolution: {integrity: sha512-5nNMTSETP4ykGegmVkhjcS8tTLW6Vl4axfEGQN3v0zdHYbK4UfoqfPChclTrJ4EoK9QynqAu9oUf8VEmrpZ5Ww==} @@ -1146,8 +1126,8 @@ packages: resolution: {integrity: sha512-ldZXEhOBb8Is7xLs01fR3YEc3DERiz5silj8tnGkFZytt1abEvl/GhUmCE0PMLaMPTa3Jk4HbKmRlHmu+gCftg==} engines: {node: '>=12'} - '@tybys/wasm-util@0.10.1': - resolution: {integrity: sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==} + '@tybys/wasm-util@0.10.2': + resolution: {integrity: sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg==} '@types/d3-array@3.2.2': resolution: {integrity: sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==} @@ -1199,8 +1179,8 @@ packages: '@types/react-syntax-highlighter@15.5.13': resolution: {integrity: sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==} - '@types/react@19.2.14': - resolution: {integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==} + '@types/react@19.2.17': + resolution: {integrity: sha512-MXfmqaVPEVgkBT/aY0aGCkRWWtByiYQXo3xdQ8r5RzuFrPiRn8Gar2tQdXSUQ2GKV3bkXckek89V8wQBY2Q/Aw==} '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} @@ -1211,169 +1191,185 @@ packages: '@types/uuid@9.0.8': resolution: {integrity: sha512-jg+97EGIcY9AGHJJRaaPVgetKDsrTgbRjQ5Msgjh/DQKEFl0DtyRr/VCOyD1T2R1MNeWPK/u7JoGhlDZnKBAfA==} - '@typescript-eslint/eslint-plugin@8.58.2': - resolution: {integrity: sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==} + '@typescript-eslint/eslint-plugin@8.61.0': + resolution: {integrity: sha512-bFNvl9ZczlVb+wR2Akszf3gHfKVj/8WanXaGJ3UstTA7brNKg0cNdk6X1Psu5V7MZ2oQtzZKOEzIUehaoxbDGw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: - '@typescript-eslint/parser': ^8.58.2 + '@typescript-eslint/parser': ^8.61.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/parser@8.58.2': - resolution: {integrity: sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==} + '@typescript-eslint/parser@8.61.0': + resolution: {integrity: sha512-5B7PfA2e1NQGCnDHd/0lW7W3gvp3d59Ryw54FYO8Uswxo9f6ikw3AZV+Xj/TvpImmpsiYyUqAfhC6kJID1jF6w==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/project-service@8.58.2': - resolution: {integrity: sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==} + '@typescript-eslint/project-service@8.61.0': + resolution: {integrity: sha512-DV42F7MLJO6Rax7SK1yg43tcnEfGUrurSpSxKuVX+a3RCTzBlH3fuxprrOJXKCJGAaw82xXocikJ0uQaqwXgGA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/scope-manager@8.58.2': - resolution: {integrity: sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==} + '@typescript-eslint/scope-manager@8.61.0': + resolution: {integrity: sha512-IWdXFHFSb6mlC3HPc7QsLDm5zYEbUla6trDEHf32D3/dnuUyXd87plScSNXSbm0/RxMvObpI17sv/EDTGrGZkA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/tsconfig-utils@8.58.2': - resolution: {integrity: sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==} + '@typescript-eslint/tsconfig-utils@8.61.0': + resolution: {integrity: sha512-O5Amvdv9ztMpxpf+vmFULGG78IE6Qwdr3bCGvqwG4nwc9H2qXkOYJJnRbRHyMkQTjv1d03olqwwwzHLMqpFePQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/type-utils@8.58.2': - resolution: {integrity: sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==} + '@typescript-eslint/type-utils@8.61.0': + resolution: {integrity: sha512-TuBiQYIkd97yBfInHCTKVYMbX4kvEmpOEuixIuzCU9p8BGT1SfyyO0d0IfDMbPIHcjn/hWnusUX5e8v5Xg+X8A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/types@8.58.2': - resolution: {integrity: sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==} + '@typescript-eslint/types@8.61.0': + resolution: {integrity: sha512-9QTQpZ5Iin4CdIodfbDQFSeiSJKidgYJYug1P9CC2xWgUTvlmixViqDZNciMjwLBZyJnG4tGmPl97rVAFb1AJg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@typescript-eslint/typescript-estree@8.58.2': - resolution: {integrity: sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==} + '@typescript-eslint/typescript-estree@8.61.0': + resolution: {integrity: sha512-42zatd5qSvvcV1JdDBCLxYRznvP4eIHpPoZXdkPFnAmanA4FuZ5dibSnCBggY8hQnqajPpoGjXFdZ7fIJKQnlA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/utils@8.58.2': - resolution: {integrity: sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==} + '@typescript-eslint/utils@8.61.0': + resolution: {integrity: sha512-3bzFt7ImFMW/jVYwJamDoe/dMOdFLSC6pom6rRjdh4SZJEYupyMzem8e7vKZLclLfpHjlwSAXOUxtKxGXUiLqA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' - '@typescript-eslint/visitor-keys@8.58.2': - resolution: {integrity: sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==} + '@typescript-eslint/visitor-keys@8.61.0': + resolution: {integrity: sha512-QVLZu3ZPQEE+HICQyAMZ2yLQhxf0meY/wx6Hx14YcTNj13JB3qHlX3lJ02L3fLGHgERRH71kvYDwiXIguT3AjQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} - '@ungap/structured-clone@1.3.0': - resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==} - deprecated: Potential CWE-502 - Update to 1.3.1 or higher + '@ungap/structured-clone@1.3.1': + resolution: {integrity: sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ==} - '@unrs/resolver-binding-android-arm-eabi@1.11.1': - resolution: {integrity: sha512-ppLRUgHVaGRWUx0R0Ut06Mjo9gBaBkg3v/8AxusGLhsIotbBLuRk51rAzqLC8gq6NyyAojEXglNjzf6R948DNw==} + '@unrs/resolver-binding-android-arm-eabi@1.12.2': + resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} cpu: [arm] os: [android] - '@unrs/resolver-binding-android-arm64@1.11.1': - resolution: {integrity: sha512-lCxkVtb4wp1v+EoN+HjIG9cIIzPkX5OtM03pQYkG+U5O/wL53LC4QbIeazgiKqluGeVEeBlZahHalCaBvU1a2g==} + '@unrs/resolver-binding-android-arm64@1.12.2': + resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==} cpu: [arm64] os: [android] - '@unrs/resolver-binding-darwin-arm64@1.11.1': - resolution: {integrity: sha512-gPVA1UjRu1Y/IsB/dQEsp2V1pm44Of6+LWvbLc9SDk1c2KhhDRDBUkQCYVWe6f26uJb3fOK8saWMgtX8IrMk3g==} + '@unrs/resolver-binding-darwin-arm64@1.12.2': + resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==} cpu: [arm64] os: [darwin] - '@unrs/resolver-binding-darwin-x64@1.11.1': - resolution: {integrity: sha512-cFzP7rWKd3lZaCsDze07QX1SC24lO8mPty9vdP+YVa3MGdVgPmFc59317b2ioXtgCMKGiCLxJ4HQs62oz6GfRQ==} + '@unrs/resolver-binding-darwin-x64@1.12.2': + resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==} cpu: [x64] os: [darwin] - '@unrs/resolver-binding-freebsd-x64@1.11.1': - resolution: {integrity: sha512-fqtGgak3zX4DCB6PFpsH5+Kmt/8CIi4Bry4rb1ho6Av2QHTREM+47y282Uqiu3ZRF5IQioJQ5qWRV6jduA+iGw==} + '@unrs/resolver-binding-freebsd-x64@1.12.2': + resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==} cpu: [x64] os: [freebsd] - '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': - resolution: {integrity: sha512-u92mvlcYtp9MRKmP+ZvMmtPN34+/3lMHlyMj7wXJDeXxuM0Vgzz0+PPJNsro1m3IZPYChIkn944wW8TYgGKFHw==} + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': + resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==} cpu: [arm] os: [linux] - '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': - resolution: {integrity: sha512-cINaoY2z7LVCrfHkIcmvj7osTOtm6VVT16b5oQdS4beibX2SYBwgYLmqhBjA1t51CarSaBuX5YNsWLjsqfW5Cw==} + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': + resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==} cpu: [arm] os: [linux] - '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': - resolution: {integrity: sha512-34gw7PjDGB9JgePJEmhEqBhWvCiiWCuXsL9hYphDF7crW7UgI05gyBAi6MF58uGcMOiOqSJ2ybEeCvHcq0BCmQ==} + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': + resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} cpu: [arm64] os: [linux] libc: [glibc] - '@unrs/resolver-binding-linux-arm64-musl@1.11.1': - resolution: {integrity: sha512-RyMIx6Uf53hhOtJDIamSbTskA99sPHS96wxVE/bJtePJJtpdKGXO1wY90oRdXuYOGOTuqjT8ACccMc4K6QmT3w==} + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': + resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} cpu: [arm64] os: [linux] libc: [musl] - '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': - resolution: {integrity: sha512-D8Vae74A4/a+mZH0FbOkFJL9DSK2R6TFPC9M+jCWYia/q2einCubX10pecpDiTmkJVUH+y8K3BZClycD8nCShA==} + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': + resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} + cpu: [loong64] + os: [linux] + libc: [glibc] + + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': + resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} + cpu: [loong64] + os: [linux] + libc: [musl] + + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': + resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} cpu: [ppc64] os: [linux] libc: [glibc] - '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': - resolution: {integrity: sha512-frxL4OrzOWVVsOc96+V3aqTIQl1O2TjgExV4EKgRY09AJ9leZpEg8Ak9phadbuX0BA4k8U5qtvMSQQGGmaJqcQ==} + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': + resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} cpu: [riscv64] os: [linux] libc: [glibc] - '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': - resolution: {integrity: sha512-mJ5vuDaIZ+l/acv01sHoXfpnyrNKOk/3aDoEdLO/Xtn9HuZlDD6jKxHlkN8ZhWyLJsRBxfv9GYM2utQ1SChKew==} + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': + resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} cpu: [riscv64] os: [linux] libc: [musl] - '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': - resolution: {integrity: sha512-kELo8ebBVtb9sA7rMe1Cph4QHreByhaZ2QEADd9NzIQsYNQpt9UkM9iqr2lhGr5afh885d/cB5QeTXSbZHTYPg==} + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': + resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} cpu: [s390x] os: [linux] libc: [glibc] - '@unrs/resolver-binding-linux-x64-gnu@1.11.1': - resolution: {integrity: sha512-C3ZAHugKgovV5YvAMsxhq0gtXuwESUKc5MhEtjBpLoHPLYM+iuwSj3lflFwK3DPm68660rZ7G8BMcwSro7hD5w==} + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} cpu: [x64] os: [linux] libc: [glibc] - '@unrs/resolver-binding-linux-x64-musl@1.11.1': - resolution: {integrity: sha512-rV0YSoyhK2nZ4vEswT/QwqzqQXw5I6CjoaYMOX0TqBlWhojUf8P94mvI7nuJTeaCkkds3QE4+zS8Ko+GdXuZtA==} + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} cpu: [x64] os: [linux] libc: [musl] - '@unrs/resolver-binding-wasm32-wasi@1.11.1': - resolution: {integrity: sha512-5u4RkfxJm+Ng7IWgkzi3qrFOvLvQYnPBmjmZQ8+szTK/b31fQCnleNl1GgEt7nIsZRIf5PLhPwT0WM+q45x/UQ==} + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} + cpu: [arm64] + os: [openharmony] + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': + resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==} engines: {node: '>=14.0.0'} cpu: [wasm32] - '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': - resolution: {integrity: sha512-nRcz5Il4ln0kMhfL8S3hLkxI85BXs3o8EYoattsJNdsX4YUU89iOkVn7g0VHSRxFuVMdM4Q1jEpIId1Ihim/Uw==} + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': + resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==} cpu: [arm64] os: [win32] - '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': - resolution: {integrity: sha512-DCEI6t5i1NmAZp6pFonpD5m7i6aFrpofcp4LA2i8IIq60Jyo28hamKBxNrZcyOwVOZkgsRp9O2sXWBWP8MnvIQ==} + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': + resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==} cpu: [ia32] os: [win32] - '@unrs/resolver-binding-win32-x64-msvc@1.11.1': - resolution: {integrity: sha512-lrW200hZdbfRtztbygyaq/6jP6AKE8qQN2KvPcJ+x7wiD038YtnYtZ82IMNJ69GJibV7bwL3y9FgK+5w/pYt6g==} + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': + resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==} cpu: [x64] os: [win32] @@ -1391,8 +1387,8 @@ packages: resolution: {integrity: sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==} engines: {node: '>= 6.0.0'} - ajv@6.14.0: - resolution: {integrity: sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==} + ajv@6.15.0: + resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} @@ -1470,18 +1466,18 @@ packages: engines: {node: ^10 || ^12 || >=14} hasBin: true peerDependencies: - postcss: ^8.1.0 + postcss: '>=8.5.10' available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} - axe-core@4.11.3: - resolution: {integrity: sha512-zBQouZixDTbo3jMGqHKyePxYxr1e5W8UdTmBQ7sNtaA9M2bE32daxxPLS/jojhKOHxQ7LWwPjfiwf/fhaJWzlg==} + axe-core@4.12.1: + resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==} engines: {node: '>=4'} - axios@1.16.1: - resolution: {integrity: sha512-caYkukvroVPO8KrzuJEb50Hm07KwfBZPEC3VeFHTsqWHvKTsy54hjJz9BS/cdaypROE2rH6xvm9mHX4fgWkr3A==} + axios@1.17.0: + resolution: {integrity: sha512-J8SwNxprqqpbfenehxWYXE7CW+wM1BB4w3+N+g+/Wx40xM4rsLrfPmHHxSWIxJLYDgSY/HqlFPIYb2/S3rxafw==} axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} @@ -1494,8 +1490,8 @@ packages: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} - baseline-browser-mapping@2.10.19: - resolution: {integrity: sha512-qCkNLi2sfBOn8XhZQ0FXsT1Ki/Yo5P90hrkRamVFRS7/KV9hpfA4HkoWNU152+8w0zPjnxo5psx5NL3PSGgv5g==} + baseline-browser-mapping@2.10.35: + resolution: {integrity: sha512-honAfLBde0HAFLdNyBEfuuENkF6zR+ozxqxa/2zJKHBe1qzLqyTSeRKpdPEHAP03rlDGyQOPnCSxnVpVqQo9Mg==} engines: {node: '>=6.0.0'} hasBin: true @@ -1503,11 +1499,11 @@ packages: resolution: {integrity: sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==} engines: {node: '>=8'} - brace-expansion@1.1.14: - resolution: {integrity: sha512-MWPGfDxnyzKU7rNOW9SP/c50vi3xrmrua/+6hfPbCS2ABNWfx24vPidzvC7krjU/RTo235sV776ymlsMtGKj8g==} + brace-expansion@1.1.15: + resolution: {integrity: sha512-EwOCDEex4quD37XhqM3omwtMoJjr//isUZz1JopUNWms+4Z2ViyM/k1YIRePpoVNnQhENnxtFjLaxNHrT7xIUg==} - brace-expansion@5.0.5: - resolution: {integrity: sha512-VZznLgtwhn+Mact9tfiwx64fA9erHH/MCXEUfB/0bX/6Fz6ny5EGTXYltMocqg4xFAQZtnO3DHWWXi8RiuN7cQ==} + brace-expansion@5.0.6: + resolution: {integrity: sha512-kLpxurY4Z4r9sgMsyG0Z9uzsBlgiU/EFKhj/h91/8yHu0edo7XuixOIH3VcJ8kkxs6/jPzoI6U9Vj3WqbMQ94g==} engines: {node: 18 || 20 || >=22} braces@3.0.3: @@ -1539,8 +1535,8 @@ packages: resolution: {integrity: sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==} engines: {node: '>= 6'} - caniuse-lite@1.0.30001788: - resolution: {integrity: sha512-6q8HFp+lOQtcf7wBK+uEenxymVWkGKkjFpCvw5W25cmMwEDU45p1xQFBQv8JDlMMry7eNxyBaR+qxgmTUZkIRQ==} + caniuse-lite@1.0.30001797: + resolution: {integrity: sha512-l8xKG+gwAIExZGl9FrF7KUwuOmk6wbEPC9Xoy/RtnWv1XG0Q4LFlagaLpUv3Kiza3W/wm27zy0yWJEieYKAP6w==} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} @@ -1670,8 +1666,8 @@ packages: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} - date-fns@4.1.0: - resolution: {integrity: sha512-Ukq0owbQXxa/U3EGtsdVBkR1w7KOQ5gIBqdH2hkvknzZPYvBxb/aa6E8L7tmjFtkwZBu3UXBbjIgPo/Ez4xaNg==} + date-fns@4.4.0: + resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} debug@3.2.7: resolution: {integrity: sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==} @@ -1736,8 +1732,8 @@ packages: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} - electron-to-chromium@1.5.337: - resolution: {integrity: sha512-15gKW9mRUNP9RdzhedJNypFUxtYWSXohFz2nTLzM272xbRXHws68kNDzyATG3qej+vUj/7Sn9hf5XTDh0XK6/w==} + electron-to-chromium@1.5.371: + resolution: {integrity: sha512-e9htk9mAYL6AzmkEhSvVVw7IWGSBJ/Bqdn2eRyRLrj1g6sncN4WbFt5qnILYoCktktr45pyjIrOiRvBThQ808w==} emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} @@ -1754,12 +1750,12 @@ packages: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} - es-iterator-helpers@1.3.2: - resolution: {integrity: sha512-HVLACW1TppGYjJ8H6/jqH/pqOtKRw6wMlrB23xfExmFWxFquAIWCmwoLsOyN96K4a5KbmOf5At9ZUO3GZbetAw==} + es-iterator-helpers@1.3.3: + resolution: {integrity: sha512-0PuBxFi+4uPanB97iDxCLWuHeYud2FALrw5HFZGtAF38UpJDbDC8frwp2cnDyae692CQ0dou60UwWfhgsa4U/g==} engines: {node: '>= 0.4'} - es-object-atoms@1.1.1: - resolution: {integrity: sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==} + es-object-atoms@1.1.2: + resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: @@ -1807,8 +1803,8 @@ packages: eslint-plugin-import-x: optional: true - eslint-module-utils@2.12.1: - resolution: {integrity: sha512-L8jSWTze7K2mTg0vos/RuLRS5soomksDPoJLXIslC7c8Wmut3bx7CPpJijDcBZtxQ5lrbUdM+s0OlNbz0DCDNw==} + eslint-module-utils@2.13.0: + resolution: {integrity: sha512-bLohSkT6469rRs8czj0tLTD8vaeIS/whvPRJVjDr7IuoTT1k5DYDERlNycjDj/HkOlvQdYurmfZ/g3fG5bgeLQ==} engines: {node: '>=4'} peerDependencies: '@typescript-eslint/parser': '*' @@ -1976,8 +1972,8 @@ packages: fraction.js@5.3.4: resolution: {integrity: sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==} - framer-motion@12.38.0: - resolution: {integrity: sha512-rFYkY/pigbcswl1XQSb7q424kSTQ8q6eAC+YUsSKooHQYuLdzdHjrt6uxUC+PRAO++q5IS7+TamgIw1AphxR+g==} + framer-motion@12.40.0: + resolution: {integrity: sha512-uaBd3qC1v3KQqBEjwTUd183K6PbS+j0yR9w9VmEOLWA/tnUcSn8Xa3uck7t4dgpDoUss8xQTcj8W2L07lrnLFg==} peerDependencies: '@emotion/is-prop-valid': '*' react: ^18.0.0 || ^19.0.0 @@ -2081,8 +2077,8 @@ packages: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} - hasown@2.0.2: - resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} + hasown@2.0.4: + resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} hast-util-parse-selector@2.2.5: @@ -2109,8 +2105,8 @@ packages: resolution: {integrity: sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==} engines: {node: '>= 4'} - immer@11.1.4: - resolution: {integrity: sha512-XREFCPo6ksxVzP4E0ekD5aMdf8WMwmdNaz6vuvxgI40UaEiu6q3p8X52aU6GdyvLY3XXX/8R7JOTXStz/nBbRw==} + immer@11.1.8: + resolution: {integrity: sha512-/tbkHMW7y10Lx6i1crLjD4/OhNkRG+Fo7byZHtah0547nIeXYcpIXaUh0IAQY6gO5459qpGGYapcEOHtFXkIuA==} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} @@ -2168,8 +2164,8 @@ packages: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} - is-core-module@2.16.1: - resolution: {integrity: sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==} + is-core-module@2.16.2: + resolution: {integrity: sha512-evOr8xfXKxE6qSR0hSXL2r3sd7ALj8+7jQEUvPYcm5sgZFdJ+AYzT6yNmJenvIYQBgIGwfwz08sL8zoL7yq2BA==} engines: {node: '>= 0.4'} is-data-view@1.0.2: @@ -2275,8 +2271,8 @@ packages: js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} - js-yaml@4.1.1: - resolution: {integrity: sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==} + js-yaml@4.2.0: + resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} hasBin: true json-buffer@3.0.1: @@ -2369,11 +2365,11 @@ packages: minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} - motion-dom@12.38.0: - resolution: {integrity: sha512-pdkHLD8QYRp8VfiNLb8xIBJis1byQ9gPT3Jnh2jqfFtAsWUA3dEepDlsWe/xMpO8McV+VdpKVcp+E+TGJEtOoA==} + motion-dom@12.40.0: + resolution: {integrity: sha512-HxU3ZaBwNPVQUBQf1xxgq+7JrPNZvjLVxgbpEZL7RrWJnsxOf0/OM+yrHG9ogLQ31Do/r57Oz2gQWPK+6q62mg==} - motion-utils@12.36.0: - resolution: {integrity: sha512-eHWisygbiwVvf6PZ1vhaHCLamvkSbPIeAYxWUuL3a2PD/TROgE7FvfHWTIH4vMl798QLfMw15nRqIaRDXTlYRg==} + motion-utils@12.39.0: + resolution: {integrity: sha512-8nadJAJjTtqRkmRF36FoJTrywK9nnFmnPwnSMyxaOCU7GDjN9RTMJIxx9De8ErM+vpPhMccr/6fo5WciyQLnMQ==} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} @@ -2381,8 +2377,8 @@ packages: mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} - nanoid@3.3.11: - resolution: {integrity: sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==} + nanoid@3.3.12: + resolution: {integrity: sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true @@ -2425,8 +2421,9 @@ packages: resolution: {integrity: sha512-pyFS63ptit/P5WqUkt+UUfe+4oevH+bFeIiPPdfb0pFeYEu/1ELnJu5l+5EcTKYL5M7zaAa7S8ddywgXypqKCw==} engines: {node: '>= 0.4'} - node-releases@2.0.37: - resolution: {integrity: sha512-1h5gKZCF+pO/o3Iqt5Jp7wc9rH3eJJ0+nh/CIoiRwjRxde/hAHyLPXYN4V3CqKAbiZPSeJFSWHmJsbkicta0Eg==} + node-releases@2.0.47: + resolution: {integrity: sha512-Uzmd6LXpouKo8EUK68IjH4+E01w/hXyV3R3g/geCJo+rXLNfh1xucB+LOzYEOQPSiUK3h/xZf0cQGcSsmyL2Og==} + engines: {node: '>=18'} normalize-path@3.0.0: resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==} @@ -2536,20 +2533,20 @@ packages: resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==} engines: {node: '>=14.0.0'} peerDependencies: - postcss: ^8.0.0 + postcss: '>=8.5.10' postcss-js@4.1.0: resolution: {integrity: sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==} engines: {node: ^12 || ^14 || >= 16} peerDependencies: - postcss: ^8.4.21 + postcss: '>=8.5.10' 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' + postcss: '>=8.5.10' tsx: ^4.8.1 yaml: ^2.4.2 peerDependenciesMeta: @@ -2566,7 +2563,7 @@ packages: resolution: {integrity: sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==} engines: {node: '>=12.0'} peerDependencies: - postcss: ^8.2.14 + postcss: '>=8.5.10' postcss-selector-parser@6.0.10: resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==} @@ -2579,27 +2576,19 @@ packages: postcss-value-parser@4.2.0: resolution: {integrity: sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==} - postcss@8.4.31: - resolution: {integrity: sha512-PS08Iboia9mts/2ygV3eLpY5ghnUcfLV/EXTOW1E2qYxJKGGBUtNjN76FYHnMs36RmARn41bC0AZmn+rR0OVpQ==} - engines: {node: ^10 || ^12 || >=14} - - postcss@8.5.10: - resolution: {integrity: sha512-pMMHxBOZKFU6HgAZ4eyGnwXF/EvPGGqUr0MnZ5+99485wwW41kW91A4LOGxSHhgugZmSChL5AlElNdwlNgcnLQ==} + postcss@8.5.15: + resolution: {integrity: sha512-FfR8sjd4em2T6fb3I2MwAJU7HWVMr9zba+enmQeeWFfCbm+UOC/0X4DS8XtpUTMwWMGbjKYP7xjfNekzyGmB3A==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} - prettier@3.8.3: - resolution: {integrity: sha512-7igPTM53cGHMW8xWuVTydi2KO233VFiTNyF5hLJqpilHfmn8C8gPf+PS7dUT64YcXFbiMGZxS9pCSxL/Dxm/Jw==} + prettier@3.8.4: + resolution: {integrity: sha512-N2MylSdi48+5N/6S5j+maeHbUSIzzZ5uOcX5Hm4QpV8Dkb1HFjfAKTKX6yNPJQD9AhcT3ifHNB66tWTTJDi11Q==} engines: {node: '>=14'} hasBin: true - prismjs@1.27.0: - resolution: {integrity: sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==} - engines: {node: '>=6'} - prismjs@1.30.0: resolution: {integrity: sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==} engines: {node: '>=6'} @@ -2632,13 +2621,13 @@ packages: date-fns: ^2.28.0 || ^3.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 - react-dom@19.2.5: - resolution: {integrity: sha512-J5bAZz+DXMMwW/wV3xzKke59Af6CHY7G4uYLN1OvBcKEsWOs4pQExj86BBKamxl/Ik5bx9whOrvBlSDfWzgSag==} + react-dom@19.2.7: + resolution: {integrity: sha512-t0BRVXvbiE/o20Hfw669rLbMCDWtYZLvmJigy2f0MxsXF+71pxhR3xOkspmsO8h3ZlNzyibAmtCa3l4lYKk6gQ==} peerDependencies: - react: ^19.2.5 + react: ^19.2.7 - react-hook-form@7.72.1: - resolution: {integrity: sha512-RhwBoy2ygeVZje+C+bwJ8g0NjTdBmDlJvAUHTxRjTmSUKPYsKfMphkS2sgEMotsY03bP358yEYlnUeZy//D9Ig==} + react-hook-form@7.78.0: + resolution: {integrity: sha512-EEZqc+N23moyzTlz61Pj+JvcXo76ICkpfOZo8JZw+sM4+wLQGh6nI2Ms+PdMOYNluFu0ghlM7B8mCzhRYtJCnA==} engines: {node: '>=18.0.0'} peerDependencies: react: ^16.8.0 || ^17 || ^18 || ^19 @@ -2649,8 +2638,8 @@ packages: react-is@18.3.1: resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} - react-redux@9.2.0: - resolution: {integrity: sha512-ROY9fvHhwOD9ySfrF0wmvu//bKCQ6AeZZq1nJNtbDC+kk5DuSuNX/n6YWYF/SYy7bSba4D4FSz8DJeKY/S/r+g==} + react-redux@9.3.0: + resolution: {integrity: sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==} peerDependencies: '@types/react': ^18.2.25 || ^19 react: ^18.0 || ^19 @@ -2708,8 +2697,8 @@ packages: react: '>=16.6.0' react-dom: '>=16.6.0' - react@19.2.5: - resolution: {integrity: sha512-llUJLzz1zTUBrskt2pwZgLq59AemifIftw4aB7JxOqf1HY2FDaGDxgwpAPVzHU1kdWabH7FauP4i1oEeer2WCA==} + react@19.2.7: + resolution: {integrity: sha512-HNe9WslTbXmFK8o8cmwgAeJFSBvt1bPdHCVKtaaV+WlAN36mpT4hcRpwbf3fY56ar2oIXzsBpOAiIRHAdY0OlQ==} engines: {node: '>=0.10.0'} read-cache@1.0.0: @@ -2749,8 +2738,8 @@ packages: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} - reselect@5.1.1: - resolution: {integrity: sha512-K/BG6eIky/SBpzfHZv/dd+9JBFiS4SWV7FIujVyJRux6e45+73RaUHXLmIR1f7WOMaQ0U1km6qwklRQxpJJY0w==} + reselect@5.2.0: + resolution: {integrity: sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==} resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} @@ -2764,8 +2753,8 @@ packages: engines: {node: '>= 0.4'} hasBin: true - resolve@2.0.0-next.6: - resolution: {integrity: sha512-3JmVl5hMGtJ3kMmB3zi3DL25KfkCEyy3Tw7Gmw7z5w8M9WlwoPFnIvwChzu1+cF3iaK3sp18hhPz8ANeimdJfA==} + resolve@2.0.0-next.7: + resolution: {integrity: sha512-tqt+NBWwyaMgw3zDsnygx4CByWjQEJHOPMdslYhppaQSJUtL/D4JO9CcBBlhPoI8lz9oJIDXkwXfhF4aWqP8xQ==} engines: {node: '>= 0.4'} hasBin: true @@ -2781,8 +2770,8 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} - safe-array-concat@1.1.3: - resolution: {integrity: sha512-AURm5f0jYEOydBj7VQlVvDrjeFgthDdEF5H1dP+6mNpoXOMo1quQqJ4wvJDyRZ9+pO3kGWoOdmV08cSv2aJV6Q==} + safe-array-concat@1.1.4: + resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} safe-push-apply@1.0.0: @@ -2800,8 +2789,8 @@ packages: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true - semver@7.7.4: - resolution: {integrity: sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==} + semver@7.8.4: + resolution: {integrity: sha512-rUCObTnP32Q08R2uuIrt7r9PlEonuTmtuXYcW6s5kjdlj3xbnwe+21yXptAUYcMAABLkYYTtnmzb3w3EDZfueA==} engines: {node: '>=10'} hasBin: true @@ -2841,8 +2830,8 @@ packages: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} - side-channel@1.1.0: - resolution: {integrity: sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==} + side-channel@1.1.1: + resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} sonner@1.7.4: @@ -2876,12 +2865,12 @@ packages: string.prototype.repeat@1.0.0: resolution: {integrity: sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w==} - string.prototype.trim@1.2.10: - resolution: {integrity: sha512-Rs66F0P/1kedk5lyYyH9uBzuiI/kNRmwJAR9quK6VOtIpZ2G+hMZd+HQbbv25MgCA6gEffoMZYxlTod4WcdrKA==} + string.prototype.trim@1.2.11: + resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} engines: {node: '>= 0.4'} - string.prototype.trimend@1.0.9: - resolution: {integrity: sha512-G7Ok5C6E/j4SGfyLCloXTrngQIQU3PWtXGst3yM7Bea9FRURf1S42ZHlZZtsNque2FN2PoUhfZXYLNWwEr4dLQ==} + string.prototype.trimend@1.0.10: + resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: @@ -2952,8 +2941,8 @@ packages: tiny-invariant@1.3.3: resolution: {integrity: sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==} - tinyglobby@0.2.16: - resolution: {integrity: sha512-pn99VhoACYR8nFHhxqix+uvsbXineAasWm5ojXoN8xEwK5Kd3/TrhNn1wByuD52UxWRLy8pu+kRMniEi6Eq9Zg==} + tinyglobby@0.2.17: + resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} to-regex-range@5.0.1: @@ -2998,8 +2987,8 @@ packages: resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} engines: {node: '>= 0.4'} - typed-array-length@1.0.7: - resolution: {integrity: sha512-3KS2b+kL7fsuk/eJZ7EQdnEmQoaho/r6KUef7hxvltNA5DR8NAUM+8wJMbJyZ4G9/7i3v5zPBIMN5aybAh2/Jg==} + typed-array-length@1.0.8: + resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} engines: {node: '>= 0.4'} typescript@5.9.3: @@ -3014,8 +3003,8 @@ packages: undici-types@6.19.8: resolution: {integrity: sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==} - unrs-resolver@1.11.1: - resolution: {integrity: sha512-bSjt9pjaEBnNiGgc9rUiHGKv5l4/TGzDmYw3RhnkJGtLhbnnA/5qJj7x3dNDCRx/PJxu774LlH8lCOlB4hEfKg==} + unrs-resolver@1.12.2: + resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} @@ -3060,9 +3049,8 @@ packages: util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} - uuid@9.0.1: - resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} - deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028). + uuid@11.1.1: + resolution: {integrity: sha512-vIYxrBCC/N/K+Js3qSN88go7kIfNPssr/hHCesKCQNAjmgvYS2oqr69kIufEG+O4+PfezOH4EbIeHCfFov8ZgQ==} hasBin: true vaul@0.9.9: @@ -3086,8 +3074,8 @@ packages: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} - which-typed-array@1.1.20: - resolution: {integrity: sha512-LYfpUkmqwl0h9A2HL09Mms427Q1RZWuOHsukfVcKRq9q95iQxdw0ix1JQrqbcDR9PH1QDwf5Qo8OZb5lksZ8Xg==} + which-typed-array@1.1.22: + resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} engines: {node: '>= 0.4'} which@2.0.2: @@ -3117,15 +3105,20 @@ snapshots: '@alloc/quick-lru@5.2.0': {} - '@babel/runtime@7.29.2': {} + '@babel/runtime@7.29.7': {} - '@emnapi/core@1.9.2': + '@emnapi/core@1.10.0': dependencies: '@emnapi/wasi-threads': 1.2.1 tslib: 2.8.1 optional: true - '@emnapi/runtime@1.9.2': + '@emnapi/runtime@1.10.0': + dependencies: + tslib: 2.8.1 + optional: true + + '@emnapi/runtime@1.11.0': dependencies: tslib: 2.8.1 optional: true @@ -3144,13 +3137,13 @@ snapshots: '@eslint/eslintrc@2.1.4': dependencies: - ajv: 6.14.0 + ajv: 6.15.0 debug: 4.4.3 espree: 9.6.1 globals: 13.24.0 ignore: 5.3.2 import-fresh: 3.3.1 - js-yaml: 4.1.1 + js-yaml: 4.2.0 minimatch: 3.1.5 strip-json-comments: 3.1.1 transitivePeerDependencies: @@ -3167,17 +3160,17 @@ snapshots: '@floating-ui/core': 1.7.5 '@floating-ui/utils': 0.2.11 - '@floating-ui/react-dom@2.1.8(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@floating-ui/react-dom@2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@floating-ui/dom': 1.7.6 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) '@floating-ui/utils@0.2.11': {} - '@hookform/resolvers@3.10.0(react-hook-form@7.72.1(react@19.2.5))': + '@hookform/resolvers@3.10.0(react-hook-form@7.78.0(react@19.2.7))': dependencies: - react-hook-form: 7.72.1(react@19.2.5) + react-hook-form: 7.78.0(react@19.2.7) '@humanwhocodes/config-array@0.13.0': dependencies: @@ -3276,7 +3269,7 @@ snapshots: '@img/sharp-wasm32@0.34.5': dependencies: - '@emnapi/runtime': 1.9.2 + '@emnapi/runtime': 1.11.0 optional: true '@img/sharp-win32-arm64@0.34.5': @@ -3302,11 +3295,11 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 - '@napi-rs/wasm-runtime@0.2.12': + '@napi-rs/wasm-runtime@1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: - '@emnapi/core': 1.9.2 - '@emnapi/runtime': 1.9.2 - '@tybys/wasm-util': 0.10.1 + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@tybys/wasm-util': 0.10.2 optional: true '@next/env@15.5.18': {} @@ -3353,627 +3346,604 @@ snapshots: '@nolyfill/is-core-module@1.0.39': {} - '@radix-ui/number@1.1.1': {} + '@radix-ui/number@1.1.2': {} - '@radix-ui/primitive@1.1.3': {} + '@radix-ui/primitive@1.1.4': {} - '@radix-ui/react-accordion@1.2.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-accordion@1.2.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collapsible': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collapsible': 1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-alert-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-alert-dialog@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dialog': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-arrow@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-arrow@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-avatar@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-avatar@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-context': 1.1.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-is-hydrated': 0.1.0(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-is-hydrated': 0.1.1(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-checkbox@1.3.4(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collapsible@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-collapsible@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-collection@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-collection@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-compose-refs@1.1.2(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-compose-refs@1.1.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-context@1.1.2(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-context@1.1.4(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-context@1.1.3(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-dialog@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - react: 19.2.5 - optionalDependencies: - '@types/react': 19.2.14 - - '@radix-ui/react-dialog@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) aria-hidden: 1.2.6 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-direction@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-direction@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-dismissable-layer@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-dismissable-layer@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-escape-keydown': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-escape-keydown': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-dropdown-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-dropdown-menu@2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-menu': 2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-focus-guards@1.1.3(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-focus-guards@1.1.4(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-focus-scope@1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-focus-scope@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-hover-card@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-hover-card@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-icons@1.3.2(react@19.2.5)': + '@radix-ui/react-icons@1.3.2(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 - '@radix-ui/react-id@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-id@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-label@2.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-label@2.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-menu@2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-menu@2.1.17(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) aria-hidden: 1.2.6 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-popover@1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-popover@1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) aria-hidden: 1.2.6 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-popper@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-popper@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@floating-ui/react-dom': 2.1.8(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-arrow': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-rect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/rect': 1.1.1 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@floating-ui/react-dom': 2.1.8(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-arrow': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-rect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/rect': 1.1.2 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-portal@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-portal@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-presence@1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-presence@1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-primitive@2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-primitive@2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-primitive@2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-progress@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-progress@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-radio-group@1.4.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-context': 1.1.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-radio-group@1.3.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-roving-focus@1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-roving-focus@1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-scroll-area@1.2.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-scroll-area@1.2.10(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-select@2.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) - - '@radix-ui/react-select@2.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-focus-guards': 1.1.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-focus-scope': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-focus-guards': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-focus-scope': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) aria-hidden: 1.2.6 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - react-remove-scroll: 2.7.2(@types/react@19.2.14)(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-remove-scroll: 2.7.2(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-separator@1.1.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-separator@1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-slider@1.3.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-slider@1.4.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/number': 1.1.1 - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/number': 1.1.2 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-slot@1.2.3(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-slot@1.2.5(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-slot@1.2.4(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-switch@1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-previous': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-size': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-switch@1.2.6(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-tabs@1.1.14(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-previous': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-size': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-direction': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-roving-focus': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-tabs@1.1.13(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-toast@1.2.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-direction': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-roving-focus': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-collection': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-toast@1.2.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-tooltip@1.2.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-collection': 1.1.7(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/primitive': 1.1.4 + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-context': 1.1.4(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dismissable-layer': 1.1.12(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-popper': 1.3.0(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-portal': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-presence': 1.1.6(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-slot': 1.2.5(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-controllable-state': 1.2.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-visually-hidden': 1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-tooltip@1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@radix-ui/react-use-callback-ref@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/primitive': 1.1.3 - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-context': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-dismissable-layer': 1.1.11(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-popper': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-portal': 1.1.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-presence': 1.1.5(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-slot': 1.2.3(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-visually-hidden': 1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@types/react': 19.2.17 - '@radix-ui/react-use-callback-ref@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-controllable-state@1.2.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + '@radix-ui/react-use-effect-event': 0.0.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-controllable-state@1.2.2(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-effect-event@0.0.3(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-use-effect-event': 0.0.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-effect-event@0.0.2(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-escape-keydown@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + '@radix-ui/react-use-callback-ref': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-escape-keydown@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-is-hydrated@0.1.1(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/react-use-callback-ref': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-is-hydrated@0.1.0(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-layout-effect@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 - use-sync-external-store: 1.6.0(react@19.2.5) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-layout-effect@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-previous@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-previous@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-rect@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - react: 19.2.5 + '@radix-ui/rect': 1.1.2 + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-rect@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-use-size@1.1.2(@types/react@19.2.17)(react@19.2.7)': dependencies: - '@radix-ui/rect': 1.1.1 - react: 19.2.5 + '@radix-ui/react-use-layout-effect': 1.1.2(@types/react@19.2.17)(react@19.2.7) + react: 19.2.7 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@radix-ui/react-use-size@1.1.1(@types/react@19.2.14)(react@19.2.5)': + '@radix-ui/react-visually-hidden@1.2.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: - '@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.2.14)(react@19.2.5) - react: 19.2.5 + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 + '@types/react-dom': 19.2.3(@types/react@19.2.17) - '@radix-ui/react-visually-hidden@1.2.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': - dependencies: - '@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - optionalDependencies: - '@types/react': 19.2.14 - '@types/react-dom': 19.2.3(@types/react@19.2.14) + '@radix-ui/rect@1.1.2': {} - '@radix-ui/rect@1.1.1': {} - - '@reduxjs/toolkit@2.11.2(react-redux@9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1))(react@19.2.5)': + '@reduxjs/toolkit@2.12.0(react-redux@9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1))(react@19.2.7)': dependencies: '@standard-schema/spec': 1.1.0 '@standard-schema/utils': 0.3.0 - immer: 11.1.4 + immer: 11.1.8 redux: 5.0.1 redux-thunk: 3.1.0(redux@5.0.1) - reselect: 5.1.1 + reselect: 5.2.0 optionalDependencies: - react: 19.2.5 - react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1) + react: 19.2.7 + react-redux: 9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1) '@rtsao/scc@1.1.0': {} @@ -3987,20 +3957,20 @@ snapshots: dependencies: tslib: 2.8.1 - '@tailwindcss/typography@0.5.19(tailwindcss@3.4.19)': + '@tailwindcss/typography@0.5.20(tailwindcss@3.4.19)': dependencies: postcss-selector-parser: 6.0.10 tailwindcss: 3.4.19 - '@tanstack/react-table@8.21.3(react-dom@19.2.5(react@19.2.5))(react@19.2.5)': + '@tanstack/react-table@8.21.3(react-dom@19.2.7(react@19.2.7))(react@19.2.7)': dependencies: '@tanstack/table-core': 8.21.3 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) '@tanstack/table-core@8.21.3': {} - '@tybys/wasm-util@0.10.1': + '@tybys/wasm-util@0.10.2': dependencies: tslib: 2.8.1 optional: true @@ -4043,17 +4013,17 @@ snapshots: '@types/react-copy-to-clipboard@5.0.7': dependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@types/react-dom@19.2.3(@types/react@19.2.14)': + '@types/react-dom@19.2.3(@types/react@19.2.17)': dependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 '@types/react-syntax-highlighter@15.5.13': dependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - '@types/react@19.2.14': + '@types/react@19.2.17': dependencies: csstype: 3.2.3 @@ -4063,14 +4033,14 @@ snapshots: '@types/uuid@9.0.8': {} - '@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/eslint-plugin@8.61.0(@typescript-eslint/parser@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.58.2(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/scope-manager': 8.58.2 - '@typescript-eslint/type-utils': 8.58.2(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/utils': 8.58.2(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.58.2 + '@typescript-eslint/parser': 8.61.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.61.0 + '@typescript-eslint/type-utils': 8.61.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/utils': 8.61.0(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.61.0 eslint: 8.57.1 ignore: 7.0.5 natural-compare: 1.4.0 @@ -4079,41 +4049,41 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/parser@8.61.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: - '@typescript-eslint/scope-manager': 8.58.2 - '@typescript-eslint/types': 8.58.2 - '@typescript-eslint/typescript-estree': 8.58.2(typescript@5.9.3) - '@typescript-eslint/visitor-keys': 8.58.2 + '@typescript-eslint/scope-manager': 8.61.0 + '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/typescript-estree': 8.61.0(typescript@5.9.3) + '@typescript-eslint/visitor-keys': 8.61.0 debug: 4.4.3 eslint: 8.57.1 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/project-service@8.58.2(typescript@5.9.3)': + '@typescript-eslint/project-service@8.61.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/tsconfig-utils': 8.58.2(typescript@5.9.3) - '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@5.9.3) + '@typescript-eslint/types': 8.61.0 debug: 4.4.3 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/scope-manager@8.58.2': + '@typescript-eslint/scope-manager@8.61.0': dependencies: - '@typescript-eslint/types': 8.58.2 - '@typescript-eslint/visitor-keys': 8.58.2 + '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/visitor-keys': 8.61.0 - '@typescript-eslint/tsconfig-utils@8.58.2(typescript@5.9.3)': + '@typescript-eslint/tsconfig-utils@8.61.0(typescript@5.9.3)': dependencies: typescript: 5.9.3 - '@typescript-eslint/type-utils@8.58.2(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/type-utils@8.61.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: - '@typescript-eslint/types': 8.58.2 - '@typescript-eslint/typescript-estree': 8.58.2(typescript@5.9.3) - '@typescript-eslint/utils': 8.58.2(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/typescript-estree': 8.61.0(typescript@5.9.3) + '@typescript-eslint/utils': 8.61.0(eslint@8.57.1)(typescript@5.9.3) debug: 4.4.3 eslint: 8.57.1 ts-api-utils: 2.5.0(typescript@5.9.3) @@ -4121,98 +4091,109 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/types@8.58.2': {} + '@typescript-eslint/types@8.61.0': {} - '@typescript-eslint/typescript-estree@8.58.2(typescript@5.9.3)': + '@typescript-eslint/typescript-estree@8.61.0(typescript@5.9.3)': dependencies: - '@typescript-eslint/project-service': 8.58.2(typescript@5.9.3) - '@typescript-eslint/tsconfig-utils': 8.58.2(typescript@5.9.3) - '@typescript-eslint/types': 8.58.2 - '@typescript-eslint/visitor-keys': 8.58.2 + '@typescript-eslint/project-service': 8.61.0(typescript@5.9.3) + '@typescript-eslint/tsconfig-utils': 8.61.0(typescript@5.9.3) + '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/visitor-keys': 8.61.0 debug: 4.4.3 minimatch: 10.2.5 - semver: 7.7.4 - tinyglobby: 0.2.16 + semver: 7.8.4 + tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@5.9.3) typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.58.2(eslint@8.57.1)(typescript@5.9.3)': + '@typescript-eslint/utils@8.61.0(eslint@8.57.1)(typescript@5.9.3)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@8.57.1) - '@typescript-eslint/scope-manager': 8.58.2 - '@typescript-eslint/types': 8.58.2 - '@typescript-eslint/typescript-estree': 8.58.2(typescript@5.9.3) + '@typescript-eslint/scope-manager': 8.61.0 + '@typescript-eslint/types': 8.61.0 + '@typescript-eslint/typescript-estree': 8.61.0(typescript@5.9.3) eslint: 8.57.1 typescript: 5.9.3 transitivePeerDependencies: - supports-color - '@typescript-eslint/visitor-keys@8.58.2': + '@typescript-eslint/visitor-keys@8.61.0': dependencies: - '@typescript-eslint/types': 8.58.2 + '@typescript-eslint/types': 8.61.0 eslint-visitor-keys: 5.0.1 - '@ungap/structured-clone@1.3.0': {} + '@ungap/structured-clone@1.3.1': {} - '@unrs/resolver-binding-android-arm-eabi@1.11.1': + '@unrs/resolver-binding-android-arm-eabi@1.12.2': optional: true - '@unrs/resolver-binding-android-arm64@1.11.1': + '@unrs/resolver-binding-android-arm64@1.12.2': optional: true - '@unrs/resolver-binding-darwin-arm64@1.11.1': + '@unrs/resolver-binding-darwin-arm64@1.12.2': optional: true - '@unrs/resolver-binding-darwin-x64@1.11.1': + '@unrs/resolver-binding-darwin-x64@1.12.2': optional: true - '@unrs/resolver-binding-freebsd-x64@1.11.1': + '@unrs/resolver-binding-freebsd-x64@1.12.2': optional: true - '@unrs/resolver-binding-linux-arm-gnueabihf@1.11.1': + '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': optional: true - '@unrs/resolver-binding-linux-arm-musleabihf@1.11.1': + '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': optional: true - '@unrs/resolver-binding-linux-arm64-gnu@1.11.1': + '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': optional: true - '@unrs/resolver-binding-linux-arm64-musl@1.11.1': + '@unrs/resolver-binding-linux-arm64-musl@1.12.2': optional: true - '@unrs/resolver-binding-linux-ppc64-gnu@1.11.1': + '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': optional: true - '@unrs/resolver-binding-linux-riscv64-gnu@1.11.1': + '@unrs/resolver-binding-linux-loong64-musl@1.12.2': optional: true - '@unrs/resolver-binding-linux-riscv64-musl@1.11.1': + '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': optional: true - '@unrs/resolver-binding-linux-s390x-gnu@1.11.1': + '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': optional: true - '@unrs/resolver-binding-linux-x64-gnu@1.11.1': + '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': optional: true - '@unrs/resolver-binding-linux-x64-musl@1.11.1': + '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': optional: true - '@unrs/resolver-binding-wasm32-wasi@1.11.1': + '@unrs/resolver-binding-linux-x64-gnu@1.12.2': + optional: true + + '@unrs/resolver-binding-linux-x64-musl@1.12.2': + optional: true + + '@unrs/resolver-binding-openharmony-arm64@1.12.2': + optional: true + + '@unrs/resolver-binding-wasm32-wasi@1.12.2': dependencies: - '@napi-rs/wasm-runtime': 0.2.12 + '@emnapi/core': 1.10.0 + '@emnapi/runtime': 1.10.0 + '@napi-rs/wasm-runtime': 1.1.5(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true - '@unrs/resolver-binding-win32-arm64-msvc@1.11.1': + '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': optional: true - '@unrs/resolver-binding-win32-ia32-msvc@1.11.1': + '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': optional: true - '@unrs/resolver-binding-win32-x64-msvc@1.11.1': + '@unrs/resolver-binding-win32-x64-msvc@1.12.2': optional: true acorn-jsx@5.3.2(acorn@8.16.0): @@ -4227,7 +4208,7 @@ snapshots: transitivePeerDependencies: - supports-color - ajv@6.14.0: + ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 @@ -4268,7 +4249,7 @@ snapshots: call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.24.2 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 is-string: 1.1.1 math-intrinsics: 1.1.0 @@ -4279,7 +4260,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.24.2 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 es-shim-unscopables: 1.1.0 array.prototype.findlastindex@1.2.6: @@ -4289,7 +4270,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.24.2 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 es-shim-unscopables: 1.1.0 array.prototype.flat@1.3.3: @@ -4330,22 +4311,22 @@ snapshots: asynckit@0.4.0: {} - autoprefixer@10.5.0(postcss@8.5.10): + autoprefixer@10.5.0(postcss@8.5.15): dependencies: browserslist: 4.28.2 - caniuse-lite: 1.0.30001788 + caniuse-lite: 1.0.30001797 fraction.js: 5.3.4 picocolors: 1.1.1 - postcss: 8.5.10 + postcss: 8.5.15 postcss-value-parser: 4.2.0 available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 - axe-core@4.11.3: {} + axe-core@4.12.1: {} - axios@1.16.1: + axios@1.17.0: dependencies: follow-redirects: 1.16.0 form-data: 4.0.5 @@ -4361,16 +4342,16 @@ snapshots: balanced-match@4.0.4: {} - baseline-browser-mapping@2.10.19: {} + baseline-browser-mapping@2.10.35: {} binary-extensions@2.3.0: {} - brace-expansion@1.1.14: + brace-expansion@1.1.15: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 - brace-expansion@5.0.5: + brace-expansion@5.0.6: dependencies: balanced-match: 4.0.4 @@ -4380,10 +4361,10 @@ snapshots: browserslist@4.28.2: dependencies: - baseline-browser-mapping: 2.10.19 - caniuse-lite: 1.0.30001788 - electron-to-chromium: 1.5.337 - node-releases: 2.0.37 + baseline-browser-mapping: 2.10.35 + caniuse-lite: 1.0.30001797 + electron-to-chromium: 1.5.371 + node-releases: 2.0.47 update-browserslist-db: 1.2.3(browserslist@4.28.2) call-bind-apply-helpers@1.0.2: @@ -4407,7 +4388,7 @@ snapshots: camelcase-css@2.0.1: {} - caniuse-lite@1.0.30001788: {} + caniuse-lite@1.0.30001797: {} chalk@4.1.2: dependencies: @@ -4440,14 +4421,14 @@ snapshots: clsx@2.1.1: {} - cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + cmdk@1.1.1(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - '@radix-ui/react-id': 1.1.1(@types/react@19.2.14)(react@19.2.5) - '@radix-ui/react-primitive': 2.1.4(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-dialog': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + '@radix-ui/react-id': 1.1.2(@types/react@19.2.17)(react@19.2.7) + '@radix-ui/react-primitive': 2.1.5(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -4540,7 +4521,7 @@ snapshots: es-errors: 1.3.0 is-data-view: 1.0.2 - date-fns@4.1.0: {} + date-fns@4.4.0: {} debug@3.2.7: dependencies: @@ -4587,7 +4568,7 @@ snapshots: dom-helpers@5.2.1: dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 csstype: 3.2.3 dunder-proto@1.0.1: @@ -4596,7 +4577,7 @@ snapshots: es-errors: 1.3.0 gopd: 1.2.0 - electron-to-chromium@1.5.337: {} + electron-to-chromium@1.5.371: {} emoji-regex@9.2.2: {} @@ -4612,7 +4593,7 @@ snapshots: data-view-byte-offset: 1.0.1 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 es-set-tostringtag: 2.1.0 es-to-primitive: 1.3.0 function.prototype.name: 1.1.8 @@ -4624,7 +4605,7 @@ snapshots: has-property-descriptors: 1.0.2 has-proto: 1.2.0 has-symbols: 1.1.0 - hasown: 2.0.2 + hasown: 2.0.4 internal-slot: 1.1.0 is-array-buffer: 3.0.5 is-callable: 1.2.7 @@ -4642,26 +4623,26 @@ snapshots: object.assign: 4.1.7 own-keys: 1.0.1 regexp.prototype.flags: 1.5.4 - safe-array-concat: 1.1.3 + safe-array-concat: 1.1.4 safe-push-apply: 1.0.0 safe-regex-test: 1.1.0 set-proto: 1.0.0 stop-iteration-iterator: 1.1.0 - string.prototype.trim: 1.2.10 - string.prototype.trimend: 1.0.9 + string.prototype.trim: 1.2.11 + string.prototype.trimend: 1.0.10 string.prototype.trimstart: 1.0.8 typed-array-buffer: 1.0.3 typed-array-byte-length: 1.0.3 typed-array-byte-offset: 1.0.4 - typed-array-length: 1.0.7 + typed-array-length: 1.0.8 unbox-primitive: 1.1.0 - which-typed-array: 1.1.20 + which-typed-array: 1.1.22 es-define-property@1.0.1: {} es-errors@1.3.0: {} - es-iterator-helpers@1.3.2: + es-iterator-helpers@1.3.3: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 @@ -4680,7 +4661,7 @@ snapshots: iterator.prototype: 1.1.5 math-intrinsics: 1.1.0 - es-object-atoms@1.1.1: + es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 @@ -4689,11 +4670,11 @@ snapshots: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 es-shim-unscopables@1.1.0: dependencies: - hasown: 2.0.2 + hasown: 2.0.4 es-to-primitive@1.3.0: dependencies: @@ -4709,12 +4690,12 @@ snapshots: dependencies: '@next/eslint-plugin-next': 15.5.18 '@rushstack/eslint-patch': 1.16.1 - '@typescript-eslint/eslint-plugin': 8.58.2(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) - '@typescript-eslint/parser': 8.58.2(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/eslint-plugin': 8.61.0(@typescript-eslint/parser@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.61.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.10 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) eslint-plugin-jsx-a11y: 6.10.2(eslint@8.57.1) eslint-plugin-react: 7.37.5(eslint@8.57.1) eslint-plugin-react-hooks: 5.2.0(eslint@8.57.1) @@ -4728,8 +4709,8 @@ snapshots: eslint-import-resolver-node@0.3.10: dependencies: debug: 3.2.7 - is-core-module: 2.16.1 - resolve: 2.0.0-next.6 + is-core-module: 2.16.2 + resolve: 2.0.0-next.7 transitivePeerDependencies: - supports-color @@ -4741,25 +4722,25 @@ snapshots: get-tsconfig: 4.14.0 is-bun-module: 2.0.0 stable-hash: 0.0.5 - tinyglobby: 0.2.16 - unrs-resolver: 1.11.1 + tinyglobby: 0.2.17 + unrs-resolver: 1.12.2 optionalDependencies: - eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-module-utils@2.12.1(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-module-utils@2.13.0(@typescript-eslint/parser@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: debug: 3.2.7 optionalDependencies: - '@typescript-eslint/parser': 8.58.2(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.61.0(eslint@8.57.1)(typescript@5.9.3) eslint: 8.57.1 eslint-import-resolver-node: 0.3.10 eslint-import-resolver-typescript: 3.10.1(eslint-plugin-import@2.32.0)(eslint@8.57.1) transitivePeerDependencies: - supports-color - eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): + eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1): dependencies: '@rtsao/scc': 1.1.0 array-includes: 3.1.9 @@ -4770,19 +4751,19 @@ snapshots: doctrine: 2.1.0 eslint: 8.57.1 eslint-import-resolver-node: 0.3.10 - eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.58.2(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) - hasown: 2.0.2 - is-core-module: 2.16.1 + eslint-module-utils: 2.13.0(@typescript-eslint/parser@8.61.0(eslint@8.57.1)(typescript@5.9.3))(eslint-import-resolver-node@0.3.10)(eslint-import-resolver-typescript@3.10.1)(eslint@8.57.1) + hasown: 2.0.4 + is-core-module: 2.16.2 is-glob: 4.0.3 minimatch: 3.1.5 object.fromentries: 2.0.8 object.groupby: 1.0.3 object.values: 1.2.1 semver: 6.3.1 - string.prototype.trimend: 1.0.9 + string.prototype.trimend: 1.0.10 tsconfig-paths: 3.15.0 optionalDependencies: - '@typescript-eslint/parser': 8.58.2(eslint@8.57.1)(typescript@5.9.3) + '@typescript-eslint/parser': 8.61.0(eslint@8.57.1)(typescript@5.9.3) transitivePeerDependencies: - eslint-import-resolver-typescript - eslint-import-resolver-webpack @@ -4794,12 +4775,12 @@ snapshots: array-includes: 3.1.9 array.prototype.flatmap: 1.3.3 ast-types-flow: 0.0.8 - axe-core: 4.11.3 + axe-core: 4.12.1 axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 eslint: 8.57.1 - hasown: 2.0.2 + hasown: 2.0.4 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 minimatch: 3.1.5 @@ -4818,17 +4799,17 @@ snapshots: array.prototype.flatmap: 1.3.3 array.prototype.tosorted: 1.1.4 doctrine: 2.1.0 - es-iterator-helpers: 1.3.2 + es-iterator-helpers: 1.3.3 eslint: 8.57.1 estraverse: 5.3.0 - hasown: 2.0.2 + hasown: 2.0.4 jsx-ast-utils: 3.3.5 minimatch: 3.1.5 object.entries: 1.1.9 object.fromentries: 2.0.8 object.values: 1.2.1 prop-types: 15.8.1 - resolve: 2.0.0-next.6 + resolve: 2.0.0-next.7 semver: 6.3.1 string.prototype.matchall: 4.0.12 string.prototype.repeat: 1.0.0 @@ -4851,8 +4832,8 @@ snapshots: '@humanwhocodes/config-array': 0.13.0 '@humanwhocodes/module-importer': 1.0.1 '@nodelib/fs.walk': 1.2.8 - '@ungap/structured-clone': 1.3.0 - ajv: 6.14.0 + '@ungap/structured-clone': 1.3.1 + ajv: 6.15.0 chalk: 4.1.2 cross-spawn: 7.0.6 debug: 4.4.3 @@ -4873,7 +4854,7 @@ snapshots: imurmurhash: 0.1.4 is-glob: 4.0.3 is-path-inside: 3.0.3 - js-yaml: 4.1.1 + js-yaml: 4.2.0 json-stable-stringify-without-jsonify: 1.0.1 levn: 0.4.1 lodash.merge: 4.6.2 @@ -4973,21 +4954,21 @@ snapshots: asynckit: 0.4.0 combined-stream: 1.0.8 es-set-tostringtag: 2.1.0 - hasown: 2.0.2 + hasown: 2.0.4 mime-types: 2.1.35 format@0.2.2: {} fraction.js@5.3.4: {} - framer-motion@12.38.0(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + framer-motion@12.40.0(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - motion-dom: 12.38.0 - motion-utils: 12.36.0 + motion-dom: 12.40.0 + motion-utils: 12.39.0 tslib: 2.8.1 optionalDependencies: - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) fs.realpath@1.0.0: {} @@ -5002,7 +4983,7 @@ snapshots: call-bound: 1.0.4 define-properties: 1.2.1 functions-have-names: 1.2.3 - hasown: 2.0.2 + hasown: 2.0.4 is-callable: 1.2.7 functions-have-names@1.2.3: {} @@ -5014,12 +4995,12 @@ snapshots: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + 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.2 + hasown: 2.0.4 math-intrinsics: 1.1.0 get-nonce@1.0.1: {} @@ -5027,7 +5008,7 @@ snapshots: get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-symbol-description@1.1.0: dependencies: @@ -5087,7 +5068,7 @@ snapshots: dependencies: has-symbols: 1.1.0 - hasown@2.0.2: + hasown@2.0.4: dependencies: function-bind: 1.1.2 @@ -5116,7 +5097,7 @@ snapshots: ignore@7.0.5: {} - immer@11.1.4: {} + immer@11.1.8: {} import-fresh@3.3.1: dependencies: @@ -5135,8 +5116,8 @@ snapshots: internal-slot@1.1.0: dependencies: es-errors: 1.3.0 - hasown: 2.0.2 - side-channel: 1.1.0 + hasown: 2.0.4 + side-channel: 1.1.1 internmap@2.0.3: {} @@ -5176,13 +5157,13 @@ snapshots: is-bun-module@2.0.0: dependencies: - semver: 7.7.4 + semver: 7.8.4 is-callable@1.2.7: {} - is-core-module@2.16.1: + is-core-module@2.16.2: dependencies: - hasown: 2.0.2 + hasown: 2.0.4 is-data-view@1.0.2: dependencies: @@ -5235,7 +5216,7 @@ snapshots: call-bound: 1.0.4 gopd: 1.2.0 has-tostringtag: 1.0.2 - hasown: 2.0.2 + hasown: 2.0.4 is-set@2.0.3: {} @@ -5256,7 +5237,7 @@ snapshots: is-typed-array@1.1.15: dependencies: - which-typed-array: 1.1.20 + which-typed-array: 1.1.22 is-weakmap@2.0.2: {} @@ -5276,7 +5257,7 @@ snapshots: iterator.prototype@1.1.5: dependencies: define-data-property: 1.1.4 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 get-proto: 1.0.1 has-symbols: 1.1.0 @@ -5286,7 +5267,7 @@ snapshots: js-tokens@4.0.0: {} - js-yaml@4.1.1: + js-yaml@4.2.0: dependencies: argparse: 2.0.1 @@ -5343,9 +5324,9 @@ snapshots: fault: 1.0.4 highlight.js: 10.7.3 - lucide-react@0.542.0(react@19.2.5): + lucide-react@0.542.0(react@19.2.7): dependencies: - react: 19.2.5 + react: 19.2.7 math-intrinsics@1.1.0: {} @@ -5364,19 +5345,19 @@ snapshots: minimatch@10.2.5: dependencies: - brace-expansion: 5.0.5 + brace-expansion: 5.0.6 minimatch@3.1.5: dependencies: - brace-expansion: 1.1.14 + brace-expansion: 1.1.15 minimist@1.2.8: {} - motion-dom@12.38.0: + motion-dom@12.40.0: dependencies: - motion-utils: 12.36.0 + motion-utils: 12.39.0 - motion-utils@12.36.0: {} + motion-utils@12.39.0: {} ms@2.1.3: {} @@ -5386,26 +5367,26 @@ snapshots: object-assign: 4.1.1 thenify-all: 1.6.0 - nanoid@3.3.11: {} + nanoid@3.3.12: {} napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} - next-themes@0.4.6(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + next-themes@0.4.6(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) - next@15.5.18(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + next@15.5.18(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: '@next/env': 15.5.18 '@swc/helpers': 0.5.15 - caniuse-lite: 1.0.30001788 - postcss: 8.4.31 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - styled-jsx: 5.1.6(react@19.2.5) + caniuse-lite: 1.0.30001797 + postcss: 8.5.15 + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + styled-jsx: 5.1.6(react@19.2.7) optionalDependencies: '@next/swc-darwin-arm64': 15.5.18 '@next/swc-darwin-x64': 15.5.18 @@ -5427,7 +5408,7 @@ snapshots: object.entries: 1.1.9 semver: 6.3.1 - node-releases@2.0.37: {} + node-releases@2.0.47: {} normalize-path@3.0.0: {} @@ -5444,7 +5425,7 @@ snapshots: call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 has-symbols: 1.1.0 object-keys: 1.1.1 @@ -5453,14 +5434,14 @@ snapshots: call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 object.fromentries@2.0.8: dependencies: call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 object.groupby@1.0.3: dependencies: @@ -5473,7 +5454,7 @@ snapshots: call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 once@1.4.0: dependencies: @@ -5535,28 +5516,28 @@ snapshots: possible-typed-array-names@1.1.0: {} - postcss-import@15.1.0(postcss@8.5.10): + postcss-import@15.1.0(postcss@8.5.15): dependencies: - postcss: 8.5.10 + postcss: 8.5.15 postcss-value-parser: 4.2.0 read-cache: 1.0.0 resolve: 1.22.12 - postcss-js@4.1.0(postcss@8.5.10): + postcss-js@4.1.0(postcss@8.5.15): dependencies: camelcase-css: 2.0.1 - postcss: 8.5.10 + postcss: 8.5.15 - postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.10): + postcss-load-config@6.0.1(jiti@1.21.7)(postcss@8.5.15): dependencies: lilconfig: 3.1.3 optionalDependencies: jiti: 1.21.7 - postcss: 8.5.10 + postcss: 8.5.15 - postcss-nested@6.2.0(postcss@8.5.10): + postcss-nested@6.2.0(postcss@8.5.15): dependencies: - postcss: 8.5.10 + postcss: 8.5.15 postcss-selector-parser: 6.1.2 postcss-selector-parser@6.0.10: @@ -5571,23 +5552,15 @@ snapshots: postcss-value-parser@4.2.0: {} - postcss@8.4.31: + postcss@8.5.15: dependencies: - nanoid: 3.3.11 - picocolors: 1.1.1 - source-map-js: 1.2.1 - - postcss@8.5.10: - dependencies: - nanoid: 3.3.11 + nanoid: 3.3.12 picocolors: 1.1.1 source-map-js: 1.2.1 prelude-ls@1.2.1: {} - prettier@3.8.3: {} - - prismjs@1.27.0: {} + prettier@3.8.4: {} prismjs@1.30.0: {} @@ -5607,94 +5580,94 @@ snapshots: queue-microtask@1.2.3: {} - react-copy-to-clipboard@5.1.1(react@19.2.5): + react-copy-to-clipboard@5.1.1(react@19.2.7): dependencies: copy-to-clipboard: 3.3.3 prop-types: 15.8.1 - react: 19.2.5 + react: 19.2.7 - react-day-picker@8.10.1(date-fns@4.1.0)(react@19.2.5): + react-day-picker@8.10.1(date-fns@4.4.0)(react@19.2.7): dependencies: - date-fns: 4.1.0 - react: 19.2.5 + date-fns: 4.4.0 + react: 19.2.7 - react-dom@19.2.5(react@19.2.5): + react-dom@19.2.7(react@19.2.7): dependencies: - react: 19.2.5 + react: 19.2.7 scheduler: 0.27.0 - react-hook-form@7.72.1(react@19.2.5): + react-hook-form@7.78.0(react@19.2.7): dependencies: - react: 19.2.5 + react: 19.2.7 react-is@16.13.1: {} react-is@18.3.1: {} - react-redux@9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1): + react-redux@9.3.0(@types/react@19.2.17)(react@19.2.7)(redux@5.0.1): dependencies: '@types/use-sync-external-store': 0.0.6 - react: 19.2.5 - use-sync-external-store: 1.6.0(react@19.2.5) + react: 19.2.7 + use-sync-external-store: 1.6.0(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 redux: 5.0.1 - react-remove-scroll-bar@2.3.8(@types/react@19.2.14)(react@19.2.5): + react-remove-scroll-bar@2.3.8(@types/react@19.2.17)(react@19.2.7): dependencies: - react: 19.2.5 - react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.5) + react: 19.2.7 + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - react-remove-scroll@2.7.2(@types/react@19.2.14)(react@19.2.5): + react-remove-scroll@2.7.2(@types/react@19.2.17)(react@19.2.7): dependencies: - react: 19.2.5 - react-remove-scroll-bar: 2.3.8(@types/react@19.2.14)(react@19.2.5) - react-style-singleton: 2.2.3(@types/react@19.2.14)(react@19.2.5) + react: 19.2.7 + react-remove-scroll-bar: 2.3.8(@types/react@19.2.17)(react@19.2.7) + react-style-singleton: 2.2.3(@types/react@19.2.17)(react@19.2.7) tslib: 2.8.1 - use-callback-ref: 1.3.3(@types/react@19.2.14)(react@19.2.5) - use-sidecar: 1.1.3(@types/react@19.2.14)(react@19.2.5) + use-callback-ref: 1.3.3(@types/react@19.2.17)(react@19.2.7) + use-sidecar: 1.1.3(@types/react@19.2.17)(react@19.2.7) optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - react-smooth@4.0.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + react-smooth@4.0.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: fast-equals: 5.4.0 prop-types: 15.8.1 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) - react-transition-group: 4.4.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) + react-transition-group: 4.4.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7) - react-style-singleton@2.2.3(@types/react@19.2.14)(react@19.2.5): + react-style-singleton@2.2.3(@types/react@19.2.17)(react@19.2.7): dependencies: get-nonce: 1.0.1 - react: 19.2.5 + react: 19.2.7 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - react-syntax-highlighter@15.6.6(react@19.2.5): + react-syntax-highlighter@15.6.6(react@19.2.7): dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 highlight.js: 10.7.3 highlightjs-vue: 1.0.0 lowlight: 1.20.0 prismjs: 1.30.0 - react: 19.2.5 + react: 19.2.7 refractor: 3.6.0 - react-transition-group@4.4.5(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + react-transition-group@4.4.5(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@babel/runtime': 7.29.2 + '@babel/runtime': 7.29.7 dom-helpers: 5.2.1 loose-envify: 1.4.0 prop-types: 15.8.1 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) - react@19.2.5: {} + react@19.2.7: {} read-cache@1.0.0: dependencies: @@ -5708,15 +5681,15 @@ snapshots: dependencies: decimal.js-light: 2.5.1 - recharts@2.15.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + recharts@2.15.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: clsx: 2.1.1 eventemitter3: 4.0.7 lodash: 4.18.1 - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) react-is: 18.3.1 - react-smooth: 4.0.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5) + react-smooth: 4.0.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7) recharts-scale: 0.4.5 tiny-invariant: 1.3.3 victory-vendor: 36.9.2 @@ -5733,7 +5706,7 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.24.2 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 get-proto: 1.0.1 which-builtin-type: 1.2.1 @@ -5742,7 +5715,7 @@ snapshots: dependencies: hastscript: 6.0.0 parse-entities: 2.0.0 - prismjs: 1.27.0 + prismjs: 1.30.0 regexp.prototype.flags@1.5.4: dependencies: @@ -5753,7 +5726,7 @@ snapshots: gopd: 1.2.0 set-function-name: 2.0.2 - reselect@5.1.1: {} + reselect@5.2.0: {} resolve-from@4.0.0: {} @@ -5762,14 +5735,14 @@ snapshots: resolve@1.22.12: dependencies: es-errors: 1.3.0 - is-core-module: 2.16.1 + is-core-module: 2.16.2 path-parse: 1.0.7 supports-preserve-symlinks-flag: 1.0.0 - resolve@2.0.0-next.6: + resolve@2.0.0-next.7: dependencies: es-errors: 1.3.0 - is-core-module: 2.16.1 + is-core-module: 2.16.2 node-exports-info: 1.6.0 object-keys: 1.1.1 path-parse: 1.0.7 @@ -5785,7 +5758,7 @@ snapshots: dependencies: queue-microtask: 1.2.3 - safe-array-concat@1.1.3: + safe-array-concat@1.1.4: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 @@ -5808,7 +5781,7 @@ snapshots: semver@6.3.1: {} - semver@7.7.4: {} + semver@7.8.4: {} set-function-length@1.2.2: dependencies: @@ -5830,13 +5803,13 @@ snapshots: dependencies: dunder-proto: 1.0.1 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 sharp@0.34.5: dependencies: '@img/colour': 1.1.0 detect-libc: 2.1.2 - semver: 7.7.4 + semver: 7.8.4 optionalDependencies: '@img/sharp-darwin-arm64': 0.34.5 '@img/sharp-darwin-x64': 0.34.5 @@ -5890,7 +5863,7 @@ snapshots: object-inspect: 1.13.4 side-channel-map: 1.0.1 - side-channel@1.1.0: + side-channel@1.1.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 @@ -5898,10 +5871,10 @@ snapshots: side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 - sonner@1.7.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + sonner@1.7.4(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) source-map-js@1.2.1: {} @@ -5927,42 +5900,43 @@ snapshots: define-properties: 1.2.1 es-abstract: 1.24.2 es-errors: 1.3.0 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 gopd: 1.2.0 has-symbols: 1.1.0 internal-slot: 1.1.0 regexp.prototype.flags: 1.5.4 set-function-name: 2.0.2 - side-channel: 1.1.0 + side-channel: 1.1.1 string.prototype.repeat@1.0.0: dependencies: define-properties: 1.2.1 es-abstract: 1.24.2 - string.prototype.trim@1.2.10: + string.prototype.trim@1.2.11: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 es-abstract: 1.24.2 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 has-property-descriptors: 1.0.2 + safe-regex-test: 1.1.0 - string.prototype.trimend@1.0.9: + string.prototype.trimend@1.0.10: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.9 define-properties: 1.2.1 - es-object-atoms: 1.1.1 + es-object-atoms: 1.1.2 strip-ansi@6.0.1: dependencies: @@ -5972,10 +5946,10 @@ snapshots: strip-json-comments@3.1.1: {} - styled-jsx@5.1.6(react@19.2.5): + styled-jsx@5.1.6(react@19.2.7): dependencies: client-only: 0.0.1 - react: 19.2.5 + react: 19.2.7 sucrase@3.35.1: dependencies: @@ -5984,7 +5958,7 @@ snapshots: lines-and-columns: 1.2.4 mz: 2.7.0 pirates: 4.0.7 - tinyglobby: 0.2.16 + tinyglobby: 0.2.17 ts-interface-checker: 0.1.13 supports-color@7.2.0: @@ -6015,11 +5989,11 @@ snapshots: normalize-path: 3.0.0 object-hash: 3.0.0 picocolors: 1.1.1 - postcss: 8.5.10 - postcss-import: 15.1.0(postcss@8.5.10) - postcss-js: 4.1.0(postcss@8.5.10) - postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.10) - postcss-nested: 6.2.0(postcss@8.5.10) + postcss: 8.5.15 + postcss-import: 15.1.0(postcss@8.5.15) + postcss-js: 4.1.0(postcss@8.5.15) + postcss-load-config: 6.0.1(jiti@1.21.7)(postcss@8.5.15) + postcss-nested: 6.2.0(postcss@8.5.15) postcss-selector-parser: 6.1.2 resolve: 1.22.12 sucrase: 3.35.1 @@ -6039,7 +6013,7 @@ snapshots: tiny-invariant@1.3.3: {} - tinyglobby@0.2.16: + tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.4) picomatch: 4.0.4 @@ -6095,7 +6069,7 @@ snapshots: is-typed-array: 1.1.15 reflect.getprototypeof: 1.0.10 - typed-array-length@1.0.7: + typed-array-length@1.0.8: dependencies: call-bind: 1.0.9 for-each: 0.3.5 @@ -6115,29 +6089,32 @@ snapshots: undici-types@6.19.8: {} - unrs-resolver@1.11.1: + unrs-resolver@1.12.2: dependencies: napi-postinstall: 0.3.4 optionalDependencies: - '@unrs/resolver-binding-android-arm-eabi': 1.11.1 - '@unrs/resolver-binding-android-arm64': 1.11.1 - '@unrs/resolver-binding-darwin-arm64': 1.11.1 - '@unrs/resolver-binding-darwin-x64': 1.11.1 - '@unrs/resolver-binding-freebsd-x64': 1.11.1 - '@unrs/resolver-binding-linux-arm-gnueabihf': 1.11.1 - '@unrs/resolver-binding-linux-arm-musleabihf': 1.11.1 - '@unrs/resolver-binding-linux-arm64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-arm64-musl': 1.11.1 - '@unrs/resolver-binding-linux-ppc64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-riscv64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-riscv64-musl': 1.11.1 - '@unrs/resolver-binding-linux-s390x-gnu': 1.11.1 - '@unrs/resolver-binding-linux-x64-gnu': 1.11.1 - '@unrs/resolver-binding-linux-x64-musl': 1.11.1 - '@unrs/resolver-binding-wasm32-wasi': 1.11.1 - '@unrs/resolver-binding-win32-arm64-msvc': 1.11.1 - '@unrs/resolver-binding-win32-ia32-msvc': 1.11.1 - '@unrs/resolver-binding-win32-x64-msvc': 1.11.1 + '@unrs/resolver-binding-android-arm-eabi': 1.12.2 + '@unrs/resolver-binding-android-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-arm64': 1.12.2 + '@unrs/resolver-binding-darwin-x64': 1.12.2 + '@unrs/resolver-binding-freebsd-x64': 1.12.2 + '@unrs/resolver-binding-linux-arm-gnueabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm-musleabihf': 1.12.2 + '@unrs/resolver-binding-linux-arm64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-arm64-musl': 1.12.2 + '@unrs/resolver-binding-linux-loong64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-loong64-musl': 1.12.2 + '@unrs/resolver-binding-linux-ppc64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-riscv64-musl': 1.12.2 + '@unrs/resolver-binding-linux-s390x-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-gnu': 1.12.2 + '@unrs/resolver-binding-linux-x64-musl': 1.12.2 + '@unrs/resolver-binding-openharmony-arm64': 1.12.2 + '@unrs/resolver-binding-wasm32-wasi': 1.12.2 + '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2 + '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 + '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 update-browserslist-db@1.2.3(browserslist@4.28.2): dependencies: @@ -6149,38 +6126,38 @@ snapshots: dependencies: punycode: 2.3.1 - use-callback-ref@1.3.3(@types/react@19.2.14)(react@19.2.5): + use-callback-ref@1.3.3(@types/react@19.2.17)(react@19.2.7): dependencies: - react: 19.2.5 + react: 19.2.7 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - use-debounce@10.1.1(react@19.2.5): + use-debounce@10.1.1(react@19.2.7): dependencies: - react: 19.2.5 + react: 19.2.7 - use-sidecar@1.1.3(@types/react@19.2.14)(react@19.2.5): + use-sidecar@1.1.3(@types/react@19.2.17)(react@19.2.7): dependencies: detect-node-es: 1.1.0 - react: 19.2.5 + react: 19.2.7 tslib: 2.8.1 optionalDependencies: - '@types/react': 19.2.14 + '@types/react': 19.2.17 - use-sync-external-store@1.6.0(react@19.2.5): + use-sync-external-store@1.6.0(react@19.2.7): dependencies: - react: 19.2.5 + react: 19.2.7 util-deprecate@1.0.2: {} - uuid@9.0.1: {} + uuid@11.1.1: {} - vaul@0.9.9(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5): + vaul@0.9.9(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7): dependencies: - '@radix-ui/react-dialog': 1.1.15(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5) - react: 19.2.5 - react-dom: 19.2.5(react@19.2.5) + '@radix-ui/react-dialog': 1.1.16(@types/react-dom@19.2.3(@types/react@19.2.17))(@types/react@19.2.17)(react-dom@19.2.7(react@19.2.7))(react@19.2.7) + react: 19.2.7 + react-dom: 19.2.7(react@19.2.7) transitivePeerDependencies: - '@types/react' - '@types/react-dom' @@ -6224,7 +6201,7 @@ snapshots: isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 - which-typed-array: 1.1.20 + which-typed-array: 1.1.22 which-collection@1.0.2: dependencies: @@ -6233,7 +6210,7 @@ snapshots: is-weakmap: 2.0.2 is-weakset: 2.0.4 - which-typed-array@1.1.20: + which-typed-array@1.1.22: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.9 diff --git a/server/dashboard/pnpm-workspace.yaml b/server/dashboard/pnpm-workspace.yaml new file mode 100644 index 000000000..4314aa28c --- /dev/null +++ b/server/dashboard/pnpm-workspace.yaml @@ -0,0 +1,7 @@ +packages: + - '.' + +overrides: + "postcss@<8.5.10": ">=8.5.10" + "prismjs@<1.30.0": ">=1.30.0" + "uuid@<11.1.1": ">=11.1.1" diff --git a/server/main.py b/server/main.py index f5cc553f5..7476698a1 100644 --- a/server/main.py +++ b/server/main.py @@ -451,6 +451,10 @@ def search_memories(search_req: SearchRequest, _auth=Depends(verify_auth)): if search_req.explain is not None: params["explain"] = search_req.explain return get_memory_instance().search(query=search_req.query, filters=filters, **params) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) + except HTTPException: + raise except Exception: raise upstream_error() diff --git a/server/requirements.txt b/server/requirements.txt index 3f6d0c8af..33a11c55c 100644 --- a/server/requirements.txt +++ b/server/requirements.txt @@ -2,7 +2,7 @@ fastapi==0.115.8 uvicorn==0.34.0 pydantic[email]==2.10.4 mem0ai>=0.1.48 -python-dotenv==1.0.1 +python-dotenv==1.2.2 psycopg>=3.2.8 psycopg-pool>=3.2.6,<4.0.0 diff --git a/skills/README.md b/skills/README.md index c14b5cab2..b38716f3b 100644 --- a/skills/README.md +++ b/skills/README.md @@ -22,8 +22,9 @@ Invoked as a slash command to execute a specific end-to-end workflow. These do r |-------|---------|---------| | [`mem0-integrate`](./mem0-integrate/) | `/mem0-integrate` — wire Mem0 into an existing repo via TDD | `npx skills add https://github.com/mem0ai/mem0 --skill mem0-integrate` | | [`mem0-test-integration`](./mem0-test-integration/) | `/mem0-test-integration` — verify what `/mem0-integrate` produced | `npx skills add https://github.com/mem0ai/mem0 --skill mem0-test-integration` | +| [`mem0-oss-to-platform`](./mem0-oss-to-platform/) | `/mem0-oss-to-platform` — migrate a project from Mem0 OSS to the hosted Platform SDK | `npx skills add https://github.com/mem0ai/mem0 --skill mem0-oss-to-platform` | -The two pipeline skills are designed to run in sequence on the same workspace: +The `mem0-integrate` and `mem0-test-integration` skills are designed to run in sequence on the same workspace: ``` /mem0-integrate → mem0-integrate/ branch + .mem0-integration/ artifacts @@ -36,6 +37,7 @@ The two pipeline skills are designed to run in sequence on the same workspace: - **Using the terminal CLI?** → `mem0-cli` - **Building with `@ai-sdk/*`?** → `mem0-vercel-ai-sdk` - **Want the assistant to wire Mem0 into an existing repo for you?** → `mem0-integrate`, then `mem0-test-integration` +- **Already using Mem0 OSS and want to move to the hosted Platform?** → `mem0-oss-to-platform` ## Links diff --git a/skills/mem0-integrate/SKILL.md b/skills/mem0-integrate/SKILL.md index fd686b645..30acaae3f 100644 --- a/skills/mem0-integrate/SKILL.md +++ b/skills/mem0-integrate/SKILL.md @@ -46,7 +46,7 @@ standalone `SKILL.md` with triggers, examples, and version-pinned code. - SDK (Python + TS, Platform + OSS): https://raw.githubusercontent.com/mem0ai/mem0/main/skills/mem0/SKILL.md - CLI: https://raw.githubusercontent.com/mem0ai/mem0/main/skills/mem0-cli/SKILL.md - Vercel AI SDK: https://raw.githubusercontent.com/mem0ai/mem0/main/skills/mem0-vercel-ai-sdk/SKILL.md -- Editor/MCP plugin glue (9 MCP tools): https://github.com/mem0ai/mem0/tree/main/mem0-plugin +- Editor/MCP plugin glue (9 MCP tools): https://github.com/mem0ai/mem0/tree/main/integrations/mem0-plugin ### SDK source (read when docs are ambiguous) Public repo. Cross-check against the `mem0_tested_versions` range in this @@ -109,7 +109,7 @@ the target stack. If yes, delegate — copy its call-site pattern into |---|---|---| | `@ai-sdk/*` + `ai` in `package.json` | `skills/mem0-vercel-ai-sdk` | Integration is via `createMem0` provider wrapper, not raw `MemoryClient`. | | CLI-only repo (Typer, Commander, Click, Cobra) with no LLM call sites | `skills/mem0-cli` | Call sites are command handlers, not model wrappers. Consider whether mem0 actually fits first. | -| Target is an MCP client / editor config (Claude Code, Cursor, Codex settings) | `mem0-plugin` | Wire via MCP server URL + hooks; no SDK code usually needed. | +| Target is an MCP client / editor config (Claude Code, Cursor, Codex settings) | `integrations/mem0-plugin` | Wire via MCP server URL + hooks; no SDK code usually needed. | | Any other Python or TS repo with an LLM call site | `skills/mem0` | Default SDK integration path. | Record the delegated skill's raw URL in `plan.md` under a diff --git a/skills/mem0-oss-to-platform/LICENSE b/skills/mem0-oss-to-platform/LICENSE new file mode 100644 index 000000000..78c99ae28 --- /dev/null +++ b/skills/mem0-oss-to-platform/LICENSE @@ -0,0 +1,189 @@ + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but not + limited to compiled object code, generated documentation, and + conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work. + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to the Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by the Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding any notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + Copyright 2024 Mem0.ai + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/skills/mem0-oss-to-platform/README.md b/skills/mem0-oss-to-platform/README.md new file mode 100644 index 000000000..2f28e0ac6 --- /dev/null +++ b/skills/mem0-oss-to-platform/README.md @@ -0,0 +1,79 @@ +# mem0-oss-to-platform — Pipeline Skill + +Migrate a project from the Mem0 Open Source (self-hosted) SDK to the Mem0 Platform (hosted) SDK, end to end. The skill audits where Mem0 is used, writes a reviewable migration plan, and executes it after you approve. + +> **This is a pipeline skill, not a reference skill.** Invoke it when you want your agent to migrate an existing project's Mem0 integration from OSS to the Platform. For day-to-day SDK coding help, install [`mem0`](../mem0/SKILL.md) instead. +> +> **Part of the Mem0 Skill Graph:** +> - Reference: [mem0](../mem0/SKILL.md) · [mem0-cli](../mem0-cli/SKILL.md) · [mem0-vercel-ai-sdk](../mem0-vercel-ai-sdk/SKILL.md) +> - Pipeline: [mem0-integrate](../mem0-integrate/SKILL.md) → [mem0-test-integration](../mem0-test-integration/SKILL.md) · **mem0-oss-to-platform** (this skill) + +## What This Skill Does + +When invoked, your assistant will: + +- **Discover** every place Mem0 is used in the project — imports, client init, config blocks, call sites, dependencies, env, and local infra +- **Verify** the exact API against the installed SDK rather than guessing +- **Map** each OSS `Memory` usage to its hosted `MemoryClient` equivalent (Python and TypeScript) +- **Flag** everything that isn't a clean 1:1 and needs a human decision +- **Write** a reviewable `MEM0_MIGRATION_PLAN.md`, then **execute it after you approve** — strictly scoped to the Mem0 integration, with no unrelated refactors + +## When to Use + +Trigger phrases: + +- "Migrate my Mem0 setup to the Platform" +- "Switch from self-hosted Mem0 to MemoryClient" +- "Use my Mem0 API key instead of a local Qdrant" +- "Move Mem0 to the hosted/managed service" + +Do **not** use this skill for general SDK usage (install [`mem0`](../mem0/SKILL.md)), or to add Mem0 to a repo that doesn't use it yet (use [`mem0-integrate`](../mem0-integrate/SKILL.md)). + +## Installation + +### CLI (Claude Code, Codex, OpenCode, OpenClaw, or any tool that supports skills) + +```bash +npx skills add https://github.com/mem0ai/mem0 --skill mem0-oss-to-platform +``` + +### Claude.ai + +1. Download this `skills/mem0-oss-to-platform` folder as a ZIP +2. Go to **Settings > Capabilities > Skills** +3. Click **Upload skill** and select the ZIP + +### Claude API (Skills API) + +```bash +curl -X POST https://api.anthropic.com/v1/skills \ + -H "x-api-key: $ANTHROPIC_API_KEY" \ + -H "Content-Type: application/json" \ + -d '{"name": "mem0-oss-to-platform", "source": "https://github.com/mem0ai/mem0/tree/main/skills/mem0-oss-to-platform"}' +``` + +### Prerequisites + +- A Mem0 Platform API key ([get one](https://app.mem0.ai/dashboard/api-keys)) +- An existing project that uses the Mem0 OSS SDK + +## Workflow + +``` +(invoke skill) → audits the repo's Mem0 usage, + writes MEM0_MIGRATION_PLAN.md, + stops for your review +(approve) → executes the plan and verifies + (compile/import, real-API smoke test) +``` + +## Links + +- [Mem0 Platform Dashboard](https://app.mem0.ai) +- [Mem0 Documentation](https://docs.mem0.ai) +- [OSS → Platform migration guide](https://docs.mem0.ai/migration/oss-v2-to-v3) +- [Platform vs OSS comparison](https://docs.mem0.ai/platform/platform-vs-oss) + +## License + +Apache-2.0 diff --git a/skills/mem0-oss-to-platform/SKILL.md b/skills/mem0-oss-to-platform/SKILL.md new file mode 100644 index 000000000..1cdde7dff --- /dev/null +++ b/skills/mem0-oss-to-platform/SKILL.md @@ -0,0 +1,120 @@ +--- +name: mem0-oss-to-platform +description: >- + Plan and then execute a migration of a project from the mem0 open-source / self-hosted SDK + (the local `Memory` class) to the mem0 Platform / hosted / managed SDK (the `MemoryClient` + class). Use this whenever a developer wants to move, switch, or migrate their mem0 usage off + OSS/self-hosted to the hosted API — e.g. "migrate my mem0 setup to the platform", "switch from + self-hosted mem0 to MemoryClient", "use my mem0 API key instead of a local Qdrant", "move mem0 + to the cloud/hosted/managed service", or "replace my local mem0 vector store + embedder config + with the platform". Applies to Python (`from mem0 import Memory` → `from mem0 import MemoryClient`) + and TypeScript/JavaScript (`import { Memory } from "mem0ai/oss"` → `import MemoryClient from "mem0ai"`). + Trigger even when the user doesn't say the word "migrate" but clearly wants their existing mem0 + integration to run against the hosted platform. It first produces a reviewable migration plan, + then executes it after the developer approves. Strictly scoped to the mem0 integration — it does + not refactor, restructure, or "improve" any unrelated code. +--- + +# Migrate mem0 OSS → mem0 Platform (hosted) + +This skill migrates a project's memory layer from the **self-hosted mem0 OSS SDK** to the +**hosted mem0 Platform SDK**, working for any project shape — an agent, a RAG pipeline, an API +service, a chatbot, a background worker. You discover where mem0 is actually used, write a plan the +developer reviews, and then execute it on approval. + +## The mental model (read this first — it's why the migration is shaped the way it is) + +OSS mem0 means **the developer runs the whole memory stack themselves**: a vector store +(Qdrant/pgvector/Chroma/…), an embedder, an LLM for fact extraction, and a local history DB. All of +that is wired up in a config object passed to `Memory`. + +The Platform means **mem0 runs that stack for them**. The developer just holds an **API key**. So +the migration is mostly *subtraction*: the local infrastructure config collapses into a single +`MemoryClient(api_key=...)`. The method calls stay recognizable (`add`/`search`/`get_all`/…), but a +few parameter conventions tighten up and the return values are server responses. + +So the core of every migration is: +1. `Memory` / `Memory.from_config({...})` → `MemoryClient()` (reads the API key from the env). +2. Delete the local `vector_store` / `llm` / `embedder` / `graph_store` / `history_db_path` config. +3. Fix up each call site to the hosted call convention (entity IDs into `filters`, pagination, etc.). +4. Flag everything that *isn't* a clean 1:1 so the developer can decide (see `references/gotchas.md`). + +**Scope discipline:** touch only mem0-related code, config, dependencies, and env. Preserve the +project's existing behavior, structure, and style. Do not rename things, "tidy" nearby code, or +change the app's logic. The developer asked to swap a backend, not to refactor their project. + +## Workflow + +Work through these phases in order. Phases 1–4 produce the plan; phase 5 runs only after approval. + +### Phase 0 — Prerequisite check +The hosted SDK needs a mem0 API key (`MEM0_API_KEY`, obtainable at https://app.mem0.ai). Confirm +the developer has one. You don't need the key value to write the plan, but flag in the plan that it +must be set (in `.env` / secrets manager, never hardcoded) before execution and verification. + +### Phase 1 — Discover the mem0 footprint +Do not assume the layout. Find every place mem0 appears. Detect the language and the **installed** +version first, then sweep for usage. Concretely, search for: +- **Imports / instantiation:** `from mem0 import Memory`, `Memory.from_config`, `Memory(`, + `import ... from "mem0ai"`, `from "mem0ai/oss"`, `new Memory(`. +- **Config blocks:** keys like `vector_store`/`vectorStore`, `embedder`, `llm`, `graph_store`/ + `graphStore`, `history_db_path`, `historyStore`, `custom_fact_extraction_prompt`, + `custom_update_memory_prompt`, `enable_graph`. +- **Every call site:** `.add(`, `.search(`, `.get_all(`/`.getAll(`, `.delete_all(`/`.deleteAll(`, + `.get(`, `.update(`, `.delete(`, `.reset(`, `.history(`. +- **Dependencies & env:** `requirements.txt`/`pyproject.toml`/`package.json` for `mem0ai` and any + local-infra deps that exist *only* for mem0 (e.g. `qdrant-client`, `chromadb`); `.env`/config for + things like `OPENAI_API_KEY` used by the local embedder/LLM; any docker-compose service (e.g. a + Qdrant container) that exists only to back mem0. + +Use Grep/Glob broadly; a single missed call site is a runtime break later. Record `file:line` for +each finding — the plan's inventory is built from this. + +### Phase 2 — Verify the API against the installed SDK (don't guess) +Versions drift, and the OSS and hosted classes have subtly different signatures. Before mapping, +confirm the **real** signatures of the installed package rather than trusting memory: +- **Python:** `python -c "import inspect; from mem0 import MemoryClient; print(inspect.signature(MemoryClient.search))"` + for each method you'll touch, and read the installed source under + `site-packages/mem0/client/main.py` if anything is ambiguous (e.g. whether a method *rejects* + top-level entity params). Also check the OSS side the project currently uses. +- **TypeScript:** read the installed types/dist under `node_modules/mem0ai/` to confirm option names + (`limit` vs `topK`, `userId` vs a nested `filters`) and the default vs `mem0ai/oss` export. + +This verification step is the single most important habit — it's what keeps the plan correct across +mem0 versions. Then consult `references/api-mapping.md` for the OSS→hosted translation of each +method (Python and TypeScript), and the official guide at https://docs.mem0.ai/migration/oss-v2-to-v3. + +### Phase 3 — Map each site and flag the gaps +For every call site and config block from Phase 1, determine the hosted equivalent using the +mapping. Most calls map cleanly. Some don't — and those matter more than the mechanical edits. +Read `references/gotchas.md` and flag anything that needs a human decision: self-hosted/data- +residency setups, local model choices moving server-side, graph-memory usage, custom prompts, hot- +path calls that now make network round-trips, and **existing locally-stored memories not carrying +over** (data migration is out of scope unless the developer asks — note it, don't silently attempt it). + +### Phase 4 — Write the plan and stop +Write the full plan to `MEM0_MIGRATION_PLAN.md` at the repo root, following the structure in +`references/plan-template.md`. It must be concrete enough to execute from and honest about the gaps. +Then **stop and present it for review.** Do not start editing code in the same turn — the whole +point is that the developer reads and approves the plan first. + +### Phase 5 — Execute on approval (guided) +Once the developer approves (they may ask for changes first — incorporate them), execute the plan: +- Make the edits file by file, staying strictly within mem0 scope. +- Update dependencies and env (`MEM0_API_KEY`; remove now-dead local-infra deps/services only if + they exist solely for mem0 and you're confident). +- **Verify**, mirroring how you'd confirm any backend swap: + - It imports / type-checks / byte-compiles. + - A smoke test exercises `add` → `search`/`get_all` → `delete_all` against the hosted API with a + real `MEM0_API_KEY`, and the app's own entry point still runs. + - No local mem0 storage directory gets created anymore (e.g. a `.mem0/`, local Qdrant path) — + proof the memory really lives on the platform now. +- Report what changed, what was verified, and any flagged concerns the developer still needs to act + on (e.g. configuring custom instructions in the dashboard, migrating old data). + +## Reference files +- `references/api-mapping.md` — exact OSS→hosted method/param/return mapping for Python and + TypeScript, plus dependency and env changes. Read during Phase 2–3. +- `references/gotchas.md` — the things that aren't a clean 1:1 and need a human decision. Read + during Phase 3 so the plan's "Concerns" section is complete. +- `references/plan-template.md` — the exact structure for `MEM0_MIGRATION_PLAN.md`. Use in Phase 4. diff --git a/skills/mem0-oss-to-platform/references/api-mapping.md b/skills/mem0-oss-to-platform/references/api-mapping.md new file mode 100644 index 000000000..9c9a04b66 --- /dev/null +++ b/skills/mem0-oss-to-platform/references/api-mapping.md @@ -0,0 +1,117 @@ +# OSS → Platform API mapping + +Exact translation of the mem0 OSS (self-hosted `Memory`) API to the hosted `MemoryClient` API. +**Always confirm against the installed package** (see SKILL.md Phase 2) — versions drift. The facts +below match `mem0ai` 2.0.x (the v3 platform API) and the official guide: +https://docs.mem0.ai/migration/oss-v2-to-v3 + +## Contents +- [Python](#python) +- [TypeScript / JavaScript](#typescript--javascript) +- [Return shapes](#return-shapes) +- [Dependencies & environment](#dependencies--environment) +- [v2→v3 default/behavior changes](#v2v3-defaultbehavior-changes) + +--- + +## Python + +### Import & client construction +```python +# OSS (self-hosted) +from mem0 import Memory +memory = Memory() # or: +memory = Memory.from_config({ # all of this local config disappears + "vector_store": {...}, + "llm": {...}, + "embedder": {...}, + "history_db_path": "...", +}) + +# Platform (hosted) +from mem0 import MemoryClient +memory = MemoryClient() # reads MEM0_API_KEY from the env +# or: MemoryClient(api_key="...") +``` +Notes: +- The client reads `MEM0_API_KEY` from the environment when `api_key` is omitted. +- **Drop** `vector_store`, `llm`, `embedder`, `graph_store`, `history_db_path` — these are managed + server-side now. +- **Drop** `org_id` / `project_id` constructor args if present — they're resolved from the API key + in v3. +- For async codebases, use `AsyncMemoryClient` (same methods, `await`-ed). + +### Method calls +| Operation | OSS `Memory` | Hosted `MemoryClient` | +|---|---|---| +| add | `memory.add(messages, user_id="u")` | `memory.add(messages, user_id="u")` — unchanged (top-level entity IDs accepted) | +| search | `memory.search(q, user_id="u", limit=N)` *(older)* or `…, filters={"user_id":"u"}, top_k=N` *(newer)* | `memory.search(q, filters={"user_id": "u"}, top_k=N)` — entity IDs **must** be inside `filters`; top-level `user_id`/`agent_id`/`app_id`/`run_id` raise `ValueError` | +| get_all | `memory.get_all(user_id="u")` or `…, filters={"user_id":"u"}` | `memory.get_all(filters={"user_id": "u"}, page=1, page_size=N)` — entity IDs in `filters`; paginated with `page`/`page_size` (**not** `top_k`) | +| delete_all | `memory.delete_all(user_id="u")` | `memory.delete_all(user_id="u")` — unchanged | +| get | `memory.get(memory_id)` | `memory.get(memory_id)` | +| update | `memory.update(memory_id, data=...)` | `memory.update(memory_id, text=...)` — confirm param name against installed sig | +| delete | `memory.delete(memory_id)` | `memory.delete(memory_id)` | +| reset | `memory.reset()` (wipes the local store) | **No global reset.** Use `memory.delete_all(filters=...)` scoped to the relevant entity. Flag this. | + +Key rule: for **search** and **get_all**, the hosted client requires entity IDs (`user_id`, +`agent_id`, `app_id`, `run_id`) inside a `filters` dict and will raise if you pass them top-level. +For **add** and **delete_all**, top-level entity IDs are accepted. + +--- + +## TypeScript / JavaScript + +The hosted and OSS SDKs ship in the same `mem0ai` npm package, distinguished by import path. +Confirm option names against `node_modules/mem0ai/` types. + +### Import & client construction +```typescript +// OSS (self-hosted) — note the "/oss" subpath +import { Memory } from "mem0ai/oss"; +const memory = new Memory({ /* vectorStore, embedder, llm, historyStore … */ }); + +// Platform (hosted) — default export from the package root +import MemoryClient from "mem0ai"; +const memory = new MemoryClient({ apiKey: process.env.MEM0_API_KEY }); +// Drop organizationId / projectId — resolved from the API key in v3. +``` + +### Method calls (option-object differences) +| Operation | OSS / old client | Hosted client (v3) | +|---|---|---| +| add | `memory.add(messages, { userId: "u" })` | `memory.add(messages, { userId: "u" })` — unchanged | +| search | `memory.search(q, { userId: "u", limit: 20 })` | `memory.search(q, { filters: { userId: "u" }, topK: 20 })` — entity IDs into `filters`; `limit` → `topK` | +| getAll | `memory.getAll({ userId: "u" })` | `memory.getAll({ filters: { userId: "u" } })` — entity IDs into `filters` | +| deleteAll | `memory.deleteAll({ userId: "u" })` | `memory.deleteAll({ userId: "u" })` | +| get / update / delete | `memory.get(id)` etc. | same, by memory id | + +Also drop legacy options that no longer apply on v3: `async_mode`, `output_format`, `enable_graph`. + +--- + +## Return shapes +- `search(...)` and `get_all(...)` return `{"results": [...]}`; each item has at least a `memory` + (text) field, plus `id` and (for search) `score`. Code that reads `result["results"]` and pulls + `item["memory"]` keeps working. +- `get_all(...)` on the hosted client is paginated: `{"count", "next", "previous", "results": [...]}`. +- `add(...)` returns the created memories. On v3 it returns **only ADD events** — if the old code + branched on `event == "UPDATE"` / `"DELETE"` from `add()` results, that branch is now dead. + +--- + +## Dependencies & environment +- **Keep** the `mem0ai` dependency — `MemoryClient` ships in the same package. No version bump is + required just to use the hosted client (confirm the installed version supports it). +- **Remove** dependencies that existed *only* to back the local mem0 store/embedder/LLM and are now + unused (e.g. `qdrant-client`, `chromadb`, a local embedding lib). Only remove what you can confirm + is unused elsewhere. +- **Add** `MEM0_API_KEY` to the environment / `.env.example` / secrets manager / deployment config. +- Local-infra services (e.g. a Qdrant docker-compose service) that existed only for mem0 can be + retired — flag this rather than deleting infrastructure unilaterally. + +## v2→v3 default/behavior changes +From the official migration guide — surface any that affect the project: +- Python `top_k` default changed 100 → 20; TS `limit` renamed to `topK`. +- New `threshold` default `0.1` (was none); new `rerank` default `false` (was true). +- `custom_fact_extraction_prompt` → `custom_instructions`; `custom_update_memory_prompt` deprecated. +- Graph memory (`enable_graph`, `graph_store`) removed from the OSS v3 surface — see gotchas. diff --git a/skills/mem0-oss-to-platform/references/gotchas.md b/skills/mem0-oss-to-platform/references/gotchas.md new file mode 100644 index 000000000..d5e610f72 --- /dev/null +++ b/skills/mem0-oss-to-platform/references/gotchas.md @@ -0,0 +1,63 @@ +# Gotchas — the things that aren't a clean 1:1 + +Swapping `Memory` for `MemoryClient` is mostly mechanical. These items are *not* mechanical: they +change behavior, move responsibility off the developer's machine, or have no direct equivalent. +Every one that applies to the project belongs in the plan's **"Concerns & decisions needed"** +section, phrased as a decision for the developer — never silently resolved. + +## 1. Data does not migrate with the code +Migrating the *code* does not move the *memories*. Anything stored in the local vector store / +history DB stays there; the hosted account starts empty. This is the most surprising gap, so call +it out prominently. Data migration is **out of scope** unless the developer explicitly asks. If they +do, the rough path is: read everything from the OSS store (`get_all` per user/entity) and re-`add` +it to the hosted client — but treat that as a separate, opt-in task. + +## 2. Self-hosting / data residency +A local or self-hosted vector store sometimes exists *on purpose* — compliance, data residency, air- +gapped deployment, cost. Moving to the managed platform sends memory content to mem0's servers. +Don't assume that's acceptable; flag it as an explicit decision, especially for regulated domains. + +## 3. Local models move server-side +If the OSS config used specific local/self-chosen models (e.g. Ollama, a particular embedder, a +non-OpenAI LLM for fact extraction), those choices disappear — extraction and embedding now run on +the platform with the platform's configuration. Memory *content and quality may shift* as a result. +Flag where the project depended on a specific model. + +## 4. Graph memory +If the project uses graph memory (`enable_graph`, `graph_store`), this changed in v3 and is handled +differently on the platform. Don't assume a drop-in mapping — verify current platform graph support +in the docs and flag the usage for the developer. + +## 5. Custom prompts / extraction config +`custom_fact_extraction_prompt` → `custom_instructions`, and `custom_update_memory_prompt` is +deprecated. On the platform these tend to be **project-level settings configured in the dashboard** +rather than passed in code. Flag any custom prompt the project relied on so the developer can re- +apply it in the dashboard. + +## 6. Every call is now a network request +Local calls become remote API calls. That introduces latency, network failures, timeouts, rate +limits, and per-call cost. Flag mem0 calls on hot paths or in tight loops, and recommend adding +error handling / retries / timeouts where the old local calls were effectively infallible. For async +apps, use `AsyncMemoryClient` (Python) so calls don't block the event loop. + +## 7. API key & secrets +The hosted client needs `MEM0_API_KEY`. It must come from the environment / a secrets manager — never +hardcoded. Ensure it's added to `.env.example`, local `.env`, CI, and deployment config. Without it +the client fails to initialize. + +## 8. Dropped constructor args & legacy options +`org_id` / `project_id` (Python) and `organizationId` / `projectId` (TS) are no longer passed to the +constructor in v3 — they're resolved from the API key. Per-call legacy options like `async_mode`, +`output_format`, and `enable_graph` are gone. Remove them rather than leaving dead args. + +## 9. Return-shape drift +- `add()` returns **only ADD events** on v3. Code that inspected `add()` results for `UPDATE` / + `DELETE` events has dead branches now. +- `search` / `get_all` return `{"results": [...]}`; `get_all` is paginated (`count`/`next`/ + `previous`/`results`). Code that limited via `top_k` on `get_all` should move to `page`/`page_size`. +- Default `top_k` dropped 100 → 20, `threshold` now `0.1`, `rerank` now `false` — result counts and + ordering can change even when the call looks equivalent. + +## 10. No global `reset()` +The OSS `reset()` wipes the whole local store. There's no hosted equivalent that nukes everything; +use `delete_all` scoped by `filters`. Flag any `reset()` call. diff --git a/skills/mem0-oss-to-platform/references/plan-template.md b/skills/mem0-oss-to-platform/references/plan-template.md new file mode 100644 index 000000000..bd9cbcd0c --- /dev/null +++ b/skills/mem0-oss-to-platform/references/plan-template.md @@ -0,0 +1,72 @@ +# Plan template — `MEM0_MIGRATION_PLAN.md` + +Write the plan to `MEM0_MIGRATION_PLAN.md` at the repo root using the structure below. Keep it +concrete enough to execute from and honest about the gaps. Fill every section from the actual +findings — don't leave placeholders. Drop a section only if it genuinely doesn't apply (and say so). + +```markdown +# mem0 OSS → Platform Migration Plan + +## Summary +- One paragraph: what's moving (self-hosted `Memory` → hosted `MemoryClient`) and why. +- Detected language(s) and the installed `mem0ai` version. +- Counts: N files touched, M call sites, plus config/deps/env changes. + +## Prerequisites +- `MEM0_API_KEY` must be set in the environment before execution/verification (https://app.mem0.ai). +- Note where it should live (`.env`, secrets manager, CI, deploy config). + +## Inventory +A table of every mem0 touchpoint found, so the developer can see the full footprint: + +| File:line | Current (OSS) usage | Category | Maps to | +|-----------|--------------------|----------|---------| +| path:LN | `Memory.from_config({...})` | client init | `MemoryClient()` | +| path:LN | `memory.search(q, user_id=...)` | call site | `search(q, filters={...}, top_k=...)` | +| ... | ... | config / dep / env / infra | ... | + +## Change set +Grouped by file. For each, a short before → after with the actual surrounding code, so the edits are +unambiguous. Example: + +### `path/to/file.py` +- Replace import `from mem0 import Memory` → `from mem0 import MemoryClient`. +- Replace the `Memory.from_config({...})` block with `MemoryClient()` (drops local vector_store/ + llm/embedder/history config). +- `search(...)`: move `user_id` into `filters={"user_id": ...}`; keep `top_k`. + ``` + # before + ... + # after + ... + ``` + +## Dependencies & config +- `requirements.txt` / `pyproject.toml` / `package.json`: keep `mem0ai`; remove now-unused local- + infra deps (list them, with the reason each is safe to remove). +- Env: add `MEM0_API_KEY`; remove env vars only used by the old local embedder/LLM if now unused. +- Infrastructure: local services that existed only for mem0 (e.g. a Qdrant docker-compose service) + can be retired — listed for the developer's confirmation, not auto-deleted. + +## Concerns & decisions needed +The non-1:1 items from the gotchas that apply here, each phrased as a decision for the developer. +Cover, where relevant: data not migrating, self-hosting/data-residency, local models moving server- +side, graph memory, custom prompts (now dashboard settings), network/latency/cost on hot paths, +return-shape changes (`add` ADD-only, `get_all` pagination, default `top_k`/threshold/rerank), and +any `reset()` usage. Be specific about which file/line each concern affects. + +## Out of scope +- Existing memory **data** is not migrated (code only). If wanted, it's a separate opt-in task + (export from the OSS store, re-add to the hosted client). +- No unrelated refactors, renames, or behavior changes. + +## Verification plan +How execution will be confirmed end-to-end: +- Imports / type-checks / byte-compiles cleanly. +- Smoke test against the hosted API with a real `MEM0_API_KEY`: `add` a fact → `search`/`get_all` + returns it → `delete_all` clears it. Plus: the app's own entry point still runs. +- Confirm no local mem0 storage dir is created anymore (e.g. `.mem0/`) — proof memory is hosted. + +## Rollback +- All changes are in version control; revert with git if needed. Note the branch/commit strategy. +``` diff --git a/skills/mem0-vercel-ai-sdk/references/memory-utilities.md b/skills/mem0-vercel-ai-sdk/references/memory-utilities.md index 56a287805..0ad297fc1 100644 --- a/skills/mem0-vercel-ai-sdk/references/memory-utilities.md +++ b/skills/mem0-vercel-ai-sdk/references/memory-utilities.md @@ -2,7 +2,7 @@ Complete reference for standalone utility functions exported from `@mem0/vercel-ai-provider`. These functions give you manual control over memory retrieval and storage, independent of the wrapped model pattern. -Source: `vercel-ai-sdk/src/mem0-utils.ts` +Source: `integrations/vercel-ai-sdk/src/mem0-utils.ts` ## `addMemories(messages, config?)` diff --git a/skills/mem0-vercel-ai-sdk/references/provider-api.md b/skills/mem0-vercel-ai-sdk/references/provider-api.md index 3a59f9883..63222b815 100644 --- a/skills/mem0-vercel-ai-sdk/references/provider-api.md +++ b/skills/mem0-vercel-ai-sdk/references/provider-api.md @@ -1,6 +1,6 @@ # Provider API Reference -Complete reference for the `@mem0/vercel-ai-provider` provider layer. Source: `vercel-ai-sdk/src/`. +Complete reference for the `@mem0/vercel-ai-provider` provider layer. Source: `integrations/vercel-ai-sdk/src/`. ## `createMem0(options?)` diff --git a/tests/memory/test_decay_feature_notice.py b/tests/memory/test_decay_feature_notice.py new file mode 100644 index 000000000..75ffa6489 --- /dev/null +++ b/tests/memory/test_decay_feature_notice.py @@ -0,0 +1,60 @@ +import pytest + +from mem0.memory import main as memory_main +from mem0.memory.main import AsyncMemory, Memory + + +def test_sync_project_update_decay_true_raises_with_notice_message(monkeypatch): + calls = [] + + def get_error(sync_type, trigger_function, trigger_parameter): + calls.append((sync_type, trigger_function, trigger_parameter)) + return "blocked decay" + + monkeypatch.setattr(memory_main, "get_decay_feature_error_message", get_error) + + with pytest.raises(ValueError, match="blocked decay"): + Memory.__new__(Memory).project.update(decay=True) + + assert calls == [("sync", "project.update", "decay")] + + +@pytest.mark.asyncio +async def test_async_project_update_decay_true_raises_with_notice_message(monkeypatch): + calls = [] + + async def get_error(sync_type, trigger_function, trigger_parameter): + calls.append((sync_type, trigger_function, trigger_parameter)) + return "blocked async decay" + + monkeypatch.setattr(memory_main, "get_decay_feature_error_message_async", get_error) + + with pytest.raises(ValueError, match="blocked async decay"): + await AsyncMemory.__new__(AsyncMemory).project.update(decay=True) + + assert calls == [("async", "project.update", "decay")] + + +@pytest.mark.parametrize("kwargs", [{}, {"decay": False}]) +def test_sync_project_update_non_trigger_raises_plain_error_without_notice(monkeypatch, kwargs): + monkeypatch.setattr( + memory_main, + "get_decay_feature_error_message", + lambda *args: pytest.fail("decay feature notice should not run"), + ) + + with pytest.raises(ValueError, match="Project updates are not supported by the OSS Memory SDK."): + Memory.__new__(Memory).project.update(**kwargs) + + +@pytest.mark.asyncio +@pytest.mark.parametrize("kwargs", [{}, {"decay": False}]) +async def test_async_project_update_non_trigger_raises_plain_error_without_notice(monkeypatch, kwargs): + monkeypatch.setattr( + memory_main, + "get_decay_feature_error_message_async", + lambda *args: pytest.fail("decay feature notice should not run"), + ) + + with pytest.raises(ValueError, match="Project updates are not supported by the OSS Memory SDK."): + await AsyncMemory.__new__(AsyncMemory).project.update(**kwargs) diff --git a/tests/memory/test_decay_usage_notice.py b/tests/memory/test_decay_usage_notice.py new file mode 100644 index 000000000..817f2d738 --- /dev/null +++ b/tests/memory/test_decay_usage_notice.py @@ -0,0 +1,214 @@ +from types import SimpleNamespace +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from mem0.memory import main as memory_main +from mem0.memory.main import AsyncMemory, Memory + + +def make_sync_memory(): + memory = Memory.__new__(Memory) + memory.vector_store = MagicMock() + memory._delete_memory = MagicMock() + return memory + + +def make_async_memory(): + memory = AsyncMemory.__new__(AsyncMemory) + memory.vector_store = MagicMock() + memory._delete_memory = AsyncMock() + return memory + + +def test_sync_delete_decay_usage_runs_after_success(monkeypatch): + memory = make_sync_memory() + existing_memory = SimpleNamespace(id="memory-1") + memory.vector_store.get.return_value = existing_memory + decay_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr( + memory_main, + "detect_decay_usage_from_delete", + MagicMock(return_value=("delete_count", "repeated_deletes", 5, None)), + ) + monkeypatch.setattr(memory_main, "display_decay_usage_notice", decay_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + result = Memory.delete(memory, "memory-1") + + assert result == {"message": "Memory deleted successfully!"} + memory._delete_memory.assert_called_once_with("memory-1", existing_memory) + decay_notice.assert_called_once_with( + memory, + "sync", + "delete", + "delete_count", + "repeated_deletes", + 5, + None, + ) + first_run_notice.assert_not_called() + + +def test_sync_delete_below_threshold_uses_first_run_notice(monkeypatch): + memory = make_sync_memory() + memory.vector_store.get.return_value = SimpleNamespace(id="memory-1") + decay_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main, "detect_decay_usage_from_delete", MagicMock(return_value=None)) + monkeypatch.setattr(memory_main, "display_decay_usage_notice", decay_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + Memory.delete(memory, "memory-1") + + decay_notice.assert_not_called() + first_run_notice.assert_called_once_with(memory, "sync", "delete") + + +def test_sync_delete_failure_does_not_trigger_decay_usage_notice(monkeypatch): + memory = make_sync_memory() + memory.vector_store.get.return_value = None + detect_decay = MagicMock() + decay_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main, "detect_decay_usage_from_delete", detect_decay) + monkeypatch.setattr(memory_main, "display_decay_usage_notice", decay_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + with pytest.raises(ValueError, match="Memory with id memory-1 not found"): + Memory.delete(memory, "memory-1") + + detect_decay.assert_not_called() + decay_notice.assert_not_called() + first_run_notice.assert_not_called() + + +def test_sync_delete_all_decay_usage_runs_after_success(monkeypatch): + memory = make_sync_memory() + memories = [SimpleNamespace(id="memory-1"), SimpleNamespace(id="memory-2")] + memory.vector_store.list.return_value = (memories, None) + decay_notice = MagicMock() + first_run_notice = MagicMock() + detect_decay = MagicMock(return_value=("delete_all", "bulk_delete", None, 2)) + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main, "detect_decay_usage_from_delete_all", detect_decay) + monkeypatch.setattr(memory_main, "display_decay_usage_notice", decay_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + result = Memory.delete_all(memory, user_id="u1") + + assert result == {"message": "Memories deleted successfully!"} + assert memory._delete_memory.call_count == 2 + detect_decay.assert_called_once_with(2) + decay_notice.assert_called_once_with( + memory, + "sync", + "delete_all", + "delete_all", + "bulk_delete", + None, + 2, + ) + first_run_notice.assert_not_called() + + +def test_sync_delete_all_zero_deletes_uses_first_run_notice(monkeypatch): + memory = make_sync_memory() + memory.vector_store.list.return_value = ([], None) + decay_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main, "detect_decay_usage_from_delete_all", MagicMock(return_value=None)) + monkeypatch.setattr(memory_main, "display_decay_usage_notice", decay_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + Memory.delete_all(memory, user_id="u1") + + decay_notice.assert_not_called() + first_run_notice.assert_called_once_with(memory, "sync", "delete_all") + + +@pytest.mark.asyncio +async def test_async_delete_decay_usage_runs_after_success(monkeypatch): + memory = make_async_memory() + existing_memory = SimpleNamespace(id="memory-1") + memory.vector_store.get.return_value = existing_memory + decay_notice = AsyncMock() + first_run_notice = AsyncMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr( + memory_main, + "detect_decay_usage_from_delete", + MagicMock(return_value=("delete_count", "repeated_deletes", 5, None)), + ) + monkeypatch.setattr(memory_main, "display_decay_usage_notice_async", decay_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice_async", first_run_notice) + + result = await AsyncMemory.delete(memory, "memory-1") + + assert result == {"message": "Memory deleted successfully!"} + memory._delete_memory.assert_awaited_once_with("memory-1", existing_memory) + decay_notice.assert_awaited_once_with( + memory, + "async", + "delete", + "delete_count", + "repeated_deletes", + 5, + None, + ) + first_run_notice.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_async_delete_failure_does_not_trigger_decay_usage_notice(monkeypatch): + memory = make_async_memory() + memory.vector_store.get.return_value = None + detect_decay = MagicMock() + decay_notice = AsyncMock() + first_run_notice = AsyncMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main, "detect_decay_usage_from_delete", detect_decay) + monkeypatch.setattr(memory_main, "display_decay_usage_notice_async", decay_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice_async", first_run_notice) + + with pytest.raises(ValueError, match="Memory with id memory-1 not found"): + await AsyncMemory.delete(memory, "memory-1") + + detect_decay.assert_not_called() + decay_notice.assert_not_awaited() + first_run_notice.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_async_delete_all_decay_usage_runs_after_success(monkeypatch): + memory = make_async_memory() + memories = [SimpleNamespace(id="memory-1"), SimpleNamespace(id="memory-2")] + memory.vector_store.list.return_value = (memories, None) + decay_notice = AsyncMock() + first_run_notice = AsyncMock() + detect_decay = MagicMock(return_value=("delete_all", "bulk_delete", None, 2)) + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main, "detect_decay_usage_from_delete_all", detect_decay) + monkeypatch.setattr(memory_main, "display_decay_usage_notice_async", decay_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice_async", first_run_notice) + + result = await AsyncMemory.delete_all(memory, user_id="u1") + + assert result == {"message": "Memories deleted successfully!"} + assert memory._delete_memory.await_count == 2 + detect_decay.assert_called_once_with(2) + decay_notice.assert_awaited_once_with( + memory, + "async", + "delete_all", + "delete_all", + "bulk_delete", + None, + 2, + ) + first_run_notice.assert_not_awaited() diff --git a/tests/memory/test_main.py b/tests/memory/test_main.py index 677e7fe4c..6e3e75064 100644 --- a/tests/memory/test_main.py +++ b/tests/memory/test_main.py @@ -828,7 +828,7 @@ class TestEntityBoostParallelism: boosts = mock_memory._compute_entity_boosts(entities, {"user_id": "u1"}) elapsed = time.perf_counter() - start - assert elapsed < 0.6, f"searches did not run concurrently (took {elapsed:.2f}s)" + assert elapsed < 0.75, f"searches did not run concurrently (took {elapsed:.2f}s)" assert concurrent_count["peak"] >= 2, "no overlap observed between entity searches" assert len(boosts) == 4 @@ -854,6 +854,6 @@ class TestEntityBoostParallelism: boosts = await mock_async_memory._compute_entity_boosts_async(entities, {"user_id": "u1"}) elapsed = time.perf_counter() - start - assert elapsed < 0.6, f"searches did not run concurrently (took {elapsed:.2f}s)" + assert elapsed < 0.75, f"searches did not run concurrently (took {elapsed:.2f}s)" assert concurrent_count["peak"] >= 2, "no overlap observed between entity searches" assert len(boosts) == 4 diff --git a/tests/memory/test_memory_utils.py b/tests/memory/test_memory_utils.py index 41a02f76d..8cf39475a 100644 --- a/tests/memory/test_memory_utils.py +++ b/tests/memory/test_memory_utils.py @@ -1,5 +1,60 @@ import pytest -from mem0.memory.utils import remove_spaces_from_entities, sanitize_relationship_for_cypher +from unittest.mock import Mock + +from mem0.memory.utils import parse_vision_messages, remove_spaces_from_entities, sanitize_relationship_for_cypher + + +class TestParseVisionMessages: + def test_multimodal_list_without_llm_extracts_text(self): + messages = [ + {"role": "user", "content": [ + {"type": "text", "text": "What is this?"}, + {"type": "image_url", "image_url": {"url": "https://example.com/img.png"}}, + ]}, + ] + result = parse_vision_messages(messages, llm=None) + assert len(result) == 1 + assert result[0]["role"] == "user" + assert result[0]["content"] == "What is this?" + + def test_image_dict_without_llm_is_skipped(self): + messages = [ + {"role": "user", "content": {"type": "image_url", "image_url": {"url": "https://example.com/img.png"}}}, + {"role": "user", "content": "hello"}, + ] + result = parse_vision_messages(messages, llm=None) + assert len(result) == 1 + assert result[0]["content"] == "hello" + + def test_multimodal_with_llm_calls_generate_response(self): + mock_llm = Mock() + mock_llm.generate_response.return_value = "A photo of a cat" + messages = [ + {"role": "user", "content": [ + {"type": "text", "text": "Describe this"}, + {"type": "image_url", "image_url": {"url": "https://example.com/cat.png"}}, + ]}, + ] + result = parse_vision_messages(messages, llm=mock_llm, vision_details="auto") + assert result[0]["content"] == "A photo of a cat" + mock_llm.generate_response.assert_called_once() + + def test_image_only_list_without_llm_is_skipped(self): + messages = [ + {"role": "user", "content": [ + {"type": "image_url", "image_url": {"url": "https://example.com/img.png"}}, + ]}, + ] + result = parse_vision_messages(messages, llm=None) + assert result == [] + + def test_plain_text_messages_pass_through(self): + messages = [ + {"role": "system", "content": "You are helpful."}, + {"role": "user", "content": "Hello"}, + ] + result = parse_vision_messages(messages, llm=None) + assert result == messages class TestRemoveSpacesFromEntities: diff --git a/tests/memory/test_notices.py b/tests/memory/test_notices.py new file mode 100644 index 000000000..f7ac6d1c4 --- /dev/null +++ b/tests/memory/test_notices.py @@ -0,0 +1,1519 @@ +import asyncio +from copy import deepcopy +from datetime import date, datetime, timedelta, timezone +from types import SimpleNamespace +from unittest.mock import MagicMock + +import pytest + +from mem0.memory import main as memory_main +from mem0.memory import notices +from mem0.memory import telemetry as telemetry_module +from mem0.memory.main import Memory + + +class FakeFlags: + def __init__(self, variant, payload): + self.variant = variant + self.payload = payload + + def get_flag(self, key): + assert key == notices.FLAG_KEY + return self.variant + + def get_flag_payload(self, key): + assert key == notices.FLAG_KEY + return self.payload + + +@pytest.fixture(autouse=True) +def reset_notice_process_state(): + notices._first_run_claimed_in_process = False + notices._decay_usage_successful_delete_count_in_process = 0 + notices._temporal_usage_capacity_reached_in_process = False + notices._decay_usage_capacity_reached_in_process = False + notices._scale_threshold_capacity_reached_in_process = False + notices._performance_slow_query_capacity_reached_in_process = False + notices._feature_error_capacity_reached_in_process.clear() + notices._scale_memory_count_adds_since_check = 0 + notices._scale_memory_count_checked_in_process = False + notices._scale_memory_count_threshold_evaluated_in_process = False + yield + notices._first_run_claimed_in_process = False + notices._decay_usage_successful_delete_count_in_process = 0 + notices._temporal_usage_capacity_reached_in_process = False + notices._decay_usage_capacity_reached_in_process = False + notices._scale_threshold_capacity_reached_in_process = False + notices._performance_slow_query_capacity_reached_in_process = False + notices._feature_error_capacity_reached_in_process.clear() + notices._scale_memory_count_adds_since_check = 0 + notices._scale_memory_count_checked_in_process = False + notices._scale_memory_count_threshold_evaluated_in_process = False + + +@pytest.fixture +def notice_harness(monkeypatch): + config = {} + telemetry = MagicMock() + telemetry.user_id = "oss-user" + + def write_config(updated): + saved = deepcopy(updated) + config.clear() + config.update(saved) + + monkeypatch.setattr(notices, "_load_config", lambda: config) + monkeypatch.setattr(notices, "_write_config", write_config) + monkeypatch.setattr(notices.telemetry_module, "MEM0_TELEMETRY", True) + monkeypatch.setattr(notices.telemetry_module, "_get_oss_telemetry", lambda: telemetry) + + return config, telemetry + + +def configure_flag(telemetry, variant, payload): + flags = FakeFlags(variant, payload) + telemetry.posthog.evaluate_flags.return_value = flags + return flags + + +def display_notice(notice_harness, variant="displayed", payload=None): + config, telemetry = notice_harness + if payload is None: + payload = { + "notices": { + "first_run": { + "enabled": True, + "notice_type": "log_line", + "copy": "Mem0 OSS notice", + } + } + } + flags = configure_flag(telemetry, variant, payload) + notices.display_first_run_notice(MagicMock(), "sync", "add") + return config, telemetry, flags + + +def temporal_payload(copy="Temporal CTA", enabled=True, notice_type="error"): + payload = { + "notices": { + "temporal_stub": { + "enabled": enabled, + "notice_type": notice_type, + } + } + } + if copy is not None: + payload["notices"]["temporal_stub"]["copy"] = copy + return payload + + +def decay_payload(copy="Decay CTA", enabled=True, notice_type="error"): + payload = { + "notices": { + "decay_stub": { + "enabled": enabled, + "notice_type": notice_type, + } + } + } + if copy is not None: + payload["notices"]["decay_stub"]["copy"] = copy + return payload + + +def temporal_usage_payload(copy="Temporal usage CTA", enabled=True, notice_type="log_line"): + payload = { + "notices": { + "temporal_usage": { + "enabled": enabled, + "notice_type": notice_type, + } + } + } + if copy is not None: + payload["notices"]["temporal_usage"]["copy"] = copy + return payload + + +def decay_usage_payload(copy="Decay usage CTA", enabled=True, notice_type="log_line"): + payload = { + "notices": { + "decay_usage": { + "enabled": enabled, + "notice_type": notice_type, + } + } + } + if copy is not None: + payload["notices"]["decay_usage"]["copy"] = copy + return payload + + +def scale_payload( + top_k_copy="Scale top {top_k}", + memory_count_copy="Scale count {memory_count}", + enabled=True, + notice_type="log_line", +): + payload = { + "notices": { + "scale_threshold": { + "enabled": enabled, + "notice_type": notice_type, + "copies": {}, + } + } + } + copies = payload["notices"]["scale_threshold"]["copies"] + if top_k_copy is not None: + copies["top_k"] = top_k_copy + if memory_count_copy is not None: + copies["memory_count"] = memory_count_copy + return payload + + +def test_displayed_notice_logs_once_and_captures_event(notice_harness, capsys): + config, telemetry, flags = display_notice(notice_harness) + + assert capsys.readouterr().err == "Mem0 OSS notice\n" + telemetry.posthog.evaluate_flags.assert_called_once_with("oss-user", flag_keys=[notices.FLAG_KEY]) + telemetry.capture_event.assert_called_once() + event_name, props = telemetry.capture_event.call_args.args + assert event_name == notices.NOTICE_EVENT + assert props["notice_id"] == "first_run" + assert props["notice_type"] == "log_line" + assert props["variant"] == "displayed" + assert props["displayed"] is True + assert props["payload"] == "Mem0 OSS notice" + assert props["bypass_reason"] is None + assert props["notice_config_found"] is True + assert props["sync_type"] == "sync" + assert props["trigger_function"] == "add" + assert telemetry.capture_event.call_args.kwargs["flags"] is flags + assert config["notice_state"]["first_run"]["consumed"] is True + assert config["notice_state"]["first_run"]["variant"] == "displayed" + + notices.display_first_run_notice(MagicMock(), "sync", "search") + assert capsys.readouterr().err == "" + assert telemetry.capture_event.call_count == 1 + + +def test_holdout_notice_is_silent_but_captures_event(notice_harness, capsys): + _, telemetry, _ = display_notice(notice_harness, variant="holdout") + + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is False + assert props["bypass_reason"] == "holdout" + assert props["disabled_reason"] is None + + +@pytest.mark.parametrize( + ("payload", "expected_reason", "expected_found"), + [ + ({}, "missing_notice_config", False), + ({"notices": {}}, "missing_notice_config", False), + ({"notices": "not-an-object"}, "missing_notice_config", False), + ({"notices": {"first_run": {"enabled": True, "notice_type": "log_line"}}}, "missing_copy", True), + ( + {"notices": {"first_run": {"enabled": False, "notice_type": "log_line", "copy": "hidden"}}}, + "payload_disabled", + True, + ), + ], +) +def test_bad_or_disabled_payload_is_silent_and_safe( + notice_harness, payload, expected_reason, expected_found, capsys +): + _, telemetry, _ = display_notice(notice_harness, payload=payload) + + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is False + assert props["bypass_reason"] == expected_reason + assert props["notice_config_found"] is expected_found + + +def test_telemetry_disabled_does_not_touch_posthog_or_state(monkeypatch, capsys): + load_config = MagicMock(return_value={}) + write_config = MagicMock() + get_telemetry = MagicMock() + + monkeypatch.setattr(notices, "_load_config", load_config) + monkeypatch.setattr(notices, "_write_config", write_config) + monkeypatch.setattr(notices.telemetry_module, "MEM0_TELEMETRY", False) + monkeypatch.setattr(notices.telemetry_module, "_get_oss_telemetry", get_telemetry) + + notices.display_first_run_notice(MagicMock(), "sync", "add") + + load_config.assert_not_called() + write_config.assert_not_called() + get_telemetry.assert_not_called() + assert capsys.readouterr().err == "" + + +def test_posthog_failure_is_silent_and_consumes_first_run(notice_harness, capsys): + config, telemetry = notice_harness + telemetry.posthog.evaluate_flags.side_effect = RuntimeError("network unavailable") + + notices.display_first_run_notice(MagicMock(), "sync", "add") + + assert capsys.readouterr().err == "" + telemetry.capture_event.assert_not_called() + assert config["notice_state"]["first_run"]["consumed"] is True + + +def test_public_add_succeeds_when_first_run_flag_eval_fails(notice_harness): + _, telemetry = notice_harness + telemetry.posthog.evaluate_flags.side_effect = RuntimeError("network unavailable") + memory = Memory.__new__(Memory) + memory.config = SimpleNamespace(llm=SimpleNamespace(config={})) + memory._add_to_vector_store = MagicMock(return_value=[{"event": "ADD", "memory": "likes tea"}]) + + result = Memory.add(memory, "The user likes tea.", user_id="u1", infer=False) + + assert result == {"results": [{"event": "ADD", "memory": "likes tea"}]} + + +def test_public_search_succeeds_when_first_run_flag_eval_fails(notice_harness, monkeypatch): + _, telemetry = notice_harness + telemetry.posthog.evaluate_flags.side_effect = RuntimeError("network unavailable") + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + memory = Memory.__new__(Memory) + memory.api_version = "v1.1" + memory.reranker = None + memory._search_vector_store = MagicMock(return_value=[{"memory": "likes tea"}]) + + result = Memory.search(memory, "favorite drink", filters={"user_id": "u1"}) + + assert result == {"results": [{"memory": "likes tea"}]} + + +def test_notice_event_bypasses_sampling(): + assert notices.NOTICE_EVENT in telemetry_module._LIFECYCLE_EVENTS + + +def test_async_notice_wrapper_uses_shared_helper(monkeypatch): + calls = [] + + def display(memory_instance, sync_type, trigger_function): + calls.append((memory_instance, sync_type, trigger_function)) + + memory = MagicMock() + monkeypatch.setattr(notices, "display_first_run_notice", display) + + asyncio.run(notices.display_first_run_notice_async(memory, "async", "search")) + + assert calls == [(memory, "async", "search")] + + +def test_async_notice_wrapper_skips_thread_when_first_run_claimed(monkeypatch): + to_thread = MagicMock() + monkeypatch.setattr(notices.asyncio, "to_thread", to_thread) + notices._first_run_claimed_in_process = True + + asyncio.run(notices.display_first_run_notice_async(MagicMock(), "async", "search")) + + to_thread.assert_not_called() + + +def test_temporal_feature_displayed_returns_payload_copy_and_captures_event(notice_harness, capsys): + _, telemetry = notice_harness + flags = configure_flag(telemetry, "displayed", temporal_payload()) + + message = notices.get_temporal_feature_error_message("sync", "add", "timestamp") + + assert message == "Temporal CTA" + assert capsys.readouterr().err == "" + telemetry.posthog.evaluate_flags.assert_called_once_with("oss-user", flag_keys=[notices.FLAG_KEY]) + telemetry.capture_event.assert_called_once() + event_name, props = telemetry.capture_event.call_args.args + assert event_name == notices.NOTICE_EVENT + assert props["notice_id"] == "temporal_stub" + assert props["notice_type"] == "error" + assert props["variant"] == "displayed" + assert props["displayed"] is True + assert props["payload"] == "Temporal CTA" + assert props["bypass_reason"] is None + assert props["disabled_reason"] is None + assert props["notice_config_found"] is True + assert props["sync_type"] == "sync" + assert props["trigger_function"] == "add" + assert props["trigger_parameter"] == "timestamp" + assert telemetry.capture_event.call_args.kwargs["flags"] is flags + + +def test_temporal_feature_holdout_returns_payload_copy_and_captures_event(notice_harness, capsys): + _, telemetry = notice_harness + configure_flag(telemetry, "holdout", temporal_payload()) + + message = notices.get_temporal_feature_error_message("sync", "search", "reference_date") + + assert message == "Temporal CTA" + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is True + assert props["bypass_reason"] is None + assert props["trigger_function"] == "search" + assert props["trigger_parameter"] == "reference_date" + + +@pytest.mark.parametrize( + ("payload", "expected_reason", "expected_found"), + [ + ({}, "missing_notice_config", False), + ({"notices": {}}, "missing_notice_config", False), + ({"notices": "not-an-object"}, "missing_notice_config", False), + (temporal_payload(copy=None), "missing_copy", True), + (temporal_payload(enabled=False, copy="hidden"), "payload_disabled", True), + ], +) +def test_temporal_feature_bad_or_disabled_payload_returns_plain_error( + notice_harness, payload, expected_reason, expected_found, capsys +): + _, telemetry = notice_harness + configure_flag(telemetry, "displayed", payload) + + message = notices.get_temporal_feature_error_message("sync", "add", "timestamp") + + assert message == notices.TEMPORAL_FEATURE_ERROR_MESSAGES["timestamp"] + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is False + assert props["bypass_reason"] == expected_reason + assert props["notice_config_found"] is expected_found + + +@pytest.mark.parametrize("variant", [None, False]) +def test_temporal_feature_blunt_flag_disable_returns_plain_error_without_event( + notice_harness, variant, capsys +): + _, telemetry = notice_harness + configure_flag(telemetry, variant, temporal_payload()) + + message = notices.get_temporal_feature_error_message("sync", "add", "timestamp") + + assert message == notices.TEMPORAL_FEATURE_ERROR_MESSAGES["timestamp"] + assert capsys.readouterr().err == "" + telemetry.capture_event.assert_not_called() + + +def test_temporal_feature_telemetry_disabled_does_not_touch_posthog(monkeypatch, capsys): + get_telemetry = MagicMock() + + monkeypatch.setattr(notices.telemetry_module, "MEM0_TELEMETRY", False) + monkeypatch.setattr(notices.telemetry_module, "_get_oss_telemetry", get_telemetry) + + message = notices.get_temporal_feature_error_message("sync", "add", "timestamp") + + assert message == notices.TEMPORAL_FEATURE_ERROR_MESSAGES["timestamp"] + get_telemetry.assert_not_called() + assert capsys.readouterr().err == "" + + +def test_temporal_feature_posthog_failure_returns_plain_error(notice_harness, capsys): + _, telemetry = notice_harness + telemetry.posthog.evaluate_flags.side_effect = RuntimeError("network unavailable") + + message = notices.get_temporal_feature_error_message("sync", "add", "timestamp") + + assert message == notices.TEMPORAL_FEATURE_ERROR_MESSAGES["timestamp"] + telemetry.capture_event.assert_not_called() + assert capsys.readouterr().err == "" + + +def test_temporal_feature_cap_blocks_repeated_posthog_evaluation(notice_harness): + config, telemetry = notice_harness + configure_flag(telemetry, "displayed", temporal_payload()) + + for _ in range(notices.FEATURE_ERROR_CAP): + assert notices.get_temporal_feature_error_message("sync", "add", "timestamp") == "Temporal CTA" + + assert telemetry.posthog.evaluate_flags.call_count == notices.FEATURE_ERROR_CAP + assert telemetry.capture_event.call_count == notices.FEATURE_ERROR_CAP + assert len(config["notice_state"]["temporal_stub"]["events"]) == notices.FEATURE_ERROR_CAP + + message = notices.get_temporal_feature_error_message("sync", "add", "timestamp") + + assert message == notices.TEMPORAL_FEATURE_ERROR_MESSAGES["timestamp"] + assert telemetry.posthog.evaluate_flags.call_count == notices.FEATURE_ERROR_CAP + assert telemetry.capture_event.call_count == notices.FEATURE_ERROR_CAP + + +def test_async_temporal_feature_wrapper_uses_shared_helper(monkeypatch): + calls = [] + + def get_error(sync_type, trigger_function, trigger_parameter): + calls.append((sync_type, trigger_function, trigger_parameter)) + return "blocked" + + monkeypatch.setattr(notices, "get_temporal_feature_error_message", get_error) + + message = asyncio.run( + notices.get_temporal_feature_error_message_async("async", "search", "reference_date") + ) + + assert message == "blocked" + assert calls == [("async", "search", "reference_date")] + + +def test_decay_feature_displayed_returns_payload_copy_and_captures_event(notice_harness, capsys): + _, telemetry = notice_harness + flags = configure_flag(telemetry, "displayed", decay_payload()) + + message = notices.get_decay_feature_error_message("sync", "project.update", "decay") + + assert message == "Decay CTA" + assert capsys.readouterr().err == "" + telemetry.posthog.evaluate_flags.assert_called_once_with("oss-user", flag_keys=[notices.FLAG_KEY]) + telemetry.capture_event.assert_called_once() + event_name, props = telemetry.capture_event.call_args.args + assert event_name == notices.NOTICE_EVENT + assert props["notice_id"] == "decay_stub" + assert props["notice_type"] == "error" + assert props["variant"] == "displayed" + assert props["displayed"] is True + assert props["payload"] == "Decay CTA" + assert props["bypass_reason"] is None + assert props["disabled_reason"] is None + assert props["notice_config_found"] is True + assert props["sync_type"] == "sync" + assert props["trigger_function"] == "project.update" + assert props["trigger_parameter"] == "decay" + assert telemetry.capture_event.call_args.kwargs["flags"] is flags + + +def test_decay_feature_holdout_returns_payload_copy_and_captures_event(notice_harness, capsys): + _, telemetry = notice_harness + configure_flag(telemetry, "holdout", decay_payload()) + + message = notices.get_decay_feature_error_message("sync", "project.update", "decay") + + assert message == "Decay CTA" + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is True + assert props["bypass_reason"] is None + assert props["variant"] == "holdout" + + +@pytest.mark.parametrize( + ("payload", "expected_reason", "expected_found"), + [ + ({}, "missing_notice_config", False), + ({"notices": {}}, "missing_notice_config", False), + ({"notices": "not-an-object"}, "missing_notice_config", False), + (decay_payload(copy=None), "missing_copy", True), + (decay_payload(enabled=False, copy="hidden"), "payload_disabled", True), + ], +) +def test_decay_feature_bad_or_disabled_payload_returns_plain_error( + notice_harness, payload, expected_reason, expected_found, capsys +): + _, telemetry = notice_harness + configure_flag(telemetry, "displayed", payload) + + message = notices.get_decay_feature_error_message("sync", "project.update", "decay") + + assert message == notices.DECAY_FEATURE_ERROR_MESSAGE + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is False + assert props["bypass_reason"] == expected_reason + assert props["notice_config_found"] is expected_found + + +@pytest.mark.parametrize("variant", [None, False]) +def test_decay_feature_blunt_flag_disable_returns_plain_error_without_event( + notice_harness, variant, capsys +): + _, telemetry = notice_harness + configure_flag(telemetry, variant, decay_payload()) + + message = notices.get_decay_feature_error_message("sync", "project.update", "decay") + + assert message == notices.DECAY_FEATURE_ERROR_MESSAGE + assert capsys.readouterr().err == "" + telemetry.capture_event.assert_not_called() + + +def test_decay_feature_telemetry_disabled_does_not_touch_posthog(monkeypatch, capsys): + get_telemetry = MagicMock() + + monkeypatch.setattr(notices.telemetry_module, "MEM0_TELEMETRY", False) + monkeypatch.setattr(notices.telemetry_module, "_get_oss_telemetry", get_telemetry) + + message = notices.get_decay_feature_error_message("sync", "project.update", "decay") + + assert message == notices.DECAY_FEATURE_ERROR_MESSAGE + get_telemetry.assert_not_called() + assert capsys.readouterr().err == "" + + +def test_decay_feature_posthog_failure_returns_plain_error(notice_harness, capsys): + _, telemetry = notice_harness + telemetry.posthog.evaluate_flags.side_effect = RuntimeError("network unavailable") + + message = notices.get_decay_feature_error_message("sync", "project.update", "decay") + + assert message == notices.DECAY_FEATURE_ERROR_MESSAGE + telemetry.capture_event.assert_not_called() + assert capsys.readouterr().err == "" + + +def test_decay_feature_cap_is_independent_from_temporal_feature_cap(notice_harness): + config, telemetry = notice_harness + configure_flag(telemetry, "displayed", temporal_payload()) + + for _ in range(notices.FEATURE_ERROR_CAP): + assert notices.get_temporal_feature_error_message("sync", "add", "timestamp") == "Temporal CTA" + + configure_flag(telemetry, "displayed", decay_payload()) + assert notices.get_decay_feature_error_message("sync", "project.update", "decay") == "Decay CTA" + + assert len(config["notice_state"]["temporal_stub"]["events"]) == notices.FEATURE_ERROR_CAP + assert len(config["notice_state"]["decay_stub"]["events"]) == 1 + + +def test_decay_feature_cap_blocks_repeated_posthog_evaluation(notice_harness): + config, telemetry = notice_harness + configure_flag(telemetry, "displayed", decay_payload()) + + for _ in range(notices.FEATURE_ERROR_CAP): + assert notices.get_decay_feature_error_message("sync", "project.update", "decay") == "Decay CTA" + + assert telemetry.posthog.evaluate_flags.call_count == notices.FEATURE_ERROR_CAP + assert telemetry.capture_event.call_count == notices.FEATURE_ERROR_CAP + assert len(config["notice_state"]["decay_stub"]["events"]) == notices.FEATURE_ERROR_CAP + + message = notices.get_decay_feature_error_message("sync", "project.update", "decay") + + assert message == notices.DECAY_FEATURE_ERROR_MESSAGE + assert telemetry.posthog.evaluate_flags.call_count == notices.FEATURE_ERROR_CAP + assert telemetry.capture_event.call_count == notices.FEATURE_ERROR_CAP + + +def test_async_decay_feature_wrapper_uses_shared_helper(monkeypatch): + calls = [] + + def get_error(sync_type, trigger_function, trigger_parameter): + calls.append((sync_type, trigger_function, trigger_parameter)) + return "blocked" + + monkeypatch.setattr(notices, "get_decay_feature_error_message", get_error) + + message = asyncio.run( + notices.get_decay_feature_error_message_async("async", "project.update", "decay") + ) + + assert message == "blocked" + assert calls == [("async", "project.update", "decay")] + + +def test_temporal_usage_query_detection_is_conservative(): + assert notices.detect_temporal_usage_from_search("what happened last week?", None) == ( + "query", + "relative_phrase", + ) + assert notices.detect_temporal_usage_from_search("notes from 2025-04-09", None) == ( + "query", + "date_like_query", + ) + assert notices.detect_temporal_usage_from_search("favorite drink", None) is None + + +def test_temporal_usage_metadata_detection(): + assert notices.detect_temporal_usage_from_metadata({"event_date": "2025-04-09"}) == ( + "metadata", + "date_like_metadata", + ) + assert notices.detect_temporal_usage_from_metadata({"timestamp": 1778112000}) == ( + "metadata", + "date_like_metadata", + ) + assert notices.detect_temporal_usage_from_metadata({"nested": {"started_at": datetime.now(timezone.utc)}}) == ( + "metadata", + "date_like_metadata", + ) + assert notices.detect_temporal_usage_from_metadata({"category": "planning"}) is None + assert notices.detect_temporal_usage_from_metadata({"notes": "we met yesterday"}) is None + assert notices.detect_temporal_usage_from_metadata({"category": "2025-04-09"}) is None + + +def test_temporal_usage_metadata_detection_never_raises_for_cyclic_input(): + metadata = {} + metadata["self"] = metadata + + assert notices.detect_temporal_usage_from_metadata(metadata) is None + + +def test_temporal_usage_filter_detection(): + filters = {"AND": [{"user_id": "u1"}, {"created_at": {"gte": "2025-04-01"}}]} + assert notices.detect_temporal_usage_from_search("favorite drink", filters) == ( + "filter", + "date_range_filter", + ) + assert notices.detect_temporal_usage_from_search("favorite drink", {"score": {"gte": 0.5}}) is None + + +def test_temporal_usage_search_detection_never_raises_for_cyclic_filters(): + filters = {} + filters["AND"] = [filters] + + assert notices.detect_temporal_usage_from_search("favorite drink", filters) is None + + +def test_temporal_usage_displayed_logs_and_captures_event(notice_harness, capsys): + config, telemetry = notice_harness + flags = configure_flag(telemetry, "displayed", temporal_usage_payload()) + + notices.display_temporal_usage_notice(MagicMock(), "sync", "search", "query", "relative_phrase") + + assert capsys.readouterr().err == "Temporal usage CTA\n" + telemetry.posthog.evaluate_flags.assert_called_once_with("oss-user", flag_keys=[notices.FLAG_KEY]) + telemetry.capture_event.assert_called_once() + event_name, props = telemetry.capture_event.call_args.args + assert event_name == notices.NOTICE_EVENT + assert props["notice_id"] == "temporal_usage" + assert props["notice_type"] == "log_line" + assert props["variant"] == "displayed" + assert props["displayed"] is True + assert props["payload"] == "Temporal usage CTA" + assert props["bypass_reason"] is None + assert props["disabled_reason"] is None + assert props["notice_config_found"] is True + assert props["sync_type"] == "sync" + assert props["trigger_function"] == "search" + assert props["trigger_source"] == "query" + assert props["trigger_reason"] == "relative_phrase" + assert telemetry.capture_event.call_args.kwargs["flags"] is flags + assert len(config["notice_state"]["temporal_usage"]["events"]) == 1 + + +def test_temporal_usage_holdout_is_silent_but_captures_event(notice_harness, capsys): + _, telemetry = notice_harness + configure_flag(telemetry, "holdout", temporal_usage_payload()) + + notices.display_temporal_usage_notice(MagicMock(), "sync", "add", "metadata", "date_like_metadata") + + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is False + assert props["bypass_reason"] == "holdout" + assert props["trigger_source"] == "metadata" + assert props["trigger_reason"] == "date_like_metadata" + + +@pytest.mark.parametrize( + ("payload", "expected_reason", "expected_found"), + [ + ({}, "missing_notice_config", False), + ({"notices": {}}, "missing_notice_config", False), + ({"notices": "not-an-object"}, "missing_notice_config", False), + (temporal_usage_payload(copy=None), "missing_copy", True), + (temporal_usage_payload(enabled=False, copy="hidden"), "payload_disabled", True), + ], +) +def test_temporal_usage_bad_or_disabled_payload_is_silent_and_safe( + notice_harness, payload, expected_reason, expected_found, capsys +): + _, telemetry = notice_harness + configure_flag(telemetry, "displayed", payload) + + notices.display_temporal_usage_notice(MagicMock(), "sync", "search", "query", "relative_phrase") + + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is False + assert props["bypass_reason"] == expected_reason + assert props["notice_config_found"] is expected_found + + +@pytest.mark.parametrize("variant", [None, False]) +def test_temporal_usage_blunt_flag_disable_does_not_capture_or_consume( + notice_harness, variant, capsys +): + config, telemetry = notice_harness + configure_flag(telemetry, variant, temporal_usage_payload()) + + notices.display_temporal_usage_notice(MagicMock(), "sync", "search", "query", "relative_phrase") + + assert capsys.readouterr().err == "" + telemetry.capture_event.assert_not_called() + assert config.get("notice_state") is None + + +def test_temporal_usage_telemetry_disabled_does_not_touch_posthog_or_state(monkeypatch, capsys): + load_config = MagicMock(return_value={}) + write_config = MagicMock() + get_telemetry = MagicMock() + + monkeypatch.setattr(notices, "_load_config", load_config) + monkeypatch.setattr(notices, "_write_config", write_config) + monkeypatch.setattr(notices.telemetry_module, "MEM0_TELEMETRY", False) + monkeypatch.setattr(notices.telemetry_module, "_get_oss_telemetry", get_telemetry) + + notices.display_temporal_usage_notice(MagicMock(), "sync", "search", "query", "relative_phrase") + + load_config.assert_not_called() + write_config.assert_not_called() + get_telemetry.assert_not_called() + assert capsys.readouterr().err == "" + + +def test_temporal_usage_cap_blocks_before_posthog_eval(notice_harness, capsys): + config, telemetry = notice_harness + configure_flag(telemetry, "displayed", temporal_usage_payload()) + + for _ in range(notices.TEMPORAL_USAGE_CAP): + notices.display_temporal_usage_notice(MagicMock(), "sync", "search", "query", "relative_phrase") + + notices.display_temporal_usage_notice(MagicMock(), "sync", "search", "query", "relative_phrase") + + assert capsys.readouterr().err == "Temporal usage CTA\n" * notices.TEMPORAL_USAGE_CAP + assert telemetry.posthog.evaluate_flags.call_count == notices.TEMPORAL_USAGE_CAP + assert telemetry.capture_event.call_count == notices.TEMPORAL_USAGE_CAP + assert len(config["notice_state"]["temporal_usage"]["events"]) == notices.TEMPORAL_USAGE_CAP + + +def test_temporal_usage_cap_ignores_old_entries(notice_harness, capsys): + config, telemetry = notice_harness + old_time = datetime.now(timezone.utc) - notices.TEMPORAL_USAGE_WINDOW - timedelta(days=1) + config["notice_state"] = { + "temporal_usage": { + "events": [ + {"evaluated_at": old_time.isoformat(), "variant": "displayed"} + for _ in range(notices.TEMPORAL_USAGE_CAP) + ] + } + } + configure_flag(telemetry, "displayed", temporal_usage_payload()) + + notices.display_temporal_usage_notice(MagicMock(), "sync", "search", "query", "relative_phrase") + + assert capsys.readouterr().err == "Temporal usage CTA\n" + assert telemetry.capture_event.call_count == 1 + assert len(config["notice_state"]["temporal_usage"]["events"]) == 1 + + +def test_temporal_usage_props_do_not_include_raw_user_inputs(notice_harness): + _, telemetry = notice_harness + configure_flag(telemetry, "displayed", temporal_usage_payload(copy="safe copy")) + + notices.display_temporal_usage_notice(MagicMock(), "sync", "search", "query", "relative_phrase") + + props = telemetry.capture_event.call_args.args[1] + assert "what happened last week" not in str(props) + assert "2025-04-09" not in str(props) + assert date.today().isoformat() not in str(props) + + +def test_decay_usage_delete_detection_reaches_threshold(notice_harness): + config, _ = notice_harness + + for _ in range(notices.DECAY_USAGE_DELETE_THRESHOLD - 1): + assert notices.detect_decay_usage_from_delete() is None + + assert config == {} + assert notices.detect_decay_usage_from_delete() == ( + "delete_count", + "repeated_deletes", + 5, + None, + ) + assert config == {} + + +def test_decay_usage_delete_detection_does_not_write_before_threshold(monkeypatch, notice_harness): + _, _ = notice_harness + write_config = MagicMock() + monkeypatch.setattr(notices, "_write_config", write_config) + + for _ in range(notices.DECAY_USAGE_DELETE_THRESHOLD - 1): + assert notices.detect_decay_usage_from_delete() is None + + write_config.assert_not_called() + + +def test_decay_usage_delete_detection_telemetry_disabled_does_not_write_state(monkeypatch, notice_harness): + config, _ = notice_harness + monkeypatch.setattr(notices.telemetry_module, "MEM0_TELEMETRY", False) + + assert notices.detect_decay_usage_from_delete() is None + + assert config == {} + + +def test_decay_usage_delete_all_detection_requires_deleted_memory(notice_harness): + assert notices.detect_decay_usage_from_delete_all(0) is None + assert notices.detect_decay_usage_from_delete_all(None) is None + assert notices.detect_decay_usage_from_delete_all(3) == ( + "delete_all", + "bulk_delete", + None, + 3, + ) + + +def test_decay_usage_displayed_logs_and_captures_event(notice_harness, capsys): + config, telemetry = notice_harness + flags = configure_flag(telemetry, "displayed", decay_usage_payload()) + + notices.display_decay_usage_notice( + MagicMock(), + "sync", + "delete", + "delete_count", + "repeated_deletes", + delete_count=5, + ) + + assert capsys.readouterr().err == "Decay usage CTA\n" + telemetry.posthog.evaluate_flags.assert_called_once_with("oss-user", flag_keys=[notices.FLAG_KEY]) + telemetry.capture_event.assert_called_once() + event_name, props = telemetry.capture_event.call_args.args + assert event_name == notices.NOTICE_EVENT + assert props["notice_id"] == "decay_usage" + assert props["notice_type"] == "log_line" + assert props["variant"] == "displayed" + assert props["displayed"] is True + assert props["payload"] == "Decay usage CTA" + assert props["bypass_reason"] is None + assert props["disabled_reason"] is None + assert props["notice_config_found"] is True + assert props["sync_type"] == "sync" + assert props["trigger_function"] == "delete" + assert props["trigger_source"] == "delete_count" + assert props["trigger_reason"] == "repeated_deletes" + assert props["delete_count"] == 5 + assert props["deleted_count"] is None + assert telemetry.capture_event.call_args.kwargs["flags"] is flags + assert len(config["notice_state"]["decay_usage"]["events"]) == 1 + + +def test_decay_usage_holdout_is_silent_but_captures_event(notice_harness, capsys): + _, telemetry = notice_harness + configure_flag(telemetry, "holdout", decay_usage_payload()) + + notices.display_decay_usage_notice( + MagicMock(), + "sync", + "delete_all", + "delete_all", + "bulk_delete", + deleted_count=3, + ) + + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is False + assert props["bypass_reason"] == "holdout" + assert props["trigger_source"] == "delete_all" + assert props["trigger_reason"] == "bulk_delete" + assert props["delete_count"] is None + assert props["deleted_count"] == 3 + + +@pytest.mark.parametrize( + ("payload", "expected_reason", "expected_found"), + [ + ({}, "missing_notice_config", False), + ({"notices": {}}, "missing_notice_config", False), + ({"notices": "not-an-object"}, "missing_notice_config", False), + (decay_usage_payload(copy=None), "missing_copy", True), + (decay_usage_payload(enabled=False, copy="hidden"), "payload_disabled", True), + ], +) +def test_decay_usage_bad_or_disabled_payload_is_silent_and_safe( + notice_harness, payload, expected_reason, expected_found, capsys +): + _, telemetry = notice_harness + configure_flag(telemetry, "displayed", payload) + + notices.display_decay_usage_notice( + MagicMock(), + "sync", + "delete", + "delete_count", + "repeated_deletes", + delete_count=5, + ) + + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is False + assert props["bypass_reason"] == expected_reason + assert props["notice_config_found"] is expected_found + + +@pytest.mark.parametrize("variant", [None, False]) +def test_decay_usage_blunt_flag_disable_does_not_capture_or_consume( + notice_harness, variant, capsys +): + config, telemetry = notice_harness + configure_flag(telemetry, variant, decay_usage_payload()) + + notices.display_decay_usage_notice( + MagicMock(), + "sync", + "delete", + "delete_count", + "repeated_deletes", + delete_count=5, + ) + + assert capsys.readouterr().err == "" + telemetry.capture_event.assert_not_called() + assert config.get("notice_state") is None + + +def test_decay_usage_telemetry_disabled_does_not_touch_posthog_or_state(monkeypatch, capsys): + load_config = MagicMock(return_value={}) + write_config = MagicMock() + get_telemetry = MagicMock() + + monkeypatch.setattr(notices, "_load_config", load_config) + monkeypatch.setattr(notices, "_write_config", write_config) + monkeypatch.setattr(notices.telemetry_module, "MEM0_TELEMETRY", False) + monkeypatch.setattr(notices.telemetry_module, "_get_oss_telemetry", get_telemetry) + + notices.display_decay_usage_notice( + MagicMock(), + "sync", + "delete", + "delete_count", + "repeated_deletes", + delete_count=5, + ) + + load_config.assert_not_called() + write_config.assert_not_called() + get_telemetry.assert_not_called() + assert capsys.readouterr().err == "" + + +def test_decay_usage_posthog_failure_does_not_consume_cap(notice_harness, capsys): + config, telemetry = notice_harness + telemetry.posthog.evaluate_flags.side_effect = RuntimeError("posthog down") + + notices.display_decay_usage_notice( + MagicMock(), + "sync", + "delete", + "delete_count", + "repeated_deletes", + delete_count=5, + ) + + assert capsys.readouterr().err == "" + telemetry.capture_event.assert_not_called() + assert config.get("notice_state") is None + + +def test_decay_usage_cap_blocks_before_posthog_eval(notice_harness, capsys): + config, telemetry = notice_harness + configure_flag(telemetry, "displayed", decay_usage_payload()) + + for _ in range(notices.DECAY_USAGE_CAP): + notices.display_decay_usage_notice( + MagicMock(), + "sync", + "delete", + "delete_count", + "repeated_deletes", + delete_count=5, + ) + + notices.display_decay_usage_notice( + MagicMock(), + "sync", + "delete", + "delete_count", + "repeated_deletes", + delete_count=5, + ) + + assert capsys.readouterr().err == "Decay usage CTA\n" * notices.DECAY_USAGE_CAP + assert telemetry.posthog.evaluate_flags.call_count == notices.DECAY_USAGE_CAP + assert telemetry.capture_event.call_count == notices.DECAY_USAGE_CAP + assert len(config["notice_state"]["decay_usage"]["events"]) == notices.DECAY_USAGE_CAP + + +def test_decay_usage_delete_detection_stops_after_cap(notice_harness): + config, telemetry = notice_harness + configure_flag(telemetry, "displayed", decay_usage_payload()) + + for _ in range(notices.DECAY_USAGE_CAP): + notices.display_decay_usage_notice( + MagicMock(), + "sync", + "delete", + "delete_count", + "repeated_deletes", + delete_count=5, + ) + + notices._decay_usage_successful_delete_count_in_process = notices.DECAY_USAGE_DELETE_THRESHOLD + assert notices.detect_decay_usage_from_delete() is None + assert len(config["notice_state"]["decay_usage"]["events"]) == notices.DECAY_USAGE_CAP + + +def test_decay_usage_cap_ignores_old_entries(notice_harness, capsys): + config, telemetry = notice_harness + old_time = datetime.now(timezone.utc) - notices.DECAY_USAGE_WINDOW - timedelta(days=1) + config["notice_state"] = { + "decay_usage": { + "events": [ + {"evaluated_at": old_time.isoformat(), "variant": "displayed"} + for _ in range(notices.DECAY_USAGE_CAP) + ] + } + } + configure_flag(telemetry, "displayed", decay_usage_payload()) + + notices.display_decay_usage_notice( + MagicMock(), + "sync", + "delete_all", + "delete_all", + "bulk_delete", + deleted_count=2, + ) + + assert capsys.readouterr().err == "Decay usage CTA\n" + assert telemetry.capture_event.call_count == 1 + assert len(config["notice_state"]["decay_usage"]["events"]) == 1 + + +def test_scale_threshold_top_k_detection(): + assert notices.detect_scale_threshold_from_top_k(49) is None + assert notices.detect_scale_threshold_from_top_k(50) == ( + "top_k", + "high_top_k", + 50, + None, + notices.SCALE_TOP_K_THRESHOLD, + ) + + +def test_scale_threshold_displayed_logs_and_captures_event(notice_harness, capsys): + config, telemetry = notice_harness + flags = configure_flag(telemetry, "displayed", scale_payload()) + + notices.display_scale_threshold_notice( + MagicMock(), + "sync", + "search", + "top_k", + "high_top_k", + top_k=50, + threshold=notices.SCALE_TOP_K_THRESHOLD, + ) + + assert capsys.readouterr().err == "Scale top 50\n" + telemetry.posthog.evaluate_flags.assert_called_once_with("oss-user", flag_keys=[notices.FLAG_KEY]) + telemetry.capture_event.assert_called_once() + event_name, props = telemetry.capture_event.call_args.args + assert event_name == notices.NOTICE_EVENT + assert props["notice_id"] == "scale_threshold" + assert props["notice_type"] == "log_line" + assert props["variant"] == "displayed" + assert props["displayed"] is True + assert props["payload"] == "Scale top 50" + assert props["bypass_reason"] is None + assert props["disabled_reason"] is None + assert props["notice_config_found"] is True + assert props["sync_type"] == "sync" + assert props["trigger_function"] == "search" + assert props["trigger_source"] == "top_k" + assert props["trigger_reason"] == "high_top_k" + assert props["top_k"] == 50 + assert props["memory_count"] is None + assert props["threshold"] == notices.SCALE_TOP_K_THRESHOLD + assert telemetry.capture_event.call_args.kwargs["flags"] is flags + assert len(config["notice_state"]["scale_threshold"]["events"]) == 1 + + +def test_scale_threshold_holdout_is_silent_but_captures_event(notice_harness, capsys): + _, telemetry = notice_harness + configure_flag(telemetry, "holdout", scale_payload()) + + notices.display_scale_threshold_notice( + MagicMock(), + "sync", + "get_all", + "top_k", + "high_top_k", + top_k=50, + threshold=notices.SCALE_TOP_K_THRESHOLD, + ) + + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is False + assert props["bypass_reason"] == "holdout" + assert props["trigger_function"] == "get_all" + + +@pytest.mark.parametrize( + ("payload", "expected_reason", "expected_found"), + [ + ({}, "missing_notice_config", False), + ({"notices": {}}, "missing_notice_config", False), + ({"notices": "not-an-object"}, "missing_notice_config", False), + (scale_payload(top_k_copy=None), "missing_copy", True), + (scale_payload(enabled=False, top_k_copy="hidden"), "payload_disabled", True), + ], +) +def test_scale_threshold_bad_or_disabled_payload_is_silent_and_safe( + notice_harness, payload, expected_reason, expected_found, capsys +): + _, telemetry = notice_harness + configure_flag(telemetry, "displayed", payload) + + notices.display_scale_threshold_notice( + MagicMock(), + "sync", + "search", + "top_k", + "high_top_k", + top_k=50, + threshold=notices.SCALE_TOP_K_THRESHOLD, + ) + + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is False + assert props["bypass_reason"] == expected_reason + assert props["notice_config_found"] is expected_found + + +@pytest.mark.parametrize("variant", [None, False]) +def test_scale_threshold_blunt_flag_disable_does_not_capture_or_consume( + notice_harness, variant, capsys +): + config, telemetry = notice_harness + configure_flag(telemetry, variant, scale_payload()) + + notices.display_scale_threshold_notice( + MagicMock(), + "sync", + "search", + "top_k", + "high_top_k", + top_k=50, + threshold=notices.SCALE_TOP_K_THRESHOLD, + ) + + assert capsys.readouterr().err == "" + telemetry.capture_event.assert_not_called() + assert config.get("notice_state") is None + + +def test_scale_threshold_telemetry_disabled_does_not_touch_posthog_or_state(monkeypatch, capsys): + load_config = MagicMock(return_value={}) + write_config = MagicMock() + get_telemetry = MagicMock() + + monkeypatch.setattr(notices, "_load_config", load_config) + monkeypatch.setattr(notices, "_write_config", write_config) + monkeypatch.setattr(notices.telemetry_module, "MEM0_TELEMETRY", False) + monkeypatch.setattr(notices.telemetry_module, "_get_oss_telemetry", get_telemetry) + + notices.display_scale_threshold_notice( + MagicMock(), + "sync", + "search", + "top_k", + "high_top_k", + top_k=50, + threshold=notices.SCALE_TOP_K_THRESHOLD, + ) + + load_config.assert_not_called() + write_config.assert_not_called() + get_telemetry.assert_not_called() + assert capsys.readouterr().err == "" + + +def test_scale_threshold_posthog_failure_does_not_consume_cap(notice_harness, capsys): + config, telemetry = notice_harness + telemetry.posthog.evaluate_flags.side_effect = RuntimeError("network unavailable") + + notices.display_scale_threshold_notice( + MagicMock(), + "sync", + "search", + "top_k", + "high_top_k", + top_k=50, + threshold=notices.SCALE_TOP_K_THRESHOLD, + ) + + assert capsys.readouterr().err == "" + telemetry.capture_event.assert_not_called() + assert config.get("notice_state") is None + + +def test_scale_threshold_cap_blocks_before_posthog_eval(notice_harness, capsys): + config, telemetry = notice_harness + configure_flag(telemetry, "displayed", scale_payload()) + + for _ in range(notices.SCALE_THRESHOLD_CAP): + notices.display_scale_threshold_notice( + MagicMock(), + "sync", + "search", + "top_k", + "high_top_k", + top_k=50, + threshold=notices.SCALE_TOP_K_THRESHOLD, + ) + + notices.display_scale_threshold_notice( + MagicMock(), + "sync", + "search", + "top_k", + "high_top_k", + top_k=50, + threshold=notices.SCALE_TOP_K_THRESHOLD, + ) + + assert capsys.readouterr().err == "Scale top 50\n" * notices.SCALE_THRESHOLD_CAP + assert telemetry.posthog.evaluate_flags.call_count == notices.SCALE_THRESHOLD_CAP + assert telemetry.capture_event.call_count == notices.SCALE_THRESHOLD_CAP + assert len(config["notice_state"]["scale_threshold"]["events"]) == notices.SCALE_THRESHOLD_CAP + + +def test_scale_threshold_cap_ignores_old_entries(notice_harness, capsys): + config, telemetry = notice_harness + old_time = datetime.now(timezone.utc) - notices.SCALE_THRESHOLD_WINDOW - timedelta(days=1) + config["notice_state"] = { + "scale_threshold": { + "events": [ + {"evaluated_at": old_time.isoformat(), "variant": "displayed"} + for _ in range(notices.SCALE_THRESHOLD_CAP) + ] + } + } + configure_flag(telemetry, "displayed", scale_payload()) + + notices.display_scale_threshold_notice( + MagicMock(), + "sync", + "search", + "top_k", + "high_top_k", + top_k=50, + threshold=notices.SCALE_TOP_K_THRESHOLD, + ) + + assert capsys.readouterr().err == "Scale top 50\n" + assert telemetry.capture_event.call_count == 1 + assert len(config["notice_state"]["scale_threshold"]["events"]) == 1 + + +def test_scale_threshold_memory_count_requires_add_result_and_provider_count(notice_harness): + config, _ = notice_harness + memory = MagicMock() + memory.vector_store.count.return_value = notices.SCALE_MEMORY_COUNT_THRESHOLD + + assert notices.detect_scale_threshold_from_add_result(memory, [{"event": "UPDATE"}]) is None + assert config.get("notice_state") is None + + notice = notices.detect_scale_threshold_from_add_result(memory, [{"event": "ADD"}]) + + assert notice == ( + "memory_count", + "memory_count_threshold", + None, + notices.SCALE_MEMORY_COUNT_THRESHOLD, + notices.SCALE_MEMORY_COUNT_THRESHOLD, + ) + assert config["notice_state"]["scale_threshold"]["memory_count_threshold_evaluated"] is True + + +def test_scale_threshold_memory_count_ignores_under_threshold_provider_count(notice_harness): + config, _ = notice_harness + memory = MagicMock() + memory.vector_store.count.return_value = notices.SCALE_MEMORY_COUNT_THRESHOLD - 1 + + notice = notices.detect_scale_threshold_from_add_result(memory, [{"event": "ADD"}]) + + assert notice is None + assert config.get("notice_state") is None + + +def test_scale_threshold_memory_count_ignores_already_evaluated_threshold(notice_harness): + config, _ = notice_harness + config["notice_state"] = {"scale_threshold": {"memory_count_threshold_evaluated": True}} + memory = MagicMock() + memory.vector_store.count.return_value = notices.SCALE_MEMORY_COUNT_THRESHOLD + + notice = notices.detect_scale_threshold_from_add_result(memory, [{"event": "ADD"}]) + + assert notice is None + memory.vector_store.count.assert_not_called() + + +def test_scale_threshold_memory_count_is_throttled_under_threshold(notice_harness): + memory = MagicMock() + memory.vector_store.count.return_value = notices.SCALE_MEMORY_COUNT_THRESHOLD - 1 + + assert notices.detect_scale_threshold_from_add_result(memory, [{"event": "ADD"}]) is None + assert notices.detect_scale_threshold_from_add_result(memory, [{"event": "ADD"}]) is None + + memory.vector_store.count.assert_called_once() + + +def test_scale_threshold_memory_count_event_marks_threshold_evaluated(notice_harness, capsys): + config, telemetry = notice_harness + configure_flag(telemetry, "displayed", scale_payload()) + + notices.display_scale_threshold_notice( + MagicMock(), + "sync", + "add", + "memory_count", + "memory_count_threshold", + memory_count=2000, + threshold=notices.SCALE_MEMORY_COUNT_THRESHOLD, + ) + + assert capsys.readouterr().err == "Scale count 2000\n" + assert config["notice_state"]["scale_threshold"]["memory_count_threshold_evaluated"] is True + props = telemetry.capture_event.call_args.args[1] + assert props["memory_count"] == 2000 + assert props["threshold"] == notices.SCALE_MEMORY_COUNT_THRESHOLD + + +def test_scale_threshold_provider_count_helpers_are_safe(): + class Info: + points_count = 2200 + + class CountStore: + def count(self): + return 2100 + + class FallbackStore: + def count(self): + raise RuntimeError("count unavailable") + + def col_info(self): + return {"count": 2300} + + memory = MagicMock() + memory.vector_store = CountStore() + assert notices._get_provider_memory_count(memory) == 2100 + + memory.vector_store = FallbackStore() + assert notices._get_provider_memory_count(memory) == 2300 + + assert notices._extract_count({"points_count": 2400}) == 2400 + assert notices._extract_count(Info()) == 2200 + assert notices._extract_count({"count": -1}) is None + + +def test_scale_threshold_props_do_not_include_raw_user_inputs(notice_harness): + _, telemetry = notice_harness + configure_flag(telemetry, "displayed", scale_payload(top_k_copy="safe copy {top_k}")) + + notices.display_scale_threshold_notice( + MagicMock(), + "sync", + "search", + "top_k", + "high_top_k", + top_k=50, + threshold=notices.SCALE_TOP_K_THRESHOLD, + ) + + props = telemetry.capture_event.call_args.args[1] + assert "favorite drink" not in str(props) + assert "user_id" not in str(props) + assert "green tea" not in str(props) + + +def test_async_scale_threshold_wrapper_uses_shared_helper(monkeypatch): + calls = [] + + def display(memory_instance, sync_type, trigger_function, trigger_source, trigger_reason, top_k, memory_count, threshold): + calls.append( + (memory_instance, sync_type, trigger_function, trigger_source, trigger_reason, top_k, memory_count, threshold) + ) + + memory = MagicMock() + monkeypatch.setattr(notices, "display_scale_threshold_notice", display) + + asyncio.run( + notices.display_scale_threshold_notice_async( + memory, + "async", + "search", + "top_k", + "high_top_k", + top_k=50, + threshold=notices.SCALE_TOP_K_THRESHOLD, + ) + ) + + assert calls == [ + ( + memory, + "async", + "search", + "top_k", + "high_top_k", + 50, + None, + notices.SCALE_TOP_K_THRESHOLD, + ) + ] + + +def test_notice_priority_temporal_usage_beats_scale_and_first_run(monkeypatch): + from mem0.memory import main as memory_main + + memory = memory_main.Memory.__new__(memory_main.Memory) + memory.api_version = "v1.1" + memory.reranker = None + memory._search_vector_store = MagicMock(return_value=[]) + calls = [] + + monkeypatch.setattr(memory_main, "capture_event", lambda *args, **kwargs: None) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice", lambda *args: calls.append("temporal")) + monkeypatch.setattr(memory_main, "display_scale_threshold_notice", lambda *args: calls.append("scale")) + monkeypatch.setattr(memory_main, "display_first_run_notice", lambda *args: calls.append("first_run")) + + memory_main.Memory.search( + memory, + "what happened last week?", + top_k=50, + filters={"user_id": "u1"}, + ) + + assert calls == ["temporal"] + + +def test_notice_priority_scale_beats_first_run(monkeypatch): + from mem0.memory import main as memory_main + + memory = memory_main.Memory.__new__(memory_main.Memory) + memory.api_version = "v1.1" + memory.reranker = None + memory._search_vector_store = MagicMock(return_value=[]) + calls = [] + + monkeypatch.setattr(memory_main, "capture_event", lambda *args, **kwargs: None) + monkeypatch.setattr(memory_main, "display_scale_threshold_notice", lambda *args: calls.append("scale")) + monkeypatch.setattr(memory_main, "display_first_run_notice", lambda *args: calls.append("first_run")) + + memory_main.Memory.search( + memory, + "favorite drink", + top_k=50, + filters={"user_id": "u1"}, + ) + + assert calls == ["scale"] diff --git a/tests/memory/test_performance_slow_query_notice.py b/tests/memory/test_performance_slow_query_notice.py new file mode 100644 index 000000000..6e1f49c3b --- /dev/null +++ b/tests/memory/test_performance_slow_query_notice.py @@ -0,0 +1,445 @@ +from copy import deepcopy +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from mem0.memory import notices +from mem0.memory import main as memory_main +from mem0.memory.main import AsyncMemory, Memory + + +class FakeFlags: + def __init__(self, variant, payload): + self.variant = variant + self.payload = payload + + def get_flag(self, key): + assert key == notices.FLAG_KEY + return self.variant + + def get_flag_payload(self, key): + assert key == notices.FLAG_KEY + return self.payload + + +@pytest.fixture(autouse=True) +def reset_notice_process_state(): + notices._performance_slow_query_capacity_reached_in_process = False + yield + notices._performance_slow_query_capacity_reached_in_process = False + + +@pytest.fixture +def notice_harness(monkeypatch): + config = {} + telemetry = MagicMock() + telemetry.user_id = "oss-user" + + def write_config(updated): + saved = deepcopy(updated) + config.clear() + config.update(saved) + + monkeypatch.setattr(notices, "_load_config", lambda: config) + monkeypatch.setattr(notices, "_write_config", write_config) + monkeypatch.setattr(notices.telemetry_module, "MEM0_TELEMETRY", True) + monkeypatch.setattr(notices.telemetry_module, "_get_oss_telemetry", lambda: telemetry) + return config, telemetry + + +def configure_flag(telemetry, variant, payload): + flags = FakeFlags(variant, payload) + telemetry.posthog.evaluate_flags.return_value = flags + return flags + + +def performance_payload(copy="Performance CTA", enabled=True, notice_type="log_line"): + payload = { + "notices": { + "performance_slow_query": { + "enabled": enabled, + "notice_type": notice_type, + } + } + } + if copy is not None: + payload["notices"]["performance_slow_query"]["copy"] = copy + return payload + + +def make_sync_memory(search_results=None): + memory = Memory.__new__(Memory) + memory.api_version = "v1.1" + memory.reranker = None + memory._search_vector_store = MagicMock(return_value=search_results or []) + return memory + + +def make_async_memory(search_results=None): + memory = AsyncMemory.__new__(AsyncMemory) + memory.api_version = "v1.1" + memory.reranker = None + memory._search_vector_store = AsyncMock(return_value=search_results or []) + return memory + + +def test_sync_slow_search_triggers_performance_notice_after_success(monkeypatch): + results = [{"id": "m1"}, {"id": "m2"}] + memory = make_sync_memory(search_results=results) + performance_notice = MagicMock() + temporal_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main.time, "perf_counter", MagicMock(side_effect=[100.0, 102.1])) + monkeypatch.setattr(memory_main, "display_performance_slow_query_notice", performance_notice) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice", temporal_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + result = Memory.search(memory, "favorite drink", filters={"user_id": "u1"}, top_k=3) + + assert result == {"results": results} + memory._search_vector_store.assert_called_once() + performance_notice.assert_called_once_with(memory, "sync", "search", pytest.approx(2.1), 3, 2) + temporal_notice.assert_not_called() + first_run_notice.assert_not_called() + + +def test_sync_fast_search_uses_first_run_notice(monkeypatch): + memory = make_sync_memory() + performance_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main.time, "perf_counter", MagicMock(side_effect=[100.0, 101.0])) + monkeypatch.setattr(memory_main, "display_performance_slow_query_notice", performance_notice) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice", MagicMock()) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + Memory.search(memory, "favorite drink", filters={"user_id": "u1"}) + + performance_notice.assert_not_called() + first_run_notice.assert_called_once_with(memory, "sync", "search") + + +def test_sync_failed_search_does_not_trigger_performance_notice(monkeypatch): + memory = make_sync_memory() + memory._search_vector_store.side_effect = RuntimeError("search failure") + performance_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main.time, "perf_counter", MagicMock(return_value=100.0)) + monkeypatch.setattr(memory_main, "display_performance_slow_query_notice", performance_notice) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice", MagicMock()) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + with pytest.raises(RuntimeError, match="search failure"): + Memory.search(memory, "favorite drink", filters={"user_id": "u1"}) + + performance_notice.assert_not_called() + first_run_notice.assert_not_called() + + +def test_sync_temporal_usage_takes_precedence_over_slow_search(monkeypatch): + memory = make_sync_memory() + performance_notice = MagicMock() + temporal_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main.time, "perf_counter", MagicMock(side_effect=[100.0, 102.1])) + monkeypatch.setattr(memory_main, "display_performance_slow_query_notice", performance_notice) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice", temporal_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + Memory.search(memory, "what happened last week?", filters={"user_id": "u1"}) + + temporal_notice.assert_called_once_with(memory, "sync", "search", "query", "relative_phrase") + performance_notice.assert_not_called() + first_run_notice.assert_not_called() + + +def test_sync_scale_takes_precedence_over_slow_search(monkeypatch): + memory = make_sync_memory() + performance_notice = MagicMock() + scale_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main.time, "perf_counter", MagicMock(side_effect=[100.0, 102.1])) + monkeypatch.setattr(memory_main, "display_performance_slow_query_notice", performance_notice) + monkeypatch.setattr(memory_main, "display_scale_threshold_notice", scale_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + Memory.search(memory, "favorite drink", filters={"user_id": "u1"}, top_k=50) + + scale_notice.assert_called_once_with( + memory, + "sync", + "search", + "top_k", + "high_top_k", + 50, + None, + notices.SCALE_TOP_K_THRESHOLD, + ) + performance_notice.assert_not_called() + first_run_notice.assert_not_called() + + +@pytest.mark.asyncio +async def test_async_slow_search_triggers_performance_notice_after_success(monkeypatch): + results = [{"id": "m1"}] + memory = make_async_memory(search_results=results) + performance_notice = AsyncMock() + temporal_notice = AsyncMock() + first_run_notice = AsyncMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main.time, "perf_counter", MagicMock(side_effect=[100.0, 102.1])) + monkeypatch.setattr(memory_main, "display_performance_slow_query_notice_async", performance_notice) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice_async", temporal_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice_async", first_run_notice) + + result = await AsyncMemory.search(memory, "favorite drink", filters={"user_id": "u1"}, top_k=4) + + assert result == {"results": results} + memory._search_vector_store.assert_awaited_once() + performance_notice.assert_awaited_once_with(memory, "async", "search", pytest.approx(2.1), 4, 1) + temporal_notice.assert_not_awaited() + first_run_notice.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_async_fast_search_uses_first_run_notice(monkeypatch): + memory = make_async_memory() + performance_notice = AsyncMock() + first_run_notice = AsyncMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main.time, "perf_counter", MagicMock(side_effect=[100.0, 101.0])) + monkeypatch.setattr(memory_main, "display_performance_slow_query_notice_async", performance_notice) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice_async", AsyncMock()) + monkeypatch.setattr(memory_main, "display_first_run_notice_async", first_run_notice) + + await AsyncMemory.search(memory, "favorite drink", filters={"user_id": "u1"}) + + performance_notice.assert_not_awaited() + first_run_notice.assert_awaited_once_with(memory, "async", "search") + + +@pytest.mark.asyncio +async def test_async_failed_search_does_not_trigger_performance_notice(monkeypatch): + memory = make_async_memory() + memory._search_vector_store.side_effect = RuntimeError("search failure") + performance_notice = AsyncMock() + first_run_notice = AsyncMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main.time, "perf_counter", MagicMock(return_value=100.0)) + monkeypatch.setattr(memory_main, "display_performance_slow_query_notice_async", performance_notice) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice_async", AsyncMock()) + monkeypatch.setattr(memory_main, "display_first_run_notice_async", first_run_notice) + + with pytest.raises(RuntimeError, match="search failure"): + await AsyncMemory.search(memory, "favorite drink", filters={"user_id": "u1"}) + + performance_notice.assert_not_awaited() + first_run_notice.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_async_temporal_usage_takes_precedence_over_slow_search(monkeypatch): + memory = make_async_memory() + performance_notice = AsyncMock() + temporal_notice = AsyncMock() + first_run_notice = AsyncMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main.time, "perf_counter", MagicMock(side_effect=[100.0, 102.1])) + monkeypatch.setattr(memory_main, "display_performance_slow_query_notice_async", performance_notice) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice_async", temporal_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice_async", first_run_notice) + + await AsyncMemory.search(memory, "what happened last week?", filters={"user_id": "u1"}) + + temporal_notice.assert_awaited_once_with(memory, "async", "search", "query", "relative_phrase") + performance_notice.assert_not_awaited() + first_run_notice.assert_not_awaited() + + +def test_performance_slow_query_displayed_logs_and_captures_event(notice_harness, capsys): + config, telemetry = notice_harness + flags = configure_flag(telemetry, "displayed", performance_payload()) + + notices.display_performance_slow_query_notice( + MagicMock(), + "sync", + "search", + elapsed_seconds=2.345, + top_k=20, + result_count=7, + ) + + assert capsys.readouterr().err == "Performance CTA\n" + telemetry.posthog.evaluate_flags.assert_called_once_with("oss-user", flag_keys=[notices.FLAG_KEY]) + telemetry.capture_event.assert_called_once() + event_name, props = telemetry.capture_event.call_args.args + assert event_name == notices.NOTICE_EVENT + assert props["notice_id"] == "performance_slow_query" + assert props["notice_type"] == "log_line" + assert props["variant"] == "displayed" + assert props["displayed"] is True + assert props["payload"] == "Performance CTA" + assert props["bypass_reason"] is None + assert props["disabled_reason"] is None + assert props["notice_config_found"] is True + assert props["sync_type"] == "sync" + assert props["trigger_function"] == "search" + assert props["trigger_reason"] == "slow_query" + assert props["elapsed_ms"] == 2345 + assert props["threshold_ms"] == 2000 + assert props["top_k"] == 20 + assert props["result_count"] == 7 + assert telemetry.capture_event.call_args.kwargs["flags"] is flags + assert len(config["notice_state"]["performance_slow_query"]["events"]) == 1 + + +def test_performance_slow_query_holdout_is_silent_but_captures_event(notice_harness, capsys): + _, telemetry = notice_harness + configure_flag(telemetry, "holdout", performance_payload()) + + notices.display_performance_slow_query_notice( + MagicMock(), + "sync", + "search", + elapsed_seconds=2.1, + top_k=10, + result_count=2, + ) + + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is False + assert props["bypass_reason"] == "holdout" + assert props["trigger_reason"] == "slow_query" + + +@pytest.mark.parametrize( + ("payload", "expected_reason", "expected_found"), + [ + ({}, "missing_notice_config", False), + ({"notices": {}}, "missing_notice_config", False), + ({"notices": "not-an-object"}, "missing_notice_config", False), + (performance_payload(copy=None), "missing_copy", True), + (performance_payload(enabled=False, copy="hidden"), "payload_disabled", True), + ], +) +def test_performance_slow_query_bad_or_disabled_payload_is_silent_and_safe( + notice_harness, payload, expected_reason, expected_found, capsys +): + _, telemetry = notice_harness + configure_flag(telemetry, "displayed", payload) + + notices.display_performance_slow_query_notice( + MagicMock(), + "sync", + "search", + elapsed_seconds=2.1, + top_k=10, + result_count=2, + ) + + assert capsys.readouterr().err == "" + props = telemetry.capture_event.call_args.args[1] + assert props["displayed"] is False + assert props["bypass_reason"] == expected_reason + assert props["notice_config_found"] is expected_found + + +@pytest.mark.parametrize("variant", [None, False]) +def test_performance_slow_query_blunt_flag_disable_does_not_capture_or_consume( + notice_harness, variant, capsys +): + config, telemetry = notice_harness + configure_flag(telemetry, variant, performance_payload()) + + notices.display_performance_slow_query_notice( + MagicMock(), + "sync", + "search", + elapsed_seconds=2.1, + top_k=10, + result_count=2, + ) + + assert capsys.readouterr().err == "" + telemetry.capture_event.assert_not_called() + assert config.get("notice_state") is None + + +def test_performance_slow_query_telemetry_disabled_does_not_touch_posthog_or_state( + monkeypatch, capsys +): + load_config = MagicMock(return_value={}) + write_config = MagicMock() + get_telemetry = MagicMock() + + monkeypatch.setattr(notices, "_load_config", load_config) + monkeypatch.setattr(notices, "_write_config", write_config) + monkeypatch.setattr(notices.telemetry_module, "MEM0_TELEMETRY", False) + monkeypatch.setattr(notices.telemetry_module, "_get_oss_telemetry", get_telemetry) + + notices.display_performance_slow_query_notice( + MagicMock(), + "sync", + "search", + elapsed_seconds=2.1, + top_k=10, + result_count=2, + ) + + load_config.assert_not_called() + write_config.assert_not_called() + get_telemetry.assert_not_called() + assert capsys.readouterr().err == "" + + +def test_performance_slow_query_cap_blocks_before_posthog_eval(notice_harness, capsys): + config, telemetry = notice_harness + configure_flag(telemetry, "displayed", performance_payload()) + + for _ in range(notices.PERFORMANCE_SLOW_QUERY_CAP): + notices.display_performance_slow_query_notice( + MagicMock(), + "sync", + "search", + elapsed_seconds=2.1, + top_k=10, + result_count=2, + ) + + notices.display_performance_slow_query_notice( + MagicMock(), + "sync", + "search", + elapsed_seconds=2.1, + top_k=10, + result_count=2, + ) + + assert capsys.readouterr().err == "Performance CTA\n" * notices.PERFORMANCE_SLOW_QUERY_CAP + assert telemetry.posthog.evaluate_flags.call_count == notices.PERFORMANCE_SLOW_QUERY_CAP + assert telemetry.capture_event.call_count == notices.PERFORMANCE_SLOW_QUERY_CAP + assert len(config["notice_state"]["performance_slow_query"]["events"]) == notices.PERFORMANCE_SLOW_QUERY_CAP + + +def test_performance_slow_query_props_do_not_include_raw_user_inputs(notice_harness): + _, telemetry = notice_harness + configure_flag(telemetry, "displayed", performance_payload(copy="safe copy")) + + notices.display_performance_slow_query_notice( + MagicMock(), + "sync", + "search", + elapsed_seconds=2.1, + top_k=10, + result_count=2, + ) + + props = telemetry.capture_event.call_args.args[1] + assert "favorite drink" not in str(props) + assert "user_id" not in str(props) + assert "green tea" not in str(props) diff --git a/tests/memory/test_temporal_feature_notice.py b/tests/memory/test_temporal_feature_notice.py new file mode 100644 index 000000000..bca2c9a50 --- /dev/null +++ b/tests/memory/test_temporal_feature_notice.py @@ -0,0 +1,96 @@ +import pytest + +from mem0.memory import main as memory_main +from mem0.memory.main import AsyncMemory, Memory + + +def test_sync_add_timestamp_raises_before_validation(monkeypatch): + calls = [] + + def get_error(sync_type, trigger_function, trigger_parameter): + calls.append((sync_type, trigger_function, trigger_parameter)) + return "blocked timestamp" + + monkeypatch.setattr(memory_main, "get_temporal_feature_error_message", get_error) + + with pytest.raises(ValueError, match="blocked timestamp"): + Memory.add(Memory.__new__(Memory), "hello", timestamp=123) + + assert calls == [("sync", "add", "timestamp")] + + +def test_sync_search_reference_date_raises_before_validation(monkeypatch): + calls = [] + + def get_error(sync_type, trigger_function, trigger_parameter): + calls.append((sync_type, trigger_function, trigger_parameter)) + return "blocked reference date" + + monkeypatch.setattr(memory_main, "get_temporal_feature_error_message", get_error) + + with pytest.raises(ValueError, match="blocked reference date"): + Memory.search(Memory.__new__(Memory), "what happened last week?", reference_date="2025-03-21") + + assert calls == [("sync", "search", "reference_date")] + + +@pytest.mark.asyncio +async def test_async_add_timestamp_raises_before_validation(monkeypatch): + calls = [] + + async def get_error(sync_type, trigger_function, trigger_parameter): + calls.append((sync_type, trigger_function, trigger_parameter)) + return "blocked async timestamp" + + monkeypatch.setattr(memory_main, "get_temporal_feature_error_message_async", get_error) + + with pytest.raises(ValueError, match="blocked async timestamp"): + await AsyncMemory.add(AsyncMemory.__new__(AsyncMemory), "hello", timestamp=123) + + assert calls == [("async", "add", "timestamp")] + + +@pytest.mark.asyncio +async def test_async_search_reference_date_raises_before_validation(monkeypatch): + calls = [] + + async def get_error(sync_type, trigger_function, trigger_parameter): + calls.append((sync_type, trigger_function, trigger_parameter)) + return "blocked async reference date" + + monkeypatch.setattr(memory_main, "get_temporal_feature_error_message_async", get_error) + + with pytest.raises(ValueError, match="blocked async reference date"): + await AsyncMemory.search( + AsyncMemory.__new__(AsyncMemory), + "what happened last week?", + reference_date="2025-03-21", + ) + + assert calls == [("async", "search", "reference_date")] + + +def test_sync_add_without_timestamp_does_not_call_temporal_feature_notice(monkeypatch): + get_error = monkeypatch.setattr( + memory_main, + "get_temporal_feature_error_message", + lambda *args: pytest.fail("temporal feature notice should not run"), + ) + + with pytest.raises(Exception, match="At least one of 'user_id', 'agent_id', or 'run_id'"): + Memory.add(Memory.__new__(Memory), "hello") + + assert get_error is None + + +def test_sync_search_without_reference_date_does_not_call_temporal_feature_notice(monkeypatch): + get_error = monkeypatch.setattr( + memory_main, + "get_temporal_feature_error_message", + lambda *args: pytest.fail("temporal feature notice should not run"), + ) + + with pytest.raises(ValueError, match="filters must contain"): + Memory.search(Memory.__new__(Memory), "hello") + + assert get_error is None diff --git a/tests/memory/test_temporal_usage_notice.py b/tests/memory/test_temporal_usage_notice.py new file mode 100644 index 000000000..07c18c244 --- /dev/null +++ b/tests/memory/test_temporal_usage_notice.py @@ -0,0 +1,202 @@ +from types import SimpleNamespace +from unittest.mock import AsyncMock, MagicMock + +import pytest + +from mem0.memory import main as memory_main +from mem0.memory.main import AsyncMemory, Memory + + +def make_sync_memory(): + memory = Memory.__new__(Memory) + memory.config = SimpleNamespace(llm=SimpleNamespace(config={})) + memory.api_version = "v1.1" + memory.reranker = None + memory._add_to_vector_store = MagicMock(return_value=[]) + memory._search_vector_store = MagicMock(return_value=[]) + return memory + + +def make_async_memory(): + memory = AsyncMemory.__new__(AsyncMemory) + memory.config = SimpleNamespace(llm=SimpleNamespace(config={})) + memory.api_version = "v1.1" + memory.reranker = None + memory._add_to_vector_store = AsyncMock(return_value=[]) + memory._search_vector_store = AsyncMock(return_value=[]) + return memory + + +def test_sync_add_temporal_metadata_triggers_notice_after_success(monkeypatch): + memory = make_sync_memory() + temporal_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "display_temporal_usage_notice", temporal_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + result = Memory.add( + memory, + "The user visited Paris.", + user_id="u1", + metadata={"event_date": "2025-04-09"}, + infer=False, + ) + + assert result == {"results": []} + memory._add_to_vector_store.assert_called_once() + temporal_notice.assert_called_once_with(memory, "sync", "add", "metadata", "date_like_metadata") + first_run_notice.assert_not_called() + + +def test_sync_add_non_temporal_metadata_uses_first_run_notice(monkeypatch): + memory = make_sync_memory() + temporal_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "display_temporal_usage_notice", temporal_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + Memory.add(memory, "The user likes tea.", user_id="u1", metadata={"topic": "drink"}, infer=False) + + temporal_notice.assert_not_called() + first_run_notice.assert_called_once_with(memory, "sync", "add") + + +def test_sync_add_failure_does_not_trigger_temporal_usage_notice(monkeypatch): + memory = make_sync_memory() + memory._add_to_vector_store.side_effect = RuntimeError("vector failure") + temporal_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "display_temporal_usage_notice", temporal_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + with pytest.raises(RuntimeError, match="vector failure"): + Memory.add( + memory, + "The user visited Paris.", + user_id="u1", + metadata={"event_date": "2025-04-09"}, + infer=False, + ) + + temporal_notice.assert_not_called() + first_run_notice.assert_not_called() + + +def test_sync_search_temporal_query_triggers_notice_after_success(monkeypatch): + memory = make_sync_memory() + temporal_notice = MagicMock() + first_run_notice = MagicMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice", temporal_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", first_run_notice) + + result = Memory.search(memory, "what happened last week?", filters={"user_id": "u1"}) + + assert result == {"results": []} + memory._search_vector_store.assert_called_once() + temporal_notice.assert_called_once_with(memory, "sync", "search", "query", "relative_phrase") + first_run_notice.assert_not_called() + + +def test_sync_search_temporal_filter_triggers_notice_after_success(monkeypatch): + memory = make_sync_memory() + temporal_notice = MagicMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice", temporal_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", MagicMock()) + + Memory.search( + memory, + "favorite drink", + filters={"user_id": "u1", "created_at": {"gte": "2025-04-01"}}, + ) + + temporal_notice.assert_called_once_with(memory, "sync", "search", "filter", "date_range_filter") + + +def test_sync_search_failure_does_not_trigger_temporal_usage_notice(monkeypatch): + memory = make_sync_memory() + memory._search_vector_store.side_effect = RuntimeError("search failure") + temporal_notice = MagicMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice", temporal_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice", MagicMock()) + + with pytest.raises(RuntimeError, match="search failure"): + Memory.search(memory, "what happened last week?", filters={"user_id": "u1"}) + + temporal_notice.assert_not_called() + + +@pytest.mark.asyncio +async def test_async_add_temporal_metadata_triggers_notice_after_success(monkeypatch): + memory = make_async_memory() + temporal_notice = AsyncMock() + first_run_notice = AsyncMock() + monkeypatch.setattr(memory_main, "display_temporal_usage_notice_async", temporal_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice_async", first_run_notice) + + result = await AsyncMemory.add( + memory, + "The user visited Paris.", + user_id="u1", + metadata={"event_date": "2025-04-09"}, + infer=False, + ) + + assert result == {"results": []} + memory._add_to_vector_store.assert_awaited_once() + temporal_notice.assert_awaited_once_with(memory, "async", "add", "metadata", "date_like_metadata") + first_run_notice.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_async_add_runs_scale_detection_in_thread(monkeypatch): + memory = make_async_memory() + scale_detector = MagicMock(return_value=("memory_count", "memory_count_threshold", None, 2000, 2000)) + scale_notice = AsyncMock() + first_run_notice = AsyncMock() + to_thread_calls = [] + + async def to_thread(fn, *args, **kwargs): + to_thread_calls.append((fn, args, kwargs)) + return fn(*args, **kwargs) + + monkeypatch.setattr(memory_main, "detect_scale_threshold_from_add_result", scale_detector) + monkeypatch.setattr(memory_main.asyncio, "to_thread", to_thread) + monkeypatch.setattr(memory_main, "display_scale_threshold_notice_async", scale_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice_async", first_run_notice) + + result = await AsyncMemory.add(memory, "The user likes tea.", user_id="u1", infer=False) + + assert result == {"results": []} + assert to_thread_calls == [(scale_detector, (memory, []), {})] + scale_detector.assert_called_once_with(memory, []) + scale_notice.assert_awaited_once_with( + memory, + "async", + "add", + "memory_count", + "memory_count_threshold", + None, + 2000, + 2000, + ) + first_run_notice.assert_not_awaited() + + +@pytest.mark.asyncio +async def test_async_search_temporal_query_triggers_notice_after_success(monkeypatch): + memory = make_async_memory() + temporal_notice = AsyncMock() + first_run_notice = AsyncMock() + monkeypatch.setattr(memory_main, "capture_event", MagicMock()) + monkeypatch.setattr(memory_main, "display_temporal_usage_notice_async", temporal_notice) + monkeypatch.setattr(memory_main, "display_first_run_notice_async", first_run_notice) + + result = await AsyncMemory.search(memory, "what happened last week?", filters={"user_id": "u1"}) + + assert result == {"results": []} + memory._search_vector_store.assert_awaited_once() + temporal_notice.assert_awaited_once_with(memory, "async", "search", "query", "relative_phrase") + first_run_notice.assert_not_awaited() diff --git a/tests/test_memory.py b/tests/test_memory.py index ae3f3ae89..371dd71ae 100644 --- a/tests/test_memory.py +++ b/tests/test_memory.py @@ -6,6 +6,7 @@ import pytest from mem0 import Memory from mem0.configs.base import MemoryConfig +from mem0.memory.main import _entity_collection_name from mem0.memory.utils import normalize_facts @@ -37,6 +38,14 @@ def test_create_memory(memory_client): assert result["results"][0]["memory"] == data +def test_entity_collection_name_uses_dash_for_s3_vectors(): + assert _entity_collection_name("s3_vectors", "test-index") == "test-index-entities" + + +def test_entity_collection_name_keeps_underscore_for_other_stores(): + assert _entity_collection_name("qdrant", "mem0") == "mem0_entities" + + def test_get_memory(memory_client): data = "Name is John Doe." memory_client.add([{"role": "user", "content": data}], user_id="test_user") diff --git a/tests/test_server_params.py b/tests/test_server_params.py index 977738d47..6ee58de69 100644 --- a/tests/test_server_params.py +++ b/tests/test_server_params.py @@ -682,3 +682,22 @@ class TestSearchEntityIdMapping: _, kwargs = mock_memory.search.call_args assert kwargs["filters"]["user_id"] == "u1" assert kwargs["filters"]["category"] == "food" + + +class TestSearchValidationErrors: + """Verify that ValueError from Memory.search() returns 400, not 502.""" + + def test_empty_filters_returns_400(self, client, mock_memory): + mock_memory.search.side_effect = ValueError( + "filters must contain at least one of: user_id, agent_id, run_id" + ) + resp = client.post("/search", json={"query": "food", "filters": {}}) + assert resp.status_code == 400 + assert "filters must contain" in resp.json()["detail"] + + def test_no_identifiers_returns_400(self, client, mock_memory): + mock_memory.search.side_effect = ValueError( + "filters must contain at least one of: user_id, agent_id, run_id" + ) + resp = client.post("/search", json={"query": "food"}) + assert resp.status_code == 400 diff --git a/tests/test_telemetry.py b/tests/test_telemetry.py index 3e9963d7e..fb7001a62 100644 --- a/tests/test_telemetry.py +++ b/tests/test_telemetry.py @@ -70,6 +70,10 @@ class TestTelemetryEnabled: with patch("mem0.memory.telemetry.get_or_create_user_id", return_value="test-user"): at = telemetry_module.AnonymousTelemetry() mock_posthog.assert_called_once() + assert ( + mock_posthog.call_args.kwargs["feature_flags_request_timeout_seconds"] + == telemetry_module.FEATURE_FLAGS_REQUEST_TIMEOUT_SECONDS + ) assert at.posthog is not None assert at.user_id == "test-user" @@ -84,6 +88,22 @@ class TestTelemetryEnabled: telemetry_module.capture_event("test.event", mock_memory) mock_at.capture_event.assert_called_once() + def test_anonymous_capture_event_passes_flags_to_posthog(self): + """capture_event() should use PostHog's event-first API and preserve flag snapshots.""" + flags = MagicMock() + with patch.object(telemetry_module, "MEM0_TELEMETRY", True): + with patch("mem0.memory.telemetry.Posthog") as mock_posthog_cls: + with patch("mem0.memory.telemetry.get_or_create_user_id", return_value="test-user"): + at = telemetry_module.AnonymousTelemetry() + at.capture_event("test.event", {"key": "value"}, flags=flags) + + mock_posthog_cls.return_value.capture.assert_called_once() + args, kwargs = mock_posthog_cls.return_value.capture.call_args + assert args == ("test.event",) + assert kwargs["distinct_id"] == "test-user" + assert kwargs["flags"] is flags + assert kwargs["properties"]["key"] == "value" + def test_capture_client_event_sends_when_enabled(self): """capture_client_event() should call client_telemetry.capture_event when enabled.""" with patch.object(telemetry_module, "MEM0_TELEMETRY", True): diff --git a/tests/test_telemetry_sampling.py b/tests/test_telemetry_sampling.py index 5c4145b3c..30a670015 100644 --- a/tests/test_telemetry_sampling.py +++ b/tests/test_telemetry_sampling.py @@ -162,16 +162,22 @@ class TestBeforeSendWiring: # before_send is None (default), not _sampling_before_send assert kwargs.get("before_send") is None - def test_anonymous_telemetry_falls_back_when_posthog_rejects_before_send(self): - """If posthog (older version) rejects before_send, construction still succeeds.""" + def test_anonymous_telemetry_constructs_posthog_with_current_kwargs(self): + """AnonymousTelemetry uses the supported PostHog constructor shape.""" with patch.object(telemetry_module, "MEM0_TELEMETRY", True): with patch("mem0.memory.telemetry.Posthog") as mock_posthog_cls: with patch("mem0.memory.telemetry.get_or_create_user_id", return_value="u"): - # First call (with before_send) raises TypeError; second call succeeds. - mock_posthog_cls.side_effect = [TypeError("unexpected kwarg before_send"), object()] at = telemetry_module.AnonymousTelemetry(before_send=telemetry_module._sampling_before_send) - # Constructor was called twice: once with before_send, once without - assert mock_posthog_cls.call_count == 2 + mock_posthog_cls.assert_called_once() + _, kwargs = mock_posthog_cls.call_args + assert kwargs["project_api_key"] == telemetry_module.PROJECT_API_KEY + assert kwargs["host"] == telemetry_module.HOST + assert kwargs["before_send"] is telemetry_module._sampling_before_send + assert ( + kwargs["feature_flags_request_timeout_seconds"] + == telemetry_module.FEATURE_FLAGS_REQUEST_TIMEOUT_SECONDS + ) + assert at.user_id == "u" assert at.posthog is not None diff --git a/tests/vector_stores/test_qdrant_config.py b/tests/vector_stores/test_qdrant_config.py new file mode 100644 index 000000000..3da99be2e --- /dev/null +++ b/tests/vector_stores/test_qdrant_config.py @@ -0,0 +1,37 @@ +from unittest.mock import MagicMock + +from mem0.configs.vector_stores.qdrant import QdrantConfig +from mem0.vector_stores import qdrant as qdrant_module + + +def test_qdrant_config_accepts_explicit_https_false(): + config = QdrantConfig( + host="127.0.0.1", + port=6333, + api_key="test-key", + https=False, + ) + + assert config.https is False + + +def test_qdrant_passes_explicit_https_to_client(monkeypatch): + client_cls = MagicMock() + monkeypatch.setattr(qdrant_module, "QdrantClient", client_cls) + monkeypatch.setattr(qdrant_module.Qdrant, "create_col", lambda *args, **kwargs: None) + + qdrant_module.Qdrant( + collection_name="memories", + embedding_model_dims=1536, + host="127.0.0.1", + port=6333, + api_key="test-key", + https=False, + ) + + client_cls.assert_called_once_with( + api_key="test-key", + host="127.0.0.1", + port=6333, + https=False, + ) diff --git a/tests/vector_stores/test_s3_vectors.py b/tests/vector_stores/test_s3_vectors.py index aaa0e62dc..68b7fa0f6 100644 --- a/tests/vector_stores/test_s3_vectors.py +++ b/tests/vector_stores/test_s3_vectors.py @@ -110,6 +110,32 @@ def test_memory_initialization_with_config(mock_boto_client, mock_llm, mock_embe pytest.fail("Memory initialization failed") +def test_memory_entity_store_uses_s3_valid_index_name(mock_boto_client, mock_llm, mock_embedder): + mock_boto_client.get_vector_bucket.return_value = {} + mock_boto_client.get_index.return_value = {} + + config = { + "vector_store": { + "provider": "s3_vectors", + "config": { + "vector_bucket_name": BUCKET_NAME, + "collection_name": INDEX_NAME, + "embedding_model_dims": EMBEDDING_DIMS, + "distance_metric": "cosine", + "region_name": REGION, + }, + } + } + + memory = Memory.from_config(config) + assert memory.entity_store is not None + + index_names = [call.kwargs["indexName"] for call in mock_boto_client.get_index.call_args_list] + assert INDEX_NAME in index_names + assert f"{INDEX_NAME}-entities" in index_names + assert f"{INDEX_NAME}_entities" not in index_names + + def test_insert(mock_boto_client): """Test inserting vectors.""" store = S3Vectors(