Repository: MemoriLabs/Memori Branch: main Commit: aee3af0f382d Files: 454 Total size: 2.4 MB Directory structure: gitextract_gi5dzs00/ ├── .dockerignore ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── bug_report.yml │ │ ├── config.yml │ │ └── feature_request.yml │ └── workflows/ │ ├── ci.yml │ ├── integration.yml │ ├── publish-npm.yml │ ├── publish-openclaw-plugin.yml │ ├── publish.yml │ ├── ts-ci.yml │ └── ts-openclaw-ci.yml ├── .gitignore ├── .pre-commit-config.yaml ├── CHANGELOG.md ├── CONTRIBUTING.md ├── Dockerfile ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.md ├── SECURITY.md ├── benchmarks/ │ ├── README.md │ ├── locomo/ │ │ ├── _run_impl.py │ │ ├── _types.py │ │ ├── loader.py │ │ ├── preprocess.py │ │ ├── provenance.py │ │ ├── report.py │ │ ├── retrieval.py │ │ ├── run.py │ │ └── scoring.py │ ├── perf/ │ │ ├── README.md │ │ ├── _results.py │ │ ├── conftest.py │ │ ├── fixtures/ │ │ │ ├── sample_data.py │ │ │ └── sample_facts.py │ │ ├── generate_percentile_report.py │ │ ├── memory_utils.py │ │ ├── run_benchmarks_ec2.sh │ │ ├── setup_ec2_benchmarks.sh │ │ ├── test_cloud_recall_benchmarks.py │ │ ├── test_hosted_recall_benchmarks.py │ │ └── test_recall_benchmarks.py │ └── scripts/ │ └── fetch_locomo.py ├── conftest.py ├── docker-compose.yml ├── docs/ │ ├── memori-byodb/ │ │ ├── concepts/ │ │ │ ├── advanced-augmentation.mdx │ │ │ ├── architecture.mdx │ │ │ ├── async-patterns.mdx │ │ │ ├── cli-quickstart.mdx │ │ │ ├── how-memory-works.mdx │ │ │ ├── knowledge-graph.mdx │ │ │ └── multi-user-support.mdx │ │ ├── contribute/ │ │ │ ├── development-setup.mdx │ │ │ └── overview.mdx │ │ ├── dashboard/ │ │ │ └── api-keys.mdx │ │ ├── databases/ │ │ │ ├── cockroachdb.mdx │ │ │ ├── mongodb.mdx │ │ │ ├── mysql.mdx │ │ │ ├── oracle.mdx │ │ │ ├── overview.mdx │ │ │ ├── postgres.mdx │ │ │ └── sqlite.mdx │ │ ├── getting-started/ │ │ │ ├── installation.mdx │ │ │ ├── python-quickstart.mdx │ │ │ └── use-cases.mdx │ │ ├── index.mdx │ │ ├── llm/ │ │ │ ├── agno.mdx │ │ │ ├── anthropic.mdx │ │ │ ├── aws-bedrock.mdx │ │ │ ├── deepseek.mdx │ │ │ ├── gemini.mdx │ │ │ ├── langchain.mdx │ │ │ ├── nebius.mdx │ │ │ ├── openai.mdx │ │ │ ├── overview.mdx │ │ │ ├── pydantic-ai.mdx │ │ │ └── xai-grok.mdx │ │ └── support/ │ │ ├── faq.mdx │ │ └── troubleshooting.mdx │ └── memori-cloud/ │ ├── benchmark/ │ │ ├── experiments.mdx │ │ ├── overview.mdx │ │ └── results.mdx │ ├── concepts/ │ │ ├── advanced-augmentation.mdx │ │ ├── architecture.mdx │ │ ├── async-patterns.mdx │ │ ├── how-memory-works.mdx │ │ ├── knowledge-graph.mdx │ │ └── multi-user-support.mdx │ ├── dashboard/ │ │ ├── analytics.mdx │ │ ├── api-keys.mdx │ │ ├── memories.mdx │ │ ├── overview.mdx │ │ └── playground.mdx │ ├── getting-started/ │ │ ├── installation.mdx │ │ ├── python-quickstart.mdx │ │ ├── typescript-quickstart.mdx │ │ └── use-cases.mdx │ ├── index.mdx │ ├── llm/ │ │ ├── agno.mdx │ │ ├── anthropic.mdx │ │ ├── aws-bedrock.mdx │ │ ├── deepseek.mdx │ │ ├── gemini.mdx │ │ ├── langchain.mdx │ │ ├── nebius.mdx │ │ ├── openai.mdx │ │ ├── overview.mdx │ │ ├── pydantic-ai.mdx │ │ └── xai-grok.mdx │ ├── mcp/ │ │ ├── agent-skills.mdx │ │ ├── client-setup.mdx │ │ └── overview.mdx │ ├── openclaw/ │ │ ├── overview.mdx │ │ └── quickstart.mdx │ └── support/ │ ├── faq.mdx │ └── troubleshooting.mdx ├── examples/ │ ├── agno/ │ │ ├── README.md │ │ ├── main.py │ │ └── pyproject.toml │ ├── cockroachdb/ │ │ ├── README.md │ │ ├── main.py │ │ └── pyproject.toml │ ├── digitalocean/ │ │ ├── README.md │ │ ├── main.py │ │ └── pyproject.toml │ ├── mongodb/ │ │ ├── README.md │ │ ├── main.py │ │ └── pyproject.toml │ ├── nebius/ │ │ ├── README.md │ │ ├── main.py │ │ └── pyproject.toml │ ├── neon/ │ │ ├── README.md │ │ ├── main.py │ │ └── pyproject.toml │ ├── oceanbase/ │ │ ├── README.md │ │ └── main.py │ ├── postgres/ │ │ ├── README.md │ │ ├── main.py │ │ └── pyproject.toml │ └── sqlite/ │ ├── README.md │ ├── main.py │ └── pyproject.toml ├── integrations/ │ └── openclaw/ │ ├── .prettierrc.json │ ├── README.md │ ├── eslint.config.js │ ├── openclaw.plugin.json │ ├── package-lock.json │ ├── package.json │ ├── src/ │ │ ├── constants.ts │ │ ├── handlers/ │ │ │ ├── augmentation.ts │ │ │ └── recall.ts │ │ ├── index.ts │ │ ├── sanitizer.ts │ │ ├── types.ts │ │ ├── utils/ │ │ │ ├── context.ts │ │ │ ├── index.ts │ │ │ ├── logger.ts │ │ │ └── memori-client.ts │ │ └── version.ts │ ├── tests/ │ │ ├── handlers/ │ │ │ ├── augmentation.test.ts │ │ │ └── recall.test.ts │ │ ├── index.test.ts │ │ ├── sanitizer.test.ts │ │ └── utils/ │ │ ├── context.test.ts │ │ ├── logger.test.ts │ │ └── memori-client.test.ts │ ├── tsconfig.json │ └── vitest.config.ts ├── memori/ │ ├── __init__.py │ ├── __main__.py │ ├── _cli.py │ ├── _config.py │ ├── _exceptions.py │ ├── _logging.py │ ├── _network.py │ ├── _setup.py │ ├── _utils.py │ ├── api/ │ │ ├── _quota.py │ │ └── _sign_up.py │ ├── embeddings/ │ │ ├── __init__.py │ │ ├── _api.py │ │ ├── _chunking.py │ │ ├── _format.py │ │ ├── _sentence_transformers.py │ │ ├── _tei.py │ │ ├── _tei_embed.py │ │ └── _utils.py │ ├── llm/ │ │ ├── __init__.py │ │ ├── _base.py │ │ ├── _clients.py │ │ ├── _constants.py │ │ ├── _invoke.py │ │ ├── _iterable.py │ │ ├── _iterator.py │ │ ├── _providers.py │ │ ├── _registry.py │ │ ├── _streaming.py │ │ ├── _utils.py │ │ ├── _xai_wrappers.py │ │ └── adapters/ │ │ ├── anthropic/ │ │ │ ├── __init__.py │ │ │ └── _adapter.py │ │ ├── bedrock/ │ │ │ ├── __init__.py │ │ │ └── _adapter.py │ │ ├── google/ │ │ │ ├── __init__.py │ │ │ └── _adapter.py │ │ ├── openai/ │ │ │ ├── __init__.py │ │ │ └── _adapter.py │ │ └── xai/ │ │ ├── __init__.py │ │ └── _adapter.py │ ├── memory/ │ │ ├── _collector.py │ │ ├── _conversation_messages.py │ │ ├── _manager.py │ │ ├── _struct.py │ │ ├── _writer.py │ │ ├── augmentation/ │ │ │ ├── __init__.py │ │ │ ├── _base.py │ │ │ ├── _db_writer.py │ │ │ ├── _handler.py │ │ │ ├── _manager.py │ │ │ ├── _message.py │ │ │ ├── _models.py │ │ │ ├── _registry.py │ │ │ ├── _runtime.py │ │ │ ├── augmentations/ │ │ │ │ └── memori/ │ │ │ │ ├── __init__.py │ │ │ │ ├── _augmentation.py │ │ │ │ └── models.py │ │ │ ├── input.py │ │ │ └── memories/ │ │ │ ├── _conversation.py │ │ │ ├── _entity.py │ │ │ └── _process.py │ │ └── recall.py │ ├── py.typed │ ├── search/ │ │ ├── __init__.py │ │ ├── _api.py │ │ ├── _core.py │ │ ├── _faiss.py │ │ ├── _lexical.py │ │ ├── _parsing.py │ │ └── _types.py │ └── storage/ │ ├── __init__.py │ ├── _base.py │ ├── _builder.py │ ├── _connection.py │ ├── _manager.py │ ├── _registry.py │ ├── adapters/ │ │ ├── dbapi/ │ │ │ ├── __init__.py │ │ │ └── _adapter.py │ │ ├── django/ │ │ │ ├── __init__.py │ │ │ └── _adapter.py │ │ ├── mongodb/ │ │ │ ├── __init__.py │ │ │ └── _adapter.py │ │ └── sqlalchemy/ │ │ ├── __init__.py │ │ └── _adapter.py │ ├── cockroachdb/ │ │ ├── _cluster_manager.py │ │ ├── _display.py │ │ └── _files.py │ ├── drivers/ │ │ ├── mongodb/ │ │ │ ├── __init__.py │ │ │ └── _driver.py │ │ ├── mysql/ │ │ │ ├── __init__.py │ │ │ └── _driver.py │ │ ├── oceanbase/ │ │ │ ├── __init__.py │ │ │ └── _driver.py │ │ ├── oracle/ │ │ │ ├── __init__.py │ │ │ └── _driver.py │ │ ├── postgresql/ │ │ │ ├── __init__.py │ │ │ └── _driver.py │ │ └── sqlite/ │ │ ├── __init__.py │ │ └── _driver.py │ └── migrations/ │ ├── _mongodb.py │ ├── _mysql.py │ ├── _oceanbase.py │ ├── _oracle.py │ ├── _postgresql.py │ └── _sqlite.py ├── memori-ts/ │ ├── .npmignore │ ├── .prettierrc.json │ ├── README.md │ ├── eslint.config.js │ ├── examples/ │ │ └── cloud/ │ │ └── simple.ts │ ├── package-lock.json │ ├── package.json │ ├── src/ │ │ ├── bin/ │ │ │ └── cli.ts │ │ ├── cli/ │ │ │ ├── commands/ │ │ │ │ ├── help.ts │ │ │ │ └── quota.ts │ │ │ ├── router.ts │ │ │ └── utils.ts │ │ ├── core/ │ │ │ ├── config.ts │ │ │ ├── errors.ts │ │ │ ├── network.ts │ │ │ └── session.ts │ │ ├── engines/ │ │ │ ├── augmentation.ts │ │ │ ├── persistence.ts │ │ │ └── recall.ts │ │ ├── index.ts │ │ ├── integrations/ │ │ │ ├── base.ts │ │ │ ├── index.ts │ │ │ └── openclaw.ts │ │ ├── memori.ts │ │ ├── types/ │ │ │ ├── api.ts │ │ │ └── integrations.ts │ │ ├── utils/ │ │ │ └── utils.ts │ │ └── version.ts │ ├── tests/ │ │ ├── cli/ │ │ │ ├── bin/ │ │ │ │ └── router.test.ts │ │ │ ├── commands/ │ │ │ │ ├── help.test.ts │ │ │ │ └── quota.test.ts │ │ │ └── utils.test.ts │ │ ├── core/ │ │ │ ├── config.test.ts │ │ │ ├── network.test.ts │ │ │ └── session.test.ts │ │ ├── engines/ │ │ │ ├── augmentation.test.ts │ │ │ ├── persistence.test.ts │ │ │ └── recall.test.ts │ │ ├── integrations/ │ │ │ ├── base.test.ts │ │ │ └── openclaw.test.ts │ │ ├── memori.test.ts │ │ └── utils/ │ │ └── utils.test.ts │ ├── tsconfig.build.json │ ├── tsconfig.json │ └── vitest.config.ts ├── pyproject.toml └── tests/ ├── benchmarks_locomo/ │ ├── test_locomo_aa_pairing.py │ ├── test_locomo_loader.py │ ├── test_locomo_preprocess.py │ ├── test_locomo_provenance.py │ └── test_locomo_run_and_report.py ├── build/ │ ├── mongodb.py │ ├── mysql.py │ ├── oceanbase.py │ ├── oracle.py │ ├── postgresql.py │ └── sqlite.py ├── database/ │ ├── core.py │ └── init_db.py ├── embeddings/ │ └── test_tei_chunking.py ├── integration/ │ ├── __init__.py │ ├── cloud/ │ │ ├── __init__.py │ │ ├── conftest.py │ │ ├── test_cloud_anthropic.py │ │ ├── test_cloud_bedrock.py │ │ ├── test_cloud_gemini.py │ │ ├── test_cloud_openai.py │ │ └── test_cloud_xai.py │ ├── conftest.py │ ├── databases/ │ │ ├── __init__.py │ │ ├── conftest.py │ │ └── test_database_storage.py │ ├── providers/ │ │ ├── __init__.py │ │ ├── test_anthropic.py │ │ ├── test_bedrock.py │ │ ├── test_google.py │ │ ├── test_openai.py │ │ └── test_xai.py │ └── test_aa_payload.py ├── llm/ │ ├── adapters/ │ │ ├── anthropic/ │ │ │ └── test_llm_adapters_anthropic_adapter.py │ │ ├── bedrock/ │ │ │ └── test_llm_adapters_bedrock_adapter.py │ │ ├── google/ │ │ │ └── test_llm_adapters_google_adapter.py │ │ ├── openai/ │ │ │ └── test_llm_adapters_openai_adapter.py │ │ └── xai/ │ │ └── test_llm_adapters_xai_adapter.py │ ├── clients/ │ │ └── oss/ │ │ ├── agno/ │ │ │ ├── anthropic_async.py │ │ │ ├── anthropic_sync.py │ │ │ ├── gemini_async.py │ │ │ ├── gemini_streaming.py │ │ │ ├── gemini_sync.py │ │ │ ├── openai_async.py │ │ │ ├── openai_streaming.py │ │ │ ├── openai_sync.py │ │ │ ├── xai_async.py │ │ │ ├── xai_streaming.py │ │ │ └── xai_sync.py │ │ ├── anthropic/ │ │ │ ├── async.py │ │ │ └── sync.py │ │ ├── gemini/ │ │ │ ├── async.py │ │ │ ├── async_streaming.py │ │ │ └── sync.py │ │ ├── langchain/ │ │ │ ├── chatbedrock/ │ │ │ │ └── async_runnable.py │ │ │ ├── chatgooglegenai/ │ │ │ │ ├── async_runnable.py │ │ │ │ ├── async_streaming.py │ │ │ │ ├── sync.py │ │ │ │ ├── sync_runnable.py │ │ │ │ └── sync_runnable_structured_output.py │ │ │ ├── chatopenai/ │ │ │ │ ├── async_ainvoke.py │ │ │ │ ├── async_runnable.py │ │ │ │ ├── async_streaming.py │ │ │ │ ├── sync.py │ │ │ │ ├── sync_runnable.py │ │ │ │ └── sync_runnable_structured_output.py │ │ │ └── chatvertexai/ │ │ │ └── sync.py │ │ ├── openai/ │ │ │ ├── async.py │ │ │ ├── async_streaming.py │ │ │ └── sync.py │ │ └── xai/ │ │ ├── async.py │ │ ├── async_stream.py │ │ └── sync.py │ ├── providers/ │ │ ├── __init__.py │ │ ├── azure_openai/ │ │ │ ├── __init__.py │ │ │ └── test_azure_openai.py │ │ └── google_genai/ │ │ ├── __init__.py │ │ └── test_google_genai.py │ ├── test_llm_base.py │ ├── test_llm_clients.py │ ├── test_llm_deprecation_warnings.py │ ├── test_llm_embeddings.py │ ├── test_llm_embeddings_bundled.py │ ├── test_llm_provider_sdk_version.py │ ├── test_llm_registry.py │ ├── test_llm_utils.py │ ├── test_llm_xai_wrappers.py │ └── unit_test_objects.py ├── memory/ │ ├── augmentation/ │ │ ├── test_aa_payload_unit.py │ │ ├── test_advanced_augmentation.py │ │ ├── test_base.py │ │ ├── test_handler.py │ │ ├── test_manager.py │ │ ├── test_manager_quota.py │ │ ├── test_models.py │ │ ├── test_quota_propagation.py │ │ └── test_registry.py │ ├── test_conversation_messages.py │ ├── test_manager_enterprise_retry.py │ ├── test_memory_augmentation_db_writer.py │ ├── test_memory_struct.py │ ├── test_memory_struct_triples.py │ ├── test_memory_writer.py │ ├── test_recall.py │ └── test_recall_eval_harness.py ├── storage/ │ ├── adapters/ │ │ ├── conftest.py │ │ ├── dbapi/ │ │ │ ├── test_dbapi_no_conflicts.py │ │ │ └── test_storage_adapters_dbapi_adapter.py │ │ ├── django/ │ │ │ └── test_storage_adapters_django_adapter.py │ │ └── sqlalchemy/ │ │ └── test_storage_adaptors_sqlalchemy_adapter.py │ ├── cockroachdb/ │ │ ├── test_storage_cockroachdb_display.py │ │ └── test_storage_cockroachdb_files.py │ ├── drivers/ │ │ ├── conftest.py │ │ ├── test_mongodb_driver.py │ │ ├── test_mysql_driver.py │ │ ├── test_oceanbase_driver.py │ │ ├── test_oracle_driver.py │ │ ├── test_postgresql_driver.py │ │ └── test_sqlite_driver.py │ ├── test_connection.py │ ├── test_connection_factory.py │ ├── test_storage_builder.py │ ├── test_storage_init.py │ ├── test_storage_manager.py │ └── test_storage_registry.py ├── test_cli.py ├── test_config.py ├── test_init.py ├── test_legacy_package_warning.py ├── test_llm_auto_registration.py ├── test_network.py ├── test_search.py └── test_utils.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .dockerignore ================================================ # Python __pycache__/ *.py[cod] *$py.class *.so .Python build/ develop-eggs/ dist/ downloads/ eggs/ .eggs/ lib/ lib64/ parts/ sdist/ var/ wheels/ *.egg-info/ .installed.cfg *.egg # Virtual environments .venv/ venv/ ENV/ env/ # Testing .pytest_cache/ .coverage htmlcov/ .tox/ # IDEs .vscode/ .idea/ *.swp *.swo *~ # Git .git/ .gitignore # Docker development files (not part of the SDK package) Dockerfile docker-compose.yml .dockerignore Makefile # OS .DS_Store Thumbs.db # Documentation docs/_build/ # Local dev files *.log .env .env.* !.env.example ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.yml ================================================ name: Bug Report description: Create a report to help us improve Memori title: "[Bug] " labels: ["bug"] body: - type: markdown attributes: value: | Thanks for taking the time to fill out this bug report! Please provide as much detail as possible. - type: checkboxes id: checks attributes: label: First Check description: Please ensure you have done the following before submitting your issue. options: - label: I added a very descriptive title to this issue. required: true - label: I searched existing issues and documentation. required: true - type: input id: version attributes: label: Memori Version description: What version of the sdk are you using? placeholder: e.g., 3.0.0 validations: required: true - type: input id: environment attributes: label: OS / Python Version description: e.g., Windows 10 / Python 3.10, or Ubuntu 22.04 / Python 3.11 placeholder: macOS / Python 3.12 validations: required: true - type: dropdown id: llm_provider attributes: label: LLM Provider description: Which LLM provider are you using? options: - OpenAI - Anthropic - Gemini - Bedrock - Grok (xAI) - Ollama / Local - Other validations: required: true - type: input id: llm_details attributes: label: LLM Model & Version description: Please specify the model name/version. placeholder: e.g., gpt-4o or claude-3-5-sonnet validations: required: true - type: dropdown id: database attributes: label: Database description: Which database integration are you using? options: - Postgres - MySQL - MongoDB - Oracle - SQLite - Neon - Supabase - Other validations: required: true - type: textarea id: description attributes: label: Description description: Please describe the bug clearly. What happened? What did you expect to happen? validations: required: true - type: textarea id: reproduction attributes: label: Minimal Reproducible Example description: Please provide a code snippet that reproduces the issue. render: python placeholder: | from memori import Memori # Your code here... - type: textarea id: logs attributes: label: Log Output / Stack Trace description: Please copy and paste any relevant log output or error messages here. render: shell - type: checkboxes id: terms attributes: label: Participation options: - label: I am willing to submit a pull request for this issue. ================================================ FILE: .github/ISSUE_TEMPLATE/config.yml ================================================ blank_issues_enabled: false ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.yml ================================================ name: Feature Request description: Suggest an idea or improvement for Memori title: "[Feature] " labels: ["enhancement"] body: - type: markdown attributes: value: | Thanks for your interest in improving Memori! Please describe your idea in detail. - type: textarea id: problem attributes: label: Is your feature request related to a problem? description: A clear and concise description of what the problem is. placeholder: Ex. I'm always frustrated when I try to integrate with... validations: required: true - type: textarea id: solution attributes: label: The Solution description: Describe the solution you'd like. placeholder: | I would like a new method `memori.recall_batch()` that accepts... validations: required: true - type: textarea id: alternatives attributes: label: Alternatives Considered description: Describe any alternative solutions or features you've considered. placeholder: I thought about using a loop, but it's too slow for my use case... - type: dropdown id: context attributes: label: Affected Components description: Which parts of Memori would this feature touch? multiple: true options: - LLM Provider / Adapter - Vector Store / Memory - Database Schema - CLI - Documentation - Other - type: checkboxes id: help attributes: label: Participation description: Would you be willing to help implement this? options: - label: I am willing to submit a pull request for this feature. ================================================ FILE: .github/workflows/ci.yml ================================================ name: CI on: push: branches: [main] paths: - 'memori/**' pull_request: branches: [main] paths: - 'memori/**' jobs: lint: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Install uv uses: astral-sh/setup-uv@v7 - name: Set up Python run: uv python install 3.12 - name: Install dependencies run: uv sync --dev - name: Run ruff linting run: uv run ruff check . - name: Run ruff format check run: uv run ruff format --check . security: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Install uv uses: astral-sh/setup-uv@v7 - name: Set up Python run: uv python install 3.12 - name: Install dependencies run: uv sync --dev - name: Run Bandit security checks run: uv run bandit -r memori -ll -ii - name: Run pip-audit for dependency vulnerabilities run: uv run pip-audit --require-hashes --disable-pip continue-on-error: true type-check: runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Install uv uses: astral-sh/setup-uv@v7 - name: Set up Python run: uv python install 3.12 - name: Install dependencies run: uv sync --dev - name: Run type checking with ty run: uvx ty check --exclude 'tests/llm/clients/**/*.py' --exclude 'tests/integration/**/*.py' --exclude 'benchmarks/**/*.py' --exclude 'examples/**/*.py' test: runs-on: ubuntu-latest strategy: matrix: python-version: ["3.10", "3.11", "3.12", "3.13", "3.14"] steps: - uses: actions/checkout@v5 - name: Install uv uses: astral-sh/setup-uv@v7 - name: Set up Python ${{ matrix.python-version }} run: uv python install ${{ matrix.python-version }} - name: Install dependencies run: uv sync --dev - name: Run pytest with coverage run: uv run pytest --ignore=tests/benchmarks - name: Upload coverage to Codecov if: matrix.python-version == '3.12' uses: codecov/codecov-action@v5 with: files: ./coverage.xml fail_ci_if_error: false ================================================ FILE: .github/workflows/integration.yml ================================================ name: Integration Tests on: workflow_dispatch: workflow_call: secrets: OPENAI_API_KEY: required: true ANTHROPIC_API_KEY: required: false GOOGLE_API_KEY: required: false XAI_API_KEY: required: false AWS_ACCESS_KEY_ID: required: false AWS_SECRET_ACCESS_KEY: required: false MEMORI_API_KEY: required: false permissions: contents: read jobs: integration-tests: runs-on: ubuntu-latest timeout-minutes: 30 services: postgres: image: postgres:15 env: POSTGRES_USER: memori POSTGRES_PASSWORD: memori POSTGRES_DB: memori_test ports: - 5432:5432 options: >- --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 5 mysql: image: mysql:8 env: MYSQL_ROOT_PASSWORD: memori MYSQL_DATABASE: memori_test MYSQL_USER: memori MYSQL_PASSWORD: memori ports: - 3306:3306 options: >- --health-cmd "mysqladmin ping" --health-interval 10s --health-timeout 5s --health-retries 5 mongodb: image: mongo:6 env: MONGO_INITDB_ROOT_USERNAME: memori MONGO_INITDB_ROOT_PASSWORD: memori ports: - 27017:27017 env: MEMORI_TEST_MODE: "1" OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} XAI_API_KEY: ${{ secrets.XAI_API_KEY }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} AWS_REGION: us-east-1 MEMORI_API_KEY: ${{ secrets.MEMORI_API_KEY }} SQLITE_DATABASE_URL: sqlite:///test_memori.db POSTGRES_DATABASE_URL: postgresql://memori:memori@localhost:5432/memori_test MYSQL_DATABASE_URL: mysql+pymysql://memori:memori@localhost:3306/memori_test MONGODB_URL: mongodb://memori:memori@localhost:27017/memori_test?authSource=admin steps: - uses: actions/checkout@v5 - name: Install uv uses: astral-sh/setup-uv@v7 with: enable-cache: true - name: Set up Python run: uv python install 3.12 - name: Install dependencies run: uv sync --dev - name: Run AA Payload Unit Tests (no API keys needed) run: | echo "Running AA payload unit tests..." uv run pytest tests/memory/augmentation/test_aa_payload_unit.py -v --tb=short - name: Run AA Integration Tests (staging API) if: env.OPENAI_API_KEY run: | echo "Running AA integration tests against staging..." uv run pytest tests/integration/test_aa_payload.py -v -m integration --tb=short - name: Run OpenAI Integration Tests if: env.OPENAI_API_KEY run: | echo "Running OpenAI integration tests..." uv run pytest tests/integration/providers/test_openai.py -v -m integration --tb=short - name: Run Anthropic Integration Tests if: env.ANTHROPIC_API_KEY run: | echo "Running Anthropic integration tests..." uv run pytest tests/integration/providers/test_anthropic.py -v -m integration --tb=short - name: Run Google Integration Tests if: env.GOOGLE_API_KEY run: | echo "Running Google integration tests..." uv run pytest tests/integration/providers/test_google.py -v -m integration --tb=short - name: Run xAI Integration Tests if: env.XAI_API_KEY run: | echo "Running xAI integration tests..." uv run pytest tests/integration/providers/test_xai.py -v -m integration --tb=short - name: Run Bedrock Integration Tests if: env.AWS_ACCESS_KEY_ID && env.AWS_SECRET_ACCESS_KEY run: | echo "Running Bedrock integration tests..." uv run pytest tests/integration/providers/test_bedrock.py -v -m integration --tb=short - name: Run cloud OpenAI Integration Tests if: env.OPENAI_API_KEY && env.MEMORI_API_KEY run: | echo "Running cloud OpenAI integration tests..." uv run pytest tests/integration/cloud/test_cloud_openai.py -v -m integration --tb=short - name: Run cloud Anthropic Integration Tests if: env.ANTHROPIC_API_KEY && env.MEMORI_API_KEY run: | echo "Running cloud Anthropic integration tests..." uv run pytest tests/integration/cloud/test_cloud_anthropic.py -v -m integration --tb=short - name: Run cloud Google Integration Tests if: env.GOOGLE_API_KEY && env.MEMORI_API_KEY run: | echo "Running cloud Google integration tests..." uv run pytest tests/integration/cloud/test_cloud_gemini.py -v -m integration --tb=short - name: Run cloud xAI Integration Tests if: env.XAI_API_KEY && env.MEMORI_API_KEY run: | echo "Running cloud xAI integration tests..." uv run pytest tests/integration/cloud/test_cloud_xai.py -v -m integration --tb=short - name: Run cloud Bedrock Integration Tests if: env.AWS_ACCESS_KEY_ID && env.AWS_SECRET_ACCESS_KEY && env.MEMORI_API_KEY run: | echo "Running cloud Bedrock integration tests..." uv run pytest tests/integration/cloud/test_cloud_bedrock.py -v -m integration --tb=short - name: Run Database Integration Tests if: env.OPENAI_API_KEY run: | echo "Running database integration tests..." uv run pytest tests/integration/databases/ -v -m integration --tb=short - name: Integration Test Summary if: always() run: | echo "==========================================" echo "Integration Test Summary" echo "==========================================" echo "MEMORI_TEST_MODE: $MEMORI_TEST_MODE (staging)" echo "" echo "Tests run:" echo " - AA Payload Unit Tests: Yes (no API keys needed)" if [ "$OPENAI_API_KEY" ]; then echo " - AA Integration Tests: Yes"; else echo " - AA Integration Tests: Skipped (no key)"; fi if [ "$OPENAI_API_KEY" ]; then echo " - Database Integration Tests: Yes"; else echo " - Database Integration Tests: Skipped (no key)"; fi echo "" echo "Databases tested:" echo " - SQLite: Yes" echo " - PostgreSQL: Yes" echo " - MySQL: Yes" echo " - MongoDB: Yes" echo "" echo "Providers tested:" if [ "$OPENAI_API_KEY" ]; then echo " - OpenAI: Yes"; else echo " - OpenAI: Skipped (no key)"; fi if [ "$ANTHROPIC_API_KEY" ]; then echo " - Anthropic: Yes"; else echo " - Anthropic: Skipped (no key)"; fi if [ "$GOOGLE_API_KEY" ]; then echo " - Google: Yes"; else echo " - Google: Skipped (no key)"; fi if [ "$XAI_API_KEY" ]; then echo " - xAI: Yes"; else echo " - xAI: Skipped (no key)"; fi if [ "$AWS_ACCESS_KEY_ID" ]; then echo " - Bedrock: Yes"; else echo " - Bedrock: Skipped (no key)"; fi echo "" echo "Cloud (requires MEMORI_API_KEY) tests:" if [ "$MEMORI_API_KEY" ] && [ "$OPENAI_API_KEY" ]; then echo " - Cloud OpenAI: Yes"; else echo " - Cloud OpenAI: Skipped (no key)"; fi if [ "$MEMORI_API_KEY" ] && [ "$ANTHROPIC_API_KEY" ]; then echo " - Cloud Anthropic: Yes"; else echo " - Cloud Anthropic: Skipped (no key)"; fi if [ "$MEMORI_API_KEY" ] && [ "$GOOGLE_API_KEY" ]; then echo " - Cloud Google: Yes"; else echo " - Cloud Google: Skipped (no key)"; fi if [ "$MEMORI_API_KEY" ] && [ "$XAI_API_KEY" ]; then echo " - Cloud xAI: Yes"; else echo " - Cloud xAI: Skipped (no key)"; fi if [ "$MEMORI_API_KEY" ] && [ "$AWS_ACCESS_KEY_ID" ]; then echo " - Cloud Bedrock: Yes"; else echo " - Cloud Bedrock: Skipped (no key)"; fi echo "==========================================" ================================================ FILE: .github/workflows/publish-npm.yml ================================================ name: Publish to npm (TS) on: workflow_dispatch: permissions: contents: read id-token: write jobs: publish: runs-on: ubuntu-latest defaults: run: working-directory: ./memori-ts steps: - name: Checkout repository uses: actions/checkout@v5 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: "24" registry-url: "https://registry.npmjs.org" - name: Install dependencies run: npm ci - name: Generate version.ts run: | VERSION=$(node -p "require('./package.json').version") echo "export const SDK_VERSION = '${VERSION}';" > src/version.ts - name: Build project run: npm run build --if-present - name: Run tests run: npm test --if-present - name: Publish Package run: npm publish --access public --tag latest ================================================ FILE: .github/workflows/publish-openclaw-plugin.yml ================================================ name: Publish OpenClaw to NPM on: workflow_dispatch: permissions: contents: read id-token: write jobs: publish: runs-on: ubuntu-latest defaults: run: working-directory: ./integrations/openclaw steps: - name: Checkout repository uses: actions/checkout@v5 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: "24" registry-url: "https://registry.npmjs.org" - name: Install dependencies run: npm ci - name: Generate version.ts run: | VERSION=$(node -p "require('./package.json').version") echo "export const SDK_VERSION = '${VERSION}';" > src/version.ts - name: Build project run: npm run build --if-present - name: Publish Package run: npm publish --access public --tag latest ================================================ FILE: .github/workflows/publish.yml ================================================ name: Publish to PyPI on: release: types: [published] workflow_dispatch: permissions: contents: read id-token: write concurrency: group: publish-pypi-${{ github.ref }} cancel-in-progress: false jobs: # Run integration tests before publishing integration-tests: uses: ./.github/workflows/integration.yml secrets: OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }} ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }} GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} XAI_API_KEY: ${{ secrets.XAI_API_KEY }} AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }} deploy: needs: integration-tests runs-on: ubuntu-latest steps: - uses: actions/checkout@v5 - name: Set up Python uses: actions/setup-python@v5 with: python-version: '3.x' cache: 'pip' - name: Install build tools run: pip install --upgrade pip build twine toml - name: Build memori package run: python -m build --outdir dist/memori - name: Verify memori distribution run: twine check dist/memori/* - name: Publish memori to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: packages-dir: dist/memori/ user: __token__ password: ${{ secrets.PYPI_API_TOKEN }} - name: Update package name to memorisdk run: | python -c " import toml with open('pyproject.toml', 'r') as f: config = toml.load(f) config['project']['name'] = 'memorisdk' with open('pyproject.toml', 'w') as f: toml.dump(config, f) " - name: Build memorisdk package run: python -m build --outdir dist/memorisdk - name: Verify memorisdk distribution run: twine check dist/memorisdk/* - name: Publish memorisdk to PyPI uses: pypa/gh-action-pypi-publish@release/v1 with: packages-dir: dist/memorisdk/ user: __token__ password: ${{ secrets.PYPI_SDK_API_TOKEN }} ================================================ FILE: .github/workflows/ts-ci.yml ================================================ name: CI Code Check (TS) on: push: branches: [ "main" ] paths: - 'memori-ts/**' pull_request: branches: [ "main" ] paths: - 'memori-ts/**' jobs: build-and-test: runs-on: ubuntu-latest strategy: matrix: node-version: [18, 20, 22, 24] defaults: run: working-directory: ./memori-ts steps: - name: Checkout repository uses: actions/checkout@v5 - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} - name: Install dependencies run: npm ci - name: Generate version.ts run: | VERSION=$(node -p "require('./package.json').version") echo "export const SDK_VERSION = '${VERSION}';" > src/version.ts - name: Run Linter run: npm run lint - name: Run Tests run: npm test - name: Verify Build run: npm run build ================================================ FILE: .github/workflows/ts-openclaw-ci.yml ================================================ name: CI Code Check (TS) on: push: branches: [ "main" ] paths: - 'integrations/openclaw/**' pull_request: branches: [ "main" ] paths: - 'integrations/openclaw/**' jobs: build-and-test: runs-on: ubuntu-latest strategy: matrix: node-version: [22, 24] defaults: run: working-directory: ./integrations/openclaw steps: - name: Checkout repository uses: actions/checkout@v5 - name: Setup Node.js ${{ matrix.node-version }} uses: actions/setup-node@v6 with: node-version: ${{ matrix.node-version }} - name: Install dependencies run: npm ci - name: Generate version.ts run: | VERSION=$(node -p "require('./package.json').version") echo "export const SDK_VERSION = '${VERSION}';" > src/version.ts - name: Run Linter run: npm run lint - name: Verify Build run: npm run build ================================================ FILE: .gitignore ================================================ # Python *.pyc *.pyo *.pyd __pycache__ *.so *.egg *.egg-info dist/ /build/ *.swp # Testing .pytest_cache .coverage coverage.xml htmlcov/ .tox # IDEs .DS_Store .idea .vscode *.swo # Virtual environments venv/ env/ .venv/ # Environment variables .env # SQLite databases *.db *.db-journal # UV uv.lock # Python packaging *.egg-info/ dist/ /build/ # Sensitive files (do not commit) google-credentials.json *.pem *.key AGENTS.md tests/examples/* # Benchmarking results tests/benchmarks/results/ results/ *.json *.csv !pyproject.toml !package.json !composer.json # Node / TypeScript node_modules/ dist/ *.tsbuildinfo coverage !package-lock.json !tsconfig.json !tsconfig.build.json !.prettierrc.json !openclaw.plugin.json ================================================ FILE: .pre-commit-config.yaml ================================================ repos: - repo: https://github.com/pre-commit/pre-commit-hooks rev: v4.5.0 hooks: - id: check-yaml - id: end-of-file-fixer - id: trailing-whitespace - id: debug-statements - repo: https://github.com/astral-sh/ruff-pre-commit rev: v0.15.1 hooks: - id: ruff-check args: [--fix] - id: ruff-format - repo: local hooks: # - id: ty # name: ty type checker # entry: uvx ty check # language: system # types: [python] # pass_filenames: false # always_run: true - id: pytest name: pytest entry: uv run pytest --ignore=tests/benchmarks --ignore=tests/integration language: system pass_filenames: false always_run: true - id: ts-format name: format (memori-ts) entry: bash -c 'cd memori-ts && npm run format' language: system pass_filenames: false files: ^memori-ts/ - id: ts-lint name: lint (memori-ts) entry: bash -c 'cd memori-ts && npm run lint:fix' language: system pass_filenames: false files: ^memori-ts/ - id: ts-typecheck name: typecheck (memori-ts) entry: bash -c 'cd memori-ts && npm run typecheck' language: system pass_filenames: false files: ^memori-ts/ - id: ts-test name: test (memori-ts) entry: bash -c 'cd memori-ts && npm run test' language: system pass_filenames: false files: ^memori-ts/ ================================================ FILE: CHANGELOG.md ================================================ # Changelog All notable changes to the Memori Python SDK will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] ### Fixed - Fixed multi-turn conversation ingestion for AzureOpenAI and OpenAI clients. Previously, only the first conversation turn was being recorded. Now `conversation_id` is resolved early in the request lifecycle, ensuring all conversation turns are properly ingested into the same conversation. (Fixes #83) [3.0.0]: https://github.com/MemoriLabs/Memori/releases/tag/v3.0.0 ================================================ FILE: CONTRIBUTING.md ================================================ [![Memori Labs](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/banner.png)](https://memorilabs.ai/) # Contributing to Memori Python SDK Thank you for your interest in contributing to Memori! ## Development Setup We use `uv` for fast dependency management and Docker for integration testing. You can develop locally or use our Docker environment. ### Prerequisites - Python 3.10+ (3.12 recommended) - [uv](https://github.com/astral-sh/uv) - Fast Python package installer - Docker and Docker Compose (for integration tests) - Make ### Quick Start (Local Development) ```bash # Install uv if you haven't already curl -LsSf https://astral.sh/uv/install.sh | sh # Clone the repository git clone https://github.com/MemoriLabs/Memori.git cd Memori # Install dependencies uv sync # Install pre-commit hooks uv run pre-commit install # Run unit tests uv run pytest ``` ### Quick Start (Docker) ```bash # Copy the example environment file cp .env.example .env # Edit .env and add your API keys (optional for unit tests) # Required for integration tests: OPENAI_API_KEY, ANTHROPIC_API_KEY, GOOGLE_API_KEY # Start the environment make dev-up ``` This will: - Build the Docker container with Python 3.12 - Install all dependencies with uv - Start PostgreSQL, MySQL, and MongoDB for integration tests - Start Mongo Express (web UI for MongoDB at http://localhost:8081) ### Development Commands #### Local Development ```bash # Run unit tests uv run pytest # Format code uv run ruff format . # Check linting uv run ruff check . # Run with coverage uv run pytest --cov=memori # Run security scans uv run bandit -r memori -ll -ii uv run pip-audit --require-hashes --disable-pip || true ``` #### Docker Development ```bash # Enter the development container make dev-shell # Run unit tests (fast, no external dependencies) make test # Initialize database schemas make init-postgres # PostgreSQL make init-mysql # MySQL make init-oceanbase # OceanBase make init-mongodb # MongoDB make init-sqlite # SQLite # Run a specific integration test script make run-integration FILE=tests/llm/clients/oss/openai/async.py # Format code make format # Check linting make lint # Run security scans make security # Stop the environment make dev-down # Clean up everything (containers, volumes, cache) make clean ``` ## Testing We use `pytest` with coverage reporting and `pytest-mock` for mocking. ### Unit Tests Unit tests use mocks and run without external dependencies: ```bash # Local uv run pytest # Docker make test ``` ### Integration Tests Integration tests require: - Database instances (PostgreSQL, MySQL, MongoDB, or SQLite) - LLM API keys (OpenAI, Anthropic, Google) ```bash # Set API keys in .env first # OPENAI_API_KEY=sk-... # ANTHROPIC_API_KEY=sk-ant-... # GOOGLE_API_KEY=... # Initialize database schema make init-postgres # or init-mysql, init-oceanbase, init-mongodb, init-sqlite # Run integration test scripts make run-integration FILE=tests/llm/clients/oss/openai/sync.py ``` ### Test Coverage We maintain high test coverage. Coverage reports are generated automatically: - Terminal output (summary) - HTML report in `htmlcov/` - XML report in `coverage.xml` View HTML coverage: ```bash open htmlcov/index.html # macOS xdg-open htmlcov/index.html # Linux ``` ## Project Structure ``` memori/ # SDK source code llm/ # LLM provider integrations (OpenAI, Anthropic, Google, etc.) memory/ # Memory system and augmentation storage/ # Storage adapters (PostgreSQL, MySQL, MongoDB, SQLite, etc.) api/ # API client for Memori Advanced Augmentation __init__.py # Main Memori class and public API py.typed # PEP 561 type hint marker tests/ # Test files build/ # Database initialization scripts llm/ # LLM provider tests (unit & integration) memory/ # Memory system tests storage/ # Storage adapter tests conftest.py # Pytest fixtures pyproject.toml # Project metadata and dependencies uv.lock # Locked dependency versions CHANGELOG.md # Version history ``` ## Code Quality We use [Ruff](https://docs.astral.sh/ruff/) for linting and formatting (configured in `pyproject.toml`): ```bash # Format code uv run ruff format . # or: make format # Check linting uv run ruff check . # or: make lint # Auto-fix issues uv run ruff check --fix . # Run security scans (Bandit + pip-audit) uv run bandit -r memori -ll -ii uv run pip-audit --require-hashes --disable-pip || true ``` ### Pre-commit Hooks We use pre-commit to automatically format and lint code: ```bash # Install hooks (one-time setup) uv run pre-commit install # Run manually uv run pre-commit run --all-files ``` ### Code Standards - Follow PEP 8 standards - Line length: 88 characters (Black-compatible) - Python 3.10+ syntax (use modern type hints) - All public APIs must have type hints - Lean, simple code preferred over complex solutions (KISS, YAGNI) - Minimize unnecessary comments - code should be self-documenting ## Pull Request Guidelines 1. **Fork and branch**: Create a feature branch from `main` 2. **Write tests**: Add/update tests for your changes 3. **Pass all checks**: Ensure tests, linting, and formatting pass 4. **Update docs**: Update README or docs if adding features 5. **Changelog**: Add entry to CHANGELOG.md under "Unreleased" 6. **Atomic commits**: Keep commits focused and well-described ## Supported Integrations ### LLM Providers - OpenAI (sync/async, streaming) - Anthropic Claude (sync/async, streaming) - Google Gemini (sync/async, streaming) - AWS Bedrock ### Frameworks - Agno - LangChain ### Database Adapters - PostgreSQL (via psycopg2, psycopg3) - MySQL / MariaDB (via pymysql) - MongoDB (via pymongo) - Oracle (via cx_Oracle, python-oracledb) - SQLite (stdlib) - CockroachDB - Neon, Supabase (PostgreSQL-compatible) - Django ORM - DB-API 2.0 compatible connections ## CLI Commands Memori provides CLI commands for managing your account and quota: ```bash # Check your API quota python3 -m memori quota # Sign up for Memori Advanced Augmentation python3 -m memori sign-up ``` These commands help you: - Monitor your memory quota and usage - Sign up for increased limits (always free for developers) - Obtain API keys for Advanced Augmentation features ## Development Notes - Docker files (Dockerfile, docker-compose.yml, Makefile) are for development only - They are NOT included in the PyPI package - The SDK has minimal runtime dependencies - fully self-contained - Development dependencies (LLM clients, database drivers) are in `[dependency-groups]` ================================================ FILE: Dockerfile ================================================ # Use Python 3.12 as base image FROM python:3.12-slim # Set working directory WORKDIR /app # Install system dependencies RUN apt-get update && apt-get install -y \ git \ curl \ build-essential \ && rm -rf /var/lib/apt/lists/* # Install uv for faster dependency management RUN pip install uv # Copy dependency files COPY pyproject.toml uv.lock* ./ # Install dependencies (including dev dependencies) RUN uv sync --all-extras # Copy the rest of the application COPY . . # Install pre-commit hooks RUN pip install pre-commit && pre-commit install || true # Add venv to PATH so all tools are available ENV PATH="/app/.venv/bin:$PATH" # Default command opens a bash shell CMD ["/bin/bash"] ================================================ FILE: LICENSE ================================================ 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 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 (which shall not include communications that are reasonably considered separate from, or merely to link to (or bind by name) the interfaces of, the Work and separate works which communicate with the Work solely through the Work's public interfaces). "Derivative Works" shall mean any work, whether in Source or Object form, that is based upon (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 separate works which communicate with the Work solely through the Work's public interfaces. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to use, reproduce, modify, distribute, and prepare Derivative Works of the Work, and to publicly display and perform the Work and such Derivative Works in all media and formats whether now known or hereafter devised. 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, trademark, patent, attribution and other notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright notice 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 Support. You may choose to offer, and to charge a fee for, warranty, support, 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 support. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [2025] [Memori Team] 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. ================================================ FILE: MANIFEST.in ================================================ # Include essential files include README.md include LICENSE # Exclude all development and Docker files exclude Dockerfile exclude docker-compose.yml exclude .dockerignore exclude Makefile exclude .env.example exclude conftest.py exclude CONTRIBUTING.md exclude .pre-commit-config.yaml exclude uv.lock exclude google-credentials.json exclude .coverage exclude coverage.xml exclude CHANGELOG.md exclude insert_facts.py exclude memori_test.db # Exclude test directories prune tests # Exclude documentation build artifacts prune docs prune htmlcov # Exclude TypeScript SDK (not shipped to PyPI) prune memori-ts prune integrations # Exclude benchmarks (not shipped with the SDK) prune benchmarks # Exclude version control and CI prune .git prune .github exclude .gitignore # Exclude IDE and editor files prune .vscode prune .venv prune .pytest_cache prune .ruff_cache global-exclude .DS_Store global-exclude *.pyc global-exclude *.pyo global-exclude __pycache__ ================================================ FILE: Makefile ================================================ .PHONY: help dev-up dev-down dev-shell dev-build dev-clean test lint format clean run-unit run-integration run-integration-provider run-integration-cloud help: ## Show this help message @echo 'Usage: make [target]' @echo '' @echo 'Available targets:' @awk 'BEGIN {FS = ":.*?## "} /^[a-zA-Z_-]+:.*?## / {printf " %-20s %s\n", $$1, $$2}' $(MAKEFILE_LIST) dev-up: ## Start development environment (builds and runs containers) docker compose up -d --build @echo "" @echo "✓ Development environment is ready!" @echo " Run 'make dev-shell' to enter the development container" @echo " Run 'make test' to run tests" dev-down: ## Stop development environment docker compose down dev-shell: ## Open a shell in the development container docker compose exec dev /bin/bash init-db: ## Initialize database schema for integration tests docker compose exec -e PYTHONPATH=/app dev python tests/database/init_db.py init-postgres: ## Initialize PostgreSQL schema docker compose exec -e PYTHONPATH=/app dev python tests/build/postgresql.py init-mysql: ## Initialize MySQL schema docker compose exec -e PYTHONPATH=/app dev python tests/build/mysql.py init-oceanbase: ## Initialize OceanBase schema docker compose exec -e PYTHONPATH=/app dev python tests/build/oceanbase.py init-oracle: ## Initialize Oracle schema docker compose exec -e PYTHONPATH=/app dev python tests/build/oracle.py init-mongodb: ## Initialize MongoDB schema docker compose exec -e PYTHONPATH=/app dev python tests/build/mongodb.py init-sqlite: ## Initialize SQLite schema docker compose exec -e PYTHONPATH=/app dev python tests/build/sqlite.py dev-build: ## Rebuild the development container docker compose build --no-cache dev-clean: ## Complete teardown: stop containers, remove images, prune build cache docker compose down -v docker builder prune -f docker compose rm -f @echo "✓ Docker environment cleaned (containers, volumes, and build cache removed)" test: ## Run tests in the container docker compose exec dev pytest run-unit: ## Run unit tests (no API keys needed) @echo "Running unit tests..." uv run pytest tests/ --ignore=tests/integration --ignore=tests/benchmarks -v --tb=short run-integration: ## Run all integration tests (requires API keys) @echo "Running all integration tests with MEMORI_TEST_MODE=1..." MEMORI_TEST_MODE=1 uv run pytest tests/integration/ -v -m integration --tb=short run-integration-provider: ## Run specific provider tests (e.g., make run-integration-provider P=openai) @echo "Running $(P) integration tests..." MEMORI_TEST_MODE=1 uv run pytest tests/integration/providers/test_$(P).py -v -m integration --tb=short run-integration-cloud: ## Run cloud integration tests (production API, requires MEMORI_API_KEY) @echo "Running cloud integration tests..." uv run pytest tests/integration/cloud/ -v -m integration --tb=short lint: ## Run linting (format check) docker compose exec dev uv run ruff check . security: ## Run security scans (Bandit + pip-audit) docker compose exec dev uv run bandit -r memori -ll -ii docker compose exec dev uv run pip-audit --require-hashes --disable-pip || true format: ## Format code docker compose exec dev uv run ruff format . clean: ## Clean up containers, volumes, and Python cache files docker compose down -v find . -type d -name __pycache__ -exec rm -rf {} + 2>/dev/null || true find . -type d -name .pytest_cache -exec rm -rf {} + 2>/dev/null || true find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true ================================================ FILE: README.md ================================================ [![Memori Labs](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/banner.png)](https://memorilabs.ai/)

The memory fabric for enterprise AI

Memori plugs into the software and infrastructure you already use. It is LLM, datastore and framework agnostic and seamlessly integrates into the architecture you've already designed.

Memori Cloud — Zero config. Get an API key and start building in minutes.

Memori%2fLabs%2FMemori | Trendshif

PyPI version NPM version Downloads License Discord

Give a Star

--- ## Getting Started ### Installation
TypeScript SDK ```bash npm install @memorilabs/memori ```
Python SDK ```bash pip install memori ```
### Quickstart Sign up at [app.memorilabs.ai](https://app.memorilabs.ai), get a Memori API key, and start building. Full docs: [memorilabs.ai/docs/memori-cloud/](https://memorilabs.ai/docs/memori-cloud/). Set `MEMORI_API_KEY` and your LLM API key (e.g. `OPENAI_API_KEY`), then:
TypeScript SDK ```typescript import { OpenAI } from 'openai'; import { Memori } from '@memorilabs/memori'; // Requires MEMORI_API_KEY and OPENAI_API_KEY in your environment const client = new OpenAI(); const mem = new Memori().llm .register(client) .attribution('user_123', 'support_agent'); async function main() { await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: 'My favorite color is blue.' }], }); // Conversations are persisted and recalled automatically in the background. const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: "What's my favorite color?" }], }); // Memori recalls that your favorite color is blue. } ```
Python SDK ```python from memori import Memori from openai import OpenAI # Requires MEMORI_API_KEY and OPENAI_API_KEY in your environment client = OpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="support_agent") response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "My favorite color is blue."}] ) # Conversations are persisted and recalled automatically. response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What's my favorite color?"}] ) # Memori recalls that your favorite color is blue. ```
## Explore the Memories Use the [Dashboard](https://app.memorilabs.ai) — Memories, Analytics, Playground, and API Keys. > [!TIP] > Want to use your own database? Check out docs for Memori BYODB here: > [https://memorilabs.ai/docs/memori-byodb/](https://memorilabs.ai/docs/memori-byodb/). ## LoCoMo Benchmark Memori was evaluated on the LoCoMo benchmark for long-conversation memory and achieved **81.95% overall accuracy** while using an average of **1,294 tokens per query**. That is just **4.97% of the full-context footprint**, showing that structured memory can preserve reasoning quality without forcing large prompts into every request. Compared with other retrieval-based memory systems, Memori outperformed Zep, LangMem, and Mem0 while reducing prompt size by roughly **67% vs. Zep** and lowering context cost by more than **20x vs. full-context prompting**. Read the [benchmark overview](docs/memori-cloud/benchmark/overview.mdx), see the [results](docs/memori-cloud/benchmark/results.mdx), or download the [paper](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memori-locomo-benchmark.pdf). !["Memori's average accuracy along with the standard deviation"](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memori-locomo-benchmark.webp) ## OpenClaw (Persistent Memory for Your Gateway) By default, OpenClaw agents forget everything between sessions. The Memori plugin fixes that. It captures durable facts and preferences after each conversation, then injects the most relevant context back into future prompts automatically. No changes to your agent code or prompts are required. The plugin hooks into OpenClaw's lifecycle, so you get structured memory, Intelligent Recall, and Advanced Augmentation with a drop-in plugin. ```bash openclaw plugins install @memorilabs/openclaw-memori openclaw plugins enable openclaw-memori openclaw config set plugins.entries.openclaw-memori.config.apiKey "YOUR_MEMORI_API_KEY" openclaw config set plugins.entries.openclaw-memori.config.entityId "your-app-user-id" openclaw gateway restart ``` For setup and configuration, see the [OpenClaw Quickstart](docs/memori-cloud/openclaw/quickstart.mdx). For architecture and lifecycle details, see the [OpenClaw Overview](docs/memori-cloud/openclaw/overview.mdx). ## MCP (Connect Your Agent in One Command) Your agent forgets everything between sessions. Memori fixes that. It remembers your stack, your conventions, and how you like things done so you stop repeating yourself. Works for solo developers and teams. Your agent learns coding patterns, reviewer preferences, and project conventions over time. For teams, that means shared context that new engineers pick up on day one instead of absorbing tribal knowledge over months. If you use Claude Code, Cursor, Codex, Warp, or Antigravity, you can connect Memori with no SDK integration needed: ```bash claude mcp add --transport http memori https://api.memorilabs.ai/mcp/ \ --header "X-Memori-API-Key: ${MEMORI_API_KEY}" \ --header "X-Memori-Entity-Id: your_username" \ --header "X-Memori-Process-Id: claude-code" ``` For Cursor, Codex, Warp, and other clients, see the [MCP client setup guide](docs/memori-cloud/mcp/client-setup.mdx). ## Attribution To get the most out of Memori, you want to attribute your LLM interactions to an entity (think person, place or thing; like a user) and a process (think your agent, LLM interaction or program). If you do not provide any attribution, Memori cannot make memories for you.
TypeScript SDK ```typescript mem.attribution("12345", "my-ai-bot"); ```
Python SDK ```python mem.attribution(entity_id="12345", process_id="my-ai-bot") ```
## Session Management Memori uses sessions to group your LLM interactions together. For example, if you have an agent that executes multiple steps you want those to be recorded in a single session. By default, Memori handles setting the session for you but you can start a new session or override the session by executing the following:
TypeScript SDK ```typescript mem.resetSession(); // or mem.setSession(sessionId); ```
Python SDK ```python mem.new_session() # or mem.set_session(session_id) ```
## Supported LLMs - Anthropic - Bedrock - DeepSeek - Gemini - Grok (xAI) - OpenAI (Chat Completions & Responses API) _(unstreamed, streamed, synchronous and asynchronous)_ ## Supported Frameworks - Agno - LangChain - Pydantic AI ## Supported Platforms - DeepSeek - Nebius AI Studio ## Examples For more examples and demos, check out the [Memori Cookbook](https://github.com/MemoriLabs/memori-cookbook). ## Memori Advanced Augmentation Memories are tracked at several different levels: - **entity**: think person, place, or thing; like a user - **process**: think your agent, LLM interaction or program - **session**: the current interactions between the entity, process and the LLM [Memori's Advanced Augmentation](docs/memori-cloud/concepts/advanced-augmentation.mdx) enhances memories at each of these levels with: - attributes - events - facts - people - preferences - relationships - rules - skills Memori knows who your user is, what tasks your agent handles and creates unparalleled context between the two. Augmentation occurs in the background incurring no latency. By default, Memori Advanced Augmentation is available without an account but rate limited. When you need increased limits, [sign up for Memori Advanced Augmentation](https://app.memorilabs.ai/signup) or use the Memori CLI: ```bash # Install the CLI via pip to manage your account python -m memori sign-up ``` Memori Advanced Augmentation is always free for developers! Once you've obtained an API key, set the following environment variable (used by both Python and TypeScript SDKs): ```bash export MEMORI_API_KEY=[api_key] ``` ## Managing Your Quota At any time, you can check your quota using the Memori CLI (works for both SDKs): ```bash python -m memori quota ``` Or by checking your account at [https://app.memorilabs.ai/](https://app.memorilabs.ai/). If you have reached your IP address quota, sign up and get an API key for increased limits. If your API key exceeds its quota limits we will email you and let you know. ## Command Line Interface (CLI) The Memori CLI is the unified tool for managing your account, keys, and quotas across all SDKs. To use it, execute the following from the command line: ```bash # Requires Python installed python -m memori ``` This will display a menu of the available options. For more information about what you can do with the Memori CLI, please reference [Command Line Interface](docs/memori-byodb/concepts/cli-quickstart.mdx). ## Contributing We welcome contributions from the community! Please see our [Contributing Guidelines](https://github.com/MemoriLabs/Memori/blob/main/CONTRIBUTING.md) for details on: - Setting up your development environment - Code style and standards - Submitting pull requests - Reporting issues --- ## Support - **Memori Cloud Documentation**: [memorilabs.ai/docs/memori-cloud/](https://memorilabs.ai/docs/memori-cloud/) - **Memori BYODB Documentation**: [https://memorilabs.ai/docs/memori-byodb/](https://memorilabs.ai/docs/memori-byodb/) - **Discord**: [https://discord.gg/abD4eGym6v](https://discord.gg/abD4eGym6v) - **Issues**: [GitHub Issues](https://github.com/MemoriLabs/Memori/issues) --- ## License Apache 2.0 - see [LICENSE](https://github.com/MemoriLabs/Memori/blob/main/LICENSE) ================================================ FILE: SECURITY.md ================================================ [![Memori Labs](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/banner.png)](https://memorilabs.ai/) ## Security Policy If you believe you have discovered a security vulnerability in Memori, please **do not** open a public GitHub issue. Instead, responsibly disclose it by: - Going to the **Security** tab of this repository - Clicking **"Report a vulnerability"** Please provide as much detail as possible in your private report, including: - A clear description of the issue - Steps to reproduce (a minimal reproducible example is greatly appreciated) - Potential impact and any proof-of-concept if available This allows us to investigate and address the issue quickly and safely before public disclosure. Thank you for helping keep Memori secure! ================================================ FILE: benchmarks/README.md ================================================ ## Benchmarks This directory contains **benchmark harnesses** that are intentionally **not** part of the default `pytest` unit test suite. ### Performance / latency (pytest-benchmark) Performance benchmarks (including **end-to-end recall latency**) live in `benchmarks/perf/`. Run locally (example): ```bash uv run pytest -m benchmark --benchmark-only benchmarks/perf/test_recall_benchmarks.py -v ``` For EC2 / VPC-adjacent database benchmarking, see `benchmarks/perf/README.md` and the helper scripts in `benchmarks/perf/`. ### LoCoMo (retrieval evaluation) LoCoMo is a benchmark dataset by Snap Research for long conversation memory. In Memori, we treat LoCoMo as a **retrieval evaluation** problem: given a question, does Memori retrieve the right supporting context (evidence)? #### Dataset LoCoMo is not vendored in this repo. Download the dataset JSON locally, then point the harness at that file path. Upstream: `https://github.com/snap-research/locomo` #### Preprocess (recommended for Memori) The upstream LoCoMo format is a **third-person** dialogue between two speakers, and some conversations include multimodal fields (e.g., image URLs + captions) that Memori does not currently handle well. To make evaluation more representative of Memori usage, we provide a small preprocessing step that: - Skips any conversation that contains multimodal turn fields (`img_url`, `blip_caption`, `query`) - Rewrites speakers so `conversation.speaker_b` becomes `assistant` and the other speaker becomes `user` Run: ```bash uv run python benchmarks/locomo/preprocess.py \ --in benchmarks/locomo10.json \ --out benchmarks/locomo10_memori.json ``` #### What gets written (artifacts) Each run writes: - `predictions.jsonl`: one row per QA question (retrieved top-k + hit@k/MRR metrics) - `summary.json`: aggregated metrics (overall + by category) - `locomo.sqlite`: SQLite DB used by Memori storage during the run - `locomo_provenance.sqlite`: (AA mode only) benchmark-only mapping of `fact_id → dia_id` for scoring #### Modes (ingestion) LoCoMo ingestion always uses **Advanced Augmentation**: - Stores turns as `conversation_message`s and runs Memori **Advanced Augmentation** to produce derived `entity_fact`s (closest to real usage). - Because LoCoMo evidence is turn-level, we write a **benchmark-only provenance DB** (`locomo_provenance.sqlite`) that maps each derived fact back to the LoCoMo `dia_id` turn(s), then score hit@k/MRR against evidence. - **Requires**: `MEMORI_API_KEY`. - **Note**: may be non-deterministic (API + model changes). #### Quickstart (advanced_augmentation, seeds + scores) Prerequisite: - `MEMORI_API_KEY` set (Advanced Augmentation API access) - LoCoMo harness forces staging routing (`MEMORI_TEST_MODE=1`) Run: ```bash export MEMORI_API_KEY="..." # Optional: increase AA request timeout (default is 30s) export MEMORI_AUGMENTATION_TIMEOUT_SECONDS=120 uv run python benchmarks/locomo/run.py \ --dataset benchmarks/locomo10.json \ --out results/locomo/aa_run \ --aa-batch per_pair ``` #### Score-only (reuse an existing DB, no AA calls) If you already seeded a SQLite DB (and, for AA runs, a provenance DB), you can skip ingestion and run retrieval+scoring directly from the existing DB: ```bash uv run python benchmarks/locomo/run.py \ --dataset benchmarks/locomo10.json \ --out results/locomo/score_only \ --sqlite-db results/locomo/aa_run/locomo.sqlite \ --provenance-db results/locomo/aa_run/locomo_provenance.sqlite \ --reuse-db ``` If the DB contains multiple prior LoCoMo runs, pass `--run-id` to choose which one to score. #### Useful knobs (AA mode) - **Batching**: - `--aa-batch per_pair` (one AA request per user+assistant pair) - **Dry-run** (inspect payload; no network call): - `--aa-dry-run` writes `aa_payload_preview.json` and prints the payload + URL. - **Metadata** (only if your AA endpoint requires it; defaults are provided): - `--meta-llm-provider` - `--meta-llm-version` - `--meta-llm-sdk-version` - `--meta-framework-provider` - `--meta-platform-provider` - **Timeout**: - AA HTTP timeout is configured via `MEMORI_AUGMENTATION_TIMEOUT_SECONDS`. ================================================ FILE: benchmarks/locomo/_run_impl.py ================================================ from __future__ import annotations import datetime import json import os import sqlite3 from dataclasses import dataclass from pathlib import Path from uuid import uuid4 from benchmarks.locomo._types import LoCoMoSample from benchmarks.locomo.loader import load_locomo_json from benchmarks.locomo.provenance import ( FactAttribution, ProvenanceStore, attribute_facts_to_turn_ids, ) from benchmarks.locomo.retrieval import ( build_turn_facts, evidence_to_turn_ids, ) from benchmarks.locomo.scoring import hit_at_k_groups, mrr_groups from memori import Memori from memori.embeddings import embed_texts from memori.memory.augmentation.input import AugmentationInput from memori.memory.recall import Recall CATEGORY_LABELS: dict[str, str] = { # LoCoMo category IDs are numeric in the dataset JSON. # Mapping here matches the upstream LoCoMo taxonomy: # 1=multi-hop, 2=temporal, 3=open-domain knowledge, 4=single-hop, 5=adversarial. "1": "multi-hop", "2": "temporal", "3": "open-domain", "4": "single-hop", "5": "adversarial", "unknown": "unknown", } @dataclass(frozen=True, slots=True) class RunConfig: dataset: str out: str sqlite_db: str = "" provenance_db: str = "" reuse_db: bool = False run_id: str = "" k: int = 5 aa_timeout: float = 180.0 aa_batch: str = "per_pair" aa_dry_run: bool = False aa_max_requests: int = 0 meta_llm_provider: str = "openai" meta_llm_version: str = "gpt-4.1-mini" meta_llm_sdk_version: str = "unknown" meta_framework_provider: str = "memori" meta_platform_provider: str = "benchmark" aa_provenance_top_n: int = 1 aa_provenance_min_score: float = 0.25 aa_provenance_mode: str = "similarity" rebuild_provenance: bool = False allow_prod_aa: bool = False max_samples: int = 0 max_questions: int = 0 only_sample_id: str = "" max_sessions: int = 0 verbose: bool = False log_every_questions: int = 0 seed_only: bool = False @dataclass(frozen=True, slots=True) class PairRequest: messages: list[dict[str, str]] pair_turn_ids: tuple[str, str] def run_locomo(cfg: RunConfig) -> dict: out_dir = Path(cfg.out) out_dir.mkdir(parents=True, exist_ok=True) predictions_path = out_dir / "predictions.jsonl" summary_path = out_dir / "summary.json" sqlite_path = ( Path(cfg.sqlite_db).expanduser() if cfg.sqlite_db else (out_dir / "locomo.sqlite") ) provenance_path = ( Path(cfg.provenance_db).expanduser() if cfg.provenance_db else (out_dir / "locomo_provenance.sqlite") ) samples = load_locomo_json(cfg.dataset) if cfg.only_sample_id: samples = [s for s in samples if s.sample_id == cfg.only_sample_id] if cfg.max_samples and cfg.max_samples > 0: samples = samples[: cfg.max_samples] run_id = _resolve_run_id( cfg=cfg, out_dir=out_dir, sqlite_path=sqlite_path, provenance_path=provenance_path, ) ts = datetime.datetime.now(datetime.timezone.utc).isoformat() mem = _init_memori(sqlite_path) recall = Recall(mem.config) prov_store = ProvenanceStore(provenance_path) if ( not cfg.reuse_db and not cfg.aa_dry_run and not cfg.allow_prod_aa and os.environ.get("MEMORI_TEST_MODE") != "1" ): raise RuntimeError( "Advanced Augmentation LoCoMo runs must target staging. " "Set MEMORI_TEST_MODE=1 (recommended) or pass --allow-prod-aa to override." ) if cfg.verbose: from memori._network import Api url = Api(mem.config).url("sdk/augmentation") print( f"[locomo][aa] resolved_api_url={url} MEMORI_TEST_MODE={os.environ.get('MEMORI_TEST_MODE')!r}" ) totals = _Totals() with predictions_path.open("w", encoding="utf-8") as f: if cfg.verbose: print( f"[locomo] start ingest=advanced_augmentation aa_batch={cfg.aa_batch} " f"samples={len(samples)} k={cfg.k} seed_only={cfg.seed_only}" ) for sample in samples: if cfg.max_sessions and cfg.max_sessions > 0: sample = LoCoMoSample( sample_id=sample.sample_id, sessions=sample.sessions[: cfg.max_sessions], qa=sample.qa, ) entity_external_id = f"locomo:{run_id}:{sample.sample_id}" if cfg.reuse_db: entity_db_id = _get_entity_id_sqlite( sqlite_path=sqlite_path, entity_external_id=entity_external_id, ) if entity_db_id is None: raise RuntimeError( f"--reuse-db was set but entity was not found in sqlite DB: external_id={entity_external_id}. " "Run ingestion first (omit --reuse-db) or pass the correct --run-id." ) else: mem.attribution( entity_id=entity_external_id, process_id="locomo-benchmark" ) entity_db_id = mem.config.storage.driver.entity.create( entity_external_id ) turn_facts, turn_index = build_turn_facts(sample) if cfg.reuse_db: fact_count = _count_entity_facts_sqlite( sqlite_path=sqlite_path, entity_db_id=entity_db_id ) if fact_count <= 0: raise RuntimeError( f"--reuse-db was set but no facts were found for entity external_id={entity_external_id}. " "Run ingestion first (omit --reuse-db) or pass the correct --run-id." ) mem.config.recall_embeddings_limit = fact_count if cfg.rebuild_provenance and not cfg.seed_only: _build_aa_provenance( mem=mem, prov_store=prov_store, run_id=run_id, sample_id=sample.sample_id, entity_db_id=entity_db_id, turn_facts=turn_facts, top_n=cfg.aa_provenance_top_n, min_score=cfg.aa_provenance_min_score, ) elif not cfg.seed_only and not prov_store.has_any( run_id=run_id, sample_id=sample.sample_id ): raise RuntimeError( f"--reuse-db was set but no provenance rows were found for run_id={run_id} sample_id={sample.sample_id}. " "Seed AA ingestion once (omit --reuse-db) to generate locomo_provenance.sqlite, " "or point --provenance-db at an existing provenance DB, " "or pass --rebuild-provenance to recompute provenance offline." ) if cfg.verbose: print( f"[locomo][reuse] sample={sample.sample_id} facts={fact_count}" ) else: _configure_aa_meta(mem, cfg) if cfg.aa_dry_run: _write_aa_payload_preview( out_dir=out_dir, mem=mem, sample=sample, cfg=cfg, entity_external_id=entity_external_id, process_id="locomo-benchmark", ) # Exit before any network call. return { "run_id": run_id, "timestamp_utc": ts, "dataset_path": str(Path(cfg.dataset).resolve()), "sqlite_db_path": str(sqlite_path), "provenance_db_path": str(provenance_path), "ingest": "advanced_augmentation", "sample_count": len(samples), "question_count": 0, "questions_by_category": {}, "metrics_overall": {}, "metrics_by_category": {}, "phase": 2, "note": "AA dry-run: printed/wrote payload preview and exited before network calls.", } conv_id = _ingest_with_advanced_augmentation( mem=mem, prov_store=prov_store, run_id=run_id, entity_external_id=entity_external_id, entity_db_id=entity_db_id, sample=sample, cfg=cfg, ) if conv_id is not None: summary_val = _read_conversation_summary(mem, conv_id) or "" print( f"[locomo][aa] sample={sample.sample_id} " f"conversation_id={conv_id} summary_is_set={bool(summary_val)}" ) if summary_val: print(f"[locomo][aa] summary={summary_val}") if cfg.seed_only: continue if cfg.verbose: fact_count = len( mem.config.storage.driver.entity_fact.get_embeddings( entity_db_id, limit=100000 ) ) print( f"[locomo][seed] sample={sample.sample_id} mode=advanced_augmentation " f"turns={len(turn_facts)} facts_written={fact_count}" ) if cfg.aa_provenance_mode == "similarity": _build_aa_provenance( mem=mem, prov_store=prov_store, run_id=run_id, sample_id=sample.sample_id, entity_db_id=entity_db_id, turn_facts=turn_facts, top_n=cfg.aa_provenance_top_n, min_score=cfg.aa_provenance_min_score, ) # Keep recall bounded to the facts created for this entity. entity_fact_driver = mem.config.storage.driver.entity_fact mem.config.recall_embeddings_limit = ( len(entity_fact_driver.get_embeddings(entity_db_id, limit=100000)) or 1 ) qa = list(sample.qa) if cfg.max_questions and cfg.max_questions > 0: qa = qa[: cfg.max_questions] available_turn_ids = {t.turn_id for t in turn_facts} for q in qa: if ( cfg.verbose and cfg.log_every_questions and totals.question_count % cfg.log_every_questions == 0 ): print( f"[locomo][score] questions={totals.question_count} " f"sample={sample.sample_id}" ) relevant = evidence_to_turn_ids(q.evidence, turn_index=turn_index) if not relevant: if cfg.verbose: print( f"[locomo][skip] sample={sample.sample_id} " f"question_id={q.question_id} reason=no_evidence" ) continue missing = sorted( tid for tid in relevant if tid not in available_turn_ids ) if missing: if cfg.verbose: preview = ", ".join(missing[:5]) more = ( "" if len(missing) <= 5 else f" (+{len(missing) - 5} more)" ) print( f"[locomo][skip] sample={sample.sample_id} " f"question_id={q.question_id} reason=missing_evidence " f"missing={preview}{more}" ) continue totals.count_question(q.category) results = recall.search_facts( query=q.question, limit=max(cfg.k, 1), entity_id=entity_db_id, ) # Scoring in AA mode needs a mapping from fact_id -> dia_id (LoCoMo turn id). # A single fact can plausibly map to multiple turns; we score using any-match # semantics per retrieved rank. retrieved_ids: list[str] = [] retrieved_groups: list[set[str]] = [] top_k = _format_top_k( results=results[: max(cfg.k, 1)], prov_store=prov_store, run_id=run_id, sample_id=sample.sample_id, retrieved_ids_out=retrieved_ids, retrieved_groups_out=retrieved_groups, provenance_limit=max(int(cfg.aa_provenance_top_n), 1), ) metrics = { "hit@1": hit_at_k_groups(relevant, retrieved_groups, 1), "hit@3": hit_at_k_groups(relevant, retrieved_groups, 3), "hit@5": hit_at_k_groups(relevant, retrieved_groups, 5), "hit@10": hit_at_k_groups(relevant, retrieved_groups, 10), "hit@20": hit_at_k_groups(relevant, retrieved_groups, 20), "hit@30": hit_at_k_groups(relevant, retrieved_groups, 30), "mrr": mrr_groups(relevant, retrieved_groups), } totals.add_metrics(category=q.category or "unknown", metrics=metrics) row = { "run_id": run_id, "timestamp_utc": ts, "sample_id": sample.sample_id, "question_id": q.question_id, "category": q.category, "question": q.question, "answers": list(q.answers), "evidence": q.evidence, "retrieval": { "status": "ok", "ingest": "advanced_augmentation", "k": cfg.k, "relevant_turn_ids": sorted(relevant), "top_k": top_k, "metrics": metrics, }, } f.write(json.dumps(row, ensure_ascii=False) + "\n") summary = totals.to_summary( run_id=run_id, timestamp_utc=ts, dataset_path=str(Path(cfg.dataset).resolve()), sqlite_db_path=str(sqlite_path), provenance_db_path=str(provenance_path), ingest="advanced_augmentation", sample_count=len(samples), ) summary_path.write_text(json.dumps(summary, indent=2), encoding="utf-8") return summary def _resolve_run_id( *, cfg: RunConfig, out_dir: Path, sqlite_path: Path, provenance_path: Path ) -> str: if cfg.run_id: return cfg.run_id if not cfg.reuse_db: return str(uuid4()) summary_path = out_dir / "summary.json" if summary_path.exists(): try: obj = json.loads(summary_path.read_text(encoding="utf-8")) rid = obj.get("run_id") if isinstance(rid, str) and rid: return rid except Exception: pass run_ids: set[str] = set() if provenance_path.exists(): run_ids |= _distinct_run_ids_from_provenance_sqlite(provenance_path) if sqlite_path.exists(): run_ids |= _distinct_run_ids_from_memori_sqlite(sqlite_path) if len(run_ids) == 1: return next(iter(run_ids)) if not run_ids: raise RuntimeError( "--reuse-db was set but no prior LoCoMo run_id was found in the DB(s). " "Pass --run-id or run ingestion once without --reuse-db." ) raise RuntimeError( "--reuse-db was set but multiple run_ids were found in the DB(s). " f"Pass --run-id to choose one. found={sorted(run_ids)}" ) def _distinct_run_ids_from_provenance_sqlite(path: Path) -> set[str]: with sqlite3.connect(str(path), check_same_thread=False) as conn: try: cur = conn.execute( "SELECT DISTINCT run_id FROM bench_locomo_fact_provenance" ) except sqlite3.OperationalError: return set() return {r[0] for r in cur.fetchall() if r and isinstance(r[0], str) and r[0]} def _distinct_run_ids_from_memori_sqlite(path: Path) -> set[str]: with sqlite3.connect(str(path), check_same_thread=False) as conn: try: cur = conn.execute( "SELECT external_id FROM memori_entity WHERE external_id LIKE 'locomo:%'" ) except sqlite3.OperationalError: return set() out: set[str] = set() for row in cur.fetchall(): if not row: continue external_id = row[0] if not isinstance(external_id, str) or not external_id.startswith( "locomo:" ): continue parts = external_id.split(":") if len(parts) >= 3 and parts[1]: out.add(parts[1]) return out def _get_entity_id_sqlite(*, sqlite_path: Path, entity_external_id: str) -> int | None: with sqlite3.connect(str(sqlite_path), check_same_thread=False) as conn: cur = conn.execute( "SELECT id FROM memori_entity WHERE external_id = ?", (entity_external_id,), ) row = cur.fetchone() if not row: return None try: return int(row[0]) except (TypeError, ValueError): return None def _count_entity_facts_sqlite(*, sqlite_path: Path, entity_db_id: int) -> int: with sqlite3.connect(str(sqlite_path), check_same_thread=False) as conn: cur = conn.execute( "SELECT COUNT(*) FROM memori_entity_fact WHERE entity_id = ?", (int(entity_db_id),), ) row = cur.fetchone() if not row: return 0 try: return int(row[0]) except (TypeError, ValueError): return 0 def _init_memori(sqlite_path: Path) -> Memori: def _conn(): return sqlite3.connect(str(sqlite_path), check_same_thread=False) mem = Memori(conn=_conn) mem.config.storage.build() return mem def _configure_aa_meta(mem: Memori, cfg: RunConfig) -> None: mem.config.framework.provider = cfg.meta_framework_provider mem.config.platform.provider = cfg.meta_platform_provider mem.config.llm.provider = cfg.meta_llm_provider mem.config.llm.version = cfg.meta_llm_version mem.config.llm.provider_sdk_version = cfg.meta_llm_sdk_version def _ingest_with_advanced_augmentation( *, mem: Memori, prov_store: ProvenanceStore, run_id: str, entity_external_id: str, entity_db_id: int, sample, cfg: RunConfig, ) -> int | None: if cfg.aa_batch == "per_pair": return _aa_enqueue_pairs_sequential( mem=mem, prov_store=prov_store, run_id=run_id, entity_external_id=entity_external_id, entity_db_id=entity_db_id, sample_id=sample.sample_id, sample=sample, timeout=cfg.aa_timeout, max_requests=int(cfg.aa_max_requests or 0), verbose=bool(cfg.verbose), ) raise ValueError(f"unknown aa batch: {cfg.aa_batch}") def _aa_enqueue_pairs_sequential( *, mem: Memori, prov_store: ProvenanceStore, run_id: str, entity_external_id: str, entity_db_id: int, sample_id: str, sample, timeout: float, max_requests: int = 0, verbose: bool = False, ) -> int | None: all_msgs, all_turn_ids = _build_aa_messages_and_turn_ids_for_sample(sample) conv_id = _create_conversation_and_persist_messages(mem, all_msgs) reqs = _build_per_pair_requests(all_msgs, all_turn_ids) max_n = int(max_requests or 0) if max_n > 0: reqs = reqs[:max_n] entity_fact_driver = mem.config.storage.driver.entity_fact known_fact_ids = { int(r["id"]) for r in entity_fact_driver.get_embeddings(int(entity_db_id), limit=100000) } for idx, req in enumerate(reqs): _enqueue_aa(mem, conv_id, entity_external_id, req.messages) mem.augmentation.wait(timeout=timeout) after = { int(r["id"]) for r in entity_fact_driver.get_embeddings(int(entity_db_id), limit=100000) } new_fact_ids = sorted(after - known_fact_ids) known_fact_ids = after if new_fact_ids: rows: list[FactAttribution] = [] for fid in new_fact_ids: for dia_id in req.pair_turn_ids: if dia_id: rows.append( FactAttribution(fact_id=fid, dia_id=dia_id, score=1.0) ) prov_store.upsert_many(rows, run_id=run_id, sample_id=sample_id) if verbose: summary_val = _read_conversation_summary(mem, int(conv_id)) or "" print( f"[locomo][aa][pair] idx={idx} new_facts={len(new_fact_ids)} " f"pair_turn_ids={req.pair_turn_ids} summary_is_set={bool(summary_val)}" ) return int(conv_id) def _read_conversation_summary(mem: Memori, conversation_id: int) -> str | None: try: obj = mem.config.storage.driver.conversation.read(int(conversation_id)) except Exception: return None if not isinstance(obj, dict): return None summary = obj.get("summary") return summary if isinstance(summary, str) and summary.strip() else None def _create_conversation_and_persist_messages( mem: Memori, msgs: list[dict[str, str]] ) -> int: mem.new_session() conv_id = mem.config.storage.driver.conversation.create( str(mem.config.session_id), mem.config.session_timeout_minutes ) for m in msgs: mem.config.storage.driver.conversation.message.create( conv_id, m["role"], "text", m["content"] ) if mem.config.storage.adapter is not None: mem.config.storage.adapter.commit() return int(conv_id) def _enqueue_aa( mem: Memori, conv_id: int, entity_external_id: str, msgs: list[dict[str, str]] ) -> None: from memori.memory.augmentation._message import ConversationMessage mem.augmentation.enqueue( AugmentationInput( conversation_id=str(conv_id), entity_id=entity_external_id, process_id="locomo-benchmark", conversation_messages=[ ConversationMessage(role=m["role"], content=m["content"]) for m in msgs ], system_prompt=None, ) ) def _build_aa_messages_for_sample(sample) -> list[dict[str, str]]: msgs, _turn_ids = _build_aa_messages_and_turn_ids_for_sample(sample) return msgs def _build_aa_messages_for_session(sample, session) -> list[dict[str, str]]: speaker_to_role = _speaker_to_role(sample) msgs, _turn_ids = _build_aa_messages_and_turn_ids_for_turns( sample, session, speaker_to_role ) return msgs def _build_aa_messages_for_turns( sample, session, speaker_to_role: dict[str, str] ) -> list[dict[str, str]]: msgs, _turn_ids = _build_aa_messages_and_turn_ids_for_turns( sample, session, speaker_to_role ) return msgs def _build_aa_messages_and_turn_ids_for_sample( sample, ) -> tuple[list[dict[str, str]], list[str]]: speaker_to_role = _speaker_to_role(sample) msgs: list[dict[str, str]] = [] turn_ids: list[str] = [] for session in sample.sessions: m, t = _build_aa_messages_and_turn_ids_for_turns( sample, session, speaker_to_role ) msgs.extend(m) turn_ids.extend(t) return msgs, turn_ids def _build_aa_messages_and_turn_ids_for_turns( sample, session, speaker_to_role: dict[str, str] ) -> tuple[list[dict[str, str]], list[str]]: msgs: list[dict[str, str]] = [] turn_ids: list[str] = [] session_id = session.session_id or "" for t_idx, turn in enumerate(session.turns): speaker = (turn.speaker or "").strip() role = speaker_to_role.get(speaker, "assistant") turn_id = ( turn.turn_id or "" ).strip() or f"{sample.sample_id}:{session_id}:{t_idx}" content = _format_turn_content( turn_id=turn_id, speaker=speaker, text=turn.text, session_date_time=session.date_time, ) msgs.append({"role": role, "content": content}) turn_ids.append(turn_id) return msgs, turn_ids def _format_turn_content( *, turn_id: str, speaker: str, text: str, session_date_time: str | None ) -> str: # Mirror real-world Memori payloads: content is the plain message text only. # Keep turn_id/speaker/session_time out of the message body (benchmark-only metadata). return str(text).strip() def _speaker_to_role(sample) -> dict[str, str]: """ Heuristic mapping: first unique speaker => user, second => assistant, others => assistant. """ out: dict[str, str] = {} ordered: list[str] = [] for session in sample.sessions: for turn in session.turns: speaker = (turn.speaker or "").strip() if not speaker or speaker in out: continue ordered.append(speaker) out[speaker] = "user" if len(ordered) == 1 else "assistant" return out def _build_aa_provenance( *, mem: Memori, prov_store: ProvenanceStore, run_id: str, sample_id: str, entity_db_id: int, turn_facts, top_n: int, min_score: float, ) -> None: turn_ids = [t.turn_id for t in turn_facts] turn_texts = [t.content for t in turn_facts] turn_embs = embed_texts( turn_texts, model=mem.config.embeddings.model, ) entity_fact_driver = mem.config.storage.driver.entity_fact rows = entity_fact_driver.get_embeddings(entity_db_id, limit=100000) fact_ids = [int(r["id"]) for r in rows] facts = entity_fact_driver.get_facts_by_ids(fact_ids) content_by_id = {int(r["id"]): r["content"] for r in facts} fact_ids_aligned = [i for i in fact_ids if i in content_by_id] fact_texts = [content_by_id[i] for i in fact_ids_aligned] fact_embs = embed_texts( fact_texts, model=mem.config.embeddings.model, ) # Clear any prior mappings for this run/sample to avoid mixing old/new strategies. prov_store.delete_sample(run_id=run_id, sample_id=sample_id) mapping = attribute_facts_to_turn_ids( turn_ids=turn_ids, turn_embeddings=turn_embs, turn_texts=turn_texts, fact_ids=fact_ids_aligned, fact_embeddings=fact_embs, fact_texts=fact_texts, top_n=top_n, min_score=min_score, ) prov_rows: list[FactAttribution] = [] for fid, mapped in mapping.items(): for dia_id, score in mapped: prov_rows.append(FactAttribution(fact_id=fid, dia_id=dia_id, score=score)) prov_store.upsert_many(prov_rows, run_id=run_id, sample_id=sample_id) def _write_aa_payload_preview( *, out_dir: Path, mem: Memori, sample, cfg: RunConfig, entity_external_id: str, process_id: str, ) -> None: """ Build and print the exact AA request payload (no network call). This is useful for debugging staging/prod routing, payload structure, and metadata. """ import hashlib from memori._network import Api from memori.memory.augmentation.augmentations.memori._augmentation import ( AdvancedAugmentation, ) dialect = ( mem.config.storage.adapter.get_dialect() if mem.config.storage and mem.config.storage.adapter else "unknown" ) aug = AdvancedAugmentation(config=mem.config, enabled=True) url = Api(mem.config).url("sdk/augmentation") all_msgs, all_turn_ids = _build_aa_messages_and_turn_ids_for_sample(sample) requests: list[dict[str, object]] = [] if cfg.aa_batch != "per_pair": raise ValueError(f"unknown aa batch: {cfg.aa_batch}") for i, req in enumerate(_build_per_pair_requests(all_msgs, all_turn_ids)): payload = aug._build_api_payload( # noqa: SLF001 - benchmark-only debug path req.messages, "", None, dialect, entity_external_id, process_id, ) requests.append( { "request_index": i, "pair_turn_ids": list(req.pair_turn_ids), "messages": req.messages, "payload": payload, } ) api_key = mem.config.api_key or "" api_key_preview = "" api_key_sha256 = "" if api_key: api_key_preview = ( f"{api_key[:4]}…{api_key[-4:]}" if len(api_key) >= 8 else "set" ) api_key_sha256 = hashlib.sha256(api_key.encode("utf-8")).hexdigest() preview = { "url": url, "ingest": "advanced_augmentation", "aa_batch": cfg.aa_batch, "entity_id": entity_external_id, "process_id": process_id, "memori_api_key": { "is_set": bool(api_key), "length": len(api_key), "preview": api_key_preview, "sha256": api_key_sha256, }, "requests": requests, } out_path = out_dir / "aa_payload_preview.json" out_path.write_text(json.dumps(preview, indent=2), encoding="utf-8") print(f"[locomo][aa-dry-run] url={url}") print(f"[locomo][aa-dry-run] wrote={out_path}") # Avoid flooding stdout for large datasets. max_print = 5 preview_print = dict(preview) preview_print["requests_total"] = len(requests) preview_print["requests_printed"] = min(max_print, len(requests)) preview_print["requests"] = requests[:max_print] print(json.dumps(preview_print, indent=2)) def _build_per_pair_requests( msgs: list[dict[str, str]], turn_ids: list[str], ) -> list[PairRequest]: """ Build per-pair AA requests that mirror SDK behavior. Each request includes *all* prior messages (oldest -> newest) plus the next strict user->assistant pair at the end. Any messages that can't be paired as a strict consecutive user+assistant pair are skipped as pair boundaries, but still remain in the context for later requests. """ if len(msgs) != len(turn_ids): raise ValueError("msgs and turn_ids must be aligned") out: list[PairRequest] = [] i = 0 while i < len(msgs) - 1: role0 = (msgs[i].get("role") or "").strip() role1 = (msgs[i + 1].get("role") or "").strip() if role0 == "user" and role1 == "assistant": tid0 = (turn_ids[i] or "").strip() tid1 = (turn_ids[i + 1] or "").strip() out.append( PairRequest(messages=list(msgs[: i + 2]), pair_turn_ids=(tid0, tid1)) ) i += 2 continue i += 1 return out def _format_top_k( *, results: list, prov_store: ProvenanceStore, run_id: str, sample_id: str, retrieved_ids_out: list[str], retrieved_groups_out: list[set[str]], provenance_limit: int = 1, ) -> list[dict[str, object]]: out: list[dict[str, object]] = [] for r in results: if isinstance(r, dict): content = r.get("content", "") fact_id = r.get("id") similarity = r.get("similarity") else: # Assume FactSearchResult or similar object with attributes content = getattr(r, "content", "") fact_id = getattr(r, "id", None) similarity = getattr(r, "similarity", None) turn_ids: list[str] = [] if isinstance(fact_id, int): dia_ids = prov_store.best_dia_ids_for_fact( run_id=run_id, sample_id=sample_id, fact_id=fact_id, limit=max(int(provenance_limit), 1), ) turn_ids = [d for d in dia_ids if d] # For backward compatibility, keep a single primary turn_id field (first). turn_id = turn_ids[0] if turn_ids else "" if turn_id: retrieved_ids_out.append(turn_id) retrieved_groups_out.append(set(turn_ids)) out.append( { "turn_id": turn_id, "turn_ids": turn_ids, "fact_id": fact_id, "similarity": similarity, "content": content, } ) return out class _Totals: def __init__(self) -> None: self.question_count = 0 self.questions_by_category: dict[str, int] = {} self.sums = { "hit@1": 0.0, "hit@3": 0.0, "hit@5": 0.0, "hit@10": 0.0, "hit@20": 0.0, "hit@30": 0.0, "mrr": 0.0, } self.sums_by_cat: dict[str, dict[str, float]] = {} self.counts_by_cat: dict[str, int] = {} def count_question(self, category: str | None) -> None: self.question_count += 1 cat = category or "unknown" self.questions_by_category[cat] = self.questions_by_category.get(cat, 0) + 1 def add_metrics(self, *, category: str, metrics: dict[str, float]) -> None: for key in self.sums: self.sums[key] += float(metrics[key]) self.sums_by_cat.setdefault( category, { "hit@1": 0.0, "hit@3": 0.0, "hit@5": 0.0, "hit@10": 0.0, "hit@20": 0.0, "hit@30": 0.0, "mrr": 0.0, }, ) for key in self.sums_by_cat[category]: self.sums_by_cat[category][key] += float(metrics[key]) self.counts_by_cat[category] = self.counts_by_cat.get(category, 0) + 1 def to_summary( self, *, run_id: str, timestamp_utc: str, dataset_path: str, sqlite_db_path: str, provenance_db_path: str, ingest: str, sample_count: int, ) -> dict: denom = float(self.question_count) if self.question_count else 1.0 metrics_overall = {k: (self.sums[k] / denom) for k in self.sums} metrics_by_category: dict[str, dict[str, float]] = {} for cat, sums_cat in self.sums_by_cat.items(): denom_cat = float(self.counts_by_cat.get(cat, 0)) or 1.0 metrics_by_category[cat] = {k: (sums_cat[k] / denom_cat) for k in sums_cat} questions_by_category_labeled = { CATEGORY_LABELS.get(cat, cat): count for cat, count in self.questions_by_category.items() } metrics_by_category_labeled = { CATEGORY_LABELS.get(cat, cat): vals for cat, vals in metrics_by_category.items() } return { "run_id": run_id, "timestamp_utc": timestamp_utc, "dataset_path": dataset_path, "sqlite_db_path": sqlite_db_path, "provenance_db_path": provenance_db_path, "ingest": ingest, "sample_count": sample_count, "question_count": self.question_count, "category_labels": dict(CATEGORY_LABELS), "questions_by_category": dict( sorted(self.questions_by_category.items(), key=lambda kv: kv[0]) ), "questions_by_category_labeled": dict( sorted(questions_by_category_labeled.items(), key=lambda kv: kv[0]) ), "metrics_overall": metrics_overall, "metrics_by_category": dict( sorted(metrics_by_category.items(), key=lambda kv: kv[0]) ), "metrics_by_category_labeled": dict( sorted(metrics_by_category_labeled.items(), key=lambda kv: kv[0]) ), "phase": 2, } ================================================ FILE: benchmarks/locomo/_types.py ================================================ from __future__ import annotations from dataclasses import dataclass from typing import Any @dataclass(frozen=True, slots=True) class LoCoMoTurn: speaker: str text: str turn_id: str | None = None timestamp: str | None = None @dataclass(frozen=True, slots=True) class LoCoMoSession: session_id: str | None turns: tuple[LoCoMoTurn, ...] date_time: str | None = None @dataclass(frozen=True, slots=True) class LoCoMoQA: question_id: str question: str answers: tuple[str, ...] category: str | None = None evidence: Any | None = None @dataclass(frozen=True, slots=True) class LoCoMoSample: sample_id: str sessions: tuple[LoCoMoSession, ...] qa: tuple[LoCoMoQA, ...] ================================================ FILE: benchmarks/locomo/loader.py ================================================ from __future__ import annotations import json import re from pathlib import Path from typing import Any from benchmarks.locomo._types import LoCoMoQA, LoCoMoSample, LoCoMoSession, LoCoMoTurn def load_locomo_json(path: str | Path) -> list[LoCoMoSample]: raw = Path(path).read_text(encoding="utf-8") data = json.loads(raw) if not isinstance(data, list): raise ValueError("LoCoMo JSON must be a list of samples") samples: list[LoCoMoSample] = [] for i, item in enumerate(data): if not isinstance(item, dict): raise ValueError(f"Sample at index {i} must be an object") sample_id = _coerce_str(item.get("sample_id")) or _coerce_str(item.get("id")) if not sample_id: raise ValueError(f"Sample at index {i} is missing 'sample_id'") sessions = _parse_conversation(item.get("conversation")) qa = _parse_qa(item.get("qa")) samples.append(LoCoMoSample(sample_id=sample_id, sessions=sessions, qa=qa)) return samples def _parse_conversation(value: Any) -> tuple[LoCoMoSession, ...]: if value is None: return () if isinstance(value, dict): return _parse_conversation_dict(value) if not isinstance(value, list): raise ValueError("'conversation' must be a list of sessions or an object") out: list[LoCoMoSession] = [] for idx, session in enumerate(value): if not isinstance(session, dict): raise ValueError(f"conversation[{idx}] must be an object") session_id = _coerce_str(session.get("session_id")) or _coerce_str( session.get("id") ) turns_raw = ( session.get("dialogue") or session.get("turns") or session.get("messages") ) turns = _parse_turns(turns_raw, context=f"conversation[{idx}]") out.append(LoCoMoSession(session_id=session_id, turns=turns, date_time=None)) return tuple(out) _SESSION_KEY_RE = re.compile(r"^session_(?P\d+)$") def _parse_conversation_dict(value: dict[str, Any]) -> tuple[LoCoMoSession, ...]: sessions: list[tuple[int, str]] = [] for key, v in value.items(): if not isinstance(key, str): continue m = _SESSION_KEY_RE.match(key) if not m: continue if not isinstance(v, list): continue sessions.append((int(m.group("n")), key)) sessions.sort(key=lambda t: t[0]) out: list[LoCoMoSession] = [] for n, key in sessions: date_time = _coerce_str(value.get(f"session_{n}_date_time")) turns = _parse_turns(value.get(key), context=f"conversation[{key}]") out.append(LoCoMoSession(session_id=key, turns=turns, date_time=date_time)) return tuple(out) def _parse_turns(value: Any, *, context: str) -> tuple[LoCoMoTurn, ...]: if value is None: return () if not isinstance(value, list): raise ValueError(f"{context}.dialogue must be a list of turns") out: list[LoCoMoTurn] = [] for idx, turn in enumerate(value): if not isinstance(turn, dict): raise ValueError(f"{context}.dialogue[{idx}] must be an object") turn_id = _coerce_str(turn.get("dia_id")) or _coerce_str(turn.get("turn_id")) speaker = ( _coerce_str(turn.get("speaker")) or _coerce_str(turn.get("role")) or "" ) text = _coerce_str(turn.get("text")) or _coerce_str(turn.get("content")) or "" if not text: continue timestamp = _coerce_str(turn.get("timestamp")) out.append( LoCoMoTurn(turn_id=turn_id, speaker=speaker, text=text, timestamp=timestamp) ) return tuple(out) def _parse_qa(value: Any) -> tuple[LoCoMoQA, ...]: if value is None: return () if not isinstance(value, list): raise ValueError("'qa' must be a list") out: list[LoCoMoQA] = [] for idx, qa in enumerate(value): if not isinstance(qa, dict): raise ValueError(f"qa[{idx}] must be an object") qid = ( _coerce_str(qa.get("question_id")) or _coerce_str(qa.get("id")) or f"q{idx}" ) question = _coerce_str(qa.get("question")) or "" if not question: continue answers_raw = qa.get("answer") if "answer" in qa else qa.get("answers") answers = _coerce_answers(answers_raw) category = _coerce_str(qa.get("category")) or _coerce_str(qa.get("type")) evidence = ( qa.get("evidence") if "evidence" in qa else qa.get("supporting_facts") ) out.append( LoCoMoQA( question_id=qid, question=question, answers=answers, category=category, evidence=evidence, ) ) return tuple(out) def _coerce_str(value: Any) -> str | None: if value is None: return None if isinstance(value, str): v = value.strip() return v or None return str(value).strip() or None def _coerce_answers(value: Any) -> tuple[str, ...]: if value is None: return () if isinstance(value, str): v = value.strip() return (v,) if v else () if isinstance(value, list): out: list[str] = [] for item in value: s = _coerce_str(item) if s: out.append(s) return tuple(out) s = _coerce_str(value) return (s,) if s else () ================================================ FILE: benchmarks/locomo/preprocess.py ================================================ from __future__ import annotations import argparse import json import re from collections import Counter from pathlib import Path from typing import Any _SESSION_KEY_RE = re.compile(r"^session_(?P\d+)$") _MULTIMODAL_KEYS = ("img_url", "blip_caption", "query") def preprocess_locomo_json(in_path: str | Path, out_path: str | Path) -> dict[str, int]: """ Preprocess LoCoMo into a Memori-friendly role format. - Removes multimodal turn fields (img_url, blip_caption, query). - Rewrites turn speakers so conversation.speaker_b becomes "assistant" and all others "user". If conversation.speaker_b is missing, the sample is kept and left unchanged. """ src = Path(in_path) dst = Path(out_path) data = json.loads(src.read_text(encoding="utf-8")) if not isinstance(data, list): raise ValueError("LoCoMo JSON must be a list of samples") kept: list[dict[str, Any]] = [] dropped_reasons: Counter[str] = Counter() removed_multimodal_turns = 0 for _idx, sample in enumerate(data): if not isinstance(sample, dict): dropped_reasons["invalid_sample"] += 1 continue conv = sample.get("conversation") removed_multimodal_turns += _strip_multimodal_fields_inplace(conv) speaker_b = None if isinstance(conv, dict): speaker_b = _coerce_str(conv.get("speaker_b")) if speaker_b: _rewrite_speakers_inplace(conv, assistant_speaker=speaker_b) else: # Keep unchanged when we can't confidently map assistant roles. dropped_reasons["missing_speaker_b"] += 0 kept.append(sample) dst.parent.mkdir(parents=True, exist_ok=True) dst.write_text(json.dumps(kept, ensure_ascii=False), encoding="utf-8") return { "samples_in": len(data), "samples_out": len(kept), "removed_multimodal_turns": int(removed_multimodal_turns), "dropped_invalid_sample": int(dropped_reasons.get("invalid_sample", 0)), } def _strip_multimodal_fields_inplace(conversation: Any) -> int: removed = 0 for turn in _iter_turn_dicts(conversation): if not isinstance(turn, dict): continue before = len(turn) for k in _MULTIMODAL_KEYS: turn.pop(k, None) removed += int(len(turn) != before) return removed def _rewrite_speakers_inplace(conversation: Any, *, assistant_speaker: str) -> None: for turn in _iter_turn_dicts(conversation): speaker = _coerce_str(turn.get("speaker")) or "" turn["speaker"] = "assistant" if speaker == assistant_speaker else "user" def _iter_turn_dicts(conversation: Any): if conversation is None: return if isinstance(conversation, list): for session in conversation: if not isinstance(session, dict): continue turns = ( session.get("dialogue") or session.get("turns") or session.get("messages") ) if not isinstance(turns, list): continue for t in turns: if isinstance(t, dict): yield t return if isinstance(conversation, dict): sessions: list[tuple[int, str]] = [] for key, v in conversation.items(): if not isinstance(key, str): continue m = _SESSION_KEY_RE.match(key) if not m: continue if isinstance(v, list): sessions.append((int(m.group("n")), key)) sessions.sort(key=lambda x: x[0]) for _, key in sessions: v = conversation.get(key) if not isinstance(v, list): continue for t in v: if isinstance(t, dict): yield t return def _coerce_str(value: Any) -> str | None: if value is None: return None if isinstance(value, str): v = value.strip() return v or None return str(value).strip() or None def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser( description="Preprocess LoCoMo: strip multimodal fields and map speaker_b -> assistant." ) parser.add_argument( "--in", dest="in_path", required=True, help="Input LoCoMo JSON path." ) parser.add_argument( "--out", dest="out_path", required=True, help="Output JSON path." ) args = parser.parse_args(argv) stats = preprocess_locomo_json(args.in_path, args.out_path) print( "[locomo][preprocess] " f"samples_in={stats['samples_in']} samples_out={stats['samples_out']} " f"removed_multimodal_turns={stats['removed_multimodal_turns']} " f"dropped_invalid_sample={stats['dropped_invalid_sample']}" ) return 0 if __name__ == "__main__": raise SystemExit(main()) ================================================ FILE: benchmarks/locomo/provenance.py ================================================ from __future__ import annotations import math import re import sqlite3 from dataclasses import dataclass from pathlib import Path from typing import TYPE_CHECKING, Any, cast from memori.search import find_similar_embeddings if TYPE_CHECKING: from memori.search._types import FactId @dataclass(frozen=True, slots=True) class FactAttribution: fact_id: int dia_id: str score: float class ProvenanceStore: def __init__(self, path: str | Path) -> None: self.path = Path(path) self.path.parent.mkdir(parents=True, exist_ok=True) self._ensure_schema() def _connect(self) -> sqlite3.Connection: return sqlite3.connect(str(self.path), check_same_thread=False) def _ensure_schema(self) -> None: with self._connect() as conn: conn.execute( """ CREATE TABLE IF NOT EXISTS bench_locomo_fact_provenance( run_id TEXT NOT NULL, sample_id TEXT NOT NULL, fact_id INTEGER NOT NULL, dia_id TEXT NOT NULL, score REAL NOT NULL, PRIMARY KEY (run_id, sample_id, fact_id, dia_id) ) """ ) conn.execute( """ CREATE INDEX IF NOT EXISTS idx_bench_locomo_fact_prov_lookup ON bench_locomo_fact_provenance(run_id, sample_id, fact_id, score DESC) """ ) conn.commit() def upsert_many( self, rows: list[FactAttribution], *, run_id: str, sample_id: str ) -> None: if not rows: return with self._connect() as conn: conn.executemany( """ INSERT OR REPLACE INTO bench_locomo_fact_provenance( run_id, sample_id, fact_id, dia_id, score ) VALUES (?, ?, ?, ?, ?) """, [ (run_id, sample_id, r.fact_id, r.dia_id, float(r.score)) for r in rows ], ) conn.commit() def best_dia_ids_for_fact( self, *, run_id: str, sample_id: str, fact_id: int, limit: int = 1 ) -> list[str]: if limit <= 0: return [] with self._connect() as conn: cur = conn.execute( """ SELECT dia_id FROM bench_locomo_fact_provenance WHERE run_id = ? AND sample_id = ? AND fact_id = ? ORDER BY score DESC LIMIT ? """, (run_id, sample_id, fact_id, limit), ) return [r[0] for r in cur.fetchall() if r and r[0]] def has_any(self, *, run_id: str, sample_id: str) -> bool: with self._connect() as conn: cur = conn.execute( """ SELECT 1 FROM bench_locomo_fact_provenance WHERE run_id = ? AND sample_id = ? LIMIT 1 """, (run_id, sample_id), ) return cur.fetchone() is not None def delete_sample(self, *, run_id: str, sample_id: str) -> None: with self._connect() as conn: conn.execute( """ DELETE FROM bench_locomo_fact_provenance WHERE run_id = ? AND sample_id = ? """, (run_id, sample_id), ) conn.commit() def attribute_facts_to_turn_ids( *, turn_ids: list[str], turn_embeddings: list[list[float]], turn_texts: list[str] | None = None, fact_ids: list[int], fact_embeddings: list[list[float]], fact_texts: list[str] | None = None, top_n: int = 1, min_score: float | None = None, ) -> dict[int, list[tuple[str, float]]]: """ Map each fact to the most similar LoCoMo turn_id(s). This is intentionally heuristic: it enables benchmark-only provenance without changing Memori's product schema. """ if top_n <= 0: return {} if len(turn_ids) != len(turn_embeddings): raise ValueError("turn_ids and turn_embeddings must be the same length") if len(fact_ids) != len(fact_embeddings): raise ValueError("fact_ids and fact_embeddings must be the same length") if turn_texts is not None and len(turn_texts) != len(turn_ids): raise ValueError("turn_texts and turn_ids must be the same length") if fact_texts is not None and len(fact_texts) != len(fact_ids): raise ValueError("fact_texts and fact_ids must be the same length") embeddings: list[tuple[FactId, Any]] = cast( "list[tuple[FactId, Any]]", list(enumerate(turn_embeddings)) ) out: dict[int, list[tuple[str, float]]] = {} for i, (fact_id, qemb) in enumerate(zip(fact_ids, fact_embeddings, strict=True)): # Use a larger semantic pool before reranking to improve coverage. semantic_pool = max(top_n, min(len(turn_ids), max(top_n * 10, 50))) similar = find_similar_embeddings(embeddings, qemb, limit=semantic_pool) fact_text = (fact_texts[i] if fact_texts is not None else "") or "" lexical_scores = ( _lexical_scores(query_text=fact_text, docs=turn_texts) if turn_texts is not None else None ) mapped: list[tuple[str, float]] = [] scored: list[tuple[int, float]] = [] for raw_idx, score in similar: # idx is always int here (from enumerate), cast for type checker idx = cast(int, raw_idx) if idx < 0 or idx >= len(turn_ids): continue lex = float(lexical_scores[idx]) if lexical_scores is not None else 0.0 combined = (0.8 * float(score)) + (0.2 * lex) scored.append((idx, combined)) scored.sort(key=lambda t: t[1], reverse=True) for idx, score in scored[:top_n]: if min_score is not None and score < min_score: continue mapped.append((turn_ids[idx], float(score))) # If the threshold filtered everything, prefer some attribution over none. if not mapped and scored: idx_best, score_best = scored[0] mapped.append((turn_ids[idx_best], float(score_best))) out[int(fact_id)] = mapped return out _TOKEN_RE = re.compile(r"[a-z0-9]+") _STOPWORDS = { "a", "an", "and", "are", "as", "at", "be", "by", "did", "do", "does", "for", "from", "had", "has", "have", "how", "i", "in", "is", "it", "of", "on", "or", "that", "the", "their", "then", "there", "to", "was", "were", "what", "when", "where", "which", "who", "why", "with", "you", "your", } def _tokenize(text: str) -> list[str]: tokens = [t for t in _TOKEN_RE.findall((text or "").lower()) if t] return [t for t in tokens if t not in _STOPWORDS] def _lexical_scores(*, query_text: str, docs: list[str]) -> list[float]: q_tokens = _tokenize(query_text) if not q_tokens or not docs: return [0.0 for _ in docs] doc_tokens = [set(_tokenize(d)) for d in docs] n = float(len(docs)) or 1.0 df: dict[str, int] = {} for t in set(q_tokens): df[t] = sum(1 for toks in doc_tokens if t in toks) idf = {t: (math.log((n + 1.0) / (float(df[t]) + 1.0)) + 1.0) for t in df} denom = sum(idf.get(t, 0.0) for t in q_tokens) or 1.0 out: list[float] = [] for toks in doc_tokens: num = sum(idf.get(t, 0.0) for t in q_tokens if t in toks) out.append(float(num / denom)) return out ================================================ FILE: benchmarks/locomo/report.py ================================================ from __future__ import annotations import argparse import json from pathlib import Path from typing import Any from benchmarks.locomo._run_impl import CATEGORY_LABELS def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser( description="Aggregate LoCoMo predictions.jsonl into summary.json" ) parser.add_argument( "--predictions", required=True, help="Path to predictions.jsonl created by benchmarks/locomo/run.py", ) parser.add_argument( "--out", required=True, help="Path to write summary.json", ) args = parser.parse_args(argv) categories: dict[str, int] = {} sample_ids: set[str] = set() total = 0 run_id: str | None = None timestamp_utc: str | None = None sums = { "hit@1": 0.0, "hit@3": 0.0, "hit@5": 0.0, "hit@10": 0.0, "hit@20": 0.0, "hit@30": 0.0, "mrr": 0.0, } sums_by_cat: dict[str, dict[str, float]] = {} counts_by_cat: dict[str, int] = {} for row in _read_jsonl(Path(args.predictions)): total += 1 sample_id = str(row.get("sample_id", "")).strip() if sample_id: sample_ids.add(sample_id) cat = str(row.get("category") or "unknown") categories[cat] = categories.get(cat, 0) + 1 metrics = ( (row.get("retrieval") or {}).get("metrics") if isinstance(row.get("retrieval"), dict) else None ) if isinstance(metrics, dict): for key in sums: if key in metrics: sums[key] += float(metrics[key]) sums_by_cat.setdefault( cat, { "hit@1": 0.0, "hit@3": 0.0, "hit@5": 0.0, "hit@10": 0.0, "hit@20": 0.0, "hit@30": 0.0, "mrr": 0.0, }, ) for key in sums_by_cat[cat]: if key in metrics: sums_by_cat[cat][key] += float(metrics[key]) counts_by_cat[cat] = counts_by_cat.get(cat, 0) + 1 run_id = run_id or _coerce_str(row.get("run_id")) timestamp_utc = timestamp_utc or _coerce_str(row.get("timestamp_utc")) denom = float(total) if total else 1.0 metrics_overall = {k: (sums[k] / denom) for k in sums} metrics_by_category: dict[str, dict[str, float]] = {} for cat, sums_cat in sums_by_cat.items(): denom_cat = float(counts_by_cat.get(cat, 0)) or 1.0 metrics_by_category[cat] = {k: (sums_cat[k] / denom_cat) for k in sums_cat} questions_by_category_labeled = { CATEGORY_LABELS.get(cat, cat): count for cat, count in categories.items() } metrics_by_category_labeled = { CATEGORY_LABELS.get(cat, cat): vals for cat, vals in metrics_by_category.items() } out = { "run_id": run_id, "timestamp_utc": timestamp_utc, "sample_count": len(sample_ids), "question_count": total, "category_labels": dict(CATEGORY_LABELS), "questions_by_category": dict(sorted(categories.items(), key=lambda kv: kv[0])), "questions_by_category_labeled": dict( sorted(questions_by_category_labeled.items(), key=lambda kv: kv[0]) ), "metrics_overall": metrics_overall, "metrics_by_category": dict( sorted(metrics_by_category.items(), key=lambda kv: kv[0]) ), "metrics_by_category_labeled": dict( sorted(metrics_by_category_labeled.items(), key=lambda kv: kv[0]) ), } Path(args.out).write_text(json.dumps(out, indent=2), encoding="utf-8") return 0 def _read_jsonl(path: Path) -> list[dict[str, Any]]: rows: list[dict[str, Any]] = [] for line in path.read_text(encoding="utf-8").splitlines(): if not line.strip(): continue rows.append(json.loads(line)) return rows def _coerce_str(value: Any) -> str | None: if value is None: return None if isinstance(value, str): v = value.strip() return v or None return str(value).strip() or None if __name__ == "__main__": raise SystemExit(main()) ================================================ FILE: benchmarks/locomo/retrieval.py ================================================ from __future__ import annotations import re from dataclasses import dataclass from typing import Any from benchmarks.locomo._types import LoCoMoSample @dataclass(frozen=True, slots=True) class TurnFact: turn_id: str content: str _TURN_ID_RE = re.compile(r"^\[(?P[^\]]+)\]\s*") def build_turn_facts( sample: LoCoMoSample, ) -> tuple[tuple[TurnFact, ...], dict[tuple[str, int], str]]: facts: list[TurnFact] = [] index: dict[tuple[str, int], str] = {} for s_idx, session in enumerate(sample.sessions): session_id = session.session_id or f"session-{s_idx}" for t_idx, turn in enumerate(session.turns): turn_id = turn.turn_id or f"{sample.sample_id}:{session_id}:{t_idx}" index[(session_id, t_idx)] = turn_id speaker = (turn.speaker or "").strip() prefix = f"[{turn_id}]" body = f"{speaker}: {turn.text}" if speaker else turn.text if session.date_time: body = f"{body} (session_time: {session.date_time})" if turn.timestamp: body = f"{body} (ts: {turn.timestamp})" facts.append(TurnFact(turn_id=turn_id, content=f"{prefix} {body}".strip())) return tuple(facts), index def extract_turn_id_from_content(content: str) -> str | None: m = _TURN_ID_RE.match(content or "") if not m: return None v = m.group("turn_id").strip() return v or None def evidence_to_turn_ids( evidence: Any, *, turn_index: dict[tuple[str, int], str] ) -> set[str]: if evidence is None: return set() out: set[str] = set() if isinstance(evidence, dict): _add_evidence_obj(evidence, out=out, turn_index=turn_index) return out if isinstance(evidence, list): for item in evidence: if isinstance(item, dict): _add_evidence_obj(item, out=out, turn_index=turn_index) elif isinstance(item, int): # If evidence is a bare turn index, only safe when there's a single session. _add_evidence_turn_index(item, out=out, turn_index=turn_index) elif isinstance(item, str): if item: out.add(item) return out if isinstance(evidence, int): _add_evidence_turn_index(evidence, out=out, turn_index=turn_index) return out if isinstance(evidence, str) and evidence: out.add(evidence) return out def _add_evidence_obj( obj: dict[str, Any], *, out: set[str], turn_index: dict[tuple[str, int], str] ) -> None: session_id = _coerce_str(obj.get("session_id")) or _coerce_str(obj.get("session")) turn_idx = _coerce_int(obj.get("turn_index")) if session_id is not None and turn_idx is not None: tid = turn_index.get((session_id, turn_idx)) if tid: out.add(tid) return turn_id = _coerce_str(obj.get("turn_id")) or _coerce_str(obj.get("evidence_id")) if turn_id: out.add(turn_id) def _add_evidence_turn_index( idx: int, *, out: set[str], turn_index: dict[tuple[str, int], str] ) -> None: sessions = {session_id for (session_id, _), _tid in turn_index.items()} if len(sessions) != 1: return (session_id,) = sessions tid = turn_index.get((session_id, idx)) if tid: out.add(tid) def _coerce_str(value: Any) -> str | None: if value is None: return None if isinstance(value, str): v = value.strip() return v or None return str(value).strip() or None def _coerce_int(value: Any) -> int | None: if value is None: return None if isinstance(value, int): return value try: return int(str(value).strip()) except Exception: return None ================================================ FILE: benchmarks/locomo/run.py ================================================ import argparse import os from benchmarks.locomo._run_impl import RunConfig, run_locomo def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description="LoCoMo benchmark harness (Phase 2)") parser.add_argument( "--allow-prod-aa", action="store_true", help="(Deprecated) Allow Advanced Augmentation calls without MEMORI_TEST_MODE=1 (production).", ) parser.add_argument( "--dataset", required=True, help="Path to a LoCoMo JSON file (downloaded locally).", ) parser.add_argument( "--out", required=True, help="Output directory for artifacts (predictions.jsonl, summary.json).", ) parser.add_argument( "--sqlite-db", default="", help="SQLite DB file path used for the run (default: /locomo.sqlite).", ) parser.add_argument( "--provenance-db", default="", help="Benchmark-only provenance DB (default: /locomo_provenance.sqlite).", ) parser.add_argument( "--reuse-db", action="store_true", help="Skip ingestion and reuse the existing SQLite/provenance DB for retrieval+scoring.", ) parser.add_argument( "--run-id", default="", help="Run namespace used for entity external IDs/provenance (required when --reuse-db and multiple runs exist in the DB).", ) parser.add_argument( "--k", type=int, default=5, help="Retrieval top-k to store and score (default: 5).", ) parser.add_argument( "--aa-timeout", type=float, default=180.0, help="Timeout (seconds) to wait for Advanced Augmentation to finish per sample.", ) parser.add_argument( "--aa-batch", choices=["per_pair"], default="per_pair", help="How to batch messages when calling Advanced Augmentation (default: per_pair).", ) parser.add_argument( "--aa-dry-run", action="store_true", help="Print/write AA request payload and exit before making any network calls.", ) parser.add_argument( "--aa-max-requests", type=int, default=0, help="Limit the number of AA requests to enqueue during ingestion (0 = no limit).", ) parser.add_argument( "--seed-only", action="store_true", help="Run ingestion only (no retrieval/scoring). Useful for AA payload/summary validation.", ) parser.add_argument( "--meta-llm-provider", default="openai", help="Metadata only: LLM provider to report to Advanced Augmentation (default: openai).", ) parser.add_argument( "--meta-llm-version", default="gpt-4.1-mini", help="Metadata only: LLM model version to report (default: gpt-4.1-mini).", ) parser.add_argument( "--meta-llm-sdk-version", default="unknown", help="Metadata only: LLM SDK version to report (default: unknown).", ) parser.add_argument( "--meta-framework-provider", default="memori", help="Metadata only: framework provider to report (default: memori).", ) parser.add_argument( "--meta-platform-provider", default="benchmark", help="Metadata only: platform provider to report (default: benchmark).", ) parser.add_argument( "--aa-provenance-top-n", type=int, default=1, help="How many turn_ids to attribute to each augmented fact (default: 1).", ) parser.add_argument( "--aa-provenance-min-score", type=float, default=0.25, help="Min cosine similarity to accept a fact->turn attribution (default: 0.25).", ) parser.add_argument( "--aa-provenance-mode", choices=["similarity"], default="similarity", help=( "How to attribute AA facts back to LoCoMo turn IDs for scoring. " "'similarity' maps facts to turns post-hoc using embedding/text similarity (default). " ), ) parser.add_argument( "--rebuild-provenance", action="store_true", help="When --reuse-db: recompute provenance offline from the SQLite DB (no AA calls).", ) parser.add_argument( "--max-samples", type=int, default=0, help="Limit number of samples (0 = no limit).", ) parser.add_argument( "--only-sample-id", default="", help="Run only a single sample_id (exact match).", ) parser.add_argument( "--max-sessions", type=int, default=0, help="Limit number of sessions per sample (0 = no limit).", ) parser.add_argument( "--max-questions", type=int, default=0, help="Limit questions per sample (0 = no limit).", ) parser.add_argument( "--verbose", action="store_true", help="Print progress/logging about seeding and scoring.", ) parser.add_argument( "--log-every-questions", type=int, default=0, help="When --verbose, log progress every N questions (0 = disabled).", ) args = parser.parse_args(argv) # LoCoMo benchmarks should never hit production AA. Force staging routing. os.environ["MEMORI_TEST_MODE"] = "1" run_locomo( RunConfig( dataset=args.dataset, out=args.out, sqlite_db=args.sqlite_db, provenance_db=args.provenance_db, reuse_db=args.reuse_db, run_id=args.run_id, k=args.k, aa_timeout=args.aa_timeout, aa_batch=args.aa_batch, aa_dry_run=args.aa_dry_run, aa_max_requests=args.aa_max_requests, meta_llm_provider=args.meta_llm_provider, meta_llm_version=args.meta_llm_version, meta_llm_sdk_version=args.meta_llm_sdk_version, meta_framework_provider=args.meta_framework_provider, meta_platform_provider=args.meta_platform_provider, aa_provenance_top_n=args.aa_provenance_top_n, aa_provenance_min_score=args.aa_provenance_min_score, aa_provenance_mode=args.aa_provenance_mode, rebuild_provenance=args.rebuild_provenance, allow_prod_aa=args.allow_prod_aa, max_samples=args.max_samples, only_sample_id=args.only_sample_id, max_sessions=args.max_sessions, max_questions=args.max_questions, seed_only=args.seed_only, verbose=args.verbose, log_every_questions=args.log_every_questions, ) ) return 0 if __name__ == "__main__": raise SystemExit(main()) ================================================ FILE: benchmarks/locomo/scoring.py ================================================ from __future__ import annotations def hit_at_k(relevant: set[str], retrieved: list[str], k: int) -> float: if not relevant or not retrieved or k <= 0: return 0.0 top = retrieved[:k] return 1.0 if any(item in relevant for item in top) else 0.0 def mrr(relevant: set[str], retrieved: list[str]) -> float: if not relevant or not retrieved: return 0.0 for i, item in enumerate(retrieved, start=1): if item in relevant: return 1.0 / float(i) return 0.0 def hit_at_k_groups(relevant: set[str], retrieved: list[set[str]], k: int) -> float: """ Variant of hit@k where each retrieved rank can map to multiple IDs. This is useful when a retrieved item (e.g., an AA fact) may plausibly originate from multiple LoCoMo turns (dia_id). """ if not relevant or not retrieved or k <= 0: return 0.0 for group in retrieved[:k]: if group and (group & relevant): return 1.0 return 0.0 def mrr_groups(relevant: set[str], retrieved: list[set[str]]) -> float: if not relevant or not retrieved: return 0.0 for i, group in enumerate(retrieved, start=1): if group and (group & relevant): return 1.0 / float(i) return 0.0 ================================================ FILE: benchmarks/perf/README.md ================================================ # Performance / latency benchmarks This folder contains **performance benchmarks** for Memori recall, powered by `pytest-benchmark`. ## Run locally ```bash uv run pytest -m benchmark --benchmark-only benchmarks/perf/test_recall_benchmarks.py -v ``` ## Run on EC2 (recommended for realistic DB latency) Use an EC2 instance in the same VPC as your database (RDS Postgres/MySQL). - Setup: ```bash chmod +x benchmarks/perf/setup_ec2_benchmarks.sh ./benchmarks/perf/setup_ec2_benchmarks.sh ``` - Run: ```bash export BENCHMARK_POSTGRES_URL="CHANGEME" DB_TYPE=postgres TEST_TYPE=all ./benchmarks/perf/run_benchmarks_ec2.sh ``` ### Environment variables - `DB_TYPE`: `postgres` (default) or `mysql` - `TEST_TYPE`: `all` (default), `end_to_end`, `db_retrieval`, `semantic_search`, `embedding` - `BENCHMARK_POSTGRES_URL`: Postgres connection string - `BENCHMARK_MYSQL_URL`: MySQL connection string ## Outputs Results are saved to `./results`: - JSON: `results_{db}_{type}_{timestamp}.json` - CSV: `report_{db}_{type}_{timestamp}.csv` ================================================ FILE: benchmarks/perf/_results.py ================================================ from __future__ import annotations import csv from pathlib import Path from typing import Any def repo_root() -> Path: return Path(__file__).resolve().parents[2] def results_dir() -> Path: path = repo_root() / "results" path.mkdir(parents=True, exist_ok=True) return path def append_csv_row(path: str | Path, *, header: list[str], row: dict[str, Any]) -> None: out_path = Path(path) out_path.parent.mkdir(parents=True, exist_ok=True) file_exists = out_path.exists() with out_path.open("a", newline="") as f: writer = csv.DictWriter(f, fieldnames=header) if not file_exists: writer.writeheader() writer.writerow(row) ================================================ FILE: benchmarks/perf/conftest.py ================================================ """Pytest fixtures for performance benchmarks.""" import os import pytest from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from benchmarks.perf.fixtures.sample_data import ( generate_facts_with_size, generate_sample_queries, ) from memori import Memori from memori.embeddings import embed_texts @pytest.fixture def postgres_db_connection(): """Create a PostgreSQL database connection factory for benchmarking (via AWS/Docker).""" postgres_uri = os.environ.get( "BENCHMARK_POSTGRES_URL", # Matches docker-compose.yml default DB name "postgresql://memori:memori@localhost:5432/memori_test", ) from sqlalchemy import text # Support SSL root certificate via environment variable (for AWS RDS) connect_args = {} sslrootcert = os.environ.get("BENCHMARK_POSTGRES_SSLROOTCERT") if sslrootcert: connect_args["sslrootcert"] = sslrootcert # Ensure sslmode is set if using SSL cert if "sslmode" not in postgres_uri: # Add sslmode=require if not already in URI separator = "&" if "?" in postgres_uri else "?" postgres_uri = f"{postgres_uri}{separator}sslmode=require" engine = create_engine( postgres_uri, pool_pre_ping=True, pool_recycle=300, connect_args=connect_args, ) try: with engine.connect() as conn: conn.execute(text("SELECT 1")) except Exception as e: pytest.skip( f"PostgreSQL not available at {postgres_uri}: {e}. " "Set BENCHMARK_POSTGRES_URL to a database that exists." ) Session = sessionmaker(autocommit=False, autoflush=False, bind=engine) yield Session engine.dispose() @pytest.fixture def mysql_db_connection(): """Create a MySQL database connection factory for benchmarking (via AWS/Docker).""" mysql_uri = os.environ.get( "BENCHMARK_MYSQL_URL", "mysql+pymysql://memori:memori@localhost:3306/memori_test", ) from sqlalchemy import text engine = create_engine( mysql_uri, pool_pre_ping=True, pool_recycle=300, ) try: with engine.connect() as conn: conn.execute(text("SELECT 1")) except Exception as e: pytest.skip(f"MySQL not available at {mysql_uri}: {e}") Session = sessionmaker(autocommit=False, autoflush=False, bind=engine) yield Session engine.dispose() @pytest.fixture( params=["postgres", "mysql"], ids=["postgres", "mysql"], ) def db_connection(request): """Parameterized fixture for realistic database types (no SQLite).""" db_type = request.param if db_type == "postgres": return request.getfixturevalue("postgres_db_connection") elif db_type == "mysql": return request.getfixturevalue("mysql_db_connection") pytest.skip(f"Unsupported benchmark database type: {db_type}") @pytest.fixture def memori_instance(db_connection, request): """Create a Memori instance with the specified database for benchmarking.""" mem = Memori(conn=db_connection) mem.config.storage.build() db_type_param = None for marker in request.node.iter_markers("parametrize"): if "db_connection" in marker.args[0]: db_type_param = marker.args[1][0] if marker.args[1] else None break # Try to infer from connection if not db_type_param: try: # SQLAlchemy sessionmaker is callable, so detect it first by presence of a bind. bind = getattr(db_connection, "kw", {}).get("bind", None) if bind is not None: db_type_param = bind.dialect.name else: db_type_param = "unknown" except Exception: db_type_param = "unknown" mem._benchmark_db_type = db_type_param return mem @pytest.fixture def sample_queries(): """Provide sample queries of varying lengths.""" return generate_sample_queries() @pytest.fixture def fact_content_size(): """Fixture for fact content size. Note: Embeddings are always 768 dimensions (3072 bytes binary) regardless of text size. """ return "small" @pytest.fixture( params=[5, 50, 100, 300, 600, 1000], ids=lambda x: f"n{x}", ) def entity_with_n_facts(memori_instance, fact_content_size, request): """Create an entity with N facts for benchmarking database retrieval.""" fact_count = request.param entity_id = f"benchmark-entity-{fact_count}-{fact_content_size}" memori_instance.attribution(entity_id=entity_id, process_id="benchmark-process") facts = generate_facts_with_size(fact_count, fact_content_size) fact_embeddings = embed_texts( facts, model=memori_instance.config.embeddings.model, ) entity_db_id = memori_instance.config.storage.driver.entity.create(entity_id) memori_instance.config.storage.driver.entity_fact.create( entity_db_id, facts, fact_embeddings ) db_type = getattr(memori_instance, "_benchmark_db_type", "unknown") return { "entity_id": entity_id, "entity_db_id": entity_db_id, "fact_count": fact_count, "content_size": fact_content_size, "db_type": db_type, "facts": facts, } ================================================ FILE: benchmarks/perf/fixtures/sample_data.py ================================================ """Helper functions for generating sample test data for benchmarks.""" import random import string random.seed(42) def generate_random_string(length: int = 10) -> str: """Generate a random string of specified length.""" return "".join(random.choices(string.ascii_letters + string.digits, k=length)) def generate_sample_fact() -> str: """Generate a realistic sample fact for testing.""" templates = [ "User likes {item}", "User lives in {location}", "User works at {company}", "User's favorite color is {color}", "User prefers {preference}", "User has {count} {item}", "User enjoys {activity}", "User's birthday is {date}", ] items = ["pizza", "coffee", "books", "music", "movies", "travel", "coding"] locations = ["New York", "San Francisco", "London", "Tokyo", "Paris"] companies = ["Tech Corp", "Startup Inc", "Big Company", "Small Business"] colors = ["blue", "red", "green", "purple", "yellow"] preferences = ["dark mode", "light mode", "minimalist design", "detailed UI"] activities = ["reading", "hiking", "cooking", "gaming", "photography"] dates = ["January 1st", "March 15th", "June 30th", "December 25th"] template = random.choice(templates) fact = template.format( item=random.choice(items), location=random.choice(locations), company=random.choice(companies), color=random.choice(colors), preference=random.choice(preferences), count=random.randint(1, 10), activity=random.choice(activities), date=random.choice(dates), ) return fact def generate_sample_queries() -> dict[str, list[str]]: """Generate sample queries of varying lengths for benchmarking.""" return { "short": [ "What do I like?", "Where do I live?", "My preferences?", "Favorite color?", "Birthday?", ], "medium": [ "What are my favorite things?", "Tell me about where I live", "What are my preferences for software?", "What is my favorite color and why?", "When is my birthday and how do I celebrate?", ], "long": [ "Can you tell me about all the things I like and enjoy doing?", "I want to know more about where I currently live and work", "What are all my preferences when it comes to software and design?", "Please provide details about my favorite color and any related memories", "I'd like to know when my birthday is and how I typically celebrate it", ], } def generate_facts(count: int) -> list[str]: """Generate a list of unique sample facts.""" facts = [] seen = set() while len(facts) < count: fact = generate_sample_fact() # Add unique identifier to ensure no duplicates unique_fact = f"{fact} (id: {len(facts)})" # Double-check uniqueness (shouldn't be needed with id, but safe) if unique_fact not in seen: facts.append(unique_fact) seen.add(unique_fact) return facts def generate_facts_with_size(count: int, size: str = "small") -> list[str]: """Generate facts for benchmarking. Args: count: Number of facts to generate size: Content size Returns: List of unique facts """ base_facts = generate_facts(count) def with_id_suffix(text: str, idx: int, max_len: int) -> str: suffix = f" (id: {idx})" if max_len <= len(suffix): return suffix[-max_len:] return text[: max_len - len(suffix)] + suffix return [with_id_suffix(fact, i, 60) for i, fact in enumerate(base_facts)] ================================================ FILE: benchmarks/perf/fixtures/sample_facts.py ================================================ # 1,000 synthetic user facts as semantic triples (subject, predicate, object) # Copy/paste into your project. Produces: user_data = {"user": "John", "facts": [(s,p,o), ...]} def build_user_data() -> dict: facts: list[tuple[str, str, str]] = [] def add(s: str, p: str, o: str) -> None: facts.append((s, p, o)) # ----------------------------- # Core profile facts (diverse) # ----------------------------- add("John", "type", "Person") add("John", "has_given_name", "John") add("John", "prefers_language", "English") add("John", "prefers_time_format", "12-hour") add("John", "prefers_temperature_unit", "Fahrenheit") add("John", "prefers_distance_unit", "miles") add("John", "has_home_city", "St_Louis") add("John", "has_home_state", "Missouri") add("John", "has_home_country", "United_States") add("John", "has_role", "Software_Engineer") add("John", "works_in", "Technology") add("John", "interested_in", "AI") add("John", "interested_in", "cloud_infrastructure") add("John", "interested_in", "personal_finance") add("John", "interested_in", "fitness") add("John", "interested_in", "cooking") add("John", "interested_in", "travel") add("John", "interested_in", "productivity") add("John", "interested_in", "home_improvement") add("John", "uses_llm_for", "writing_assistance") add("John", "uses_llm_for", "coding_help") add("John", "uses_llm_for", "travel_planning") add("John", "uses_llm_for", "learning_new_topics") add("John", "uses_llm_for", "recipe_ideas") add("John", "uses_llm_for", "career_advice") add("John", "uses_llm_for", "data_analysis") add("John", "uses_llm_for", "brainstorming") add("John", "prefers_response_style", "structured") add("John", "prefers_response_style", "actionable") add("John", "concerned_about", "latency") add("John", "concerned_about", "cost") add("John", "concerned_about", "privacy") add("John", "primary_os", "macOS") add("John", "primary_browser", "Chrome") add("John", "primary_editor", "VS_Code") add("John", "primary_shell", "zsh") add("John", "primary_email_provider", "Gmail") add("John", "primary_calendar", "Google_Calendar") add("John", "primary_messaging_app", "Slack") add("John", "primary_code_host", "GitHub") add("John", "prefers_document_format", "Markdown") add("John", "prefers_spreadsheet_tool", "Google_Sheets") add("John", "uses_password_manager", "1Password") add("John", "uses_cloud_storage", "Google_Drive") add("John", "uses_issue_tracker", "GitHub_Issues") add("John", "uses_ci_cd", "GitHub_Actions") add("John", "prefers_container_runtime", "Docker") add("John", "prefers_database", "PostgreSQL") add("John", "prefers_cache", "Redis") add("John", "prefers_language_for_backend", "Python") add("John", "knows_language", "JavaScript") add("John", "knows_language", "SQL") add("John", "knows_language", "Bash") add("John", "learning_language", "Rust") add("John", "prefers_testing_framework", "pytest") add("John", "prefers_package_manager", "uv") add("John", "uses_llm_provider", "OpenAI") add("John", "uses_llm_provider", "Anthropic") add("John", "uses_model_family", "GPT") add("John", "uses_model_family", "Claude") add("John", "has_hobby", "fantasy_football") add("John", "follows_sport", "NFL") add("John", "follows_sport", "NHL") add("John", "prefers_coffee_drink", "latte") add("John", "prefers_breakfast", "oatmeal") add("John", "prefers_lunch", "salad") add("John", "prefers_dinner", "grilled_chicken") add("John", "commutes_by", "car") add("John", "prefers_meeting_platform", "Zoom") # ----------------------------- # Family / household facts # ----------------------------- family = [ ("Spouse_01", "spouse"), ("Child_01", "child"), ("Child_02", "child"), ("Parent_01", "parent"), ("Parent_02", "parent"), ("Sibling_01", "sibling"), ("Sibling_02", "sibling"), ("Pet_01", "pet"), ] for i, (entity, rel) in enumerate(family, start=1): add(entity, "type", "Person" if "Pet" not in entity else "Animal") add("John", f"has_{rel}", entity) add(entity, "has_first_name", entity.split("_")[0]) add(entity, "located_in", f"City_{(i % 25) + 1:03d}") add( entity, "preferred_contact_method", ["text", "call", "email"][i % 3] if "Pet" not in entity else "n/a", ) # ----------------------------- # Friends (varied interests) # ----------------------------- friend_interests = [ "music", "travel", "tech", "food", "sports", "finance", "fitness", "gaming", "books", "photography", ] contact_methods = ["email", "text", "slack", "signal", "call"] for i in range(1, 61): # 60 friends f = f"Friend_{i:03d}" add(f, "type", "Person") add("John", "has_friend", f) add(f, "located_in", f"City_{(i % 80) + 1:03d}") add(f, "interested_in", friend_interests[i % len(friend_interests)]) add(f, "preferred_contact_method", contact_methods[i % len(contact_methods)]) # ----------------------------- # Coworkers (teams, roles, tools) # ----------------------------- roles = [ "Backend_Engineer", "Frontend_Engineer", "DevOps_Engineer", "Product_Manager", "Designer", "Data_Engineer", ] teams = ["Platform", "Infra", "Product", "Data", "Security", "Growth"] tools = ["Jira", "Linear", "Confluence", "Notion", "Figma", "Datadog", "Grafana"] for i in range(1, 41): # 40 coworkers c = f"Coworker_{i:03d}" add(c, "type", "Person") add("John", "works_with", c) add(c, "has_role", roles[i % len(roles)]) add(c, "member_of_team", teams[i % len(teams)]) add(c, "uses_tool", tools[i % len(tools)]) # ----------------------------- # Places: cities, venues, travel # ----------------------------- for i in range(1, 81): # 80 cities city = f"City_{i:03d}" add(city, "type", "City") add(city, "in_country", "United_States" if i <= 60 else "International") add("John", "has_visited", city if i <= 45 else f"Planned_{city}") add( city, "has_timezone", "America/Chicago" if i % 3 == 0 else "America/New_York" if i % 3 == 1 else "America/Los_Angeles", ) venue_types = [ "Restaurant", "Coffee_Shop", "Gym", "Airport", "Hotel", "Park", "Museum", "Stadium", ] for i in range(1, 81): # 80 venues v = f"Venue_{i:03d}" add(v, "type", venue_types[i % len(venue_types)]) add(v, "located_in", f"City_{(i % 80) + 1:03d}") add("John", "likes_place", v if i % 4 != 0 else f"Neutral_{v}") add(v, "has_price_tier", ["$", "$$", "$$$"][i % 3]) # ----------------------------- # Devices, apps, services, accounts # ----------------------------- devices = [ "MacBook_Pro", "iPhone", "iPad", "AirPods", "Smart_TV", "Router", "NAS", "Mechanical_Keyboard", "Gaming_PC", "Monitor_34inch", "Standing_Desk", "Ergonomic_Chair", "Fitness_Tracker", "Smart_Speaker", "Kindle", "External_SSD", "Webcam", "Microphone", "Printer", "Smart_Thermostat", ] for d in devices: add(d, "type", "Device") add("John", "owns_device", d) add(d, "used_for", "work" if "MacBook" in d or "Monitor" in d else "personal") add(d, "has_status", "active") apps = [ "Notion", "Slack", "Gmail", "Google_Calendar", "Spotify", "YouTube", "Netflix", "Amazon", "GitHub", "Figma", "Jira", "Linear", "Zoom", "1Password", "Google_Drive", ] for i, app in enumerate(apps, start=1): add(app, "type", "App") add("John", "uses_app", app) add(app, "has_version", f"{(i % 5) + 1}.{(i % 10)}.{(i % 20)}") return {"user": "John", "facts": facts} ================================================ FILE: benchmarks/perf/generate_percentile_report.py ================================================ """Generate percentile report (p50/p95/p99) from benchmark JSON results.""" import json import sys from pathlib import Path def calculate_percentile(data, percentile): """Calculate percentile from sorted data.""" if not data: return None sorted_data = sorted(data) index = (len(sorted_data) - 1) * percentile / 100 lower = int(index) upper = lower + 1 weight = index - lower if upper >= len(sorted_data): return sorted_data[lower] return sorted_data[lower] * (1 - weight) + sorted_data[upper] * weight def extract_n_from_test_name(test_name): """Extract N (number of records) from test name.""" import re # Our parametrized ids use "n{N}" (e.g. "[n1000-postgres-small]") match = re.search(r"n(\d+)", test_name) if match: return int(match.group(1)) # Backwards-compatible: plain numeric parameter (e.g. "[1000]") match = re.search(r"\[(\d+)\]", test_name) return int(match.group(1)) if match else None def extract_db_type_from_test_name(test_name): """Extract database type from test name.""" import re # Look for database type in test name (postgres, mysql) match = re.search(r"\[(postgres|mysql)[-\]]", test_name) if not match: match = re.search(r"-(postgres|mysql)[-\]]", test_name) if not match: match = re.search(r"(postgres|mysql)", test_name) return match.group(1) if match else "unknown" def extract_content_size_from_test_name(test_name): """Extract content size from test name.""" import re match = re.search(r"\[small[-\]]", test_name) if not match: match = re.search(r"-small[-\]]", test_name) if not match: match = re.search(r"small", test_name) return match.group(0) if match else "small" def extract_benchmark_id_from_test_name(test_name): """ Extract a stable benchmark identifier from pytest-benchmark's name field. Examples: "test_benchmark_end_to_end_recall[n1000-sqlite-small]" -> "test_benchmark_end_to_end_recall" "test_benchmark_query_embedding_short" -> "test_benchmark_query_embedding_short" """ import re match = re.match(r"([^\[]+)", test_name) return match.group(1) if match else test_name def generate_percentile_report(json_file_path, max_n=None): """Generate p50/p95/p99 report from benchmark JSON. Args: json_file_path: Path to pytest-benchmark JSON output max_n: Optional maximum N value to include (filters out tests with N > max_n) """ with open(json_file_path) as f: data = json.load(f) benchmarks = {} for benchmark in data.get("benchmarks", []): test_name = benchmark.get("name", "") benchmark_id = extract_benchmark_id_from_test_name(test_name) n = extract_n_from_test_name(test_name) # Skip if N is None (couldn't extract) or exceeds max_n filter if n is None: continue if max_n is not None and n > max_n: continue db_type = extract_db_type_from_test_name(test_name) content_size = extract_content_size_from_test_name(test_name) extra_info = benchmark.get("extra_info", {}) or {} stats = benchmark.get("stats", {}) times = stats.get("data", []) if not times: continue peak_rss_bytes = extra_info.get("peak_rss_bytes") p50 = calculate_percentile(times, 50) p95 = calculate_percentile(times, 95) p99 = calculate_percentile(times, 99) # Create composite key. We include benchmark_id so different benchmark types # don't overwrite each other (e.g. end-to-end vs db fetch for same N/db/size). key = (benchmark_id, n, db_type, content_size) benchmarks[key] = { "benchmark_id": benchmark_id, "n": n, "db_type": db_type, "content_size": content_size, "p50": p50, "p95": p95, "p99": p99, "mean": stats.get("mean", 0), "min": stats.get("min", 0), "max": stats.get("max", 0), "peak_rss_bytes": peak_rss_bytes, } return benchmarks def generate_report(benchmarks, output_format="table"): """Generate percentile report in specified format as string.""" lines = [] if output_format == "table": lines.append("\n" + "=" * 100) lines.append( "PERCENTILE REPORT (p50/p95/p99) per N, Database Type, and Content Size" ) lines.append("=" * 100) lines.append( f"{'Benchmark':<34} {'N':<8} {'DB':<12} {'Size':<8} {'p50 (ms)':<12} {'p95 (ms)':<12} " f"{'p99 (ms)':<12} {'Mean (ms)':<12} {'Peak RSS (MB)':<14}" ) lines.append("-" * 100) for key in sorted(benchmarks.keys()): stats = benchmarks[key] peak_rss_mb = ( (stats["peak_rss_bytes"] / (1024 * 1024)) if stats.get("peak_rss_bytes") is not None else None ) lines.append( f"{stats['benchmark_id']:<34} " f"{stats['n']:<8} " f"{stats['db_type']:<12} " f"{stats['content_size']:<8} " f"{stats['p50'] * 1000:<12.4f} " f"{stats['p95'] * 1000:<12.4f} " f"{stats['p99'] * 1000:<12.4f} " f"{stats['mean'] * 1000:<12.4f} " f"{(f'{peak_rss_mb:.1f}' if peak_rss_mb is not None else ''):<14}" ) lines.append("=" * 100) return "\n".join(lines) if output_format == "csv": lines.append( "benchmark_id,N,db_type,content_size,p50_ms,p95_ms,p99_ms,mean_ms,min_ms,max_ms,peak_rss_mb" ) for key in sorted(benchmarks.keys()): stats = benchmarks[key] peak_rss_mb = ( (stats["peak_rss_bytes"] / (1024 * 1024)) if stats.get("peak_rss_bytes") is not None else None ) lines.append( f"{stats['benchmark_id']}," f"{stats['n']}," f"{stats['db_type']}," f"{stats['content_size']}," f"{stats['p50'] * 1000:.4f}," f"{stats['p95'] * 1000:.4f}," f"{stats['p99'] * 1000:.4f}," f"{stats['mean'] * 1000:.4f}," f"{stats['min'] * 1000:.4f}," f"{stats['max'] * 1000:.4f}," f"{(f'{peak_rss_mb:.4f}' if peak_rss_mb is not None else '')}" ) return "\n".join(lines) if output_format == "json": output = {} for key in sorted(benchmarks.keys()): stats = benchmarks[key] output_key = f"{stats['benchmark_id']}_{stats['n']}_{stats['db_type']}_{stats['content_size']}" peak_rss_mb = ( (stats["peak_rss_bytes"] / (1024 * 1024)) if stats.get("peak_rss_bytes") is not None else None ) output[output_key] = { "benchmark_id": stats["benchmark_id"], "n": stats["n"], "db_type": stats["db_type"], "content_size": stats["content_size"], "p50_ms": stats["p50"] * 1000, "p95_ms": stats["p95"] * 1000, "p99_ms": stats["p99"] * 1000, "mean_ms": stats["mean"] * 1000, "min_ms": stats["min"] * 1000, "max_ms": stats["max"] * 1000, "peak_rss_mb": peak_rss_mb, } return json.dumps(output, indent=2) return "" def main(): if len(sys.argv) < 2: print( "Usage: python generate_percentile_report.py [format] [output_file] [max_n]" ) print(" format: table (default), csv, or json") print(" output_file: optional file path to write report (default: stdout)") print( " max_n: optional maximum N value to include (filters out tests with N > max_n)" ) sys.exit(1) json_file = Path(sys.argv[1]) if not json_file.exists(): print(f"Error: File not found: {json_file}") sys.exit(1) output_format = sys.argv[2] if len(sys.argv) > 2 else "table" if output_format not in ["table", "csv", "json"]: print(f"Error: Invalid format '{output_format}'. Use: table, csv, or json") sys.exit(1) output_file = sys.argv[3] if len(sys.argv) > 3 else None max_n = int(sys.argv[4]) if len(sys.argv) > 4 else None benchmarks = generate_percentile_report(json_file, max_n=max_n) if not benchmarks: print("No benchmark data found.") sys.exit(1) report = generate_report(benchmarks, output_format) if output_file: Path(output_file).write_text(report, encoding="utf-8") print(f"Report written to: {output_file}") else: print(report) if __name__ == "__main__": main() ================================================ FILE: benchmarks/perf/memory_utils.py ================================================ import threading import time from collections.abc import Callable from typing import TypeVar T = TypeVar("T") def measure_peak_rss_bytes( fn: Callable[[], T], *, sample_interval_seconds: float = 0.005 ) -> tuple[T, int | None]: """ Measure approximate peak RSS (process resident set size) while running fn. Returns (result, peak_rss_bytes). If psutil isn't available, peak is None. """ try: import psutil except Exception: return fn(), None proc = psutil.Process() peak = {"rss": proc.memory_info().rss} stop = threading.Event() def _sampler() -> None: while not stop.is_set(): try: rss = proc.memory_info().rss if rss > peak["rss"]: peak["rss"] = rss except Exception: pass time.sleep(sample_interval_seconds) t = threading.Thread(target=_sampler, daemon=True) t.start() try: result = fn() finally: stop.set() t.join(timeout=1.0) return result, int(peak["rss"]) ================================================ FILE: benchmarks/perf/run_benchmarks_ec2.sh ================================================ #!/bin/bash # Shared benchmark execution functions for AWS EC2 environment set -e # Get script location to handle relative paths correctly BENCHMARK_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(cd "$BENCHMARK_DIR/../.." && pwd)" # Default settings DB_TYPE=${DB_TYPE:-"postgres"} TEST_TYPE=${TEST_TYPE:-"all"} # options: all, end_to_end, db_retrieval, semantic_search, embedding OUTPUT_DIR=${OUTPUT_DIR:-"$REPO_ROOT/results"} mkdir -p "$OUTPUT_DIR" run_benchmarks() { local db=$1 local type=$2 local timestamp=$(date +%Y%m%d_%H%M%S) local output_json="$OUTPUT_DIR/results_${db}_${type}_${timestamp}.json" local output_csv="$OUTPUT_DIR/report_${db}_${type}_${timestamp}.csv" echo "====================================================" echo "Running benchmarks for: DB=$db, Type=$type" echo "Output JSON: $output_json" echo "Output CSV: $output_csv" echo "====================================================" # Determine pytest filter (-k) based on test type local filter="" case $type in "end_to_end") filter="TestEndToEndRecallBenchmarks" ;; "db_retrieval") filter="DatabaseEmbeddingRetrievalBenchmarks or DatabaseFactContentRetrievalBenchmarks" ;; "semantic_search") filter="TestSemanticSearchBenchmarks" ;; "embedding") filter="TestQueryEmbeddingBenchmarks" ;; "all") filter="" ;; *) echo "Unknown test type: $type"; exit 1 ;; esac # Add database filter if [[ -n "$filter" ]]; then filter="($filter) and $db" else filter="$db" fi # Run benchmarks from repo root ( cd "$REPO_ROOT" uv run pytest -m benchmark \ --benchmark-only \ benchmarks/perf/test_recall_benchmarks.py \ -k "$filter" \ -v \ --benchmark-json="$output_json" # Automatically convert to CSV if [[ -f "$output_json" ]]; then echo "Converting results to CSV..." uv run python benchmarks/perf/generate_percentile_report.py \ "$output_json" \ csv \ "$output_csv" echo "CSV Report generated: $output_csv" else echo "Warning: JSON results file not found, skipping CSV generation." fi ) } # If script is executed directly (not sourced), run based on env vars if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then # Print usage info if help requested if [[ "$1" == "--help" || "$1" == "-h" ]]; then echo "Usage: DB_TYPE=[postgres|mysql] TEST_TYPE=[all|end_to_end|db_retrieval|semantic_search|embedding] $0" exit 0 fi run_benchmarks "$DB_TYPE" "$TEST_TYPE" echo "====================================================" echo "Benchmark Run Complete" echo "====================================================" fi ================================================ FILE: benchmarks/perf/setup_ec2_benchmarks.sh ================================================ #!/bin/bash # Setup script for running benchmarks on AWS EC2 set -e echo "Setting up Memori benchmarks on EC2..." # Install system dependencies sudo apt-get update sudo apt-get install -y \ python3.12 \ python3.12-venv \ git \ curl \ build-essential \ postgresql-client \ default-mysql-client # Install uv curl -LsSf https://astral.sh/uv/install.sh | sh source $HOME/.cargo/env # Clone repository (replace with your repo URL if not already cloned) # git clone https://github.com/MemoriLabs/Memori.git # cd Memori # Sync dependencies uv sync --all-extras # Source the runner to get run_benchmarks function SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" source "$SCRIPT_DIR/run_benchmarks_ec2.sh" echo "Setup complete!" echo "" echo "To run all benchmarks for Postgres:" echo " DB_TYPE=postgres TEST_TYPE=all ./benchmarks/perf/run_benchmarks_ec2.sh" echo "" echo "To run end-to-end benchmarks for MySQL:" echo " DB_TYPE=mysql TEST_TYPE=end_to_end ./benchmarks/perf/run_benchmarks_ec2.sh" echo "" echo "Results will be automatically saved to the ./results directory as CSV." ================================================ FILE: benchmarks/perf/test_cloud_recall_benchmarks.py ================================================ """Cloud (Memori API) performance / latency benchmarks. These benchmarks exercise the cloud recall path (network + API latency) rather than local DB-backed recall. """ from __future__ import annotations import os from uuid import uuid4 import pytest from memori._config import Config from memori.llm._base import BaseInvoke from memori.llm._constants import OPENAI_LLM_PROVIDER from memori.memory._manager import Manager from memori.memory.recall import Recall def _make_cloud_cfg(*, entity_id: str, process_id: str) -> Config: cfg = Config() cfg.cloud = True cfg.entity_id = entity_id cfg.process_id = process_id if cfg.session_id is None: cfg.session_id = uuid4() # Pick a provider so injection rules are deterministic. cfg.llm.provider = OPENAI_LLM_PROVIDER cfg.framework.provider = "bench" return cfg def _seed_cloud_messages( cfg: Config, *, n_messages: int, entity_id: str, process_id: str ) -> None: cfg.cloud = True cfg.entity_id = entity_id cfg.process_id = process_id if cfg.session_id is None: cfg.session_id = uuid4() messages: list[dict[str, object]] = [] for i in range(n_messages): messages.append({"role": "user", "type": None, "text": f"I like item_{i}."}) messages.append({"role": "assistant", "type": None, "text": "Ok."}) Manager(cfg).execute( { "attribution": { "entity": {"id": str(entity_id)}, "process": {"id": str(process_id)}, }, "messages": messages, "session": {"id": str(cfg.session_id)}, } ) @pytest.mark.benchmark @pytest.mark.parametrize("n_messages", [20, 100], ids=["n20", "n100"]) def test_benchmark_cloud_recall_latency(benchmark, n_messages: int) -> None: """Benchmark end-to-end cloud recall latency. Includes: HTTP request + cloud service time. Excludes: local DB retrieval. """ os.environ["MEMORI_TEST_MODE"] = "1" api_key = os.environ.get("MEMORI_API_KEY") if not api_key: pytest.skip("Set MEMORI_API_KEY to benchmark cloud recall.") cfg = Config() entity_id = os.environ.get("BENCHMARK_CLOUD_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_CLOUD_PROCESS_ID", "bench-cloud-process") _seed_cloud_messages( cfg, n_messages=n_messages, entity_id=entity_id, process_id=process_id, ) recall = Recall(cfg) def _call(): return recall.search_facts(query="What do I like?", limit=5) result = benchmark(_call) assert isinstance(result, list) @pytest.mark.benchmark @pytest.mark.parametrize("n_history_pairs", [20, 100], ids=["n20", "n100"]) def test_benchmark_cloud_pre_llm_overhead(benchmark, n_history_pairs: int) -> None: """Benchmark Memori overhead up to the LLM call (cloud mode). This measures the "added latency" before calling an LLM: - (optional) fetch cloud conversation history - cloud recall request (server-side embeddings + retrieval) - prompt/message injection logic It intentionally does NOT call an LLM provider. """ if not os.environ.get("MEMORI_API_KEY"): pytest.skip("Set MEMORI_API_KEY to benchmark cloud pre-LLM overhead.") entity_id = os.environ.get("BENCHMARK_CLOUD_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_CLOUD_PROCESS_ID", "bench-cloud-process") cfg = _make_cloud_cfg(entity_id=entity_id, process_id=process_id) # Seed a stable session for cloud history fetch. _seed_cloud_messages( cfg, n_messages=n_history_pairs, entity_id=entity_id, process_id=process_id, ) query = "What do I like?" def _prepare(): invoke = BaseInvoke(cfg, lambda **_kwargs: None) kwargs = {"messages": [{"role": "user", "content": query}]} kwargs = invoke.inject_conversation_messages(kwargs) kwargs = invoke.inject_recalled_facts(kwargs) return kwargs result = benchmark(_prepare) assert isinstance(result, dict) @pytest.mark.benchmark @pytest.mark.parametrize("n_history_pairs", [20, 100], ids=["n20", "n100"]) def test_benchmark_cloud_network_history_get(benchmark, n_history_pairs: int) -> None: """cloud conversation history fetched via recall (POST), no injection.""" if not os.environ.get("MEMORI_API_KEY"): pytest.skip("Set MEMORI_API_KEY to benchmark cloud history GET.") entity_id = os.environ.get("BENCHMARK_CLOUD_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_CLOUD_PROCESS_ID", "bench-cloud-process") cfg = _make_cloud_cfg(entity_id=entity_id, process_id=process_id) _seed_cloud_messages( cfg, n_messages=n_history_pairs, entity_id=entity_id, process_id=process_id, ) def _call(): recall = Recall(cfg) data = recall._cloud_recall(query="History fetch benchmark") _facts, messages = recall._parse_cloud_recall_response(data) return messages result = benchmark(_call) assert isinstance(result, list) @pytest.mark.benchmark @pytest.mark.parametrize("n_history_pairs", [20, 100], ids=["n20", "n100"]) def test_benchmark_cloud_network_recall_post(benchmark, n_history_pairs: int) -> None: """ONLY cloud recall call (POST /recall), no injection. We seed N history pairs first so cloud has a consistent amount of data. """ if not os.environ.get("MEMORI_API_KEY"): pytest.skip("Set MEMORI_API_KEY to benchmark cloud recall POST.") entity_id = os.environ.get("BENCHMARK_CLOUD_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_cloud_PROCESS_ID", "bench-cloud-process") cfg = _make_cloud_cfg(entity_id=entity_id, process_id=process_id) _seed_cloud_messages( cfg, n_messages=n_history_pairs, entity_id=entity_id, process_id=process_id, ) recall = Recall(cfg) def _call(): return recall.search_facts(query="What do I like?", limit=5) result = benchmark(_call) assert isinstance(result, list) @pytest.mark.benchmark @pytest.mark.parametrize("n_history_pairs", [20, 100], ids=["n20", "n100"]) def test_benchmark_cloud_network_only_history_plus_recall( benchmark, n_history_pairs: int ) -> None: """ONLY the cloud recall call (history + facts), no injection.""" if not os.environ.get("MEMORI_API_KEY"): pytest.skip("Set MEMORI_API_KEY to benchmark cloud network-only overhead.") entity_id = os.environ.get("BENCHMARK_cloud_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_CLOUD_PROCESS_ID", "bench-cloud-process") cfg = _make_cloud_cfg(entity_id=entity_id, process_id=process_id) _seed_cloud_messages( cfg, n_messages=n_history_pairs, entity_id=entity_id, process_id=process_id, ) recall = Recall(cfg) def _call(): data = recall._cloud_recall(query="What do I like?") facts, _messages = recall._parse_cloud_recall_response(data) return facts result = benchmark(_call) assert isinstance(result, list) def _make_invoke_with_stubbed_history( cfg: Config, *, n_history_pairs: int ) -> BaseInvoke: history: list[dict[str, str]] = [] for i in range(n_history_pairs): history.append({"role": "user", "content": f"I like item_{i}."}) history.append({"role": "assistant", "content": "Ok."}) invoke = BaseInvoke(cfg, lambda **_kwargs: None) invoke._cloud_conversation_messages = history return invoke @pytest.mark.benchmark @pytest.mark.parametrize("n_history_pairs", [20, 100], ids=["n20", "n100"]) def test_benchmark_cloud_injection_only_history( benchmark, n_history_pairs: int ) -> None: """ONLY Python-side history injection (no network).""" entity_id = os.environ.get("BENCHMARK_CLOUD_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_CLOUD_PROCESS_ID", "bench-cloud-process") cfg = _make_cloud_cfg(entity_id=entity_id, process_id=process_id) query = "What do I like?" invoke = _make_invoke_with_stubbed_history(cfg, n_history_pairs=n_history_pairs) def _call(): kwargs = {"messages": [{"role": "user", "content": query}]} return invoke.inject_conversation_messages(kwargs) result = benchmark(_call) assert isinstance(result, dict) @pytest.mark.benchmark @pytest.mark.parametrize("n_recalled_facts", [5, 20], ids=["n5", "n20"]) def test_benchmark_cloud_injection_only_recalled_facts( benchmark, n_recalled_facts: int, mocker ) -> None: """ONLY Python-side recalled-facts injection (no network).""" entity_id = os.environ.get("BENCHMARK_CLOUD_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_CLOUD_PROCESS_ID", "bench-cloud-process") cfg = _make_cloud_cfg(entity_id=entity_id, process_id=process_id) fake_facts: list[dict[str, object]] = [ {"content": f"User likes item_{i}", "rank_score": 1.0, "date_created": None} for i in range(n_recalled_facts) ] mocker.patch("memori.memory.recall.Recall.search_facts", return_value=fake_facts) invoke = BaseInvoke(cfg, lambda **_kwargs: None) query = "What do I like?" def _call(): kwargs = {"messages": [{"role": "user", "content": query}]} return invoke.inject_recalled_facts(kwargs) result = benchmark(_call) assert isinstance(result, dict) ================================================ FILE: benchmarks/perf/test_hosted_recall_benchmarks.py ================================================ """Cloud (Memori API) performance / latency benchmarks. These benchmarks exercise the cloud recall path (network + API latency) rather than local DB-backed recall. """ from __future__ import annotations import os from uuid import uuid4 import pytest from memori._config import Config from memori.llm._base import BaseInvoke from memori.llm._constants import OPENAI_LLM_PROVIDER from memori.memory._manager import Manager from memori.memory.recall import Recall def _make_cloud_cfg(*, entity_id: str, process_id: str) -> Config: cfg = Config() cfg.cloud = True cfg.entity_id = entity_id cfg.process_id = process_id if cfg.session_id is None: cfg.session_id = uuid4() # Pick a provider so injection rules are deterministic. cfg.llm.provider = OPENAI_LLM_PROVIDER cfg.framework.provider = "bench" return cfg def _seed_cloud_messages( cfg: Config, *, n_messages: int, entity_id: str, process_id: str ) -> None: cfg.cloud = True cfg.entity_id = entity_id cfg.process_id = process_id if cfg.session_id is None: cfg.session_id = uuid4() messages: list[dict[str, object]] = [] for i in range(n_messages): messages.append({"role": "user", "type": None, "text": f"I like item_{i}."}) messages.append({"role": "assistant", "type": None, "text": "Ok."}) Manager(cfg).execute( { "attribution": { "entity": {"id": str(entity_id)}, "process": {"id": str(process_id)}, }, "messages": messages, "session": {"id": str(cfg.session_id)}, } ) @pytest.mark.benchmark @pytest.mark.parametrize("n_messages", [20, 100], ids=["n20", "n100"]) def test_benchmark_cloud_recall_latency(benchmark, n_messages: int) -> None: """Benchmark end-to-end cloud recall latency. Includes: HTTP request + cloud service time. Excludes: local DB retrieval. """ os.environ["MEMORI_TEST_MODE"] = "1" api_key = os.environ.get("MEMORI_API_KEY") if not api_key: pytest.skip("Set MEMORI_API_KEY to benchmark cloud recall.") cfg = Config() entity_id = os.environ.get("BENCHMARK_CLOUD_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_CLOUD_PROCESS_ID", "bench-cloud-process") _seed_cloud_messages( cfg, n_messages=n_messages, entity_id=entity_id, process_id=process_id, ) recall = Recall(cfg) def _call(): return recall.search_facts(query="What do I like?", limit=5) result = benchmark(_call) assert isinstance(result, list) @pytest.mark.benchmark @pytest.mark.parametrize("n_history_pairs", [20, 100], ids=["n20", "n100"]) def test_benchmark_cloud_pre_llm_overhead(benchmark, n_history_pairs: int) -> None: """Benchmark Memori overhead up to the LLM call (cloud mode). This measures the "added latency" before calling an LLM: - (optional) fetch cloud conversation history - cloud recall request (server-side embeddings + retrieval) - prompt/message injection logic It intentionally does NOT call an LLM provider. """ if not os.environ.get("MEMORI_API_KEY"): pytest.skip("Set MEMORI_API_KEY to benchmark cloud pre-LLM overhead.") entity_id = os.environ.get("BENCHMARK_CLOUD_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_CLOUD_PROCESS_ID", "bench-cloud-process") cfg = _make_cloud_cfg(entity_id=entity_id, process_id=process_id) # Seed a stable session for cloud history fetch. _seed_cloud_messages( cfg, n_messages=n_history_pairs, entity_id=entity_id, process_id=process_id, ) query = "What do I like?" def _prepare(): invoke = BaseInvoke(cfg, lambda **_kwargs: None) kwargs = {"messages": [{"role": "user", "content": query}]} kwargs = invoke.inject_conversation_messages(kwargs) kwargs = invoke.inject_recalled_facts(kwargs) return kwargs result = benchmark(_prepare) assert isinstance(result, dict) @pytest.mark.benchmark @pytest.mark.parametrize("n_history_pairs", [20, 100], ids=["n20", "n100"]) def test_benchmark_cloud_network_history_get(benchmark, n_history_pairs: int) -> None: """cloud conversation history fetched via recall (POST), no injection.""" if not os.environ.get("MEMORI_API_KEY"): pytest.skip("Set MEMORI_API_KEY to benchmark cloud history GET.") entity_id = os.environ.get("BENCHMARK_CLOUD_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_CLOUD_PROCESS_ID", "bench-cloud-process") cfg = _make_cloud_cfg(entity_id=entity_id, process_id=process_id) _seed_cloud_messages( cfg, n_messages=n_history_pairs, entity_id=entity_id, process_id=process_id, ) def _call(): recall = Recall(cfg) data = recall._cloud_recall(query="History fetch benchmark") _facts, messages = recall._parse_cloud_recall_response(data) return messages result = benchmark(_call) assert isinstance(result, list) @pytest.mark.benchmark @pytest.mark.parametrize("n_history_pairs", [20, 100], ids=["n20", "n100"]) def test_benchmark_cloud_network_recall_post(benchmark, n_history_pairs: int) -> None: """ONLY cloud recall call (POST /recall), no injection. We seed N history pairs first so cloud has a consistent amount of data. """ if not os.environ.get("MEMORI_API_KEY"): pytest.skip("Set MEMORI_API_KEY to benchmark cloud recall POST.") entity_id = os.environ.get("BENCHMARK_CLOUD_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_cloud_PROCESS_ID", "bench-cloud-process") cfg = _make_cloud_cfg(entity_id=entity_id, process_id=process_id) _seed_cloud_messages( cfg, n_messages=n_history_pairs, entity_id=entity_id, process_id=process_id, ) recall = Recall(cfg) def _call(): return recall.search_facts(query="What do I like?", limit=5) result = benchmark(_call) assert isinstance(result, list) @pytest.mark.benchmark @pytest.mark.parametrize("n_history_pairs", [20, 100], ids=["n20", "n100"]) def test_benchmark_cloud_network_only_history_plus_recall( benchmark, n_history_pairs: int ) -> None: """ONLY the cloud recall call (history + facts), no injection.""" if not os.environ.get("MEMORI_API_KEY"): pytest.skip("Set MEMORI_API_KEY to benchmark cloud network-only overhead.") entity_id = os.environ.get("BENCHMARK_cloud_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_CLOUD_PROCESS_ID", "bench-cloud-process") cfg = _make_cloud_cfg(entity_id=entity_id, process_id=process_id) _seed_cloud_messages( cfg, n_messages=n_history_pairs, entity_id=entity_id, process_id=process_id, ) recall = Recall(cfg) def _call(): data = recall._cloud_recall(query="What do I like?") facts, _messages = recall._parse_cloud_recall_response(data) return facts result = benchmark(_call) assert isinstance(result, list) def _make_invoke_with_stubbed_history( cfg: Config, *, n_history_pairs: int ) -> BaseInvoke: history: list[dict[str, str]] = [] for i in range(n_history_pairs): history.append({"role": "user", "content": f"I like item_{i}."}) history.append({"role": "assistant", "content": "Ok."}) invoke = BaseInvoke(cfg, lambda **_kwargs: None) invoke._cloud_conversation_messages = history return invoke @pytest.mark.benchmark @pytest.mark.parametrize("n_history_pairs", [20, 100], ids=["n20", "n100"]) def test_benchmark_cloud_injection_only_history( benchmark, n_history_pairs: int ) -> None: """ONLY Python-side history injection (no network).""" entity_id = os.environ.get("BENCHMARK_CLOUD_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_CLOUD_PROCESS_ID", "bench-cloud-process") cfg = _make_cloud_cfg(entity_id=entity_id, process_id=process_id) query = "What do I like?" invoke = _make_invoke_with_stubbed_history(cfg, n_history_pairs=n_history_pairs) def _call(): kwargs = {"messages": [{"role": "user", "content": query}]} return invoke.inject_conversation_messages(kwargs) result = benchmark(_call) assert isinstance(result, dict) @pytest.mark.benchmark @pytest.mark.parametrize("n_recalled_facts", [5, 20], ids=["n5", "n20"]) def test_benchmark_cloud_injection_only_recalled_facts( benchmark, n_recalled_facts: int, mocker ) -> None: """ONLY Python-side recalled-facts injection (no network).""" entity_id = os.environ.get("BENCHMARK_CLOUD_ENTITY_ID", "bench-cloud-entity") process_id = os.environ.get("BENCHMARK_CLOUD_PROCESS_ID", "bench-cloud-process") cfg = _make_cloud_cfg(entity_id=entity_id, process_id=process_id) fake_facts: list[dict[str, object]] = [ {"content": f"User likes item_{i}", "rank_score": 1.0, "date_created": None} for i in range(n_recalled_facts) ] mocker.patch("memori.memory.recall.Recall.search_facts", return_value=fake_facts) invoke = BaseInvoke(cfg, lambda **_kwargs: None) query = "What do I like?" def _call(): kwargs = {"messages": [{"role": "user", "content": query}]} return invoke.inject_recalled_facts(kwargs) result = benchmark(_call) assert isinstance(result, dict) ================================================ FILE: benchmarks/perf/test_recall_benchmarks.py ================================================ """Performance benchmarks for Memori recall functionality.""" import datetime import os from time import perf_counter from typing import cast import pytest from benchmarks.perf._results import append_csv_row, results_dir from benchmarks.perf.memory_utils import measure_peak_rss_bytes from memori._config import Config from memori.embeddings import embed_texts from memori.memory.recall import Recall from memori.search import find_similar_embeddings from memori.search._lexical import lexical_scores_for_ids # noqa: PLC2701 from memori.search._types import FactId def _default_benchmark_csv_path() -> str: return str(results_dir() / "recall_benchmarks.csv") def _write_benchmark_row(*, benchmark, row: dict[str, object]) -> None: csv_path = ( os.environ.get("BENCHMARK_RESULTS_CSV_PATH") or _default_benchmark_csv_path() ) stats = getattr(benchmark, "stats", None) row_out: dict[str, object] = dict(row) row_out["timestamp_utc"] = datetime.datetime.now(datetime.timezone.utc).isoformat() for key in ( "mean", "stddev", "median", "min", "max", "rounds", "iterations", "ops", ): value = getattr(stats, key, None) if stats is not None else None if value is not None: row_out[key] = value header = [ "timestamp_utc", "test", "db", "fact_count", "query_size", "retrieval_limit", "one_shot_seconds", "peak_rss_bytes", "mean", "stddev", "median", "min", "max", "rounds", "iterations", "ops", ] append_csv_row(csv_path, header=header, row=row_out) @pytest.mark.benchmark class TestQueryEmbeddingBenchmarks: """Benchmarks for query embedding generation.""" def test_benchmark_query_embedding_short(self, benchmark, sample_queries): """Benchmark embedding generation for short queries.""" query = sample_queries["short"][0] cfg = Config() def _embed(): return embed_texts( query, model=cfg.embeddings.model, ) start = perf_counter() result = benchmark(_embed) one_shot_seconds = perf_counter() - start assert len(result) > 0 assert len(result[0]) > 0 _write_benchmark_row( benchmark=benchmark, row={ "test": "query_embedding_short", "db": "", "fact_count": "", "query_size": "short", "retrieval_limit": "", "one_shot_seconds": one_shot_seconds, "peak_rss_bytes": benchmark.extra_info.get("peak_rss_bytes", ""), }, ) def test_benchmark_query_embedding_medium(self, benchmark, sample_queries): """Benchmark embedding generation for medium-length queries.""" query = sample_queries["medium"][0] cfg = Config() def _embed(): return embed_texts( query, model=cfg.embeddings.model, ) start = perf_counter() result = benchmark(_embed) one_shot_seconds = perf_counter() - start assert len(result) > 0 assert len(result[0]) > 0 _write_benchmark_row( benchmark=benchmark, row={ "test": "query_embedding_medium", "db": "", "fact_count": "", "query_size": "medium", "retrieval_limit": "", "one_shot_seconds": one_shot_seconds, "peak_rss_bytes": benchmark.extra_info.get("peak_rss_bytes", ""), }, ) def test_benchmark_query_embedding_long(self, benchmark, sample_queries): """Benchmark embedding generation for long queries.""" query = sample_queries["long"][0] cfg = Config() def _embed(): return embed_texts( query, model=cfg.embeddings.model, ) start = perf_counter() result = benchmark(_embed) one_shot_seconds = perf_counter() - start assert len(result) > 0 assert len(result[0]) > 0 _write_benchmark_row( benchmark=benchmark, row={ "test": "query_embedding_long", "db": "", "fact_count": "", "query_size": "long", "retrieval_limit": "", "one_shot_seconds": one_shot_seconds, "peak_rss_bytes": benchmark.extra_info.get("peak_rss_bytes", ""), }, ) def test_benchmark_query_embedding_batch(self, benchmark, sample_queries): """Benchmark embedding generation for multiple queries at once.""" queries = sample_queries["short"][:5] cfg = Config() def _embed(): return embed_texts( queries, model=cfg.embeddings.model, ) start = perf_counter() result = benchmark(_embed) one_shot_seconds = perf_counter() - start assert len(result) == len(queries) assert all(len(emb) > 0 for emb in result) _write_benchmark_row( benchmark=benchmark, row={ "test": "query_embedding_batch", "db": "", "fact_count": "", "query_size": "batch", "retrieval_limit": "", "one_shot_seconds": one_shot_seconds, "peak_rss_bytes": benchmark.extra_info.get("peak_rss_bytes", ""), }, ) @pytest.mark.benchmark class TestDatabaseEmbeddingRetrievalBenchmarks: """Benchmarks for database embedding retrieval.""" def test_benchmark_db_embedding_retrieval( self, benchmark, memori_instance, entity_with_n_facts ): """Benchmark retrieving embeddings from database for different fact counts.""" entity_db_id = entity_with_n_facts["entity_db_id"] fact_count = entity_with_n_facts["fact_count"] entity_fact_driver = memori_instance.config.storage.driver.entity_fact def _retrieve(): return entity_fact_driver.get_embeddings(entity_db_id, limit=fact_count) _, peak_rss = measure_peak_rss_bytes(_retrieve) if peak_rss is not None: benchmark.extra_info["peak_rss_bytes"] = peak_rss result = benchmark(_retrieve) assert len(result) == fact_count assert all("id" in row and "content_embedding" in row for row in result) _write_benchmark_row( benchmark=benchmark, row={ "test": "db_embedding_retrieval", "db": entity_with_n_facts["db_type"], "fact_count": fact_count, "query_size": "", "retrieval_limit": "", "one_shot_seconds": "", "peak_rss_bytes": benchmark.extra_info.get("peak_rss_bytes", ""), }, ) @pytest.mark.benchmark class TestDatabaseFactContentRetrievalBenchmarks: """Benchmarks for fetching fact content by ids (final recall DB step). This benchmarks the final step after semantic search has already identified the top-k most similar embeddings. We only retrieve content for those top results (typically 5-10 facts), not all facts in the database. """ @pytest.mark.parametrize("retrieval_limit", [5, 10], ids=["limit5", "limit10"]) def test_benchmark_db_fact_content_retrieval( self, benchmark, memori_instance, entity_with_n_facts, retrieval_limit ): """Benchmark retrieving content for top-k facts after semantic search. Args: retrieval_limit: Number of fact IDs to retrieve content for (after semantic search has already filtered to top results). This should be small (5-10). """ entity_db_id = entity_with_n_facts["entity_db_id"] entity_fact_driver = memori_instance.config.storage.driver.entity_fact # Simulate semantic search returning top-k IDs (outside benchmark timing) # In real flow: get_embeddings(embeddings_limit=1000) -> FAISS search -> top-k IDs seed_rows = entity_fact_driver.get_embeddings( entity_db_id, limit=retrieval_limit ) fact_ids = [row["id"] for row in seed_rows] def _retrieve(): return entity_fact_driver.get_facts_by_ids(fact_ids) _, peak_rss = measure_peak_rss_bytes(_retrieve) if peak_rss is not None: benchmark.extra_info["peak_rss_bytes"] = peak_rss result = benchmark(_retrieve) assert len(result) == len(fact_ids) assert all("id" in row and "content" in row for row in result) _write_benchmark_row( benchmark=benchmark, row={ "test": "db_fact_content_retrieval", "db": entity_with_n_facts["db_type"], "fact_count": entity_with_n_facts["fact_count"], "query_size": "", "retrieval_limit": retrieval_limit, "one_shot_seconds": "", "peak_rss_bytes": benchmark.extra_info.get("peak_rss_bytes", ""), }, ) @pytest.mark.benchmark class TestSemanticSearchBenchmarks: """Benchmarks for semantic search (FAISS similarity search).""" def test_benchmark_semantic_search( self, benchmark, memori_instance, entity_with_n_facts, sample_queries ): """Benchmark FAISS similarity search for different embedding counts.""" entity_db_id = entity_with_n_facts["entity_db_id"] fact_count = entity_with_n_facts["fact_count"] entity_fact_driver = memori_instance.config.storage.driver.entity_fact db_results = entity_fact_driver.get_embeddings(entity_db_id, limit=fact_count) embeddings = [(row["id"], row["content_embedding"]) for row in db_results] query = sample_queries["short"][0] query_embedding = embed_texts( query, model=memori_instance.config.embeddings.model, )[0] def _search(): return find_similar_embeddings(embeddings, query_embedding, limit=5) _, peak_rss = measure_peak_rss_bytes(_search) if peak_rss is not None: benchmark.extra_info["peak_rss_bytes"] = peak_rss result = benchmark(_search) assert len(result) > 0 assert all(isinstance(item, tuple) and len(item) == 2 for item in result) assert all( isinstance(item[0], int) and isinstance(item[1], float) for item in result ) _write_benchmark_row( benchmark=benchmark, row={ "test": "semantic_search_faiss", "db": entity_with_n_facts["db_type"], "fact_count": fact_count, "query_size": "short", "retrieval_limit": "", "one_shot_seconds": "", "peak_rss_bytes": benchmark.extra_info.get("peak_rss_bytes", ""), }, ) @pytest.mark.benchmark class TestLexicalBenchmarks: """Benchmarks for BM25 scoring.""" @pytest.mark.parametrize("fact_count", [200, 1000], ids=["facts200", "facts1000"]) def test_benchmark_bm25_scoring(self, benchmark, fact_count): ids = list(range(1, fact_count + 1)) content_map = {i: f"fact {i} user likes blue pizza coffee {i % 7}" for i in ids} query_text = "blue pizza" def _score(): return lexical_scores_for_ids( query_text=query_text, ids=cast(list[FactId], ids), content_map=cast(dict[FactId, str], content_map), ) _, peak_rss = measure_peak_rss_bytes(_score) if peak_rss is not None: benchmark.extra_info["peak_rss_bytes"] = peak_rss result = benchmark(_score) assert isinstance(result, dict) assert len(result) == fact_count _write_benchmark_row( benchmark=benchmark, row={ "test": "bm25_scoring", "db": "", "fact_count": fact_count, "query_size": "short", "retrieval_limit": "", "one_shot_seconds": "", "peak_rss_bytes": benchmark.extra_info.get("peak_rss_bytes", ""), }, ) @pytest.mark.benchmark class TestEndToEndRecallBenchmarks: """Benchmarks for end-to-end recall (embed query + DB + FAISS + content fetch).""" @pytest.mark.parametrize( "query_size", ["short", "medium", "long"], ids=["short_query", "medium_query", "long_query"], ) def test_benchmark_end_to_end_recall( self, benchmark, memori_instance, entity_with_n_facts, sample_queries, query_size, ): entity_db_id = entity_with_n_facts["entity_db_id"] query = sample_queries[query_size][0] recall = Recall(memori_instance.config) def _recall(): return recall.search_facts(query=query, limit=5, entity_id=entity_db_id) _, peak_rss = measure_peak_rss_bytes(_recall) if peak_rss is not None: benchmark.extra_info["peak_rss_bytes"] = peak_rss start = perf_counter() result = benchmark(_recall) one_shot_seconds = perf_counter() - start assert isinstance(result, list) assert len(result) <= 5 _write_benchmark_row( benchmark=benchmark, row={ "test": "end_to_end_recall", "db": entity_with_n_facts["db_type"], "fact_count": entity_with_n_facts["fact_count"], "query_size": query_size, "retrieval_limit": "", "one_shot_seconds": one_shot_seconds, "peak_rss_bytes": benchmark.extra_info.get("peak_rss_bytes", ""), }, ) ================================================ FILE: benchmarks/scripts/fetch_locomo.py ================================================ from __future__ import annotations import argparse import hashlib import shutil import sys from pathlib import Path def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser( description="Fetch/prepare LoCoMo dataset locally (does not commit data to git)." ) parser.add_argument( "--source", default="", help="Path to an existing LoCoMo JSON file to copy into the cache.", ) parser.add_argument( "--dest", default="", help="Destination file path (defaults to ~/.cache/memori/benchmarks/locomo/locomo.json).", ) parser.add_argument( "--print-dest", action="store_true", help="Print resolved destination path and exit.", ) args = parser.parse_args(argv) dest = _default_dest() if not args.dest else Path(args.dest).expanduser() dest = dest.resolve() if args.print_dest: print(str(dest)) return 0 if not args.source: print( "No --source provided.\n\n" "Download LoCoMo from Snap Research and pass the JSON path here.\n" "Upstream repo: https://github.com/snap-research/locomo\n", file=sys.stderr, ) return 2 src = Path(args.source).expanduser().resolve() if not src.exists(): print(f"Source file not found: {src}", file=sys.stderr) return 2 dest.parent.mkdir(parents=True, exist_ok=True) shutil.copyfile(src, dest) digest = _sha256(dest) print(f"Wrote: {dest}") print(f"sha256: {digest}") return 0 def _default_dest() -> Path: return Path("~/.cache/memori/benchmarks/locomo/locomo.json").expanduser() def _sha256(path: Path) -> str: h = hashlib.sha256() with path.open("rb") as f: for chunk in iter(lambda: f.read(1024 * 1024), b""): h.update(chunk) return h.hexdigest() if __name__ == "__main__": raise SystemExit(main()) ================================================ FILE: conftest.py ================================================ import pytest from memori._config import Config from memori.storage import Manager as StorageManager @pytest.fixture def mock_mysql_session(mocker): session = mocker.MagicMock() session.get_bind.return_value.dialect.name = "mysql" type(session).__module__ = "sqlalchemy.orm.session" mock_result = mocker.MagicMock() mock_result.mappings.return_value.fetchone.return_value = {"1": 1} mock_result.mappings.return_value.fetchall.return_value = [] session.connection.return_value.exec_driver_sql.return_value = mock_result return session @pytest.fixture def mock_postgres_session(mocker): session = mocker.MagicMock() session.get_bind.return_value.dialect.name = "postgresql" type(session).__module__ = "sqlalchemy.orm.session" mock_result = mocker.MagicMock() mock_result.mappings.return_value.fetchone.return_value = {"one": 1} mock_result.mappings.return_value.fetchall.return_value = [] session.connection.return_value.exec_driver_sql.return_value = mock_result return session @pytest.fixture def session(mock_mysql_session): return mock_mysql_session @pytest.fixture def postgres_session(mock_postgres_session): return mock_postgres_session @pytest.fixture def config(mocker, session): config = Config() config.storage = StorageManager(config) config.storage.adapter = mocker.MagicMock() config.storage.driver = mocker.MagicMock() return config ================================================ FILE: docker-compose.yml ================================================ services: dev: build: context: . dockerfile: Dockerfile volumes: - .:/app - /app/.venv - /app/__pycache__ - ./google-credentials.json:/app/google-credentials.json:ro working_dir: /app stdin_open: true tty: true env_file: - .env environment: - PYTHONUNBUFFERED=1 - DATABASE_URL=postgresql://memori:memori@postgres:5432/memori_test - MONGODB_URL=mongodb://memori:memori@mongodb:27017/memori_test?authSource=admin - MYSQL_DATABASE_URL=mysql+pymysql://memori:memori@mysql:3306/memori_test - OCEANBASE_DATABASE_URL=mysql+oceanbase://root:@oceanbase:2881/memori_test?charset=utf8mb4 - ORACLE_DATABASE_URL=oracle+oracledb://system:memori@oracle:1521/?service_name=FREEPDB1 - GOOGLE_APPLICATION_CREDENTIALS=/app/google-credentials.json command: /bin/bash depends_on: postgres: condition: service_healthy mongodb: condition: service_healthy mysql: condition: service_healthy oceanbase: condition: service_started oracle: condition: service_healthy networks: - memori-network postgres: image: postgres:16 environment: POSTGRES_USER: memori POSTGRES_PASSWORD: memori POSTGRES_DB: memori_test ports: - "5432:5432" volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: ["CMD-SHELL", "pg_isready -U memori"] interval: 5s timeout: 5s retries: 5 networks: - memori-network mongodb: image: mongo:7.0 environment: MONGO_INITDB_ROOT_USERNAME: memori MONGO_INITDB_ROOT_PASSWORD: memori MONGO_INITDB_DATABASE: memori_test ports: - "27017:27017" volumes: - mongodb_data:/data/db healthcheck: test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"] interval: 5s timeout: 5s retries: 5 networks: - memori-network mongo-express: image: mongo-express:1.0.0 restart: unless-stopped ports: - "8081:8081" environment: ME_CONFIG_MONGODB_ADMINUSERNAME: memori ME_CONFIG_MONGODB_ADMINPASSWORD: memori ME_CONFIG_MONGODB_URL: mongodb://memori:memori@mongodb:27017/ ME_CONFIG_BASICAUTH_USERNAME: admin ME_CONFIG_BASICAUTH_PASSWORD: pass depends_on: - mongodb networks: - memori-network mysql: image: mysql:8 environment: MYSQL_ROOT_PASSWORD: memori MYSQL_DATABASE: memori_test MYSQL_USER: memori MYSQL_PASSWORD: memori ports: - "3307:3306" volumes: - mysql_data:/var/lib/mysql healthcheck: test: ["CMD", "mysqladmin", "ping", "-h", "localhost", "-u", "memori", "-pmemori"] interval: 5s timeout: 5s retries: 5 networks: - memori-network oceanbase: image: quay.io/oceanbase/seekdb:latest ports: - "2882:2881" networks: - memori-network oracle: image: container-registry.oracle.com/database/free:latest-lite environment: ORACLE_PWD: memori ORACLE_CHARACTERSET: AL32UTF8 ports: - "1521:1521" volumes: - oracle_data:/opt/oracle/oradata shm_size: 2gb healthcheck: test: ["CMD-SHELL", "echo 'SELECT 1 FROM DUAL;' | sqlplus -s system/memori@FREEPDB1 || exit 1"] interval: 15s timeout: 10s retries: 20 start_period: 90s networks: - memori-network volumes: postgres_data: mongodb_data: mysql_data: oracle_data: networks: memori-network: driver: bridge ================================================ FILE: docs/memori-byodb/concepts/advanced-augmentation.mdx ================================================ --- title: Advanced Augmentation description: How Memori's Advanced Augmentation engine extracts structured facts, preferences, and knowledge from your AI conversations — all stored in your own database. --- # Advanced Augmentation Advanced Augmentation is the AI engine inside Memori that turns raw conversations into structured, searchable memories. It runs asynchronously in the background to minimize impact on your response path. All extracted data is stored directly in your database. ## What It Does When your application has a conversation through a Memori-wrapped LLM client, the augmentation engine: 1. Reads the full conversation (user messages and AI responses) 2. Identifies facts, preferences, skills, attributes, and events 3. Extracts semantic triples (subject-predicate-object relationships) 4. Generates vector embeddings for semantic search 5. Stores everything in your database No extra code required — just initialize Memori with your database connection and set attribution. ## How It Works The augmentation flow is fully asynchronous and designed to avoid blocking your main request path. 1. Your app makes an LLM call through the wrapped client 2. Memori returns the response immediately 3. In the background, the conversation is queued for processing 4. The augmentation engine extracts structured memories 5. Memories are stored in your database for future recall In short-lived scripts, call `mem.augmentation.wait()` to ensure processing completes before exit. ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") mem.config.storage.build() # This returns immediately — no augmentation delay response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "I love hiking in the mountains."} ] ) print(response.choices[0].message.content) # Only needed in short-lived scripts mem.augmentation.wait() ``` ## Extraction Types | Type | What it captures | Scope | | ---------------------- | ------------------------------------------------------- | ------------------------------------ | | **Facts** | Objective information with vector embeddings | Per entity — shared across processes | | **Preferences** | User choices, opinions, and tastes | Per entity | | **Skills & Knowledge** | Abilities and expertise levels | Per entity | | **Attributes** | Process-level information about what your agent handles | Per process | **Database tables:** | Table | Purpose | | ----------------------------- | -------------------------------------------- | | `memori_conversation` | Stores conversations | | `memori_conversation_message` | Individual messages within conversations | | `memori_session` | Groups related LLM interactions | | `memori_entity` | Entities (users, organizations, etc.) | | `memori_process` | Processes (agents, bots, workflows) | | `memori_entity_fact` | Extracted facts with vector embeddings | | `memori_process_attribute` | Process-level attributes and characteristics | ## Semantic Triples Advanced Augmentation uses named-entity recognition to extract semantic triples (subject, predicate, object). These form the building blocks of the [Knowledge Graph](/docs/memori-byodb/concepts/knowledge-graph). **Example** — from _"My favorite database is PostgreSQL and I use it with FastAPI"_: | Subject | Predicate | Object | | ------- | ----------------- | -------------------- | | user | favorite_database | PostgreSQL | | user | uses | FastAPI | | user | uses_with | PostgreSQL + FastAPI | Memori automatically deduplicates triples — if the same fact is mentioned multiple times, it increments the mention count and updates the timestamp. **Database tables:** `memori_subject`, `memori_predicate`, `memori_object`, `memori_knowledge_graph` ## Embeddings Vector embeddings are created using the **all-mpnet-base-v2** sentence transformer with **768 dimensions**. These embeddings power the semantic search used for context recall via cosine similarity. ## Context Recall When a query is sent to an LLM through a wrapped client, Memori automatically: 1. Intercepts the outbound LLM call 2. Uses semantic search to find entity facts matching the query 3. Passes vector embeddings to FAISS for similarity ranking 4. Injects the top-N most relevant facts into the system prompt 5. Forwards the enriched request to the LLM provider ## Schema ERD ![Memori Schema ERD](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/erd.png) ## When to Use It - Chatbots or AI assistants with returning users - Use cases that need to remember user preferences across sessions - You want personalized AI interactions - With multi-step workflows or agentic systems - For building relationships between entities For Memori to provide all Advanced Augmentation capabilities, attribution must be set before making LLM calls. Without attribution, Memori cannot create or recall memories. ================================================ FILE: docs/memori-byodb/concepts/architecture.mdx ================================================ --- title: Architecture description: Understand how Memori's open-source architecture works — from your app to your own database, with local storage, augmentation, and recall. --- # Architecture Memori is a modular memory layer for AI applications. You connect your LLM client, set attribution, point Memori at your database, and it handles everything else — storage, augmentation, knowledge graph construction, and recall. All data stays on your infrastructure. ## System Overview !["Memori BYODB System Overview"](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memori-byodb-architecture-detail.webp) ## Core Components **Memori Core** — The central coordinator between your application and your database. Manages attribution, coordinates storage and augmentation, provides LLM wrappers, and exposes the Recall API. **LLM Provider Wrappers** — Wraps your existing LLM client transparently. Intercepts calls, captures messages and responses, persists conversation data to your database. Supports sync, async, and streaming. **Attribution System** — Tags every memory with who created it and in what context. Tracks three dimensions: entity (the user), process (the agent), and session (the conversation thread). **Storage System** — Stores all data in your database with no external dependencies. Supports SQLAlchemy `sessionmaker`, DB-API 2.0 connections, Django ORM, and MongoDB. Works with SQLite, PostgreSQL, MySQL, MariaDB, Oracle, CockroachDB, and OceanBase, including providers like Neon, Supabase, and AWS RDS/Aurora. **Advanced Augmentation** — Turns raw conversations into structured memories. Extracts facts, preferences, and skills, generates vector embeddings locally, and builds a knowledge graph. Runs asynchronously with zero latency impact. ## Configuration Setting up Memori requires a database connection and attribution: ```python import sqlite3 from memori import Memori from openai import OpenAI def get_connection(): return sqlite3.connect("memori.db") client = OpenAI() mem = Memori(conn=get_connection).llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") mem.config.storage.build() ``` ## Data Flow 1. **Conversation Capture** — Every LLM call through the wrapped client is captured and stored in your database. Your app gets the response immediately. 2. **Attribution Tracking** — Attribution links every conversation to a specific entity and process so memories are properly scoped and indexed. 3. **Augmentation** — After a conversation completes, Memori processes it asynchronously — extracts facts, generates embeddings locally, and builds knowledge graph triples. 4. **Recall** — On the next LLM call, Memori surfaces the right memories at the right time: semantic search, intelligent ranking and decay, and seamless injection of the most relevant context into the system prompt so your AI stays contextually aware. ================================================ FILE: docs/memori-byodb/concepts/async-patterns.mdx ================================================ --- title: Async Patterns description: Best practices for using Memori with async/await — AsyncOpenAI, AsyncAnthropic, FastAPI, and thread safety. --- # Async Patterns Memori works with Python's async/await out of the box. Use `AsyncOpenAI` or `AsyncAnthropic` instead of their sync counterparts — everything else stays the same. ## When to Use Async | Scenario | Async? | Why | | ------------------------ | ------ | --------------------------- | | Web servers (FastAPI) | Yes | Concurrent request handling | | Chatbots with many users | Yes | Non-blocking I/O | | CLI scripts | No | Sync is simpler | | Jupyter notebooks | No | Event loop already running | ## Basic Async Setup ```python import os import asyncio from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import AsyncOpenAI engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) async def main(): client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id="user_123", process_id="async_agent") mem.config.storage.build() response = await client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "I prefer async Python."}] ) print(response.choices[0].message.content) mem.augmentation.wait() asyncio.run(main()) ``` Works identically with `AsyncAnthropic` — just swap the client. ## FastAPI Example ```python import os from fastapi import FastAPI from pydantic import BaseModel from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import AsyncOpenAI app = FastAPI() engine = create_engine("sqlite:///memori.db", connect_args={"check_same_thread": False}) SessionLocal = sessionmaker(bind=engine) Memori(conn=SessionLocal).config.storage.build() class ChatRequest(BaseModel): message: str @app.post("/chat/{user_id}") async def chat(user_id: str, req: ChatRequest): client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id=user_id, process_id="fastapi_async") response = await client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": req.message}] ) return {"response": response.choices[0].message.content} ``` ## Thread Safety | Pattern | Safe? | Why | | ------------------------------- | ----- | ------------------------- | | `conn=SessionLocal` (factory) | Yes | New session per operation | | `conn=lambda: existing_session` | No | Shares one session | For production async apps, use PostgreSQL with larger pools: ```python engine = create_engine( "postgresql+psycopg://user:pass@host/db", pool_pre_ping=True, pool_size=20, max_overflow=40, pool_recycle=300 ) ``` ================================================ FILE: docs/memori-byodb/concepts/cli-quickstart.mdx ================================================ --- title: CLI Quickstart description: Get started with the Memori command line interface for setup, diagnostics, and management. --- # CLI Quickstart The Memori CLI lets you manage your environment, check quotas, and perform setup tasks from the terminal. It is included with the `memori` package. ## View Available Commands ```bash python -m memori ``` ## Essential Setup Pre-download the embedding model that Memori uses for semantic search. Without this, the model downloads automatically on first use (slower). ```bash python -m memori setup ``` ## Command Reference | Command | Parameters | Description | | ---------------------------- | ---------- | ---------------------------------------------- | | `setup` | None | Prepare environment (download embedding model) | | `quota` | None | Display available augmentation quota | | `sign-up` | `` | Sign up for an API key | | `cockroachdb cluster start` | None | Start a new CockroachDB cluster | | `cockroachdb cluster claim` | None | Get cluster claim URL | | `cockroachdb cluster delete` | None | Delete cluster (destructive) | ```bash {{ title: 'Setup' }} # Cache the embedding model for faster runtime python -m memori setup ``` ```bash {{ title: 'Check Quota' }} # View augmentation usage and limits python -m memori quota ``` ```bash {{ title: 'Sign Up' }} # Get an API key for higher augmentation limits python -m memori sign-up your-email@example.com ``` ```bash {{ title: 'CockroachDB' }} # Start a new serverless database cluster python -m memori cockroachdb cluster start # Get the cluster claim URL python -m memori cockroachdb cluster claim # Delete the cluster (destructive!) python -m memori cockroachdb cluster delete ``` The `cockroachdb cluster delete` command permanently removes your cluster including all data. This cannot be undone. ================================================ FILE: docs/memori-byodb/concepts/how-memory-works.mdx ================================================ --- title: How Memori Works description: Understand the core concepts behind Memori — entities, processes, sessions, memory types, attribution, and how recall brings it all together. --- # How Memori Works Memori gives your AI application long-term memory. Instead of forgetting everything after each conversation, your AI can remember facts, preferences, and context across sessions and across different applications — all stored in your own database. ## Attribution Every memory in Memori is tagged with three pieces of information: **who**, **what**, and **when**. - **Entity (`entity_id`)** — The person, place, or thing generating memories. Typically a user ID (e.g., `"user_alice"`, `"company_acme"`). - **Process (`process_id`)** — The agent, program, or workflow creating memories (e.g., `"support_bot"`, `"code_review_agent"`). - **Session (`session_id`)** — Groups related LLM interactions into a conversation thread. Auto-generated as a UUID by default. The combination of `entity_id` + `process_id` + `session_id` creates a unique memory scope — different users have isolated memories, the same user can have different context in different applications, and each conversation is tracked separately. ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) # Set attribution before any LLM calls mem.attribution( entity_id="user_alice", process_id="support_bot" ) # session_id is auto-generated response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "I prefer dark mode."} ] ) ``` ## Memory Types When you have a conversation through a Memori-wrapped LLM client, Advanced Augmentation extracts structured memories in the background: | Type | What it captures | Example | | --------------- | -------------------------- | ----------------------------------------------- | | **Facts** | Objective information | "User uses PostgreSQL for production databases" | | **Preferences** | Choices, opinions, tastes | "Prefers concise answers" | | **Skills** | Abilities and expertise | "Experienced with React (5 years)" | | **Rules** | Constraints and principles | "Follows test-driven development" | | **Events** | Milestones and occurrences | "Product launched recently" | ## How Recall Works Recall brings stored memories back into your AI conversations. There are two modes. ### Automatic Recall (Default) On every LLM call, Memori automatically: 1. Intercepts the outbound request 2. Uses semantic search to find relevant facts for the current entity 3. Injects the most relevant memories into the system prompt 4. Forwards the enriched request to the LLM No extra code required — it happens transparently. ### Manual Recall Use `mem.recall()` when you want to retrieve memories explicitly — for custom prompts, displaying memories in a UI, or debugging. ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.attribution(entity_id="user_alice", process_id="support_bot") facts = mem.recall("coding preferences", limit=5) for fact in facts: print(f"Fact: {fact.content}") print(f"Score: {fact.similarity:.4f}") ``` Each returned fact includes `id`, `content`, `similarity` (0–1 relevance score), `rank_score`, and `date_created`. ### Recall Configuration Memori uses the **all-mpnet-base-v2** sentence transformer with cosine similarity for semantic search. You can tune recall behavior with these configuration options: | Option | Default | Description | | --------------------------------------- | ------- | -------------------------------------------------- | | `mem.config.recall_relevance_threshold` | `0.1` | Minimum similarity score for a fact to be included | | `mem.config.recall_embeddings_limit` | `1000` | Maximum number of embeddings to compare against | ```python # Example: tune recall for broader or narrower results mem.config.recall_relevance_threshold = 0.05 # Lower = more results mem.config.recall_embeddings_limit = 500 # Reduce for lower memory usage ``` ## Memory Lifecycle !["Memori Lifecycle"](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memori-lifecycle.webp) 1. **Conversation** — Your user talks to your AI through the wrapped LLM client 2. **Capture** — Memori intercepts and stores the raw conversation in your database 3. **Augmentation** — Advanced Augmentation processes the conversation asynchronously 4. **Extraction** — Facts, preferences, skills, rules, and events are identified 5. **Storage** — Extracted memories are stored in your database with vector embeddings 6. **Recall** — On the next LLM call, relevant memories are retrieved and injected into context ================================================ FILE: docs/memori-byodb/concepts/knowledge-graph.mdx ================================================ --- title: Knowledge Graph description: How Memori automatically builds a knowledge graph from your AI conversations using semantic triples, stored in your own database where you can query it directly. --- # Knowledge Graph Memori automatically builds a knowledge graph from your AI conversations. Every time Advanced Augmentation processes a conversation, it extracts structured relationships — semantic triples — and connects them into a graph. Because you own the database, you can query the knowledge graph directly using SQL. ## How It Works 1. **Conversation captured** — Your user talks to your AI through the Memori-wrapped LLM client 2. **Augmentation processes** — Memori analyzes the conversation in the background 3. **NER extraction** — Named-entity recognition identifies key entities and relationships 4. **Triple creation** — Relationships are expressed as subject-predicate-object triples 5. **Graph storage** — Triples are stored and deduplicated in your database 6. **Recall ready** — The graph is available for semantic search on the next LLM call ## Semantic Triples Every fact in the knowledge graph is a semantic triple — a three-part statement: **[Subject]** **[Predicate]** **[Object]**. - "Alice" "prefers" "dark mode" - "PostgreSQL" "is" "a relational database" - "The project" "uses" "FastAPI" ### Example Extraction From _"My favorite database is PostgreSQL and I use it with FastAPI for our REST APIs. I've been using Python for about 8 years"_: | Subject | Predicate | Object | | ------- | ----------------- | -------------------- | | user | favorite_database | PostgreSQL | | user | uses | FastAPI | | user | uses_for | REST APIs | | user | uses_with | PostgreSQL + FastAPI | | user | experience_years | Python (8 years) | Memori automatically deduplicates triples — frequently mentioned facts get a higher mention count and updated timestamp. ## Database Tables | Table | Purpose | | ------------------------ | ---------------------------------------------------- | | `memori_subject` | Stores unique subjects | | `memori_predicate` | Stores unique predicates | | `memori_object` | Stores unique objects | | `memori_knowledge_graph` | Links subjects, predicates, and objects into triples | | `memori_entity_fact` | Stores facts with vector embeddings for recall | ## Querying ### Via Recall API ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.attribution(entity_id="user_alice", process_id="my_agent") facts = mem.recall("database preferences", limit=5) for fact in facts: print(f"Fact: {fact.content}") print(f"Similarity: {fact.similarity:.4f}") ``` ### Via Direct SQL Since the knowledge graph lives in your database, you can query it directly for debugging, dashboards, or exploration. ```sql {{ title: 'View All Triples' }} SELECT s.name AS subject, p.name AS predicate, o.name AS object FROM memori_knowledge_graph kg JOIN memori_subject s ON kg.subject_id = s.id JOIN memori_predicate p ON kg.predicate_id = p.id JOIN memori_object o ON kg.object_id = o.id; ``` ```sql {{ title: 'Triples for an Entity' }} SELECT s.name AS subject, p.name AS predicate, o.name AS object, kg.mention_count, kg.last_mentioned_at FROM memori_knowledge_graph kg JOIN memori_subject s ON kg.subject_id = s.id JOIN memori_predicate p ON kg.predicate_id = p.id JOIN memori_object o ON kg.object_id = o.id WHERE kg.entity_id = 'user_alice' ORDER BY kg.mention_count DESC; ``` ```sql {{ title: 'View Facts' }} SELECT content, created_at FROM memori_entity_fact WHERE entity_id = 'user_alice' ORDER BY created_at DESC; ``` ## Scope | Aspect | Scope | | -------------- | --------------------------------------------------------------- | | **Triples** | Per entity — shared across all processes | | **Visibility** | All processes for an entity can see and use the graph | | **Growth** | Conversations from any process contribute to the entity's graph | If Alice tells your support bot about PostgreSQL, your code assistant also knows she uses PostgreSQL. ================================================ FILE: docs/memori-byodb/concepts/multi-user-support.mdx ================================================ --- title: Multi-User Support description: How Memori isolates memories across users, applications, and sessions so each user gets a personalized experience — all in your own database. --- # Multi-User Support Memori provides built-in multi-user and multi-process isolation through its attribution system. Each combination of entity, process, and session creates an isolated memory space — User A never sees User B's memories, and your support bot has different context than your sales bot. ## Isolation Model !["Memori - Multi User Support"](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memori-multi-user-support.webp) ## What's Shared vs Isolated | Data | Scope | | ------------------- | ---------------------------------------- | | **Facts** | Per entity — shared across all processes | | **Preferences** | Per entity | | **Skills** | Per entity | | **Attributes** | Per process | | **Conversations** | Per entity + process + session | | **Sessions** | Per entity + process | | **Knowledge Graph** | Per entity | ## Examples ```python {{ title: 'Per-User Isolation' }} from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) # User A's conversations mem.attribution(entity_id="user_alice", process_id="support_bot") response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "I prefer dark mode"}] ) # User B's conversations — completely isolated mem.attribution(entity_id="user_bob", process_id="support_bot") response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What are my preferences?"}] ) # Bob will NOT see Alice's preferences ``` ```python {{ title: 'Multi-Process' }} from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) # Same user, different processes mem.attribution(entity_id="user_alice", process_id="support_bot") response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "I use PostgreSQL for my databases"} ] ) # Switch to a different process for the same user mem.attribution(entity_id="user_alice", process_id="sales_bot") # The sales bot can recall Alice's facts (like "uses PostgreSQL") # because facts are shared across processes for the same entity. response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "What databases do I use?"} ] ) ``` ```python {{ title: 'Session Management' }} from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id="user_alice", process_id="support_bot") # Get the current session ID current_session = mem.config.session_id # Start a new conversation group mem.new_session() # Or restore a previous session mem.set_session(current_session) ``` ## Common Patterns ### Web Application Set the entity ID from the authenticated user's session. Works with Flask, FastAPI, Django, or any framework. ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) def handle_chat(user_id: str, message: str): client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id=user_id, process_id="web_assistant") response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": message}] ) return response.choices[0].message.content ``` ### Multi-Agent System Give each agent a unique process ID. Facts are shared across agents for the same user, but each maintains its own conversation history. ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) def create_agent(user_id: str, agent_name: str): client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id=user_id, process_id=agent_name) return client # Three agents, one user, shared facts support = create_agent("user_alice", "support_agent") sales = create_agent("user_alice", "sales_agent") onboard = create_agent("user_alice", "onboarding_agent") ``` ================================================ FILE: docs/memori-byodb/contribute/development-setup.mdx ================================================ --- title: Development Setup description: Set up your local development environment for contributing to the Memori open-source project. --- # Development Setup This guide walks you through setting up a local development environment for working on the Memori codebase. Follow these steps to clone the repository, install dependencies, and run the test suite. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Prerequisites Before you begin, make sure you have the following installed on your system: - **Python 3.10 or higher** — Memori requires Python 3.10+. Check your version with `python --version`. - **git** — For cloning the repository and managing branches. - **pip** — Python's package manager (usually included with Python). ## Clone the Repository Start by forking the repository on GitHub, then clone your fork locally: ```bash git clone https://github.com/YOUR_USERNAME/Memori.git cd Memori ``` If you plan to submit a pull request, make sure to keep your fork synced with the upstream repository: ```bash git remote add upstream https://github.com/MemoriLabs/Memori.git git fetch upstream ``` ## Install Dependencies It is recommended to use a virtual environment to avoid conflicts with other Python projects: ```bash {{ title: 'venv (Built-in)' }} python -m venv .venv source .venv/bin/activate # macOS/Linux # .venv\Scripts\activate # Windows ``` ```bash {{ title: 'conda' }} conda create -n memori python=3.12 conda activate memori ``` Once your virtual environment is active, install Memori with development dependencies: ```bash pip install -e ".[dev]" ``` This installs Memori in editable mode (`-e`) so that changes you make to the source code are immediately reflected without reinstalling. The `[dev]` extra includes testing and linting tools. If the project uses a `requirements-dev.txt` file instead, install with: ```bash pip install -e . pip install -r requirements-dev.txt ``` ## Running Tests Memori uses pytest for its test suite. Run all tests from the project root: ```bash pytest ``` To run tests with verbose output: ```bash pytest -v ``` To run a specific test file or test function: ```bash # Run all tests in a specific file pytest tests/test_openai.py # Run a specific test function pytest tests/test_openai.py::test_sync_completion ``` To run tests with coverage reporting: ```bash pytest --cov=memori --cov-report=term-missing ``` ## Local Development Workflow Here is a typical workflow for making changes to Memori: 1. **Create a branch** for your feature or fix: ```bash git checkout -b feat/my-new-feature ``` 2. **Make your changes** — Edit the source code in the `memori/` directory. 3. **Write tests** — Add or update tests in the `tests/` directory to cover your changes. 4. **Run the tests** to make sure nothing is broken: ```bash pytest ``` 5. **Check code style** — The project uses Ruff for linting and formatting: ```bash # Lint ruff check . # Format ruff format --check . ``` 6. **Commit your changes** with a descriptive message: ```bash git add . git commit -m "feat: add support for Redis storage backend" ``` 7. **Push to your fork** and open a pull request: ```bash git push origin feat/my-new-feature ``` ## Project Structure Here is a high-level overview of the Memori repository structure: | Directory | Purpose | | ----------- | ------------------------------- | | `memori/` | Core library source code | | `tests/` | Test suite (pytest) | | `docs/` | Documentation source files | | `examples/` | Example scripts and usage demos | The core integration logic for each LLM provider typically lives in separate modules within the `memori/` directory. When adding support for a new provider, look at existing integrations (such as OpenAI or Anthropic) as templates. ================================================ FILE: docs/memori-byodb/contribute/overview.mdx ================================================ --- title: Contributing to Memori description: How to contribute to the Memori open-source project — code, documentation, issues, and discussions. --- # Contributing to Memori Thank you for your interest in contributing to Memori. Whether you are fixing a bug, adding a feature, improving documentation, or just asking a question, every contribution helps make Memori better for everyone. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). The Memori source code lives on GitHub at [github.com/MemoriLabs/Memori](https://github.com/MemoriLabs/Memori). ## Ways to Contribute There are many ways to contribute to Memori, and not all of them involve writing code: ### Report Bugs Found a bug? Open an issue on [GitHub](https://github.com/MemoriLabs/Memori/issues) with: - A clear title describing the problem - Steps to reproduce the issue - What you expected to happen versus what actually happened - Your Python version, OS, and Memori version (`pip show memori`) ### Suggest Features Have an idea for a new feature or improvement? Open a [GitHub Discussion](https://github.com/MemoriLabs/Memori/discussions) or an issue tagged with `enhancement`. Describe the use case and why it would be valuable. ### Improve Documentation Documentation improvements are always welcome. This includes: - Fixing typos or unclear explanations - Adding new examples or use cases - Improving the guides for specific LLM providers or databases - Translating documentation ### Contribute Code Ready to write some code? Here is the process: 1. **Fork the repository** — Create your own fork of [MemoriLabs/Memori](https://github.com/MemoriLabs/Memori) on GitHub 2. **Create a branch** — Branch off `main` with a descriptive name (for example, `fix/streaming-bug` or `feat/redis-support`) 3. **Make your changes** — Write your code, add tests, and update documentation if needed 4. **Run the tests** — Make sure all existing tests pass and your new tests cover the changes 5. **Submit a pull request** — Open a PR back to the `main` branch with a clear description of what you changed and why See the [Development Setup](/docs/memori-byodb/contribute/development-setup) guide for instructions on setting up your local environment. ## Pull Request Guidelines When submitting a pull request: - **Keep PRs focused** — One feature or fix per PR. This makes review faster and easier. - **Write descriptive commit messages** — Explain what changed and why, not just what files were modified. - **Include tests** — New features should include tests. Bug fixes should include a test that reproduces the bug. - **Update documentation** — If your change affects the public API or user-facing behavior, update the relevant docs. - **Follow existing patterns** — Look at how existing code is structured and follow the same conventions. ## Code of Conduct We are committed to providing a welcoming and inclusive experience for everyone. All contributors are expected to be respectful and constructive in their interactions. Harassment, discrimination, and toxic behavior are not tolerated. Please review the project's Code of Conduct on the [GitHub repository](https://github.com/MemoriLabs/Memori) before contributing. ## Community Join the Memori community: - **GitHub Issues** — For bug reports and feature requests: [github.com/MemoriLabs/Memori/issues](https://github.com/MemoriLabs/Memori/issues) - **GitHub Discussions** — For questions, ideas, and general conversation: [github.com/MemoriLabs/Memori/discussions](https://github.com/MemoriLabs/Memori/discussions) ================================================ FILE: docs/memori-byodb/dashboard/api-keys.mdx ================================================ --- title: API Keys description: Create and manage Memori API keys from the dashboard. --- # API Keys Use the **API keys** page in [app.memorilabs.ai](https://app.memorilabs.ai) to create and manage keys for your organization. ## Overview The table includes: - **Name** - **Key** (masked) - **Created at** - **Last used** If no keys are connected yet, the table shows **No connected APIs.** ## Create a New API Key 1. Open **API keys** in the dashboard sidebar. 2. Click **Create API key**. 3. In **New API key**, enter a value in **Name**. 4. Click **Create key**. ![API keys page with empty table state and New API key modal](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/new-api-key-modal.webp) ## Copy the Key (Shown Once) After creating a key, the **Your API key** modal appears with the full key value and a **Copy** button. The full key value is shown only once. Copy it and store it securely before clicking **Continue**. 1. Click **Copy**. 2. Click **Continue** to return to the API keys table. ![API keys page with table row and Your API key modal for one-time copy](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/your-api-key-modal.webp) ## Security Best Practices Never commit API keys to version control. Treat API keys like passwords. - Store keys in environment variables or a secure secrets manager - Rotate keys immediately if you suspect exposure - Use separate keys for development, staging, and production ================================================ FILE: docs/memori-byodb/databases/cockroachdb.mdx ================================================ --- title: CockroachDB description: Set up Memori with CockroachDB — distributed SQL database with PostgreSQL compatibility, automatic scaling, and strong consistency. --- # CockroachDB CockroachDB is a distributed SQL database that uses the PostgreSQL wire protocol. It works with Memori through the same `psycopg` driver as PostgreSQL — no special adapter needed. ## Install ```bash pip install memori psycopg2-binary ``` ## Quick Start ```python {{ title: 'Basic' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "cockroachdb+psycopg2://user:password@localhost:26257/memori_db", pool_pre_ping=True ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ```python {{ title: 'CockroachDB Cloud' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "cockroachdb+psycopg2://user:password@free-tier.gcp-us-central1.cockroachlabs.cloud:26257/memori_db" "?sslmode=verify-full", pool_pre_ping=True, pool_size=10, max_overflow=20 ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ## Connection Strings | Environment | Connection String | | --------------------- | -------------------------------------------------------------------------------------------------- | | **Local** | `cockroachdb+psycopg2://root@localhost:26257/memori_db` | | **With Auth** | `cockroachdb+psycopg2://user:pass@host:26257/memori_db` | | **CockroachDB Cloud** | `cockroachdb+psycopg2://user:pass@cluster.cockroachlabs.cloud:26257/memori_db?sslmode=verify-full` | | **PostgreSQL scheme** | `postgresql+psycopg2://user:pass@host:26257/memori_db` | ## Complete Example ```python import os from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine( os.getenv("COCKROACHDB_URL"), pool_pre_ping=True, pool_size=10, max_overflow=20, pool_recycle=300 ) SessionLocal = sessionmaker(bind=engine) client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") mem.config.storage.build() response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "I manage a distributed systems team."}] ) print(response.choices[0].message.content) mem.augmentation.wait() facts = mem.recall("job role") print(facts) ``` ================================================ FILE: docs/memori-byodb/databases/mongodb.mdx ================================================ --- title: MongoDB description: Set up Memori with MongoDB — document-oriented AI memory using PyMongo. --- # MongoDB MongoDB stores data as flexible JSON-like documents. Memori integrates through PyMongo, giving you a NoSQL option for AI agent memory. ## Install ```bash pip install memori pymongo ``` ## Quick Start ```python from memori import Memori from pymongo import MongoClient client = MongoClient("mongodb://localhost:27017") def get_db(): return client["memori_db"] mem = Memori(conn=get_db) mem.config.storage.build() ``` ## Connection Strings | Environment | Connection String | | --------------- | ---------------------------------------------------- | | **Local** | `mongodb://localhost:27017` | | **With Auth** | `mongodb://user:password@localhost:27017` | | **Atlas (SRV)** | `mongodb+srv://user:password@cluster.mongodb.net` | | **Replica Set** | `mongodb://host1:27017,host2:27017/?replicaSet=myRS` | ## Complete Example ```python import os from pymongo import MongoClient from memori import Memori from openai import OpenAI mongo_client = MongoClient("mongodb://localhost:27017") def get_db(): return mongo_client["memori_db"] client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori(conn=get_db).llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") mem.config.storage.build() response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "I love hiking in the Rockies."}] ) print(response.choices[0].message.content) mem.augmentation.wait() facts = mem.recall("hobbies and outdoor activities") print(facts) ``` ## MongoDB Atlas ```python from pymongo import MongoClient mongo_client = MongoClient( "mongodb+srv://user:password@cluster.mongodb.net" "/?retryWrites=true&w=majority" ) def get_db(): return mongo_client["memori_db"] ``` ## Connection Pooling PyMongo manages its own connection pool: ```python mongo_client = MongoClient( "mongodb://localhost:27017", maxPoolSize=50, minPoolSize=5, maxIdleTimeMS=30000 ) ``` ================================================ FILE: docs/memori-byodb/databases/mysql.mdx ================================================ --- title: MySQL description: Set up Memori with MySQL — use your existing MySQL infrastructure for AI agent memory. --- # MySQL If your infrastructure already runs MySQL, you can use it directly with Memori without setting up a separate database. ## Install ```bash {{ title: 'PyMySQL (Pure Python)' }} pip install memori pymysql ``` ```bash {{ title: 'mysqlclient (C Extension)' }} pip install memori mysqlclient ``` ## Quick Start ```python {{ title: 'PyMySQL' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "mysql+pymysql://user:password@localhost:3306/memori_db", pool_pre_ping=True ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ```python {{ title: 'mysqlclient' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "mysql+mysqldb://user:password@localhost:3306/memori_db", pool_pre_ping=True ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ## Connection Strings | Driver | Connection String | | ---------------- | --------------------------------------------------------------------- | | **PyMySQL** | `mysql+pymysql://user:pass@host:3306/database` | | **mysqlclient** | `mysql+mysqldb://user:pass@host:3306/database` | | **With charset** | `mysql+pymysql://user:pass@host:3306/database?charset=utf8mb4` | | **With SSL** | `mysql+pymysql://user:pass@host:3306/database?ssl_ca=/path/to/ca.pem` | ## Complete Example ```python import os from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine( "mysql+pymysql://user:password@localhost:3306/memori_db" "?charset=utf8mb4", pool_pre_ping=True, pool_size=5, max_overflow=10, pool_recycle=1800 ) SessionLocal = sessionmaker(bind=engine) client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") mem.config.storage.build() response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "I work at Acme Corp as a designer."}] ) print(response.choices[0].message.content) mem.augmentation.wait() facts = mem.recall("workplace") print(facts) ``` ================================================ FILE: docs/memori-byodb/databases/oracle.mdx ================================================ --- title: Oracle description: Set up Memori with Oracle Database — enterprise-grade AI memory for Oracle infrastructure. --- # Oracle Oracle Database integrates with Memori through SQLAlchemy, so you can add AI agent memory to existing Oracle infrastructure. ## Install ```bash {{ title: 'oracledb (Recommended)' }} pip install memori oracledb ``` ```bash {{ title: 'cx_Oracle (Legacy)' }} pip install memori cx_Oracle ``` ## Quick Start ```python {{ title: 'oracledb (Modern)' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "oracle+oracledb://user:password@hostname:1521/service_name" ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ```python {{ title: 'cx_Oracle (Legacy)' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "oracle+cx_oracle://user:password@hostname:1521/service_name" ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ## Connection Strings | Format | Connection String | | ---------------- | ----------------------------------------------------------------------- | | **oracledb** | `oracle+oracledb://user:pass@host:1521/service_name` | | **cx_Oracle** | `oracle+cx_oracle://user:pass@host:1521/service_name` | | **TNS Name** | `oracle+oracledb://user:pass@tns_alias` | | **Oracle Cloud** | `oracle+oracledb://user:pass@host:1522/service?ssl_server_dn_match=yes` | ## Complete Example ```python import os from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine( "oracle+oracledb://memori_user:password@oracle-host:1521/ORCL", pool_pre_ping=True, pool_size=5, max_overflow=10, pool_recycle=300 ) SessionLocal = sessionmaker(bind=engine) client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") mem.config.storage.build() response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Our quarterly review is on March 15."}] ) print(response.choices[0].message.content) mem.augmentation.wait() facts = mem.recall("quarterly review date") print(facts) ``` ## Oracle Cloud For Oracle Autonomous Database, use the wallet-based connection: ```python import oracledb oracledb.init_oracle_client(config_dir="/path/to/wallet") engine = create_engine( "oracle+oracledb://ADMIN:password@db_alias" "?config_dir=/path/to/wallet" "&wallet_location=/path/to/wallet" "&wallet_password=wallet_password" ) ``` ================================================ FILE: docs/memori-byodb/databases/overview.mdx ================================================ --- title: Databases Overview description: Memori supports SQLite, PostgreSQL, MySQL, MariaDB, Oracle, MongoDB, CockroachDB, and OceanBase, plus providers like Neon, Supabase, and AWS RDS/Aurora. --- # Databases Overview Memori is database-agnostic. With the BYODB (Bring Your Own Database) approach, your data stays on your infrastructure — you choose the database, you own the data. Want a zero-setup option? Try Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Supported Databases | Database | Best For | Driver | Connection String | | ----------------------------------------------------------- | ---------------------------------- | -------------------------- | --------------------------------------------------- | | **[SQLite](/docs/memori-byodb/databases/sqlite)** | Development, prototyping | Built-in (`sqlite3`) | `sqlite:///memori.db` | | **[PostgreSQL](/docs/memori-byodb/databases/postgres)** | Production, high concurrency | `psycopg2-binary` | `postgresql://user:pass@host/db` | | **[MySQL](/docs/memori-byodb/databases/mysql)** | Existing MySQL infrastructure | `pymysql` or `mysqlclient` | `mysql+pymysql://user:pass@host/db` | | **MariaDB** | MySQL-compatible alternative | `pymysql` or `mysqlclient` | `mysql+pymysql://user:pass@host/db` | | **[Oracle](/docs/memori-byodb/databases/oracle)** | Enterprise environments | `oracledb` or `cx_Oracle` | `oracle+oracledb://user:pass@host/service` | | **[MongoDB](/docs/memori-byodb/databases/mongodb)** | Document-oriented, flexible schema | `pymongo` | `mongodb://host:27017` | | **[CockroachDB](/docs/memori-byodb/databases/cockroachdb)** | Distributed SQL, global scale | `psycopg2-binary` | `cockroachdb+psycopg2://user:pass@host/db` | | **OceanBase** | Distributed HTAP workloads | `pyobvector` | `mysql+pyobvector://user:pass@host:2881/db` | | **Neon** | Serverless PostgreSQL | `psycopg2-binary` | `postgresql://user:pass@ep-xxx.neon.tech/db` | | **Supabase** | Hosted PostgreSQL with extras | `psycopg2-binary` | `postgresql://user:pass@db.xxx.supabase.co:5432/db` | MariaDB uses the same drivers and connection strings as MySQL. Neon and Supabase are PostgreSQL-compatible — see the [PostgreSQL](/docs/memori-byodb/databases/postgres) page for full setup details. ## Managed Providers Memori also works with hosted services that expose standard PostgreSQL/MySQL endpoints: | Provider | Engine Compatibility | Typical Host Pattern | | -------------- | -------------------- | --------------------- | | **Neon** | PostgreSQL | `*.neon.tech` | | **Supabase** | PostgreSQL | `*.supabase.co` | | **AWS RDS** | PostgreSQL/MySQL | `*.rds.amazonaws.com` | | **AWS Aurora** | PostgreSQL/MySQL | `*.rds.amazonaws.com` | ## Quick Start Code ```python {{ title: 'SQLite (DB API 2.0)' }} import sqlite3 from memori import Memori def get_connection(): return sqlite3.connect("memori.db") mem = Memori(conn=get_connection) mem.config.storage.build() ``` ```python {{ title: 'PostgreSQL' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "postgresql+psycopg://user:password@localhost:5432/memori_db", pool_pre_ping=True ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ```python {{ title: 'MySQL' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "mysql+pymysql://user:password@localhost:3306/memori_db", pool_pre_ping=True ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ```python {{ title: 'Oracle' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "oracle+oracledb://user:password@localhost:1521/service_name" ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ```python {{ title: 'MongoDB' }} from memori import Memori from pymongo import MongoClient client = MongoClient("mongodb://localhost:27017") def get_db(): return client["memori_db"] mem = Memori(conn=get_db) mem.config.storage.build() ``` ```python {{ title: 'CockroachDB' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "cockroachdb+psycopg2://user:password@localhost:26257/memori_db", pool_pre_ping=True ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ```python {{ title: 'OceanBase' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "mysql+pyobvector://user:password@localhost:2881/memori_db" ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ## Connection Methods Memori supports four connection methods depending on your stack: | Method | Description | Use Case | | -------------- | ----------------------------------------- | ----------------------------------------- | | **SQLAlchemy** | Industry-standard ORM with `sessionmaker` | Production applications, connection pools | | **DB API 2.0** | Direct Python database drivers (PEP 249) | Lightweight, minimal dependencies | | **Django ORM** | Native Django ORM integration | Django applications | | **MongoDB** | Function returning a database object | Document databases via `pymongo` | Memori accepts a `conn` parameter — a callable that returns a new connection each time it is called. - **SQLAlchemy databases** (SQLite, PostgreSQL, MySQL, MariaDB, Oracle, CockroachDB, OceanBase, plus providers like Neon/Supabase/RDS/Aurora): pass a `sessionmaker` - **DB API 2.0**: pass a function that returns a PEP 249 connection object - **Django ORM**: use Django's native ORM integration - **MongoDB**: pass a function returning a database instance ================================================ FILE: docs/memori-byodb/databases/postgres.mdx ================================================ --- title: PostgreSQL description: Set up Memori with PostgreSQL — recommended for production with connection pooling and high concurrency. --- # PostgreSQL PostgreSQL is the recommended database for production Memori deployments. Full concurrent write support, connection pooling, and cloud-ready. ## Install ```bash pip install memori psycopg2-binary ``` ## Quick Start ```python {{ title: 'Basic' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "postgresql+psycopg://user:password@localhost:5432/memori_db", pool_pre_ping=True ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ```python {{ title: 'With Connection Pool' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "postgresql+psycopg://user:password@localhost:5432/memori_db", pool_pre_ping=True, pool_size=10, max_overflow=20, pool_recycle=300 ) SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ## Complete Example ```python import os from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine( os.getenv("DATABASE_URL"), pool_pre_ping=True, pool_size=10, max_overflow=20, pool_recycle=300 ) SessionLocal = sessionmaker(bind=engine) client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") mem.config.storage.build() response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "I'm a senior engineer at Google."}] ) print(response.choices[0].message.content) mem.augmentation.wait() facts = mem.recall("job title and company") print(facts) ``` ## Cloud Providers | Provider | Connection Format | | -------------------- | ------------------------------------------------------------ | | **Neon** | `postgresql+psycopg://...@*.neon.tech/...` | | **Supabase** | `postgresql+psycopg://...@*.supabase.co/...` | | **AWS RDS** | `postgresql+psycopg://...@*.rds.amazonaws.com/...` | | **AWS Aurora** | `postgresql+psycopg://...@*.rds.amazonaws.com/...` | | **Google Cloud SQL** | `postgresql+psycopg://...@*.cloudsql/...` | | **Azure Database** | `postgresql+psycopg://...@*.postgres.database.azure.com/...` | ## SSL Connections For cloud-hosted PostgreSQL, use SSL: ```python engine = create_engine( "postgresql+psycopg://user:password@host:5432/memori_db" "?sslmode=require", pool_pre_ping=True ) ``` | Mode | Description | | ------------- | ----------------------------------------- | | `require` | SSL required, no certificate verification | | `verify-ca` | SSL + verify server certificate | | `verify-full` | SSL + verify certificate + hostname | ================================================ FILE: docs/memori-byodb/databases/sqlite.mdx ================================================ --- title: SQLite description: Set up Memori with SQLite — zero install, file-based storage perfect for development and prototyping. --- # SQLite SQLite requires no server setup and stores data in a local file. It is built into Python, so it works out of the box. ## Install No extra packages needed — SQLite is built into Python. ```bash pip install memori openai ``` ## Quick Start ```python {{ title: 'SQLAlchemy (Recommended)' }} from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) mem.config.storage.build() ``` ```python {{ title: 'DB API 2.0' }} import sqlite3 from memori import Memori def get_connection(): return sqlite3.connect("memori.db") mem = Memori(conn=get_connection) mem.config.storage.build() ``` ## Connection Strings | Path Type | Connection String | Description | | ------------- | ------------------------------------- | ---------------------------- | | **Relative** | `sqlite:///memori.db` | File in current directory | | **Absolute** | `sqlite:////home/user/data/memori.db` | Absolute path (four slashes) | | **In-Memory** | `sqlite:///:memory:` | Temporary, lost on exit | ## Complete Example ```python import os from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") mem.config.storage.build() response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "My favorite language is Python."}] ) print(response.choices[0].message.content) mem.augmentation.wait() facts = mem.recall("favorite programming language") print(facts) ``` ## Multi-Threading For web servers or multi-threaded apps, disable the same-thread check: ```python engine = create_engine( "sqlite:///memori.db", connect_args={"check_same_thread": False} ) ``` ================================================ FILE: docs/memori-byodb/getting-started/installation.mdx ================================================ --- title: Installation description: Install Memori and set up your database for the Memori BYODB. --- # Installation Get Memori installed and connected to your own database in a few steps. Memori BYODB supports the Python SDK only. For TypeScript, use [Memori Cloud](/docs/memori-cloud). ## Install Memori ```bash {{ title: 'pip' }} pip install memori ``` ```bash {{ title: 'poetry' }} poetry add memori ``` ```bash {{ title: 'uv' }} uv add memori ``` ## Install Your Database Driver Memori supports SQLite, PostgreSQL, MySQL, MariaDB, Oracle, MongoDB, CockroachDB, and OceanBase. Managed services like Neon, Supabase, and AWS RDS/Aurora work through their compatible PostgreSQL/MySQL engines. Install the driver for your preferred database: ```bash {{ title: 'SQLite (built-in)' }} # No extra install needed! # SQLite support is included with Python. ``` ```bash {{ title: 'PostgreSQL' }} pip install psycopg2-binary # Or for async: pip install asyncpg ``` ```bash {{ title: 'MySQL' }} pip install pymysql # Or: pip install mysqlclient ``` ```bash {{ title: 'MariaDB' }} pip install pymysql # Or: pip install mysqlclient ``` ```bash {{ title: 'Oracle' }} pip install oracledb ``` ```bash {{ title: 'MongoDB' }} pip install pymongo ``` ```bash {{ title: 'CockroachDB' }} pip install psycopg2-binary ``` ```bash {{ title: 'OceanBase' }} pip install pyobvector ``` Neon, Supabase, and AWS RDS/Aurora use standard PostgreSQL drivers (`psycopg2-binary` or `psycopg`). ## Connection Patterns | Pattern | What to pass to `conn` | Works With | | ---------- | ----------------------------------------------- | --------------------------------------------------------------------- | | SQLAlchemy | `sessionmaker` | SQLite, PostgreSQL, MySQL, MariaDB, Oracle, CockroachDB, OceanBase | | DB API 2.0 | Function that returns a PEP 249 connection | SQLite and SQL drivers (`sqlite3`, `psycopg2`, `pymysql`, `oracledb`) | | Django ORM | Django connection callable | Django applications | | MongoDB | Function that returns a MongoDB database object | MongoDB via `pymongo` | ```python {{ title: 'SQLite (DB API 2.0)' }} import sqlite3 def get_connection(): return sqlite3.connect("memori.db") from memori import Memori mem = Memori(conn=get_connection) ``` ```python {{ title: 'PostgreSQL (SQLAlchemy)' }} from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "postgresql+psycopg2://user:password@localhost:5432/mydb" ) SessionLocal = sessionmaker(bind=engine) from memori import Memori mem = Memori(conn=SessionLocal) ``` ```python {{ title: 'PostgreSQL (DB API 2.0)' }} import psycopg2 from memori import Memori def get_connection(): return psycopg2.connect( dbname="mydb", user="user", password="password", host="localhost", port=5432, ) mem = Memori(conn=get_connection) ``` ```python {{ title: 'MySQL / MariaDB (SQLAlchemy)' }} from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine( "mysql+pymysql://user:password@localhost:3306/mydb" ) SessionLocal = sessionmaker(bind=engine) from memori import Memori mem = Memori(conn=SessionLocal) ``` ```python {{ title: 'MongoDB' }} from pymongo import MongoClient from memori import Memori client = MongoClient("mongodb://localhost:27017") def get_db(): return client["memori_db"] mem = Memori(conn=get_db) ``` ## Create the Schema After setting up your connection, run `build()` once to create the Memori tables in your database. This only needs to be done the first time, or when you upgrade Memori. ```python import sqlite3 from memori import Memori def get_connection(): return sqlite3.connect("memori.db") mem = Memori(conn=get_connection) mem.config.storage.build() # Creates all required tables ``` ## Install Your LLM Provider Install the SDK for your preferred LLM provider: ```bash {{ title: 'OpenAI' }} pip install openai ``` ```bash {{ title: 'Anthropic' }} pip install anthropic ``` ```bash {{ title: 'Google Gemini' }} pip install google-genai ``` ```bash {{ title: 'LangChain' }} pip install langchain-openai ``` ## Set Up Your LLM Provider Key You will need an API key for your LLM provider: ```bash # OpenAI export OPENAI_API_KEY="your-openai-key" # Anthropic export ANTHROPIC_API_KEY="your-anthropic-key" # Google Gemini export GOOGLE_API_KEY="your-google-key" ``` ## Set Up Your Memori API Key (Optional) A Memori API key unlocks higher augmentation quotas (5,000/month vs 100 without a key). You can sign up directly from the CLI: ```bash python -m memori sign-up your-email@example.com ``` Then set the key as an environment variable: ```bash export MEMORI_API_KEY="your-api-key-here" ``` Or add it to a `.env` file in your project root: ``` MEMORI_API_KEY=your-api-key-here ``` Check your current quota anytime: ```bash python -m memori quota ``` ## Pre-download the Embedding Model Memori uses a local embedding model for semantic search. On first run, it downloads the model automatically, which can take a moment. To pre-download it: ```bash python -m memori setup ``` ## Verify Installation Run `pip show memori` in your terminal to confirm the package is installed. ================================================ FILE: docs/memori-byodb/getting-started/python-quickstart.mdx ================================================ --- title: Python SDK Quickstart description: Get started with Memori BYODB in under 3 minutes using SQLite and OpenAI. --- # Python SDK Quickstart Get started with Memori in under 3 minutes. Since Memori BYODB is open source, you bring your own database — and for this quick start, we will use SQLite so there is nothing extra to install. Want a zero-setup option? Try Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). In this example, we will use Memori with OpenAI and SQLite. Check out the [LLM providers](/docs/memori-byodb/llm/overview) and [database](/docs/memori-byodb/databases/overview) guides for other integrations. ## Prerequisites - Python 3.10 or higher - An OpenAI API key ## Step 1: Install Libraries Install Memori and the OpenAI SDK: ```bash {{ title: 'pip' }} pip install memori openai ``` ```bash {{ title: 'poetry' }} poetry add memori openai ``` ```bash {{ title: 'uv' }} uv add memori openai ``` ## Step 2: Set Environment Variables Set your OpenAI API key as an environment variable: ```bash export OPENAI_API_KEY="your-openai-api-key" ``` ## Step 3: Run Your First Memori Application Create a new Python file `quickstart.py` and add the following code: ### Setup & Configuration Import libraries, set up a SQLite database with Python's built-in `sqlite3`, and initialize Memori with your OpenAI client. - `conn` accepts a connection factory (SQLAlchemy, DB-API 2.0, Django ORM, or MongoDB callable) - `llm.register()` wraps your LLM client for automatic memory capture - `attribution()` links memories to a specific user and process - `build()` creates the Memori schema tables in your database ```python import os import sqlite3 from memori import Memori from openai import OpenAI def get_sqlite_connection(): return sqlite3.connect("memori.db") client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori(conn=get_sqlite_connection).llm.register(client) mem.attribution(entity_id="user_123", process_id="test-ai-agent") mem.config.storage.build() ``` If your app already uses SQLAlchemy, you can pass a `sessionmaker` instead: ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) ``` ### First Conversation Tell the LLM a fact about yourself. Memori automatically captures the conversation and processes it through Advanced Augmentation. Since augmentation runs asynchronously, call `augmentation.wait()` in short-lived scripts to ensure memories are fully processed before continuing. ```python response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "My favorite color is blue."} ] ) print(response.choices[0].message.content + "\n") # Wait for background augmentation to finish mem.augmentation.wait() ``` ### Memory Recall Create a completely new client and Memori instance — no prior context carried over. When you ask the LLM what it remembers, Memori automatically injects the relevant facts via semantic search. The second response should correctly recall your favorite color, proving memory persistence works across sessions. ```python client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori(conn=get_sqlite_connection).llm.register(client) mem.attribution(entity_id="user_123", process_id="test-ai-agent") response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "What's my favorite color?"} ] ) print(response.choices[0].message.content + "\n") ``` ## Step 4: Run the Application Execute your Python file: ```bash python quickstart.py ``` You should see the AI respond to both questions, with the second response correctly recalling that your favorite color is blue! ## Step 5: Inspect Your Memories Since you own the database, you can inspect what Memori stored directly: ```bash {{ title: 'Messages' }} sqlite3 memori.db "SELECT * FROM memori_conversation_message;" ``` ```bash {{ title: 'Facts' }} sqlite3 memori.db "SELECT * FROM memori_entity_fact;" ``` ```bash {{ title: 'Knowledge Graph' }} sqlite3 memori.db "SELECT * FROM memori_knowledge_graph;" ``` ```bash {{ title: 'Sessions' }} sqlite3 memori.db "SELECT * FROM memori_session;" ``` ================================================ FILE: docs/memori-byodb/getting-started/use-cases.mdx ================================================ --- title: Use Cases description: Common use cases and applications for Memori open source. --- # Use Cases Memori is designed for any application where AI agents need to remember context across conversations. Here are the most common use cases — all running with your own database. Want a zero-setup option? Try Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Customer Support Chatbots Build support bots that remember customer history, preferences, and previous issues. No more "Can you repeat your account number?" — Memori recalls everything automatically. **Benefits:** - Remember customer preferences and history - Recall previous support tickets and resolutions - Personalize responses based on past interactions - Track issues across multiple sessions - All data stays in your database for compliance ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) # Each customer gets their own memory space mem.attribution( entity_id="customer_456", process_id="support_bot" ) # Memori automatically recalls relevant context response = client.chat.completions.create( model="gpt-4o-mini", messages=[{ "role": "user", "content": "I'm having that issue again" }] ) # Memori injects: "Customer previously reported # login timeout issues on 2024-01-15" ``` ## Personalized AI Assistants Create AI assistants that learn and adapt to each user over time. Memori builds a profile of preferences, skills, and context that makes every interaction more relevant. **Benefits:** - Learn coding preferences and tech stack - Remember project context across sessions - Adapt communication style to user preferences - Build long-term user profiles automatically ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from anthropic import Anthropic engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = Anthropic() mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution( entity_id="developer_789", process_id="code_assistant" ) # Over time, Memori learns: # - "Uses Python 3.12 with FastAPI" # - "Prefers type hints and dataclasses" # - "Works on e-commerce platform" response = client.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=[{ "role": "user", "content": "How should I structure this endpoint?" }] ) ``` ## Multi-Agent Workflows Coordinate multiple AI agents that share context through Memori. Each agent contributes to a shared memory space while maintaining its own process identity. **Benefits:** - Share context between specialized agents - Track which agent contributed what information - Maintain conversation continuity across handoffs - Build collective knowledge graphs ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori from openai import OpenAI engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) # Research agent gathers information mem.attribution( entity_id="project_alpha", process_id="research_agent" ) client.chat.completions.create( model="gpt-4o-mini", messages=[{ "role": "user", "content": "Research competitor pricing" }] ) # Analysis agent recalls research findings mem.attribution( entity_id="project_alpha", process_id="analysis_agent" ) # Memori shares context across agents # for the same entity ``` ================================================ FILE: docs/memori-byodb/index.mdx ================================================ --- title: Introduction description: Memori is an open-source, structured memory layer for AI agents — own your data, choose your database, keep full control. --- # What is Memori? __Memori__ is a memory layer for LLM applications, agents, and copilots. It continuously captures interactions, extracts structured knowledge, and intelligently ranks, decays, and retrieves the relevant memories. So your AI remembers the right things at the right time across every session. Memori BYODB supports the Python SDK only. For TypeScript, use [Memori Cloud](/docs/memori-cloud). Memori uses [Advanced Augmentation](/docs/memori-byodb/concepts/advanced-augmentation) to turn raw conversations into structured, searchable memories. It runs asynchronously in the background to minimize impact on your response path ## Why Memori BYODB? ### Database Freedom Use SQLite, PostgreSQL, MySQL, MariaDB, Oracle, MongoDB, CockroachDB, or OceanBase. Managed providers like Neon, Supabase, and AWS RDS/Aurora are also supported through their compatible engines. ### Full Data Ownership Your data stays in your database, on your infrastructure. Full compliance and regulatory control with no third-party storage. ### LLM Provider Support OpenAI, Anthropic, Gemini, and Grok (xAI) via direct SDK wrappers. Bedrock is supported via LangChain `ChatBedrock`. OpenAI-compatible providers (Nebius, Deepseek, NVIDIA NIM, Azure OpenAI, and more) work through OpenAI's `base_url` parameter. Supports sync, async, streamed, and unstreamed modes, plus LangChain , Agno, and Pydantic AI. ### Intelligent Recall Intelligent Recall surfaces the right memories at the right time. Memories are ranked by relevance and importance, with intelligent decay so older or less relevant facts recede — so your AI stays contextually aware without clutter. Recall any memory later with semantic search; use manual recall for custom prompts, UIs, or debugging. See [How Memori Works](/docs/memori-byodb/concepts/how-memory-works#how-recall-works) for automatic vs manual recall and tuning. ## Quick Example Get started with a database connection and your favorite LLM: - **One-line setup** — Connect your DB and LLM; memory capture, augmentation, and recall work without extra config. - **Semantic recall** — Queries like “what does this user prefer?” pull the right memories automatically. - **Dashboard** — Use [app.memorilabs.ai](https://app.memorilabs.ai) for API keys, usage, and (with Memori Cloud) the Graph Explorer and Playground. - **Your data, your rules** — Store everything in your DB; compliance, backups, and custom analytics stay under your control. - **Roadmap** — Knowledge-graph APIs, configurable decay, and richer dashboarding are on the way. ```python import os import sqlite3 from memori import Memori from openai import OpenAI # Requires OPENAI_API_KEY in your environment def get_sqlite_connection(): return sqlite3.connect("memori.db") client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori(conn=get_sqlite_connection).llm.register(client) # Track conversations by user and process mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="support_agent") # All conversations automatically persisted and recalled response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "My favorite color is blue."}] ) ``` ## OpenAI-Compatible Providers Memori supports any model that uses OpenAI's client interface via the `base_url` parameter — including Nebius, Deepseek, NVIDIA NIM, and more. ```python import os import sqlite3 from memori import Memori from openai import OpenAI def get_sqlite_connection(): return sqlite3.connect("memori.db") client = OpenAI( base_url="https://api.studio.nebius.com/v1/", api_key=os.getenv("NEBIUS_API_KEY"), ) mem = Memori(conn=get_sqlite_connection).llm.register(client) ``` ## Core Concepts | Concept | Description | Example | | ---------------- | ----------------------------------------------------- | ---------------------------------------- | | **Entity** | Person, place, or thing (like a user) | `entity_id="user_123"` | | **Process** | Your agent, LLM interaction, or program | `process_id="support_agent"` | | **Session** | Groups LLM interactions together | Auto-generated UUID, manually manageable | | **Augmentation** | Background AI enhancement of memories | Auto-runs after wrapped LLM calls | | **Recall** | Retrieve relevant memories from previous interactions | Auto-injects recalled memories | ## Architecture Overview The diagram has three lanes: your app, the Memori SDK, and your own database. Your app calls the LLM normally, Memori intercepts the call, and the synchronous response path continues with zero added latency. Synchronous capture: conversation messages are stored in `memori_conversation_message` while your normal LLM flow continues. Recall injection: relevant memories are pulled from `memori_entity_fact` and injected into later prompts. Async augmentation: background processing extracts facts, preferences, rules, events, and relationships from conversations. Own-your-data storage: structured memory records are written to your database, including `memori_entity_fact`, `memori_process_attribute`, and `memori_knowledge_graph`. !["Memori BYODB architecture"](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memori-byodb-architecture.webp) ================================================ FILE: docs/memori-byodb/llm/agno.mdx ================================================ --- title: Agno description: Using Memori with Agno agents and Memori BYODB. --- # Agno Memori integrates with Agno at the model layer. Register your Agno model with `llm.register(...)` and Memori captures `run()`, `arun()`, and streamed responses automatically. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Quick Start ```python {{ title: 'Sync' }} from agno.agent import Agent from agno.models.openai import OpenAIChat from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) model = OpenAIChat(id="gpt-4o-mini") mem = Memori(conn=SessionLocal).llm.register(openai_chat=model) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="agno_agent") agent = Agent( model=model, instructions=["Be helpful and concise"], markdown=True, ) response = agent.run("Hello!", session_id="support-session") print(response.content) ``` ```python {{ title: 'Async' }} import asyncio from agno.agent import Agent from agno.models.openai import OpenAIChat from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) model = OpenAIChat(id="gpt-4o-mini") mem = Memori(conn=SessionLocal).llm.register(openai_chat=model) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="agno_agent") agent = Agent( model=model, instructions=["Be helpful and concise"], markdown=True, ) async def main(): response = await agent.arun("Hello!", session_id="support-session") print(response.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} from agno.agent import Agent from agno.models.openai import OpenAIChat from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) model = OpenAIChat(id="gpt-4o-mini") mem = Memori(conn=SessionLocal).llm.register(openai_chat=model) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="agno_agent") agent = Agent( model=model, instructions=["Be helpful and concise"], markdown=True, ) stream = agent.run("Hello!", session_id="support-session", stream=True) for chunk in stream: if hasattr(chunk, "content") and chunk.content: print(chunk.content, end="") ``` ## Different Providers Agno supports multiple model families. Use the matching registration keyword in Memori. | Package | Model Class | Registration Keyword | | ----------------------- | ------------ | -------------------- | | `agno.models.openai` | `OpenAIChat` | `openai_chat=model` | | `agno.models.anthropic` | `Claude` | `claude=model` | | `agno.models.google` | `Gemini` | `gemini=model` | | `agno.models.xai` | `xAI` | `xai=model` | ```python {{ title: 'OpenAI' }} from agno.models.openai import OpenAIChat model = OpenAIChat(id="gpt-4o-mini") mem = Memori(conn=SessionLocal).llm.register(openai_chat=model) ``` ```python {{ title: 'Anthropic' }} from agno.models.anthropic import Claude model = Claude(id="claude-sonnet-4-20250514") mem = Memori(conn=SessionLocal).llm.register(claude=model) ``` ```python {{ title: 'Google Gemini' }} from agno.models.google import Gemini model = Gemini(id="gemini-2.0-flash-exp") mem = Memori(conn=SessionLocal).llm.register(gemini=model) ``` ```python {{ title: 'xAI' }} from agno.models.xai import xAI model = xAI(id="grok-3") mem = Memori(conn=SessionLocal).llm.register(xai=model) ``` ## Supported Modes | Mode | Method | | ------------ | ------------------------ | | **Sync** | `agent.run()` | | **Async** | `await agent.arun()` | | **Streamed** | `agent.run(stream=True)` | ================================================ FILE: docs/memori-byodb/llm/anthropic.mdx ================================================ --- title: Anthropic description: Using Memori with Anthropic Claude models and Memori BYODB. --- # Anthropic Memori supports all Anthropic Claude models. Register your client once and every conversation is automatically captured and stored. Note: `max_tokens` is required for all Anthropic API calls. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Quick Start ```python {{ title: 'Sync' }} from anthropic import Anthropic from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = Anthropic() mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="claude_assistant") response = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) print(response.content[0].text) ``` ```python {{ title: 'Async' }} import asyncio from anthropic import AsyncAnthropic from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = AsyncAnthropic() mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="claude_assistant") async def main(): response = await client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) print(response.content[0].text) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} from anthropic import Anthropic from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = Anthropic() mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="claude_assistant") with client.messages.stream( model="claude-3-5-sonnet-20241022", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) as stream: for text in stream.text_stream: print(text, end="") ``` ## Supported Modes | Mode | Method | | ------------ | -------------------------------- | | **Sync** | `client.messages.create()` | | **Async** | `await client.messages.create()` | | **Streamed** | `client.messages.stream()` | ## System Prompts Anthropic supports a top-level `system` parameter separate from the `messages` array. Memori captures both. ```python response = client.messages.create( model="claude-3-5-sonnet-20241022", max_tokens=1024, system="You are a helpful coding assistant.", messages=[{"role": "user", "content": "Explain Python decorators."}] ) print(response.content[0].text) ``` ================================================ FILE: docs/memori-byodb/llm/aws-bedrock.mdx ================================================ --- title: AWS Bedrock description: Using Memori with AWS Bedrock models via the LangChain ChatBedrock adapter and Memori BYODB. --- # AWS Bedrock Memori supports AWS Bedrock through `langchain-aws`. Register using the `chatbedrock` keyword to access Claude, Llama, Mistral, and other Bedrock models. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Quick Start ```python {{ title: 'Sync' }} from langchain_aws import ChatBedrock from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = ChatBedrock( model_id="anthropic.claude-3-5-sonnet-20241022-v2:0", region_name="us-east-1" ) mem = Memori(conn=SessionLocal).llm.register(chatbedrock=client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="bedrock_agent") response = client.invoke("Hello!") print(response.content) ``` ```python {{ title: 'Async' }} import asyncio from langchain_aws import ChatBedrock from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = ChatBedrock( model_id="anthropic.claude-3-5-sonnet-20241022-v2:0", region_name="us-east-1" ) mem = Memori(conn=SessionLocal).llm.register(chatbedrock=client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="bedrock_agent") async def main(): response = await client.ainvoke("Hello!") print(response.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} from langchain_aws import ChatBedrock from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = ChatBedrock( model_id="anthropic.claude-3-5-sonnet-20241022-v2:0", region_name="us-east-1" ) mem = Memori(conn=SessionLocal).llm.register(chatbedrock=client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="bedrock_agent") for chunk in client.stream("Hello!"): print(chunk.content, end="") ``` ## Supported Modes | Mode | Method | | ------------ | ------------------------ | | **Sync** | `client.invoke()` | | **Async** | `await client.ainvoke()` | | **Streamed** | `client.stream()` | ## Available Models | Model | Model ID | | --------------------- | ------------------------------------------- | | **Claude 3.5 Sonnet** | `anthropic.claude-3-5-sonnet-20241022-v2:0` | | **Claude 3 Haiku** | `anthropic.claude-3-haiku-20240307-v1:0` | | **Llama 3.1 70B** | `meta.llama3-1-70b-instruct-v1:0` | | **Mistral Large** | `mistral.mistral-large-2407-v1:0` | ================================================ FILE: docs/memori-byodb/llm/deepseek.mdx ================================================ --- title: DeepSeek description: Using Memori with DeepSeek models via the OpenAI-compatible API and Memori BYODB. --- # DeepSeek DeepSeek provides an OpenAI-compatible API. Use the `openai` Python package with `base_url="https://api.deepseek.com"` — no special adapter needed. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Quick Start ```python {{ title: 'Sync' }} import os from memori import Memori from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI( base_url="https://api.deepseek.com", api_key=os.getenv("DEEPSEEK_API_KEY"), ) mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="deepseek_assistant") response = client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```python {{ title: 'Async' }} import os, asyncio from memori import Memori from openai import AsyncOpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = AsyncOpenAI( base_url="https://api.deepseek.com", api_key=os.getenv("DEEPSEEK_API_KEY"), ) mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="deepseek_assistant") async def main(): response = await client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} import os from memori import Memori from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI( base_url="https://api.deepseek.com", api_key=os.getenv("DEEPSEEK_API_KEY"), ) mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="deepseek_assistant") stream = client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "Hello!"}], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` ## Supported Modes | Mode | Method | | ------------ | ---------------------------------------- | | **Sync** | `client.chat.completions.create()` | | **Async** | `await client.chat.completions.create()` | | **Streamed** | `stream=True` parameter | ================================================ FILE: docs/memori-byodb/llm/gemini.mdx ================================================ --- title: Google Gemini description: Using Memori with Google Gemini models and Memori BYODB. --- # Google Gemini Memori integrates with Google Gemini via the `google-genai` SDK. Register the `GenerativeModel` instance and all `generate_content()` calls are automatically captured. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Quick Start ```python {{ title: 'Sync' }} import os from memori import Memori import google.generativeai as genai from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) client = genai.GenerativeModel("gemini-2.0-flash-exp") mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="gemini_assistant") response = client.generate_content("Hello!") print(response.text) ``` ```python {{ title: 'Async' }} import os, asyncio from memori import Memori import google.generativeai as genai from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) client = genai.GenerativeModel("gemini-2.0-flash-exp") mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="gemini_assistant") async def main(): response = await client.generate_content_async("Hello!") print(response.text) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} import os from memori import Memori import google.generativeai as genai from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) client = genai.GenerativeModel("gemini-2.0-flash-exp") mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="gemini_assistant") response = client.generate_content("Hello!", stream=True) for chunk in response: print(chunk.text, end="") ``` ## Supported Modes | Mode | Method | | ------------ | --------------------------------------- | | **Sync** | `client.generate_content()` | | **Async** | `await client.generate_content_async()` | | **Streamed** | `stream=True` parameter | ## Multi-Turn Conversations Use `start_chat()` for multi-turn interactions. Memori tracks the full conversation automatically. ```python chat = client.start_chat() response = chat.send_message("My name is Alice.") print(response.text) response = chat.send_message("What's my name?") print(response.text) ``` ================================================ FILE: docs/memori-byodb/llm/langchain.mdx ================================================ --- title: LangChain description: Using Memori with LangChain chat models and Memori BYODB. --- # LangChain Memori supports any LangChain chat model. Each class has its own registration keyword: `chatopenai` for ChatOpenAI/ChatAnthropic, `chatbedrock` for ChatBedrock, `chatgooglegenai` for ChatGoogleGenerativeAI. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Quick Start ```python {{ title: 'Sync' }} from langchain_openai import ChatOpenAI from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = ChatOpenAI(model="gpt-4o-mini") mem = Memori(conn=SessionLocal).llm.register(chatopenai=client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="langchain_agent") response = client.invoke("Hello!") print(response.content) ``` ```python {{ title: 'Async' }} import asyncio from langchain_openai import ChatOpenAI from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = ChatOpenAI(model="gpt-4o-mini") mem = Memori(conn=SessionLocal).llm.register(chatopenai=client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="langchain_agent") async def main(): response = await client.ainvoke("Hello!") print(response.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} from langchain_openai import ChatOpenAI from memori import Memori from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = ChatOpenAI(model="gpt-4o-mini") mem = Memori(conn=SessionLocal).llm.register(chatopenai=client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="langchain_agent") for chunk in client.stream("Hello!"): print(chunk.content, end="") ``` ## Different Providers | Package | Chat Model | Registration Keyword | | ------------------------ | ------------------------ | ------------------------ | | `langchain-openai` | `ChatOpenAI` | `chatopenai=client` | | `langchain-anthropic` | `ChatAnthropic` | `chatopenai=client` | | `langchain-google-genai` | `ChatGoogleGenerativeAI` | `chatgooglegenai=client` | | `langchain-aws` | `ChatBedrock` | `chatbedrock=client` | ```python {{ title: 'Anthropic' }} from langchain_anthropic import ChatAnthropic client = ChatAnthropic(model="claude-3-5-sonnet-20241022") mem = Memori(conn=SessionLocal).llm.register(chatopenai=client) ``` ```python {{ title: 'Google Gemini' }} from langchain_google_genai import ChatGoogleGenerativeAI client = ChatGoogleGenerativeAI(model="gemini-2.0-flash-exp") mem = Memori(conn=SessionLocal).llm.register(chatgooglegenai=client) ``` ```python {{ title: 'AWS Bedrock' }} from langchain_aws import ChatBedrock client = ChatBedrock(model_id="anthropic.claude-3-5-sonnet-20241022-v2:0", region_name="us-east-1") mem = Memori(conn=SessionLocal).llm.register(chatbedrock=client) ``` ## Supported Modes | Mode | Method | | ------------ | ------------------------ | | **Sync** | `client.invoke()` | | **Async** | `await client.ainvoke()` | | **Streamed** | `client.stream()` | ================================================ FILE: docs/memori-byodb/llm/nebius.mdx ================================================ --- title: Nebius AI Studio description: Using Memori with Nebius AI Studio models via the OpenAI-compatible API and Memori BYODB. --- # Nebius AI Studio Nebius AI Studio provides an OpenAI-compatible API. Use the `openai` Python package with `base_url="https://api.studio.nebius.com/v1/"` — no special adapter needed. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Quick Start ```python {{ title: 'Sync' }} import os from memori import Memori from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI( base_url="https://api.studio.nebius.com/v1/", api_key=os.getenv("NEBIUS_API_KEY"), ) mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="nebius_assistant") response = client.chat.completions.create( model="meta-llama/Llama-3.3-70B-Instruct", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```python {{ title: 'Async' }} import os, asyncio from memori import Memori from openai import AsyncOpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = AsyncOpenAI( base_url="https://api.studio.nebius.com/v1/", api_key=os.getenv("NEBIUS_API_KEY"), ) mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="nebius_assistant") async def main(): response = await client.chat.completions.create( model="meta-llama/Llama-3.3-70B-Instruct", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} import os from memori import Memori from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI( base_url="https://api.studio.nebius.com/v1/", api_key=os.getenv("NEBIUS_API_KEY"), ) mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="nebius_assistant") stream = client.chat.completions.create( model="meta-llama/Llama-3.3-70B-Instruct", messages=[{"role": "user", "content": "Hello!"}], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` ## Supported Modes | Mode | Method | | ------------ | ---------------------------------------- | | **Sync** | `client.chat.completions.create()` | | **Async** | `await client.chat.completions.create()` | | **Streamed** | `stream=True` parameter | ================================================ FILE: docs/memori-byodb/llm/openai.mdx ================================================ --- title: OpenAI description: Using Memori with OpenAI models including GPT-4o, GPT-4.1, and the Responses API with Memori BYODB. --- # OpenAI Memori supports all OpenAI models and API styles. Register your client once and every call is automatically captured and stored. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Quick Start ```python {{ title: 'Sync' }} from memori import Memori from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="my_agent") response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```python {{ title: 'Async' }} import asyncio from memori import Memori from openai import AsyncOpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = AsyncOpenAI() mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="my_agent") async def main(): response = await client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} from memori import Memori from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="my_agent") stream = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Hello!"}], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` ```python {{ title: 'Responses API' }} from memori import Memori from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="my_agent") response = client.responses.create( model="gpt-4o-mini", input="Hello!", instructions="You are a helpful assistant." ) print(response.output_text) ``` ## Supported Modes | Mode | Method | | ----------------- | ---------------------------------------- | | **Sync** | `client.chat.completions.create()` | | **Async** | `await client.chat.completions.create()` | | **Streamed** | `stream=True` parameter | | **Responses API** | `client.responses.create()` | ## Multi-Turn Conversations Memori captures each call and links them within the same session. Pass your full conversation history as usual. ```python from memori import Memori from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") messages = [{"role": "user", "content": "My name is Alice."}] response = client.chat.completions.create(model="gpt-4o-mini", messages=messages) messages.append({"role": "assistant", "content": response.choices[0].message.content}) messages.append({"role": "user", "content": "What's my name?"}) response = client.chat.completions.create(model="gpt-4o-mini", messages=messages) print(response.choices[0].message.content) ``` ================================================ FILE: docs/memori-byodb/llm/overview.mdx ================================================ --- title: LLM Providers Overview description: Memori is LLM-agnostic. Register any supported client with a local database connection and Memori handles memory capture, augmentation, and recall automatically. --- # LLM Providers Overview Memori works with all major LLM providers and frameworks. Register any supported client and Memori handles memory capture, augmentation, and recall automatically. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Supported Providers | Provider | Integration | Install | | ----------------------------------------------------- | ------------------ | ------------------------------------- | | **[OpenAI](/docs/memori-byodb/llm/openai)** | Direct SDK wrapper | `pip install memori openai` | | **[Anthropic](/docs/memori-byodb/llm/anthropic)** | Direct SDK wrapper | `pip install memori anthropic` | | **[Google Gemini](/docs/memori-byodb/llm/gemini)** | Direct SDK wrapper | `pip install memori google-genai` | | **[xAI Grok](/docs/memori-byodb/llm/xai-grok)** | OpenAI-compatible | `pip install memori openai` | | **[AWS Bedrock](/docs/memori-byodb/llm/aws-bedrock)** | LangChain adapter | `pip install memori langchain-aws` | | **[LangChain](/docs/memori-byodb/llm/langchain)** | Framework support | `pip install memori langchain-openai` | | **[Agno](/docs/memori-byodb/llm/agno)** | Framework support | `pip install memori agno` | | **[Pydantic AI](/docs/memori-byodb/llm/pydantic-ai)** | Framework support | `pip install memori pydantic-ai` | | **[Nebius AI Studio](/docs/memori-byodb/llm/nebius)** | OpenAI-compatible | `pip install memori openai` | | **[DeepSeek](/docs/memori-byodb/llm/deepseek)** | OpenAI-compatible | `pip install memori openai` | All providers support sync, async, streamed, and unstreamed modes. Any provider with an OpenAI-compatible API works by setting a custom `base_url` — including Nebius AI Studio, DeepSeek, NVIDIA NIM, Azure OpenAI, and more. See [OpenAI-Compatible Providers](#openai-compatible-providers) below. ## Pydantic AI Register the `Agent` instance directly — Memori wraps `run_sync` and `run` automatically. ```python from memori import Memori from pydantic_ai import Agent from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) agent = Agent("openai:gpt-4o-mini") mem = Memori(conn=SessionLocal).llm.register(agent) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="pydantic_agent") result = agent.run_sync("Hello!") print(result.output) ``` ## OpenAI-Compatible Providers Any provider with an OpenAI-compatible API works by setting a custom `base_url`. Examples: xAI, Nebius AI Studio, DeepSeek, Azure OpenAI. ```python import os from memori import Memori from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI( base_url="https://api.studio.nebius.com/v1/", api_key=os.getenv("NEBIUS_API_KEY"), ) mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") response = client.chat.completions.create( model="meta-llama/Llama-3.3-70B-Instruct", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ## OpenAI Responses API ```python from memori import Memori from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") response = client.responses.create( model="gpt-4o-mini", input="Hello!", instructions="You are a helpful assistant." ) print(response.output_text) ``` ================================================ FILE: docs/memori-byodb/llm/pydantic-ai.mdx ================================================ --- title: Pydantic AI description: Using Memori with Pydantic AI agents and Memori BYODB. --- # Pydantic AI Memori integrates at the agent level — register the `Agent` instance and Memori wraps `run_sync` and `run` automatically. Works with any Pydantic AI-supported model. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Quick Start ```python from memori import Memori from pydantic_ai import Agent from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) agent = Agent("openai:gpt-4o-mini") mem = Memori(conn=SessionLocal).llm.register(agent) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="pydantic_agent") result = agent.run_sync("Hello!") print(result.output) ``` ## Async Usage ```python import asyncio from memori import Memori from pydantic_ai import Agent from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) agent = Agent("openai:gpt-4o-mini") mem = Memori(conn=SessionLocal).llm.register(agent) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="pydantic_agent") async def main(): result = await agent.run("Hello!") print(result.output) asyncio.run(main()) ``` ## Different Providers Pydantic AI supports multiple providers via model strings. Registration is identical regardless of provider. ```python {{ title: 'OpenAI' }} agent = Agent("openai:gpt-4o-mini") mem = Memori(conn=SessionLocal).llm.register(agent) ``` ```python {{ title: 'Anthropic' }} agent = Agent("anthropic:claude-3-5-sonnet-20241022") mem = Memori(conn=SessionLocal).llm.register(agent) ``` ```python {{ title: 'Google Gemini' }} agent = Agent("google-gla:gemini-2.0-flash-exp") mem = Memori(conn=SessionLocal).llm.register(agent) ``` ## Supported Modes | Mode | Method | | ------------ | --------------------------- | | **Sync** | `agent.run_sync()` | | **Async** | `await agent.run()` | | **Streamed** | Via agent streaming support | ================================================ FILE: docs/memori-byodb/llm/xai-grok.mdx ================================================ --- title: xAI Grok description: Using Memori with xAI Grok models via the OpenAI-compatible API and Memori BYODB. --- # xAI Grok xAI provides an OpenAI-compatible API. Use the `openai` Python package with `base_url="https://api.x.ai/v1"` — no special adapter needed. Want a zero-setup option? The Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ## Quick Start ```python {{ title: 'Sync' }} import os from memori import Memori from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI( base_url="https://api.x.ai/v1", api_key=os.getenv("XAI_API_KEY") ) mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="grok_assistant") response = client.chat.completions.create( model="grok-2-latest", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```python {{ title: 'Async' }} import os, asyncio from memori import Memori from openai import AsyncOpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = AsyncOpenAI( base_url="https://api.x.ai/v1", api_key=os.getenv("XAI_API_KEY") ) mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="grok_assistant") async def main(): response = await client.chat.completions.create( model="grok-2-latest", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} import os from memori import Memori from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) client = OpenAI( base_url="https://api.x.ai/v1", api_key=os.getenv("XAI_API_KEY") ) mem = Memori(conn=SessionLocal).llm.register(client) mem.config.storage.build() mem.attribution(entity_id="user_123", process_id="grok_assistant") stream = client.chat.completions.create( model="grok-2-latest", messages=[{"role": "user", "content": "Hello!"}], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` ## Supported Modes | Mode | Method | | ------------ | ---------------------------------------- | | **Sync** | `client.chat.completions.create()` | | **Async** | `await client.chat.completions.create()` | | **Streamed** | `stream=True` parameter | ================================================ FILE: docs/memori-byodb/support/faq.mdx ================================================ --- title: FAQ description: Frequently asked questions about Memori open source. --- # FAQ Want a zero-setup option? Try Memori Cloud at [app.memorilabs.ai](https://app.memorilabs.ai). ### What is Memori? Memori is a memory layer for LLM applications, agents, and copilots. It continuously captures interactions, extracts structured knowledge, and intelligently ranks, decays, and retrieves the relevant memories. So your AI remembers the right things at the right time across every session. ### Memori Cloud vs Memori BYODB? **Memori Cloud** — managed storage, dashboard UI, just add an API key. **Memori BYODB** — bring your own database, full control. Both use the same Python SDK. ### Which databases are supported? SQLite, PostgreSQL, MySQL, MariaDB, Oracle, MongoDB, CockroachDB, and OceanBase. Managed providers like Neon, Supabase, and AWS RDS/Aurora are supported through compatible PostgreSQL/MySQL engines. See the [Database guides](/docs/memori-byodb/databases/overview). ### Do I need SQLAlchemy to use BYODB? No. SQLAlchemy is one option, but not a requirement. Memori also supports direct DB-API 2.0 connection factories, Django ORM integration, and MongoDB database callables. ### Which LLM providers work? OpenAI, Anthropic, Google Gemini, xAI Grok, Deepseek, Nebius AI Studio, AWS Bedrock, LangChain, Agno, and Pydantic AI. All support sync, async, and streaming. See the [LLM guides](/docs/memori-byodb/llm/overview). ### Do I need a Memori API key? No. The open-source version works without one. An API key only unlocks higher Advanced Augmentation quotas (100 free without key, 5,000/month with a free key). ### Do I need to manage infrastructure? Yes — you run your own database. For the simplest setup, use SQLite (single file, no server). For production, PostgreSQL is recommended. ### Does it support async? Yes. All providers support async mode out of the box. Just use your provider's async client. ### How does augmentation work? It runs in the background after each conversation and extracts facts, preferences, and relationships while minimizing impact on your LLM response path. See [Advanced Augmentation](/docs/memori-byodb/concepts/advanced-augmentation). ### Can I use multiple LLM providers? Yes. Register multiple clients on the same Memori instance. Memories are shared across providers since they're linked to entities, not providers. ### Can I migrate between Memori Cloud and BYODB? Yes. Both use the same SDK — just swap `conn=SessionLocal` for `api_key="..."`. Existing local memories don't auto-transfer though. ### What is `augmentation.wait()`? It blocks until background augmentation finishes. Only needed in short-lived scripts that might exit before processing completes. ### Still have questions? - [Troubleshooting](/docs/memori-byodb/support/troubleshooting) guide - [GitHub Issues](https://github.com/MemoriLabs/Memori/issues) - [Discord](https://discord.gg/abD4eGym6v) ================================================ FILE: docs/memori-byodb/support/troubleshooting.mdx ================================================ --- title: Troubleshooting description: Common issues and solutions when using Memori open source. --- # Troubleshooting Quick fixes for the most common Memori issues. ## Installation **`pip install memori` fails** — Requires Python 3.10+. Run `python --version` to check, then `pip install --upgrade pip && pip install memori`. **Missing binary deps** — Install prerequisites first: `pip install numpy>=1.24.0 sentence-transformers>=3.0.0 memori`. ## Database Connection **`No connection factory provided`** — You must pass `conn` when initializing: ```python from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori engine = create_engine("sqlite:///memori.db") SessionLocal = sessionmaker(bind=engine) mem = Memori(conn=SessionLocal) ``` **`Table does not exist`** — Run `mem.config.storage.build()` once after initialization or after upgrading. **Connection pool errors** — Enable pre-ping and recycling: ```python engine = create_engine("postgresql+psycopg2://user:pass@host/db", pool_pre_ping=True, pool_recycle=300) ``` **Connection string formats:** | Database | Format | | ---------- | ---------------------------------------------- | | SQLite | `sqlite:///memori.db` | | PostgreSQL | `postgresql+psycopg2://user:pass@host:5432/db` | | MySQL | `mysql+pymysql://user:pass@host:3306/db` | ## No Memories Being Created 1. **Set attribution** before LLM calls — without it, no memories are stored: ```python mem.attribution(entity_id="user_123", process_id="my_app") ``` 2. **Wait for augmentation** in short-lived scripts: ```python mem.augmentation.wait() ``` 3. **Register your LLM client** — conversations aren't captured without registration: ```python client = OpenAI() mem = Memori(conn=SessionLocal).llm.register(client) ``` ## Recall Returns Empty - Verify `entity_id` matches what was used when memories were created - Wait for augmentation: `mem.augmentation.wait()` - Increase limit: `mem.recall("query", limit=10)` - Lower threshold: `mem.config.recall_relevance_threshold = 0.05` ## Quota Exceeded ``` QuotaExceededError: your IP address is over quota ``` Sign up for a free API key at [app.memorilabs.ai](https://app.memorilabs.ai) — gives 5,000/month (vs 100 without a key). Set it via `export MEMORI_API_KEY="your-key"`. ## Performance **Slow first run** — Memori downloads the embedding model on first use. Pre-download with `python -m memori setup`. **High memory usage** — Reduce embeddings limit: `mem.config.recall_embeddings_limit = 500`. Use PostgreSQL for production. **Network timeouts** — Increase timeout: `mem.config.request_secs_timeout = 10` and retries: `mem.config.request_num_backoff = 10`. ## Debug Logging ```python import logging logging.basicConfig(level=logging.DEBUG, format="%(asctime)s | %(name)s | %(levelname)s | %(message)s") from memori import Memori mem = Memori(conn=SessionLocal, debug_truncate=False) ``` ## Getting Help - [GitHub Issues](https://github.com/MemoriLabs/Memori/issues) - [Discord](https://discord.gg/abD4eGym6v) - [Examples](https://github.com/MemoriLabs/Memori/tree/main/examples) Include your Python version, Memori version (`pip show memori`), database type, and full error trace. ================================================ FILE: docs/memori-cloud/benchmark/experiments.mdx ================================================ --- title: LoCoMo Benchmark Experiments description: These experiments evaluate the quality and accuracy of the memory assets produced by Memori's Advanced Augmentation pipeline. --- # LoCoMo Benchmark Experiments The primary objective of these experiments is to evaluate the quality and accuracy of the memory assets produced by Memori's Advanced Augmentation pipeline. ### Dataset: The LoCoMo Benchmark The primary dataset utilized for benchmarking is the Long Conversation Memory (LoCoMo) dataset. LoCoMo is a rigorous framework engineered to evaluate an AI agent's ability to track, retain, and synthesize information across extensive, multi-session chat histories. Unlike standard QA datasets, LoCoMo challenges models with complex state tracking, temporal reasoning, and the retrieval of subtle user preferences buried deep within noisy, unstructured conversational logs To ensure a fair comparison with other published results on this benchmark, we excluded the adversarial category from the evaluation. ### Evaluating Memory Extraction via Advanced Augmentation To measure the quality of Memori's Advanced Augmentation, all sections of each LoCoMo conversation were processed through the pipeline. Each session produced a set of semantic triples along with a conversation-level summary. The extracted triples were embedded using the Gemma-300 embedding model, enabling efficient semantic retrieval for the benchmark’s question-answering tasks. All generated memories were indexed and stored locally using FAISS to support fast similarity search. The ultimate accuracy of the LLM’s answers serves as a direct reflection of how well the Advanced Augmentation pipeline structured, preserved, and surfaced the relevant facts. ### Answer Generation Each question in the LoCoMo benchmark was answered using GPT-4.1-mini, conditioned on the retrieved triples and their corresponding summaries (the utilized prompt is presented in the appendix). Triples were retrieved using a hybrid search approach that combines cosine similarity over embeddings with BM25 keyword matching. ### Performance Metrics: LLM-as-a-Judge We employ an LLM-as-a-Judge methodology (the utilized prompt is presented in the Appendix), using GPT-4.1-mini as the evaluator. The judge model analyzes the user query, the ground-truth answer, and the generated response to provide a nuanced assessment. ### Token-Driven Cost Analysis Beyond response quality, practical deployment considerations are paramount for enterprise AI applications. We evaluate Memori against traditional architectures (e.g., standard RAG) by systematically measuring system efficiency as a function of context consumption.. The absolute number of tokens added to the LLM prompt is the primary driver of operational costs in conversational AI. We measure the exact number of tokens extracted during retrieval and injected into the prompt context. This metric highlights a critical architectural distinction: while traditional architectures consume massive token budgets by indiscriminately injecting large, raw text chunks or full histories into the prompt, Memori retrieves highly concise, structured memory facts. By minimizing the context footprint, Memori directly curtails API expenditure and optimizes operational economics. ================================================ FILE: docs/memori-cloud/benchmark/overview.mdx ================================================ --- title: LoCoMo Benchmark Overview description: Memori - A Persistent Memory Layer for Efficient, Context-Aware LLM Agents --- # LoCoMo Benchmark Overview As large language models (LLMs) evolve into autonomous agents, persistent memory at the API layer is essential for enabling context-aware behavior across LLMs and multi-session interactions. Existing approaches force vendor lock-in and rely on injecting large volumes of raw conversation into prompts, leading to high token costs and degraded performance. We introduce Memori, a LLM-agnostic persistent memory layer that treats memory as a data structuring problem. Its Advanced Augmentation pipeline converts unstructured dialogue into compact semantic triples and conversation summaries, enabling precise retrieval and coherent reasoning. Evaluated on the LoCoMo benchmark, Memori achieves 81.95% accuracy, outperforming existing memory systems while using only 1,294 tokens per query (\~5% of full context). This results in substantial cost reductions, including 67% fewer tokens than competing approaches and over 20× savings compared to full-context methods. These results show that effective memory in LLM agents depends on structured representations instead of larger context windows, enabling scalable and cost-efficient deployment. Download the Paper → [Memori LoCoMo Benchmark PDF](https://s3.us-east-1.amazonaws.com/files.memorilabs.ai/memori-locomo-benchmark.pdf) ## Introduction In recent years, large language models (LLMs) have rapidly evolved into fully sophisticated AI agents. These foundation-model-powered systems have demonstrated strong performance across diverse domains, including research, software engineering, and scientific discovery, pushing the trajectory toward more general and adaptive intelligence. As the field matures, it is evident that modern agents extend beyond a pure LLM backbone and incorporate additional capabilities such as reasoning, planning, perception, memory, and tool use. Together, these components transform LLMs from static conditional generators into adaptive systems capable of interacting with external environments and improving over time. Large language models (LLMs) have quickly become sophisticated AI agents. These foundation-model-powered systems perform well in research, software engineering, and scientific discovery, driving the move toward general intelligence. Modern agents now go beyond using only LLMs by adding reasoning, planning, perception, memory, and tool use. These components let LLMs act as adaptive systems that interact with their environments and improve over time. Among these capabilities, memory stands out as a foundational pillar. Unlike reasoning or tool use, which are increasingly internalized within model parameters, memory remains largely dependent on external system design. This dependency arises because LLM parameters cannot be updated in real time during deployment. Memory mechanisms, therefore, play a key role in enabling agents to persist information across interactions, adapt to user context, and evolve based on experience. This reliance on external memory is especially apparent from an application perspective: persistent memory is essential. Domains such as personalized assistants, recommendation systems, social simulations, and complex investigative workflows all require agents to retain and reason over historical information. Without memory, these systems behave as stateless responders, repeatedly reprocessing context and failing to build continuity over time. From a broader research perspective, agents’ ability to continually evolve through interaction is central to the pursuit of general intelligence. This capacity is fundamentally grounded in memory Enabling long-term, cross-session, cross-model memory introduces significant challenges. Naively storing and injecting past interactions into the prompt leads to rapidly growing context windows. This increases both cost and instability. As context size grows, models become more prone to overlooking critical information. They may produce inconsistent outputs and suffer from what is commonly referred to as *context rot*, in which relevant information is present but not effectively used. These limitations highlight a key insight: memory in LLM systems is not simply a storage problem, but a structuring problem. The challenge is to transform noisy, unstructured conversational data into representations that are efficient to retrieve. These representations must also be effective for downstream reasoning. Memori implements this as a persistent memory layer that incrementally distills conversational data into structured representations. This process is handled by Advanced Augmentation, a memory creation pipeline that extracts, compresses, and organizes high-signal information from raw interactions for efficient retrieval and downstream use. Through empirical evaluation on the LoCoMo benchmark, we demonstrate that high-quality memory structuring enables strong reasoning performance while dramatically reducing the number of tokens required in the prompt, thereby improving the cost-efficiency and scalability of LLM agents. ## System Architecture As depicted in Figure 1, Memori operates as a decoupled memory layer positioned between the application logic and the underlying LLM. The system integrates via a lightweight **Memori SDK**, seamlessly wrapping existing LLM clients to intercept requests and manage memory natively. !["Memori Cloud System Overview"](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memori-cloud-architecture-detail.webp) The core differentiators of the Memori architecture lie in how it structures unstructured data and how it intelligently retrieves that data for reasoning. ### Advanced Augmentation: Structuring the Unstructured Raw conversation logs are noisy, filled with colloquialisms, pleasantries, self-corrections, and tangential discussions. When these raw, unstructured transcripts are directly chunked and embedded, as is standard in traditional RAG architectures, the resulting vector space becomes heavily cluttered. Direct retrieval from this noisy data is highly inefficient, leading to false positives, contradictory context, and massively inflated token consumption during the generation phase. To solve this, **Advanced Augmentation** functions as an automated cognitive filter. It is a background memory creation pipeline designed to distill raw dialogue into searchable memory assets, shifting the system's memory from mere text storage to an organized knowledge base. * **Semantic Extraction & Triple Generation:** Rather than saving sentences, the pipeline deconstructs dialogue messages into atomic units of knowledge. It actively scans conversations for concrete facts, user preferences, constraints, and evolving attributes, structuring them into semantic triples (subject–predicate–object). Each triple is then linked to the exact conversation in which it was mentioned. This design delivers two key advantages. First, it produces a low-noise, high-signal index that improves vector search retrieval accuracy. Second, it functions as a compression layer. * **Conversation Summarization:** While semantic triples excel at capturing granular, static facts, they inherently strip away the surrounding context. An isolated triple might state what a user prefers, but it lacks the narrative of *why* a decision was made or how a user’s goal evolved throughout a specific interaction. To bridge this gap, the pipeline simultaneously generates Conversation Summaries. These are concise, high-level overviews of specific conversational threads that capture the user’s overarching intent, the dialogue's chronological progression, and the task's implicit context. Because triples are tied to their source, each individual triple can be directly linked to the proper summary of the conversation in which it appears, allowing the system to easily retrieve the background story behind any isolated fact. Advanced Augmentation creates an interconnected, dual-layered memory asset: Triples provide the precise, token-efficient facts needed for exact recall, while Conversation Summaries provide the cohesive narrative flow required for the LLM to understand temporal changes and execute complex reasoning. By linking atomic triples directly to the summaries of the conversations they originated from, the system ensures that granular facts are never divorced from their broader context. ================================================ FILE: docs/memori-cloud/benchmark/results.mdx ================================================ --- title: LoCoMo Benchmark Results description: See how Memori’s Advanced Augmentation performed on the LoCoMo benchmark. Using our LLM-as-a-Judge framework, we evaluated four reasoning categories (Multi-Hop, Temporal, Open-Domain, and Single-Hop) and compared Memori against several memory baselines and a Full-Context ceiling. --- # LoCoMo Benchmark Results This section summarizes how Memori’s Advanced Augmentation performed on the LoCoMo benchmark. Using our LLM-as-a-Judge framework, we evaluated four reasoning categories (Multi-Hop, Temporal, Open-Domain, and Single-Hop) and compared Memori against several memory baselines and a Full-Context ceiling. We also examined the vital tradeoff between output accuracy and token cost efficiency. The results are presented in Table 1\. | Table 1: LLM-as-a-Judge Evaluation Results on the LoCoMo Benchmark | | | | | | | ----- | :---: | :---: | :---: | :---: | :---: | | Method | Single-hop (%) | Multi-hop (%) | Open-domain (%) | Temporal (%) | Overall (%) | | Memori | 87.87 | 72.70 | 63.54 | 80.37 | 81.95 | | Zep | 79.43 | 69.16 | 73.96 | 83.33 | 79.09 | | LangMem | 74.47 | 61.06 | 67.71 | 86.92 | 78.05 | | Mem0 | 62.41 | 57.32 | 44.79 | 66.47 | 62.47 | | Full-Context (Ceiling) | 88.53 | 77.70 | 71.88 | 92.70 | 87.52 | This table compares the factual accuracy and reasoning capabilities of Memori’s Advanced Augmentation assets against state-of-the-art baselines and a full-context ceiling. Memori performance values were computed using the average of three rounds. Graphical representation of Memori's average accuracy along with the standard deviation is presented in Figure 2\. !["Memori's average accuracy along with the standard deviation"](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memori-locomo-benchmark.webp) ## **Overall Performance** As expected, the Full-Context setup achieved the highest score (87.52%). However, passing the entire conversation history into the prompt is fundamentally impractical in production due to prohibitive token costs, uncontrolled context expansion, and context degradation over time. Among retrieval-based systems, Memori achieved a leading overall score of 81.95%, successfully outperforming Zep (79.09%), LangMem (78.05%), and Mem0 (62.47%). This validates the assumption that by structuring unstructured chat logs into semantic triples and summaries, Memori effectively isolates high-signal knowledge. This structured memory design significantly narrows the gap to the Full-Context ceiling while keeping context windows highly manageable and operational token costs low. ## **Performance by Category** Analyzing the results across reasoning categories highlights the specific strengths of Memori's extracted memory assets: * **Single-Hop Reasoning (87.87%):** Memori excels in direct fact retrieval, outperforming both LangMem (74.47%) and Zep (79.43%). By minimizing conversational noise and structuring data cleanly, the LLM is fed exact, undeniable facts, minimizing token consumption while maximizing direct recall. * **Temporal Reasoning (80.37%):** Memori outperforms Mem0 (66.47%) but trails LangMem (86.92%) and Zep (83.33%) in temporal tracking. Isolated semantic triples capture static facts but often miss the temporal context needed to identify changes in user states or preferences across sessions. Memori’s summaries help rebuild this timeline, but the results show it needs better temporal reasoning. * **Multi-Hop Reasoning (72.70%):** Memori performs strongly when asked to connect disparate pieces of information, outperforming Zep (69.16%) and trailing LangMem (61.06%) by a narrow margin. The combination of precise triples and cohesive summaries provides the necessary backdrop that helps the LLM connect isolated facts without needing the entire conversational transcript injected into the prompt. * **Open-Domain Reasoning (63.54%):** This category remains challenging across all retrieval-based systems. Open-ended questions often lack clear retrieval anchors, making them difficult to match with granular triples. Furthermore, these queries require broad synthesis across massive contexts rather than simple fact extraction. While Memori lags slightly behind LangMem (67.71%) here, it is important to note that improving open-domain scores typically requires retrieving significantly larger chunks of text, which actively works against the system's core operational goal: strictly minimizing the number of tokens added to the context to control API costs. ## **Token Usage and Cost Efficiency** Traditional memory architectures and standard RAG setups often rely on retrieving raw, uncompressed text chunks. This indiscriminately injects conversational noise and redundant dialogue into the prompt, consuming context limits and inflating API bills. Memori’s Advanced Augmentation pipeline completely bypasses this inefficiency by acting as an intelligent cognitive filter. Rather than retrieving raw text, it compresses chat logs into structured representations, including dense semantic triples and concise conversation-level summaries. As a result, only high-signal, structured information is passed to the LLM, minimizing context overhead while preserving relevant context. | Table 2: Token Usage and Cost Efficiency | | | | | ----- | :---: | :---: | :---: | | Method | Added Tokens to Context (mean) | Context Cost ($) | Context Footprint (%) | | **Memori** | **1,294** | **0.001035** | **4.97** | | Full-context | 26,031 | 0.020825 | 100.00 | | Mem0 | 1,764 | 0.001411 | 6.78 | | Zep | 3,911 | 0.003129 | 15.02 | This table analyzes the operational efficiency of each method by measuring the absolute number of tokens added to the context and the resulting cost per query. Costs are computed based on current gpt-4.1-mini pricing: $0.8 per 1M tokens. Memori requires an average of only 1,294 tokens to ground each LLM response. This token footprint represents just 4.97% of the full conversational context, while achieving the 81.95% overall accuracy detailed in the previous section. When compared to competing memory frameworks, the operational advantages become even more pronounced: * **Compared to Zep (3,911 tokens):** Memori reduces the prompt size by roughly 67% per query, directly cutting API inference costs by the same margin, while simultaneously delivering a higher accuracy score (81.95% vs. 79.09%). * **Compared to the Full-Context approach (26,031 tokens):** Passing the entire history is financially unsustainable for persistent agents, costing over 20 times more per turn than Memori. Furthermore, repeatedly injecting 26K+ tokens drastically increases the risk of "lost in the middle" hallucinations. ## **Conclusion** For LLM agents to scale in production, persistent memory must address two fundamental challenges: context degradation and rapidly increasing token costs. Memori approaches this not as a storage issue, but as a data structuring problem. Through its Advanced Augmentation memory creation pipeline, Memori transforms noisy conversational logs into compact, high-signal representations, combining precise semantic triples with coherent conversation-level summaries. This dual representation enables accurate fact retrieval alongside strong temporal and contextual reasoning, without inflating the prompt with unnecessary tokens. Our evaluation on the LoCoMo benchmark demonstrates the effectiveness of this approach: * **High-Quality Reasoning:** Memori achieves state-of-the-art performance among retrieval-based systems, with particularly strong gains in temporal and single-hop reasoning \- highlighting the impact of structured memory on reasoning fidelity. * **Minimal Context Footprint:** Responses are grounded using a small fraction of the original conversation, showing that well-structured memory can replace large, unfiltered context without sacrificing accuracy. * **Cost-Efficient Scaling:** By significantly reducing the number of tokens injected into the prompt, Memori directly lowers inference costs and enables sustainable deployment of long-running agents. These results highlight a fundamental shift in memory design for LLM systems: performance is determined not by how much context is used, but by the quality of its structure. Memori eliminates the traditional tradeoff between reasoning quality and operational cost. By delivering accurate, cross-session recall with a compact context footprint, it provides a practical and scalable foundation for deploying persistent AI agents in real-world environments. ================================================ FILE: docs/memori-cloud/concepts/advanced-augmentation.mdx ================================================ --- title: Advanced Augmentation description: How Memori's Advanced Augmentation engine extracts structured facts, preferences, and knowledge from your AI conversations — all managed in Memori Cloud. --- # Advanced Augmentation Advanced Augmentation is the AI engine inside Memori Cloud that turns raw conversations into structured, searchable memories. It runs asynchronously in the background to minimize impact on your response path. ## What It Does When your application has a conversation through a Memori-wrapped LLM client, the augmentation engine: 1. Reads the full conversation (user messages and AI responses) 2. Identifies facts, preferences, skills, and attributes 3. Extracts semantic triples (subject-predicate-object relationships) 4. Generates vector embeddings for semantic search 5. Stores everything in your managed memory space No extra code required — just initialize Memori and set attribution. ## How It Works The augmentation flow is fully asynchronous and designed to avoid blocking your main request path. 1. Your app makes an LLM call through the wrapped client 2. Memori returns the response immediately 3. In the background, the conversation is queued for processing 4. The augmentation engine extracts structured memories 5. Memories are stored in Memori Cloud for future recall ```python {{ title: 'Python' }} from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") # This returns immediately — no augmentation delay response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "I love hiking in the mountains."} ] ) print(response.choices[0].message.content) ``` ```typescript {{ title: 'TypeScript' }} import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const client = new OpenAI(); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'my_agent'); // This returns immediately — no augmentation delay const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [ { role: 'user', content: 'I love hiking in the mountains.' }, ], }); console.log(response.choices[0].message.content); ``` ## Extraction Types | Type | What it captures | Scope | | ---------------------- | ------------------------------------------------------- | ------------------------------------ | | **Facts** | Objective information with vector embeddings | Per entity — shared across processes | | **Preferences** | User choices, opinions, and tastes | Per entity | | **Skills & Knowledge** | Abilities and expertise levels | Per entity | | **Attributes** | Process-level information about what your agent handles | Per process | ## Semantic Triples Advanced Augmentation uses named-entity recognition to extract semantic triples (subject, predicate, object). These form the building blocks of the [Knowledge Graph](/docs/memori-cloud/concepts/knowledge-graph). **Example** — from _"My favorite database is PostgreSQL and I use it with FastAPI"_: | Subject | Predicate | Object | | ------- | ----------------- | -------------------- | | user | favorite_database | PostgreSQL | | user | uses | FastAPI | | user | uses_with | PostgreSQL + FastAPI | Memori automatically deduplicates triples — if the same fact is mentioned multiple times, it increments the mention count and updates the timestamp. ## Context Recall When a query is sent to an LLM through a wrapped client, Memori automatically: 1. Intercepts the outbound LLM call 2. Uses semantic search to find entity facts matching the query 3. Ranks facts by vector similarity 4. Injects the most relevant facts into the system prompt 5. Forwards the enriched request to the LLM provider For Memori to provide all Advanced Augmentation capabilities, attribution must be set before making LLM calls. Without attribution, Memori cannot create or recall memories. ================================================ FILE: docs/memori-cloud/concepts/architecture.mdx ================================================ --- title: Architecture description: Understand how Memori's Cloud platform is designed — from your app to Memori Cloud, with managed storage, augmentation, and recall. --- # Architecture Memori Cloud is a managed memory platform for AI applications. Connect your LLM client, set attribution, and Memori handles the rest — storage, augmentation, knowledge graph construction, and recall. ## System Overview !["Memori Cloud System Overview"](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memori-cloud-architecture-detail.webp) ## Core Components ### Your Application Your code and existing LLM client. It sends requests through the Memori SDK and receives model responses as usual. ### Memori SDK The integration layer between your app and Memori Cloud. It provides LLM wrappers, attribution, and the Recall API. ### Memori Cloud The managed backend that processes captured conversations and powers storage, augmentation, and recall services. ### Managed Storage Stores conversations, sessions, and facts for each attribution scope. ### Advanced Augmentation Processes raw conversation data into structured memory through fact extraction, embeddings, and knowledge graph construction. ### Recall Engine Surfaces the right memories at the right time — semantic search over stored memory, intelligent ranking and decay, and seamless injection of relevant context into every LLM call so your AI stays contextually aware. ## Configuration Setting up Memori requires only your API key and attribution: ```python {{ title: 'Python' }} from memori import Memori from openai import OpenAI # Set MEMORI_API_KEY as an environment variable # export MEMORI_API_KEY="your-memori-api-key" client = OpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") ``` ```typescript {{ title: 'TypeScript' }} import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; // Set MEMORI_API_KEY as an environment variable // export MEMORI_API_KEY="your-memori-api-key" const client = new OpenAI(); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'my_agent'); ``` ## Data Flow 1. **Conversation Capture** — Every LLM call through the wrapped client is captured and sent to Memori Cloud. Your app gets the response immediately. 2. **Attribution Tracking** — Attribution links every conversation to a specific entity and process so memories are properly scoped and indexed. 3. **Augmentation** — After a conversation completes, Memori Cloud processes it asynchronously — extracts facts, generates embeddings, and builds knowledge graph triples. 4. **Recall** — On the next LLM call, Memori embeds the query, performs vector search across the entity's stored facts, and injects the most relevant memories into the context. ================================================ FILE: docs/memori-cloud/concepts/async-patterns.mdx ================================================ --- title: Async Patterns description: Best practices for using Memori with async/await in Python and TypeScript. --- # Async Patterns Memori works with async/await out of the box in both Python and TypeScript. In Python, use `AsyncOpenAI` or `AsyncAnthropic` instead of their sync counterparts — everything else stays the same. TypeScript is natively async. ## When to Use Async | Scenario | Python | TypeScript | Why | | ------------------------ | ------ | ---------- | --------------------------- | | Web servers | Yes | Default | Concurrent request handling | | Chatbots with many users | Yes | Default | Non-blocking I/O | | CLI scripts | No | Default | Sync is simpler in Python | | Jupyter notebooks | No | — | Event loop already running | TypeScript is natively async — all Memori SDK calls return Promises. No special async client or `asyncio` setup is needed. ## Basic Async Setup ```python {{ title: 'Python' }} import asyncio from memori import Memori from openai import AsyncOpenAI client = AsyncOpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="async_agent") async def main(): response = await client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "I prefer async Python."}] ) print(response.choices[0].message.content) asyncio.run(main()) ``` ```typescript {{ title: 'TypeScript' }} import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const client = new OpenAI(); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'async_agent'); const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: 'I prefer TypeScript.' }], }); console.log(response.choices[0].message.content); ``` ## Web Server Example ```python import os from fastapi import FastAPI from pydantic import BaseModel from memori import Memori from openai import AsyncOpenAI app = FastAPI() class ChatRequest(BaseModel): message: str @app.post("/chat/{user_id}") async def chat(user_id: str, req: ChatRequest): client = AsyncOpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori().llm.register(client) mem.attribution(entity_id=user_id, process_id="fastapi_async") response = await client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": req.message}] ) return {"response": response.choices[0].message.content} ``` ```typescript import express from 'express'; import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const app = express(); app.use(express.json()); app.post('/chat/:userId', async (req, res) => { const client = new OpenAI(); const mem = new Memori().llm.register(client); mem.attribution(req.params.userId, 'express_async'); const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: req.body.message }], }); res.json({ response: response.choices[0].message.content }); }); app.listen(3000); ``` ================================================ FILE: docs/memori-cloud/concepts/how-memory-works.mdx ================================================ --- title: How Memori Works description: Understand the core concepts behind Memori — entities, processes, sessions, memory types, attribution, and how recall brings it all together. --- # How Memori Works Memori gives your AI application long-term memory. Instead of forgetting everything after each conversation, your AI can remember facts, preferences, and context across sessions and across different applications. ## Attribution Every memory in Memori is tagged with three dimensions: **who** (entity), **what** (process), and **which conversation** (session). - **Entity (`entity_id`)** — The person, place, or thing generating memories. Typically a user ID (e.g., `"user_alice"`, `"company_acme"`). - **Process (`process_id`)** — The agent, program, or workflow creating memories (e.g., `"support_bot"`, `"code_review_agent"`). - **Session (`session_id`)** — Groups related LLM interactions into a conversation thread. Auto-generated as a UUID by default. The combination of `entity_id` + `process_id` + `session_id` creates a unique memory scope — different users have isolated memories, the same user can have different context in different applications, and each conversation is tracked separately. ```python {{ title: 'Python' }} from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) # Set attribution before any LLM calls mem.attribution( entity_id="user_alice", process_id="support_bot" ) # session_id is auto-generated response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "I prefer dark mode."} ] ) ``` ```typescript {{ title: 'TypeScript' }} import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const client = new OpenAI(); const mem = new Memori().llm.register(client); // Set attribution before any LLM calls mem.attribution('user_alice', 'support_bot'); // session ID is auto-generated const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [ { role: 'user', content: 'I prefer dark mode.' }, ], }); ``` ## Memory Types When you have a conversation through a Memori-wrapped LLM client, Advanced Augmentation extracts structured memories in the background: | Type | What it captures | Example | | ---------------------- | ----------------------------------------- | ----------------------------------------------- | | **Facts** | Objective information with embeddings | "User uses PostgreSQL for production databases" | | **Preferences** | Choices, opinions, and tastes | "Prefers concise answers" | | **Skills & Knowledge** | Abilities and expertise levels | "Experienced with React (5 years)" | | **Attributes** | Process-level information about the agent | "Handles billing and subscription queries" | ## How Recall Works Recall brings stored memories back into your AI conversations. There are two modes. ### Automatic Recall (Default) On every LLM call, Memori automatically: 1. Intercepts the outbound request 2. Uses semantic search to find relevant facts for the current entity 3. Injects the most relevant memories into the system prompt 4. Forwards the enriched request to the LLM No extra code required — it happens transparently. ### Manual Recall Use mem.recall() to retrieve memories explicitly — useful for building custom prompts, displaying memories in a UI, or debugging. ```python {{ title: 'Python' }} from memori import Memori mem = Memori() mem.attribution(entity_id="user_alice", process_id="support_bot") facts = mem.recall("coding preferences", limit=5) for fact in facts: print(f"Fact: {fact.content}") print(f"Score: {fact.similarity:.4f}") ``` ```typescript {{ title: 'TypeScript' }} import { Memori } from '@memorilabs/memori'; const mem = new Memori(); mem.attribution('user_alice', 'support_bot'); const facts = await mem.recall('coding preferences'); for (const fact of facts) { console.log(`Fact: ${fact.content}`); console.log(`Score: ${fact.score.toFixed(4)}`); } ``` Each returned fact includes `id`, `content`, `similarity` (0–1 relevance score), `rank_score`, and `date_created`. ### Recall Configuration Memori uses semantic search (vector similarity) to find relevant facts. You can tune recall behavior with: | Option | Default | Description | | --------------------------------------- | ------- | -------------------------------------------------- | | `mem.config.recall_relevance_threshold` | `0.1` | Minimum similarity score for a fact to be included | | `mem.config.recall_embeddings_limit` | `1000` | Maximum number of embeddings to compare against | ```python {{ title: 'Python' }} # Example: tune recall for broader or narrower results mem.config.recall_relevance_threshold = 0.05 # Lower = more results mem.config.recall_embeddings_limit = 500 # Reduce for lower memory usage ``` ```typescript {{ title: 'TypeScript' }} // Example: tune recall for broader or narrower results mem.config.recallRelevanceThreshold = 0.05; // Lower = more results ``` ## Memory Lifecycle !["Memori Lifecycle"](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memori-lifecycle.webp) 1. **Conversation** — Your user talks to your AI through the wrapped LLM client 2. **Capture** — Memori intercepts and stores the raw conversation 3. **Augmentation** — Advanced Augmentation processes the conversation asynchronously, extracting structured memories 4. **Extraction** — Facts, preferences, skills, and attributes are identified 5. **Storage** — Extracted memories are stored in Memori Cloud with vector embeddings 6. **Recall** — On the next LLM call, relevant memories are retrieved and injected into context ================================================ FILE: docs/memori-cloud/concepts/knowledge-graph.mdx ================================================ --- title: Knowledge Graph description: How Memori automatically builds a knowledge graph from your AI conversations using semantic triples, and how to query it through the Recall API. --- # Knowledge Graph Memori automatically builds a knowledge graph from your AI conversations. Each time Advanced Augmentation processes a conversation, it extracts structured relationships — semantic triples — and connects them into a graph. This powers richer recall and gives your AI deeper understanding of each user. ## How It Works 1. **Conversation captured** — Your user talks to your AI through the Memori-wrapped LLM client 2. **Augmentation processes** — Memori Cloud analyzes the conversation in the background 3. **NER extraction** — Named-entity recognition identifies key entities and relationships 4. **Triple creation** — Relationships are expressed as subject-predicate-object triples 5. **Graph storage** — Triples are stored and deduplicated in the knowledge graph 6. **Recall ready** — The graph is available for semantic search on subsequent LLM calls ![Memori Cloud Graph](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/entities-knowledge-graph.webp) ## Semantic Triples Every fact in the knowledge graph is a semantic triple — a three-part statement: **[Subject]** **[Predicate]** **[Object]**. - "Alice" "prefers" "dark mode" - "PostgreSQL" "is" "a relational database" - "The project" "uses" "FastAPI" ### Example Extraction From _"My favorite database is PostgreSQL and I use it with FastAPI for our REST APIs. I've been using Python for about 8 years"_: | Subject | Predicate | Object | | ------- | ----------------- | -------------------- | | user | favorite_database | PostgreSQL | | user | uses | FastAPI | | user | uses_for | REST APIs | | user | uses_with | PostgreSQL + FastAPI | | user | experience_years | Python (8 years) | Over time, as more conversations happen, the graph grows richer. Memori connects new facts to existing ones, building a comprehensive picture of each entity. ## Visualizing the Graph The Memori Playground at [app.memorilabs.ai](https://app.memorilabs.ai) includes a **Memory Graph Viewer** that shows: | Element | What it shows | | ------------------ | ---------------------------------------------- | | **Nodes** | Subjects and objects from semantic triples | | **Edges** | Predicates (relationships) between nodes | | **Mention counts** | How often a fact was discussed across sessions | | **Timestamps** | When facts were first and last seen | ## Scope The knowledge graph follows the same scoping rules as other memory types: | Aspect | Scope | | -------------- | --------------------------------------------------------------- | | **Triples** | Per entity — shared across all processes | | **Visibility** | All processes for an entity can see and use the graph | | **Growth** | Conversations from any process contribute to the entity's graph | If Alice tells your support bot about PostgreSQL, your code assistant also knows she uses PostgreSQL. ## Querying the Graph The knowledge graph is automatically used during recall. When you call `mem.recall()` or make an LLM call through a wrapped client, Memori searches across both extracted facts and the knowledge graph to find the most relevant context. ================================================ FILE: docs/memori-cloud/concepts/multi-user-support.mdx ================================================ --- title: Multi-User Support description: How Memori isolates memories across users, applications, and sessions so each user gets a personalized experience. --- # Multi-User Support Memori provides built-in multi-user and multi-process isolation through its attribution system. Each combination of entity, process, and session creates an isolated memory space — user A never sees user B's memories, and your support bot has different context than your sales bot. ## Isolation Model !["Memori - Multi User Support"](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memori-multi-user-support.webp) ## What's Shared vs Isolated | Data | Scope | | ------------------- | ------------------------------------------ | | **Facts** | Per entity — shared across all processes | | **Preferences** | Per entity | | **Skills** | Per entity | | **Attributes** | Per process | | **Conversations** | Per entity + process + session | | **Sessions** | Per entity + process (auto-generated UUID) | | **Knowledge Graph** | Per entity | ## Examples ```python from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) # User A's conversations mem.attribution(entity_id="user_alice", process_id="support_bot") response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "I prefer dark mode"}] ) # User B's conversations — completely isolated mem.attribution(entity_id="user_bob", process_id="support_bot") response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What are my preferences?"}] ) # Bob will NOT see Alice's preferences ``` ```typescript import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const client = new OpenAI(); const mem = new Memori().llm.register(client); // User A's conversations mem.attribution('user_alice', 'support_bot'); await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: 'I prefer dark mode' }], }); // User B's conversations — completely isolated mem.attribution('user_bob', 'support_bot'); const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: 'What are my preferences?' }], }); // Bob will NOT see Alice's preferences ``` ```python from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) # Same user, different processes mem.attribution(entity_id="user_alice", process_id="support_bot") response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "I use PostgreSQL for my databases"} ] ) # Switch to a different process for the same user mem.attribution(entity_id="user_alice", process_id="sales_bot") # The sales bot can recall Alice's facts (like "uses PostgreSQL") # because facts are shared across processes for the same entity. response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "What databases do I use?"} ] ) ``` ```typescript import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const client = new OpenAI(); const mem = new Memori().llm.register(client); // Same user, different processes mem.attribution('user_alice', 'support_bot'); await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [ { role: 'user', content: 'I use PostgreSQL for my databases' }, ], }); // Switch to a different process for the same user mem.attribution('user_alice', 'sales_bot'); // The sales bot can recall Alice's facts (like "uses PostgreSQL") // because facts are shared across processes for the same entity. const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [ { role: 'user', content: 'What databases do I use?' }, ], }); ``` ```python from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id="user_alice", process_id="support_bot") # Get the current session ID current_session = mem.config.session_id # Start a new conversation group mem.new_session() # Or restore a previous session mem.set_session(current_session) ``` ```typescript import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const client = new OpenAI(); const mem = new Memori().llm.register(client); mem.attribution('user_alice', 'support_bot'); // Get the current session ID const currentSession = mem.session.id; // Start a new conversation group mem.resetSession(); // Or restore a previous session mem.setSession(currentSession); ``` ## Common Patterns ### Web Application Set the entity ID from the authenticated user. Works with Flask, FastAPI, Django, or any web framework. ```python from memori import Memori from openai import OpenAI def handle_chat(user_id: str, message: str): client = OpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id=user_id, process_id="web_assistant") response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": message}] ) return response.choices[0].message.content ``` ```typescript import express from 'express'; import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const app = express(); app.use(express.json()); app.post('/chat/:userId', async (req, res) => { const client = new OpenAI(); const mem = new Memori().llm.register(client); mem.attribution(req.params.userId, 'web_assistant'); const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: req.body.message }], }); res.json({ response: response.choices[0].message.content }); }); ``` ### Multi-Agent System Give each agent a unique process ID. Facts are shared across agents for the same entity, but each agent maintains its own conversation history. ```python from memori import Memori from openai import OpenAI def create_agent(user_id: str, agent_name: str): client = OpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id=user_id, process_id=agent_name) return client # Three agents, one user, shared facts support = create_agent("user_alice", "support_agent") sales = create_agent("user_alice", "sales_agent") onboard = create_agent("user_alice", "onboarding_agent") ``` ```typescript import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; function createAgent(userId: string, agentName: string) { const client = new OpenAI(); const mem = new Memori().llm.register(client); mem.attribution(userId, agentName); return client; } // Three agents, one user, shared facts const support = createAgent('user_alice', 'support_agent'); const sales = createAgent('user_alice', 'sales_agent'); const onboard = createAgent('user_alice', 'onboarding_agent'); ``` ================================================ FILE: docs/memori-cloud/dashboard/analytics.mdx ================================================ --- title: Analytics description: Understand memory volume, retrieval performance, usage quotas, and top subjects in Memori Cloud analytics. --- # Analytics Use **Analytics** in [app.memorilabs.ai](https://app.memorilabs.ai) to monitor memory creation, recall activity, traffic trends, and quota usage. Use the date selector (for example **Last 7 days**) to scope metrics, and check **Last refreshed** for data freshness. **What Each Data Point Means:** | Data point | Meaning | | --------------------------------- | ------------------------------------------------------------------------------------------ | | **Unique memories** | Total number of unique memory records in the selected time range. | | **Entities** | Number of unique extracted entities attached to memories in the selected range. | | **Recalls** | Number of memory retrieval events served in the selected range. | | **Cache hit rate** | Percentage of recall requests served from cache instead of a full retrieval path. | | **Total conversations** | Number of conversation threads processed by Memori. | | **Total sessions** | Number of distinct sessions in the selected range. | | **Trend chart** | Time-series trend for the active activity metric across the selected period. | | **Monthly quota resets on ...** | Date when monthly limits reset for your organization. | | **Memories created** (usage bar) | Current month created-memory usage vs monthly plan limit. | | **Memories recalled** (usage bar) | Current month recall usage vs monthly plan limit. | | **Top subjects** | Ranked subjects/entities by memory volume, with entity type tags and relative volume bars. | ![Analytics page with KPI cards, activity chart, monthly usage, and top subjects](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/analytics-overview.webp) ## Read the Page in Order 1. Start with the top KPI cards to understand overall volume and retrieval load. 2. Check **Activity and performance** for traffic trends and directional changes (up/down percentages). 3. Review **Monthly usage** to confirm distance from quota limits. 4. Use **Top subjects** to see which entities dominate memory creation. ## Interpreting Directional Changes - Green upward changes indicate growth versus the comparison period. - Red downward changes indicate decreases versus the comparison period. - Combine change percentages with absolute counts to avoid overreacting to small-volume shifts. ================================================ FILE: docs/memori-cloud/dashboard/api-keys.mdx ================================================ --- title: API Keys description: Create and manage Memori API keys from the dashboard. --- # API Keys Use the **API keys** page in [app.memorilabs.ai](https://app.memorilabs.ai) to create and manage keys for your organization. ## Overview The table includes: - **Name** - **Key** (masked) - **Created at** - **Last used** If no keys are connected yet, the table shows **No connected APIs.** ## Create a New API Key 1. Open **API keys** in the dashboard sidebar. 2. Click **Create API key**. 3. In **New API key**, enter a value in **Name**. 4. Click **Create key**. ![API keys page with empty table state and New API key modal](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/new-api-key-modal.webp) ## Copy the Key (Shown Once) After creating a key, the **Your API key** modal appears with the full key value and a **Copy** button. The full key value is shown only once. Copy it and store it securely before clicking **Continue**. 1. Click **Copy**. 2. Click **Continue** to return to the API keys table. ![API keys page with table row and Your API key modal for one-time copy](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/your-api-key-modal.webp) ## Security Best Practices Never commit API keys to version control. Treat API keys like passwords. - Store keys in environment variables or a secure secrets manager - Rotate keys immediately if you suspect exposure - Use separate keys for development, staging, and production ================================================ FILE: docs/memori-cloud/dashboard/memories.mdx ================================================ --- title: Memories description: Explore memories and entities in table and graph views, then drill into details, associations, and retrieval behavior. --- # Memories Use **Memories** in [app.memorilabs.ai](https://app.memorilabs.ai) to inspect all memories created by your configured applications and agents. The page supports two ways to explore data: - **Table** view for row-based inspection - **Knowledge graph** view for relationship-first exploration ## Start in Table View The default table flow uses two tabs: - **Memories** for individual memory rows - **Subjects** for extracted entities/subjects and their counts Use **Semantic search** to find relevant rows quickly, then open the view menu to switch between **Table** and **Knowledge graph**. ### Memories tab - **Summary**: The memory text captured from conversations - **Entity ID**: The associated entity for that memory - **Recalls**: Number of times this memory was retrieved - **# of mentions**: How often this memory was mentioned in context - **Last retrieved**: Most recent retrieval time - **Created at**: When the memory was first created ![Memories table view with semantic search and table or knowledge graph switcher](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memories-overview-table.webp) ### Subjects tab - **Subject**: Extracted subject/entity value - **Type**: Entity class (for example `GPE`, `PRODUCT`, `ORGANIZATION`, `MONEY`) - **Memory count**: Number of memories linked to that subject - **Created at**: Subject creation date The **All types** filter helps narrow the table by subject type. ![Subjects table view with all types filter and subject memory counts](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memories-detail-drawer.webp) ## Open Memory Details 1. In **Memories**, click any row. 2. Review the **Memory details** panel. 3. Use the embedded **Subjects** graph and list to inspect associations. The **Details** section includes: - **Memory ID** - **Entity ID** - **Importance score** - **Created at** - **Last retrieved** - **Recalls** - **# of mentions** ![Memory details drawer showing details fields and associated subjects graph](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memories-subjects-table.webp) ## Switch to Knowledge Graph 1. Open the view switcher and select **Knowledge graph**. 2. Use the **Entities** tab to explore nodes and relationships. 3. Filter with **Search memories** and time range (for example **All time**). ![Entities knowledge graph view with search and time filter](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/entities-knowledge-graph.webp) ## Recommended Exploration Workflow 1. Start in **Memories** table to validate recency and retrieval metrics. 2. Open **Memory details** to verify entity associations and subject links. 3. Switch to **Knowledge graph** to follow cross-entity relationships. 4. Open an entity and trace back through **Associated memories**. ================================================ FILE: docs/memori-cloud/dashboard/overview.mdx ================================================ --- title: Dashboard Overview description: An overview of the Memori Cloud dashboard at app.memorilabs.ai for managing API keys, testing memories, and monitoring usage. --- # Dashboard Overview The Memori Cloud dashboard at [app.memorilabs.ai](https://app.memorilabs.ai) is where you manage API keys, test memory in real-time, and monitor usage. ## Dashboard Sections ### API Keys Create and manage your Memori API keys. The free plan unlocks 5,000 memories created and 15,000 recalled per month. [Manage API Keys](/docs/memori-cloud/dashboard/api-keys). ### Playground Chat with an AI and watch memories get extracted in real-time. The fastest way to understand how Memori works. [Try the Playground](/docs/memori-cloud/dashboard/playground). ## Memory Usage | Plan | Memories Created / month | Memories Recalled / month | Requirements | | ----------------------- | ------------------------ | ------------------------- | ---------------------------------------------------------- | | **Free (with API key)** | 5,000 | 15,000 | [Sign up and create an API key](https://app.memorilabs.ai) | | **Starter** | 25,000 | 100,000 | [Sign up and subscribe](/pricing) | | **Pro** | 150,000 | 500,000 | [Sign up and subscribe](/pricing) | | **Enterprise** | Custom | Custom | [Contact us](/pricing) | ## Getting Started 1. **Sign up** — Go to [app.memorilabs.ai](https://app.memorilabs.ai) and create an account 2. **Create an API key** — Navigate to API Keys and generate your first key 3. **Try the Playground** — Chat with an AI and see memories being extracted live 4. **Integrate** — Copy your API key into your application code ```python import os from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="my-agent") ``` ================================================ FILE: docs/memori-cloud/dashboard/playground.mdx ================================================ --- title: Playground description: Use Playground to validate memory extraction end to end with chat, extracted memories, and the memory graph. --- # Playground Use **Playground** in [app.memorilabs.ai](https://app.memorilabs.ai) to test how conversation turns become extracted memories and relationship graphs. ## Playground Layout The screen is split into three working areas: - Left navigation: **Memories**, **API keys**, **Playground**, **Documentation**, **Settings** - Center panel: **Playground** conversation and composer - Right column: **Memory usage**, **Extracted memories** with **Clean**, and **Memory graph** with expand This layout keeps input, extracted output, and relationship visualization on one page so you can validate memory behavior quickly. ![Playground overview with conversation, memory usage, extracted memories, and memory graph](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/playground-memory-graph-expanded.webp) ## Core Workflow 1. Chat in **Playground** with user details or preferences. 2. Watch new records appear in **Extracted memories**. 3. Verify those same facts and links in **Memory graph**. 4. Use the graph expand control for deeper inspection when needed. ## Memory Graph **Memory graph** is a relationship view over the same memory set shown in **Extracted memories**. - Nodes represent subjects and entities (for example: `User`, `Emma`, `Queens`, `NYC Juniors`). - Edges show relation text (for example: `has name`, `lives in`, `plays as`). - Use the expand icon to open a larger graph canvas for graph-first review. ![Memory graph card in Playground](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/playground-overview.webp) ## Extracted Memories and Persistence The **Extracted memories** list shows normalized memory statements such as: - `Northeastern University located in Boston, Massachusetts` - `User plays as libero` - `User played club volleyball growing up for NYC Juniors` These memories are persisted as records in the database and are generated asynchronously through advanced augmentation after conversation turns are processed. ![Extracted memories panel with Clean action](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/playground-extracted-memories.webp) ================================================ FILE: docs/memori-cloud/getting-started/installation.mdx ================================================ --- title: Installation description: Install Memori and set up your API key for the Memori Cloud. --- # Installation Get Memori installed and connected to Memori Cloud in a few steps. ## Python ### Install Memori ```bash {{ title: 'pip' }} pip install memori ``` ```bash {{ title: 'poetry' }} poetry add memori ``` ```bash {{ title: 'uv' }} uv add memori ``` ### Install Your LLM Provider Install the SDK for your preferred LLM provider: ```bash {{ title: 'OpenAI' }} pip install openai ``` ```bash {{ title: 'Anthropic' }} pip install anthropic ``` ```bash {{ title: 'Google Gemini' }} pip install google-genai ``` ```bash {{ title: 'xAI Grok' }} pip install openai ``` ```bash {{ title: 'Nebius AI Studio' }} pip install openai ``` ```bash {{ title: 'AWS Bedrock' }} pip install langchain-aws ``` ```bash {{ title: 'LangChain' }} pip install langchain-openai ``` ```bash {{ title: 'Pydantic AI' }} pip install pydantic-ai ``` ## TypeScript The TypeScript SDK is for Memori Cloud only. For self-hosted setups, use the [Python SDK](/docs/memori-byodb/getting-started/installation). ### Install Memori ```bash {{ title: 'npm' }} npm install @memorilabs/memori ``` ```bash {{ title: 'yarn' }} yarn add @memorilabs/memori ``` ```bash {{ title: 'pnpm' }} pnpm add @memorilabs/memori ``` ### Install Your LLM Provider ```bash {{ title: 'OpenAI' }} npm install openai ``` ```bash {{ title: 'Anthropic' }} npm install @anthropic-ai/sdk ``` ```bash {{ title: 'Google Gemini' }} npm install @google/genai ``` ## Set Your API Key Sign up at [app.memorilabs.ai](https://app.memorilabs.ai) to get your Memori API key. The SDK reads it automatically from the environment: Your Memori API key from [app.memorilabs.ai](https://app.memorilabs.ai) connects your application to Memori Cloud. Set this as an environment variable. The Memori SDK reads it automatically on initialization. ```bash {{ title: 'Environment Variable' }} export MEMORI_API_KEY="your-memori-api-key" ``` ```bash {{ title: '.env File' }} # Create a .env file in your project root MEMORI_API_KEY=your-memori-api-key OPENAI_API_KEY=your-openai-api-key ``` ## Set Your LLM Provider Key You'll also need an API key for your LLM provider: ```bash # OpenAI export OPENAI_API_KEY="your-openai-key" # Anthropic export ANTHROPIC_API_KEY="your-anthropic-key" # Google Gemini export GOOGLE_API_KEY="your-google-key" ``` ## Verify Installation ```bash {{ title: 'Python' }} pip show memori ``` ```bash {{ title: 'TypeScript' }} npx tsx -e "import { Memori } from '@memorilabs/memori'; console.log('Memori installed successfully')" ``` ================================================ FILE: docs/memori-cloud/getting-started/python-quickstart.mdx ================================================ --- title: Python SDK Quickstart description: Get started with Memori Cloud in under 3 minutes. --- # Python SDK Quickstart Get started with Memori in under 3 minutes using Python. No database setup required — just your Memori API key and your favorite LLM provider. In this example, we'll use Memori with OpenAI. Check out our [Integration guides](/docs/memori-cloud/llm/overview) for other LLM providers and frameworks. ## Prerequisites - Python 3.10 or higher - An OpenAI API key - A Memori API key from [app.memorilabs.ai](https://app.memorilabs.ai) ## Step 1: Install Libraries Install Memori and the OpenAI SDK: ```bash {{ title: 'pip' }} pip install memori openai ``` ```bash {{ title: 'poetry' }} poetry add memori openai ``` ```bash {{ title: 'uv' }} uv add memori openai ``` ## Step 2: Set Environment Variables Set your API keys as environment variables: ```bash export MEMORI_API_KEY="your-memori-api-key" export OPENAI_API_KEY="your-openai-api-key" ``` ## Step 3: Run Your First Memori Application Create a new Python file `quickstart.py` and add the following code: ### Setup & Configuration Import libraries and initialize Memori with your API key and OpenAI client. - Memori reads your `MEMORI_API_KEY` from the environment automatically - `llm.register()` wraps your LLM client for automatic memory capture - `attribution()` links memories to a specific user and process ```python import os from memori import Memori from openai import OpenAI client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="test-ai-agent") ``` ### First Conversation Tell the LLM a fact about yourself. Memori automatically captures the conversation and processes it through Advanced Augmentation. ```python response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "My favorite color is blue."} ] ) print(response.choices[0].message.content + "\n") ``` ### Memory Recall Create a completely new client and Memori instance — no prior context carried over. Memori automatically injects relevant facts via semantic search, so the second response should correctly recall your favorite color. This verifies recall from stored memories, not prior in-memory message history. ```python client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="test-ai-agent") response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "What's my favorite color?"} ] ) print(response.choices[0].message.content + "\n") ``` ## Step 4: Run the Application Execute your Python file: ```bash python quickstart.py ``` You should see the AI respond to both questions, with the second response correctly recalling that your favorite color is blue! ## Step 5: Check the Dashboard Visit [app.memorilabs.ai](https://app.memorilabs.ai) to see your memory usage and try the Graph Explorer to interact with your memories visually. ================================================ FILE: docs/memori-cloud/getting-started/typescript-quickstart.mdx ================================================ --- title: Typescript SDK Quickstart description: Get started with Memori Cloud and the TypeScript SDK in under 3 minutes. --- # Typescript SDK Quickstart Get started with Memori in under 3 minutes using TypeScript. No database setup required — just your Memori API key and your favorite LLM provider. In this example, we'll use Memori with OpenAI. Check out our [Integration guides](/docs/memori-cloud/llm/overview) for other LLM providers. ## Prerequisites - Node.js 18 or higher - An OpenAI API key - A Memori API key from [app.memorilabs.ai](https://app.memorilabs.ai) ## Step 1: Install Libraries Install Memori and the OpenAI SDK: ```bash {{ title: 'npm' }} npm install @memorilabs/memori openai ``` ```bash {{ title: 'yarn' }} yarn add @memorilabs/memori openai ``` ```bash {{ title: 'pnpm' }} pnpm add @memorilabs/memori openai ``` ## Step 2: Set Environment Variables Set your API keys as environment variables: ```bash export MEMORI_API_KEY="your-memori-api-key" export OPENAI_API_KEY="your-openai-api-key" ``` ## Step 3: Run Your First Memori Application Create a new file `quickstart.ts` and add the following code: ### Setup & Configuration Import libraries and initialize Memori with your API key and OpenAI client. - Memori reads your `MEMORI_API_KEY` from the environment automatically - `llm.register()` wraps your LLM client for automatic memory capture - `attribution()` links memories to a specific user and process ```typescript import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const client = new OpenAI(); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'test-ai-agent'); ``` ### First Conversation Tell the LLM a fact about yourself. Memori automatically captures the conversation and processes it through Advanced Augmentation. ```typescript const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [ { role: 'user', content: 'My favorite color is blue.' } ], }); console.log(response.choices[0].message.content + '\n'); ``` ### Memory Recall Create a completely new client and Memori instance — no prior context carried over. Memori automatically injects relevant facts via semantic search, so the second response should correctly recall your favorite color. This verifies recall from stored memories, not prior in-memory message history. ```typescript const client2 = new OpenAI(); const mem2 = new Memori().llm.register(client2); mem2.attribution('user_123', 'test-ai-agent'); const response2 = await client2.chat.completions.create({ model: 'gpt-4o-mini', messages: [ { role: 'user', content: "What's my favorite color?" } ], }); console.log(response2.choices[0].message.content + '\n'); ``` ## Step 4: Run the Application Execute your TypeScript file: ```bash npx tsx quickstart.ts ``` You should see the AI respond to both questions, with the second response correctly recalling that your favorite color is blue! ## Step 5: Check the Dashboard Visit [app.memorilabs.ai](https://app.memorilabs.ai) to see your memory usage and try the Graph Explorer to interact with your memories visually. ================================================ FILE: docs/memori-cloud/getting-started/use-cases.mdx ================================================ --- title: Use Cases description: Common use cases and applications for Memori. --- # Use Cases Memori is designed for any application where AI agents need to remember context across conversations. Here are the most common use cases. ## Customer Support Chatbots Build support bots that remember customer history, preferences, and past issues. No more "Can you repeat your account number?" — Memori recalls context automatically. **Benefits:** - Remember customer preferences and history - Recall previous support tickets and resolutions - Personalize responses based on past interactions - Track issues across multiple sessions ```python {{ title: 'Python' }} from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) # Each customer gets their own memory space mem.attribution( entity_id="customer_456", process_id="support_bot" ) # Memori automatically recalls relevant context response = client.chat.completions.create( model="gpt-4o-mini", messages=[{ "role": "user", "content": "I'm having that issue again" }] ) # Memori injects: "Customer previously reported # login timeout issues on 2024-01-15" ``` ```typescript {{ title: 'TypeScript' }} import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const client = new OpenAI(); const mem = new Memori().llm.register(client); // Each customer gets their own memory space mem.attribution('customer_456', 'support_bot'); // Memori automatically recalls relevant context const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: "I'm having that issue again", }], }); // Memori injects: "Customer previously reported // login timeout issues on 2024-01-15" ``` ## Personalized AI Assistants Create AI assistants that learn and adapt to each user over time. Memori builds a profile of preferences, skills, and context that makes every interaction more relevant. **Benefits:** - Learn coding preferences and tech stack - Remember project context across sessions - Adapt communication style to user preferences - Build long-term user profiles automatically ```python {{ title: 'Python' }} from memori import Memori from anthropic import Anthropic client = Anthropic() mem = Memori().llm.register(client) mem.attribution( entity_id="developer_789", process_id="code_assistant" ) # Over time, Memori learns: # - "Uses Python 3.12 with FastAPI" # - "Prefers type hints and dataclasses" # - "Works on e-commerce platform" response = client.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=[{ "role": "user", "content": "How should I structure this endpoint?" }] ) ``` ```typescript {{ title: 'TypeScript' }} import Anthropic from '@anthropic-ai/sdk'; import { Memori } from '@memorilabs/memori'; const client = new Anthropic(); const mem = new Memori().llm.register(client); mem.attribution('developer_789', 'code_assistant'); // Over time, Memori learns: // - "Uses TypeScript with Express" // - "Prefers strict types and interfaces" // - "Works on e-commerce platform" const response = await client.messages.create({ model: 'claude-sonnet-4-5-20250929', max_tokens: 1024, messages: [{ role: 'user', content: 'How should I structure this endpoint?', }], }); ``` ## Multi-Agent Workflows Coordinate multiple AI agents that share context through Memori. Each agent contributes to a shared memory space while maintaining its own process identity and conversation history. **Benefits:** - Share context between specialized agents - Track which agent contributed what information - Maintain conversation continuity across handoffs - Build collective knowledge graphs ```python {{ title: 'Python' }} from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) # Research agent gathers information mem.attribution( entity_id="project_alpha", process_id="research_agent" ) client.chat.completions.create( model="gpt-4o-mini", messages=[{ "role": "user", "content": "Research competitor pricing" }] ) # Analysis agent recalls research findings mem.attribution( entity_id="project_alpha", process_id="analysis_agent" ) # Memori shares context across agents # for the same entity ``` ```typescript {{ title: 'TypeScript' }} import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const client = new OpenAI(); const mem = new Memori().llm.register(client); // Research agent gathers information mem.attribution('project_alpha', 'research_agent'); await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: 'Research competitor pricing', }], }); // Analysis agent recalls research findings mem.attribution('project_alpha', 'analysis_agent'); // Memori shares context across agents // for the same entity ``` For runnable versions of these examples and more, see the [examples folder](https://github.com/MemoriLabs/Memori/tree/main/examples) on GitHub. ================================================ FILE: docs/memori-cloud/index.mdx ================================================ --- title: Introduction description: Memori gives your AI agents structured, persistent memory — no database setup required. --- # What is Memori? __Memori__ is a memory layer for LLM applications, agents, and copilots. It continuously captures interactions, extracts structured knowledge, and intelligently ranks, decays, and retrieves the relevant memories. So your AI remembers the right things at the right time across every session. ## Why Memori Cloud? With the Memori Cloud platform: [app.memorilabs.ai](https://app.memorilabs.ai), you skip all database configuration. Sign up, get a Memori API key, and start building AI agents with memory in minutes. ### LLM Provider Support OpenAI, Anthropic, Gemini, and Grok (xAI) via direct SDK wrappers. Bedrock is supported via LangChain `ChatBedrock`. OpenAI-compatible providers (Nebius, Deepseek, NVIDIA NIM, Azure OpenAI, and more) work through OpenAI's `base_url` parameter. Supports sync, async, streamed, and unstreamed modes, plus LangChain , Agno, and Pydantic AI. ### Zero Configuration No database setup needed. Connect your LLM client with an API key and start building memories immediately. ### Framework Integration Native support for LangChain, Agno, and Pydantic AI with seamless integration into your existing workflows. ### Advanced Augmentation Background AI processing extracts facts, preferences, and relationships from your conversations automatically. ### Intelligent Recall Intelligent Recall surfaces the right memories at the right time. Memories are ranked by relevance and importance, with intelligent decay so older or less relevant facts recede — so your AI stays contextually aware without clutter. Use manual recall when you need to display memories in your UI, build custom prompts, or debug. See [How Memori Works](/docs/memori-cloud/concepts/how-memory-works#how-recall-works) for automatic vs manual recall and tuning. ```python {{ title: 'Python' }} from memori import Memori from openai import OpenAI # Requires MEMORI_API_KEY and OPENAI_API_KEY in your environment client = OpenAI() mem = Memori().llm.register(client) # Track conversations by user and process mem.attribution(entity_id="user_123", process_id="support_agent") # All conversations automatically persisted and recalled response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "My favorite color is blue."}] ) ``` ```typescript {{ title: 'TypeScript' }} import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; // Requires MEMORI_API_KEY and OPENAI_API_KEY in your environment const client = new OpenAI(); const mem = new Memori().llm.register(client); // Track conversations by user and process mem.attribution('user_123', 'support_agent'); // All conversations automatically persisted and recalled const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: 'My favorite color is blue.' }], }); ``` ## Core Concepts | Concept | Description | Example | | ---------------- | ----------------------------------------------------- | ---------------------------------------- | | **Entity** | Person, place, or thing (like a user) | `entity_id="user_123"` | | **Process** | Your agent, LLM interaction, or program | `process_id="support_agent"` | | **Session** | Groups LLM interactions together | Auto-generated UUID, manually manageable | | **Augmentation** | Background AI enhancement of memories | Auto-runs after wrapped LLM calls | | **Recall** | Retrieve relevant memories from previous interactions | Auto-injects recalled memories | ## Architecture Overview The diagram has three lanes: your app, the Memori SDK, and Memori Cloud. Your app calls the LLM normally, Memori captures context on the response path, and memory processing continues in the background. Synchronous capture: conversation messages are sent to Memori Cloud while your normal LLM flow continues. Recall injection: relevant memories are fetched from managed storage and injected into later prompts. Async augmentation: Memori Cloud extracts facts, preferences, rules, events, and relationships from conversations without blocking your app. !["Memori Cloud architecture"](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/docs/memori-cloud-architecture-detail.webp) ================================================ FILE: docs/memori-cloud/llm/agno.mdx ================================================ --- title: Agno description: Using Memori with Agno agents on Memori Cloud. --- # Agno Memori Cloud integrates with Agno at the model layer. Register your Agno model with `llm.register(...)` and Memori captures `run()`, `arun()`, and streamed responses automatically. TypeScript support for Agno is coming soon. The TypeScript SDK currently supports [OpenAI](/docs/memori-cloud/llm/openai), [Anthropic](/docs/memori-cloud/llm/anthropic), and [Gemini](/docs/memori-cloud/llm/gemini). ## Quick Start ```python {{ title: 'Sync' }} from agno.agent import Agent from agno.models.openai import OpenAIChat from memori import Memori model = OpenAIChat(id="gpt-4o-mini") mem = Memori().llm.register(openai_chat=model) mem.attribution(entity_id="user_123", process_id="agno_agent") agent = Agent( model=model, instructions=["Be helpful and concise"], markdown=True, ) response = agent.run("Hello!", session_id="support-session") print(response.content) ``` ```python {{ title: 'Async' }} import asyncio from agno.agent import Agent from agno.models.openai import OpenAIChat from memori import Memori model = OpenAIChat(id="gpt-4o-mini") mem = Memori().llm.register(openai_chat=model) mem.attribution(entity_id="user_123", process_id="agno_agent") agent = Agent( model=model, instructions=["Be helpful and concise"], markdown=True, ) async def main(): response = await agent.arun("Hello!", session_id="support-session") print(response.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} from agno.agent import Agent from agno.models.openai import OpenAIChat from memori import Memori model = OpenAIChat(id="gpt-4o-mini") mem = Memori().llm.register(openai_chat=model) mem.attribution(entity_id="user_123", process_id="agno_agent") agent = Agent( model=model, instructions=["Be helpful and concise"], markdown=True, ) stream = agent.run("Hello!", session_id="support-session", stream=True) for chunk in stream: if hasattr(chunk, "content") and chunk.content: print(chunk.content, end="") ``` ## Different Providers Agno supports multiple model families. Use the matching registration keyword in Memori. | Package | Model Class | Registration Keyword | | ----------------------- | ------------ | -------------------- | | `agno.models.openai` | `OpenAIChat` | `openai_chat=model` | | `agno.models.anthropic` | `Claude` | `claude=model` | | `agno.models.google` | `Gemini` | `gemini=model` | | `agno.models.xai` | `xAI` | `xai=model` | ```python {{ title: 'OpenAI' }} from agno.models.openai import OpenAIChat model = OpenAIChat(id="gpt-4o-mini") mem = Memori().llm.register(openai_chat=model) ``` ```python {{ title: 'Anthropic' }} from agno.models.anthropic import Claude model = Claude(id="claude-sonnet-4-20250514") mem = Memori().llm.register(claude=model) ``` ```python {{ title: 'Google Gemini' }} from agno.models.google import Gemini model = Gemini(id="gemini-2.0-flash-exp") mem = Memori().llm.register(gemini=model) ``` ```python {{ title: 'xAI' }} from agno.models.xai import xAI model = xAI(id="grok-3") mem = Memori().llm.register(xai=model) ``` ## Supported Modes | Mode | Method | | ------------ | ------------------------ | | **Sync** | `agent.run()` | | **Async** | `await agent.arun()` | | **Streamed** | `agent.run(stream=True)` | ================================================ FILE: docs/memori-cloud/llm/anthropic.mdx ================================================ --- title: Anthropic description: Using Memori with Anthropic Claude models on Memori Cloud. --- # Anthropic Memori Cloud supports all Anthropic Claude models. The `max_tokens` parameter is required for all Anthropic API calls. ## Quick Start ```python {{ title: 'Python' }} from anthropic import Anthropic from memori import Memori client = Anthropic() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="claude_assistant") response = client.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) print(response.content[0].text) ``` ```typescript {{ title: 'TypeScript' }} import Anthropic from '@anthropic-ai/sdk'; import { Memori } from '@memorilabs/memori'; const client = new Anthropic(); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'claude_assistant'); const response = await client.messages.create({ model: 'claude-sonnet-4-5-20250929', max_tokens: 1024, messages: [{ role: 'user', content: 'Hello!' }], }); console.log(response.content[0].text); ``` ## Supported Modes | Mode | Python | TypeScript | | ------------ | -------------------------------- | -------------------------------------- | | **Sync** | `client.messages.create()` | — | | **Async** | `await client.messages.create()` | `await client.messages.create()` | | **Streamed** | `client.messages.stream()` | `stream: true` parameter | ## Additional Modes ### Async (Python) ```python import asyncio from anthropic import AsyncAnthropic from memori import Memori client = AsyncAnthropic() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="claude_assistant") async def main(): response = await client.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) print(response.content[0].text) asyncio.run(main()) ``` ### Streaming ```python {{ title: 'Python' }} from anthropic import Anthropic from memori import Memori client = Anthropic() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="claude_assistant") with client.messages.stream( model="claude-sonnet-4-5-20250929", max_tokens=1024, messages=[{"role": "user", "content": "Hello!"}] ) as stream: for text in stream.text_stream: print(text, end="") ``` ```typescript {{ title: 'TypeScript' }} import Anthropic from '@anthropic-ai/sdk'; import { Memori } from '@memorilabs/memori'; const client = new Anthropic(); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'claude_assistant'); const stream = await client.messages.create({ model: 'claude-sonnet-4-5-20250929', max_tokens: 1024, stream: true, messages: [{ role: 'user', content: 'Hello!' }], }); for await (const event of stream) { if (event.type === 'content_block_delta' && event.delta.type === 'text_delta') { process.stdout.write(event.delta.text); } } ``` ## System Prompts Anthropic supports a top-level `system` parameter separate from the `messages` array. Memori captures both. ```python from anthropic import Anthropic from memori import Memori client = Anthropic() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="claude_assistant") response = client.messages.create( model="claude-sonnet-4-5-20250929", max_tokens=1024, system="You are a helpful coding assistant.", messages=[ {"role": "user", "content": "Explain Python decorators."} ] ) print(response.content[0].text) ``` ### TypeScript ```typescript import Anthropic from '@anthropic-ai/sdk'; import { Memori } from '@memorilabs/memori'; const client = new Anthropic(); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'claude_assistant'); const response = await client.messages.create({ model: 'claude-sonnet-4-5-20250929', max_tokens: 1024, system: 'You are a helpful coding assistant.', messages: [ { role: 'user', content: 'Explain TypeScript generics.' }, ], }); console.log(response.content[0].text); ``` ================================================ FILE: docs/memori-cloud/llm/aws-bedrock.mdx ================================================ --- title: AWS Bedrock description: Using Memori with AWS Bedrock models via the LangChain ChatBedrock adapter on Memori Cloud. --- # AWS Bedrock Memori supports AWS Bedrock through `langchain-aws`. Register using the `chatbedrock` keyword to access Claude, Llama, Mistral, and other Bedrock models. TypeScript support for AWS Bedrock is coming soon. The TypeScript SDK currently supports [OpenAI](/docs/memori-cloud/llm/openai), [Anthropic](/docs/memori-cloud/llm/anthropic), and [Gemini](/docs/memori-cloud/llm/gemini). ## Quick Start ```python {{ title: 'Sync' }} from langchain_aws import ChatBedrock from memori import Memori client = ChatBedrock( model_id="anthropic.claude-sonnet-4-5-20250929", region_name="us-east-1" ) mem = Memori().llm.register(chatbedrock=client) mem.attribution(entity_id="user_123", process_id="bedrock_agent") response = client.invoke("Hello!") print(response.content) ``` ```python {{ title: 'Async' }} import asyncio from langchain_aws import ChatBedrock from memori import Memori client = ChatBedrock( model_id="anthropic.claude-sonnet-4-5-20250929", region_name="us-east-1" ) mem = Memori().llm.register(chatbedrock=client) mem.attribution(entity_id="user_123", process_id="bedrock_agent") async def main(): response = await client.ainvoke("Hello!") print(response.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} from langchain_aws import ChatBedrock from memori import Memori client = ChatBedrock( model_id="anthropic.claude-sonnet-4-5-20250929", region_name="us-east-1" ) mem = Memori().llm.register(chatbedrock=client) mem.attribution(entity_id="user_123", process_id="bedrock_agent") for chunk in client.stream("Hello!"): print(chunk.content, end="") ``` ## Supported Modes | Mode | Method | | ------------ | ------------------------ | | **Sync** | `client.invoke()` | | **Async** | `await client.ainvoke()` | | **Streamed** | `client.stream()` | ## Available Models | Model | Model ID | | --------------------- | ------------------------------------------- | | **Claude 3.5 Sonnet** | `anthropic.claude-3-5-sonnet-20241022-v2:0` | | **Claude 3 Haiku** | `anthropic.claude-3-haiku-20240307-v1:0` | | **Llama 3.1 70B** | `meta.llama3-1-70b-instruct-v1:0` | | **Mistral Large** | `mistral.mistral-large-2407-v1:0` | ================================================ FILE: docs/memori-cloud/llm/deepseek.mdx ================================================ --- title: DeepSeek description: Using Memori with DeepSeek models via the OpenAI-compatible API on Memori Cloud. --- # DeepSeek DeepSeek provides an OpenAI-compatible API. Use the `openai` Python package with `base_url="https://api.deepseek.com"` — no special adapter needed. TypeScript support for DeepSeek is coming soon. The TypeScript SDK currently supports [OpenAI](/docs/memori-cloud/llm/openai), [Anthropic](/docs/memori-cloud/llm/anthropic), and [Gemini](/docs/memori-cloud/llm/gemini). ## Quick Start ```python {{ title: 'Sync' }} import os from memori import Memori from openai import OpenAI client = OpenAI( base_url="https://api.deepseek.com", api_key=os.getenv("DEEPSEEK_API_KEY"), ) mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="deepseek_assistant") response = client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```python {{ title: 'Async' }} import os, asyncio from memori import Memori from openai import AsyncOpenAI client = AsyncOpenAI( base_url="https://api.deepseek.com", api_key=os.getenv("DEEPSEEK_API_KEY"), ) mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="deepseek_assistant") async def main(): response = await client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} import os from memori import Memori from openai import OpenAI client = OpenAI( base_url="https://api.deepseek.com", api_key=os.getenv("DEEPSEEK_API_KEY"), ) mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="deepseek_assistant") stream = client.chat.completions.create( model="deepseek-chat", messages=[{"role": "user", "content": "Hello!"}], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` ## Supported Modes | Mode | Method | | ------------ | ---------------------------------------- | | **Sync** | `client.chat.completions.create()` | | **Async** | `await client.chat.completions.create()` | | **Streamed** | `stream=True` parameter | ================================================ FILE: docs/memori-cloud/llm/gemini.mdx ================================================ --- title: Google Gemini description: Using Memori with Google Gemini models on Memori Cloud. --- # Google Gemini Memori integrates with Google Gemini via the `google-genai` SDK. Register the `GenerativeModel` instance and all `generate_content()` calls are automatically captured. ## Quick Start ```python {{ title: 'Python' }} import os from memori import Memori import google.generativeai as genai genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) client = genai.GenerativeModel("gemini-2.0-flash-exp") # Use any Gemini model mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="gemini_assistant") response = client.generate_content("Hello!") print(response.text) ``` ```typescript {{ title: 'TypeScript' }} import { GoogleGenAI } from '@google/genai'; import { Memori } from '@memorilabs/memori'; const client = new GoogleGenAI({ apiKey: process.env.GOOGLE_API_KEY }); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'gemini_assistant'); const response = await client.models.generateContent({ model: 'gemini-2.0-flash', contents: 'Hello!', }); console.log(response.text); ``` ## Supported Modes | Mode | Python | TypeScript | | ------------ | --------------------------------------- | ----------------------------------------------- | | **Sync** | `client.generate_content()` | — | | **Async** | `await client.generate_content_async()` | `await client.models.generateContent()` | | **Streamed** | `stream=True` parameter | `client.models.generateContentStream()` | ## Additional Modes ### Async (Python) ```python import os, asyncio from memori import Memori import google.generativeai as genai genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) client = genai.GenerativeModel("gemini-2.0-flash-exp") # Use any Gemini model mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="gemini_assistant") async def main(): response = await client.generate_content_async("Hello!") print(response.text) asyncio.run(main()) ``` ### Streaming ```python {{ title: 'Python' }} import os from memori import Memori import google.generativeai as genai genai.configure(api_key=os.getenv("GOOGLE_API_KEY")) client = genai.GenerativeModel("gemini-2.0-flash-exp") # Use any Gemini model mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="gemini_assistant") response = client.generate_content("Hello!", stream=True) for chunk in response: print(chunk.text, end="") ``` ```typescript {{ title: 'TypeScript' }} import { GoogleGenAI } from '@google/genai'; import { Memori } from '@memorilabs/memori'; const client = new GoogleGenAI({ apiKey: process.env.GOOGLE_API_KEY }); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'gemini_assistant'); const stream = await client.models.generateContentStream({ model: 'gemini-2.0-flash', contents: 'Hello!', }); for await (const chunk of stream) { process.stdout.write(chunk.text ?? ''); } ``` ## Multi-Turn Conversations Use `start_chat()` for multi-turn interactions. Memori tracks the full conversation automatically. ```python chat = client.start_chat() response = chat.send_message("My name is Alice.") print(response.text) response = chat.send_message("What's my name?") print(response.text) ``` ### TypeScript Multi-turn conversations in TypeScript use the standard messages array pattern: ```typescript import { GoogleGenAI } from '@google/genai'; import { Memori } from '@memorilabs/memori'; const client = new GoogleGenAI({ apiKey: process.env.GOOGLE_API_KEY }); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'gemini_assistant'); const response = await client.models.generateContent({ model: 'gemini-2.0-flash', contents: [ { role: 'user', parts: [{ text: 'My name is Alice.' }] }, ], }); console.log(response.text); const response2 = await client.models.generateContent({ model: 'gemini-2.0-flash', contents: [ { role: 'user', parts: [{ text: 'My name is Alice.' }] }, { role: 'model', parts: [{ text: response.text ?? '' }] }, { role: 'user', parts: [{ text: "What's my name?" }] }, ], }); console.log(response2.text); ``` ================================================ FILE: docs/memori-cloud/llm/langchain.mdx ================================================ --- title: LangChain description: Using Memori with LangChain chat models on Memori Cloud. --- # LangChain Memori Cloud supports any LangChain chat model. Each class has its own registration keyword: `ChatOpenAI` for OpenAI, `ChatAnthropic` for Anthropic, `ChatBedrock` for BedRock, `ChatGoogleGenerativeAI` for Google Gen AI models. TypeScript support for LangChain is coming soon. The TypeScript SDK currently supports [OpenAI](/docs/memori-cloud/llm/openai), [Anthropic](/docs/memori-cloud/llm/anthropic), and [Gemini](/docs/memori-cloud/llm/gemini). ## Quick Start ```python {{ title: 'Sync' }} from langchain_openai import ChatOpenAI from memori import Memori client = ChatOpenAI(model="gpt-4o-mini") mem = Memori().llm.register(chatopenai=client) mem.attribution(entity_id="user_123", process_id="langchain_agent") response = client.invoke("Hello!") print(response.content) ``` ```python {{ title: 'Async' }} import asyncio from langchain_openai import ChatOpenAI from memori import Memori client = ChatOpenAI(model="gpt-4o-mini") mem = Memori().llm.register(chatopenai=client) mem.attribution(entity_id="user_123", process_id="langchain_agent") async def main(): response = await client.ainvoke("Hello!") print(response.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} from langchain_openai import ChatOpenAI from memori import Memori client = ChatOpenAI(model="gpt-4o-mini") mem = Memori().llm.register(chatopenai=client) mem.attribution(entity_id="user_123", process_id="langchain_agent") for chunk in client.stream("Hello!"): print(chunk.content, end="") ``` ## Different Providers | Package | Chat Model | Registration Keyword | | ------------------------ | ------------------------ | ------------------------ | | `langchain-openai` | `ChatOpenAI` | `chatopenai=client` | | `langchain-anthropic` | `ChatAnthropic` | `chatanthropic=client` | | `langchain-google-genai` | `ChatGoogleGenerativeAI` | `chatgooglegenai=client` | | `langchain-aws` | `ChatBedrock` | `chatbedrock=client` | ```python {{ title: 'Anthropic' }} from langchain_anthropic import ChatAnthropic from memori import Memori client = ChatAnthropic(model="claude-sonnet-4-5-20250929") mem = Memori().llm.register(chatopenai=client) ``` ```python {{ title: 'Google Gemini' }} from langchain_google_genai import ChatGoogleGenerativeAI from memori import Memori client = ChatGoogleGenerativeAI(model="gemini-2.0-flash-exp") mem = Memori().llm.register(chatgooglegenai=client) ``` ```python {{ title: 'AWS Bedrock' }} from langchain_aws import ChatBedrock from memori import Memori client = ChatBedrock(model_id="anthropic.claude-sonnet-4-5-20250929", region_name="us-east-1") mem = Memori().llm.register(chatbedrock=client) ``` ## Supported Modes | Mode | Method | | ------------ | ------------------------ | | **Sync** | `client.invoke()` | | **Async** | `await client.ainvoke()` | | **Streamed** | `client.stream()` | ================================================ FILE: docs/memori-cloud/llm/nebius.mdx ================================================ --- title: Nebius AI Studio description: Using Memori with Nebius AI Studio models via the OpenAI-compatible API on Memori Cloud. --- # Nebius AI Studio Nebius AI Studio provides an OpenAI-compatible API. Use the `openai` Python package with `base_url="https://api.studio.nebius.com/v1/"` — no special adapter needed. TypeScript support for Nebius AI Studio is coming soon. The TypeScript SDK currently supports [OpenAI](/docs/memori-cloud/llm/openai), [Anthropic](/docs/memori-cloud/llm/anthropic), and [Gemini](/docs/memori-cloud/llm/gemini). ## Quick Start ```python {{ title: 'Sync' }} import os from memori import Memori from openai import OpenAI client = OpenAI( base_url="https://api.studio.nebius.com/v1/", api_key=os.getenv("NEBIUS_API_KEY"), ) mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="nebius_assistant") response = client.chat.completions.create( model="meta-llama/Llama-3.3-70B-Instruct", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```python {{ title: 'Async' }} import os, asyncio from memori import Memori from openai import AsyncOpenAI client = AsyncOpenAI( base_url="https://api.studio.nebius.com/v1/", api_key=os.getenv("NEBIUS_API_KEY"), ) mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="nebius_assistant") async def main(): response = await client.chat.completions.create( model="meta-llama/Llama-3.3-70B-Instruct", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} import os from memori import Memori from openai import OpenAI client = OpenAI( base_url="https://api.studio.nebius.com/v1/", api_key=os.getenv("NEBIUS_API_KEY"), ) mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="nebius_assistant") stream = client.chat.completions.create( model="meta-llama/Llama-3.3-70B-Instruct", messages=[{"role": "user", "content": "Hello!"}], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` ## Supported Modes | Mode | Method | | ------------ | ---------------------------------------- | | **Sync** | `client.chat.completions.create()` | | **Async** | `await client.chat.completions.create()` | | **Streamed** | `stream=True` parameter | ================================================ FILE: docs/memori-cloud/llm/openai.mdx ================================================ --- title: OpenAI description: Using Memori with OpenAI models including GPT-4o, GPT-4.1, and the Responses API on Memori Cloud. --- # OpenAI Memori supports all OpenAI Chat Completions and Responses APIs. Both sync and async clients are fully supported. ## Quick Start ```python {{ title: 'Python' }} from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```typescript {{ title: 'TypeScript' }} import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const client = new OpenAI(); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'my_agent'); const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: 'Hello!' }], }); console.log(response.choices[0].message.content); ``` ## Supported Modes | Mode | Python | TypeScript | | ----------------- | ---------------------------------------- | ---------------------------------------- | | **Sync** | `client.chat.completions.create()` | — | | **Async** | `await client.chat.completions.create()` | `await client.chat.completions.create()` | | **Streamed** | `stream=True` parameter | `stream: true` parameter | | **Responses API** | `client.responses.create()` | — | ## Additional Modes ### Async (Python) ```python import asyncio from memori import Memori from openai import AsyncOpenAI client = AsyncOpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") async def main(): response = await client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) asyncio.run(main()) ``` ### Streaming ```python {{ title: 'Python' }} from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") stream = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "Hello!"}], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` ```typescript {{ title: 'TypeScript' }} import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const client = new OpenAI(); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'my_agent'); const stream = await client.chat.completions.create({ model: 'gpt-4o-mini', messages: [{ role: 'user', content: 'Hello!' }], stream: true, }); for await (const chunk of stream) { if (chunk.choices[0]?.delta?.content) { process.stdout.write(chunk.choices[0].delta.content); } } ``` ### Responses API (Python) ```python from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") response = client.responses.create( model="gpt-4o-mini", input="Hello!", instructions="You are a helpful assistant." ) print(response.output_text) ``` ## Multi-Turn Conversations Memori automatically captures each interaction and links them within the same session. ```python from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") messages = [ {"role": "user", "content": "My name is Alice."} ] response = client.chat.completions.create( model="gpt-4o-mini", messages=messages ) messages.append({ "role": "assistant", "content": response.choices[0].message.content }) messages.append({ "role": "user", "content": "What's my name?" }) response = client.chat.completions.create( model="gpt-4o-mini", messages=messages ) print(response.choices[0].message.content) ``` ```typescript import OpenAI from 'openai'; import { Memori } from '@memorilabs/memori'; const client = new OpenAI(); const mem = new Memori().llm.register(client); mem.attribution('user_123', 'my_agent'); const messages: OpenAI.ChatCompletionMessageParam[] = [ { role: 'user', content: 'My name is Alice.' }, ]; const response = await client.chat.completions.create({ model: 'gpt-4o-mini', messages, }); messages.push({ role: 'assistant', content: response.choices[0].message.content!, }); messages.push({ role: 'user', content: "What's my name?", }); const response2 = await client.chat.completions.create({ model: 'gpt-4o-mini', messages, }); console.log(response2.choices[0].message.content); ``` ================================================ FILE: docs/memori-cloud/llm/overview.mdx ================================================ --- title: Integration Overview description: Memori is LLM-agnostic. Register any supported client and Memori handles memory capture, augmentation, and recall automatically. --- # Integration Overview Memori Cloud works with all major LLM providers and frameworks. Register any supported client and Memori handles memory capture, augmentation, and recall automatically — with your Memori API key and provider credentials, no database setup required. ## Supported Providers | Provider | Integration | Python Install | TypeScript Install | | ----------------------------------------------------- | ------------------ | ------------------------------------- | --------------------------------------------------- | | **[OpenAI](/docs/memori-cloud/llm/openai)** | Direct SDK wrapper | `pip install memori openai` | `npm install @memorilabs/memori openai` | | **[Anthropic](/docs/memori-cloud/llm/anthropic)** | Direct SDK wrapper | `pip install memori anthropic` | `npm install @memorilabs/memori @anthropic-ai/sdk` | | **[Google Gemini](/docs/memori-cloud/llm/gemini)** | Direct SDK wrapper | `pip install memori google-genai` | `npm install @memorilabs/memori @google/genai` | | **[xAI Grok](/docs/memori-cloud/llm/xai-grok)** | OpenAI-compatible | `pip install memori openai` | Coming soon | | **[Nebius AI Studio](/docs/memori-cloud/llm/nebius)** | OpenAI-compatible | `pip install memori openai` | Coming soon | | **[DeepSeek](/docs/memori-cloud/llm/deepseek)** | OpenAI-compatible | `pip install memori openai` | Coming soon | | **[AWS Bedrock](/docs/memori-cloud/llm/aws-bedrock)** | LangChain adapter | `pip install memori langchain-aws` | Coming soon | | **[LangChain](/docs/memori-cloud/llm/langchain)** | Framework support | `pip install memori langchain-openai` | Coming soon | | **[Agno](/docs/memori-cloud/llm/agno)** | Framework support | `pip install memori agno` | Coming soon | | **[Pydantic AI](/docs/memori-cloud/llm/pydantic-ai)** | Framework support | `pip install memori pydantic-ai` | Coming soon | All providers support sync, async, streamed, and unstreamed modes. ## Pydantic AI Register the `Agent` instance directly — Memori wraps `run_sync` and `run` automatically. ```python from memori import Memori from pydantic_ai import Agent agent = Agent("openai:gpt-4o-mini") mem = Memori().llm.register(agent) mem.attribution(entity_id="user_123", process_id="pydantic_agent") result = agent.run_sync("Hello!") print(result.output) ``` ## OpenAI-Compatible Providers Any provider with an OpenAI-compatible API works by setting a custom `base_url`. Dedicated guides: [xAI Grok](/docs/memori-cloud/llm/xai-grok), [Nebius AI Studio](/docs/memori-cloud/llm/nebius), [DeepSeek](/docs/memori-cloud/llm/deepseek). Same pattern works for Azure OpenAI, NVIDIA NIM, and others. ```python import os from memori import Memori from openai import OpenAI client = OpenAI( base_url="https://api.studio.nebius.com/v1/", api_key=os.getenv("NEBIUS_API_KEY"), ) mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") response = client.chat.completions.create( model="meta-llama/Llama-3.3-70B-Instruct", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ## OpenAI Responses API ```python from memori import Memori from openai import OpenAI client = OpenAI() mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="my_agent") response = client.responses.create( model="gpt-4o-mini", input="Hello!", instructions="You are a helpful assistant." ) print(response.output_text) ``` ================================================ FILE: docs/memori-cloud/llm/pydantic-ai.mdx ================================================ --- title: Pydantic AI description: Using Memori with Pydantic AI agents on Memori Cloud. --- # Pydantic AI Memori integrates at the agent level — register the `Agent` instance and Memori wraps `run_sync` and `run` automatically. Works with any Pydantic AI-supported model. TypeScript support for Pydantic AI is coming soon. The TypeScript SDK currently supports [OpenAI](/docs/memori-cloud/llm/openai), [Anthropic](/docs/memori-cloud/llm/anthropic), and [Gemini](/docs/memori-cloud/llm/gemini). ## Quick Start ```python from memori import Memori from pydantic_ai import Agent agent = Agent("openai:gpt-4o-mini") mem = Memori().llm.register(agent) mem.attribution(entity_id="user_123", process_id="pydantic_agent") result = agent.run_sync("Hello!") print(result.output) ``` ## Async Usage ```python import asyncio from memori import Memori from pydantic_ai import Agent agent = Agent("openai:gpt-4o-mini") mem = Memori().llm.register(agent) mem.attribution(entity_id="user_123", process_id="pydantic_agent") async def main(): result = await agent.run("Hello!") print(result.output) asyncio.run(main()) ``` ## Different Providers Pydantic AI supports multiple providers via model strings. Registration is identical regardless of provider. ```python {{ title: 'OpenAI' }} agent = Agent("openai:gpt-4o-mini") mem = Memori().llm.register(agent) ``` ```python {{ title: 'Anthropic' }} agent = Agent("anthropic:claude-sonnet-4-5-20250929") mem = Memori().llm.register(agent) ``` ```python {{ title: 'Google Gemini' }} agent = Agent("google-gla:gemini-2.0-flash-exp") mem = Memori().llm.register(agent) ``` ## Supported Modes | Mode | Method | | ------------ | --------------------------- | | **Sync** | `agent.run_sync()` | | **Async** | `await agent.run()` | | **Streamed** | Via agent streaming support | ================================================ FILE: docs/memori-cloud/llm/xai-grok.mdx ================================================ --- title: xAI Grok description: Using Memori with xAI Grok models via the OpenAI-compatible API on Memori Cloud. --- # xAI Grok xAI provides an OpenAI-compatible API. Use the `openai` Python package with `base_url="https://api.x.ai/v1"` — no special adapter needed. TypeScript support for xAI Grok is coming soon. The TypeScript SDK currently supports [OpenAI](/docs/memori-cloud/llm/openai), [Anthropic](/docs/memori-cloud/llm/anthropic), and [Gemini](/docs/memori-cloud/llm/gemini). ## Quick Start ```python {{ title: 'Sync' }} import os from memori import Memori from openai import OpenAI client = OpenAI( base_url="https://api.x.ai/v1", api_key=os.getenv("XAI_API_KEY") ) mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="grok_assistant") response = client.chat.completions.create( model="grok-2-latest", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) ``` ```python {{ title: 'Async' }} import os, asyncio from memori import Memori from openai import AsyncOpenAI client = AsyncOpenAI( base_url="https://api.x.ai/v1", api_key=os.getenv("XAI_API_KEY") ) mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="grok_assistant") async def main(): response = await client.chat.completions.create( model="grok-2-latest", messages=[{"role": "user", "content": "Hello!"}] ) print(response.choices[0].message.content) asyncio.run(main()) ``` ```python {{ title: 'Streaming' }} import os from memori import Memori from openai import OpenAI client = OpenAI( base_url="https://api.x.ai/v1", api_key=os.getenv("XAI_API_KEY") ) mem = Memori().llm.register(client) mem.attribution(entity_id="user_123", process_id="grok_assistant") stream = client.chat.completions.create( model="grok-2-latest", messages=[{"role": "user", "content": "Hello!"}], stream=True ) for chunk in stream: if chunk.choices[0].delta.content: print(chunk.choices[0].delta.content, end="") ``` ## Supported Modes | Mode | Method | | ------------ | ---------------------------------------- | | **Sync** | `client.chat.completions.create()` | | **Async** | `await client.chat.completions.create()` | | **Streamed** | `stream=True` parameter | ================================================ FILE: docs/memori-cloud/mcp/agent-skills.mdx ================================================ --- title: Agent Skills description: Add a SKILL.md file to teach your IDE agent when and how to use Memori MCP tools for reliable recall and memory storage. --- # Agent Skills In addition to the MCP server config, we strongly recommend adding an **Agent Skills file** (`SKILL.md`) that teaches your IDE agent _when_ and _how_ to use the Memori MCP tools. ## Why Add a Skills File - **Reliability** — the agent consistently calls `recall` at the start of a turn and `advanced_augmentation` after responding - **Lower prompt friction** — you don't need to re-explain the memory workflow every session - **Focused context** — most clients only load the full skill instructions when the skill is triggered ## The SKILL.md File Create a `SKILL.md` file with the content below. This teaches the agent the full decision flow for when to recall, when to store memories, and what to skip. ````markdown --- name: memori-mcp-usage description: Decides when to call Memori MCP tools for recall and advanced augmentation. Use when the user mentions remembering, preferences, "last time", personalization, prior context, recurring patterns, or when consistency across turns matters. --- # Memori MCP usage ### Tool contract - `entity_id` and `process_id` are provided via MCP headers (do not pass as tool args unless the tool schema requires it). - `session_id` is generated by the server. ## Decision flow per turn (follow in this order) 1. Is this a trivial, administrative, or closing message? Examples: "thanks", "ok", "got it", "goodbye", "ignore everything before" → Skip both recall **and** augment. Answer directly. 2. Does the user explicitly say **not** to remember / store / save / log / keep anything from this turn? → Recall if the question needs prior context (step 3), but **never** augment this turn. 3. Should we recall prior memory? Check conditions below. → Yes → Call `recall` (usually once per turn) → Use results to ground / personalize the answer → No useful memories? → Proceed normally 4. After drafting the final assistant response → Should we augment? Check conditions below. → Yes → Call `advanced_augmentation` with the full turn `[user message, assistant response]` → No → Skip Skip memory calls entirely for very short / purely reactive turns unless personalization is clearly relevant. ## When to recall (`recall`) Call `recall` when **any** of these are true: - User references prior context: "as I said", "last time", "you already know", "same as before", "remember when", "like we did" - Preferences or style matter: tone, verbosity, length, formatting, units, structure, templates, language, emoji usage, etc. - Personal / project constraints apply: timezone, location, environment, setup, repo conventions, tooling stack, recurring workflows - Consistency across answers is important: decision patterns, naming conventions, approval thresholds, recurring questions - User asks to be reminded of something they previously told you Query strategies (ranked best to worst): 1. **Best**: user's exact latest message (preserves nuance, wording, typos, personal style) 2. **Good**: very short rephrased intent when message is long/noisy 3. **Avoid**: overly generic queries ("preferences", "remember", "info about me") — poor precision **Avoid**: dumping full conversation history as query Prefer **one** recall per turn in almost all cases. ## When to write (`advanced_augmentation`) Call `advanced_augmentation` **after** drafting your response **only** when the turn reveals a **durable, stable fact or preference** the user would want applied in future sessions. Ask yourself: *"Would this information still be useful to recall weeks from now, in a completely different conversation?"* If the answer is no, skip augment. Augment-worthy signals: - Explicit preferences ("always do X", "from now on", "default to...", "prefer...") - Identity / profile facts user wants persisted (timezone, role, typical environment, location hints) - Long-lived project or workflow context (naming conventions, tooling choices, architecture decisions, coding principles) - Relationships or team structure ("Alice owns the auth service", "report to Bob") ### Do NOT call advanced_augmentation - Secrets, API keys, tokens, passwords (even partially redacted) - Large logs, stack traces, error dumps - Clearly ephemeral info: one-time codes, temp links, live prices - Debugging marked temporary ("just this once", "for this error only") - Role-play, hypotheticals, fiction - Purely reactive chit-chat with no lasting preference - Session activity / progress updates: test results, lint output, command exit codes - Task management actions: to-do completions, progress checkpoints, commit messages - Routine code changes: refactoring steps, file renames, import cleanups - Tool output narration: summarizing what a tool returned - Conversation-scoped decisions: one-off choices that don't represent a lasting rule **Rule of thumb**: if the information describes *what happened during this session* rather than *a fact or preference that transcends sessions*, do not augment. ### Examples **Should augment:** - "From now on keep answers short, include TL;DR, max 250 words" - "My location is Paris, France; I prefer Central time and 12-hour format" - "This project uses ruff + mypy + pytest; strongly prefer KISS/DRY/YAGNI" **Should NOT augment:** - "All 57 tests passed after the refactor" - "Renamed `_utils.py` to `_helpers.py`" - "The linter found 3 errors, now fixed" **Should NOT use Memori tools at all:** - "Thanks man!" - "Temp login code is 493021" - "What's the weather like in KC right now?" ```` ## Installing Skills by Client ### Cursor Cursor supports skill folders containing a `SKILL.md`: - **Project (recommended for teams):** `.cursor/skills/memori-mcp/SKILL.md` - **Personal (all projects):** `~/.cursor/skills/memori-mcp/SKILL.md` ```bash mkdir -p .cursor/skills/memori-mcp # Add SKILL.md content to .cursor/skills/memori-mcp/SKILL.md ``` Restart Cursor so it re-discovers skills. ### Claude Code Claude Code skills live in either: - **Personal:** `~/.claude/skills/memori-mcp/SKILL.md` - **Project:** `.claude/skills/memori-mcp/SKILL.md` ```bash mkdir -p .claude/skills/memori-mcp # Add SKILL.md content to .claude/skills/memori-mcp/SKILL.md ``` Restart Claude Code or reload the session. ### OpenAI Codex Codex scans repo-level skills under `.agents/skills`: ```bash mkdir -p .agents/skills/memori-mcp # Add SKILL.md content to .agents/skills/memori-mcp/SKILL.md ``` Restart Codex if it doesn't pick up changes automatically. ### Warp Warp supports custom agent rules that serve the same purpose as a skills file. Add the SKILL.md content to your Warp rules configuration: - **Project:** `.warp/rules/memori-mcp.md` - **Global:** `~/.warp/rules/memori-mcp.md` ```bash mkdir -p .warp/rules # Add SKILL.md content to .warp/rules/memori-mcp.md ``` Restart Warp so the rules take effect. ### Antigravity Antigravity skills can be stored at: - **Workspace:** `/.agent/skills/memori-mcp/SKILL.md` - **Global:** `~/.gemini/antigravity/skills/memori-mcp/SKILL.md` ```bash mkdir -p .agent/skills/memori-mcp # Add SKILL.md content to .agent/skills/memori-mcp/SKILL.md ``` ### LangChain LangChain agents use Memori MCP to persist user preferences and project context across invocations. The integration logic lives in your application layer using the `langchain-mcp-adapters` package. To implement this, instantiate `MultiServerMCPClient` per request with the end-user's ID set as `X-Memori-Entity-Id`, as shown on the Client Setup page. ### Slack A Slack bot backed by an LLM uses Memori MCP to persist user preferences across all channels. As Slack is a cloud based service, the integration logic lives in your bot's backend. To implement this, set the headers dynamically in your server's outgoing MCP tool calls using the Slack User ID from the incoming event payload as shown on the Client Setup page. ### Notion Custom Notion integrations use Memori MCP to maintain a consistent memory of a user's writing style and project context across different pages and databases. To implement this, your middleware must map the Notion User ID to the Memori headers as shown on the Client Setup page. This ensures that regardless of which workspace or page the user is on, the AI assistant retrieves the same persistent preferences. ## How It All Works Together Once both the MCP config and skills file are installed: - The **MCP config** ensures the tools exist (`recall`, `advanced_augmentation`) - The **skills file** ensures the agent uses them _consistently and correctly_ — recall before responding, store after responding After installing both, try telling your agent a preference like "I always use tabs over spaces." In a later session, ask it to write code — it should recall and apply the preference automatically. ================================================ FILE: docs/memori-cloud/mcp/client-setup.mdx ================================================ --- title: Client Setup description: Configure the Memori MCP server in Cursor, Claude Code, Codex, Warp, Antigravity, and other MCP clients with custom connectors. --- # Client Setup Connect your IDE agent to the Memori MCP server. Each client uses a local MCP configuration with your API key and attribution headers. ## Prerequisites - A Memori API key from [app.memorilabs.ai](https://app.memorilabs.ai) - An `entity_id` (identifies the end user) and optional `process_id` (identifies the agent or workflow) Set these as environment variables or replace the placeholders directly in the config: ```bash export MEMORI_API_KEY="your-memori-api-key" export MEMORI_ENTITY_ID="user_123" export MEMORI_PROCESS_ID="my_agent" # optional ``` ## Cursor Create `~/.cursor/mcp.json` (global) or `.cursor/mcp.json` (project-level): ```json { "mcpServers": { "memori": { "url": "https://api.memorilabs.ai/mcp/", "headers": { "X-Memori-API-Key": "${MEMORI_API_KEY}", "X-Memori-Entity-Id": "${MEMORI_ENTITY_ID}", "X-Memori-Process-Id": "${MEMORI_PROCESS_ID}" } } } } ``` Restart Cursor after saving the file. ## Claude Code You can configure via the CLI or a `.mcp.json` file. ### CLI ```bash claude mcp add --transport http memori https://api.memorilabs.ai/mcp/ \ --header "X-Memori-API-Key: ${MEMORI_API_KEY}" \ --header "X-Memori-Entity-Id: ${MEMORI_ENTITY_ID}" \ --header "X-Memori-Process-Id: ${MEMORI_PROCESS_ID}" ``` ### Project Config Create `.mcp.json` in your project root: ```json { "mcpServers": { "memori": { "type": "http", "url": "https://api.memorilabs.ai/mcp/", "headers": { "X-Memori-API-Key": "${MEMORI_API_KEY}", "X-Memori-Entity-Id": "${MEMORI_ENTITY_ID}", "X-Memori-Process-Id": "${MEMORI_PROCESS_ID}" } } } } ``` Run `/mcp` inside Claude Code to verify the server status. ## OpenAI Codex Codex reads MCP server configuration from `~/.codex/config.toml`. Add this: ```toml [mcp_servers.memori] enabled = true url = "https://api.memorilabs.ai/mcp/" [mcp_servers.memori.http_headers] X-Memori-API-Key = "${MEMORI_API_KEY}" X-Memori-Entity-Id = "${MEMORI_ENTITY_ID}" X-Memori-Process-Id = "${MEMORI_PROCESS_ID}" ``` `X-Memori-Process-Id` is optional. You can also add the server from the Codex UI: **Settings > Settings > MCP Servers > + Add Server**. ## Warp Warp cloud agents support MCP JSON config with remote URLs. Add the Memori server to your Warp MCP configuration: ```json { "memori": { "serverUrl": "https://api.memorilabs.ai/mcp/", "headers": { "X-Memori-API-Key": "your-memori-api-key", "X-Memori-Entity-Id": "user_123", "X-Memori-Process-Id": "my_agent" } } } ``` `X-Memori-Process-Id` is optional. ## Antigravity Open **Manage MCP Servers** and edit `mcp_config.json`: ```json { "mcpServers": { "memori": { "serverUrl": "https://api.memorilabs.ai/mcp/", "headers": { "X-Memori-API-Key": "your-memori-api-key", "X-Memori-Entity-Id": "user_123", "X-Memori-Process-Id": "my_agent" } } } } ``` Save and restart Antigravity so the tool list refreshes. ## LangChain Set headers dynamically based on the current user session: ```python from langchain_mcp_adapters.client import MultiServerMCPClient client = MultiServerMCPClient({ "memori": { "transport": "streamable_http", "url": "https://api.memorilabs.ai/mcp/", "headers": { "X-Memori-API-Key": "your-memori-api-key", "X-Memori-Entity-Id": "user_123", "X-Memori-Process-Id": "langchain_agent" } } }) # Load the tools to be used by your LangChain agent tools = await client.get_tools() ``` ## Slack Set headers dynamically per request using the Slack user ID from the event payload: ```ts const memoriHeaders = { "X-Memori-API-Key": process.env.MEMORI_API_KEY, "X-Memori-Entity-Id": slackEvent.user, // e.g. "U04ABCDEF" "X-Memori-Process-Id": "supportbot", }; ``` Pass these headers in every MCP tool call. `session_id` is derived automatically from `entity_id` and the current UTC hour. Use `process_id` to isolate memories by integration or workspace so preferences from a personal workspace don't bleed into a team workspace. ## Notion Set the entity and process IDs from the Notion API user object: ```ts const memoriHeaders = { "X-Memori-API-Key": process.env.MEMORI_API_KEY, "X-Memori-Entity-Id": notionUser.id, // e.g. "a1b2c3d4-e5f6-7890-abcd-ef1234567890" "X-Memori-Process-Id": "notion_writing_assistant", }; ``` Pass these headers in every MCP tool call. `session_id` is derived automatically from `entity_id` and the current UTC hour. Use `process_id` to isolate memories by integration or workspace so preferences from a personal workspace don't bleed into a team workspace. ## Verifying the Connection After configuring any client: 1. Confirm the MCP server shows as **connected** in your client's UI 2. Check that `recall` and `advanced_augmentation` appear in the tools list 3. Send a test message — `recall` should return a response (even if empty for new entities) 4. Verify `advanced_augmentation` returns `memory being created` If you receive 401 errors, double-check your `X-Memori-API-Key` value. See [Troubleshooting](/docs/memori-cloud/support/troubleshooting) for more help. ================================================ FILE: docs/memori-cloud/mcp/overview.mdx ================================================ --- title: Memori MCP Overview description: Use the Model Context Protocol (MCP) to connect AI agents directly to Memori for real-time recall and memory storage. --- # Memori MCP (Model Context Protocol) MCP (Model Context Protocol) is a standard way for AI agents to connect to external tools and APIs. With Memori's MCP server, your agent can retrieve relevant memories before answering and store durable facts after responding — keeping context across sessions without any SDK integration. ## Why Use MCP Without MCP, your agent only sees the current conversation. With MCP, it can: - **Store** stable user facts and preferences after answering using the `advanced_augmentation` tool - **Recall** relevant memories before answering using the `recall` tool - **Maintain context** across sessions using `entity_id`, `process_id`, and `session_id` MCP is ideal for IDE agents (Cursor, Claude Code, Codex, Warp, Antigravity) and other MCP-compatible clients where you want persistent memory without writing integration code. ## Server Details | Property | Value | | ------------- | ---------------------------------------- | | **Server** | Memori MCP | | **Endpoint** | `https://api.memorilabs.ai/mcp/` | | **Transport** | Stateless HTTP | | **Auth** | API key via request headers | ### Headers Your Memori API key from [app.memorilabs.ai](https://app.memorilabs.ai). Stable end-user or entity identifier (e.g. `user_123`). Optional process, app, or workflow identifier (e.g. `my_agent`) for memory isolation. `session_id` is derived automatically as `-`. You do not need to provide it. ## Tools ### `recall` Fetches relevant memories at the start of a user turn. The latest user message — typically passed verbatim. `entity_id` is read from MCP headers. `process_id` is read when provided. Returns a structured memory payload with relevant context. ### `advanced_augmentation` Stores durable memory after the agent has drafted a response. The user's message for this turn. The assistant's response for this turn. `entity_id` is read from MCP headers. `process_id` is read when provided. Returns a confirmation: `memory being created`. ## Example Agent Flow Given the user message: _"I prefer Python and use uv for dependency management."_ 1. Agent calls `recall` with the user message as `query` 2. Agent composes a response using any returned facts 3. Agent sends the response to the user 4. Agent calls `advanced_augmentation` with the `user_message` and `assistant_response` On a later turn like _"Write a hello world script"_, the agent recalls the Python + uv preference and personalizes its response. ## Validation Checklist After configuring your client, verify the setup: - MCP server shows as connected and healthy in your client UI - Tools list includes `recall` and `advanced_augmentation` - Calls return non-401 responses - `recall` returns memories for known entities - `advanced_augmentation` returns `memory being created` ================================================ FILE: docs/memori-cloud/openclaw/overview.mdx ================================================ --- title: OpenClaw Overview description: Give your OpenClaw agents structured, persistent memory with the Memori plugin — auto-recall and auto-capture across every session. --- # OpenClaw Overview By default, OpenClaw agents forget everything between sessions. The Memori plugin fixes that. It watches conversations, extracts what matters, and brings it back when relevant — automatically. ## The Problem with OpenClaw's Built-In Memory OpenClaw ships with a basic memory layer, but it has fundamental limitations that break down at scale: | Limitation | What happens | | --- | --- | | **Flat markdown files** | Memory lives in plain text files with no structure or relationships. The agent reads and writes large chunks of text with no way to index, query, or deduplicate facts. | | **Context compaction** | As sessions grow longer, important details begin to disappear. Memory gets lost when context is compressed to stay within token limits. | | **No relationship reasoning** | OpenClaw retrieves semantically similar text, but cannot understand relationships between facts or connect them to each other. | | **Cross-project noise** | When working across multiple projects, memories are not isolated. Searches return irrelevant results from other contexts, polluting the agent's responses. | | **No user isolation** | Users do not have their own isolated memories. Memories bleed across users and data is mixed, creating privacy and relevance issues. | ## What Changes When You Add Memori? The Memori plugin replaces OpenClaw's flat-file memory workflow with managed, structured memory that is scoped by `entity_id`, `process_id`, and `session_id` and enriched automatically through OpenClaw's existing hooks. ### 1. Structured memory storage Instead of raw markdown blobs, Memori stores conversations, facts, preferences, and knowledge-graph triples as structured records tied to an entity, process, and session. Facts are extracted as subject-predicate-object relationships, deduplicated over time, and connected into a graph so related memories stay queryable instead of being buried in text files. ### 2. Advanced Augmentation After each conversation, Memori processes the user and assistant exchange asynchronously in the background, identifies facts, preferences, skills, and attributes, generates embeddings for semantic search, and updates the knowledge graph without blocking the agent's response path. ### 3. Intelligent Recall Before the agent responds, Memori searches the current entity's stored facts and knowledge graph, ranks memories by semantic relevance and importance, and injects the most useful context into the prompt so durable knowledge survives context-window compression. ### 4. Production-ready observability Memori Cloud gives you dashboard visibility into memory creation, recalls, cache hit rate, sessions, quota usage, top subjects, per-memory retrieval metrics, and knowledge-graph relationships, so you can inspect what was stored and how recall is behaving in production. > The plugin still remains drop-in: OpenClaw handles the agent loop, while Memori adds recall, augmentation, sanitization, and observability around it. ## How It Works The plugin hooks into two points in OpenClaw's event lifecycle: ### 1. `before_prompt_build` — Intelligent Recall When a user sends a message, the plugin: 1. Intercepts the event and extracts the user's prompt 2. Sanitizes the prompt (strips metadata, timestamps, system messages) 3. Queries the Memori API for relevant memories 4. Prepends matching memories to the agent's system context The agent then responds with full awareness of prior context. ### 2. `agent_end` — Advanced Augmentation After the agent finishes responding, the plugin: 1. Extracts the last user and assistant messages 2. Sanitizes both (removes thinking blocks, metadata fences, timestamps) 3. Sends the cleaned exchange to the Memori API 4. Memori extracts durable facts, updates stale ones, and merges duplicates This runs asynchronously and never blocks the agent's response. ================================================ FILE: docs/memori-cloud/openclaw/quickstart.mdx ================================================ --- title: Quickstart description: Install and configure the Memori plugin for OpenClaw in under 5 minutes. --- # OpenClaw Quickstart Get persistent memory running in your OpenClaw gateway in three steps. ## Prerequisites - [OpenClaw](https://openclaw.ai) `v2026.3.2` or later - A Memori API key from [app.memorilabs.ai](https://app.memorilabs.ai) - An Entity ID to attribute memories to (e.g., a user ID or agent name) ## 1. Install and Enable ```bash # Install the plugin from npm openclaw plugins install @memorilabs/openclaw-memori # Enable it in your workspace openclaw plugins enable openclaw-memori # Restart the gateway to load the plugin openclaw gateway restart ``` ## 2. Configure You need two values: your Memori API key and an Entity ID. ### Option A: Via CLI (Recommended) ```bash openclaw config set plugins.entries.openclaw-memori.config.apiKey "YOUR_MEMORI_API_KEY" openclaw config set plugins.entries.openclaw-memori.config.entityId "your-app-user-id" ``` ### Option B: Via `openclaw.json` Add the following to `~/.openclaw/openclaw.json`: ```json { "plugins": { "entries": { "openclaw-memori": { "enabled": true, "config": { "apiKey": "your-memori-api-key", "entityId": "your-app-user-id" } } } } } ``` ### Configuration Options Your Memori API key from [app.memorilabs.ai](https://app.memorilabs.ai). The unique identifier for the entity (user, agent, or tenant) to attribute memories to. ## 3. Verify After configuring, restart the gateway and check the logs: ```bash openclaw gateway restart openclaw gateway logs --filter "[Memori]" ``` You should see: ``` [Memori] === INITIALIZING PLUGIN === [Memori] Tracking Entity ID: your-app-user-id ``` ### Test the Memory Loop 1. Send a message with a preference: > "I always use TypeScript and prefer functional patterns." 2. Check the logs for a successful augmentation: ``` [Memori] === AUGMENTATION HOOK START === [Memori] Capturing conversation turn... [Memori] Augmentation successful! ``` 3. In a new session, ask a question that should trigger recall: > "Write a hello world script." 4. The agent should recall and apply your TypeScript preference automatically: ``` [Memori] === RECALL HOOK START === [Memori] Executing SDK Recall... [Memori] Successfully injected memory context. ``` ## What Happens Under the Hood Once installed, the plugin works automatically on every conversation turn: | Phase | Hook | What the plugin does | | --- | --- | --- | | **Before response** | `before_prompt_build` | Queries Memori for relevant memories and injects them into the agent's context | | **After response** | `agent_end` | Extracts the user/assistant exchange, sanitizes it, and sends it to Memori for fact extraction | No changes to your agent's code or prompts are required. The plugin handles everything through OpenClaw's event lifecycle. The plugin is fully stateless and thread-safe. You can run it across multiple agents in the same gateway without any shared state or concurrency issues. ================================================ FILE: docs/memori-cloud/support/faq.mdx ================================================ --- title: FAQ description: Frequently asked questions about Memori Cloud. --- # FAQ ## What is Memori? __Memori__ is a memory layer for LLM applications, agents, and copilots. It continuously captures interactions, extracts structured knowledge, and intelligently ranks, decays, and retrieves the relevant memories. So your AI remembers the right things at the right time across every session. ## Which languages are supported? Memori Cloud supports both **Python** and **TypeScript** SDKs. The Python SDK (`memori`) works with both Memori Cloud and BYODB. The TypeScript SDK (`@memorilabs/memori`) is currently Cloud-only. Both SDKs support OpenAI, Anthropic, and Gemini, with additional provider support in Python. ## Memori Cloud vs Memori BYODB? **Memori Cloud** — managed storage, dashboard UI, just add an API key. Available for Python and TypeScript. **Memori BYODB** — bring your own database & maintain full control. Python only. ## Which LLM providers are supported? **Python:** OpenAI, Anthropic, Google Gemini, xAI Grok, Deepseek, Nebius AI Studio, AWS Bedrock, LangChain, Agno, and Pydantic AI. All support sync, async, and streaming. **TypeScript:** OpenAI, Anthropic, and Google Gemini. More providers coming soon. See the [Integration Overview](/docs/memori-cloud/llm/overview). ## Do I need to set up a database? No. The Memori Cloud platform manages all storage. Just initialize with `mem = Memori()`. ## Is there a free tier? Yes. 5000 memory read & writes per month. Sign up at [app.memorilabs.ai](https://app.memorilabs.ai). ## What counts as a "memory"? Each fact, preference, or relationship extracted by Advanced Augmentation counts as one memory. A single conversation can generate multiple memories. ## Does it support async? Yes. In Python, use your provider's async client (e.g. `AsyncOpenAI`). TypeScript is natively async — all SDK calls return Promises. ## How does augmentation work? It runs in the background after each conversation and extracts facts, preferences, and relationships while minimizing impact on your LLM response path. See [Advanced Augmentation](/docs/memori-cloud/concepts/advanced-augmentation). ## Can I use multiple LLM providers? Yes. Register multiple clients on the same Memori instance. Memories are shared across providers since they're scoped to entities, not to providers. ## Can I migrate between Memori Cloud and Memori BYODB? Yes. In Python, both use the same SDK — just remove `conn=SessionLocal` for Memori Cloud or add it for Memori BYODB. Existing memories don't auto-transfer. The TypeScript SDK is Cloud-only. ## Still have questions? - [Troubleshooting guide](/docs/memori-cloud/support/troubleshooting) - [GitHub Issues](https://github.com/MemoriLabs/Memori/issues) - [Discord](https://discord.gg/abD4eGym6v) ================================================ FILE: docs/memori-cloud/support/troubleshooting.mdx ================================================ --- title: Troubleshooting description: Common issues and solutions when using Memori Cloud. --- # Troubleshooting Quick fixes for the most common Memori issues. ## Installation ### Python **If `pip install memori` fails** — Requires Python 3.10+. Run `python --version` to check, then `pip install --upgrade pip && pip install memori`. ### TypeScript **If `npm install @memorilabs/memori` fails** — Requires Node.js 18+. Run `node --version` to check, then update Node.js and retry. **Module resolution errors** — Ensure your `tsconfig.json` uses `"moduleResolution": "node"` or `"bundler"`. The SDK ships ESM with TypeScript declarations. ## API Key Issues **Invalid or missing API key** — Set the `MEMORI_API_KEY` environment variable: ```bash export MEMORI_API_KEY="your-memori-api-key" ``` **Quota exceeded** — Upgrade your account at [app.memorilabs.ai/settings/billing](https://app.memorilabs.ai/settings/billing). ## No Memories Being Created 1. **Register your LLM client** — conversations aren't captured without registration: ```python {{ title: 'Python' }} client = OpenAI() mem = Memori().llm.register(client) ``` ```typescript {{ title: 'TypeScript' }} const client = new OpenAI(); const mem = new Memori().llm.register(client); ``` 2. **Set attribution** before LLM calls — without it, no memories are stored: ```python {{ title: 'Python' }} mem.attribution(entity_id="user_123", process_id="my_app") ``` ```typescript {{ title: 'TypeScript' }} mem.attribution('user_123', 'my_app'); ``` ## Recall Returns Empty - Verify `entity_id` matches what was used when memories were created - Increase limit: `mem.recall("query", limit=10)` - Lower threshold: `mem.config.recall_relevance_threshold = 0.05` ## Performance **Network timeouts** — Increase timeout: `mem.config.request_secs_timeout = 10` and retries: `mem.config.request_num_backoff = 10`. ## Debug Logging Enable debug logging to inspect request flow, attribution, and augmentation behavior when troubleshooting missing memories or API errors. Use it temporarily in development, since logs can include sensitive request metadata. ```python {{ title: 'Python' }} import logging logging.basicConfig(level=logging.DEBUG, format="%(asctime)s | %(name)s | %(levelname)s | %(message)s") from memori import Memori mem = Memori() ``` ```typescript {{ title: 'TypeScript' }} import { Memori } from '@memorilabs/memori'; const mem = new Memori({ debug: true }); ``` ## Getting Help - [GitHub Issues](https://github.com/MemoriLabs/Memori/issues) - [Discord](https://discord.gg/abD4eGym6v) - [Examples](https://github.com/MemoriLabs/Memori/tree/main/examples) Include your language, runtime version (Python/Node.js), Memori version (`pip show memori` or check `package.json`), and full error trace. ================================================ FILE: examples/agno/README.md ================================================ # Memori + Agno Example Example showing how to use Memori with Agno agents to add persistent memory across conversations. ## Quick Start 1. **Install dependencies**: ```bash uv sync ``` 2. **Set your OpenAI API key**: Create a `.env` file: ```bash OPENAI_API_KEY=your_api_key_here ``` 3. **Run the example**: ```bash uv run python main.py ``` ## What This Example Demonstrates - **Agno integration**: Use Memori with Agno's agent framework - **Persistent memory**: Conversations are stored in SQLite and recalled automatically - **Context awareness**: The agent remembers details from earlier in the conversation - **Customer support use case**: Shows a realistic scenario where memory is valuable ================================================ FILE: examples/agno/main.py ================================================ """ Memori + Agno + SQLite Example Demonstrates how Memori adds persistent memory to Agno agents. """ import os from agno.agent import Agent from agno.models.openai import OpenAIChat from dotenv import load_dotenv from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori load_dotenv() db_path = os.getenv("DATABASE_PATH", "memori_agno.db") engine = create_engine(f"sqlite:///{db_path}") Session = sessionmaker(bind=engine) model = OpenAIChat(id="gpt-4o-mini") mem = Memori(conn=Session).llm.register(openai_chat=model) mem.attribution(entity_id="customer-456", process_id="support-agent") mem.config.storage.build() agent = Agent( model=model, instructions=[ "You are a helpful customer support agent.", "Remember customer preferences and history from previous conversations.", ], markdown=True, ) if __name__ == "__main__": print("Customer: Hi, I'd like to order a large pepperoni pizza with extra cheese") response1 = agent.run( "Hi, I'd like to order a large pepperoni pizza with extra cheese" ) print(f"Agent: {response1.content}\n") print("Customer: Actually, can you remind me what I just ordered?") response2 = agent.run("Actually, can you remind me what I just ordered?") print(f"Agent: {response2.content}\n") print("Customer: Perfect! And what size was that again?") response3 = agent.run("Perfect! And what size was that again?") print(f"Agent: {response3.content}") # Advanced Augmentation runs asynchronously to efficiently # create memories. For this example, a short lived command # line program, we need to wait for it to finish. mem.augmentation.wait() ================================================ FILE: examples/agno/pyproject.toml ================================================ [project] name = "memori-agno-example" version = "0.1.0" requires-python = ">=3.9" dependencies = [ "memori", "agno", "sqlalchemy", "python-dotenv", ] ================================================ FILE: examples/cockroachdb/README.md ================================================ # Memori + CockroachDB Example **Memori + CockroachDB** brings durable, distributed memory to AI - instantly, globally, and at any scale. Memori transforms conversations into structured, queryable intelligence, while CockroachDB keeps that memory available, resilient, and consistently accurate across regions. Deploy and scale effortlessly from prototype to production with zero downtime on enterprise-grade infrastructure. Give your AI a foundation to remember, reason, and evolve - with the simplicity of cloud and the reliability and power of distributed SQL. ## Getting Started Install Memori: ```bash pip install memori ``` Sign up for [CockroachDB Cloud](https://www.cockroachlabs.com/product/cloud/). You may need to record the database connection string for your implementation. Once you've signed up, your database is provisioned and ready for use with Memori. ## Quick Start 1. **Install dependencies**: ```bash uv sync ``` 2. **Set environment variables**: ```bash export OPENAI_API_KEY=your_api_key_here export COCKROACHDB_CONNECTION_STRING=postgresql://user:password@host:26257/defaultdb?sslmode=verify-full ``` 3. **Run the example**: ```bash uv run python main.py ``` ## What This Example Demonstrates - **Serverless CockroachDB**: Connect to CockroachDB's cloud serverless Postgres with zero database management - **Automatic persistence**: All conversation messages are automatically stored in your CockroachDB database - **Context preservation**: Memori injects relevant conversation history into each LLM call - **Interactive chat**: Type messages and see how Memori maintains context across the conversation ================================================ FILE: examples/cockroachdb/main.py ================================================ """ Quickstart: Memori + OpenAI + CockroachDB Demonstrates how Memori adds memory across conversations. """ import os import psycopg2 from openai import OpenAI from memori import Memori client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) def get_conn(): return psycopg2.connect(os.getenv("COCKROACHDB_CONNECTION_STRING")) mem = Memori(conn=get_conn).llm.register(client) mem.attribution(entity_id="user-123", process_id="my-app") mem.config.storage.build() if __name__ == "__main__": print("You: My favorite color is blue and I live in Paris") response1 = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "My favorite color is blue and I live in Paris"} ], ) print(f"AI: {response1.choices[0].message.content}\n") print("You: What's my favorite color?") response2 = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What's my favorite color?"}], ) print(f"AI: {response2.choices[0].message.content}\n") print("You: What city do I live in?") response3 = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What city do I live in?"}], ) print(f"AI: {response3.choices[0].message.content}") # Advanced Augmentation runs asynchronously to efficiently # create memories. For this example, a short lived command # line program, we need to wait for it to finish. mem.augmentation.wait() ================================================ FILE: examples/cockroachdb/pyproject.toml ================================================ [project] name = "memori-cockroachdb-example" version = "0.1.0" description = "Memori SDK example with CockroachDB" readme = "README.md" requires-python = ">=3.10" dependencies = [ "memori>=3.0.0", "openai>=2.6.1", "psycopg2-binary>=2.9.11", "python-dotenv>=1.2.1", ] ================================================ FILE: examples/digitalocean/README.md ================================================ # Memori + DigitalOcean Gradient Example Example showing how to use Memori with DigitalOcean Gradient AI Agents to add persistent memory across conversations. ## Quick Start 1. **Install dependencies**: ```bash uv sync ``` 2. **Set environment variables**: Create a `.env` file: ```bash AGENT_ENDPOINT=your_gradient_agent_endpoint AGENT_ACCESS_KEY=your_gradient_access_key DATABASE_CONNECTION_STRING=postgresql+psycopg2://user:password@localhost:5432/dbname ``` 3. **Run the example**: ```bash uv run python main.py ``` ## What This Example Demonstrates - **DigitalOcean Gradient integration**: Use Memori with DigitalOcean's Gradient AI platform - **Persistent memory**: Conversations are stored in PostgreSQL and recalled automatically - **OpenAI-compatible API**: Gradient agents use OpenAI's API format for easy integration - **Context awareness**: The agent remembers details from earlier in the conversation ================================================ FILE: examples/digitalocean/main.py ================================================ """ Memori + DigitalOcean Gradient AI Example Demonstrates how Memori adds persistent memory to DigitalOcean Gradient AI Agents. """ import os from dotenv import load_dotenv from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori load_dotenv() agent_endpoint = os.getenv("AGENT_ENDPOINT") agent_access_key = os.getenv("AGENT_ACCESS_KEY") if not agent_endpoint or not agent_access_key: raise ValueError("AGENT_ENDPOINT and AGENT_ACCESS_KEY must be set in .env") base_url = ( agent_endpoint if agent_endpoint.endswith("/api/v1/") else f"{agent_endpoint}/api/v1/" ) client = OpenAI(base_url=base_url, api_key=agent_access_key) database_connection_string = os.getenv("DATABASE_CONNECTION_STRING") if not database_connection_string: raise ValueError("DATABASE_CONNECTION_STRING must be set in .env") engine = create_engine(database_connection_string) Session = sessionmaker(bind=engine) mem = Memori(conn=Session).llm.register(client) mem.attribution(entity_id="user-123", process_id="gradient-agent") mem.config.storage.build() if __name__ == "__main__": print("You: My favorite color is blue and I live in Paris") response1 = client.chat.completions.create( model="n/a", messages=[ {"role": "user", "content": "My favorite color is blue and I live in Paris"} ], ) print(f"AI: {response1.choices[0].message.content}\n") print("You: What's my favorite color?") response2 = client.chat.completions.create( model="n/a", messages=[{"role": "user", "content": "What's my favorite color?"}], ) print(f"AI: {response2.choices[0].message.content}\n") print("You: What city do I live in?") response3 = client.chat.completions.create( model="n/a", messages=[{"role": "user", "content": "What city do I live in?"}], ) print(f"AI: {response3.choices[0].message.content}") # Advanced Augmentation runs asynchronously to efficiently # create memories. For this example, a short lived command # line program, we need to wait for it to finish. mem.augmentation.wait() ================================================ FILE: examples/digitalocean/pyproject.toml ================================================ [project] name = "memori-digitalocean-example" version = "0.1.0" description = "Memori SDK example with DigitalOcean Gradient AI" readme = "README.md" requires-python = ">=3.10" dependencies = [ "memori", "openai>=2.6.1", "SQLAlchemy>=2.0.0", "psycopg2-binary>=2.9.0", "python-dotenv>=1.2.1", ] ================================================ FILE: examples/mongodb/README.md ================================================ # Memori + MongoDB Example Example showing how to use Memori with MongoDB. ## Quick Start 1. **Install dependencies**: ```bash uv sync ``` 2. **Set environment variables**: ```bash export OPENAI_API_KEY=your_api_key_here export MONGODB_CONNECTION_STRING=mongodb+srv://user:password@cluster.mongodb.net/dbname ``` 3. **Run the example**: ```bash uv run python main.py ``` ## What This Example Demonstrates - **NoSQL flexibility**: Store conversation data in MongoDB's document model - **Automatic persistence**: All conversation messages are automatically stored in MongoDB collections - **Context preservation**: Memori injects relevant conversation history into each LLM call - **Interactive chat**: Type messages and see how Memori maintains context across the conversation - **Cloud-ready**: Works seamlessly with MongoDB Atlas free tier ================================================ FILE: examples/mongodb/main.py ================================================ """ Quickstart: Memori + OpenAI + MongoDB Demonstrates how Memori adds memory across conversations. """ import os from openai import OpenAI from pymongo import MongoClient from memori import Memori client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) mongo_client = MongoClient(os.getenv("MONGODB_CONNECTION_STRING")) db = mongo_client["memori"] mem = Memori(conn=lambda: db).llm.register(client) mem.attribution(entity_id="user-123", process_id="my-app") mem.config.storage.build() if __name__ == "__main__": print("You: My favorite color is blue and I live in Paris") response1 = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "My favorite color is blue and I live in Paris"} ], ) print(f"AI: {response1.choices[0].message.content}\n") print("You: What's my favorite color?") response2 = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What's my favorite color?"}], ) print(f"AI: {response2.choices[0].message.content}\n") print("You: What city do I live in?") response3 = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What city do I live in?"}], ) print(f"AI: {response3.choices[0].message.content}") # Advanced Augmentation runs asynchronously to efficiently # create memories. For this example, a short lived command # line program, we need to wait for it to finish. mem.augmentation.wait() ================================================ FILE: examples/mongodb/pyproject.toml ================================================ [project] name = "memori-mongodb-example" version = "0.1.0" description = "Memori SDK example with MongoDB" readme = "README.md" requires-python = ">=3.10" dependencies = [ "memori>=3.0.0", "openai>=2.6.1", "pymongo>=4.7.0", "python-dotenv>=1.2.1", ] ================================================ FILE: examples/nebius/README.md ================================================ # Memori + Nebius AI Studio Example Example showing how to use Memori with Nebius AI Studio to add persistent memory across conversations. [Nebius AI Studio](https://nebius.com/ai-studio) provides an OpenAI-compatible API for state-of-the-art open-source models including Llama, Qwen, DeepSeek, and more. Since Nebius uses the OpenAI SDK, it works seamlessly with Memori's automatic instrumentation. ## Quick Start 1. **Install dependencies**: ```bash uv sync ``` 2. **Get a Nebius API key**: - Sign up at [https://studio.nebius.ai/](https://studio.nebius.ai/) - Get your API key from the dashboard 3. **Set your API key**: Create a `.env` file: ```bash NEBIUS_API_KEY=your_api_key_here ``` 4. **Run the example**: ```bash uv run python main.py ``` ## What This Example Demonstrates - **Nebius AI Studio integration**: Use Memori with Nebius's OpenAI-compatible API - **Open-source models**: Access to models like Llama 3.1 70B with persistent memory - **Automatic registration**: Simply use `.llm.register(client)` - Memori auto-detects Nebius - **Persistent memory**: Conversations are stored in SQLite and recalled automatically - **Context awareness**: The LLM remembers details from earlier in the conversation ## Available Models Nebius AI Studio supports 60+ open-source models. Check the [model catalog](https://studio.nebius.ai/) for the full list including: - Meta Llama 3.1 (8B, 70B, 405B) - Qwen 2.5 - DeepSeek - Mistral - And many more ## Alternative Base URLs Nebius offers multiple endpoints: - Studio API: `https://api.studio.nebius.com/v1/` - Token Factory: `https://api.tokenfactory.nebius.com/` Simply change the `base_url` parameter to use a different endpoint. ================================================ FILE: examples/nebius/main.py ================================================ """ Memori + Nebius AI Studio + SQLite Example Demonstrates how Memori adds persistent memory to Nebius AI Studio LLMs. Nebius AI Studio provides an OpenAI-compatible API with state-of-the-art open-source models. """ import os from dotenv import load_dotenv from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori load_dotenv() db_path = os.getenv("DATABASE_PATH", "memori_nebius.db") engine = create_engine(f"sqlite:///{db_path}") Session = sessionmaker(bind=engine) client = OpenAI( base_url="https://api.studio.nebius.com/v1/", api_key=os.getenv("NEBIUS_API_KEY"), ) mem = Memori(conn=Session).llm.register(client) mem.attribution(entity_id="user-789", process_id="nebius-chat-app") mem.config.storage.build() if __name__ == "__main__": print("User: My favorite color is blue and I live in Paris") response1 = client.chat.completions.create( model="meta-llama/Llama-3.3-70B-Instruct", messages=[ { "role": "user", "content": "My favorite color is blue and I live in Paris.", } ], ) print(f"Assistant: {response1.choices[0].message.content}\n") print("User: What's my favorite color?") response2 = client.chat.completions.create( model="meta-llama/Llama-3.3-70B-Instruct", messages=[{"role": "user", "content": "What's my favorite color?"}], ) print(f"Assistant: {response2.choices[0].message.content}\n") print("User: Where do I live?") response3 = client.chat.completions.create( model="meta-llama/Llama-3.3-70B-Instruct", messages=[{"role": "user", "content": "Where do I live?"}], ) print(f"Assistant: {response3.choices[0].message.content}") # Advanced Augmentation runs asynchronously to efficiently # create memories. For this example, a short lived command # line program, we need to wait for it to finish. mem.augmentation.wait() ================================================ FILE: examples/nebius/pyproject.toml ================================================ [project] name = "memori-nebius-example" version = "0.1.0" requires-python = ">=3.9" dependencies = [ "memori", "openai", "sqlalchemy", "python-dotenv", ] ================================================ FILE: examples/neon/README.md ================================================ # Memori + Neon Example Sign up for [Neon serverless Postgres](https://neon.tech). You may need to record the database connection string for your implementation. Once you've signed up, your database is provisioned and ready for use with Memori. ## Quick Start 1. **Install dependencies**: ```bash uv sync ``` 2. **Set environment variables**: ```bash export OPENAI_API_KEY=your_api_key_here export NEON_CONNECTION_STRING=postgresql://user:pass@ep-xyz-123.us-east-2.aws.neon.tech/dbname?sslmode=require ``` 3. **Run the example**: ```bash uv run python main.py ``` ## What This Example Demonstrates - **Serverless PostgreSQL**: Connect to Neon's serverless Postgres with zero database management - **Automatic persistence**: All conversation messages are automatically stored in your Neon database - **Context preservation**: Memori injects relevant conversation history into each LLM call - **Interactive chat**: Type messages and see how Memori maintains context across the conversation ================================================ FILE: examples/neon/main.py ================================================ """ Quickstart: Memori + OpenAI + Neon Demonstrates how Memori adds memory across conversations. """ import os from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) neon_connection_string = os.getenv("NEON_CONNECTION_STRING") if not neon_connection_string: raise ValueError("NEON_CONNECTION_STRING must be set in the environment") engine = create_engine(neon_connection_string) Session = sessionmaker(bind=engine) mem = Memori(conn=Session).llm.register(client) mem.attribution(entity_id="user-123", process_id="my-app") mem.config.storage.build() if __name__ == "__main__": print("You: My favorite color is blue and I live in Paris") response1 = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "My favorite color is blue and I live in Paris"} ], ) print(f"AI: {response1.choices[0].message.content}\n") print("You: What's my favorite color?") response2 = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What's my favorite color?"}], ) print(f"AI: {response2.choices[0].message.content}\n") print("You: What city do I live in?") response3 = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What city do I live in?"}], ) print(f"AI: {response3.choices[0].message.content}") # Advanced Augmentation runs asynchronously to efficiently # create memories. For this example, a short lived command # line program, we need to wait for it to finish. mem.augmentation.wait() ================================================ FILE: examples/neon/pyproject.toml ================================================ [project] name = "memori-neon-example" version = "0.1.0" description = "Memori SDK example with Neon serverless Postgres" readme = "README.md" requires-python = ">=3.10" dependencies = [ "memori", "openai>=2.6.1", "SQLAlchemy>=2.0.0", "psycopg[binary]>=3.2.0", "psycopg2-binary>=2.9.0", "python-dotenv>=1.2.1", ] [tool.uv.sources] memori = { path = "../../..", editable = true } ================================================ FILE: examples/oceanbase/README.md ================================================ # Memori + OceanBase Example Example showing how to use Memori with OceanBase (or SeekDB). ## Quick Start 1. **Install dependencies**: ```bash uv sync ``` 2. **Set environment variables**: ```bash export OPENAI_API_KEY=your_api_key_here export DATABASE_CONNECTION_STRING=mysql+oceanbase://root:@localhost:2881/memori_test?charset=utf8mb4 ``` 3. **Run the example**: ```bash uv run python main.py ``` ## What This Example Demonstrates - **OceanBase integration**: Connect to OceanBase or SeekDB using the pyobvector dialect - **Automatic persistence**: Conversation messages are stored in OceanBase tables - **Context preservation**: Memori injects relevant history into each LLM call ================================================ FILE: examples/oceanbase/main.py ================================================ """ Quickstart: Memori + OpenAI + OceanBase Demonstrates how Memori adds memory across conversations. """ import os from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.dialects import registry from sqlalchemy.orm import sessionmaker from memori import Memori client = OpenAI( api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_BASE_URL"), ) registry.register("mysql.oceanbase", "pyobvector.schema.dialect", "OceanBaseDialect") database_connection_string = os.getenv("DATABASE_CONNECTION_STRING") if not database_connection_string: raise ValueError("DATABASE_CONNECTION_STRING must be set in the environment") engine = create_engine(database_connection_string, pool_pre_ping=True) Session = sessionmaker(bind=engine) mem = Memori(conn=Session).llm.register(client) mem.attribution(entity_id="user-123", process_id="my-app") mem.config.storage.build() if __name__ == "__main__": model = os.getenv("OPENAI_MODEL", "qwen-plus") print("You: My favorite color is blue and I live in Paris") response1 = client.chat.completions.create( model=model, messages=[ {"role": "user", "content": "My favorite color is blue and I live in Paris"} ], ) print(f"AI: {response1.choices[0].message.content}\n") print("You: What's my favorite color?") response2 = client.chat.completions.create( model=model, messages=[{"role": "user", "content": "What's my favorite color?"}], ) print(f"AI: {response2.choices[0].message.content}\n") print("You: What city do I live in?") response3 = client.chat.completions.create( model=model, messages=[{"role": "user", "content": "What city do I live in?"}], ) print(f"AI: {response3.choices[0].message.content}") # Wait for background augmentation in short-lived scripts. mem.augmentation.wait() ================================================ FILE: examples/postgres/README.md ================================================ # Memori + PostgreSQL Example Example showing how to use Memori with PostgreSQL. ## Quick Start 1. **Install dependencies**: ```bash uv sync ``` 2. **Set environment variables**: ```bash export OPENAI_API_KEY=your_api_key_here export DATABASE_CONNECTION_STRING=postgresql+psycopg://user:password@localhost:5432/dbname ``` 3. **Run the example**: ```bash uv run python main.py ``` ## What This Example Demonstrates - **PostgreSQL integration**: Connect to any PostgreSQL database (local, AWS RDS, or other managed database services) - **Automatic persistence**: All conversation messages are automatically stored in your database - **Context preservation**: Memori injects relevant conversation history into each LLM call - **Interactive chat**: Type messages and see how Memori maintains context across the conversation ================================================ FILE: examples/postgres/main.py ================================================ """ Quickstart: Memori + OpenAI + PostgreSQL Demonstrates how Memori adds memory across conversations. """ import os from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori client = OpenAI(api_key=os.getenv("OPENAI_API_KEY")) database_connection_string = os.getenv("DATABASE_CONNECTION_STRING") if not database_connection_string: raise ValueError("DATABASE_CONNECTION_STRING must be set in the environment") engine = create_engine(database_connection_string) Session = sessionmaker(bind=engine) mem = Memori(conn=Session).llm.register(client) mem.attribution(entity_id="user-123", process_id="my-app") mem.config.storage.build() if __name__ == "__main__": print("You: My favorite color is blue and I live in Paris") response1 = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "My favorite color is blue and I live in Paris"} ], ) print(f"AI: {response1.choices[0].message.content}\n") print("You: What's my favorite color?") response2 = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What's my favorite color?"}], ) print(f"AI: {response2.choices[0].message.content}\n") print("You: What city do I live in?") response3 = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What city do I live in?"}], ) print(f"AI: {response3.choices[0].message.content}") # Advanced Augmentation runs asynchronously to efficiently # create memories. For this example, a short lived command # line program, we need to wait for it to finish. mem.augmentation.wait() ================================================ FILE: examples/postgres/pyproject.toml ================================================ [project] name = "memori-postgres-example" version = "0.1.0" description = "Memori SDK example with PostgreSQL" readme = "README.md" requires-python = ">=3.10" dependencies = [ "memori>=3.0.0", "openai>=2.6.1", "SQLAlchemy>=2.0.0", "psycopg[binary]>=3.2.0", "python-dotenv>=1.2.1", ] ================================================ FILE: examples/sqlite/README.md ================================================ # Memori + SQLite Example Example showing how to use Memori with SQLite. ## Quick Start 1. **Install dependencies**: ```bash uv sync ``` 2. **Set environment variables**: ```bash export OPENAI_API_KEY=your_api_key_here ``` 3. **Run the example**: ```bash uv run python main.py ``` ## What This Example Demonstrates - **Automatic persistence**: All conversation messages are automatically stored in the SQLite database - **Context preservation**: Memori injects relevant conversation history into each LLM call - **Interactive chat**: Type messages and see how Memori maintains context across the conversation - **Portable**: The database file can be copied, backed up, or shared easily ================================================ FILE: examples/sqlite/main.py ================================================ """ Quickstart: Memori + OpenAI + SQLite Demonstrates how Memori adds memory across conversations. """ import os from openai import OpenAI from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker from memori import Memori # Setup OpenAI client = OpenAI(api_key=os.getenv("OPENAI_API_KEY", "")) # Setup SQLite engine = create_engine("sqlite:///memori.db") Session = sessionmaker(bind=engine) # Setup Memori - that's it! mem = Memori(conn=Session).llm.register(client) mem.attribution(entity_id="user-123", process_id="my-app") mem.config.storage.build() if __name__ == "__main__": # First conversation - establish facts print("You: My favorite color is blue and I live in Paris") response1 = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "user", "content": "My favorite color is blue and I live in Paris"} ], ) print(f"AI: {response1.choices[0].message.content}\n") # Second conversation - Memori recalls context automatically print("You: What's my favorite color?") response2 = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What's my favorite color?"}], ) print(f"AI: {response2.choices[0].message.content}\n") # Third conversation - context is maintained print("You: What city do I live in?") response3 = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": "What city do I live in?"}], ) print(f"AI: {response3.choices[0].message.content}") # Advanced Augmentation runs asynchronously to efficiently # create memories. For this example, a short lived command # line program, we need to wait for it to finish. mem.augmentation.wait() ================================================ FILE: examples/sqlite/pyproject.toml ================================================ [project] name = "memori-sqlite-example" version = "0.1.0" description = "Memori SDK example with SQLite" readme = "README.md" requires-python = ">=3.10" dependencies = [ "memori>=3.0.0", "openai>=2.6.1", "SQLAlchemy>=2.0.0", "python-dotenv>=1.2.1", ] ================================================ FILE: integrations/openclaw/.prettierrc.json ================================================ { "semi": true, "trailingComma": "es5", "singleQuote": true, "printWidth": 100, "tabWidth": 2, "arrowParens": "always" } ================================================ FILE: integrations/openclaw/README.md ================================================ [![Memori Labs](https://s3.us-east-1.amazonaws.com/images.memorilabs.ai/banner.png)](https://memorilabs.ai/)

The memory fabric for enterprise AI

Give OpenClaw persistent, structured memory with Memori. Capture what matters, recall it when relevant, and move from lightweight experimentation to production-ready memory infrastructure.

NPM version NPM Downloads License Discord

--- ## Why Memori for OpenClaw? OpenClaw ships with a simple file-first memory system designed for lightweight experimentation. As deployments scale into production environments, teams often run into memory problems that need more structured, deterministic infrastructure. Memori provides a drop-in memory layer purpose-built for agentic systems running OpenClaw in production. It works through OpenClaw's plugin lifecycle, so you get persistent, structured memory without changing your agent logic. ## Common Challenges with Default OpenClaw Memory ### 1. Fact conflicts in long-running agents OpenClaw stores memory as plain markdown files. When facts change or contradict over time, there is no deterministic conflict resolution or lifecycle management. Memori introduces structured memory with update logic, decay policies, and deterministic fact handling. ### 2. Context loss from token limits As sessions grow, context must be compacted to fit within model token limits. Important details can be dropped during compression. Memori stores memory outside the prompt and retrieves the right facts at query time, eliminating compaction loss. ### 3. No relationship reasoning OpenClaw retrieves semantically similar text but does not model relationships between entities. Memori builds structured memory graphs that let agents reason across linked facts, not just retrieve similar chunks. ### 4. Cross-project noise When multiple projects share memory storage, irrelevant context can bleed across workflows. Memori supports scoped memory namespaces to isolate projects and workflows. ### 5. No user-level isolation Default memory systems do not provide deterministic isolation across users. Memori enforces user-scoped memory boundaries for secure multi-user deployments. ## What Changes When You Add Memori? The Memori plugin replaces OpenClaw's flat-file memory workflow with managed, structured memory that is scoped by `entity_id`, `process_id`, and `session_id` and enriched automatically through OpenClaw's existing hooks. | Capability | What changes | | --- | --- | | **Structured memory storage** | Instead of raw markdown blobs, Memori stores conversations, facts, preferences, and knowledge-graph triples as structured records tied to an entity, process, and session. Facts are extracted as subject-predicate-object relationships, deduplicated over time, and connected into a graph so related memories stay queryable instead of being buried in text files. | | **Advanced Augmentation** | After each conversation, Memori processes the user and assistant exchange asynchronously in the background, identifies facts, preferences, skills, and attributes, generates embeddings for semantic search, and updates the knowledge graph without blocking the agent's response path. | | **Intelligent Recall** | Before the agent responds, Memori searches the current entity's stored facts and knowledge graph, ranks memories by semantic relevance and importance, and injects the most useful context into the prompt so durable knowledge survives context-window compression. | | **Production-ready observability** | Memori Cloud gives you dashboard visibility into memory creation, recalls, cache hit rate, sessions, quota usage, top subjects, per-memory retrieval metrics, and knowledge-graph relationships, so you can inspect what was stored and how recall is behaving in production. | The plugin still remains drop-in: OpenClaw handles the agent loop, while Memori adds recall, augmentation, sanitization, and observability around it. ## Quickstart Get persistent memory running in your OpenClaw gateway in three steps. ### Prerequisites - [OpenClaw](https://openclaw.ai) `v2026.3.2` or later - A Memori API key from [app.memorilabs.ai](https://app.memorilabs.ai) - An Entity ID to attribute memories to, such as a user ID, tenant ID, or agent name ### 1. Install and Enable Run the following commands in your terminal to install and enable the plugin: ```bash # 1. Install the plugin from npm openclaw plugins install @memorilabs/openclaw-memori # 2. Enable it in your workspace openclaw plugins enable openclaw-memori # 3. Restart the OpenClaw gateway openclaw gateway restart ``` ### 2. Configure The plugin needs your Memori API key and an Entity ID to function. You can configure this via the OpenClaw CLI or your `openclaw.json` file. ### Option A: Via OpenClaw CLI (Recommended) ```bash openclaw config set plugins.entries.openclaw-memori.config.apiKey "YOUR_MEMORI_API_KEY" openclaw config set plugins.entries.openclaw-memori.config.entityId "your-app-user-id" ``` ### Option B: Via `openclaw.json` Add the following to your `~/.openclaw/openclaw.json` file: ```json { "plugins": { "entries": { "openclaw-memori": { "enabled": true, "config": { "apiKey": "your-memori-api-key", "entityId": "your-app-user-id" } } } } } ``` ### Configuration Options | Option | Type | Required | Description | | ---------- | -------- | -------- | --------------------------------------------------------------------------------------------------- | | `apiKey` | `string` | **Yes** | Your Memori API key. | | `entityId` | `string` | **Yes** | The unique identifier for the entity (e.g., user, agent, or tenant) to attribute these memories to. | ### 3. Verify Restart the gateway and inspect the logs: ```bash openclaw gateway restart openclaw gateway logs --filter "[Memori]" ``` You should see: ```text [Memori] === INITIALIZING PLUGIN === [Memori] Tracking Entity ID: your-app-user-id ``` To test the full memory loop: 1. Send a message with a durable preference: `I always use TypeScript and prefer functional patterns.` 2. Confirm augmentation ran: `Augmentation successful!` 3. Start a new session and ask: `Write a hello world script.` 4. Confirm recall ran: `Successfully injected memory context.` ## How It Works This plugin integrates with OpenClaw's event lifecycle to provide persistent memory without interfering with the agent's core logic: 1. **`before_prompt_build` (Intelligent Recall):** When a user sends a message, the plugin intercepts the event, queries the Memori API, and safely prepends relevant memories to the agent's system context. 2. **`agent_end` (Advanced Augmentation):** Once the agent finishes generating its response, the plugin captures the final `user` and `assistant` messages, sanitizes them, and sends them to the Memori integration endpoint for long-term storage and entity mapping. ## Contributing We welcome contributions from the community! Please see our [Contributing Guidelines](https://github.com/MemoriLabs/Memori/blob/main/CONTRIBUTING.md) for details on code style, standards, and submitting pull requests. To build from source: ```bash # Clone the repository git clone https://github.com/memorilabs/openclaw-memori.git cd openclaw-memori # Install dependencies and build npm install npm run build # Run formatting, linting, and type checking npm run check ``` --- ## Support - **Documentation**: [https://memorilabs.ai/docs](https://memorilabs.ai/docs) - **Discord**: [https://discord.gg/abD4eGym6v](https://discord.gg/abD4eGym6v) - **Issues**: [GitHub Issues](https://github.com/memorilabs/openclaw-memori/issues) --- ## License Apache 2.0 - see [LICENSE](https://github.com/MemoriLabs/Memori/blob/main/LICENSE) ================================================ FILE: integrations/openclaw/eslint.config.js ================================================ import eslint from '@eslint/js'; import tseslint from 'typescript-eslint'; import prettier from 'eslint-config-prettier'; import tsdoc from 'eslint-plugin-tsdoc'; export default tseslint.config( eslint.configs.recommended, ...tseslint.configs.strictTypeChecked, prettier, { languageOptions: { parserOptions: { projectService: true, tsconfigRootDir: import.meta.dirname, }, }, plugins: { tsdoc: tsdoc, }, rules: { '@typescript-eslint/no-unused-vars': [ 'error', { argsIgnorePattern: '^_', varsIgnorePattern: '^_' }, ], '@typescript-eslint/no-explicit-any': 'warn', '@typescript-eslint/no-extraneous-class': 'off', '@typescript-eslint/restrict-template-expressions': 'off', 'tsdoc/syntax': 'warn', }, }, { files: ['tests/**/*.ts'], rules: { '@typescript-eslint/no-explicit-any': 'off', '@typescript-eslint/no-unsafe-assignment': 'off', '@typescript-eslint/no-unsafe-member-access': 'off', '@typescript-eslint/no-unsafe-argument': 'off', '@typescript-eslint/no-unsafe-return': 'off', '@typescript-eslint/no-unsafe-call': 'off', '@typescript-eslint/require-await': 'off', '@typescript-eslint/no-non-null-assertion': 'off', '@typescript-eslint/unbound-method': 'off', }, }, { ignores: [ 'dist/**', 'node_modules/**', '*.config.js', '*.config.ts', 'tests/**', '**/*.test.ts', '**/*.spec.ts', 'coverage', ], } ); ================================================ FILE: integrations/openclaw/openclaw.plugin.json ================================================ { "id": "openclaw-memori", "name": "Memori System", "version": "0.0.2", "description": "Hosted memory backend", "kind": "memory", "main": "dist/index.js", "uiHints": { "apiKey": { "label": "Memori API Key", "sensitive": true, "placeholder": "Enter your Memori API key...", "help": "API key from memorilabs.ai (or use MEMORI_API_KEY env var)" }, "entityId": { "label": "Entity ID", "placeholder": "e.g., your-app-user-id", "help": "Required. The unique identifier to attribute these memories to." } }, "configSchema": { "type": "object", "properties": { "apiKey": { "type": "string", "title": "API Key", "description": "Memori API Key" }, "entityId": { "type": "string", "title": "Entity ID", "description": "Required. Hardcode a specific Entity ID for memories." } }, "required": [] } } ================================================ FILE: integrations/openclaw/package-lock.json ================================================ { "name": "@memorilabs/openclaw-memori", "version": "0.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@memorilabs/openclaw-memori", "version": "0.0.4", "license": "Apache-2.0", "dependencies": { "@memorilabs/memori": "^0.0.5" }, "devDependencies": { "@eslint/js": "^9.0.0", "@types/node": "^20.0.0", "@vitest/coverage-v8": "^4.0.18", "eslint": "^9.0.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-tsdoc": "^0.5.1", "prettier": "^3.0.0", "typescript": "^5.0.0", "typescript-eslint": "^8.0.0", "vitest": "^4.0.18" }, "engines": { "node": ">=22.0.0" }, "peerDependencies": { "openclaw": "^2026.3.2" } }, "node_modules/@agentclientprotocol/sdk": { "version": "0.16.1", "resolved": "https://registry.npmjs.org/@agentclientprotocol/sdk/-/sdk-0.16.1.tgz", "integrity": "sha512-1ad+Sc/0sCtZGHthxxvgEUo5Wsbw16I+aF+YwdiLnPwkZG8KAGUEAPK6LM6Pf69lCyJPt1Aomk1d+8oE3C4ZEw==", "license": "Apache-2.0", "peer": true, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "node_modules/@anthropic-ai/sdk": { "version": "0.73.0", "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.73.0.tgz", "integrity": "sha512-URURVzhxXGJDGUGFunIOtBlSl7KWvZiAAKY/ttTkZAkXT9bTPqdk2eK0b8qqSxXpikh3QKPnPYpiyX98zf5ebw==", "license": "MIT", "peer": true, "dependencies": { "json-schema-to-ts": "^3.1.1" }, "bin": { "anthropic-ai-sdk": "bin/cli" }, "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" }, "peerDependenciesMeta": { "zod": { "optional": true } } }, "node_modules/@aws-crypto/crc32": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/crc32/-/crc32-5.2.0.tgz", "integrity": "sha512-nLbCWqQNgUiwwtFsen1AdzAtvuLRsQS8rYgMuxCrdKf9kOssamGLuPwyTY9wyYblNr9+1XM8v6zoDTPPSIeANg==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@aws-crypto/sha256-browser": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-browser/-/sha256-browser-5.2.0.tgz", "integrity": "sha512-AXfN/lGotSQwu6HNcEsIASo7kWXZ5HYWvfOmSNKDsEqC4OashTp8alTmaz+F7TC2L083SFv5RdB+qU3Vs1kZqw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-crypto/sha256-js": "^5.2.0", "@aws-crypto/supports-web-crypto": "^5.2.0", "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "@aws-sdk/util-locate-window": "^3.0.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/is-array-buffer": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-buffer-from": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-crypto/sha256-browser/node_modules/@smithy/util-utf8": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-crypto/sha256-js": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-5.2.0.tgz", "integrity": "sha512-FFQQyu7edu4ufvIZ+OadFpHHOt+eSTBaYaki44c+akjg7qZg9oOQeLlk77F6tSYqjDAFClrHJk9tMf0HdVyOvA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-crypto/util": "^5.2.0", "@aws-sdk/types": "^3.222.0", "tslib": "^2.6.2" }, "engines": { "node": ">=16.0.0" } }, "node_modules/@aws-crypto/supports-web-crypto": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/supports-web-crypto/-/supports-web-crypto-5.2.0.tgz", "integrity": "sha512-iAvUotm021kM33eCdNfwIN//F77/IADDSs58i+MDaOqFrVjZo9bAal0NK7HurRuWLLpF1iLX7gbWrjHjeo+YFg==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.6.2" } }, "node_modules/@aws-crypto/util": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-5.2.0.tgz", "integrity": "sha512-4RkU9EsI6ZpBve5fseQlGNUWKMa1RLPQ1dnjnQoe07ldfIzcsGb5hC5W0Dm7u423KWzawlrpbjXBrXCEv9zazQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/types": "^3.222.0", "@smithy/util-utf8": "^2.0.0", "tslib": "^2.6.2" } }, "node_modules/@aws-crypto/util/node_modules/@smithy/is-array-buffer": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-2.2.0.tgz", "integrity": "sha512-GGP3O9QFD24uGeAXYUjwSTXARoqpZykHadOmA8G5vfJPK0/DC67qa//0qvqrJzL1xc8WQWX7/yc7fwudjPHPhA==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-crypto/util/node_modules/@smithy/util-buffer-from": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-2.2.0.tgz", "integrity": "sha512-IJdWBbTcMQ6DA0gdNhh/BwrLkDR+ADW5Kr1aZmd4k3DIF6ezMV4R2NIAmT08wQJ3yUK82thHWmC/TnK/wpMMIA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/is-array-buffer": "^2.2.0", "tslib": "^2.6.2" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-crypto/util/node_modules/@smithy/util-utf8": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-2.3.0.tgz", "integrity": "sha512-R8Rdn8Hy72KKcebgLiv8jQcQkXoLMOGGv5uI1/k0l+snqkOzQ1R0ChUBCxWMlBsFMekWjq0wRudIweFs7sKT5A==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/util-buffer-from": "^2.2.0", "tslib": "^2.6.2" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@aws-sdk/client-bedrock": { "version": "3.1007.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock/-/client-bedrock-3.1007.0.tgz", "integrity": "sha512-49hH8o6ALKkCiBUgg20HkwxNamP1yYA/n8Si73Z438EqhZGpCfScP3FfxVhrfD5o+4bV4Whi9BTzPKCa/PfUww==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.19", "@aws-sdk/credential-provider-node": "^3.972.19", "@aws-sdk/middleware-host-header": "^3.972.7", "@aws-sdk/middleware-logger": "^3.972.7", "@aws-sdk/middleware-recursion-detection": "^3.972.7", "@aws-sdk/middleware-user-agent": "^3.972.20", "@aws-sdk/region-config-resolver": "^3.972.7", "@aws-sdk/token-providers": "3.1007.0", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-endpoints": "^3.996.4", "@aws-sdk/util-user-agent-browser": "^3.972.7", "@aws-sdk/util-user-agent-node": "^3.973.5", "@smithy/config-resolver": "^4.4.10", "@smithy/core": "^3.23.9", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/hash-node": "^4.2.11", "@smithy/invalid-dependency": "^4.2.11", "@smithy/middleware-content-length": "^4.2.11", "@smithy/middleware-endpoint": "^4.4.23", "@smithy/middleware-retry": "^4.4.40", "@smithy/middleware-serde": "^4.2.12", "@smithy/middleware-stack": "^4.2.11", "@smithy/node-config-provider": "^4.3.11", "@smithy/node-http-handler": "^4.4.14", "@smithy/protocol-http": "^5.3.11", "@smithy/smithy-client": "^4.12.3", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.39", "@smithy/util-defaults-mode-node": "^4.2.42", "@smithy/util-endpoints": "^3.3.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-retry": "^4.2.11", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/client-bedrock-runtime": { "version": "3.1007.0", "resolved": "https://registry.npmjs.org/@aws-sdk/client-bedrock-runtime/-/client-bedrock-runtime-3.1007.0.tgz", "integrity": "sha512-X7iWTQAZrCvQH2lfrZktVPfR3jdLPNtI4zkk4NA/vXzW5k8VNgdVuWUSm8cAzIXnhV3YThvDpLhEk87igNyGWQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.19", "@aws-sdk/credential-provider-node": "^3.972.19", "@aws-sdk/eventstream-handler-node": "^3.972.10", "@aws-sdk/middleware-eventstream": "^3.972.7", "@aws-sdk/middleware-host-header": "^3.972.7", "@aws-sdk/middleware-logger": "^3.972.7", "@aws-sdk/middleware-recursion-detection": "^3.972.7", "@aws-sdk/middleware-user-agent": "^3.972.20", "@aws-sdk/middleware-websocket": "^3.972.12", "@aws-sdk/region-config-resolver": "^3.972.7", "@aws-sdk/token-providers": "3.1007.0", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-endpoints": "^3.996.4", "@aws-sdk/util-user-agent-browser": "^3.972.7", "@aws-sdk/util-user-agent-node": "^3.973.5", "@smithy/config-resolver": "^4.4.10", "@smithy/core": "^3.23.9", "@smithy/eventstream-serde-browser": "^4.2.11", "@smithy/eventstream-serde-config-resolver": "^4.3.11", "@smithy/eventstream-serde-node": "^4.2.11", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/hash-node": "^4.2.11", "@smithy/invalid-dependency": "^4.2.11", "@smithy/middleware-content-length": "^4.2.11", "@smithy/middleware-endpoint": "^4.4.23", "@smithy/middleware-retry": "^4.4.40", "@smithy/middleware-serde": "^4.2.12", "@smithy/middleware-stack": "^4.2.11", "@smithy/node-config-provider": "^4.3.11", "@smithy/node-http-handler": "^4.4.14", "@smithy/protocol-http": "^5.3.11", "@smithy/smithy-client": "^4.12.3", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.39", "@smithy/util-defaults-mode-node": "^4.2.42", "@smithy/util-endpoints": "^3.3.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-retry": "^4.2.11", "@smithy/util-stream": "^4.5.17", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/core": { "version": "3.973.19", "resolved": "https://registry.npmjs.org/@aws-sdk/core/-/core-3.973.19.tgz", "integrity": "sha512-56KePyOcZnKTWCd89oJS1G6j3HZ9Kc+bh/8+EbvtaCCXdP6T7O7NzCiPuHRhFLWnzXIaXX3CxAz0nI5My9spHQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.5", "@aws-sdk/xml-builder": "^3.972.10", "@smithy/core": "^3.23.9", "@smithy/node-config-provider": "^4.3.11", "@smithy/property-provider": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/signature-v4": "^5.3.11", "@smithy/smithy-client": "^4.12.3", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-env": { "version": "3.972.17", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-env/-/credential-provider-env-3.972.17.tgz", "integrity": "sha512-MBAMW6YELzE1SdkOniqr51mrjapQUv8JXSGxtwRjQV0mwVDutVsn22OPAUt4RcLRvdiHQmNBDEFP9iTeSVCOlA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.19", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-http": { "version": "3.972.19", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-http/-/credential-provider-http-3.972.19.tgz", "integrity": "sha512-9EJROO8LXll5a7eUFqu48k6BChrtokbmgeMWmsH7lBb6lVbtjslUYz/ShLi+SHkYzTomiGBhmzTW7y+H4BxsnA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.19", "@aws-sdk/types": "^3.973.5", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/node-http-handler": "^4.4.14", "@smithy/property-provider": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/smithy-client": "^4.12.3", "@smithy/types": "^4.13.0", "@smithy/util-stream": "^4.5.17", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-ini": { "version": "3.972.18", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-ini/-/credential-provider-ini-3.972.18.tgz", "integrity": "sha512-vthIAXJISZnj2576HeyLBj4WTeX+I7PwWeRkbOa0mVX39K13SCGxCgOFuKj2ytm9qTlLOmXe4cdEnroteFtJfw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.19", "@aws-sdk/credential-provider-env": "^3.972.17", "@aws-sdk/credential-provider-http": "^3.972.19", "@aws-sdk/credential-provider-login": "^3.972.18", "@aws-sdk/credential-provider-process": "^3.972.17", "@aws-sdk/credential-provider-sso": "^3.972.18", "@aws-sdk/credential-provider-web-identity": "^3.972.18", "@aws-sdk/nested-clients": "^3.996.8", "@aws-sdk/types": "^3.973.5", "@smithy/credential-provider-imds": "^4.2.11", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-login": { "version": "3.972.18", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-login/-/credential-provider-login-3.972.18.tgz", "integrity": "sha512-kINzc5BBxdYBkPZ0/i1AMPMOk5b5QaFNbYMElVw5QTX13AKj6jcxnv/YNl9oW9mg+Y08ti19hh01HhyEAxsSJQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.19", "@aws-sdk/nested-clients": "^3.996.8", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/protocol-http": "^5.3.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-node": { "version": "3.972.19", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-node/-/credential-provider-node-3.972.19.tgz", "integrity": "sha512-yDWQ9dFTr+IMxwanFe7+tbN5++q8psZBjlUwOiCXn1EzANoBgtqBwcpYcHaMGtn0Wlfj4NuXdf2JaEx1lz5RaQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/credential-provider-env": "^3.972.17", "@aws-sdk/credential-provider-http": "^3.972.19", "@aws-sdk/credential-provider-ini": "^3.972.18", "@aws-sdk/credential-provider-process": "^3.972.17", "@aws-sdk/credential-provider-sso": "^3.972.18", "@aws-sdk/credential-provider-web-identity": "^3.972.18", "@aws-sdk/types": "^3.973.5", "@smithy/credential-provider-imds": "^4.2.11", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-process": { "version": "3.972.17", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-process/-/credential-provider-process-3.972.17.tgz", "integrity": "sha512-c8G8wT1axpJDgaP3xzcy+q8Y1fTi9A2eIQJvyhQ9xuXrUZhlCfXbC0vM9bM1CUXiZppFQ1p7g0tuUMvil/gCPg==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.19", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-sso": { "version": "3.972.18", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-sso/-/credential-provider-sso-3.972.18.tgz", "integrity": "sha512-YHYEfj5S2aqInRt5ub8nDOX8vAxgMvd84wm2Y3WVNfFa/53vOv9T7WOAqXI25qjj3uEcV46xxfqdDQk04h5XQA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.19", "@aws-sdk/nested-clients": "^3.996.8", "@aws-sdk/token-providers": "3.1005.0", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-sso/node_modules/@aws-sdk/token-providers": { "version": "3.1005.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1005.0.tgz", "integrity": "sha512-vMxd+ivKqSxU9bHx5vmAlFKDAkjGotFU56IOkDa5DaTu1WWwbcse0yFHEm9I537oVvodaiwMl3VBwgHfzQ2rvw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.19", "@aws-sdk/nested-clients": "^3.996.8", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/credential-provider-web-identity": { "version": "3.972.18", "resolved": "https://registry.npmjs.org/@aws-sdk/credential-provider-web-identity/-/credential-provider-web-identity-3.972.18.tgz", "integrity": "sha512-OqlEQpJ+J3T5B96qtC1zLLwkBloechP+fezKbCH0sbd2cCc0Ra55XpxWpk/hRj69xAOYtHvoC4orx6eTa4zU7g==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.19", "@aws-sdk/nested-clients": "^3.996.8", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/eventstream-handler-node": { "version": "3.972.10", "resolved": "https://registry.npmjs.org/@aws-sdk/eventstream-handler-node/-/eventstream-handler-node-3.972.10.tgz", "integrity": "sha512-g2Z9s6Y4iNh0wICaEqutgYgt/Pmhv5Ev9G3eKGFe2w9VuZDhc76vYdop6I5OocmpHV79d4TuLG+JWg5rQIVDVA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/eventstream-codec": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-eventstream": { "version": "3.972.7", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-eventstream/-/middleware-eventstream-3.972.7.tgz", "integrity": "sha512-VWndapHYCfwLgPpCb/xwlMKG4imhFzKJzZcKOEioGn7OHY+6gdr0K7oqy1HZgbLa3ACznZ9fku+DzmAi8fUC0g==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-host-header": { "version": "3.972.7", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-host-header/-/middleware-host-header-3.972.7.tgz", "integrity": "sha512-aHQZgztBFEpDU1BB00VWCIIm85JjGjQW1OG9+98BdmaOpguJvzmXBGbnAiYcciCd+IS4e9BEq664lhzGnWJHgQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-logger": { "version": "3.972.7", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-logger/-/middleware-logger-3.972.7.tgz", "integrity": "sha512-LXhiWlWb26txCU1vcI9PneESSeRp/RYY/McuM4SpdrimQR5NgwaPb4VJCadVeuGWgh6QmqZ6rAKSoL1ob16W6w==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-recursion-detection": { "version": "3.972.7", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-recursion-detection/-/middleware-recursion-detection-3.972.7.tgz", "integrity": "sha512-l2VQdcBcYLzIzykCHtXlbpiVCZ94/xniLIkAj0jpnpjY4xlgZx7f56Ypn+uV1y3gG0tNVytJqo3K9bfMFee7SQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.5", "@aws/lambda-invoke-store": "^0.2.2", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-user-agent": { "version": "3.972.20", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-user-agent/-/middleware-user-agent-3.972.20.tgz", "integrity": "sha512-3kNTLtpUdeahxtnJRnj/oIdLAUdzTfr9N40KtxNhtdrq+Q1RPMdCJINRXq37m4t5+r3H70wgC3opW46OzFcZYA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.19", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-endpoints": "^3.996.4", "@smithy/core": "^3.23.9", "@smithy/protocol-http": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-retry": "^4.2.11", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/middleware-websocket": { "version": "3.972.12", "resolved": "https://registry.npmjs.org/@aws-sdk/middleware-websocket/-/middleware-websocket-3.972.12.tgz", "integrity": "sha512-iyPP6FVDKe/5wy5ojC0akpDFG1vX3FeCUU47JuwN8xfvT66xlEI8qUJZPtN55TJVFzzWZJpWL78eqUE31md08Q==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-format-url": "^3.972.7", "@smithy/eventstream-codec": "^4.2.11", "@smithy/eventstream-serde-browser": "^4.2.11", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/protocol-http": "^5.3.11", "@smithy/signature-v4": "^5.3.11", "@smithy/types": "^4.13.0", "@smithy/util-base64": "^4.3.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">= 14.0.0" } }, "node_modules/@aws-sdk/nested-clients": { "version": "3.996.8", "resolved": "https://registry.npmjs.org/@aws-sdk/nested-clients/-/nested-clients-3.996.8.tgz", "integrity": "sha512-6HlLm8ciMW8VzfB80kfIx16PBA9lOa9Dl+dmCBi78JDhvGlx3I7Rorwi5PpVRkL31RprXnYna3yBf6UKkD/PqA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-crypto/sha256-browser": "5.2.0", "@aws-crypto/sha256-js": "5.2.0", "@aws-sdk/core": "^3.973.19", "@aws-sdk/middleware-host-header": "^3.972.7", "@aws-sdk/middleware-logger": "^3.972.7", "@aws-sdk/middleware-recursion-detection": "^3.972.7", "@aws-sdk/middleware-user-agent": "^3.972.20", "@aws-sdk/region-config-resolver": "^3.972.7", "@aws-sdk/types": "^3.973.5", "@aws-sdk/util-endpoints": "^3.996.4", "@aws-sdk/util-user-agent-browser": "^3.972.7", "@aws-sdk/util-user-agent-node": "^3.973.5", "@smithy/config-resolver": "^4.4.10", "@smithy/core": "^3.23.9", "@smithy/fetch-http-handler": "^5.3.13", "@smithy/hash-node": "^4.2.11", "@smithy/invalid-dependency": "^4.2.11", "@smithy/middleware-content-length": "^4.2.11", "@smithy/middleware-endpoint": "^4.4.23", "@smithy/middleware-retry": "^4.4.40", "@smithy/middleware-serde": "^4.2.12", "@smithy/middleware-stack": "^4.2.11", "@smithy/node-config-provider": "^4.3.11", "@smithy/node-http-handler": "^4.4.14", "@smithy/protocol-http": "^5.3.11", "@smithy/smithy-client": "^4.12.3", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-body-length-node": "^4.2.3", "@smithy/util-defaults-mode-browser": "^4.3.39", "@smithy/util-defaults-mode-node": "^4.2.42", "@smithy/util-endpoints": "^3.3.2", "@smithy/util-middleware": "^4.2.11", "@smithy/util-retry": "^4.2.11", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/region-config-resolver": { "version": "3.972.7", "resolved": "https://registry.npmjs.org/@aws-sdk/region-config-resolver/-/region-config-resolver-3.972.7.tgz", "integrity": "sha512-/Ev/6AI8bvt4HAAptzSjThGUMjcWaX3GX8oERkB0F0F9x2dLSBdgFDiyrRz3i0u0ZFZFQ1b28is4QhyqXTUsVA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/config-resolver": "^4.4.10", "@smithy/node-config-provider": "^4.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/token-providers": { "version": "3.1007.0", "resolved": "https://registry.npmjs.org/@aws-sdk/token-providers/-/token-providers-3.1007.0.tgz", "integrity": "sha512-kKvVyr53vvVc5k6RbvI6jhafxufxO2SkEw8QeEzJqwOXH/IMY7Cm0IyhnBGdqj80iiIIiIM2jGe7Fn3TIdwdrw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/core": "^3.973.19", "@aws-sdk/nested-clients": "^3.996.8", "@aws-sdk/types": "^3.973.5", "@smithy/property-provider": "^4.2.11", "@smithy/shared-ini-file-loader": "^4.4.6", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/types": { "version": "3.973.5", "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.973.5.tgz", "integrity": "sha512-hl7BGwDCWsjH8NkZfx+HgS7H2LyM2lTMAI7ba9c8O0KqdBLTdNJivsHpqjg9rNlAlPyREb6DeDRXUl0s8uFdmQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/util-endpoints": { "version": "3.996.4", "resolved": "https://registry.npmjs.org/@aws-sdk/util-endpoints/-/util-endpoints-3.996.4.tgz", "integrity": "sha512-Hek90FBmd4joCFj+Vc98KLJh73Zqj3s2W56gjAcTkrNLMDI5nIFkG9YpfcJiVI1YlE2Ne1uOQNe+IgQ/Vz2XRA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "@smithy/url-parser": "^4.2.11", "@smithy/util-endpoints": "^3.3.2", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/util-format-url": { "version": "3.972.7", "resolved": "https://registry.npmjs.org/@aws-sdk/util-format-url/-/util-format-url-3.972.7.tgz", "integrity": "sha512-V+PbnWfUl93GuFwsOHsAq7hY/fnm9kElRqR8IexIJr5Rvif9e614X5sGSyz3mVSf1YAZ+VTy63W1/pGdA55zyA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/querystring-builder": "^4.2.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/util-locate-window": { "version": "3.965.5", "resolved": "https://registry.npmjs.org/@aws-sdk/util-locate-window/-/util-locate-window-3.965.5.tgz", "integrity": "sha512-WhlJNNINQB+9qtLtZJcpQdgZw3SCDCpXdUJP7cToGwHbCWCnRckGlc6Bx/OhWwIYFNAn+FIydY8SZ0QmVu3xTQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws-sdk/util-user-agent-browser": { "version": "3.972.7", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-browser/-/util-user-agent-browser-3.972.7.tgz", "integrity": "sha512-7SJVuvhKhMF/BkNS1n0QAJYgvEwYbK2QLKBrzDiwQGiTRU6Yf1f3nehTzm/l21xdAOtWSfp2uWSddPnP2ZtsVw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/types": "^3.973.5", "@smithy/types": "^4.13.0", "bowser": "^2.11.0", "tslib": "^2.6.2" } }, "node_modules/@aws-sdk/util-user-agent-node": { "version": "3.973.5", "resolved": "https://registry.npmjs.org/@aws-sdk/util-user-agent-node/-/util-user-agent-node-3.973.5.tgz", "integrity": "sha512-Dyy38O4GeMk7UQ48RupfHif//gqnOPbq/zlvRssc11E2mClT+aUfc3VS2yD8oLtzqO3RsqQ9I3gOBB4/+HjPOw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-sdk/middleware-user-agent": "^3.972.20", "@aws-sdk/types": "^3.973.5", "@smithy/node-config-provider": "^4.3.11", "@smithy/types": "^4.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" }, "peerDependencies": { "aws-crt": ">=1.0.0" }, "peerDependenciesMeta": { "aws-crt": { "optional": true } } }, "node_modules/@aws-sdk/xml-builder": { "version": "3.972.10", "resolved": "https://registry.npmjs.org/@aws-sdk/xml-builder/-/xml-builder-3.972.10.tgz", "integrity": "sha512-OnejAIVD+CxzyAUrVic7lG+3QRltyja9LoNqCE/1YVs8ichoTbJlVSaZ9iSMcnHLyzrSNtvaOGjSDRP+d/ouFA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.0", "fast-xml-parser": "5.4.1", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@aws/lambda-invoke-store": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/@aws/lambda-invoke-store/-/lambda-invoke-store-0.2.4.tgz", "integrity": "sha512-iY8yvjE0y651BixKNPgmv1WrQc+GZ142sb0z4gYnChDDY2YqI4P/jsSopBWrKfAt7LOJAkOXt7rC/hms+WclQQ==", "license": "Apache-2.0", "peer": true, "engines": { "node": ">=18.0.0" } }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { "version": "7.28.5", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.0.tgz", "integrity": "sha512-IyDgFV5GeDUVX4YdF/3CPULtVGSXXMLh1xVIgdCgxApktqnQV0r7/8Nqthg+8YLGaAtdyIlo2qIdZrbCv4+7ww==", "dev": true, "license": "MIT", "dependencies": { "@babel/types": "^7.29.0" }, "bin": { "parser": "bin/babel-parser.js" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/runtime": { "version": "7.28.6", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "license": "MIT", "peer": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { "version": "7.29.0", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", "dev": true, "license": "MIT", "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@bcoe/v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/@borewit/text-codec": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.2.2.tgz", "integrity": "sha512-DDaRehssg1aNrH4+2hnj1B7vnUGEjU6OIlyRdkMd0aUdIUvKXrJfXsy8LVtXAy7DRvYVluWbMspsRhz2lcW0mQ==", "license": "MIT", "peer": true, "funding": { "type": "github", "url": "https://github.com/sponsors/Borewit" } }, "node_modules/@buape/carbon": { "version": "0.0.0-beta-20260216184201", "resolved": "https://registry.npmjs.org/@buape/carbon/-/carbon-0.0.0-beta-20260216184201.tgz", "integrity": "sha512-u5mgYcigfPVqT7D9gVTGd+3YSflTreQmrWog7ORbb0z5w9eT8ft4rJOdw9fGwr75zMu9kXpSBaAcY2eZoJFSdA==", "license": "MIT", "peer": true, "dependencies": { "@types/node": "^25.0.9", "discord-api-types": "0.38.37" }, "optionalDependencies": { "@cloudflare/workers-types": "4.20260120.0", "@discordjs/voice": "0.19.0", "@hono/node-server": "1.19.9", "@types/bun": "1.3.9", "@types/ws": "8.18.1", "ws": "8.19.0" } }, "node_modules/@buape/carbon/node_modules/@discordjs/voice": { "version": "0.19.0", "resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.19.0.tgz", "integrity": "sha512-UyX6rGEXzVyPzb1yvjHtPfTlnLvB5jX/stAMdiytHhfoydX+98hfympdOwsnTktzr+IRvphxTbdErgYDJkEsvw==", "license": "Apache-2.0", "optional": true, "peer": true, "dependencies": { "@types/ws": "^8.18.1", "discord-api-types": "^0.38.16", "prism-media": "^1.3.5", "tslib": "^2.8.1", "ws": "^8.18.3" }, "engines": { "node": ">=22.12.0" }, "funding": { "url": "https://github.com/discordjs/discord.js?sponsor" } }, "node_modules/@buape/carbon/node_modules/@types/node": { "version": "25.5.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", "license": "MIT", "peer": true, "dependencies": { "undici-types": "~7.18.0" } }, "node_modules/@buape/carbon/node_modules/discord-api-types": { "version": "0.38.37", "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.37.tgz", "integrity": "sha512-Cv47jzY1jkGkh5sv0bfHYqGgKOWO1peOrGMkDFM4UmaGMOTgOW8QSexhvixa9sVOiz8MnVOBryWYyw/CEVhj7w==", "license": "MIT", "peer": true, "workspaces": [ "scripts/actions/documentation" ] }, "node_modules/@buape/carbon/node_modules/opusscript": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/opusscript/-/opusscript-0.0.8.tgz", "integrity": "sha512-VSTi1aWFuCkRCVq+tx/BQ5q9fMnQ9pVZ3JU4UHKqTkf0ED3fKEPdr+gKAAl3IA2hj9rrP6iyq3hlcJq3HELtNQ==", "extraneous": true, "license": "MIT" }, "node_modules/@buape/carbon/node_modules/prism-media": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.5.tgz", "integrity": "sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==", "license": "Apache-2.0", "optional": true, "peer": true, "peerDependencies": { "@discordjs/opus": ">=0.8.0 <1.0.0", "ffmpeg-static": "^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0", "node-opus": "^0.3.3", "opusscript": "^0.0.8" }, "peerDependenciesMeta": { "@discordjs/opus": { "optional": true }, "ffmpeg-static": { "optional": true }, "node-opus": { "optional": true }, "opusscript": { "optional": true } } }, "node_modules/@buape/carbon/node_modules/undici-types": { "version": "7.18.2", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", "license": "MIT", "peer": true }, "node_modules/@cacheable/memory": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/@cacheable/memory/-/memory-2.0.8.tgz", "integrity": "sha512-FvEb29x5wVwu/Kf93IWwsOOEuhHh6dYCJF3vcKLzXc0KXIW181AOzv6ceT4ZpBHDvAfG60eqb+ekmrnLHIy+jw==", "license": "MIT", "peer": true, "dependencies": { "@cacheable/utils": "^2.4.0", "@keyv/bigmap": "^1.3.1", "hookified": "^1.15.1", "keyv": "^5.6.0" } }, "node_modules/@cacheable/memory/node_modules/@keyv/bigmap": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@keyv/bigmap/-/bigmap-1.3.1.tgz", "integrity": "sha512-WbzE9sdmQtKy8vrNPa9BRnwZh5UF4s1KTmSK0KUVLo3eff5BlQNNWDnFOouNpKfPKDnms9xynJjsMYjMaT/aFQ==", "license": "MIT", "peer": true, "dependencies": { "hashery": "^1.4.0", "hookified": "^1.15.0" }, "engines": { "node": ">= 18" }, "peerDependencies": { "keyv": "^5.6.0" } }, "node_modules/@cacheable/memory/node_modules/keyv": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "license": "MIT", "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } }, "node_modules/@cacheable/node-cache": { "version": "1.7.6", "resolved": "https://registry.npmjs.org/@cacheable/node-cache/-/node-cache-1.7.6.tgz", "integrity": "sha512-6Omk2SgNnjtxB5f/E6bTIWIt5xhdpx39fGNRQgU9lojvRxU68v+qY+SXXLsp3ZGukqoPjsK21wZ6XABFr/Ge3A==", "license": "MIT", "peer": true, "dependencies": { "cacheable": "^2.3.1", "hookified": "^1.14.0", "keyv": "^5.5.5" }, "engines": { "node": ">=18" } }, "node_modules/@cacheable/node-cache/node_modules/keyv": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "license": "MIT", "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } }, "node_modules/@cacheable/utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/@cacheable/utils/-/utils-2.4.0.tgz", "integrity": "sha512-PeMMsqjVq+bF0WBsxFBxr/WozBJiZKY0rUojuaCoIaKnEl3Ju1wfEwS+SV1DU/cSe8fqHIPiYJFif8T3MVt4cQ==", "license": "MIT", "peer": true, "dependencies": { "hashery": "^1.5.0", "keyv": "^5.6.0" } }, "node_modules/@cacheable/utils/node_modules/keyv": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "license": "MIT", "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } }, "node_modules/@clack/core": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@clack/core/-/core-1.1.0.tgz", "integrity": "sha512-SVcm4Dqm2ukn64/8Gub2wnlA5nS2iWJyCkdNHcvNHPIeBTGojpdJ+9cZKwLfmqy7irD4N5qLteSilJlE0WLAtA==", "license": "MIT", "peer": true, "dependencies": { "sisteransi": "^1.0.5" } }, "node_modules/@clack/prompts": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@clack/prompts/-/prompts-1.1.0.tgz", "integrity": "sha512-pkqbPGtohJAvm4Dphs2M8xE29ggupihHdy1x84HNojZuMtFsHiUlRvqD24tM2+XmI+61LlfNceM3Wr7U5QES5g==", "license": "MIT", "peer": true, "dependencies": { "@clack/core": "1.1.0", "sisteransi": "^1.0.5" } }, "node_modules/@cloudflare/workers-types": { "version": "4.20260120.0", "resolved": "https://registry.npmjs.org/@cloudflare/workers-types/-/workers-types-4.20260120.0.tgz", "integrity": "sha512-B8pueG+a5S+mdK3z8oKu1ShcxloZ7qWb68IEyLLaepvdryIbNC7JVPcY0bWsjS56UQVKc5fnyRge3yZIwc9bxw==", "license": "MIT OR Apache-2.0", "optional": true, "peer": true }, "node_modules/@discordjs/voice": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@discordjs/voice/-/voice-0.19.1.tgz", "integrity": "sha512-XYbFVyUBB7zhRvrjREfiWDwio24nEp/vFaVe6u9aBIC5UYuT7HvoMt8LgNfZ5hOyaCW0flFr72pkhUGz+gWw4Q==", "license": "Apache-2.0", "peer": true, "dependencies": { "@snazzah/davey": "^0.1.9", "@types/ws": "^8.18.1", "discord-api-types": "^0.38.41", "prism-media": "^1.3.5", "tslib": "^2.8.1", "ws": "^8.19.0" }, "engines": { "node": ">=22.12.0" }, "funding": { "url": "https://github.com/discordjs/discord.js?sponsor" } }, "node_modules/@discordjs/voice/node_modules/opusscript": { "version": "0.0.8", "resolved": "https://registry.npmjs.org/opusscript/-/opusscript-0.0.8.tgz", "integrity": "sha512-VSTi1aWFuCkRCVq+tx/BQ5q9fMnQ9pVZ3JU4UHKqTkf0ED3fKEPdr+gKAAl3IA2hj9rrP6iyq3hlcJq3HELtNQ==", "extraneous": true, "license": "MIT" }, "node_modules/@discordjs/voice/node_modules/prism-media": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/prism-media/-/prism-media-1.3.5.tgz", "integrity": "sha512-IQdl0Q01m4LrkN1EGIE9lphov5Hy7WWlH6ulf5QdGePLlPas9p2mhgddTEHrlaXYjjFToM1/rWuwF37VF4taaA==", "license": "Apache-2.0", "peer": true, "peerDependencies": { "@discordjs/opus": ">=0.8.0 <1.0.0", "ffmpeg-static": "^5.0.2 || ^4.2.7 || ^3.0.0 || ^2.4.0", "node-opus": "^0.3.3", "opusscript": "^0.0.8" }, "peerDependenciesMeta": { "@discordjs/opus": { "optional": true }, "ffmpeg-static": { "optional": true }, "node-opus": { "optional": true }, "opusscript": { "optional": true } } }, "node_modules/@emnapi/core": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@emnapi/core/-/core-1.9.0.tgz", "integrity": "sha512-0DQ98G9ZQZOxfUcQn1waV2yS8aWdZ6kJMbYCJB3oUBecjWYO1fqJ+a1DRfPF3O5JEkwqwP1A9QEN/9mYm2Yd0w==", "license": "MIT", "optional": true, "dependencies": { "@emnapi/wasi-threads": "1.2.0", "tslib": "^2.4.0" } }, "node_modules/@emnapi/runtime": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.9.0.tgz", "integrity": "sha512-QN75eB0IH2ywSpRpNddCRfQIhmJYBCJ1x5Lb3IscKAL8bMnVAKnRg8dCoXbHzVLLH7P38N2Z3mtulB7W0J0FKw==", "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@emnapi/wasi-threads/-/wasi-threads-1.2.0.tgz", "integrity": "sha512-N10dEJNSsUx41Z6pZsXU8FjPjpBEplgH24sfkmITrBED1/U2Esum9F3lfLrMjKHHjmi557zQn7kR9R+XWXu5Rg==", "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/eslint-utils/node_modules/eslint-visitor-keys": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint-community/regexpp": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { "version": "0.21.2", "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.2.tgz", "integrity": "sha512-nJl2KGTlrf9GjLimgIru+V/mzgSK0ABCDQRvxw5BjURL7WfH5uoWmizbH7QB6MmnMBd8cIC9uceWnezL1VZWWw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.5" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/config-helpers": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.17.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/core": { "version": "0.17.0", "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@types/json-schema": "^7.0.15" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.5.tgz", "integrity": "sha512-4IlJx0X0qftVsN5E+/vGujTRIFtwuLbNsVUe7TO6zYPDR1O6nFwvwhIKEKSrl6dZchmYBITazxKoUYOjdtjlRg==", "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.14.0", "debug": "^4.3.2", "espree": "^10.0.1", "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.1", "minimatch": "^3.1.5", "strip-json-comments": "^3.1.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/js": { "version": "9.39.4", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.4.tgz", "integrity": "sha512-nE7DEIchvtiFTwBw4Lfbu59PG+kCofhjsKaCWzxTpt4lfRjRMqG6uMBzKXuEcyXhOHoUp9riAm7/aWYGhXZ9cw==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { "version": "2.1.7", "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, "license": "Apache-2.0", "dependencies": { "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@google/genai": { "version": "1.44.0", "resolved": "https://registry.npmjs.org/@google/genai/-/genai-1.44.0.tgz", "integrity": "sha512-kRt9ZtuXmz+tLlcNntN/VV4LRdpl6ZOu5B1KbfNgfR65db15O6sUQcwnwLka8sT/V6qysD93fWrgJHF2L7dA9A==", "license": "Apache-2.0", "peer": true, "dependencies": { "google-auth-library": "^10.3.0", "p-retry": "^4.6.2", "protobufjs": "^7.5.4", "ws": "^8.18.0" }, "engines": { "node": ">=20.0.0" }, "peerDependencies": { "@modelcontextprotocol/sdk": "^1.25.2" }, "peerDependenciesMeta": { "@modelcontextprotocol/sdk": { "optional": true } } }, "node_modules/@grammyjs/runner": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@grammyjs/runner/-/runner-2.0.3.tgz", "integrity": "sha512-nckmTs1dPWfVQteK9cxqxzE+0m1VRvluLWB8UgFzsjg62w3qthPJt0TYtJBEdG7OedvfQq4vnFAyE6iaMkR42A==", "license": "MIT", "peer": true, "dependencies": { "abort-controller": "^3.0.0" }, "engines": { "node": ">=12.20.0 || >=14.13.1" }, "peerDependencies": { "grammy": "^1.13.1" } }, "node_modules/@grammyjs/transformer-throttler": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@grammyjs/transformer-throttler/-/transformer-throttler-1.2.1.tgz", "integrity": "sha512-CpWB0F3rJdUiKsq7826QhQsxbZi4wqfz1ccKX+fr+AOC+o8K7ZvS+wqX0suSu1QCsyUq2MDpNiKhyL2ZOJUS4w==", "license": "MIT", "peer": true, "dependencies": { "bottleneck": "^2.0.0" }, "engines": { "node": "^12.20.0 || >=14.13.1" }, "peerDependencies": { "grammy": "^1.0.0" } }, "node_modules/@grammyjs/types": { "version": "3.25.0", "resolved": "https://registry.npmjs.org/@grammyjs/types/-/types-3.25.0.tgz", "integrity": "sha512-iN9i5p+8ZOu9OMxWNcguojQfz4K/PDyMPOnL7PPCON+SoA/F8OKMH3uR7CVUkYfdNe0GCz8QOzAWrnqusQYFOg==", "license": "MIT", "peer": true }, "node_modules/@hapi/boom": { "version": "9.1.4", "resolved": "https://registry.npmjs.org/@hapi/boom/-/boom-9.1.4.tgz", "integrity": "sha512-Ls1oH8jaN1vNsqcaHVYJrKmgMcKsC1wcp8bujvXrHaAqD2iDYq3HoOwsxwo09Cuda5R5nC0o0IxlrlTuvPuzSw==", "license": "BSD-3-Clause", "peer": true, "dependencies": { "@hapi/hoek": "9.x.x" } }, "node_modules/@hapi/hoek": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/@hapi/hoek/-/hoek-9.3.0.tgz", "integrity": "sha512-/c6rf4UJlmHlC9b5BaNvzAcFv7HZ2QHaV0D4/HNlBdvFnvQq8RI4kYdhyPCl7Xj+oWvTWQ8ujhqS53LIgAe6KQ==", "license": "BSD-3-Clause", "peer": true }, "node_modules/@homebridge/ciao": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@homebridge/ciao/-/ciao-1.3.5.tgz", "integrity": "sha512-f7MAw7YuoEYgJEQ1VyRcLHGuVmCpmXi65GVR8CAtPWPqIZf/HFr4vHzVpOfQMpEQw9Pt5uh07guuLt5HE8ruog==", "license": "MIT", "peer": true, "dependencies": { "debug": "^4.4.3", "fast-deep-equal": "^3.1.3", "source-map-support": "^0.5.21", "tslib": "^2.8.1" }, "bin": { "ciao-bcs": "lib/bonjour-conformance-testing.js" } }, "node_modules/@hono/node-server": { "version": "1.19.11", "resolved": "https://registry.npmjs.org/@hono/node-server/-/node-server-1.19.11.tgz", "integrity": "sha512-dr8/3zEaB+p0D2n/IUrlPF1HZm586qgJNXK1a9fhg/PzdtkK7Ksd5l312tJX2yBuALqDYBlG20QEbayqPyxn+g==", "license": "MIT", "optional": true, "peer": true, "engines": { "node": ">=18.14.1" }, "peerDependencies": { "hono": "^4" } }, "node_modules/@huggingface/jinja": { "version": "0.5.6", "resolved": "https://registry.npmjs.org/@huggingface/jinja/-/jinja-0.5.6.tgz", "integrity": "sha512-MyMWyLnjqo+KRJYSH7oWNbsOn5onuIvfXYPcc0WOGxU0eHUV7oAYUoQTl2BMdu7ml+ea/bu11UM+EshbeHwtIA==", "license": "MIT", "peer": true, "engines": { "node": ">=18" } }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18.0" } }, "node_modules/@humanfs/node": { "version": "0.16.7", "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", "dev": true, "license": "Apache-2.0", "dependencies": { "@humanfs/core": "^0.19.1", "@humanwhocodes/retry": "^0.4.0" }, "engines": { "node": ">=18.18.0" } }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.22" }, "funding": { "type": "github", "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@humanwhocodes/retry": { "version": "0.4.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=18.18" }, "funding": { "type": "github", "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@img/colour": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.1.0.tgz", "integrity": "sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==", "license": "MIT", "peer": true, "engines": { "node": ">=18" } }, "node_modules/@img/sharp-darwin-arm64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.5.tgz", "integrity": "sha512-imtQ3WMJXbMY4fxb/Ndp6HBTNVtWCUI0WdobyheGf5+ad6xX8VIDO8u2xE4qc/fr08CKG/7dDseFtn6M6g/r3w==", "cpu": [ "arm64" ], "license": "Apache-2.0", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, "node_modules/@img/sharp-darwin-x64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", "cpu": [ "x64" ], "license": "Apache-2.0", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { "@img/sharp-libvips-darwin-x64": "1.2.4" } }, "node_modules/@img/sharp-libvips-darwin-arm64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.4.tgz", "integrity": "sha512-zqjjo7RatFfFoP0MkQ51jfuFZBnVE2pRiaydKJ1G/rHZvnsrHAOcQALIi9sA5co5xenQdTugCvtb1cuf78Vf4g==", "cpu": [ "arm64" ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ "darwin" ], "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-darwin-x64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", "cpu": [ "x64" ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ "darwin" ], "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-arm": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", "cpu": [ "arm" ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-arm64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", "cpu": [ "arm64" ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-ppc64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", "cpu": [ "ppc64" ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-riscv64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", "cpu": [ "riscv64" ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-s390x": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", "cpu": [ "s390x" ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linux-x64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", "cpu": [ "x64" ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linuxmusl-arm64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", "cpu": [ "arm64" ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-libvips-linuxmusl-x64": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", "cpu": [ "x64" ], "license": "LGPL-3.0-or-later", "optional": true, "os": [ "linux" ], "peer": true, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-linux-arm": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", "cpu": [ "arm" ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { "@img/sharp-libvips-linux-arm": "1.2.4" } }, "node_modules/@img/sharp-linux-arm64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", "cpu": [ "arm64" ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { "@img/sharp-libvips-linux-arm64": "1.2.4" } }, "node_modules/@img/sharp-linux-ppc64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", "cpu": [ "ppc64" ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { "@img/sharp-libvips-linux-ppc64": "1.2.4" } }, "node_modules/@img/sharp-linux-riscv64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", "cpu": [ "riscv64" ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { "@img/sharp-libvips-linux-riscv64": "1.2.4" } }, "node_modules/@img/sharp-linux-s390x": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", "cpu": [ "s390x" ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { "@img/sharp-libvips-linux-s390x": "1.2.4" } }, "node_modules/@img/sharp-linux-x64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", "cpu": [ "x64" ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { "@img/sharp-libvips-linux-x64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-arm64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", "cpu": [ "arm64" ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" } }, "node_modules/@img/sharp-linuxmusl-x64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", "cpu": [ "x64" ], "license": "Apache-2.0", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { "@img/sharp-libvips-linuxmusl-x64": "1.2.4" } }, "node_modules/@img/sharp-wasm32": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", "cpu": [ "wasm32" ], "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", "optional": true, "peer": true, "dependencies": { "@emnapi/runtime": "^1.7.0" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-arm64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", "cpu": [ "arm64" ], "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-ia32": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", "cpu": [ "ia32" ], "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@img/sharp-win32-x64": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", "cpu": [ "x64" ], "license": "Apache-2.0 AND LGPL-3.0-or-later", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" } }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "license": "ISC", "peer": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", "strip-ansi": "^7.0.1", "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", "wrap-ansi": "^8.1.0", "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" }, "engines": { "node": ">=12" } }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", "peer": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", "license": "MIT", "peer": true }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "license": "MIT", "peer": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", "strip-ansi": "^7.0.1" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "license": "MIT", "peer": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", "strip-ansi": "^7.0.1" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/@isaacs/fs-minipass": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", "license": "ISC", "peer": true, "dependencies": { "minipass": "^7.0.4" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.5", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", "dev": true, "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.31", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@keyv/serialize": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", "license": "MIT", "peer": true }, "node_modules/@kwsites/file-exists": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/file-exists/-/file-exists-1.1.1.tgz", "integrity": "sha512-m9/5YGR18lIwxSFDwfE3oA7bWuq9kdau6ugN4H2rJeyhFQZcG9AgSHkQtSD15a8WvTgfz9aikZMrKPHvbpqFiw==", "license": "MIT", "peer": true, "dependencies": { "debug": "^4.1.1" } }, "node_modules/@kwsites/promise-deferred": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@kwsites/promise-deferred/-/promise-deferred-1.1.1.tgz", "integrity": "sha512-GaHYm+c0O9MjZRu0ongGBRbinu8gVAMd2UZjji6jVmqKtZluZnptXGWhz1E8j8D2HJ3f/yMxKAUC0b+57wncIw==", "license": "MIT", "peer": true }, "node_modules/@larksuiteoapi/node-sdk": { "version": "1.59.0", "resolved": "https://registry.npmjs.org/@larksuiteoapi/node-sdk/-/node-sdk-1.59.0.tgz", "integrity": "sha512-sBpkruTvZDOxnVtoTbepWKRX0j1Y1ZElQYu0x7+v088sI9pcpbVp6ZzCGn62dhrKPatzNyCJyzYCPXPYQWccrA==", "license": "MIT", "peer": true, "dependencies": { "axios": "~1.13.3", "lodash.identity": "^3.0.0", "lodash.merge": "^4.6.2", "lodash.pickby": "^4.6.0", "protobufjs": "^7.2.6", "qs": "^6.14.2", "ws": "^8.19.0" } }, "node_modules/@line/bot-sdk": { "version": "10.6.0", "resolved": "https://registry.npmjs.org/@line/bot-sdk/-/bot-sdk-10.6.0.tgz", "integrity": "sha512-4hSpglL/G/cW2JCcohaYz/BS0uOSJNV9IEYdMm0EiPEvDLayoI2hGq2D86uYPQFD2gvgkyhmzdShpWLG3P5r3w==", "license": "Apache-2.0", "peer": true, "dependencies": { "@types/node": "^24.0.0" }, "engines": { "node": ">=20" }, "optionalDependencies": { "axios": "^1.7.4" } }, "node_modules/@line/bot-sdk/node_modules/@types/node": { "version": "24.12.0", "resolved": "https://registry.npmjs.org/@types/node/-/node-24.12.0.tgz", "integrity": "sha512-GYDxsZi3ChgmckRT9HPU0WEhKLP08ev/Yfcq2AstjrDASOYCSXeyjDsHg4v5t4jOj7cyDX3vmprafKlWIG9MXQ==", "license": "MIT", "peer": true, "dependencies": { "undici-types": "~7.16.0" } }, "node_modules/@line/bot-sdk/node_modules/undici-types": { "version": "7.16.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "license": "MIT", "peer": true }, "node_modules/@lydell/node-pty": { "version": "1.2.0-beta.3", "resolved": "https://registry.npmjs.org/@lydell/node-pty/-/node-pty-1.2.0-beta.3.tgz", "integrity": "sha512-ngGAItlRhmJXrhspxt8kX13n1dVFqzETOq0m/+gqSkO8NJBvNMwP7FZckMwps2UFySdr4yxCXNGu/bumg5at6A==", "license": "MIT", "peer": true, "optionalDependencies": { "@lydell/node-pty-darwin-arm64": "1.2.0-beta.3", "@lydell/node-pty-darwin-x64": "1.2.0-beta.3", "@lydell/node-pty-linux-arm64": "1.2.0-beta.3", "@lydell/node-pty-linux-x64": "1.2.0-beta.3", "@lydell/node-pty-win32-arm64": "1.2.0-beta.3", "@lydell/node-pty-win32-x64": "1.2.0-beta.3" } }, "node_modules/@lydell/node-pty-darwin-arm64": { "version": "1.2.0-beta.3", "resolved": "https://registry.npmjs.org/@lydell/node-pty-darwin-arm64/-/node-pty-darwin-arm64-1.2.0-beta.3.tgz", "integrity": "sha512-owcv+e1/OSu3bf9ZBdUQqJsQF888KyuSIiPYFNn0fLhgkhm9F3Pvha76Kj5mCPnodf7hh3suDe7upw7GPRXftQ==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true }, "node_modules/@lydell/node-pty-darwin-x64": { "version": "1.2.0-beta.3", "resolved": "https://registry.npmjs.org/@lydell/node-pty-darwin-x64/-/node-pty-darwin-x64-1.2.0-beta.3.tgz", "integrity": "sha512-k38O+UviWrWdxtqZBBc/D8NJU11Rey8Y2YMwSWNxLv3eXZZdF5IVpbBkI/2RmLsV5nCcciqLPbukxeZnEfPlwA==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true }, "node_modules/@lydell/node-pty-linux-arm64": { "version": "1.2.0-beta.3", "resolved": "https://registry.npmjs.org/@lydell/node-pty-linux-arm64/-/node-pty-linux-arm64-1.2.0-beta.3.tgz", "integrity": "sha512-HUwRpGu3O+4sv9DAQFKnyW5LYhyYu2SDUa/bdFO/t4dIFCM4uDJEq47wfRM7+aYtJTi1b3lakN8SlWeuFQqJQQ==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true }, "node_modules/@lydell/node-pty-linux-x64": { "version": "1.2.0-beta.3", "resolved": "https://registry.npmjs.org/@lydell/node-pty-linux-x64/-/node-pty-linux-x64-1.2.0-beta.3.tgz", "integrity": "sha512-+RRY0PoCUeQaCvPR7/UnkGbxulwbFtoTWJfe+o4T1RcNtngrgaI55I9nl8CD8uqhGrB3smKuyvPM5UtwGhASUw==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true }, "node_modules/@lydell/node-pty-win32-arm64": { "version": "1.2.0-beta.3", "resolved": "https://registry.npmjs.org/@lydell/node-pty-win32-arm64/-/node-pty-win32-arm64-1.2.0-beta.3.tgz", "integrity": "sha512-UEDd9ASp2M3iIYpIzfmfBlpyn4+K1G4CAjYcHWStptCkefoSVXWTiUBIa1KjBjZi3/xmsHIDpBEYTkGWuvLt2Q==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true }, "node_modules/@lydell/node-pty-win32-x64": { "version": "1.2.0-beta.3", "resolved": "https://registry.npmjs.org/@lydell/node-pty-win32-x64/-/node-pty-win32-x64-1.2.0-beta.3.tgz", "integrity": "sha512-TpdqSFYx7/Rj+68tuP6F/lkRYrHCYAIJgaS1bx3SctTkb5QAQCFwOKHd4xlsivmEOMT2LdhkJggPxwX9PAO5pQ==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true }, "node_modules/@mariozechner/clipboard": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@mariozechner/clipboard/-/clipboard-0.3.2.tgz", "integrity": "sha512-IHQpksNjo7EAtGuHFU+tbWDp5LarH3HU/8WiB9O70ZEoBPHOg0/6afwSLK0QyNMMmx4Bpi/zl6+DcBXe95nWYA==", "license": "MIT", "optional": true, "peer": true, "engines": { "node": ">= 10" }, "optionalDependencies": { "@mariozechner/clipboard-darwin-arm64": "0.3.2", "@mariozechner/clipboard-darwin-universal": "0.3.2", "@mariozechner/clipboard-darwin-x64": "0.3.2", "@mariozechner/clipboard-linux-arm64-gnu": "0.3.2", "@mariozechner/clipboard-linux-arm64-musl": "0.3.2", "@mariozechner/clipboard-linux-riscv64-gnu": "0.3.2", "@mariozechner/clipboard-linux-x64-gnu": "0.3.2", "@mariozechner/clipboard-linux-x64-musl": "0.3.2", "@mariozechner/clipboard-win32-arm64-msvc": "0.3.2", "@mariozechner/clipboard-win32-x64-msvc": "0.3.2" } }, "node_modules/@mariozechner/clipboard-darwin-arm64": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-arm64/-/clipboard-darwin-arm64-0.3.2.tgz", "integrity": "sha512-uBf6K7Je1ihsgvmWxA8UCGCeI+nbRVRXoarZdLjl6slz94Zs1tNKFZqx7aCI5O1i3e0B6ja82zZ06BWrl0MCVw==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-darwin-universal": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-universal/-/clipboard-darwin-universal-0.3.2.tgz", "integrity": "sha512-mxSheKTW2U9LsBdXy0SdmdCAE5HqNS9QUmpNHLnfJ+SsbFKALjEZc5oRrVMXxGQSirDvYf5bjmRyT0QYYonnlg==", "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-darwin-x64": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-darwin-x64/-/clipboard-darwin-x64-0.3.2.tgz", "integrity": "sha512-U1BcVEoidvwIp95+HJswSW+xr28EQiHR7rZjH6pn8Sja5yO4Yoe3yCN0Zm8Lo72BbSOK/fTSq0je7CJpaPCspg==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-linux-arm64-gnu": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-gnu/-/clipboard-linux-arm64-gnu-0.3.2.tgz", "integrity": "sha512-BsinwG3yWTIjdgNCxsFlip7LkfwPk+ruw/aFCXHUg/fb5XC/Ksp+YMQ7u0LUtiKzIv/7LMXgZInJQH6gxbAaqQ==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-linux-arm64-musl": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-arm64-musl/-/clipboard-linux-arm64-musl-0.3.2.tgz", "integrity": "sha512-0/Gi5Xq2V6goXBop19ePoHvXsmJD9SzFlO3S+d6+T2b+BlPcpOu3Oa0wTjl+cZrLAAEzA86aPNBI+VVAFDFPKw==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-linux-riscv64-gnu": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-riscv64-gnu/-/clipboard-linux-riscv64-gnu-0.3.2.tgz", "integrity": "sha512-2AFFiXB24qf0zOZsxI1GJGb9wQGlOJyN6UwoXqmKS3dpQi/l6ix30IzDDA4c4ZcCcx4D+9HLYXhC1w7Sov8pXA==", "cpu": [ "riscv64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-linux-x64-gnu": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-gnu/-/clipboard-linux-x64-gnu-0.3.2.tgz", "integrity": "sha512-v6fVnsn7WMGg73Dab8QMwyFce7tzGfgEixKgzLP8f1GJqkJZi5zO4k4FOHzSgUufgLil63gnxvMpjWkgfeQN7A==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-linux-x64-musl": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-linux-x64-musl/-/clipboard-linux-x64-musl-0.3.2.tgz", "integrity": "sha512-xVUtnoMQ8v2JVyfJLKKXACA6avdnchdbBkTsZs8BgJQo29qwCp5NIHAUO8gbJ40iaEGToW5RlmVk2M9V0HsHEw==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-win32-arm64-msvc": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-arm64-msvc/-/clipboard-win32-arm64-msvc-0.3.2.tgz", "integrity": "sha512-AEgg95TNi8TGgak2wSXZkXKCvAUTjWoU1Pqb0ON7JHrX78p616XUFNTJohtIon3e0w6k0pYPZeCuqRCza/Tqeg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/clipboard-win32-x64-msvc": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/@mariozechner/clipboard-win32-x64-msvc/-/clipboard-win32-x64-msvc-0.3.2.tgz", "integrity": "sha512-tGRuYpZwDOD7HBrCpyRuhGnHHSCknELvqwKKUG4JSfSB7JIU7LKRh6zx6fMUOQd8uISK35TjFg5UcNih+vJhFA==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@mariozechner/jiti": { "version": "2.6.5", "resolved": "https://registry.npmjs.org/@mariozechner/jiti/-/jiti-2.6.5.tgz", "integrity": "sha512-faGUlTcXka5l7rv0lP3K3vGW/ejRuOS24RR2aSFWREUQqzjgdsuWNo/IiPqL3kWRGt6Ahl2+qcDAwtdeWeuGUw==", "license": "MIT", "peer": true, "dependencies": { "std-env": "^3.10.0", "yoctocolors": "^2.1.2" }, "bin": { "jiti": "lib/jiti-cli.mjs" } }, "node_modules/@mariozechner/jiti/node_modules/std-env": { "version": "3.10.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", "license": "MIT", "peer": true }, "node_modules/@mariozechner/pi-agent-core": { "version": "0.57.1", "resolved": "https://registry.npmjs.org/@mariozechner/pi-agent-core/-/pi-agent-core-0.57.1.tgz", "integrity": "sha512-WXsBbkNWOObFGHkhixaT8GXJpHDd3+fn8QntYF+4R8Sa9WB90ENXWidO6b7vcKX+JX0jjO5dIsQxmzosARJKlg==", "license": "MIT", "peer": true, "dependencies": { "@mariozechner/pi-ai": "^0.57.1" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@mariozechner/pi-ai": { "version": "0.57.1", "resolved": "https://registry.npmjs.org/@mariozechner/pi-ai/-/pi-ai-0.57.1.tgz", "integrity": "sha512-Bd/J4a3YpdzJVyHLih0vDSdB0QPL4ti0XsAwtHOK/8eVhB0fHM1CpcgIrcBFJ23TMcKXMi0qamz18ERfp8tmgg==", "license": "MIT", "peer": true, "dependencies": { "@anthropic-ai/sdk": "^0.73.0", "@aws-sdk/client-bedrock-runtime": "^3.983.0", "@google/genai": "^1.40.0", "@mistralai/mistralai": "1.14.1", "@sinclair/typebox": "^0.34.41", "ajv": "^8.17.1", "ajv-formats": "^3.0.1", "chalk": "^5.6.2", "openai": "6.26.0", "partial-json": "^0.1.7", "proxy-agent": "^6.5.0", "undici": "^7.19.1", "zod-to-json-schema": "^3.24.6" }, "bin": { "pi-ai": "dist/cli.js" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@mariozechner/pi-ai/node_modules/ajv": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/@mariozechner/pi-ai/node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "peer": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/@mariozechner/pi-ai/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT", "peer": true }, "node_modules/@mariozechner/pi-coding-agent": { "version": "0.57.1", "resolved": "https://registry.npmjs.org/@mariozechner/pi-coding-agent/-/pi-coding-agent-0.57.1.tgz", "integrity": "sha512-u5MQEduj68rwVIsRsqrWkJYiJCyPph/a6bMoJAQKo1sb+Pc17Y/ojwa+wGssnUMjEB38AQKofWTVe8NFEpSWNw==", "license": "MIT", "peer": true, "dependencies": { "@mariozechner/jiti": "^2.6.2", "@mariozechner/pi-agent-core": "^0.57.1", "@mariozechner/pi-ai": "^0.57.1", "@mariozechner/pi-tui": "^0.57.1", "@silvia-odwyer/photon-node": "^0.3.4", "chalk": "^5.5.0", "cli-highlight": "^2.1.11", "diff": "^8.0.2", "extract-zip": "^2.0.1", "file-type": "^21.1.1", "glob": "^13.0.1", "hosted-git-info": "^9.0.2", "ignore": "^7.0.5", "marked": "^15.0.12", "minimatch": "^10.2.3", "proper-lockfile": "^4.1.2", "strip-ansi": "^7.1.0", "undici": "^7.19.1", "yaml": "^2.8.2" }, "bin": { "pi": "dist/cli.js" }, "engines": { "node": ">=20.6.0" }, "optionalDependencies": { "@mariozechner/clipboard": "^0.3.2" } }, "node_modules/@mariozechner/pi-coding-agent/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "peer": true, "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@mariozechner/pi-coding-agent/node_modules/brace-expansion": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "license": "MIT", "peer": true, "dependencies": { "balanced-match": "^4.0.2" }, "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@mariozechner/pi-coding-agent/node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "peer": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/@mariozechner/pi-coding-agent/node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "license": "MIT", "peer": true, "engines": { "node": ">= 4" } }, "node_modules/@mariozechner/pi-coding-agent/node_modules/minimatch": { "version": "10.2.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "license": "BlueOak-1.0.0", "peer": true, "dependencies": { "brace-expansion": "^5.0.2" }, "engines": { "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@mariozechner/pi-tui": { "version": "0.57.1", "resolved": "https://registry.npmjs.org/@mariozechner/pi-tui/-/pi-tui-0.57.1.tgz", "integrity": "sha512-cjoRghLbeAHV0tTJeHgZXaryUi5zzBZofeZ7uJun1gztnckLLRjoVeaPTujNlc5BIfyKvFqhh1QWCZng/MXlpg==", "license": "MIT", "peer": true, "dependencies": { "@types/mime-types": "^2.1.4", "chalk": "^5.5.0", "get-east-asian-width": "^1.3.0", "marked": "^15.0.12", "mime-types": "^3.0.1" }, "engines": { "node": ">=20.0.0" }, "optionalDependencies": { "koffi": "^2.9.0" } }, "node_modules/@mariozechner/pi-tui/node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "peer": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/@memorilabs/axon": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/@memorilabs/axon/-/axon-0.1.2.tgz", "integrity": "sha512-75oxynwLv6peVlr37NU/k4EYZ8D7DmZiDcVDb2lYOOq678rUIJM4ALteVPbi4a3pQ0i763yjTGvaJ/wrI3wHVw==", "license": "Apache-2.0", "engines": { "node": ">=18.0.0" }, "peerDependencies": { "@anthropic-ai/sdk": "*", "@google/genai": "*", "openai": "*" }, "peerDependenciesMeta": { "@anthropic-ai/sdk": { "optional": true }, "@google/genai": { "optional": true }, "openai": { "optional": true } } }, "node_modules/@memorilabs/memori": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/@memorilabs/memori/-/memori-0.0.5.tgz", "integrity": "sha512-ZVlW3sl1Syw9LTqVqMkge/A7T0KJ5Lt26KX3VcQklZWoodp/XpBeMHuoByjgn3E5EvakyWoO4zMeIM+zj9+IaA==", "license": "Apache-2.0", "dependencies": { "@memorilabs/axon": "^0.1.2" }, "bin": { "memori": "dist/bin/cli.js" }, "engines": { "node": ">=18.0.0" }, "peerDependencies": { "@anthropic-ai/sdk": "*", "openai": "*" }, "peerDependenciesMeta": { "@anthropic-ai/sdk": { "optional": true }, "openai": { "optional": true } } }, "node_modules/@microsoft/tsdoc": { "version": "0.16.0", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc/-/tsdoc-0.16.0.tgz", "integrity": "sha512-xgAyonlVVS+q7Vc7qLW0UrJU7rSFcETRWsqdXZtjzRU8dF+6CkozTK4V4y1LwOX7j8r/vHphjDeMeGI4tNGeGA==", "dev": true, "license": "MIT" }, "node_modules/@microsoft/tsdoc-config": { "version": "0.18.1", "resolved": "https://registry.npmjs.org/@microsoft/tsdoc-config/-/tsdoc-config-0.18.1.tgz", "integrity": "sha512-9brPoVdfN9k9g0dcWkFeA7IH9bbcttzDJlXvkf8b2OBzd5MueR1V2wkKBL0abn0otvmkHJC6aapBOTJDDeMCZg==", "dev": true, "license": "MIT", "dependencies": { "@microsoft/tsdoc": "0.16.0", "ajv": "~8.18.0", "jju": "~1.4.0", "resolve": "~1.22.2" } }, "node_modules/@microsoft/tsdoc-config/node_modules/ajv": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/@microsoft/tsdoc-config/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true, "license": "MIT" }, "node_modules/@mistralai/mistralai": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/@mistralai/mistralai/-/mistralai-1.14.1.tgz", "integrity": "sha512-IiLmmZFCCTReQgPAT33r7KQ1nYo5JPdvGkrkZqA8qQ2qB1GHgs5LoP5K2ICyrjnpw2n8oSxMM/VP+liiKcGNlQ==", "peer": true, "dependencies": { "ws": "^8.18.0", "zod": "^3.25.0 || ^4.0.0", "zod-to-json-schema": "^3.24.1" } }, "node_modules/@mozilla/readability": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/@mozilla/readability/-/readability-0.6.0.tgz", "integrity": "sha512-juG5VWh4qAivzTAeMzvY9xs9HY5rAcr2E4I7tiSSCokRFi7XIZCAu92ZkSTsIj1OPceCifL3cpfteP3pDT9/QQ==", "license": "Apache-2.0", "peer": true, "engines": { "node": ">=14.0.0" } }, "node_modules/@napi-rs/canvas": { "version": "0.1.96", "resolved": "https://registry.npmjs.org/@napi-rs/canvas/-/canvas-0.1.96.tgz", "integrity": "sha512-6NNmNxvoJKeucVjxaaRUt3La2i5jShgiAbaY3G/72s1Vp3U06XPrAIxkAjBxpDcamEn/t+WJ4OOlGmvILo4/Ew==", "license": "MIT", "peer": true, "workspaces": [ "e2e/*" ], "engines": { "node": ">= 10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" }, "optionalDependencies": { "@napi-rs/canvas-android-arm64": "0.1.96", "@napi-rs/canvas-darwin-arm64": "0.1.96", "@napi-rs/canvas-darwin-x64": "0.1.96", "@napi-rs/canvas-linux-arm-gnueabihf": "0.1.96", "@napi-rs/canvas-linux-arm64-gnu": "0.1.96", "@napi-rs/canvas-linux-arm64-musl": "0.1.96", "@napi-rs/canvas-linux-riscv64-gnu": "0.1.96", "@napi-rs/canvas-linux-x64-gnu": "0.1.96", "@napi-rs/canvas-linux-x64-musl": "0.1.96", "@napi-rs/canvas-win32-arm64-msvc": "0.1.96", "@napi-rs/canvas-win32-x64-msvc": "0.1.96" } }, "node_modules/@napi-rs/canvas-android-arm64": { "version": "0.1.96", "resolved": "https://registry.npmjs.org/@napi-rs/canvas-android-arm64/-/canvas-android-arm64-0.1.96.tgz", "integrity": "sha512-ew1sPrN3dGdZ3L4FoohPfnjq0f9/Jk7o+wP7HkQZokcXgIUD6FIyICEWGhMYzv53j63wUcPvZeAwgewX58/egg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "android" ], "peer": true, "engines": { "node": ">= 10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-darwin-arm64": { "version": "0.1.96", "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-arm64/-/canvas-darwin-arm64-0.1.96.tgz", "integrity": "sha512-Q/wOXZ5PzTqpdmA5eUOcegCf4Go/zz3aZ5DlzSeDpOjFmfwMKh8EzLAoweQ+mJVagcHQyzoJhaTEnrO68TNyNg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": ">= 10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-darwin-x64": { "version": "0.1.96", "resolved": "https://registry.npmjs.org/@napi-rs/canvas-darwin-x64/-/canvas-darwin-x64-0.1.96.tgz", "integrity": "sha512-UrXiQz28tQEvGM1qvyptewOAfmUrrd5+wvi6Rzjj2VprZI8iZ2KIvBD2lTTG1bVF95AbeDeG7PJA0D9sLKaOFA==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": ">= 10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-linux-arm-gnueabihf": { "version": "0.1.96", "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm-gnueabihf/-/canvas-linux-arm-gnueabihf-0.1.96.tgz", "integrity": "sha512-I90ODxweD8aEP6XKU/NU+biso95MwCtQ2F46dUvhec1HesFi0tq/tAJkYic/1aBSiO/1kGKmSeD1B0duOHhEHQ==", "cpu": [ "arm" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-linux-arm64-gnu": { "version": "0.1.96", "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-gnu/-/canvas-linux-arm64-gnu-0.1.96.tgz", "integrity": "sha512-Dx/0+RFV++w3PcRy+4xNXkghhXjA5d0Mw1bs95emn5Llinp1vihMaA6WJt3oYv2LAHc36+gnrhIBsPhUyI2SGw==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-linux-arm64-musl": { "version": "0.1.96", "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-arm64-musl/-/canvas-linux-arm64-musl-0.1.96.tgz", "integrity": "sha512-UvOi7fii3IE2KDfEfhh8m+LpzSRvhGK7o1eho99M2M0HTik11k3GX+2qgVx9EtujN3/bhFFS1kSO3+vPMaJ0Mg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-linux-riscv64-gnu": { "version": "0.1.96", "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-riscv64-gnu/-/canvas-linux-riscv64-gnu-0.1.96.tgz", "integrity": "sha512-MBSukhGCQ5nRtf9NbFYWOU080yqkZU1PbuH4o1ROvB4CbPl12fchDR35tU83Wz8gWIM9JTn99lBn9DenPIv7Ig==", "cpu": [ "riscv64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-linux-x64-gnu": { "version": "0.1.96", "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-gnu/-/canvas-linux-x64-gnu-0.1.96.tgz", "integrity": "sha512-I/ccu2SstyKiV3HIeVzyBIWfrJo8cN7+MSQZPnabewWV6hfJ2nY7Df2WqOHmobBRUw84uGR6zfQHsUEio/m5Vg==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-linux-x64-musl": { "version": "0.1.96", "resolved": "https://registry.npmjs.org/@napi-rs/canvas-linux-x64-musl/-/canvas-linux-x64-musl-0.1.96.tgz", "integrity": "sha512-H3uov7qnTl73GDT4h52lAqpJPsl1tIUyNPWJyhQ6gHakohNqqRq3uf80+NEpzcytKGEOENP1wX3yGwZxhjiWEQ==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-win32-arm64-msvc": { "version": "0.1.96", "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-arm64-msvc/-/canvas-win32-arm64-msvc-0.1.96.tgz", "integrity": "sha512-ATp6Y+djOjYtkfV/VRH7CZ8I1MEtkUQBmKUbuWw5zWEHHqfL0cEcInE4Cxgx7zkNAhEdBbnH8HMVrqNp+/gwxA==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">= 10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/canvas-win32-x64-msvc": { "version": "0.1.96", "resolved": "https://registry.npmjs.org/@napi-rs/canvas-win32-x64-msvc/-/canvas-win32-x64-msvc-0.1.96.tgz", "integrity": "sha512-UYGdTltVd+Z8mcIuoqGmAXXUvwH5CLf2M6mIB5B0/JmX5J041jETjqtSYl7gN+aj3k1by/SG6sS0hAwCqyK7zw==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">= 10" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@napi-rs/wasm-runtime": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@napi-rs/wasm-runtime/-/wasm-runtime-1.1.1.tgz", "integrity": "sha512-p64ah1M1ld8xjWv3qbvFwHiFVWrq1yFvV4f7w+mzaqiR4IlSgkqhcRdHwsGgomwzBH51sRY4NEowLxnaBjcW/A==", "license": "MIT", "optional": true, "dependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1", "@tybys/wasm-util": "^0.10.1" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Brooooooklyn" } }, "node_modules/@node-llama-cpp/linux-arm64": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-arm64/-/linux-arm64-3.16.2.tgz", "integrity": "sha512-CxzgPsS84wL3W5sZRgxP3c9iJKEW+USrak1SmX6EAJxW/v9QGzehvT6W/aR1FyfidiIyQtOp3ga0Gg/9xfJPGw==", "cpu": [ "arm64", "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/linux-armv7l": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-armv7l/-/linux-armv7l-3.16.2.tgz", "integrity": "sha512-9G6W/MkQ/DLwGmpcj143NQ50QJg5gQZfzVf5RYx77VczBqhgwkgYHILekYrOs4xanOeqeJ8jnOnQQSp1YaJZUg==", "cpu": [ "arm", "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/linux-x64": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64/-/linux-x64-3.16.2.tgz", "integrity": "sha512-OXYf8rVfoDyvN+YrfKk8F9An9a5GOxVIM8OcR1U911tc0oRNf8yfJrQ8KrM75R26gwq0Y6YZwVTP0vRCInwWOw==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/linux-x64-cuda": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64-cuda/-/linux-x64-cuda-3.16.2.tgz", "integrity": "sha512-LTBQFqjin7tyrLNJz0XWTB5QAHDsZV71/qiiRRjXdBKSZHVVaPLfdgxypGu7ggPeBNsv+MckRXdlH5C7yMtE4A==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/linux-x64-cuda-ext": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64-cuda-ext/-/linux-x64-cuda-ext-3.16.2.tgz", "integrity": "sha512-47d9myCJauZyzAlN7IK1eIt/4CcBMslF+yHy4q+yJotD/RV/S6qRpK2kGn+ybtdVjkPGNCoPkHKcyla9iIVjbw==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/linux-x64-vulkan": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/linux-x64-vulkan/-/linux-x64-vulkan-3.16.2.tgz", "integrity": "sha512-HDLAw4ZhwJuhKuF6n4x520yZXAQZahUOXtCGvPubjfpmIOElKrfDvCVlRsthAP0JwcwINzIQlVys3boMIXfBgw==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/mac-arm64-metal": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/mac-arm64-metal/-/mac-arm64-metal-3.16.2.tgz", "integrity": "sha512-nEZ74qB0lUohF88yR741YUrUqz/qD+FJFzUTHj0FwxAynSZCjvwtzEDtavRlh3qd3yLD/0ChNn00/RQ54ISImw==", "cpu": [ "arm64", "x64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/mac-x64": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/mac-x64/-/mac-x64-3.16.2.tgz", "integrity": "sha512-BjA+DgeDt+kRxVMV6kChb9XVXm7U5b90jUif7Z/s6ZXtOOnV6exrTM2W09kbSqAiNhZmctcVY83h2dwNTZ/yIw==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/win-arm64": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-arm64/-/win-arm64-3.16.2.tgz", "integrity": "sha512-XHNFQzUjYODtkZjIn4NbQVrBtGB9RI9TpisiALryqfrIqagQmjBh6dmxZWlt5uduKAfT7M2/2vrABGR490FACA==", "cpu": [ "arm64", "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/win-x64": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-x64/-/win-x64-3.16.2.tgz", "integrity": "sha512-etrivzbyLNVhZlUosFW8JSL0OSiuKQf9qcI3dNdehD907sHquQbBJrG7lXcdL6IecvXySp3oAwCkM87VJ0b3Fg==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/win-x64-cuda": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-x64-cuda/-/win-x64-cuda-3.16.2.tgz", "integrity": "sha512-jStDELHrU3rKQMOk5Hs5bWEazyjE2hzHwpNf6SblOpaGkajM/HJtxEZoL0mLHJx5qeXs4oOVkr7AzuLy0WPpNA==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/win-x64-cuda-ext": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-x64-cuda-ext/-/win-x64-cuda-ext-3.16.2.tgz", "integrity": "sha512-sdv4Kzn9bOQWNBRvw6B/zcn8dQRfZhjIHv5AfDBIOfRlSCgjebFpBeYUoU4wZPpjr3ISwcqO5MEWsw+AbUdV3Q==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@node-llama-cpp/win-x64-vulkan": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/@node-llama-cpp/win-x64-vulkan/-/win-x64-vulkan-3.16.2.tgz", "integrity": "sha512-9xuHFCOhCQjZgQSFrk79EuSKn9nGWt/SAq/3wujQSQLtgp8jGdtZgwcmuDUoemInf10en2dcOmEt7t8dQdC3XA==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">=20.0.0" } }, "node_modules/@octokit/app": { "version": "16.1.2", "resolved": "https://registry.npmjs.org/@octokit/app/-/app-16.1.2.tgz", "integrity": "sha512-8j7sEpUYVj18dxvh0KWj6W/l6uAiVRBl1JBDVRqH1VHKAO/G5eRVl4yEoYACjakWers1DjUkcCHyJNQK47JqyQ==", "license": "MIT", "peer": true, "dependencies": { "@octokit/auth-app": "^8.1.2", "@octokit/auth-unauthenticated": "^7.0.3", "@octokit/core": "^7.0.6", "@octokit/oauth-app": "^8.0.3", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/types": "^16.0.0", "@octokit/webhooks": "^14.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/auth-app": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/@octokit/auth-app/-/auth-app-8.2.0.tgz", "integrity": "sha512-vVjdtQQwomrZ4V46B9LaCsxsySxGoHsyw6IYBov/TqJVROrlYdyNgw5q6tQbB7KZt53v1l1W53RiqTvpzL907g==", "license": "MIT", "peer": true, "dependencies": { "@octokit/auth-oauth-app": "^9.0.3", "@octokit/auth-oauth-user": "^6.0.2", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "toad-cache": "^3.7.0", "universal-github-app-jwt": "^2.2.0", "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/auth-oauth-app": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-app/-/auth-oauth-app-9.0.3.tgz", "integrity": "sha512-+yoFQquaF8OxJSxTb7rnytBIC2ZLbLqA/yb71I4ZXT9+Slw4TziV9j/kyGhUFRRTF2+7WlnIWsePZCWHs+OGjg==", "license": "MIT", "peer": true, "dependencies": { "@octokit/auth-oauth-device": "^8.0.3", "@octokit/auth-oauth-user": "^6.0.2", "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/auth-oauth-device": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-device/-/auth-oauth-device-8.0.3.tgz", "integrity": "sha512-zh2W0mKKMh/VWZhSqlaCzY7qFyrgd9oTWmTmHaXnHNeQRCZr/CXy2jCgHo4e4dJVTiuxP5dLa0YM5p5QVhJHbw==", "license": "MIT", "peer": true, "dependencies": { "@octokit/oauth-methods": "^6.0.2", "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/auth-oauth-user": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@octokit/auth-oauth-user/-/auth-oauth-user-6.0.2.tgz", "integrity": "sha512-qLoPPc6E6GJoz3XeDG/pnDhJpTkODTGG4kY0/Py154i/I003O9NazkrwJwRuzgCalhzyIeWQ+6MDvkUmKXjg/A==", "license": "MIT", "peer": true, "dependencies": { "@octokit/auth-oauth-device": "^8.0.3", "@octokit/oauth-methods": "^6.0.2", "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/auth-token": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", "license": "MIT", "peer": true, "engines": { "node": ">= 20" } }, "node_modules/@octokit/auth-unauthenticated": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@octokit/auth-unauthenticated/-/auth-unauthenticated-7.0.3.tgz", "integrity": "sha512-8Jb1mtUdmBHL7lGmop9mU9ArMRUTRhg8vp0T1VtZ4yd9vEm3zcLwmjQkhNEduKawOOORie61xhtYIhTDN+ZQ3g==", "license": "MIT", "peer": true, "dependencies": { "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/core": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "license": "MIT", "peer": true, "dependencies": { "@octokit/auth-token": "^6.0.0", "@octokit/graphql": "^9.0.3", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/endpoint": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.3.tgz", "integrity": "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==", "license": "MIT", "peer": true, "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/graphql": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", "license": "MIT", "peer": true, "dependencies": { "@octokit/request": "^10.0.6", "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/oauth-app": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/@octokit/oauth-app/-/oauth-app-8.0.3.tgz", "integrity": "sha512-jnAjvTsPepyUaMu9e69hYBuozEPgYqP4Z3UnpmvoIzHDpf8EXDGvTY1l1jK0RsZ194oRd+k6Hm13oRU8EoDFwg==", "license": "MIT", "peer": true, "dependencies": { "@octokit/auth-oauth-app": "^9.0.2", "@octokit/auth-oauth-user": "^6.0.1", "@octokit/auth-unauthenticated": "^7.0.2", "@octokit/core": "^7.0.5", "@octokit/oauth-authorization-url": "^8.0.0", "@octokit/oauth-methods": "^6.0.1", "@types/aws-lambda": "^8.10.83", "universal-user-agent": "^7.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/oauth-authorization-url": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/@octokit/oauth-authorization-url/-/oauth-authorization-url-8.0.0.tgz", "integrity": "sha512-7QoLPRh/ssEA/HuHBHdVdSgF8xNLz/Bc5m9fZkArJE5bb6NmVkDm3anKxXPmN1zh6b5WKZPRr3697xKT/yM3qQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 20" } }, "node_modules/@octokit/oauth-methods": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/@octokit/oauth-methods/-/oauth-methods-6.0.2.tgz", "integrity": "sha512-HiNOO3MqLxlt5Da5bZbLV8Zarnphi4y9XehrbaFMkcoJ+FL7sMxH/UlUsCVxpddVu4qvNDrBdaTVE2o4ITK8ng==", "license": "MIT", "peer": true, "dependencies": { "@octokit/oauth-authorization-url": "^8.0.0", "@octokit/request": "^10.0.6", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/openapi-types": { "version": "27.0.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", "license": "MIT", "peer": true }, "node_modules/@octokit/openapi-webhooks-types": { "version": "12.1.0", "resolved": "https://registry.npmjs.org/@octokit/openapi-webhooks-types/-/openapi-webhooks-types-12.1.0.tgz", "integrity": "sha512-WiuzhOsiOvb7W3Pvmhf8d2C6qaLHXrWiLBP4nJ/4kydu+wpagV5Fkz9RfQwV2afYzv3PB+3xYgp4mAdNGjDprA==", "license": "MIT", "peer": true }, "node_modules/@octokit/plugin-paginate-graphql": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-graphql/-/plugin-paginate-graphql-6.0.0.tgz", "integrity": "sha512-crfpnIoFiBtRkvPqOyLOsw12XsveYuY2ieP6uYDosoUegBJpSVxGwut9sxUgFFcll3VTOTqpUf8yGd8x1OmAkQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 20" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-paginate-rest": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", "license": "MIT", "peer": true, "dependencies": { "@octokit/types": "^16.0.0" }, "engines": { "node": ">= 20" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { "version": "17.0.0", "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", "license": "MIT", "peer": true, "dependencies": { "@octokit/types": "^16.0.0" }, "engines": { "node": ">= 20" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-retry": { "version": "8.1.0", "resolved": "https://registry.npmjs.org/@octokit/plugin-retry/-/plugin-retry-8.1.0.tgz", "integrity": "sha512-O1FZgXeiGb2sowEr/hYTr6YunGdSAFWnr2fyW39Ah85H8O33ELASQxcvOFF5LE6Tjekcyu2ms4qAzJVhSaJxTw==", "license": "MIT", "peer": true, "dependencies": { "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "bottleneck": "^2.15.3" }, "engines": { "node": ">= 20" }, "peerDependencies": { "@octokit/core": ">=7" } }, "node_modules/@octokit/plugin-throttling": { "version": "11.0.3", "resolved": "https://registry.npmjs.org/@octokit/plugin-throttling/-/plugin-throttling-11.0.3.tgz", "integrity": "sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==", "license": "MIT", "peer": true, "dependencies": { "@octokit/types": "^16.0.0", "bottleneck": "^2.15.3" }, "engines": { "node": ">= 20" }, "peerDependencies": { "@octokit/core": "^7.0.0" } }, "node_modules/@octokit/request": { "version": "10.0.8", "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.8.tgz", "integrity": "sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==", "license": "MIT", "peer": true, "dependencies": { "@octokit/endpoint": "^11.0.3", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "fast-content-type-parse": "^3.0.0", "json-with-bigint": "^3.5.3", "universal-user-agent": "^7.0.2" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/request-error": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", "license": "MIT", "peer": true, "dependencies": { "@octokit/types": "^16.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/types": { "version": "16.0.0", "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", "license": "MIT", "peer": true, "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "node_modules/@octokit/webhooks": { "version": "14.2.0", "resolved": "https://registry.npmjs.org/@octokit/webhooks/-/webhooks-14.2.0.tgz", "integrity": "sha512-da6KbdNCV5sr1/txD896V+6W0iamFWrvVl8cHkBSPT+YlvmT3DwXa4jxZnQc+gnuTEqSWbBeoSZYTayXH9wXcw==", "license": "MIT", "peer": true, "dependencies": { "@octokit/openapi-webhooks-types": "12.1.0", "@octokit/request-error": "^7.0.0", "@octokit/webhooks-methods": "^6.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/@octokit/webhooks-methods": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@octokit/webhooks-methods/-/webhooks-methods-6.0.0.tgz", "integrity": "sha512-MFlzzoDJVw/GcbfzVC1RLR36QqkTLUf79vLVO3D+xn7r0QgxnFoLZgtrzxiQErAjFUOdH6fas2KeQJ1yr/qaXQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 20" } }, "node_modules/@oxc-project/runtime": { "version": "0.115.0", "resolved": "https://registry.npmjs.org/@oxc-project/runtime/-/runtime-0.115.0.tgz", "integrity": "sha512-Rg8Wlt5dCbXhQnsXPrkOjL1DTSvXLgb2R/KYfnf1/K+R0k6UMLEmbQXPM+kwrWqSmWA2t0B1EtHy2/3zikQpvQ==", "dev": true, "license": "MIT", "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@oxc-project/types": { "version": "0.115.0", "resolved": "https://registry.npmjs.org/@oxc-project/types/-/types-0.115.0.tgz", "integrity": "sha512-4n91DKnebUS4yjUHl2g3/b2T+IUdCfmoZGhmwsovZCDaJSs+QkVAM+0AqqTxHSsHfeiMuueT75cZaZcT/m0pSw==", "dev": true, "license": "MIT", "funding": { "url": "https://github.com/sponsors/Boshen" } }, "node_modules/@pinojs/redact": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/@pinojs/redact/-/redact-0.4.0.tgz", "integrity": "sha512-k2ENnmBugE/rzQfEcdWHcCY+/FM3VLzH9cYEsbdsoqrvzAKRhUZeRNhAZvB8OitQJ1TBed3yqWtdjzS6wJKBwg==", "license": "MIT", "peer": true }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "license": "MIT", "optional": true, "peer": true, "engines": { "node": ">=14" } }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", "license": "BSD-3-Clause", "peer": true }, "node_modules/@protobufjs/base64": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", "license": "BSD-3-Clause", "peer": true }, "node_modules/@protobufjs/codegen": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", "license": "BSD-3-Clause", "peer": true }, "node_modules/@protobufjs/eventemitter": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", "license": "BSD-3-Clause", "peer": true }, "node_modules/@protobufjs/fetch": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", "license": "BSD-3-Clause", "peer": true, "dependencies": { "@protobufjs/aspromise": "^1.1.1", "@protobufjs/inquire": "^1.1.0" } }, "node_modules/@protobufjs/float": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", "license": "BSD-3-Clause", "peer": true }, "node_modules/@protobufjs/inquire": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", "license": "BSD-3-Clause", "peer": true }, "node_modules/@protobufjs/path": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", "license": "BSD-3-Clause", "peer": true }, "node_modules/@protobufjs/pool": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", "license": "BSD-3-Clause", "peer": true }, "node_modules/@protobufjs/utf8": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", "license": "BSD-3-Clause", "peer": true }, "node_modules/@reflink/reflink": { "version": "0.1.19", "resolved": "https://registry.npmjs.org/@reflink/reflink/-/reflink-0.1.19.tgz", "integrity": "sha512-DmCG8GzysnCZ15bres3N5AHCmwBwYgp0As6xjhQ47rAUTUXxJiK+lLUxaGsX3hd/30qUpVElh05PbGuxRPgJwA==", "license": "MIT", "optional": true, "peer": true, "engines": { "node": ">= 10" }, "optionalDependencies": { "@reflink/reflink-darwin-arm64": "0.1.19", "@reflink/reflink-darwin-x64": "0.1.19", "@reflink/reflink-linux-arm64-gnu": "0.1.19", "@reflink/reflink-linux-arm64-musl": "0.1.19", "@reflink/reflink-linux-x64-gnu": "0.1.19", "@reflink/reflink-linux-x64-musl": "0.1.19", "@reflink/reflink-win32-arm64-msvc": "0.1.19", "@reflink/reflink-win32-x64-msvc": "0.1.19" } }, "node_modules/@reflink/reflink-darwin-arm64": { "version": "0.1.19", "resolved": "https://registry.npmjs.org/@reflink/reflink-darwin-arm64/-/reflink-darwin-arm64-0.1.19.tgz", "integrity": "sha512-ruy44Lpepdk1FqDz38vExBY/PVUsjxZA+chd9wozjUH9JjuDT/HEaQYA6wYN9mf041l0yLVar6BCZuWABJvHSA==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-darwin-x64": { "version": "0.1.19", "resolved": "https://registry.npmjs.org/@reflink/reflink-darwin-x64/-/reflink-darwin-x64-0.1.19.tgz", "integrity": "sha512-By85MSWrMZa+c26TcnAy8SDk0sTUkYlNnwknSchkhHpGXOtjNDUOxJE9oByBnGbeuIE1PiQsxDG3Ud+IVV9yuA==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-linux-arm64-gnu": { "version": "0.1.19", "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-arm64-gnu/-/reflink-linux-arm64-gnu-0.1.19.tgz", "integrity": "sha512-7P+er8+rP9iNeN+bfmccM4hTAaLP6PQJPKWSA4iSk2bNvo6KU6RyPgYeHxXmzNKzPVRcypZQTpFgstHam6maVg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-linux-arm64-musl": { "version": "0.1.19", "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-arm64-musl/-/reflink-linux-arm64-musl-0.1.19.tgz", "integrity": "sha512-37iO/Dp6m5DDaC2sf3zPtx/hl9FV3Xze4xoYidrxxS9bgP3S8ALroxRK6xBG/1TtfXKTvolvp+IjrUU6ujIGmA==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-linux-x64-gnu": { "version": "0.1.19", "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-x64-gnu/-/reflink-linux-x64-gnu-0.1.19.tgz", "integrity": "sha512-jbI8jvuYCaA3MVUdu8vLoLAFqC+iNMpiSuLbxlAgg7x3K5bsS8nOpTRnkLF7vISJ+rVR8W+7ThXlXlUQ93ulkw==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-linux-x64-musl": { "version": "0.1.19", "resolved": "https://registry.npmjs.org/@reflink/reflink-linux-x64-musl/-/reflink-linux-x64-musl-0.1.19.tgz", "integrity": "sha512-e9FBWDe+lv7QKAwtKOt6A2W/fyy/aEEfr0g6j/hWzvQcrzHCsz07BNQYlNOjTfeytrtLU7k449H1PI95jA4OjQ==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-win32-arm64-msvc": { "version": "0.1.19", "resolved": "https://registry.npmjs.org/@reflink/reflink-win32-arm64-msvc/-/reflink-win32-arm64-msvc-0.1.19.tgz", "integrity": "sha512-09PxnVIQcd+UOn4WAW73WU6PXL7DwGS6wPlkMhMg2zlHHG65F3vHepOw06HFCq+N42qkaNAc8AKIabWvtk6cIQ==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@reflink/reflink-win32-x64-msvc": { "version": "0.1.19", "resolved": "https://registry.npmjs.org/@reflink/reflink-win32-x64-msvc/-/reflink-win32-x64-msvc-0.1.19.tgz", "integrity": "sha512-E//yT4ni2SyhwP8JRjVGWr3cbnhWDiPLgnQ66qqaanjjnMiu3O/2tjCPQXlcGc/DEYofpDc9fvhv6tALQsMV9w==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@rolldown/binding-android-arm64": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-android-arm64/-/binding-android-arm64-1.0.0-rc.9.tgz", "integrity": "sha512-lcJL0bN5hpgJfSIz/8PIf02irmyL43P+j1pTCfbD1DbLkmGRuFIA4DD3B3ZOvGqG0XiVvRznbKtN0COQVaKUTg==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "android" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-arm64": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-arm64/-/binding-darwin-arm64-1.0.0-rc.9.tgz", "integrity": "sha512-J7Zk3kLYFsLtuH6U+F4pS2sYVzac0qkjcO5QxHS7OS7yZu2LRs+IXo+uvJ/mvpyUljDJ3LROZPoQfgBIpCMhdQ==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-darwin-x64": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-darwin-x64/-/binding-darwin-x64-1.0.0-rc.9.tgz", "integrity": "sha512-iwtmmghy8nhfRGeNAIltcNXzD0QMNaaA5U/NyZc1Ia4bxrzFByNMDoppoC+hl7cDiUq5/1CnFthpT9n+UtfFyg==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-freebsd-x64": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-freebsd-x64/-/binding-freebsd-x64-1.0.0-rc.9.tgz", "integrity": "sha512-DLFYI78SCiZr5VvdEplsVC2Vx53lnA4/Ga5C65iyldMVaErr86aiqCoNBLl92PXPfDtUYjUh+xFFor40ueNs4Q==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm-gnueabihf": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm-gnueabihf/-/binding-linux-arm-gnueabihf-1.0.0-rc.9.tgz", "integrity": "sha512-CsjTmTwd0Hri6iTw/DRMK7kOZ7FwAkrO4h8YWKoX/kcj833e4coqo2wzIFywtch/8Eb5enQ/lwLM7w6JX1W5RQ==", "cpu": [ "arm" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-gnu": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-gnu/-/binding-linux-arm64-gnu-1.0.0-rc.9.tgz", "integrity": "sha512-2x9O2JbSPxpxMDhP9Z74mahAStibTlrBMW0520+epJH5sac7/LwZW5Bmg/E6CXuEF53JJFW509uP+lSedaUNxg==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-arm64-musl": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-arm64-musl/-/binding-linux-arm64-musl-1.0.0-rc.9.tgz", "integrity": "sha512-JA1QRW31ogheAIRhIg9tjMfsYbglXXYGNPLdPEYrwFxdbkQCAzvpSCSHCDWNl4hTtrol8WeboCSEpjdZK8qrCg==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-ppc64-gnu": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-ppc64-gnu/-/binding-linux-ppc64-gnu-1.0.0-rc.9.tgz", "integrity": "sha512-aOKU9dJheda8Kj8Y3w9gnt9QFOO+qKPAl8SWd7JPHP+Cu0EuDAE5wokQubLzIDQWg2myXq2XhTpOVS07qqvT+w==", "cpu": [ "ppc64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-s390x-gnu": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-s390x-gnu/-/binding-linux-s390x-gnu-1.0.0-rc.9.tgz", "integrity": "sha512-OalO94fqj7IWRn3VdXWty75jC5dk4C197AWEuMhIpvVv2lw9fiPhud0+bW2ctCxb3YoBZor71QHbY+9/WToadA==", "cpu": [ "s390x" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-gnu": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-gnu/-/binding-linux-x64-gnu-1.0.0-rc.9.tgz", "integrity": "sha512-cVEl1vZtBsBZna3YMjGXNvnYYrOJ7RzuWvZU0ffvJUexWkukMaDuGhUXn0rjnV0ptzGVkvc+vW9Yqy6h8YX4pg==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-linux-x64-musl": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-linux-x64-musl/-/binding-linux-x64-musl-1.0.0-rc.9.tgz", "integrity": "sha512-UzYnKCIIc4heAKgI4PZ3dfBGUZefGCJ1TPDuLHoCzgrMYPb5Rv6TLFuYtyM4rWyHM7hymNdsg5ik2C+UD9VDbA==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-openharmony-arm64": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-openharmony-arm64/-/binding-openharmony-arm64-1.0.0-rc.9.tgz", "integrity": "sha512-+6zoiF+RRyf5cdlFQP7nm58mq7+/2PFaY2DNQeD4B87N36JzfF/l9mdBkkmTvSYcYPE8tMh/o3cRlsx1ldLfog==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "openharmony" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-wasm32-wasi": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-wasm32-wasi/-/binding-wasm32-wasi-1.0.0-rc.9.tgz", "integrity": "sha512-rgFN6sA/dyebil3YTlL2evvi/M+ivhfnyxec7AccTpRPccno/rPoNlqybEZQBkcbZu8Hy+eqNJCqfBR8P7Pg8g==", "cpu": [ "wasm32" ], "dev": true, "license": "MIT", "optional": true, "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@rolldown/binding-win32-arm64-msvc": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-arm64-msvc/-/binding-win32-arm64-msvc-1.0.0-rc.9.tgz", "integrity": "sha512-lHVNUG/8nlF1IQk1C0Ci574qKYyty2goMiPlRqkC5R+3LkXDkL5Dhx8ytbxq35m+pkHVIvIxviD+TWLdfeuadA==", "cpu": [ "arm64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/binding-win32-x64-msvc": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/binding-win32-x64-msvc/-/binding-win32-x64-msvc-1.0.0-rc.9.tgz", "integrity": "sha512-G0oA4+w1iY5AGi5HcDTxWsoxF509hrFIPB2rduV5aDqS9FtDg1CAfa7V34qImbjfhIcA8C+RekocJZA96EarwQ==", "cpu": [ "x64" ], "dev": true, "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { "node": "^20.19.0 || >=22.12.0" } }, "node_modules/@rolldown/pluginutils": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-rc.9.tgz", "integrity": "sha512-w6oiRWgEBl04QkFZgmW+jnU1EC9b57Oihi2ot3HNWIQRqgHp5PnYDia5iZ5FF7rpa4EQdiqMDXjlqKGXBhsoXw==", "dev": true, "license": "MIT" }, "node_modules/@silvia-odwyer/photon-node": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/@silvia-odwyer/photon-node/-/photon-node-0.3.4.tgz", "integrity": "sha512-bnly4BKB3KDTFxrUIcgCLbaeVVS8lrAkri1pEzskpmxu9MdfGQTy8b8EgcD83ywD3RPMsIulY8xJH5Awa+t9fA==", "license": "Apache-2.0", "peer": true }, "node_modules/@sinclair/typebox": { "version": "0.34.48", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.34.48.tgz", "integrity": "sha512-kKJTNuK3AQOrgjjotVxMrCn1sUJwM76wMszfq1kdU4uYVJjvEWuFQ6HgvLt4Xz3fSmZlTOxJ/Ie13KnIcWQXFA==", "license": "MIT", "peer": true }, "node_modules/@slack/bolt": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@slack/bolt/-/bolt-4.6.0.tgz", "integrity": "sha512-xPgfUs2+OXSugz54Ky07pA890+Qydk22SYToi8uGpXeHSt1JWwFJkRyd/9Vlg5I1AdfdpGXExDpwnbuN9Q/2dQ==", "license": "MIT", "peer": true, "dependencies": { "@slack/logger": "^4.0.0", "@slack/oauth": "^3.0.4", "@slack/socket-mode": "^2.0.5", "@slack/types": "^2.18.0", "@slack/web-api": "^7.12.0", "axios": "^1.12.0", "express": "^5.0.0", "path-to-regexp": "^8.1.0", "raw-body": "^3", "tsscmp": "^1.0.6" }, "engines": { "node": ">=18", "npm": ">=8.6.0" }, "peerDependencies": { "@types/express": "^5.0.0" } }, "node_modules/@slack/logger": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/@slack/logger/-/logger-4.0.0.tgz", "integrity": "sha512-Wz7QYfPAlG/DR+DfABddUZeNgoeY7d1J39OCR2jR+v7VBsB8ezulDK5szTnDDPDwLH5IWhLvXIHlCFZV7MSKgA==", "license": "MIT", "peer": true, "dependencies": { "@types/node": ">=18.0.0" }, "engines": { "node": ">= 18", "npm": ">= 8.6.0" } }, "node_modules/@slack/oauth": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@slack/oauth/-/oauth-3.0.4.tgz", "integrity": "sha512-+8H0g7mbrHndEUbYCP7uYyBCbwqmm3E6Mo3nfsDvZZW74zKk1ochfH/fWSvGInYNCVvaBUbg3RZBbTp0j8yJCg==", "license": "MIT", "peer": true, "dependencies": { "@slack/logger": "^4", "@slack/web-api": "^7.10.0", "@types/jsonwebtoken": "^9", "@types/node": ">=18", "jsonwebtoken": "^9" }, "engines": { "node": ">=18", "npm": ">=8.6.0" } }, "node_modules/@slack/socket-mode": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@slack/socket-mode/-/socket-mode-2.0.5.tgz", "integrity": "sha512-VaapvmrAifeFLAFaDPfGhEwwunTKsI6bQhYzxRXw7BSujZUae5sANO76WqlVsLXuhVtCVrBWPiS2snAQR2RHJQ==", "license": "MIT", "peer": true, "dependencies": { "@slack/logger": "^4", "@slack/web-api": "^7.10.0", "@types/node": ">=18", "@types/ws": "^8", "eventemitter3": "^5", "ws": "^8" }, "engines": { "node": ">= 18", "npm": ">= 8.6.0" } }, "node_modules/@slack/types": { "version": "2.20.0", "resolved": "https://registry.npmjs.org/@slack/types/-/types-2.20.0.tgz", "integrity": "sha512-PVF6P6nxzDMrzPC8fSCsnwaI+kF8YfEpxf3MqXmdyjyWTYsZQURpkK7WWUWvP5QpH55pB7zyYL9Qem/xSgc5VA==", "license": "MIT", "peer": true, "engines": { "node": ">= 12.13.0", "npm": ">= 6.12.0" } }, "node_modules/@slack/web-api": { "version": "7.14.1", "resolved": "https://registry.npmjs.org/@slack/web-api/-/web-api-7.14.1.tgz", "integrity": "sha512-RoygyteJeFswxDPJjUMESn9dldWVMD2xUcHHd9DenVavSfVC6FeVnSdDerOO7m8LLvw4Q132nQM4hX8JiF7dng==", "license": "MIT", "peer": true, "dependencies": { "@slack/logger": "^4.0.0", "@slack/types": "^2.20.0", "@types/node": ">=18.0.0", "@types/retry": "0.12.0", "axios": "^1.13.5", "eventemitter3": "^5.0.1", "form-data": "^4.0.4", "is-electron": "2.2.2", "is-stream": "^2", "p-queue": "^6", "p-retry": "^4", "retry": "^0.13.1" }, "engines": { "node": ">= 18", "npm": ">= 8.6.0" } }, "node_modules/@smithy/abort-controller": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/abort-controller/-/abort-controller-4.2.12.tgz", "integrity": "sha512-xolrFw6b+2iYGl6EcOL7IJY71vvyZ0DJ3mcKtpykqPe2uscwtzDZJa1uVQXyP7w9Dd+kGwYnPbMsJrGISKiY/Q==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/config-resolver": { "version": "4.4.11", "resolved": "https://registry.npmjs.org/@smithy/config-resolver/-/config-resolver-4.4.11.tgz", "integrity": "sha512-YxFiiG4YDAtX7WMN7RuhHZLeTmRRAOyCbr+zB8e3AQzHPnUhS8zXjB1+cniPVQI3xbWsQPM0X2aaIkO/ME0ymw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "@smithy/util-config-provider": "^4.2.2", "@smithy/util-endpoints": "^3.3.3", "@smithy/util-middleware": "^4.2.12", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/core": { "version": "3.23.10", "resolved": "https://registry.npmjs.org/@smithy/core/-/core-3.23.10.tgz", "integrity": "sha512-pn0HaJpxmdeCLdbAm79SUjX8IPiej9ANHNHec4K4u5Bkf5BqYCbAgK3c8NTCVf44DnlWJK7W1mimlgBPUQ3IlA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-base64": "^4.3.2", "@smithy/util-body-length-browser": "^4.2.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-stream": "^4.5.18", "@smithy/util-utf8": "^4.2.2", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/credential-provider-imds": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/credential-provider-imds/-/credential-provider-imds-4.2.12.tgz", "integrity": "sha512-cr2lR792vNZcYMriSIj+Um3x9KWrjcu98kn234xA6reOAFMmbRpQMOv8KPgEmLLtx3eldU6c5wALKFqNOhugmg==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/eventstream-codec": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/eventstream-codec/-/eventstream-codec-4.2.12.tgz", "integrity": "sha512-FE3bZdEl62ojmy8x4FHqxq2+BuOHlcxiH5vaZ6aqHJr3AIZzwF5jfx8dEiU/X0a8RboyNDjmXjlbr8AdEyLgiA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@aws-crypto/crc32": "5.2.0", "@smithy/types": "^4.13.1", "@smithy/util-hex-encoding": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/eventstream-serde-browser": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-browser/-/eventstream-serde-browser-4.2.12.tgz", "integrity": "sha512-XUSuMxlTxV5pp4VpqZf6Sa3vT/Q75FVkLSpSSE3KkWBvAQWeuWt1msTv8fJfgA4/jcJhrbrbMzN1AC/hvPmm5A==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/eventstream-serde-config-resolver": { "version": "4.3.12", "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-config-resolver/-/eventstream-serde-config-resolver-4.3.12.tgz", "integrity": "sha512-7epsAZ3QvfHkngz6RXQYseyZYHlmWXSTPOfPmXkiS+zA6TBNo1awUaMFL9vxyXlGdoELmCZyZe1nQE+imbmV+Q==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/eventstream-serde-node": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-node/-/eventstream-serde-node-4.2.12.tgz", "integrity": "sha512-D1pFuExo31854eAvg89KMn9Oab/wEeJR6Buy32B49A9Ogdtx5fwZPqBHUlDzaCDpycTFk2+fSQgX689Qsk7UGA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/eventstream-serde-universal": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/eventstream-serde-universal": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/eventstream-serde-universal/-/eventstream-serde-universal-4.2.12.tgz", "integrity": "sha512-+yNuTiyBACxOJUTvbsNsSOfH9G9oKbaJE1lNL3YHpGcuucl6rPZMi3nrpehpVOVR2E07YqFFmtwpImtpzlouHQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/eventstream-codec": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/fetch-http-handler": { "version": "5.3.14", "resolved": "https://registry.npmjs.org/@smithy/fetch-http-handler/-/fetch-http-handler-5.3.14.tgz", "integrity": "sha512-Aswg1yMsujkikRVv+JIDw2ybTgx0cnTnv7pMee46OX6lTMwk/QpH1lbx3vN3feMwyNrFcSUbYBtbgwHXXn3CIA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/hash-node": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/hash-node/-/hash-node-4.2.12.tgz", "integrity": "sha512-QhBYbGrbxTkZ43QoTPrK72DoYviDeg6YKDrHTMJbbC+A0sml3kSjzFtXP7BtbyJnXojLfTQldGdUR0RGD8dA3w==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.1", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/invalid-dependency": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/invalid-dependency/-/invalid-dependency-4.2.12.tgz", "integrity": "sha512-/4F1zb7Z8LOu1PalTdESFHR0RbPwHd3FcaG1sI3UEIriQTWakysgJr65lc1jj6QY5ye7aFsisajotH6UhWfm/g==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/is-array-buffer": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@smithy/is-array-buffer/-/is-array-buffer-4.2.2.tgz", "integrity": "sha512-n6rQ4N8Jj4YTQO3YFrlgZuwKodf4zUFs7EJIWH86pSCWBaAtAGBFfCM7Wx6D2bBJ2xqFNxGBSrUWswT3M0VJow==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/middleware-content-length": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/middleware-content-length/-/middleware-content-length-4.2.12.tgz", "integrity": "sha512-YE58Yz+cvFInWI/wOTrB+DbvUVz/pLn5mC5MvOV4fdRUc6qGwygyngcucRQjAhiCEbmfLOXX0gntSIcgMvAjmA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/middleware-endpoint": { "version": "4.4.24", "resolved": "https://registry.npmjs.org/@smithy/middleware-endpoint/-/middleware-endpoint-4.4.24.tgz", "integrity": "sha512-k7SZG+7IbS4fVAI47p+QixmcjqliCoZ7T5ZtAJMHyViiv7AhMC9aXtgxvNQ8TQmbUe7kotsvW2XeEEqnTmdOXg==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/core": "^3.23.10", "@smithy/middleware-serde": "^4.2.13", "@smithy/node-config-provider": "^4.3.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "@smithy/url-parser": "^4.2.12", "@smithy/util-middleware": "^4.2.12", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/middleware-retry": { "version": "4.4.41", "resolved": "https://registry.npmjs.org/@smithy/middleware-retry/-/middleware-retry-4.4.41.tgz", "integrity": "sha512-qjeS0KGftfz2CL4/IziPmQurzemKRPh6sekt3IFbj1519nkj+JM+RcdjVrC1AQFFZhmW3zz7KqwOgN+qJZeVlQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/protocol-http": "^5.3.12", "@smithy/service-error-classification": "^4.2.12", "@smithy/smithy-client": "^4.12.4", "@smithy/types": "^4.13.1", "@smithy/util-middleware": "^4.2.12", "@smithy/util-retry": "^4.2.12", "@smithy/uuid": "^1.1.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/middleware-serde": { "version": "4.2.13", "resolved": "https://registry.npmjs.org/@smithy/middleware-serde/-/middleware-serde-4.2.13.tgz", "integrity": "sha512-appEschlOmriCVGLYTTjKdbnXIZ55XT9TsV+aGuj5Jiw988gmEZwJwPkYqlZdwajMKgfxt5epjFTGriyYf4Kiw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/core": "^3.23.10", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/middleware-stack": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/middleware-stack/-/middleware-stack-4.2.12.tgz", "integrity": "sha512-kruC5gRHwsCOuyCd4ouQxYjgRAym2uDlCvQ5acuMtRrcdfg7mFBg6blaxcJ09STpt3ziEkis6bhg1uwrWU7txw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/node-config-provider": { "version": "4.3.12", "resolved": "https://registry.npmjs.org/@smithy/node-config-provider/-/node-config-provider-4.3.12.tgz", "integrity": "sha512-tr2oKX2xMcO+rBOjobSwVAkV05SIfUKz8iI53rzxEmgW3GOOPOv0UioSDk+J8OpRQnpnhsO3Af6IEBabQBVmiw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/shared-ini-file-loader": "^4.4.7", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/node-http-handler": { "version": "4.4.15", "resolved": "https://registry.npmjs.org/@smithy/node-http-handler/-/node-http-handler-4.4.15.tgz", "integrity": "sha512-2z3Z7Qfts2Eui5Oy+MLJjwKx1LT0Hm/b6W0XJXkUIFHP1W9D4BhdvxWW2W5xPP92CoXO+B4C/zSH67uIxMkWoA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/abort-controller": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/querystring-builder": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/property-provider": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/property-provider/-/property-provider-4.2.12.tgz", "integrity": "sha512-jqve46eYU1v7pZ5BM+fmkbq3DerkSluPr5EhvOcHxygxzD05ByDRppRwRPPpFrsFo5yDtCYLKu+kreHKVrvc7A==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/protocol-http": { "version": "5.3.12", "resolved": "https://registry.npmjs.org/@smithy/protocol-http/-/protocol-http-5.3.12.tgz", "integrity": "sha512-fit0GZK9I1xoRlR4jXmbLhoN0OdEpa96ul8M65XdmXnxXkuMxM0Y8HDT0Fh0Xb4I85MBvBClOzgSrV1X2s1Hxw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/querystring-builder": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/querystring-builder/-/querystring-builder-4.2.12.tgz", "integrity": "sha512-6wTZjGABQufekycfDGMEB84BgtdOE/rCVTov+EDXQ8NHKTUNIp/j27IliwP7tjIU9LR+sSzyGBOXjeEtVgzCHg==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.1", "@smithy/util-uri-escape": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/querystring-parser": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/querystring-parser/-/querystring-parser-4.2.12.tgz", "integrity": "sha512-P2OdvrgiAKpkPNKlKUtWbNZKB1XjPxM086NeVhK+W+wI46pIKdWBe5QyXvhUm3MEcyS/rkLvY8rZzyUdmyDZBw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/service-error-classification": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/service-error-classification/-/service-error-classification-4.2.12.tgz", "integrity": "sha512-LlP29oSQN0Tw0b6D0Xo6BIikBswuIiGYbRACy5ujw/JgWSzTdYj46U83ssf6Ux0GyNJVivs2uReU8pt7Eu9okQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.1" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/shared-ini-file-loader": { "version": "4.4.7", "resolved": "https://registry.npmjs.org/@smithy/shared-ini-file-loader/-/shared-ini-file-loader-4.4.7.tgz", "integrity": "sha512-HrOKWsUb+otTeo1HxVWeEb99t5ER1XrBi/xka2Wv6NVmTbuCUC1dvlrksdvxFtODLBjsC+PHK+fuy2x/7Ynyiw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/signature-v4": { "version": "5.3.12", "resolved": "https://registry.npmjs.org/@smithy/signature-v4/-/signature-v4-5.3.12.tgz", "integrity": "sha512-B/FBwO3MVOL00DaRSXfXfa/TRXRheagt/q5A2NM13u7q+sHS59EOVGQNfG7DkmVtdQm5m3vOosoKAXSqn/OEgw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-middleware": "^4.2.12", "@smithy/util-uri-escape": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/smithy-client": { "version": "4.12.4", "resolved": "https://registry.npmjs.org/@smithy/smithy-client/-/smithy-client-4.12.4.tgz", "integrity": "sha512-kbFGh3QrUj7Z9zYHCip+dGVyRGiFo6JK0A+9InOwmU4ZCkJs3HKhjLL/ABe5I8kp9uScqrftcWrDh7YxlWmmZA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/core": "^3.23.10", "@smithy/middleware-endpoint": "^4.4.24", "@smithy/middleware-stack": "^4.2.12", "@smithy/protocol-http": "^5.3.12", "@smithy/types": "^4.13.1", "@smithy/util-stream": "^4.5.18", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/types": { "version": "4.13.1", "resolved": "https://registry.npmjs.org/@smithy/types/-/types-4.13.1.tgz", "integrity": "sha512-787F3yzE2UiJIQ+wYW1CVg2odHjmaWLGksnKQHUrK/lYZSEcy1msuLVvxaR/sI2/aDe9U+TBuLsXnr3vod1g0g==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/url-parser": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/url-parser/-/url-parser-4.2.12.tgz", "integrity": "sha512-wOPKPEpso+doCZGIlr+e1lVI6+9VAKfL4kZWFgzVgGWY2hZxshNKod4l2LXS3PRC9otH/JRSjtEHqQ/7eLciRA==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/querystring-parser": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-base64": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/@smithy/util-base64/-/util-base64-4.3.2.tgz", "integrity": "sha512-XRH6b0H/5A3SgblmMa5ErXQ2XKhfbQB+Fm/oyLZ2O2kCUrwgg55bU0RekmzAhuwOjA9qdN5VU2BprOvGGUkOOQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-body-length-browser": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-browser/-/util-body-length-browser-4.2.2.tgz", "integrity": "sha512-JKCrLNOup3OOgmzeaKQwi4ZCTWlYR5H4Gm1r2uTMVBXoemo1UEghk5vtMi1xSu2ymgKVGW631e2fp9/R610ZjQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-body-length-node": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/@smithy/util-body-length-node/-/util-body-length-node-4.2.3.tgz", "integrity": "sha512-ZkJGvqBzMHVHE7r/hcuCxlTY8pQr1kMtdsVPs7ex4mMU+EAbcXppfo5NmyxMYi2XU49eqaz56j2gsk4dHHPG/g==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-buffer-from": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@smithy/util-buffer-from/-/util-buffer-from-4.2.2.tgz", "integrity": "sha512-FDXD7cvUoFWwN6vtQfEta540Y/YBe5JneK3SoZg9bThSoOAC/eGeYEua6RkBgKjGa/sz6Y+DuBZj3+YEY21y4Q==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/is-array-buffer": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-config-provider": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@smithy/util-config-provider/-/util-config-provider-4.2.2.tgz", "integrity": "sha512-dWU03V3XUprJwaUIFVv4iOnS1FC9HnMHDfUrlNDSh4315v0cWyaIErP8KiqGVbf5z+JupoVpNM7ZB3jFiTejvQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-defaults-mode-browser": { "version": "4.3.40", "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-browser/-/util-defaults-mode-browser-4.3.40.tgz", "integrity": "sha512-TB++dVe/aHkhCw8+fVUiGEEyz70Drftze6uk5VGBDJAjEj2mqNFftkeY7Jyit3uui346NkZxzLMGM0yzD/S8og==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.4", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-defaults-mode-node": { "version": "4.2.43", "resolved": "https://registry.npmjs.org/@smithy/util-defaults-mode-node/-/util-defaults-mode-node-4.2.43.tgz", "integrity": "sha512-cHmr8Q1BJstJC8ahvYrcyqjSIwrgLbpphOYmfMvF+EVsKUU52b3DDLb0SyiAzR16o7FR1r2IVUFfWWu7ADh1iw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/config-resolver": "^4.4.11", "@smithy/credential-provider-imds": "^4.2.12", "@smithy/node-config-provider": "^4.3.12", "@smithy/property-provider": "^4.2.12", "@smithy/smithy-client": "^4.12.4", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-endpoints": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/@smithy/util-endpoints/-/util-endpoints-3.3.3.tgz", "integrity": "sha512-VACQVe50j0HZPjpwWcjyT51KUQ4AnsvEaQ2lKHOSL4mNLD0G9BjEniQ+yCt1qqfKfiAHRAts26ud7hBjamrwig==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/node-config-provider": "^4.3.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-hex-encoding": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@smithy/util-hex-encoding/-/util-hex-encoding-4.2.2.tgz", "integrity": "sha512-Qcz3W5vuHK4sLQdyT93k/rfrUwdJ8/HZ+nMUOyGdpeGA1Wxt65zYwi3oEl9kOM+RswvYq90fzkNDahPS8K0OIg==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-middleware": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/util-middleware/-/util-middleware-4.2.12.tgz", "integrity": "sha512-Er805uFUOvgc0l8nv0e0su0VFISoxhJ/AwOn3gL2NWNY2LUEldP5WtVcRYSQBcjg0y9NfG8JYrCJaYDpupBHJQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-retry": { "version": "4.2.12", "resolved": "https://registry.npmjs.org/@smithy/util-retry/-/util-retry-4.2.12.tgz", "integrity": "sha512-1zopLDUEOwumjcHdJ1mwBHddubYF8GMQvstVCLC54Y46rqoHwlIU+8ZzUeaBcD+WCJHyDGSeZ2ml9YSe9aqcoQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/service-error-classification": "^4.2.12", "@smithy/types": "^4.13.1", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-stream": { "version": "4.5.18", "resolved": "https://registry.npmjs.org/@smithy/util-stream/-/util-stream-4.5.18.tgz", "integrity": "sha512-o0hxsNp2rC7Kz93RNER/mv5G60kntYPPjV9e9Zoa3Mm455bCGHlFW6TywziCQRlLzvrQj/mmWJimAvJWF/wfjg==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/fetch-http-handler": "^5.3.14", "@smithy/node-http-handler": "^4.4.15", "@smithy/types": "^4.13.1", "@smithy/util-base64": "^4.3.2", "@smithy/util-buffer-from": "^4.2.2", "@smithy/util-hex-encoding": "^4.2.2", "@smithy/util-utf8": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-uri-escape": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@smithy/util-uri-escape/-/util-uri-escape-4.2.2.tgz", "integrity": "sha512-2kAStBlvq+lTXHyAZYfJRb/DfS3rsinLiwb+69SstC9Vb0s9vNWkRwpnj918Pfi85mzi42sOqdV72OLxWAISnw==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/util-utf8": { "version": "4.2.2", "resolved": "https://registry.npmjs.org/@smithy/util-utf8/-/util-utf8-4.2.2.tgz", "integrity": "sha512-75MeYpjdWRe8M5E3AW0O4Cx3UadweS+cwdXjwYGBW5h/gxxnbeZ877sLPX/ZJA9GVTlL/qG0dXP29JWFCD1Ayw==", "license": "Apache-2.0", "peer": true, "dependencies": { "@smithy/util-buffer-from": "^4.2.2", "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@smithy/uuid": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@smithy/uuid/-/uuid-1.1.2.tgz", "integrity": "sha512-O/IEdcCUKkubz60tFbGA7ceITTAJsty+lBjNoorP4Z6XRqaFb/OjQjZODophEcuq68nKm6/0r+6/lLQ+XVpk8g==", "license": "Apache-2.0", "peer": true, "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@snazzah/davey": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey/-/davey-0.1.10.tgz", "integrity": "sha512-J5f7vV5/tnj0xGnqufFRd6qiWn3FcR3iXjpjpEmO2Ok+Io0AASkMaZ3I39TsL45as0Qo5bq9wWuamFQ77PjJ+g==", "license": "MIT", "peer": true, "engines": { "node": ">= 10" }, "funding": { "url": "https://github.com/sponsors/Snazzah" }, "optionalDependencies": { "@snazzah/davey-android-arm-eabi": "0.1.10", "@snazzah/davey-android-arm64": "0.1.10", "@snazzah/davey-darwin-arm64": "0.1.10", "@snazzah/davey-darwin-x64": "0.1.10", "@snazzah/davey-freebsd-x64": "0.1.10", "@snazzah/davey-linux-arm-gnueabihf": "0.1.10", "@snazzah/davey-linux-arm64-gnu": "0.1.10", "@snazzah/davey-linux-arm64-musl": "0.1.10", "@snazzah/davey-linux-x64-gnu": "0.1.10", "@snazzah/davey-linux-x64-musl": "0.1.10", "@snazzah/davey-wasm32-wasi": "0.1.10", "@snazzah/davey-win32-arm64-msvc": "0.1.10", "@snazzah/davey-win32-ia32-msvc": "0.1.10", "@snazzah/davey-win32-x64-msvc": "0.1.10" } }, "node_modules/@snazzah/davey-android-arm-eabi": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-android-arm-eabi/-/davey-android-arm-eabi-0.1.10.tgz", "integrity": "sha512-7bwHxSNEI2wVXOT6xnmpnO9SHb2xwAnf9oEdL45dlfVHTgU1Okg5rwGwRvZ2aLVFFbTyecfC8EVZyhpyTkjLSw==", "cpu": [ "arm" ], "license": "MIT", "optional": true, "os": [ "android" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@snazzah/davey-android-arm64": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-android-arm64/-/davey-android-arm64-0.1.10.tgz", "integrity": "sha512-68WUf2LQwQTP9MgPcCqTWwJztJSIk0keGfF2Y/b+MihSDh29fYJl7C0rbz69aUrVCvCC2lYkB/46P8X1kBz7yg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "android" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@snazzah/davey-darwin-arm64": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-darwin-arm64/-/davey-darwin-arm64-0.1.10.tgz", "integrity": "sha512-nYC+DWCGUC1jUGEenCNQE/jJpL/02m0ebY/NvTCQbul5ktI/ShVzgA3kzssEhZvhf6jbH048Rs39wDhp/b24Jg==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@snazzah/davey-darwin-x64": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-darwin-x64/-/davey-darwin-x64-0.1.10.tgz", "integrity": "sha512-0q5Rrcs+O9sSSnPX+A3R3djEQs2nTAtMe5N3lApO6lZas/QNMl6wkEWCvTbDc2cfAYBMSk2jgc1awlRXi4LX3Q==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "darwin" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@snazzah/davey-freebsd-x64": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-freebsd-x64/-/davey-freebsd-x64-0.1.10.tgz", "integrity": "sha512-/Gq5YDD6Oz8iBqVJLswUnetCv9JCRo1quYX5ujzpAG8zPCNItZo4g4h5p9C+h4Yoay2quWBYhoaVqQKT96bm8g==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "freebsd" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@snazzah/davey-linux-arm-gnueabihf": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-linux-arm-gnueabihf/-/davey-linux-arm-gnueabihf-0.1.10.tgz", "integrity": "sha512-0Z7Vrt0WIbgxws9CeHB9qlueYJlvltI44rUuZmysdi70UcHGxlr7nE3MnzYCr9nRWRegohn8EQPWHMKMDJH2GA==", "cpu": [ "arm" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@snazzah/davey-linux-arm64-gnu": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-linux-arm64-gnu/-/davey-linux-arm64-gnu-0.1.10.tgz", "integrity": "sha512-xhZQycn4QB+qXhqm/QmZ+kb9MHMXcbjjoPfvcIL4WMQXFG/zUWHW8EiBk7ZTEGMOpeab3F9D1+MlgumglYByUQ==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@snazzah/davey-linux-arm64-musl": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-linux-arm64-musl/-/davey-linux-arm64-musl-0.1.10.tgz", "integrity": "sha512-pudzQCP9rZItwW4qHHvciMwtNd9kWH4l73g6Id1LRpe6sc8jiFBV7W+YXITj2PZbI0by6XPfkRP6Dk5IkGOuAw==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@snazzah/davey-linux-x64-gnu": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-linux-x64-gnu/-/davey-linux-x64-gnu-0.1.10.tgz", "integrity": "sha512-DC8qRmk+xJEFNqjxKB46cETKeDQqgUqE5p39KXS2k6Vl/XTi8pw8pXOxrPfYte5neoqlWAVQzbxuLnwpyRJVEQ==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@snazzah/davey-linux-x64-musl": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-linux-x64-musl/-/davey-linux-x64-musl-0.1.10.tgz", "integrity": "sha512-wPR5/2QmsF7sR0WUaCwbk4XI3TLcxK9PVK8mhgcAYyuRpbhcVgNGWXs8ulcyMSXve5pFRJAFAuMTGCEb014peg==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "linux" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@snazzah/davey-wasm32-wasi": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-wasm32-wasi/-/davey-wasm32-wasi-0.1.10.tgz", "integrity": "sha512-SfQavU+eKTDbRmPeLRodrVSfsWq25PYTmH1nIZW3B27L6IkijzjXZZuxiU1ZG1gdI5fB7mwXrOTtx34t+vAG7Q==", "cpu": [ "wasm32" ], "license": "MIT", "optional": true, "peer": true, "dependencies": { "@napi-rs/wasm-runtime": "^1.1.1" }, "engines": { "node": ">=14.0.0" } }, "node_modules/@snazzah/davey-win32-arm64-msvc": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-win32-arm64-msvc/-/davey-win32-arm64-msvc-0.1.10.tgz", "integrity": "sha512-Raafk53smYs67wZCY9bQXHXzbaiRMS5QCdjTdin3D9fF5A06T/0Zv1z7/YnaN+O3GSL/Ou3RvynF7SziToYiFQ==", "cpu": [ "arm64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@snazzah/davey-win32-ia32-msvc": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-win32-ia32-msvc/-/davey-win32-ia32-msvc-0.1.10.tgz", "integrity": "sha512-pAs43l/DiZ+icqBwxIwNePzuYxFM1ZblVuf7t6vwwSLxvova7vnREnU7qDVjbc5/YTUHOsqYy3S6TpZMzDo2lw==", "cpu": [ "ia32" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@snazzah/davey-win32-x64-msvc": { "version": "0.1.10", "resolved": "https://registry.npmjs.org/@snazzah/davey-win32-x64-msvc/-/davey-win32-x64-msvc-0.1.10.tgz", "integrity": "sha512-kr6148VVBoUT4CtD+5hYshTFRny7R/xQZxXFhFc0fYjtmdMVM8Px9M91olg1JFNxuNzdfMfTufR58Q3wfBocug==", "cpu": [ "x64" ], "license": "MIT", "optional": true, "os": [ "win32" ], "peer": true, "engines": { "node": ">= 10" } }, "node_modules/@standard-schema/spec": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz", "integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==", "dev": true, "license": "MIT" }, "node_modules/@tinyhttp/content-disposition": { "version": "2.2.4", "resolved": "https://registry.npmjs.org/@tinyhttp/content-disposition/-/content-disposition-2.2.4.tgz", "integrity": "sha512-5Kc5CM2Ysn3vTTArBs2vESUt0AQiWZA86yc1TI3B+lxXmtEq133C1nxXNOgnzhrivdPZIh3zLj5gDnZjoLL5GA==", "license": "MIT", "peer": true, "engines": { "node": ">=12.17.0" }, "funding": { "type": "individual", "url": "https://github.com/tinyhttp/tinyhttp?sponsor=1" } }, "node_modules/@tokenizer/inflate": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", "license": "MIT", "peer": true, "dependencies": { "debug": "^4.4.3", "token-types": "^6.1.1" }, "engines": { "node": ">=18" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Borewit" } }, "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", "license": "MIT", "peer": true }, "node_modules/@tootallnate/quickjs-emscripten": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/@tootallnate/quickjs-emscripten/-/quickjs-emscripten-0.23.0.tgz", "integrity": "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA==", "license": "MIT", "peer": true }, "node_modules/@tybys/wasm-util": { "version": "0.10.1", "resolved": "https://registry.npmjs.org/@tybys/wasm-util/-/wasm-util-0.10.1.tgz", "integrity": "sha512-9tTaPJLSiejZKx+Bmog4uSubteqTvFrVrURwkmHixBo0G4seD0zUxp98E1DzUBJxLQ3NPwXrGKDiVjwx/DpPsg==", "license": "MIT", "optional": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/@types/aws-lambda": { "version": "8.10.161", "resolved": "https://registry.npmjs.org/@types/aws-lambda/-/aws-lambda-8.10.161.tgz", "integrity": "sha512-rUYdp+MQwSFocxIOcSsYSF3YYYC/uUpMbCY/mbO21vGqfrEYvNSoPyKYDj6RhXXpPfS0KstW9RwG3qXh9sL7FQ==", "license": "MIT", "peer": true }, "node_modules/@types/body-parser": { "version": "1.19.6", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.6.tgz", "integrity": "sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==", "license": "MIT", "peer": true, "dependencies": { "@types/connect": "*", "@types/node": "*" } }, "node_modules/@types/bun": { "version": "1.3.9", "resolved": "https://registry.npmjs.org/@types/bun/-/bun-1.3.9.tgz", "integrity": "sha512-KQ571yULOdWJiMH+RIWIOZ7B2RXQGpL1YQrBtLIV3FqDcCu6FsbFUBwhdKUlCKUpS3PJDsHlJ1QKlpxoVR+xtw==", "license": "MIT", "optional": true, "peer": true, "dependencies": { "bun-types": "1.3.9" } }, "node_modules/@types/chai": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/@types/chai/-/chai-5.2.3.tgz", "integrity": "sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==", "dev": true, "license": "MIT", "dependencies": { "@types/deep-eql": "*", "assertion-error": "^2.0.1" } }, "node_modules/@types/connect": { "version": "3.4.38", "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", "license": "MIT", "peer": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/deep-eql": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/deep-eql/-/deep-eql-4.0.2.tgz", "integrity": "sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==", "dev": true, "license": "MIT" }, "node_modules/@types/estree": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, "node_modules/@types/express": { "version": "5.0.6", "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.6.tgz", "integrity": "sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==", "license": "MIT", "peer": true, "dependencies": { "@types/body-parser": "*", "@types/express-serve-static-core": "^5.0.0", "@types/serve-static": "^2" } }, "node_modules/@types/express-serve-static-core": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.1.1.tgz", "integrity": "sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==", "license": "MIT", "peer": true, "dependencies": { "@types/node": "*", "@types/qs": "*", "@types/range-parser": "*", "@types/send": "*" } }, "node_modules/@types/http-errors": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.5.tgz", "integrity": "sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==", "license": "MIT", "peer": true }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, "license": "MIT" }, "node_modules/@types/jsonwebtoken": { "version": "9.0.10", "resolved": "https://registry.npmjs.org/@types/jsonwebtoken/-/jsonwebtoken-9.0.10.tgz", "integrity": "sha512-asx5hIG9Qmf/1oStypjanR7iKTv0gXQ1Ov/jfrX6kS/EO0OFni8orbmGCn0672NHR3kXHwpAwR+B368ZGN/2rA==", "license": "MIT", "peer": true, "dependencies": { "@types/ms": "*", "@types/node": "*" } }, "node_modules/@types/long": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.2.tgz", "integrity": "sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA==", "license": "MIT", "peer": true }, "node_modules/@types/mime-types": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@types/mime-types/-/mime-types-2.1.4.tgz", "integrity": "sha512-lfU4b34HOri+kAY5UheuFMWPDOI+OPceBSHZKp69gEyTL/mmJ4cnU6Y/rlme3UL3GyOn6Y42hyIEw0/q8sWx5w==", "license": "MIT", "peer": true }, "node_modules/@types/ms": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-2.1.0.tgz", "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", "license": "MIT", "peer": true }, "node_modules/@types/node": { "version": "20.19.37", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.37.tgz", "integrity": "sha512-8kzdPJ3FsNsVIurqBs7oodNnCEVbni9yUEkaHbgptDACOPW04jimGagZ51E6+lXUwJjgnBw+hyko/lkFWCldqw==", "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, "node_modules/@types/qs": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.15.0.tgz", "integrity": "sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==", "license": "MIT", "peer": true }, "node_modules/@types/range-parser": { "version": "1.2.7", "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", "license": "MIT", "peer": true }, "node_modules/@types/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.0.tgz", "integrity": "sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA==", "license": "MIT", "peer": true }, "node_modules/@types/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/@types/send/-/send-1.2.1.tgz", "integrity": "sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==", "license": "MIT", "peer": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/serve-static": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-2.2.0.tgz", "integrity": "sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==", "license": "MIT", "peer": true, "dependencies": { "@types/http-errors": "*", "@types/node": "*" } }, "node_modules/@types/ws": { "version": "8.18.1", "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.18.1.tgz", "integrity": "sha512-ThVF6DCVhA8kUGy+aazFQ4kXQ7E1Ty7A3ypFOe0IcJV8O/M511G99AW24irKrW56Wt44yG9+ij8FaqoBGkuBXg==", "license": "MIT", "peer": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/yauzl": { "version": "2.10.3", "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.10.3.tgz", "integrity": "sha512-oJoftv0LSuaDZE3Le4DbKX+KS9G36NzOeSap90UIK0yMA/NhKJhqlSGtNDORNRaIbQfzjXDrQa0ytJ6mNRGz/Q==", "license": "MIT", "optional": true, "peer": true, "dependencies": { "@types/node": "*" } }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.57.0.tgz", "integrity": "sha512-qeu4rTHR3/IaFORbD16gmjq9+rEs9fGKdX0kF6BKSfi+gCuG3RCKLlSBYzn/bGsY9Tj7KE/DAQStbp8AHJGHEQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/type-utils": "8.57.0", "@typescript-eslint/utils": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "@typescript-eslint/parser": "^8.57.0", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/project-service": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.0", "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/scope-manager": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/tsconfig-utils": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/types": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/typescript-estree": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/project-service": "8.57.0", "@typescript-eslint/tsconfig-utils": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/visitor-keys": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/brace-expansion": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/minimatch": { "version": "10.2.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.2" }, "engines": { "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/parser": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.57.0.tgz", "integrity": "sha512-XZzOmihLIr8AD1b9hL9ccNMzEMWt/dE2u7NyTY9jJG6YNiNthaD5XtUHVF2uCXZ15ng+z2hT3MVuxnUYhq6k1g==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/project-service": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.0", "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/scope-manager": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/tsconfig-utils": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/types": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/typescript-estree": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/project-service": "8.57.0", "@typescript-eslint/tsconfig-utils": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/parser/node_modules/@typescript-eslint/visitor-keys": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/parser/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/parser/node_modules/brace-expansion": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/parser/node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@typescript-eslint/parser/node_modules/minimatch": { "version": "10.2.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.2" }, "engines": { "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/project-service": { "version": "8.56.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.56.1.tgz", "integrity": "sha512-TAdqQTzHNNvlVFfR+hu2PDJrURiwKsUvxFn1M0h95BB8ah5jejas08jUWG4dBA68jDMI988IvtfdAI53JzEHOQ==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.56.1", "@typescript-eslint/types": "^8.56.1", "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.56.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.56.1.tgz", "integrity": "sha512-YAi4VDKcIZp0O4tz/haYKhmIDZFEUPOreKbfdAN3SzUDMcPhJ8QI99xQXqX+HoUVq8cs85eRKnD+rne2UAnj2w==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/tsconfig-utils": { "version": "8.56.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.56.1.tgz", "integrity": "sha512-qOtCYzKEeyr3aR9f28mPJqBty7+DBqsdd63eO0yyDwc6vgThj2UjWfJIcsFeSucYydqcuudMOprZ+x1SpF3ZuQ==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.57.0.tgz", "integrity": "sha512-yjgh7gmDcJ1+TcEg8x3uWQmn8ifvSupnPfjP21twPKrDP/pTHlEQgmKcitzF/rzPSmv7QjJ90vRpN4U+zoUjwQ==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/utils": "8.57.0", "debug": "^4.4.3", "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/project-service": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.0", "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/scope-manager": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/tsconfig-utils": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/types": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/typescript-estree": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/project-service": "8.57.0", "@typescript-eslint/tsconfig-utils": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/utils": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/type-utils/node_modules/@typescript-eslint/visitor-keys": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/type-utils/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/type-utils/node_modules/brace-expansion": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/type-utils/node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@typescript-eslint/type-utils/node_modules/minimatch": { "version": "10.2.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.2" }, "engines": { "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/types": { "version": "8.56.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.56.1.tgz", "integrity": "sha512-dbMkdIUkIkchgGDIv7KLUpa0Mda4IYjo4IAMJUZ+3xNoUXxMsk9YtKpTHSChRS85o+H9ftm51gsK1dZReY9CVw==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/typescript-estree": { "version": "8.56.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.56.1.tgz", "integrity": "sha512-qzUL1qgalIvKWAf9C1HpvBjif+Vm6rcT5wZd4VoMb9+Km3iS3Cv9DY6dMRMDtPnwRAFyAi7YXJpTIEXLvdfPxg==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/project-service": "8.56.1", "@typescript-eslint/tsconfig-utils": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/visitor-keys": "8.56.1", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { "node": "18 || 20 || >=22" } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/minimatch": { "version": "10.2.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.2" }, "engines": { "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/@typescript-eslint/utils": { "version": "8.56.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.56.1.tgz", "integrity": "sha512-HPAVNIME3tABJ61siYlHzSWCGtOoeP2RTIaHXFMPqjrQKCGB9OgUVdiNgH7TJS2JNIQ5qQ4RsAUDuGaGme/KOA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.56.1", "@typescript-eslint/types": "8.56.1", "@typescript-eslint/typescript-estree": "8.56.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { "version": "8.56.1", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.56.1.tgz", "integrity": "sha512-KiROIzYdEV85YygXw6BI/Dx4fnBlFQu6Mq4QE4MOH9fFnhohw6wX/OAvDY2/C+ut0I3RSPKenvZJIVYqJNkhEw==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.56.1", "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@vitest/coverage-v8": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@vitest/coverage-v8/-/coverage-v8-4.1.0.tgz", "integrity": "sha512-nDWulKeik2bL2Va/Wl4x7DLuTKAXa906iRFooIRPR+huHkcvp9QDkPQ2RJdmjOFrqOqvNfoSQLF68deE3xC3CQ==", "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^1.0.2", "@vitest/utils": "4.1.0", "ast-v8-to-istanbul": "^1.0.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.2.0", "magicast": "^0.5.2", "obug": "^2.1.1", "std-env": "^4.0.0-rc.1", "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "@vitest/browser": "4.1.0", "vitest": "4.1.0" }, "peerDependenciesMeta": { "@vitest/browser": { "optional": true } } }, "node_modules/@vitest/expect": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.1.0.tgz", "integrity": "sha512-EIxG7k4wlWweuCLG9Y5InKFwpMEOyrMb6ZJ1ihYu02LVj/bzUwn2VMU+13PinsjRW75XnITeFrQBMH5+dLvCDA==", "dev": true, "license": "MIT", "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.0", "@vitest/utils": "4.1.0", "chai": "^6.2.2", "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/mocker": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.1.0.tgz", "integrity": "sha512-evxREh+Hork43+Y4IOhTo+h5lGmVRyjqI739Rz4RlUPqwrkFFDF6EMvOOYjTx4E8Tl6gyCLRL8Mu7Ry12a13Tw==", "dev": true, "license": "MIT", "dependencies": { "@vitest/spy": "4.1.0", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "funding": { "url": "https://opencollective.com/vitest" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" }, "peerDependenciesMeta": { "msw": { "optional": true }, "vite": { "optional": true } } }, "node_modules/@vitest/pretty-format": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.1.0.tgz", "integrity": "sha512-3RZLZlh88Ib0J7NQTRATfc/3ZPOnSUn2uDBUoGNn5T36+bALixmzphN26OUD3LRXWkJu4H0s5vvUeqBiw+kS0A==", "dev": true, "license": "MIT", "dependencies": { "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/runner": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.1.0.tgz", "integrity": "sha512-Duvx2OzQ7d6OjchL+trw+aSrb9idh7pnNfxrklo14p3zmNL4qPCDeIJAK+eBKYjkIwG96Bc6vYuxhqDXQOWpoQ==", "dev": true, "license": "MIT", "dependencies": { "@vitest/utils": "4.1.0", "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/snapshot": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.1.0.tgz", "integrity": "sha512-0Vy9euT1kgsnj1CHttwi9i9o+4rRLEaPRSOJ5gyv579GJkNpgJK+B4HSv/rAWixx2wdAFci1X4CEPjiu2bXIMg==", "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "4.1.0", "@vitest/utils": "4.1.0", "magic-string": "^0.30.21", "pathe": "^2.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/spy": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.1.0.tgz", "integrity": "sha512-pz77k+PgNpyMDv2FV6qmk5ZVau6c3R8HC8v342T2xlFxQKTrSeYw9waIJG8KgV9fFwAtTu4ceRzMivPTH6wSxw==", "dev": true, "license": "MIT", "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@vitest/utils": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.1.0.tgz", "integrity": "sha512-XfPXT6a8TZY3dcGY8EdwsBulFCIw+BeeX0RZn2x/BtiY/75YGh8FeWGG8QISN/WhaqSrE2OrlDgtF8q5uhOTmw==", "dev": true, "license": "MIT", "dependencies": { "@vitest/pretty-format": "4.1.0", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.0.3" }, "funding": { "url": "https://opencollective.com/vitest" } }, "node_modules/@whiskeysockets/baileys": { "version": "7.0.0-rc.9", "resolved": "https://registry.npmjs.org/@whiskeysockets/baileys/-/baileys-7.0.0-rc.9.tgz", "integrity": "sha512-YFm5gKXfDP9byCXCW3OPHKXLzrAKzolzgVUlRosHHgwbnf2YOO3XknkMm6J7+F0ns8OA0uuSBhgkRHTDtqkacw==", "hasInstallScript": true, "license": "MIT", "peer": true, "dependencies": { "@cacheable/node-cache": "^1.4.0", "@hapi/boom": "^9.1.3", "async-mutex": "^0.5.0", "libsignal": "git+https://github.com/whiskeysockets/libsignal-node.git", "lru-cache": "^11.1.0", "music-metadata": "^11.7.0", "p-queue": "^9.0.0", "pino": "^9.6", "protobufjs": "^7.2.4", "ws": "^8.13.0" }, "engines": { "node": ">=20.0.0" }, "peerDependencies": { "audio-decode": "^2.1.3", "jimp": "^1.6.0", "link-preview-js": "^3.0.0", "sharp": "*" }, "peerDependenciesMeta": { "audio-decode": { "optional": true }, "jimp": { "optional": true }, "link-preview-js": { "optional": true } } }, "node_modules/@whiskeysockets/baileys/node_modules/p-queue": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-9.1.0.tgz", "integrity": "sha512-O/ZPaXuQV29uSLbxWBGGZO1mCQXV2BLIwUr59JUU9SoH76mnYvtms7aafH/isNSNGwuEfP6W/4xD0/TJXxrizw==", "license": "MIT", "peer": true, "dependencies": { "eventemitter3": "^5.0.1", "p-timeout": "^7.0.0" }, "engines": { "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/@whiskeysockets/baileys/node_modules/p-timeout": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-7.0.1.tgz", "integrity": "sha512-AxTM2wDGORHGEkPCt8yqxOTMgpfbEHqF51f/5fJCmwFC3C/zNcGT63SymH2ttOAaiIws2zVg4+izQCjrakcwHg==", "license": "MIT", "peer": true, "engines": { "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", "peer": true, "dependencies": { "event-target-shim": "^5.0.0" }, "engines": { "node": ">=6.5" } }, "node_modules/accepts": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" }, "engines": { "node": ">=0.4.0" } }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/agent-base": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-8.0.0.tgz", "integrity": "sha512-QT8i0hCz6C/KQ+KTAbSNwCHDGdmUJl2tp2ZpNlGSWCfhUNVbYG2WLE3MdZGBAgXPV4GAvjGMxo+C1hroyxmZEg==", "license": "MIT", "peer": true, "engines": { "node": ">= 14" } }, "node_modules/ajv": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/ajv-formats": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", "peer": true, "dependencies": { "ajv": "^8.0.0" }, "peerDependencies": { "ajv": "^8.0.0" }, "peerDependenciesMeta": { "ajv": { "optional": true } } }, "node_modules/ajv-formats/node_modules/ajv": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/ajv-formats/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT", "peer": true }, "node_modules/ansi-escapes": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-6.2.1.tgz", "integrity": "sha512-4nJ3yixlEthEJ9Rk4vPcdBRkZvQZlYyu8j4/Mqz5sgIkddmEnH2Yj2ZrnP9S3tQOvSNRUIgVNF/1yPpRAGNRig==", "license": "MIT", "peer": true, "engines": { "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ansi-regex": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", "peer": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-regex?sponsor=1" } }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/any-promise": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", "license": "MIT", "peer": true }, "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "license": "Python-2.0" }, "node_modules/assertion-error": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-2.0.1.tgz", "integrity": "sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==", "dev": true, "license": "MIT", "engines": { "node": ">=12" } }, "node_modules/ast-types": { "version": "0.13.4", "resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.13.4.tgz", "integrity": "sha512-x1FCFnFifvYDDzTaLII71vG5uvDwgtmDTEVWAxrgeiR8VjMONcCXJx7E+USjDtHlwFmt9MysbqgF9b9Vjr6w+w==", "license": "MIT", "peer": true, "dependencies": { "tslib": "^2.0.1" }, "engines": { "node": ">=4" } }, "node_modules/ast-v8-to-istanbul": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/ast-v8-to-istanbul/-/ast-v8-to-istanbul-1.0.0.tgz", "integrity": "sha512-1fSfIwuDICFA4LKkCzRPO7F0hzFf0B7+Xqrl27ynQaa+Rh0e1Es0v6kWHPott3lU10AyAr7oKHa65OppjLn3Rg==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.31", "estree-walker": "^3.0.3", "js-tokens": "^10.0.0" } }, "node_modules/async-mutex": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/async-mutex/-/async-mutex-0.5.0.tgz", "integrity": "sha512-1A94B18jkJ3DYq284ohPxoXbfTA5HsQ7/Mf4DEhcyLx3Bz27Rh59iScbB6EPiP+B+joue6YCxcMXSbFC1tZKwA==", "license": "MIT", "peer": true, "dependencies": { "tslib": "^2.4.0" } }, "node_modules/async-retry": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", "license": "MIT", "peer": true, "dependencies": { "retry": "0.13.1" } }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", "license": "MIT", "peer": true }, "node_modules/atomic-sleep": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz", "integrity": "sha512-kNOjDqAh7px0XWNI+4QbzoiR/nTkHAWNud2uvnJquD1/x5a7EQZMJT0AczqK0Qn67oY/TTQ1LbUKajZpp3I9tQ==", "license": "MIT", "peer": true, "engines": { "node": ">=8.0.0" } }, "node_modules/axios": { "version": "1.13.6", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.6.tgz", "integrity": "sha512-ChTCHMouEe2kn713WHbQGcuYrr6fXTBiu460OTwWrWob16g1bXn4vtz07Ope7ewMozJAnEquLk5lWQWtBig9DQ==", "license": "MIT", "peer": true, "dependencies": { "follow-redirects": "^1.15.11", "form-data": "^4.0.5", "proxy-from-env": "^1.1.0" } }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ], "license": "MIT", "peer": true }, "node_modules/basic-ftp": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/basic-ftp/-/basic-ftp-5.2.0.tgz", "integrity": "sha512-VoMINM2rqJwJgfdHq6RiUudKt2BV+FY5ZFezP/ypmwayk68+NzzAQy4XXLlqsGD4MCzq3DrmNFD/uUmBJuGoXw==", "license": "MIT", "peer": true, "engines": { "node": ">=10.0.0" } }, "node_modules/before-after-hook": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", "license": "Apache-2.0", "peer": true }, "node_modules/bignumber.js": { "version": "9.3.1", "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.3.1.tgz", "integrity": "sha512-Ko0uX15oIUS7wJ3Rb30Fs6SkVbLmPBAKdlm7q9+ak9bbIeFf0MwuBsQV6z7+X768/cHsfg+WlysDWJcmthjsjQ==", "license": "MIT", "peer": true, "engines": { "node": "*" } }, "node_modules/body-parser": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.2.tgz", "integrity": "sha512-oP5VkATKlNwcgvxi0vM0p/D3n2C3EReYVX+DNYs5TjZFn/oQt2j+4sVJtSMr18pdRr8wjTcBl6LoV+FUwzPmNA==", "license": "MIT", "peer": true, "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", "debug": "^4.4.3", "http-errors": "^2.0.0", "iconv-lite": "^0.7.0", "on-finished": "^2.4.1", "qs": "^6.14.1", "raw-body": "^3.0.1", "type-is": "^2.0.1" }, "engines": { "node": ">=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/boolbase": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", "license": "ISC", "peer": true }, "node_modules/bottleneck": { "version": "2.19.5", "resolved": "https://registry.npmjs.org/bottleneck/-/bottleneck-2.19.5.tgz", "integrity": "sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==", "license": "MIT", "peer": true }, "node_modules/bowser": { "version": "2.14.1", "resolved": "https://registry.npmjs.org/bowser/-/bowser-2.14.1.tgz", "integrity": "sha512-tzPjzCxygAKWFOJP011oxFHs57HzIhOEracIgAePE4pqB3LikALKnSzUyU4MGs9/iCEUuHlAJTjTc5M+u7YEGg==", "license": "MIT", "peer": true }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/buffer-crc32": { "version": "0.2.13", "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "license": "MIT", "peer": true, "engines": { "node": "*" } }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause", "peer": true }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "license": "MIT", "peer": true }, "node_modules/bun-types": { "version": "1.3.9", "resolved": "https://registry.npmjs.org/bun-types/-/bun-types-1.3.9.tgz", "integrity": "sha512-+UBWWOakIP4Tswh0Bt0QD0alpTY8cb5hvgiYeWCMet9YukHbzuruIEeXC2D7nMJPB12kbh8C7XJykSexEqGKJg==", "license": "MIT", "optional": true, "peer": true, "dependencies": { "@types/node": "*" } }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/cacheable": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/cacheable/-/cacheable-2.3.3.tgz", "integrity": "sha512-iffYMX4zxKp54evOH27fm92hs+DeC1DhXmNVN8Tr94M/iZIV42dqTHSR2Ik4TOSPyOAwKr7Yu3rN9ALoLkbWyQ==", "license": "MIT", "peer": true, "dependencies": { "@cacheable/memory": "^2.0.8", "@cacheable/utils": "^2.4.0", "hookified": "^1.15.0", "keyv": "^5.6.0", "qified": "^0.6.0" } }, "node_modules/cacheable/node_modules/keyv": { "version": "5.6.0", "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.6.0.tgz", "integrity": "sha512-CYDD3SOtsHtyXeEORYRx2qBtpDJFjRTGXUtmNEMGyzYOKj1TE3tycdlho7kA1Ufx9OYWZzg52QFBGALTirzDSw==", "license": "MIT", "peer": true, "dependencies": { "@keyv/serialize": "^1.1.1" } }, "node_modules/call-bind-apply-helpers": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", "peer": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/call-bound": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/chai": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/chai/-/chai-6.2.2.tgz", "integrity": "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/chmodrp": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/chmodrp/-/chmodrp-1.0.2.tgz", "integrity": "sha512-TdngOlFV1FLTzU0o1w8MB6/BFywhtLC0SzRTGJU7T9lmdjlCWeMRt1iVo0Ki+ldwNk0BqNiKoc8xpLZEQ8mY1w==", "license": "MIT", "peer": true }, "node_modules/chokidar": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-5.0.0.tgz", "integrity": "sha512-TQMmc3w+5AxjpL8iIiwebF73dRDF4fBIieAqGn9RGCWaEVwQ6Fb2cGe31Yns0RRIzii5goJ1Y7xbMwo1TxMplw==", "license": "MIT", "peer": true, "dependencies": { "readdirp": "^5.0.0" }, "engines": { "node": ">= 20.19.0" }, "funding": { "url": "https://paulmillr.com/funding/" } }, "node_modules/chownr": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", "license": "BlueOak-1.0.0", "peer": true, "engines": { "node": ">=18" } }, "node_modules/ci-info": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-4.4.0.tgz", "integrity": "sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/sibiraj-s" } ], "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/cli-cursor": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-5.0.0.tgz", "integrity": "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==", "license": "MIT", "peer": true, "dependencies": { "restore-cursor": "^5.0.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cli-highlight": { "version": "2.1.11", "resolved": "https://registry.npmjs.org/cli-highlight/-/cli-highlight-2.1.11.tgz", "integrity": "sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==", "license": "ISC", "peer": true, "dependencies": { "chalk": "^4.0.0", "highlight.js": "^10.7.1", "mz": "^2.4.0", "parse5": "^5.1.1", "parse5-htmlparser2-tree-adapter": "^6.0.0", "yargs": "^16.0.0" }, "bin": { "highlight": "bin/highlight" }, "engines": { "node": ">=8.0.0", "npm": ">=5.0.0" } }, "node_modules/cli-highlight/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/cli-highlight/node_modules/cliui": { "version": "7.0.4", "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "license": "ISC", "peer": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, "node_modules/cli-highlight/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/cli-highlight/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/cli-highlight/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/cli-highlight/node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "license": "MIT", "peer": true, "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" }, "engines": { "node": ">=10" } }, "node_modules/cli-highlight/node_modules/yargs-parser": { "version": "20.2.9", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "license": "ISC", "peer": true, "engines": { "node": ">=10" } }, "node_modules/cli-spinners": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "license": "MIT", "peer": true, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "license": "ISC", "peer": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" }, "engines": { "node": ">=12" } }, "node_modules/cliui/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/cliui/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/cliui/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/cliui/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/cmake-js": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/cmake-js/-/cmake-js-8.0.0.tgz", "integrity": "sha512-YbUP88RDwCvoQkZhRtGURYm9RIpWdtvZuhT87fKNoLjk8kIFIFeARpKfuZQGdwfH99GZpUmqSfcDrK62X7lTgg==", "license": "MIT", "peer": true, "dependencies": { "debug": "^4.4.3", "fs-extra": "^11.3.3", "node-api-headers": "^1.8.0", "rc": "1.2.8", "semver": "^7.7.3", "tar": "^7.5.6", "url-join": "^4.0.1", "which": "^6.0.0", "yargs": "^17.7.2" }, "bin": { "cmake-js": "bin/cmake-js" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/cmake-js/node_modules/isexe": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", "license": "BlueOak-1.0.0", "peer": true, "engines": { "node": ">=20" } }, "node_modules/cmake-js/node_modules/which": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", "license": "ISC", "peer": true, "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "license": "MIT" }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", "peer": true, "dependencies": { "delayed-stream": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/commander": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", "license": "MIT", "peer": true, "engines": { "node": ">=14" } }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, "node_modules/content-disposition": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", "peer": true, "engines": { "node": ">=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true, "license": "MIT" }, "node_modules/cookie": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/cookie-signature": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", "peer": true, "engines": { "node": ">=6.6.0" } }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "license": "MIT", "peer": true }, "node_modules/croner": { "version": "10.0.1", "resolved": "https://registry.npmjs.org/croner/-/croner-10.0.1.tgz", "integrity": "sha512-ixNtAJndqh173VQ4KodSdJEI6nuioBWI0V1ITNKhZZsO0pEMoDxz539T4FTTbSZ/xIOSuDnzxLVRqBVSvPNE2g==", "funding": [ { "type": "other", "url": "https://paypal.me/hexagonpp" }, { "type": "github", "url": "https://github.com/sponsors/hexagon" } ], "license": "MIT", "peer": true, "engines": { "node": ">=18.0" } }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, "node_modules/css-select": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", "license": "BSD-2-Clause", "peer": true, "dependencies": { "boolbase": "^1.0.0", "css-what": "^6.1.0", "domhandler": "^5.0.2", "domutils": "^3.0.1", "nth-check": "^2.0.1" }, "funding": { "url": "https://github.com/sponsors/fb55" } }, "node_modules/css-what": { "version": "6.2.2", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", "license": "BSD-2-Clause", "peer": true, "engines": { "node": ">= 6" }, "funding": { "url": "https://github.com/sponsors/fb55" } }, "node_modules/cssom": { "version": "0.5.0", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.5.0.tgz", "integrity": "sha512-iKuQcq+NdHqlAcwUY0o/HL69XQrUaQdMjmStJ8JFmUaiiQErlhrmuigkg/CU4E2J0IyUKUrMAgl36TvN67MqTw==", "license": "MIT", "peer": true }, "node_modules/curve25519-js": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/curve25519-js/-/curve25519-js-0.0.4.tgz", "integrity": "sha512-axn2UMEnkhyDUPWOwVKBMVIzSQy2ejH2xRGy1wq81dqRwApXfIzfbE3hIX0ZRFBIihf/KDqK158DLwESu4AK1w==", "license": "MIT", "peer": true }, "node_modules/data-uri-to-buffer": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", "license": "MIT", "peer": true, "engines": { "node": ">= 12" } }, "node_modules/debug": { "version": "4.4.3", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/deep-extend": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "license": "MIT", "peer": true, "engines": { "node": ">=4.0.0" } }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/degenerator": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/degenerator/-/degenerator-5.0.1.tgz", "integrity": "sha512-TllpMR/t0M5sqCXfj85i4XaAzxmS5tVA16dqvdkMwGmzI+dXLXnw3J+3Vdv7VKw+ThlTMboK6i9rnZ6Nntj5CQ==", "license": "MIT", "peer": true, "dependencies": { "ast-types": "^0.13.4", "escodegen": "^2.1.0", "esprima": "^4.0.1" }, "engines": { "node": ">= 14" } }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", "peer": true, "engines": { "node": ">=0.4.0" } }, "node_modules/depd": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/detect-libc": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", "engines": { "node": ">=8" } }, "node_modules/diff": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.3.tgz", "integrity": "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==", "license": "BSD-3-Clause", "peer": true, "engines": { "node": ">=0.3.1" } }, "node_modules/discord-api-types": { "version": "0.38.42", "resolved": "https://registry.npmjs.org/discord-api-types/-/discord-api-types-0.38.42.tgz", "integrity": "sha512-qs1kya7S84r5RR8m9kgttywGrmmoHaRifU1askAoi+wkoSefLpZP6aGXusjNw5b0jD3zOg3LTwUa3Tf2iHIceQ==", "license": "MIT", "peer": true, "workspaces": [ "scripts/actions/documentation" ] }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", "license": "MIT", "peer": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.2", "entities": "^4.2.0" }, "funding": { "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" } }, "node_modules/domelementtype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/fb55" } ], "license": "BSD-2-Clause", "peer": true }, "node_modules/domhandler": { "version": "5.0.3", "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", "license": "BSD-2-Clause", "peer": true, "dependencies": { "domelementtype": "^2.3.0" }, "engines": { "node": ">= 4" }, "funding": { "url": "https://github.com/fb55/domhandler?sponsor=1" } }, "node_modules/domutils": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", "license": "BSD-2-Clause", "peer": true, "dependencies": { "dom-serializer": "^2.0.0", "domelementtype": "^2.3.0", "domhandler": "^5.0.3" }, "funding": { "url": "https://github.com/fb55/domutils?sponsor=1" } }, "node_modules/dotenv": { "version": "17.3.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", "license": "BSD-2-Clause", "peer": true, "engines": { "node": ">=12" }, "funding": { "url": "https://dotenvx.com" } }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", "gopd": "^1.2.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "license": "MIT", "peer": true }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "safe-buffer": "^5.0.1" } }, "node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT", "peer": true }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "license": "MIT", "peer": true }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/end-of-stream": { "version": "1.4.5", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", "license": "MIT", "peer": true, "dependencies": { "once": "^1.4.0" } }, "node_modules/entities": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", "license": "BSD-2-Clause", "peer": true, "engines": { "node": ">=0.12" }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/env-var": { "version": "7.5.0", "resolved": "https://registry.npmjs.org/env-var/-/env-var-7.5.0.tgz", "integrity": "sha512-mKZOzLRN0ETzau2W2QXefbFjo5EF4yWq28OyKb9ICdeNhHJlOE/pHHnz4hdYJ9cNZXcJHo5xN4OT4pzuSHSNvA==", "license": "MIT", "peer": true, "engines": { "node": ">=10" } }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.4" } }, "node_modules/es-errors": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.4" } }, "node_modules/es-module-lexer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-2.0.0.tgz", "integrity": "sha512-5POEcUuZybH7IdmGsD8wlf0AI55wMecM9rVBTI/qEAy2c1kTOm3DjFYjrBdI2K3BaJjJYfYFeRtM0t9ssnRuxw==", "dev": true, "license": "MIT" }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", "peer": true, "dependencies": { "es-errors": "^1.3.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/es-set-tostringtag": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "license": "MIT", "peer": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", "peer": true, "engines": { "node": ">=6" } }, "node_modules/escape-html": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT", "peer": true }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/escodegen": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.1.0.tgz", "integrity": "sha512-2NlIDTwUWJN0mRPQOdtQBzbUHvdGY2P1VXSyU83Q3xKxM7WHX2Ql8dKq782Q9TgQUNOLEzEYu9bzLNj1q88I5w==", "license": "BSD-2-Clause", "peer": true, "dependencies": { "esprima": "^4.0.1", "estraverse": "^5.2.0", "esutils": "^2.0.2" }, "bin": { "escodegen": "bin/escodegen.js", "esgenerate": "bin/esgenerate.js" }, "engines": { "node": ">=6.0" }, "optionalDependencies": { "source-map": "~0.6.1" } }, "node_modules/eslint": { "version": "9.39.4", "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.4.tgz", "integrity": "sha512-XoMjdBOwe/esVgEvLmNsD3IRHkm7fbKIUGvrleloJXUZgDHig2IPWNniv+GwjyJXzuNqVjlr5+4yVUZjycJwfQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", "@eslint/config-array": "^0.21.2", "@eslint/config-helpers": "^0.4.2", "@eslint/core": "^0.17.0", "@eslint/eslintrc": "^3.3.5", "@eslint/js": "9.39.4", "@eslint/plugin-kit": "^0.4.1", "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", "ajv": "^6.14.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", "eslint-scope": "^8.4.0", "eslint-visitor-keys": "^4.2.1", "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^8.0.0", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.5", "natural-compare": "^1.4.0", "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://eslint.org/donate" }, "peerDependencies": { "jiti": "*" }, "peerDependenciesMeta": { "jiti": { "optional": true } } }, "node_modules/eslint-config-prettier": { "version": "10.1.8", "resolved": "https://registry.npmjs.org/eslint-config-prettier/-/eslint-config-prettier-10.1.8.tgz", "integrity": "sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==", "dev": true, "license": "MIT", "bin": { "eslint-config-prettier": "bin/cli.js" }, "funding": { "url": "https://opencollective.com/eslint-config-prettier" }, "peerDependencies": { "eslint": ">=7.0.0" } }, "node_modules/eslint-plugin-tsdoc": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/eslint-plugin-tsdoc/-/eslint-plugin-tsdoc-0.5.2.tgz", "integrity": "sha512-BlvqjWZdBJDIPO/YU3zcPCF23CvjYT3gyu63yo6b609NNV3D1b6zceAREy2xnweuBoDpZcLNuPyAUq9cvx6bbQ==", "dev": true, "license": "MIT", "dependencies": { "@microsoft/tsdoc": "0.16.0", "@microsoft/tsdoc-config": "0.18.1", "@typescript-eslint/utils": "~8.56.0" } }, "node_modules/eslint-scope": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/espree": { "version": "10.4.0", "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "license": "BSD-2-Clause", "peer": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" }, "engines": { "node": ">=4" } }, "node_modules/esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, "engines": { "node": ">=0.10" } }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, "engines": { "node": ">=4.0" } }, "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/estree-walker": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-3.0.3.tgz", "integrity": "sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==", "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" } }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/event-target-shim": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", "peer": true, "engines": { "node": ">=6" } }, "node_modules/eventemitter3": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", "license": "MIT", "peer": true }, "node_modules/expect-type": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/expect-type/-/expect-type-1.3.0.tgz", "integrity": "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.0.0" } }, "node_modules/express": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", "content-disposition": "^1.0.0", "content-type": "^1.0.5", "cookie": "^0.7.1", "cookie-signature": "^1.2.1", "debug": "^4.4.0", "depd": "^2.0.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "finalhandler": "^2.1.0", "fresh": "^2.0.0", "http-errors": "^2.0.0", "merge-descriptors": "^2.0.0", "mime-types": "^3.0.0", "on-finished": "^2.4.1", "once": "^1.4.0", "parseurl": "^1.3.3", "proxy-addr": "^2.0.7", "qs": "^6.14.0", "range-parser": "^1.2.1", "router": "^2.2.0", "send": "^1.1.0", "serve-static": "^2.2.0", "statuses": "^2.0.1", "type-is": "^2.0.1", "vary": "^1.1.2" }, "engines": { "node": ">= 18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "license": "MIT", "peer": true }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", "integrity": "sha512-GDhU9ntwuKyGXdZBUgTIe+vXnWj0fppUEtMDL0+idd5Sta8TGpHssn/eusA9mrPr9qNDym6SxAYZjNvCn/9RBg==", "license": "BSD-2-Clause", "peer": true, "dependencies": { "debug": "^4.1.1", "get-stream": "^5.1.0", "yauzl": "^2.10.0" }, "bin": { "extract-zip": "cli.js" }, "engines": { "node": ">= 10.17.0" }, "optionalDependencies": { "@types/yauzl": "^2.9.1" } }, "node_modules/fast-content-type-parse": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/fastify" }, { "type": "opencollective", "url": "https://opencollective.com/fastify" } ], "license": "MIT", "peer": true }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", "license": "MIT" }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, "node_modules/fast-uri": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/fastify" }, { "type": "opencollective", "url": "https://opencollective.com/fastify" } ], "license": "BSD-3-Clause" }, "node_modules/fast-xml-builder": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/fast-xml-builder/-/fast-xml-builder-1.1.2.tgz", "integrity": "sha512-NJAmiuVaJEjVa7TjLZKlYd7RqmzOC91EtPFXHvlTcqBVo50Qh7XV5IwvXi1c7NRz2Q/majGX9YLcwJtWgHjtkA==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } ], "license": "MIT", "peer": true, "dependencies": { "path-expression-matcher": "^1.1.3" } }, "node_modules/fast-xml-parser": { "version": "5.4.1", "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.4.1.tgz", "integrity": "sha512-BQ30U1mKkvXQXXkAGcuyUA/GA26oEB7NzOtsxCDtyu62sjGw5QraKFhx2Em3WQNjPw9PG6MQ9yuIIgkSDfGu5A==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } ], "license": "MIT", "peer": true, "dependencies": { "fast-xml-builder": "^1.0.0", "strnum": "^2.1.2" }, "bin": { "fxparser": "src/cli/cli.js" } }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "license": "MIT", "peer": true, "dependencies": { "pend": "~1.2.0" } }, "node_modules/fdir": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", "dev": true, "license": "MIT", "engines": { "node": ">=12.0.0" }, "peerDependencies": { "picomatch": "^3 || ^4" }, "peerDependenciesMeta": { "picomatch": { "optional": true } } }, "node_modules/fetch-blob": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/jimmywarting" }, { "type": "paypal", "url": "https://paypal.me/jimmywarting" } ], "license": "MIT", "peer": true, "dependencies": { "node-domexception": "^1.0.0", "web-streams-polyfill": "^3.0.3" }, "engines": { "node": "^12.20 || >= 14.13" } }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, "license": "MIT", "dependencies": { "flat-cache": "^4.0.0" }, "engines": { "node": ">=16.0.0" } }, "node_modules/file-type": { "version": "21.3.1", "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.1.tgz", "integrity": "sha512-SrzXX46I/zsRDjTb82eucsGg0ODq2NpGDp4HcsFKApPy8P8vACjpJRDoGGMfEzhFC0ry61ajd7f72J3603anBA==", "license": "MIT", "peer": true, "dependencies": { "@tokenizer/inflate": "^0.4.1", "strtok3": "^10.3.4", "token-types": "^6.1.1", "uint8array-extras": "^1.4.0" }, "engines": { "node": ">=20" }, "funding": { "url": "https://github.com/sindresorhus/file-type?sponsor=1" } }, "node_modules/filename-reserved-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/filename-reserved-regex/-/filename-reserved-regex-3.0.0.tgz", "integrity": "sha512-hn4cQfU6GOT/7cFHXBqeBg2TbrMBgdD0kcjLhvSQYYwm3s4B6cjvBfb7nBALJLAXqmU5xajSa7X2NnUud/VCdw==", "license": "MIT", "peer": true, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/filenamify": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/filenamify/-/filenamify-6.0.0.tgz", "integrity": "sha512-vqIlNogKeyD3yzrm0yhRMQg8hOVwYcYRfjEoODd49iCprMn4HL85gK3HcykQE53EPIpX3HcAbGA5ELQv216dAQ==", "license": "MIT", "peer": true, "dependencies": { "filename-reserved-regex": "^3.0.0" }, "engines": { "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", "peer": true, "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "on-finished": "^2.4.1", "parseurl": "^1.3.3", "statuses": "^2.0.1" }, "engines": { "node": ">= 18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.4" }, "engines": { "node": ">=16" } }, "node_modules/flatted": { "version": "3.4.1", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.4.1.tgz", "integrity": "sha512-IxfVbRFVlV8V/yRaGzk0UVIcsKKHMSfYw66T/u4nTwlWteQePsxe//LjudR1AMX4tZW3WFCh3Zqa/sjlqpbURQ==", "dev": true, "license": "ISC" }, "node_modules/follow-redirects": { "version": "1.15.11", "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.11.tgz", "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", "funding": [ { "type": "individual", "url": "https://github.com/sponsors/RubenVerborgh" } ], "license": "MIT", "peer": true, "engines": { "node": ">=4.0" }, "peerDependenciesMeta": { "debug": { "optional": true } } }, "node_modules/foreground-child": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", "peer": true, "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/foreground-child/node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "license": "ISC", "peer": true, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, "node_modules/form-data/node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/form-data/node_modules/mime-types": { "version": "2.1.35", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", "peer": true, "dependencies": { "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/formdata-polyfill": { "version": "4.0.10", "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", "license": "MIT", "peer": true, "dependencies": { "fetch-blob": "^3.1.2" }, "engines": { "node": ">=12.20.0" } }, "node_modules/forwarded": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/fresh": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/fs-extra": { "version": "11.3.4", "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.4.tgz", "integrity": "sha512-CTXd6rk/M3/ULNQj8FBqBWHYBVYybQ3VPBw0xGKFe3tuH7ytT6ACnvzpIQ3UZtB8yvUKC2cXn1a+x+5EVQLovA==", "license": "MIT", "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" }, "engines": { "node": ">=14.14" } }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/gaxios": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-7.1.3.tgz", "integrity": "sha512-YGGyuEdVIjqxkxVH1pUTMY/XtmmsApXrCVv5EU25iX6inEPbV+VakJfLealkBtJN69AQmh1eGOdCl9Sm1UP6XQ==", "license": "Apache-2.0", "peer": true, "dependencies": { "extend": "^3.0.2", "https-proxy-agent": "^7.0.1", "node-fetch": "^3.3.2", "rimraf": "^5.0.1" }, "engines": { "node": ">=18" } }, "node_modules/gaxios/node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 14" } }, "node_modules/gaxios/node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" }, "engines": { "node": ">= 14" } }, "node_modules/gcp-metadata": { "version": "8.1.2", "resolved": "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-8.1.2.tgz", "integrity": "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg==", "license": "Apache-2.0", "peer": true, "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" }, "engines": { "node": ">=18" } }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", "peer": true, "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-east-asian-width": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.5.0.tgz", "integrity": "sha512-CQ+bEO+Tva/qlmw24dCejulK5pMzVnUOFOijVogd3KQs07HnRIgp8TGipvCCRT06xeYEbpbgwaCxglFyiuIcmA==", "license": "MIT", "peer": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/get-intrinsic": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", "hasown": "^2.0.2", "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", "peer": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" }, "engines": { "node": ">= 0.4" } }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", "license": "MIT", "peer": true, "dependencies": { "pump": "^3.0.0" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/get-uri": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/get-uri/-/get-uri-6.0.5.tgz", "integrity": "sha512-b1O07XYq8eRuVzBNgJLstU6FYc1tS6wnMtF1I1D9lE8LxZSOGZ7LhxN54yPP6mGw5f2CkXY2BQUL9Fx41qvcIg==", "license": "MIT", "peer": true, "dependencies": { "basic-ftp": "^5.0.2", "data-uri-to-buffer": "^6.0.2", "debug": "^4.3.4" }, "engines": { "node": ">= 14" } }, "node_modules/get-uri/node_modules/data-uri-to-buffer": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-6.0.2.tgz", "integrity": "sha512-7hvf7/GW8e86rW0ptuwS3OcBGDjIi6SZva7hCyWC0yYry2cOPmLIjXAUHI6DK2HsnwJd9ifmt57i8eV2n4YNpw==", "license": "MIT", "peer": true, "engines": { "node": ">= 14" } }, "node_modules/glob": { "version": "13.0.6", "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", "license": "BlueOak-1.0.0", "peer": true, "dependencies": { "minimatch": "^10.2.2", "minipass": "^7.1.3", "path-scurry": "^2.0.2" }, "engines": { "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, "engines": { "node": ">=10.13.0" } }, "node_modules/glob/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "license": "MIT", "peer": true, "engines": { "node": "18 || 20 || >=22" } }, "node_modules/glob/node_modules/brace-expansion": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "license": "MIT", "peer": true, "dependencies": { "balanced-match": "^4.0.2" }, "engines": { "node": "18 || 20 || >=22" } }, "node_modules/glob/node_modules/minimatch": { "version": "10.2.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "license": "BlueOak-1.0.0", "peer": true, "dependencies": { "brace-expansion": "^5.0.2" }, "engines": { "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/globals": { "version": "14.0.0", "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, "license": "MIT", "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/google-auth-library": { "version": "10.6.1", "resolved": "https://registry.npmjs.org/google-auth-library/-/google-auth-library-10.6.1.tgz", "integrity": "sha512-5awwuLrzNol+pFDmKJd0dKtZ0fPLAtoA5p7YO4ODsDu6ONJUVqbYwvv8y2ZBO5MBNp9TJXigB19710kYpBPdtA==", "license": "Apache-2.0", "peer": true, "dependencies": { "base64-js": "^1.3.0", "ecdsa-sig-formatter": "^1.0.11", "gaxios": "7.1.3", "gcp-metadata": "8.1.2", "google-logging-utils": "1.1.3", "jws": "^4.0.0" }, "engines": { "node": ">=18" } }, "node_modules/google-logging-utils": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-1.1.3.tgz", "integrity": "sha512-eAmLkjDjAFCVXg7A1unxHsLf961m6y17QFqXqAXGj/gVkKFrEICfStRfwUlGNfeCEjNRa32JEWOUTlYXPyyKvA==", "license": "Apache-2.0", "peer": true, "engines": { "node": ">=14" } }, "node_modules/gopd": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", "license": "ISC", "peer": true }, "node_modules/grammy": { "version": "1.41.1", "resolved": "https://registry.npmjs.org/grammy/-/grammy-1.41.1.tgz", "integrity": "sha512-wcHAQ1e7svL3fJMpDchcQVcWUmywhuepOOjHUHmMmWAwUJEIyK5ea5sbSjZd+Gy1aMpZeP8VYJa+4tP+j1YptQ==", "license": "MIT", "peer": true, "dependencies": { "@grammyjs/types": "3.25.0", "abort-controller": "^3.0.0", "debug": "^4.4.3", "node-fetch": "^2.7.0" }, "engines": { "node": "^12.20.0 || >=14.13.1" } }, "node_modules/grammy/node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", "license": "MIT", "peer": true, "dependencies": { "whatwg-url": "^5.0.0" }, "engines": { "node": "4.x || >=6.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "peerDependenciesMeta": { "encoding": { "optional": true } } }, "node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/has-symbols": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/has-tostringtag": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "license": "MIT", "peer": true, "dependencies": { "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/hashery": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/hashery/-/hashery-1.5.0.tgz", "integrity": "sha512-nhQ6ExaOIqti2FDWoEMWARUqIKyjr2VcZzXShrI+A3zpeiuPWzx6iPftt44LhP74E5sW36B75N6VHbvRtpvO6Q==", "license": "MIT", "peer": true, "dependencies": { "hookified": "^1.14.0" }, "engines": { "node": ">=20" } }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/highlight.js": { "version": "10.7.3", "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-10.7.3.tgz", "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", "license": "BSD-3-Clause", "peer": true, "engines": { "node": "*" } }, "node_modules/hono": { "version": "4.12.7", "resolved": "https://registry.npmjs.org/hono/-/hono-4.12.7.tgz", "integrity": "sha512-jq9l1DM0zVIvsm3lv9Nw9nlJnMNPOcAtsbsgiUhWcFzPE99Gvo6yRTlszSLLYacMeQ6quHD6hMfId8crVHvexw==", "license": "MIT", "peer": true, "engines": { "node": ">=16.9.0" } }, "node_modules/hookified": { "version": "1.15.1", "resolved": "https://registry.npmjs.org/hookified/-/hookified-1.15.1.tgz", "integrity": "sha512-MvG/clsADq1GPM2KGo2nyfaWVyn9naPiXrqIe4jYjXNZQt238kWyOGrsyc/DmRAQ+Re6yeo6yX/yoNCG5KAEVg==", "license": "MIT", "peer": true }, "node_modules/hosted-git-info": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-9.0.2.tgz", "integrity": "sha512-M422h7o/BR3rmCQ8UHi7cyyMqKltdP9Uo+J2fXK+RSAY+wTcKOIRyhTuKv4qn+DJf3g+PL890AzId5KZpX+CBg==", "license": "ISC", "peer": true, "dependencies": { "lru-cache": "^11.1.0" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true, "license": "MIT" }, "node_modules/htmlparser2": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", "funding": [ "https://github.com/fb55/htmlparser2?sponsor=1", { "type": "github", "url": "https://github.com/sponsors/fb55" } ], "license": "MIT", "peer": true, "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.2.2", "entities": "^7.0.1" } }, "node_modules/htmlparser2/node_modules/entities": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", "license": "BSD-2-Clause", "peer": true, "engines": { "node": ">=0.12" }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", "peer": true, "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", "setprototypeof": "~1.2.0", "statuses": "~2.0.2", "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/http-proxy-agent": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "license": "MIT", "peer": true, "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" }, "engines": { "node": ">= 14" } }, "node_modules/http-proxy-agent/node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 14" } }, "node_modules/https-proxy-agent": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-8.0.0.tgz", "integrity": "sha512-YYeW+iCnAS3xhvj2dvVoWgsbca3RfQy/IlaNHHOtDmU0jMqPI9euIq3Y9BJETdxk16h9NHHCKqp/KB9nIMStCQ==", "license": "MIT", "peer": true, "dependencies": { "agent-base": "8.0.0", "debug": "^4.3.4" }, "engines": { "node": ">= 14" } }, "node_modules/iconv-lite": { "version": "0.7.2", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.2.tgz", "integrity": "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw==", "license": "MIT", "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/ieee754": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ], "license": "BSD-3-Clause", "peer": true }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/immediate": { "version": "3.0.6", "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz", "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "license": "MIT", "peer": true }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "license": "ISC", "peer": true }, "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", "license": "ISC", "peer": true }, "node_modules/ip-address": { "version": "10.1.0", "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "license": "MIT", "peer": true, "engines": { "node": ">= 12" } }, "node_modules/ipaddr.js": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-2.3.0.tgz", "integrity": "sha512-Zv/pA+ciVFbCSBBjGfaKUya/CcGmUHzTydLMaTwrUUEM2DIEO3iZvueGxmacvmN50fGpGVKeTXpb2LcYQxeVdg==", "license": "MIT", "peer": true, "engines": { "node": ">= 10" } }, "node_modules/ipull": { "version": "3.9.5", "resolved": "https://registry.npmjs.org/ipull/-/ipull-3.9.5.tgz", "integrity": "sha512-5w/yZB5lXmTfsvNawmvkCjYo4SJNuKQz/av8TC1UiOyfOHyaM+DReqbpU2XpWYfmY+NIUbRRH8PUAWsxaS+IfA==", "license": "MIT", "peer": true, "dependencies": { "@tinyhttp/content-disposition": "^2.2.0", "async-retry": "^1.3.3", "chalk": "^5.3.0", "ci-info": "^4.0.0", "cli-spinners": "^2.9.2", "commander": "^10.0.0", "eventemitter3": "^5.0.1", "filenamify": "^6.0.0", "fs-extra": "^11.1.1", "is-unicode-supported": "^2.0.0", "lifecycle-utils": "^2.0.1", "lodash.debounce": "^4.0.8", "lowdb": "^7.0.1", "pretty-bytes": "^6.1.0", "pretty-ms": "^8.0.0", "sleep-promise": "^9.1.0", "slice-ansi": "^7.1.0", "stdout-update": "^4.0.1", "strip-ansi": "^7.1.0" }, "bin": { "ipull": "dist/cli/cli.js" }, "engines": { "node": ">=18.0.0" }, "funding": { "type": "github", "url": "https://github.com/ido-pluto/ipull?sponsor=1" }, "optionalDependencies": { "@reflink/reflink": "^0.1.16" } }, "node_modules/ipull/node_modules/ansi-styles": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", "peer": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/ipull/node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "peer": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/ipull/node_modules/lifecycle-utils": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/lifecycle-utils/-/lifecycle-utils-2.1.0.tgz", "integrity": "sha512-AnrXnE2/OF9PHCyFg0RSqsnQTzV991XaZA/buhFDoc58xU7rhSCDgCz/09Lqpsn4MpoPHt7TRAXV1kWZypFVsA==", "license": "MIT", "peer": true }, "node_modules/ipull/node_modules/parse-ms": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-3.0.0.tgz", "integrity": "sha512-Tpb8Z7r7XbbtBTrM9UhpkzzaMrqA2VXMT3YChzYltwV3P3pM6t8wl7TvpMnSTosz1aQAdVib7kdoys7vYOPerw==", "license": "MIT", "peer": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ipull/node_modules/pretty-ms": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-8.0.0.tgz", "integrity": "sha512-ASJqOugUF1bbzI35STMBUpZqdfYKlJugy6JBziGi2EE+AL5JPJGSzvpeVXojxrr0ViUYoToUjb5kjSEGf7Y83Q==", "license": "MIT", "peer": true, "dependencies": { "parse-ms": "^3.0.0" }, "engines": { "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ipull/node_modules/slice-ansi": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-7.1.2.tgz", "integrity": "sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==", "license": "MIT", "peer": true, "dependencies": { "ansi-styles": "^6.2.1", "is-fullwidth-code-point": "^5.0.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "license": "MIT", "dependencies": { "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/is-electron": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/is-electron/-/is-electron-2.2.2.tgz", "integrity": "sha512-FO/Rhvz5tuw4MCWkpMzHFKWD2LsfHzIb7i6MdPYZ/KW7AlxawyLkqdy+jPZP1WubqEADE3O4FUENlJHDfQASRg==", "license": "MIT", "peer": true }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/is-fullwidth-code-point": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-5.1.0.tgz", "integrity": "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==", "license": "MIT", "peer": true, "dependencies": { "get-east-asian-width": "^1.3.1" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, "node_modules/is-interactive": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", "integrity": "sha512-qP1vozQRI+BMOPcjFzrjXuQvdak2pHNUMZoeG2eRbiSqyvbEf/wQtEOTOX1guk6E3t36RkaqiSt8A/6YElNxLQ==", "license": "MIT", "peer": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-promise": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", "license": "MIT", "peer": true }, "node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "license": "MIT", "peer": true, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/is-unicode-supported": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-2.1.0.tgz", "integrity": "sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==", "license": "MIT", "peer": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "license": "MIT", "peer": true }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "license": "ISC" }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" } }, "node_modules/istanbul-reports": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "license": "BlueOak-1.0.0", "peer": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, "funding": { "url": "https://github.com/sponsors/isaacs" }, "optionalDependencies": { "@pkgjs/parseargs": "^0.11.0" } }, "node_modules/jiti": { "version": "2.6.1", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.6.1.tgz", "integrity": "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==", "license": "MIT", "peer": true, "bin": { "jiti": "lib/jiti-cli.mjs" } }, "node_modules/jju": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz", "integrity": "sha512-8wb9Yw966OSxApiCt0K3yNJL8pnNeIv+OEq2YMidz4FKP6nonSRoOXc80iXY4JaN2FC11B9qsNmDsm+ZOfMROA==", "dev": true, "license": "MIT" }, "node_modules/js-tokens": { "version": "10.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-10.0.0.tgz", "integrity": "sha512-lM/UBzQmfJRo9ABXbPWemivdCW8V2G8FHaHdypQaIy523snUjog0W71ayWXTjiR+ixeMyVHN2XcpnTd/liPg/Q==", "dev": true, "license": "MIT" }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "node_modules/json-bigint": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz", "integrity": "sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ==", "license": "MIT", "peer": true, "dependencies": { "bignumber.js": "^9.0.0" } }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-schema-to-ts": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", "license": "MIT", "peer": true, "dependencies": { "@babel/runtime": "^7.18.3", "ts-algebra": "^2.0.0" }, "engines": { "node": ">=16" } }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, "node_modules/json-with-bigint": { "version": "3.5.7", "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.7.tgz", "integrity": "sha512-7ei3MdAI5+fJPVnKlW77TKNKwQ5ppSzWvhPuSuINT/GYW9ZOC1eRKOuhV9yHG5aEsUPj9BBx5JIekkmoLHxZOw==", "license": "MIT", "peer": true }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "license": "MIT", "peer": true, "bin": { "json5": "lib/cli.js" }, "engines": { "node": ">=6" } }, "node_modules/jsonfile": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "license": "MIT", "peer": true, "dependencies": { "universalify": "^2.0.0" }, "optionalDependencies": { "graceful-fs": "^4.1.6" } }, "node_modules/jsonwebtoken": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", "license": "MIT", "peer": true, "dependencies": { "jws": "^4.0.1", "lodash.includes": "^4.3.0", "lodash.isboolean": "^3.0.3", "lodash.isinteger": "^4.0.4", "lodash.isnumber": "^3.0.3", "lodash.isplainobject": "^4.0.6", "lodash.isstring": "^4.0.1", "lodash.once": "^4.0.0", "ms": "^2.1.1", "semver": "^7.5.4" }, "engines": { "node": ">=12", "npm": ">=6" } }, "node_modules/jszip": { "version": "3.10.1", "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz", "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==", "license": "(MIT OR GPL-3.0-or-later)", "peer": true, "dependencies": { "lie": "~3.3.0", "pako": "~1.0.2", "readable-stream": "~2.3.6", "setimmediate": "^1.0.5" } }, "node_modules/jwa": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "license": "MIT", "peer": true, "dependencies": { "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "node_modules/jws": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "license": "MIT", "peer": true, "dependencies": { "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, "node_modules/koffi": { "version": "2.15.2", "resolved": "https://registry.npmjs.org/koffi/-/koffi-2.15.2.tgz", "integrity": "sha512-r9tjJLVRSOhCRWdVyQlF3/Ugzeg13jlzS4czS82MAgLff4W+BcYOW7g8Y62t9O5JYjYOLAjAovAZDNlDfZNu+g==", "hasInstallScript": true, "license": "MIT", "optional": true, "peer": true, "funding": { "url": "https://liberapay.com/Koromix" } }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/libsignal": { "name": "@whiskeysockets/libsignal-node", "version": "2.0.1", "resolved": "git+ssh://git@github.com/whiskeysockets/libsignal-node.git#1c30d7d7e76a3b0aa120b04dc6a26f5a12dccf67", "license": "GPL-3.0", "peer": true, "dependencies": { "curve25519-js": "^0.0.4", "protobufjs": "6.8.8" } }, "node_modules/libsignal/node_modules/@types/node": { "version": "10.17.60", "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", "license": "MIT", "peer": true }, "node_modules/libsignal/node_modules/long": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==", "license": "Apache-2.0", "peer": true }, "node_modules/libsignal/node_modules/protobufjs": { "version": "6.8.8", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.8.8.tgz", "integrity": "sha512-AAmHtD5pXgZfi7GMpllpO3q1Xw1OYldr+dMUlAnffGTAhqkg72WdmSY71uKBF/JuyiKs8psYbtKrhi0ASCD8qw==", "hasInstallScript": true, "license": "BSD-3-Clause", "peer": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/long": "^4.0.0", "@types/node": "^10.1.0", "long": "^4.0.0" }, "bin": { "pbjs": "bin/pbjs", "pbts": "bin/pbts" } }, "node_modules/lie": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz", "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==", "license": "MIT", "peer": true, "dependencies": { "immediate": "~3.0.5" } }, "node_modules/lifecycle-utils": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/lifecycle-utils/-/lifecycle-utils-3.1.1.tgz", "integrity": "sha512-gNd3OvhFNjHykJE3uGntz7UuPzWlK9phrIdXxU9Adis0+ExkwnZibfxCJWiWWZ+a6VbKiZrb+9D9hCQWd4vjTg==", "license": "MIT", "peer": true }, "node_modules/lightningcss": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", "dev": true, "license": "MPL-2.0", "dependencies": { "detect-libc": "^2.0.3" }, "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" }, "optionalDependencies": { "lightningcss-android-arm64": "1.32.0", "lightningcss-darwin-arm64": "1.32.0", "lightningcss-darwin-x64": "1.32.0", "lightningcss-freebsd-x64": "1.32.0", "lightningcss-linux-arm-gnueabihf": "1.32.0", "lightningcss-linux-arm64-gnu": "1.32.0", "lightningcss-linux-arm64-musl": "1.32.0", "lightningcss-linux-x64-gnu": "1.32.0", "lightningcss-linux-x64-musl": "1.32.0", "lightningcss-win32-arm64-msvc": "1.32.0", "lightningcss-win32-x64-msvc": "1.32.0" } }, "node_modules/lightningcss-android-arm64": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "android" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-arm64": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-darwin-x64": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "darwin" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-freebsd-x64": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "freebsd" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm-gnueabihf": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", "cpu": [ "arm" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm64-gnu": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-arm64-musl": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-x64-gnu": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-linux-x64-musl": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "linux" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-win32-arm64-msvc": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", "cpu": [ "arm64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/lightningcss-win32-x64-msvc": { "version": "1.32.0", "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", "cpu": [ "x64" ], "dev": true, "license": "MPL-2.0", "optional": true, "os": [ "win32" ], "engines": { "node": ">= 12.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/parcel" } }, "node_modules/linkedom": { "version": "0.18.12", "resolved": "https://registry.npmjs.org/linkedom/-/linkedom-0.18.12.tgz", "integrity": "sha512-jalJsOwIKuQJSeTvsgzPe9iJzyfVaEJiEXl+25EkKevsULHvMJzpNqwvj1jOESWdmgKDiXObyjOYwlUqG7wo1Q==", "license": "ISC", "peer": true, "dependencies": { "css-select": "^5.1.0", "cssom": "^0.5.0", "html-escaper": "^3.0.3", "htmlparser2": "^10.0.0", "uhyphen": "^0.2.0" }, "engines": { "node": ">=16" }, "peerDependencies": { "canvas": ">= 2" }, "peerDependenciesMeta": { "canvas": { "optional": true } } }, "node_modules/linkedom/node_modules/html-escaper": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-3.0.3.tgz", "integrity": "sha512-RuMffC89BOWQoY0WKGpIhn5gX3iI54O6nRA0yC124NYVtzjmFWBIiFd8M0x+ZdX0P9R4lADg1mgP8C7PxGOWuQ==", "license": "MIT", "peer": true }, "node_modules/linkify-it": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "license": "MIT", "peer": true, "dependencies": { "uc.micro": "^2.0.0" } }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", "integrity": "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==", "license": "MIT", "peer": true }, "node_modules/lodash.identity": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/lodash.identity/-/lodash.identity-3.0.0.tgz", "integrity": "sha512-AupTIzdLQxJS5wIYUQlgGyk2XRTfGXA+MCghDHqZk0pzUNYvd3EESS6dkChNauNYVIutcb0dfHw1ri9Q1yPV8Q==", "license": "MIT", "peer": true }, "node_modules/lodash.includes": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", "license": "MIT", "peer": true }, "node_modules/lodash.isboolean": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", "license": "MIT", "peer": true }, "node_modules/lodash.isinteger": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", "license": "MIT", "peer": true }, "node_modules/lodash.isnumber": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", "license": "MIT", "peer": true }, "node_modules/lodash.isplainobject": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", "license": "MIT", "peer": true }, "node_modules/lodash.isstring": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", "license": "MIT", "peer": true }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "license": "MIT" }, "node_modules/lodash.once": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", "license": "MIT", "peer": true }, "node_modules/lodash.pickby": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q==", "license": "MIT", "peer": true }, "node_modules/log-symbols": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-7.0.1.tgz", "integrity": "sha512-ja1E3yCr9i/0hmBVaM0bfwDjnGy8I/s6PP4DFp+yP+a+mrHO4Rm7DtmnqROTUkHIkqffC84YY7AeqX6oFk0WFg==", "license": "MIT", "peer": true, "dependencies": { "is-unicode-supported": "^2.0.0", "yoctocolors": "^2.1.1" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/long": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", "license": "Apache-2.0", "peer": true }, "node_modules/lowdb": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/lowdb/-/lowdb-7.0.1.tgz", "integrity": "sha512-neJAj8GwF0e8EpycYIDFqEPcx9Qz4GUho20jWFR7YiFeXzF1YMLdxB36PypcTSPMA+4+LvgyMacYhlr18Zlymw==", "license": "MIT", "peer": true, "dependencies": { "steno": "^4.0.2" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/typicode" } }, "node_modules/lru-cache": { "version": "11.2.6", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.6.tgz", "integrity": "sha512-ESL2CrkS/2wTPfuend7Zhkzo2u0daGJ/A2VucJOgQ/C48S/zB8MMeMHSGKYpXhIjbPxfuezITkaBH1wqv00DDQ==", "license": "BlueOak-1.0.0", "peer": true, "engines": { "node": "20 || >=22" } }, "node_modules/magic-string": { "version": "0.30.21", "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.5" } }, "node_modules/magicast": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/magicast/-/magicast-0.5.2.tgz", "integrity": "sha512-E3ZJh4J3S9KfwdjZhe2afj6R9lGIN5Pher1pF39UGrXRqq/VDaGVIGN13BjHd2u8B61hArAGOnso7nBOouW3TQ==", "dev": true, "license": "MIT", "dependencies": { "@babel/parser": "^7.29.0", "@babel/types": "^7.29.0", "source-map-js": "^1.2.1" } }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "license": "MIT", "dependencies": { "semver": "^7.5.3" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/markdown-it": { "version": "14.1.1", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.1.tgz", "integrity": "sha512-BuU2qnTti9YKgK5N+IeMubp14ZUKUUw7yeJbkjtosvHiP0AZ5c8IAgEMk79D0eC8F23r4Ac/q8cAIFdm2FtyoA==", "license": "MIT", "peer": true, "dependencies": { "argparse": "^2.0.1", "entities": "^4.4.0", "linkify-it": "^5.0.0", "mdurl": "^2.0.0", "punycode.js": "^2.3.1", "uc.micro": "^2.1.0" }, "bin": { "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/marked": { "version": "15.0.12", "resolved": "https://registry.npmjs.org/marked/-/marked-15.0.12.tgz", "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", "license": "MIT", "peer": true, "bin": { "marked": "bin/marked.js" }, "engines": { "node": ">= 18" } }, "node_modules/math-intrinsics": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.4" } }, "node_modules/mdurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", "license": "MIT", "peer": true }, "node_modules/media-typer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/merge-descriptors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", "peer": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/mime-db": { "version": "1.54.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", "peer": true, "dependencies": { "mime-db": "^1.54.0" }, "engines": { "node": ">=18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/mimic-function": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/mimic-function/-/mimic-function-5.0.1.tgz", "integrity": "sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==", "license": "MIT", "peer": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/minimist": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", "license": "MIT", "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/minipass": { "version": "7.1.3", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", "license": "BlueOak-1.0.0", "peer": true, "engines": { "node": ">=16 || 14 >=14.17" } }, "node_modules/minizlib": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "license": "MIT", "peer": true, "dependencies": { "minipass": "^7.1.2" }, "engines": { "node": ">= 18" } }, "node_modules/ms": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "license": "MIT" }, "node_modules/music-metadata": { "version": "11.12.2", "resolved": "https://registry.npmjs.org/music-metadata/-/music-metadata-11.12.2.tgz", "integrity": "sha512-LsJHPigbRdAD49lcLAPtg5bjsIkc79mEbi9yV2NsWb9cA5jYG+4qLG2Lt3cTXHhCSEh5rFbed5AY8RIT0SXJ3A==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/Borewit" }, { "type": "buymeacoffee", "url": "https://buymeacoffee.com/borewit" } ], "license": "MIT", "peer": true, "dependencies": { "@borewit/text-codec": "^0.2.2", "@tokenizer/token": "^0.3.0", "content-type": "^1.0.5", "debug": "^4.4.3", "file-type": "^21.3.1", "media-typer": "^1.1.0", "strtok3": "^10.3.4", "token-types": "^6.1.2", "uint8array-extras": "^1.5.0", "win-guid": "^0.2.1" }, "engines": { "node": ">=18" } }, "node_modules/mz": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/mz/-/mz-2.7.0.tgz", "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", "license": "MIT", "peer": true, "dependencies": { "any-promise": "^1.0.0", "object-assign": "^4.0.1", "thenify-all": "^1.0.0" } }, "node_modules/nanoid": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.6.tgz", "integrity": "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "peer": true, "bin": { "nanoid": "bin/nanoid.js" }, "engines": { "node": "^18 || >=20" } }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/negotiator": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/netmask": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/netmask/-/netmask-2.0.2.tgz", "integrity": "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.4.0" } }, "node_modules/node-addon-api": { "version": "8.6.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.6.0.tgz", "integrity": "sha512-gBVjCaqDlRUk0EwoPNKzIr9KkS9041G/q31IBShPs1Xz6UTA+EXdZADbzqAJQrpDRq71CIMnOP5VMut3SL0z5Q==", "license": "MIT", "peer": true, "engines": { "node": "^18 || ^20 || >= 21" } }, "node_modules/node-api-headers": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/node-api-headers/-/node-api-headers-1.8.0.tgz", "integrity": "sha512-jfnmiKWjRAGbdD1yQS28bknFM1tbHC1oucyuMPjmkEs+kpiu76aRs40WlTmBmyEgzDM76ge1DQ7XJ3R5deiVjQ==", "license": "MIT", "peer": true }, "node_modules/node-domexception": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", "deprecated": "Use your platform's native DOMException instead", "funding": [ { "type": "github", "url": "https://github.com/sponsors/jimmywarting" }, { "type": "github", "url": "https://paypal.me/jimmywarting" } ], "license": "MIT", "peer": true, "engines": { "node": ">=10.5.0" } }, "node_modules/node-edge-tts": { "version": "1.2.10", "resolved": "https://registry.npmjs.org/node-edge-tts/-/node-edge-tts-1.2.10.tgz", "integrity": "sha512-bV2i4XU54D45+US0Zm1HcJRkifuB3W438dWyuJEHLQdKxnuqlI1kim2MOvR6Q3XUQZvfF9PoDyR1Rt7aeXhPdQ==", "license": "MIT", "peer": true, "dependencies": { "https-proxy-agent": "^7.0.1", "ws": "^8.13.0", "yargs": "^17.7.2" }, "bin": { "node-edge-tts": "bin.js" } }, "node_modules/node-edge-tts/node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 14" } }, "node_modules/node-edge-tts/node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" }, "engines": { "node": ">= 14" } }, "node_modules/node-fetch": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", "license": "MIT", "peer": true, "dependencies": { "data-uri-to-buffer": "^4.0.0", "fetch-blob": "^3.1.4", "formdata-polyfill": "^4.0.10" }, "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/node-fetch" } }, "node_modules/node-llama-cpp": { "version": "3.16.2", "resolved": "https://registry.npmjs.org/node-llama-cpp/-/node-llama-cpp-3.16.2.tgz", "integrity": "sha512-ovhuTaXSWfcoyfI8ljWxO2Rg63mNxqQQAbDGkXRhlgsL7UjPqm2Nsy1bTNa0ZaQRg3vezG4agnCJTImrICY/0A==", "hasInstallScript": true, "license": "MIT", "peer": true, "dependencies": { "@huggingface/jinja": "^0.5.5", "async-retry": "^1.3.3", "bytes": "^3.1.2", "chalk": "^5.6.2", "chmodrp": "^1.0.2", "cmake-js": "^8.0.0", "cross-spawn": "^7.0.6", "env-var": "^7.5.0", "filenamify": "^6.0.0", "fs-extra": "^11.3.0", "ignore": "^7.0.4", "ipull": "^3.9.5", "is-unicode-supported": "^2.1.0", "lifecycle-utils": "^3.1.1", "log-symbols": "^7.0.1", "nanoid": "^5.1.6", "node-addon-api": "^8.5.0", "octokit": "^5.0.5", "ora": "^9.3.0", "pretty-ms": "^9.3.0", "proper-lockfile": "^4.1.2", "semver": "^7.7.1", "simple-git": "^3.31.1", "slice-ansi": "^8.0.0", "stdout-update": "^4.0.1", "strip-ansi": "^7.1.2", "validate-npm-package-name": "^7.0.2", "which": "^6.0.1", "yargs": "^17.7.2" }, "bin": { "nlc": "dist/cli/cli.js", "node-llama-cpp": "dist/cli/cli.js" }, "engines": { "node": ">=20.0.0" }, "funding": { "type": "github", "url": "https://github.com/sponsors/giladgd" }, "optionalDependencies": { "@node-llama-cpp/linux-arm64": "3.16.2", "@node-llama-cpp/linux-armv7l": "3.16.2", "@node-llama-cpp/linux-x64": "3.16.2", "@node-llama-cpp/linux-x64-cuda": "3.16.2", "@node-llama-cpp/linux-x64-cuda-ext": "3.16.2", "@node-llama-cpp/linux-x64-vulkan": "3.16.2", "@node-llama-cpp/mac-arm64-metal": "3.16.2", "@node-llama-cpp/mac-x64": "3.16.2", "@node-llama-cpp/win-arm64": "3.16.2", "@node-llama-cpp/win-x64": "3.16.2", "@node-llama-cpp/win-x64-cuda": "3.16.2", "@node-llama-cpp/win-x64-cuda-ext": "3.16.2", "@node-llama-cpp/win-x64-vulkan": "3.16.2" }, "peerDependencies": { "typescript": ">=5.0.0" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/node-llama-cpp/node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "peer": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/node-llama-cpp/node_modules/ignore": { "version": "7.0.5", "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", "license": "MIT", "peer": true, "engines": { "node": ">= 4" } }, "node_modules/node-llama-cpp/node_modules/isexe": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-4.0.0.tgz", "integrity": "sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==", "license": "BlueOak-1.0.0", "peer": true, "engines": { "node": ">=20" } }, "node_modules/node-llama-cpp/node_modules/which": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/which/-/which-6.0.1.tgz", "integrity": "sha512-oGLe46MIrCRqX7ytPUf66EAYvdeMIZYn3WaocqqKZAxrBpkqHfL/qvTyJ/bTk5+AqHCjXmrv3CEWgy368zhRUg==", "license": "ISC", "peer": true, "dependencies": { "isexe": "^4.0.0" }, "bin": { "node-which": "bin/which.js" }, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/node-readable-to-web-readable-stream": { "version": "0.4.2", "resolved": "https://registry.npmjs.org/node-readable-to-web-readable-stream/-/node-readable-to-web-readable-stream-0.4.2.tgz", "integrity": "sha512-/cMZNI34v//jUTrI+UIo4ieHAB5EZRY/+7OmXZgBxaWBMcW2tGdceIw06RFxWxrKZ5Jp3sI2i5TsRo+CBhtVLQ==", "license": "MIT", "optional": true, "peer": true }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", "license": "BSD-2-Clause", "peer": true, "dependencies": { "boolbase": "^1.0.0" }, "funding": { "url": "https://github.com/fb55/nth-check?sponsor=1" } }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", "license": "MIT", "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/object-inspect": { "version": "1.13.4", "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/obug": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/obug/-/obug-2.1.1.tgz", "integrity": "sha512-uTqF9MuPraAQ+IsnPf366RG4cP9RtUi7MLO1N3KEc+wb0a6yKpeL0lmk2IB1jY5KHPAlTc6T/JRdC/YqxHNwkQ==", "dev": true, "funding": [ "https://github.com/sponsors/sxzz", "https://opencollective.com/debug" ], "license": "MIT" }, "node_modules/octokit": { "version": "5.0.5", "resolved": "https://registry.npmjs.org/octokit/-/octokit-5.0.5.tgz", "integrity": "sha512-4+/OFSqOjoyULo7eN7EA97DE0Xydj/PW5aIckxqQIoFjFwqXKuFCvXUJObyJfBF9Khu4RL/jlDRI9FPaMGfPnw==", "license": "MIT", "peer": true, "dependencies": { "@octokit/app": "^16.1.2", "@octokit/core": "^7.0.6", "@octokit/oauth-app": "^8.0.3", "@octokit/plugin-paginate-graphql": "^6.0.0", "@octokit/plugin-paginate-rest": "^14.0.0", "@octokit/plugin-rest-endpoint-methods": "^17.0.0", "@octokit/plugin-retry": "^8.0.3", "@octokit/plugin-throttling": "^11.0.3", "@octokit/request-error": "^7.0.2", "@octokit/types": "^16.0.0", "@octokit/webhooks": "^14.0.0" }, "engines": { "node": ">= 20" } }, "node_modules/on-exit-leak-free": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/on-exit-leak-free/-/on-exit-leak-free-2.1.2.tgz", "integrity": "sha512-0eJJY6hXLGf1udHwfNftBqH+g73EU4B504nZeKpz1sYRKafAghwxEJunB2O7rDZkL4PGfsMVnTXZ2EjibbqcsA==", "license": "MIT", "peer": true, "engines": { "node": ">=14.0.0" } }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", "peer": true, "dependencies": { "ee-first": "1.1.1" }, "engines": { "node": ">= 0.8" } }, "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", "peer": true, "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/onetime/-/onetime-7.0.0.tgz", "integrity": "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==", "license": "MIT", "peer": true, "dependencies": { "mimic-function": "^5.0.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/openai": { "version": "6.26.0", "resolved": "https://registry.npmjs.org/openai/-/openai-6.26.0.tgz", "integrity": "sha512-zd23dbWTjiJ6sSAX6s0HrCZi41JwTA1bQVs0wLQPZ2/5o2gxOJA5wh7yOAUgwYybfhDXyhwlpeQf7Mlgx8EOCA==", "license": "Apache-2.0", "peer": true, "bin": { "openai": "bin/cli" }, "peerDependencies": { "ws": "^8.18.0", "zod": "^3.25 || ^4.0" }, "peerDependenciesMeta": { "ws": { "optional": true }, "zod": { "optional": true } } }, "node_modules/openclaw": { "version": "2026.3.11", "resolved": "https://registry.npmjs.org/openclaw/-/openclaw-2026.3.11.tgz", "integrity": "sha512-bxwiBmHPakwfpY5tqC9lrV5TCu5PKf0c1bHNc3nhrb+pqKcPEWV4zOjDVFLQUHr98ihgWA+3pacy4b3LQ8wduQ==", "license": "MIT", "peer": true, "dependencies": { "@agentclientprotocol/sdk": "0.16.1", "@aws-sdk/client-bedrock": "^3.1007.0", "@buape/carbon": "0.0.0-beta-20260216184201", "@clack/prompts": "^1.1.0", "@discordjs/voice": "^0.19.1", "@grammyjs/runner": "^2.0.3", "@grammyjs/transformer-throttler": "^1.2.1", "@homebridge/ciao": "^1.3.5", "@larksuiteoapi/node-sdk": "^1.59.0", "@line/bot-sdk": "^10.6.0", "@lydell/node-pty": "1.2.0-beta.3", "@mariozechner/pi-agent-core": "0.57.1", "@mariozechner/pi-ai": "0.57.1", "@mariozechner/pi-coding-agent": "0.57.1", "@mariozechner/pi-tui": "0.57.1", "@mozilla/readability": "^0.6.0", "@sinclair/typebox": "0.34.48", "@slack/bolt": "^4.6.0", "@slack/web-api": "^7.14.1", "@whiskeysockets/baileys": "7.0.0-rc.9", "ajv": "^8.18.0", "chalk": "^5.6.2", "chokidar": "^5.0.0", "cli-highlight": "^2.1.11", "commander": "^14.0.3", "croner": "^10.0.1", "discord-api-types": "^0.38.42", "dotenv": "^17.3.1", "express": "^5.2.1", "file-type": "^21.3.1", "grammy": "^1.41.1", "hono": "4.12.7", "https-proxy-agent": "^8.0.0", "ipaddr.js": "^2.3.0", "jiti": "^2.6.1", "json5": "^2.2.3", "jszip": "^3.10.1", "linkedom": "^0.18.12", "long": "^5.3.2", "markdown-it": "^14.1.1", "node-edge-tts": "^1.2.10", "opusscript": "^0.1.1", "osc-progress": "^0.3.0", "pdfjs-dist": "^5.5.207", "playwright-core": "1.58.2", "qrcode-terminal": "^0.12.0", "sharp": "^0.34.5", "sqlite-vec": "0.1.7-alpha.2", "tar": "7.5.11", "tslog": "^4.10.2", "undici": "^7.22.0", "ws": "^8.19.0", "yaml": "^2.8.2", "zod": "^4.3.6" }, "bin": { "openclaw": "openclaw.mjs" }, "engines": { "node": ">=22.12.0" }, "peerDependencies": { "@napi-rs/canvas": "^0.1.89", "node-llama-cpp": "3.16.2" } }, "node_modules/openclaw/node_modules/ajv": { "version": "8.18.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.18.0.tgz", "integrity": "sha512-PlXPeEWMXMZ7sPYOHqmDyCJzcfNrUr3fGNKtezX14ykXOEIvyK81d+qydx89KY5O71FKMPaQ2vBfBFI5NHR63A==", "license": "MIT", "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", "require-from-string": "^2.0.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, "node_modules/openclaw/node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "peer": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/openclaw/node_modules/commander": { "version": "14.0.3", "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", "license": "MIT", "peer": true, "engines": { "node": ">=20" } }, "node_modules/openclaw/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "license": "MIT", "peer": true }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/opusscript": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/opusscript/-/opusscript-0.1.1.tgz", "integrity": "sha512-mL0fZZOUnXdZ78woRXp18lApwpp0lF5tozJOD1Wut0dgrA9WuQTgSels/CSmFleaAZrJi/nci5KOVtbuxeWoQA==", "license": "MIT", "peer": true }, "node_modules/ora": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/ora/-/ora-9.3.0.tgz", "integrity": "sha512-lBX72MWFduWEf7v7uWf5DHp9Jn5BI8bNPGuFgtXMmr2uDz2Gz2749y3am3agSDdkhHPHYmmxEGSKH85ZLGzgXw==", "license": "MIT", "peer": true, "dependencies": { "chalk": "^5.6.2", "cli-cursor": "^5.0.0", "cli-spinners": "^3.2.0", "is-interactive": "^2.0.0", "is-unicode-supported": "^2.1.0", "log-symbols": "^7.0.1", "stdin-discarder": "^0.3.1", "string-width": "^8.1.0" }, "engines": { "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ora/node_modules/chalk": { "version": "5.6.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", "license": "MIT", "peer": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" }, "funding": { "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/ora/node_modules/cli-spinners": { "version": "3.4.0", "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-3.4.0.tgz", "integrity": "sha512-bXfOC4QcT1tKXGorxL3wbJm6XJPDqEnij2gQ2m7ESQuE+/z9YFIWnl/5RpTiKWbMq3EVKR4fRLJGn6DVfu0mpw==", "license": "MIT", "peer": true, "engines": { "node": ">=18.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/osc-progress": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/osc-progress/-/osc-progress-0.3.0.tgz", "integrity": "sha512-4/8JfsetakdeEa4vAYV45FW20aY+B/+K8NEXp5Eiar3wR8726whgHrbSg5Ar/ZY1FLJ/AGtUqV7W2IVF+Gvp9A==", "license": "MIT", "peer": true, "engines": { "node": ">=20" } }, "node_modules/p-finally": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", "integrity": "sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==", "license": "MIT", "peer": true, "engines": { "node": ">=4" } }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-queue": { "version": "6.6.2", "resolved": "https://registry.npmjs.org/p-queue/-/p-queue-6.6.2.tgz", "integrity": "sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ==", "license": "MIT", "peer": true, "dependencies": { "eventemitter3": "^4.0.4", "p-timeout": "^3.2.0" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-queue/node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", "integrity": "sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==", "license": "MIT", "peer": true }, "node_modules/p-retry": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/p-retry/-/p-retry-4.6.2.tgz", "integrity": "sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ==", "license": "MIT", "peer": true, "dependencies": { "@types/retry": "0.12.0", "retry": "^0.13.1" }, "engines": { "node": ">=8" } }, "node_modules/p-timeout": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-3.2.0.tgz", "integrity": "sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg==", "license": "MIT", "peer": true, "dependencies": { "p-finally": "^1.0.0" }, "engines": { "node": ">=8" } }, "node_modules/pac-proxy-agent": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/pac-proxy-agent/-/pac-proxy-agent-7.2.0.tgz", "integrity": "sha512-TEB8ESquiLMc0lV8vcd5Ql/JAKAoyzHFXaStwjkzpOpC5Yv+pIzLfHvjTSdf3vpa2bMiUQrg9i6276yn8666aA==", "license": "MIT", "peer": true, "dependencies": { "@tootallnate/quickjs-emscripten": "^0.23.0", "agent-base": "^7.1.2", "debug": "^4.3.4", "get-uri": "^6.0.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.6", "pac-resolver": "^7.0.1", "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" } }, "node_modules/pac-proxy-agent/node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 14" } }, "node_modules/pac-proxy-agent/node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" }, "engines": { "node": ">= 14" } }, "node_modules/pac-resolver": { "version": "7.0.1", "resolved": "https://registry.npmjs.org/pac-resolver/-/pac-resolver-7.0.1.tgz", "integrity": "sha512-5NPgf87AT2STgwa2ntRMr45jTKrYBGkVU36yT0ig/n/GMAa3oPqhZfIQ2kMEimReg0+t9kZViDVZ83qfVUlckg==", "license": "MIT", "peer": true, "dependencies": { "degenerator": "^5.0.0", "netmask": "^2.0.2" }, "engines": { "node": ">= 14" } }, "node_modules/package-json-from-dist": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "license": "BlueOak-1.0.0", "peer": true }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==", "license": "(MIT AND Zlib)", "peer": true }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, "engines": { "node": ">=6" } }, "node_modules/parse-ms": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse-ms/-/parse-ms-4.0.0.tgz", "integrity": "sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==", "license": "MIT", "peer": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", "integrity": "sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==", "license": "MIT", "peer": true }, "node_modules/parse5-htmlparser2-tree-adapter": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-6.0.1.tgz", "integrity": "sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==", "license": "MIT", "peer": true, "dependencies": { "parse5": "^6.0.1" } }, "node_modules/parse5-htmlparser2-tree-adapter/node_modules/parse5": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-6.0.1.tgz", "integrity": "sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==", "license": "MIT", "peer": true }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/partial-json": { "version": "0.1.7", "resolved": "https://registry.npmjs.org/partial-json/-/partial-json-0.1.7.tgz", "integrity": "sha512-Njv/59hHaokb/hRUjce3Hdv12wd60MtM9Z5Olmn+nehe0QDAsRtRbJPvJ0Z91TusF0SuZRIvnM+S4l6EIP8leA==", "license": "MIT", "peer": true }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-expression-matcher": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/path-expression-matcher/-/path-expression-matcher-1.1.3.tgz", "integrity": "sha512-qdVgY8KXmVdJZRSS1JdEPOKPdTiEK/pi0RkcT2sw1RhXxohdujUlJFPuS1TSkevZ9vzd3ZlL7ULl1MHGTApKzQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } ], "license": "MIT", "peer": true, "engines": { "node": ">=14.0.0" } }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true, "license": "MIT" }, "node_modules/path-scurry": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", "license": "BlueOak-1.0.0", "peer": true, "dependencies": { "lru-cache": "^11.0.0", "minipass": "^7.1.2" }, "engines": { "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/path-to-regexp": { "version": "8.3.0", "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/pathe": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/pathe/-/pathe-2.0.3.tgz", "integrity": "sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==", "dev": true, "license": "MIT" }, "node_modules/pdfjs-dist": { "version": "5.5.207", "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-5.5.207.tgz", "integrity": "sha512-WMqqw06w1vUt9ZfT0gOFhMf3wHsWhaCrxGrckGs5Cci6ybDW87IvPaOd2pnBwT6BJuP/CzXDZxjFgmSULLdsdw==", "license": "Apache-2.0", "peer": true, "engines": { "node": ">=20.19.0 || >=22.13.0 || >=24" }, "optionalDependencies": { "@napi-rs/canvas": "^0.1.95", "node-readable-to-web-readable-stream": "^0.4.2" } }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", "license": "MIT", "peer": true }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, "node_modules/picomatch": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/pino": { "version": "9.14.0", "resolved": "https://registry.npmjs.org/pino/-/pino-9.14.0.tgz", "integrity": "sha512-8OEwKp5juEvb/MjpIc4hjqfgCNysrS94RIOMXYvpYCdm/jglrKEiAYmiumbmGhCvs+IcInsphYDFwqrjr7398w==", "license": "MIT", "peer": true, "dependencies": { "@pinojs/redact": "^0.4.0", "atomic-sleep": "^1.0.0", "on-exit-leak-free": "^2.1.0", "pino-abstract-transport": "^2.0.0", "pino-std-serializers": "^7.0.0", "process-warning": "^5.0.0", "quick-format-unescaped": "^4.0.3", "real-require": "^0.2.0", "safe-stable-stringify": "^2.3.1", "sonic-boom": "^4.0.1", "thread-stream": "^3.0.0" }, "bin": { "pino": "bin.js" } }, "node_modules/pino-abstract-transport": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/pino-abstract-transport/-/pino-abstract-transport-2.0.0.tgz", "integrity": "sha512-F63x5tizV6WCh4R6RHyi2Ml+M70DNRXt/+HANowMflpgGFMAym/VKm6G7ZOQRjqN7XbGxK1Lg9t6ZrtzOaivMw==", "license": "MIT", "peer": true, "dependencies": { "split2": "^4.0.0" } }, "node_modules/pino-std-serializers": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/pino-std-serializers/-/pino-std-serializers-7.1.0.tgz", "integrity": "sha512-BndPH67/JxGExRgiX1dX0w1FvZck5Wa4aal9198SrRhZjH3GxKQUKIBnYJTdj2HDN3UQAS06HlfcSbQj2OHmaw==", "license": "MIT", "peer": true }, "node_modules/playwright-core": { "version": "1.58.2", "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.58.2.tgz", "integrity": "sha512-yZkEtftgwS8CsfYo7nm0KE8jsvm6i/PTgVtB8DL726wNf6H2IMsDuxCpJj59KDaxCtSnrWan2AeDqM7JBaultg==", "license": "Apache-2.0", "peer": true, "bin": { "playwright-core": "cli.js" }, "engines": { "node": ">=18" } }, "node_modules/postcss": { "version": "8.5.8", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.8.tgz", "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/postcss/" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/postcss" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" } }, "node_modules/postcss/node_modules/nanoid": { "version": "3.3.11", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/ai" } ], "license": "MIT", "bin": { "nanoid": "bin/nanoid.cjs" }, "engines": { "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "node_modules/prettier": { "version": "3.8.1", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", "dev": true, "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/prettier/prettier?sponsor=1" } }, "node_modules/pretty-bytes": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/pretty-bytes/-/pretty-bytes-6.1.1.tgz", "integrity": "sha512-mQUvGU6aUFQ+rNvTIAcZuWGRT9a6f6Yrg9bHs4ImKF+HZCEK+plBvnAZYSIQztknZF2qnzNtr6F8s0+IuptdlQ==", "license": "MIT", "peer": true, "engines": { "node": "^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/pretty-ms": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/pretty-ms/-/pretty-ms-9.3.0.tgz", "integrity": "sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==", "license": "MIT", "peer": true, "dependencies": { "parse-ms": "^4.0.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "license": "MIT", "peer": true }, "node_modules/process-warning": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-5.0.0.tgz", "integrity": "sha512-a39t9ApHNx2L4+HBnQKqxxHNs1r7KF+Intd8Q/g1bUh6q0WIp9voPXJ/x0j+ZL45KF1pJd9+q2jLIRMfvEshkA==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/fastify" }, { "type": "opencollective", "url": "https://opencollective.com/fastify" } ], "license": "MIT", "peer": true }, "node_modules/proper-lockfile": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", "license": "MIT", "peer": true, "dependencies": { "graceful-fs": "^4.2.4", "retry": "^0.12.0", "signal-exit": "^3.0.2" } }, "node_modules/proper-lockfile/node_modules/retry": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", "license": "MIT", "peer": true, "engines": { "node": ">= 4" } }, "node_modules/protobufjs": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", "hasInstallScript": true, "license": "BSD-3-Clause", "peer": true, "dependencies": { "@protobufjs/aspromise": "^1.1.2", "@protobufjs/base64": "^1.1.2", "@protobufjs/codegen": "^2.0.4", "@protobufjs/eventemitter": "^1.1.0", "@protobufjs/fetch": "^1.1.0", "@protobufjs/float": "^1.0.2", "@protobufjs/inquire": "^1.1.0", "@protobufjs/path": "^1.1.2", "@protobufjs/pool": "^1.1.0", "@protobufjs/utf8": "^1.1.0", "@types/node": ">=13.7.0", "long": "^5.0.0" }, "engines": { "node": ">=12.0.0" } }, "node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", "peer": true, "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" }, "engines": { "node": ">= 0.10" } }, "node_modules/proxy-addr/node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.10" } }, "node_modules/proxy-agent": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/proxy-agent/-/proxy-agent-6.5.0.tgz", "integrity": "sha512-TmatMXdr2KlRiA2CyDu8GqR8EjahTG3aY3nXjdzFyoZbmB8hrBsTyMezhULIXKnC0jpfjlmiZ3+EaCzoInSu/A==", "license": "MIT", "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "http-proxy-agent": "^7.0.1", "https-proxy-agent": "^7.0.6", "lru-cache": "^7.14.1", "pac-proxy-agent": "^7.1.0", "proxy-from-env": "^1.1.0", "socks-proxy-agent": "^8.0.5" }, "engines": { "node": ">= 14" } }, "node_modules/proxy-agent/node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 14" } }, "node_modules/proxy-agent/node_modules/https-proxy-agent": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "license": "MIT", "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "4" }, "engines": { "node": ">= 14" } }, "node_modules/proxy-agent/node_modules/lru-cache": { "version": "7.18.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-7.18.3.tgz", "integrity": "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA==", "license": "ISC", "peer": true, "engines": { "node": ">=12" } }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", "license": "MIT", "peer": true }, "node_modules/pump": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", "license": "MIT", "peer": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/punycode.js": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", "license": "MIT", "peer": true, "engines": { "node": ">=6" } }, "node_modules/qified": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/qified/-/qified-0.6.0.tgz", "integrity": "sha512-tsSGN1x3h569ZSU1u6diwhltLyfUWDp3YbFHedapTmpBl0B3P6U3+Qptg7xu+v+1io1EwhdPyyRHYbEw0KN2FA==", "license": "MIT", "peer": true, "dependencies": { "hookified": "^1.14.0" }, "engines": { "node": ">=20" } }, "node_modules/qrcode-terminal": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/qrcode-terminal/-/qrcode-terminal-0.12.0.tgz", "integrity": "sha512-EXtzRZmC+YGmGlDFbXKxQiMZNwCLEO6BANKXG4iCtSIM0yqc/pappSx3RIKr4r0uh5JsBckOXeKrB3Iz7mdQpQ==", "peer": true, "bin": { "qrcode-terminal": "bin/qrcode-terminal.js" } }, "node_modules/qs": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", "license": "BSD-3-Clause", "peer": true, "dependencies": { "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", "integrity": "sha512-tYC1Q1hgyRuHgloV/YXs2w15unPVh8qfu/qCTfhTYamaw7fyhumKa2yGpdSo87vY32rIclj+4fWYQXUMs9EHvg==", "license": "MIT", "peer": true }, "node_modules/range-parser": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.6" } }, "node_modules/raw-body": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", "peer": true, "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", "iconv-lite": "~0.7.0", "unpipe": "~1.0.0" }, "engines": { "node": ">= 0.10" } }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "peer": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", "minimist": "^1.2.0", "strip-json-comments": "~2.0.1" }, "bin": { "rc": "cli.js" } }, "node_modules/rc/node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", "license": "MIT", "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "license": "MIT", "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "node_modules/readable-stream/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT", "peer": true }, "node_modules/readdirp": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-5.0.0.tgz", "integrity": "sha512-9u/XQ1pvrQtYyMpZe7DXKv2p5CNvyVwzUB6uhLAnQwHMSgKMBR62lc7AHljaeteeHXn11XTAaLLUVZYVZyuRBQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 20.19.0" }, "funding": { "type": "individual", "url": "https://paulmillr.com/funding/" } }, "node_modules/real-require": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/real-require/-/real-require-0.2.0.tgz", "integrity": "sha512-57frrGM/OCTLqLOAh0mhVA9VBMHd+9U7Zb2THMGdBUoZVOtGbJzjxsYGDJ3A9AYYCP4hn6y1TVbaOfzWtm5GFg==", "license": "MIT", "peer": true, "engines": { "node": ">= 12.13.0" } }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "license": "MIT", "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/require-from-string": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/resolve": { "version": "1.22.11", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "dev": true, "license": "MIT", "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/restore-cursor": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-5.1.0.tgz", "integrity": "sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==", "license": "MIT", "peer": true, "dependencies": { "onetime": "^7.0.0", "signal-exit": "^4.1.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/restore-cursor/node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "license": "ISC", "peer": true, "engines": { "node": ">=14" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/retry": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", "license": "MIT", "peer": true, "engines": { "node": ">= 4" } }, "node_modules/rimraf": { "version": "5.0.10", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.10.tgz", "integrity": "sha512-l0OE8wL34P4nJH/H2ffoaniAokM2qSmrtXHmlpvYr5AVVX8msAyW0l8NVJFDxlSK4u3Uh/f41cQheDVdnYijwQ==", "license": "ISC", "peer": true, "dependencies": { "glob": "^10.3.7" }, "bin": { "rimraf": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rimraf/node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", "peer": true, "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/rimraf/node_modules/glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "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", "license": "ISC", "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", "minimatch": "^9.0.4", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", "path-scurry": "^1.11.1" }, "bin": { "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rimraf/node_modules/lru-cache": { "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "license": "ISC", "peer": true }, "node_modules/rimraf/node_modules/minimatch": { "version": "9.0.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", "license": "ISC", "peer": true, "dependencies": { "brace-expansion": "^2.0.2" }, "engines": { "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rimraf/node_modules/path-scurry": { "version": "1.11.1", "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "license": "BlueOak-1.0.0", "peer": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" }, "engines": { "node": ">=16 || 14 >=14.18" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rolldown": { "version": "1.0.0-rc.9", "resolved": "https://registry.npmjs.org/rolldown/-/rolldown-1.0.0-rc.9.tgz", "integrity": "sha512-9EbgWge7ZH+yqb4d2EnELAntgPTWbfL8ajiTW+SyhJEC4qhBbkCKbqFV4Ge4zmu5ziQuVbWxb/XwLZ+RIO7E8Q==", "dev": true, "license": "MIT", "dependencies": { "@oxc-project/types": "=0.115.0", "@rolldown/pluginutils": "1.0.0-rc.9" }, "bin": { "rolldown": "bin/cli.mjs" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "optionalDependencies": { "@rolldown/binding-android-arm64": "1.0.0-rc.9", "@rolldown/binding-darwin-arm64": "1.0.0-rc.9", "@rolldown/binding-darwin-x64": "1.0.0-rc.9", "@rolldown/binding-freebsd-x64": "1.0.0-rc.9", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-rc.9", "@rolldown/binding-linux-arm64-gnu": "1.0.0-rc.9", "@rolldown/binding-linux-arm64-musl": "1.0.0-rc.9", "@rolldown/binding-linux-ppc64-gnu": "1.0.0-rc.9", "@rolldown/binding-linux-s390x-gnu": "1.0.0-rc.9", "@rolldown/binding-linux-x64-gnu": "1.0.0-rc.9", "@rolldown/binding-linux-x64-musl": "1.0.0-rc.9", "@rolldown/binding-openharmony-arm64": "1.0.0-rc.9", "@rolldown/binding-wasm32-wasi": "1.0.0-rc.9", "@rolldown/binding-win32-arm64-msvc": "1.0.0-rc.9", "@rolldown/binding-win32-x64-msvc": "1.0.0-rc.9" } }, "node_modules/router": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", "peer": true, "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", "is-promise": "^4.0.0", "parseurl": "^1.3.3", "path-to-regexp": "^8.0.0" }, "engines": { "node": ">= 18" } }, "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/feross" }, { "type": "patreon", "url": "https://www.patreon.com/feross" }, { "type": "consulting", "url": "https://feross.org/support" } ], "license": "MIT", "peer": true }, "node_modules/safe-stable-stringify": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "license": "MIT", "peer": true, "engines": { "node": ">=10" } }, "node_modules/safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "license": "MIT", "peer": true }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/send": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", "peer": true, "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" }, "engines": { "node": ">= 18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/serve-static": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", "peer": true, "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "parseurl": "^1.3.3", "send": "^1.2.0" }, "engines": { "node": ">= 18" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" } }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==", "license": "MIT", "peer": true }, "node_modules/setprototypeof": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", "license": "ISC", "peer": true }, "node_modules/sharp": { "version": "0.34.5", "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.5.tgz", "integrity": "sha512-Ou9I5Ft9WNcCbXrU9cMgPBcCK8LiwLqcbywW3t4oDV37n1pzpuNLsYiAV8eODnjbtQlSDwZ2cUEeQz4E54Hltg==", "hasInstallScript": true, "license": "Apache-2.0", "peer": true, "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", "semver": "^7.7.3" }, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, "funding": { "url": "https://opencollective.com/libvips" }, "optionalDependencies": { "@img/sharp-darwin-arm64": "0.34.5", "@img/sharp-darwin-x64": "0.34.5", "@img/sharp-libvips-darwin-arm64": "1.2.4", "@img/sharp-libvips-darwin-x64": "1.2.4", "@img/sharp-libvips-linux-arm": "1.2.4", "@img/sharp-libvips-linux-arm64": "1.2.4", "@img/sharp-libvips-linux-ppc64": "1.2.4", "@img/sharp-libvips-linux-riscv64": "1.2.4", "@img/sharp-libvips-linux-s390x": "1.2.4", "@img/sharp-libvips-linux-x64": "1.2.4", "@img/sharp-libvips-linuxmusl-arm64": "1.2.4", "@img/sharp-libvips-linuxmusl-x64": "1.2.4", "@img/sharp-linux-arm": "0.34.5", "@img/sharp-linux-arm64": "0.34.5", "@img/sharp-linux-ppc64": "0.34.5", "@img/sharp-linux-riscv64": "0.34.5", "@img/sharp-linux-s390x": "0.34.5", "@img/sharp-linux-x64": "0.34.5", "@img/sharp-linuxmusl-arm64": "0.34.5", "@img/sharp-linuxmusl-x64": "0.34.5", "@img/sharp-wasm32": "0.34.5", "@img/sharp-win32-arm64": "0.34.5", "@img/sharp-win32-ia32": "0.34.5", "@img/sharp-win32-x64": "0.34.5" } }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "side-channel-map": "^1.0.1", "side-channel-weakmap": "^1.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/side-channel-list": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/side-channel-map": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/side-channel-weakmap": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/siginfo": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/siginfo/-/siginfo-2.0.0.tgz", "integrity": "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==", "dev": true, "license": "ISC" }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "license": "ISC", "peer": true }, "node_modules/simple-git": { "version": "3.33.0", "resolved": "https://registry.npmjs.org/simple-git/-/simple-git-3.33.0.tgz", "integrity": "sha512-D4V/tGC2sjsoNhoMybKyGoE+v8A60hRawKQ1iFRA1zwuDgGZCBJ4ByOzZ5J8joBbi4Oam0qiPH+GhzmSBwbJng==", "license": "MIT", "peer": true, "dependencies": { "@kwsites/file-exists": "^1.1.1", "@kwsites/promise-deferred": "^1.1.1", "debug": "^4.4.0" }, "funding": { "type": "github", "url": "https://github.com/steveukx/git-js?sponsor=1" } }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "license": "MIT", "peer": true }, "node_modules/sleep-promise": { "version": "9.1.0", "resolved": "https://registry.npmjs.org/sleep-promise/-/sleep-promise-9.1.0.tgz", "integrity": "sha512-UHYzVpz9Xn8b+jikYSD6bqvf754xL2uBUzDFwiU6NcdZeifPr6UfgU43xpkPu67VMS88+TI2PSI7Eohgqf2fKA==", "license": "MIT", "peer": true }, "node_modules/slice-ansi": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-8.0.0.tgz", "integrity": "sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==", "license": "MIT", "peer": true, "dependencies": { "ansi-styles": "^6.2.3", "is-fullwidth-code-point": "^5.1.0" }, "engines": { "node": ">=20" }, "funding": { "url": "https://github.com/chalk/slice-ansi?sponsor=1" } }, "node_modules/slice-ansi/node_modules/ansi-styles": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", "peer": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/smart-buffer": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", "license": "MIT", "peer": true, "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks": { "version": "2.8.7", "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "license": "MIT", "peer": true, "dependencies": { "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { "node": ">= 10.0.0", "npm": ">= 3.0.0" } }, "node_modules/socks-proxy-agent": { "version": "8.0.5", "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "license": "MIT", "peer": true, "dependencies": { "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" }, "engines": { "node": ">= 14" } }, "node_modules/socks-proxy-agent/node_modules/agent-base": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 14" } }, "node_modules/sonic-boom": { "version": "4.2.1", "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-4.2.1.tgz", "integrity": "sha512-w6AxtubXa2wTXAUsZMMWERrsIRAdrK0Sc+FUytWvYAhBJLyuI4llrMIC1DtlNSdI99EI86KZum2MMq3EAZlF9Q==", "license": "MIT", "peer": true, "dependencies": { "atomic-sleep": "^1.0.0" } }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "license": "BSD-3-Clause", "peer": true, "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "license": "MIT", "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==", "license": "ISC", "peer": true, "engines": { "node": ">= 10.x" } }, "node_modules/sqlite-vec": { "version": "0.1.7-alpha.2", "resolved": "https://registry.npmjs.org/sqlite-vec/-/sqlite-vec-0.1.7-alpha.2.tgz", "integrity": "sha512-rNgRCv+4V4Ed3yc33Qr+nNmjhtrMnnHzXfLVPeGb28Dx5mmDL3Ngw/Wk8vhCGjj76+oC6gnkmMG8y73BZWGBwQ==", "license": "MIT OR Apache", "peer": true, "optionalDependencies": { "sqlite-vec-darwin-arm64": "0.1.7-alpha.2", "sqlite-vec-darwin-x64": "0.1.7-alpha.2", "sqlite-vec-linux-arm64": "0.1.7-alpha.2", "sqlite-vec-linux-x64": "0.1.7-alpha.2", "sqlite-vec-windows-x64": "0.1.7-alpha.2" } }, "node_modules/sqlite-vec-darwin-arm64": { "version": "0.1.7-alpha.2", "resolved": "https://registry.npmjs.org/sqlite-vec-darwin-arm64/-/sqlite-vec-darwin-arm64-0.1.7-alpha.2.tgz", "integrity": "sha512-raIATOqFYkeCHhb/t3r7W7Cf2lVYdf4J3ogJ6GFc8PQEgHCPEsi+bYnm2JT84MzLfTlSTIdxr4/NKv+zF7oLPw==", "cpu": [ "arm64" ], "license": "MIT OR Apache", "optional": true, "os": [ "darwin" ], "peer": true }, "node_modules/sqlite-vec-darwin-x64": { "version": "0.1.7-alpha.2", "resolved": "https://registry.npmjs.org/sqlite-vec-darwin-x64/-/sqlite-vec-darwin-x64-0.1.7-alpha.2.tgz", "integrity": "sha512-jeZEELsQjjRsVojsvU5iKxOvkaVuE+JYC8Y4Ma8U45aAERrDYmqZoHvgSG7cg1PXL3bMlumFTAmHynf1y4pOzA==", "cpu": [ "x64" ], "license": "MIT OR Apache", "optional": true, "os": [ "darwin" ], "peer": true }, "node_modules/sqlite-vec-linux-arm64": { "version": "0.1.7-alpha.2", "resolved": "https://registry.npmjs.org/sqlite-vec-linux-arm64/-/sqlite-vec-linux-arm64-0.1.7-alpha.2.tgz", "integrity": "sha512-6Spj4Nfi7tG13jsUG+W7jnT0bCTWbyPImu2M8nWp20fNrd1SZ4g3CSlDAK8GBdavX7wRlbBHCZ+BDa++rbDewA==", "cpu": [ "arm64" ], "license": "MIT OR Apache", "optional": true, "os": [ "linux" ], "peer": true }, "node_modules/sqlite-vec-linux-x64": { "version": "0.1.7-alpha.2", "resolved": "https://registry.npmjs.org/sqlite-vec-linux-x64/-/sqlite-vec-linux-x64-0.1.7-alpha.2.tgz", "integrity": "sha512-IcgrbHaDccTVhXDf8Orwdc2+hgDLAFORl6OBUhcvlmwswwBP1hqBTSEhovClG4NItwTOBNgpwOoQ7Qp3VDPWLg==", "cpu": [ "x64" ], "license": "MIT OR Apache", "optional": true, "os": [ "linux" ], "peer": true }, "node_modules/sqlite-vec-windows-x64": { "version": "0.1.7-alpha.2", "resolved": "https://registry.npmjs.org/sqlite-vec-windows-x64/-/sqlite-vec-windows-x64-0.1.7-alpha.2.tgz", "integrity": "sha512-TRP6hTjAcwvQ6xpCZvjP00pdlda8J38ArFy1lMYhtQWXiIBmWnhMaMbq4kaeCYwvTTddfidatRS+TJrwIKB/oQ==", "cpu": [ "x64" ], "license": "MIT OR Apache", "optional": true, "os": [ "win32" ], "peer": true }, "node_modules/stackback": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/stackback/-/stackback-0.0.2.tgz", "integrity": "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==", "dev": true, "license": "MIT" }, "node_modules/statuses": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/std-env": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/std-env/-/std-env-4.0.0.tgz", "integrity": "sha512-zUMPtQ/HBY3/50VbpkupYHbRroTRZJPRLvreamgErJVys0ceuzMkD44J/QjqhHjOzK42GQ3QZIeFG1OYfOtKqQ==", "dev": true, "license": "MIT" }, "node_modules/stdin-discarder": { "version": "0.3.1", "resolved": "https://registry.npmjs.org/stdin-discarder/-/stdin-discarder-0.3.1.tgz", "integrity": "sha512-reExS1kSGoElkextOcPkel4NE99S0BWxjUHQeDFnR8S993JxpPX7KU4MNmO19NXhlJp+8dmdCbKQVNgLJh2teA==", "license": "MIT", "peer": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/stdout-update": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/stdout-update/-/stdout-update-4.0.1.tgz", "integrity": "sha512-wiS21Jthlvl1to+oorePvcyrIkiG/6M3D3VTmDUlJm7Cy6SbFhKkAvX+YBuHLxck/tO3mrdpC/cNesigQc3+UQ==", "license": "MIT", "peer": true, "dependencies": { "ansi-escapes": "^6.2.0", "ansi-styles": "^6.2.1", "string-width": "^7.1.0", "strip-ansi": "^7.1.0" }, "engines": { "node": ">=16.0.0" } }, "node_modules/stdout-update/node_modules/ansi-styles": { "version": "6.2.3", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", "peer": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/stdout-update/node_modules/emoji-regex": { "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", "license": "MIT", "peer": true }, "node_modules/stdout-update/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/steno": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/steno/-/steno-4.0.2.tgz", "integrity": "sha512-yhPIQXjrlt1xv7dyPQg2P17URmXbuM5pdGkpiMB3RenprfiBlvK415Lctfe0eshk90oA7/tNq7WEiMK8RSP39A==", "license": "MIT", "peer": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/typicode" } }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "license": "MIT", "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } }, "node_modules/string_decoder/node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "license": "MIT", "peer": true }, "node_modules/string-width": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-8.2.0.tgz", "integrity": "sha512-6hJPQ8N0V0P3SNmP6h2J99RLuzrWz2gvT7VnK5tKvrNqJoyS9W4/Fb8mo31UiPvy00z7DQXkP2hnKBVav76thw==", "license": "MIT", "peer": true, "dependencies": { "get-east-asian-width": "^1.5.0", "strip-ansi": "^7.1.2" }, "engines": { "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/string-width-cjs/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/string-width-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/string-width-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", "license": "MIT", "peer": true, "dependencies": { "ansi-regex": "^6.2.2" }, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/chalk/strip-ansi?sponsor=1" } }, "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/strnum": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.2.0.tgz", "integrity": "sha512-Y7Bj8XyJxnPAORMZj/xltsfo55uOiyHcU2tnAVzHUnSJR/KsEX+9RoDeXEnsXtl/CX4fAcrt64gZ13aGaWPeBg==", "funding": [ { "type": "github", "url": "https://github.com/sponsors/NaturalIntelligence" } ], "license": "MIT", "peer": true }, "node_modules/strtok3": { "version": "10.3.4", "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", "license": "MIT", "peer": true, "dependencies": { "@tokenizer/token": "^0.3.0" }, "engines": { "node": ">=18" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Borewit" } }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/tar": { "version": "7.5.11", "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.11.tgz", "integrity": "sha512-ChjMH33/KetonMTAtpYdgUFr0tbz69Fp2v7zWxQfYZX4g5ZN2nOBXm1R2xyA+lMIKrLKIoKAwFj93jE/avX9cQ==", "license": "BlueOak-1.0.0", "peer": true, "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" }, "engines": { "node": ">=18" } }, "node_modules/thenify": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/thenify/-/thenify-3.3.1.tgz", "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", "license": "MIT", "peer": true, "dependencies": { "any-promise": "^1.0.0" } }, "node_modules/thenify-all": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/thenify-all/-/thenify-all-1.6.0.tgz", "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", "license": "MIT", "peer": true, "dependencies": { "thenify": ">= 3.1.0 < 4" }, "engines": { "node": ">=0.8" } }, "node_modules/thread-stream": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/thread-stream/-/thread-stream-3.1.0.tgz", "integrity": "sha512-OqyPZ9u96VohAyMfJykzmivOrY2wfMSf3C5TtFJVgN+Hm6aj+voFhlK+kZEIv2FBh1X6Xp3DlnCOfEQ3B2J86A==", "license": "MIT", "peer": true, "dependencies": { "real-require": "^0.2.0" } }, "node_modules/tinybench": { "version": "2.9.0", "resolved": "https://registry.npmjs.org/tinybench/-/tinybench-2.9.0.tgz", "integrity": "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==", "dev": true, "license": "MIT" }, "node_modules/tinyexec": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-1.0.2.tgz", "integrity": "sha512-W/KYk+NFhkmsYpuHq5JykngiOCnxeVL8v8dFnqxSD8qEEdRfXk1SDM6JzNqcERbcGYj9tMrDQBYV9cjgnunFIg==", "dev": true, "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/tinyglobby": { "version": "0.2.15", "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" }, "funding": { "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/tinyrainbow": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.1.0.tgz", "integrity": "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==", "dev": true, "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/toad-cache": { "version": "3.7.0", "resolved": "https://registry.npmjs.org/toad-cache/-/toad-cache-3.7.0.tgz", "integrity": "sha512-/m8M+2BJUpoJdgAHoG+baCwBT+tf2VraSfkBgl0Y00qIWt41DJ8R5B8nsEw0I58YwF5IZH6z24/2TobDKnqSWw==", "license": "MIT", "peer": true, "engines": { "node": ">=12" } }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", "peer": true, "engines": { "node": ">=0.6" } }, "node_modules/token-types": { "version": "6.1.2", "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.2.tgz", "integrity": "sha512-dRXchy+C0IgK8WPC6xvCHFRIWYUbqqdEIKPaKo/AcTUNzwLTK6AH7RjdLWsEZcAN/TBdtfUw3PYEgPr5VPr6ww==", "license": "MIT", "peer": true, "dependencies": { "@borewit/text-codec": "^0.2.1", "@tokenizer/token": "^0.3.0", "ieee754": "^1.2.1" }, "engines": { "node": ">=14.16" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Borewit" } }, "node_modules/tr46": { "version": "0.0.3", "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", "license": "MIT", "peer": true }, "node_modules/ts-algebra": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", "license": "MIT", "peer": true }, "node_modules/ts-api-utils": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", "dev": true, "license": "MIT", "engines": { "node": ">=18.12" }, "peerDependencies": { "typescript": ">=4.8.4" } }, "node_modules/tslib": { "version": "2.8.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", "license": "0BSD" }, "node_modules/tslog": { "version": "4.10.2", "resolved": "https://registry.npmjs.org/tslog/-/tslog-4.10.2.tgz", "integrity": "sha512-XuELoRpMR+sq8fuWwX7P0bcj+PRNiicOKDEb3fGNURhxWVyykCi9BNq7c4uVz7h7P0sj8qgBsr5SWS6yBClq3g==", "license": "MIT", "peer": true, "engines": { "node": ">=16" }, "funding": { "url": "https://github.com/fullstack-build/tslog?sponsor=1" } }, "node_modules/tsscmp": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", "license": "MIT", "peer": true, "engines": { "node": ">=0.6.x" } }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/type-is": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", "peer": true, "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", "mime-types": "^3.0.0" }, "engines": { "node": ">= 0.6" } }, "node_modules/typescript": { "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "devOptional": true, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=14.17" } }, "node_modules/typescript-eslint": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.57.0.tgz", "integrity": "sha512-W8GcigEMEeB07xEZol8oJ26rigm3+bfPHxHvwbYUlu1fUDsGuQ7Hiskx5xGW/xM4USc9Ephe3jtv7ZYPQntHeA==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/eslint-plugin": "8.57.0", "@typescript-eslint/parser": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0", "@typescript-eslint/utils": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/project-service": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.57.0.tgz", "integrity": "sha512-pR+dK0BlxCLxtWfaKQWtYr7MhKmzqZxuii+ZjuFlZlIGRZm22HnXFqa2eY+90MUz8/i80YJmzFGDUsi8dMOV5w==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/tsconfig-utils": "^8.57.0", "@typescript-eslint/types": "^8.57.0", "debug": "^4.4.3" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/scope-manager": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.57.0.tgz", "integrity": "sha512-nvExQqAHF01lUM66MskSaZulpPL5pgy5hI5RfrxviLgzZVffB5yYzw27uK/ft8QnKXI2X0LBrHJFr1TaZtAibw==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/tsconfig-utils": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.57.0.tgz", "integrity": "sha512-LtXRihc5ytjJIQEH+xqjB0+YgsV4/tW35XKX3GTZHpWtcC8SPkT/d4tqdf1cKtesryHm2bgp6l555NYcT2NLvA==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/types": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.57.0.tgz", "integrity": "sha512-dTLI8PEXhjUC7B9Kre+u0XznO696BhXcTlOn0/6kf1fHaQW8+VjJAVHJ3eTI14ZapTxdkOmc80HblPQLaEeJdg==", "dev": true, "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.57.0.tgz", "integrity": "sha512-m7faHcyVg0BT3VdYTlX8GdJEM7COexXxS6KqGopxdtkQRvBanK377QDHr4W/vIPAR+ah9+B/RclSW5ldVniO1Q==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/project-service": "8.57.0", "@typescript-eslint/tsconfig-utils": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/visitor-keys": "8.57.0", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", "tinyglobby": "^0.2.15", "ts-api-utils": "^2.4.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.57.0.tgz", "integrity": "sha512-5iIHvpD3CZe06riAsbNxxreP+MuYgVUsV0n4bwLH//VJmgtt54sQeY2GszntJ4BjYCpMzrfVh2SBnUQTtys2lQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", "@typescript-eslint/scope-manager": "8.57.0", "@typescript-eslint/types": "8.57.0", "@typescript-eslint/typescript-estree": "8.57.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.0.0" } }, "node_modules/typescript-eslint/node_modules/@typescript-eslint/visitor-keys": { "version": "8.57.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.57.0.tgz", "integrity": "sha512-zm6xx8UT/Xy2oSr2ZXD0pZo7Jx2XsCoID2IUh9YSTFRu7z+WdwYTRk6LhUftm1crwqbuoF6I8zAFeCMw0YjwDg==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "8.57.0", "eslint-visitor-keys": "^5.0.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/typescript-eslint/node_modules/balanced-match": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", "dev": true, "license": "MIT", "engines": { "node": "18 || 20 || >=22" } }, "node_modules/typescript-eslint/node_modules/brace-expansion": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.4.tgz", "integrity": "sha512-h+DEnpVvxmfVefa4jFbCf5HdH5YMDXRsmKflpf1pILZWRFlTbJpxeU55nJl4Smt5HQaGzg1o6RHFPJaOqnmBDg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^4.0.2" }, "engines": { "node": "18 || 20 || >=22" } }, "node_modules/typescript-eslint/node_modules/eslint-visitor-keys": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-5.0.1.tgz", "integrity": "sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^20.19.0 || ^22.13.0 || >=24" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/typescript-eslint/node_modules/minimatch": { "version": "10.2.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.4.tgz", "integrity": "sha512-oRjTw/97aTBN0RHbYCdtF1MQfvusSIBQM0IZEgzl6426+8jSC0nF1a/GmnVLpfB9yyr6g6FTqWqiZVbxrtaCIg==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "brace-expansion": "^5.0.2" }, "engines": { "node": "18 || 20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/uc.micro": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", "license": "MIT", "peer": true }, "node_modules/uhyphen": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/uhyphen/-/uhyphen-0.2.0.tgz", "integrity": "sha512-qz3o9CHXmJJPGBdqzab7qAYuW8kQGKNEuoHFYrBwV6hWIMcpAmxDLXojcHfFr9US1Pe6zUswEIJIbLI610fuqA==", "license": "ISC", "peer": true }, "node_modules/uint8array-extras": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", "license": "MIT", "peer": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/undici": { "version": "7.23.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.23.0.tgz", "integrity": "sha512-HVMxHKZKi+eL2mrUZDzDkKW3XvCjynhbtpSq20xQp4ePDFeSFuAfnvM0GIwZIv8fiKHjXFQ5WjxhCt15KRNj+g==", "license": "MIT", "peer": true, "engines": { "node": ">=20.18.1" } }, "node_modules/undici-types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, "node_modules/universal-github-app-jwt": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/universal-github-app-jwt/-/universal-github-app-jwt-2.2.2.tgz", "integrity": "sha512-dcmbeSrOdTnsjGjUfAlqNDJrhxXizjAz94ija9Qw8YkZ1uu0d+GoZzyH+Jb9tIIqvGsadUfwg+22k5aDqqwzbw==", "license": "MIT", "peer": true }, "node_modules/universal-user-agent": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", "license": "ISC", "peer": true }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "license": "MIT", "peer": true, "engines": { "node": ">= 10.0.0" } }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } }, "node_modules/url-join": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", "license": "MIT", "peer": true }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "license": "MIT", "peer": true }, "node_modules/validate-npm-package-name": { "version": "7.0.2", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-7.0.2.tgz", "integrity": "sha512-hVDIBwsRruT73PbK7uP5ebUt+ezEtCmzZz3F59BSr2F6OVFnJ/6h8liuvdLrQ88Xmnk6/+xGGuq+pG9WwTuy3A==", "license": "ISC", "peer": true, "engines": { "node": "^20.17.0 || >=22.9.0" } }, "node_modules/vary": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", "peer": true, "engines": { "node": ">= 0.8" } }, "node_modules/vite": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.0.tgz", "integrity": "sha512-fPGaRNj9Zytaf8LEiBhY7Z6ijnFKdzU/+mL8EFBaKr7Vw1/FWcTBAMW0wLPJAGMPX38ZPVCVgLceWiEqeoqL2Q==", "dev": true, "license": "MIT", "dependencies": { "@oxc-project/runtime": "0.115.0", "lightningcss": "^1.32.0", "picomatch": "^4.0.3", "postcss": "^8.5.8", "rolldown": "1.0.0-rc.9", "tinyglobby": "^0.2.15" }, "bin": { "vite": "bin/vite.js" }, "engines": { "node": "^20.19.0 || >=22.12.0" }, "funding": { "url": "https://github.com/vitejs/vite?sponsor=1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "@vitejs/devtools": "^0.0.0-alpha.31", "esbuild": "^0.27.0", "jiti": ">=1.21.0", "less": "^4.0.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, "@vitejs/devtools": { "optional": true }, "esbuild": { "optional": true }, "jiti": { "optional": true }, "less": { "optional": true }, "sass": { "optional": true }, "sass-embedded": { "optional": true }, "stylus": { "optional": true }, "sugarss": { "optional": true }, "terser": { "optional": true }, "tsx": { "optional": true }, "yaml": { "optional": true } } }, "node_modules/vitest": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.1.0.tgz", "integrity": "sha512-YbDrMF9jM2Lqc++2530UourxZHmkKLxrs4+mYhEwqWS97WJ7wOYEkcr+QfRgJ3PW9wz3odRijLZjHEaRLTNbqw==", "dev": true, "license": "MIT", "dependencies": { "@vitest/expect": "4.1.0", "@vitest/mocker": "4.1.0", "@vitest/pretty-format": "4.1.0", "@vitest/runner": "4.1.0", "@vitest/snapshot": "4.1.0", "@vitest/spy": "4.1.0", "@vitest/utils": "4.1.0", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.0.3", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0", "why-is-node-running": "^2.3.0" }, "bin": { "vitest": "vitest.mjs" }, "engines": { "node": "^20.0.0 || ^22.0.0 || >=24.0.0" }, "funding": { "url": "https://opencollective.com/vitest" }, "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.0", "@vitest/browser-preview": "4.1.0", "@vitest/browser-webdriverio": "4.1.0", "@vitest/ui": "4.1.0", "happy-dom": "*", "jsdom": "*", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0-0" }, "peerDependenciesMeta": { "@edge-runtime/vm": { "optional": true }, "@opentelemetry/api": { "optional": true }, "@types/node": { "optional": true }, "@vitest/browser-playwright": { "optional": true }, "@vitest/browser-preview": { "optional": true }, "@vitest/browser-webdriverio": { "optional": true }, "@vitest/ui": { "optional": true }, "happy-dom": { "optional": true }, "jsdom": { "optional": true }, "vite": { "optional": false } } }, "node_modules/web-streams-polyfill": { "version": "3.3.3", "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", "license": "MIT", "peer": true, "engines": { "node": ">= 8" } }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", "license": "BSD-2-Clause", "peer": true }, "node_modules/whatwg-url": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", "license": "MIT", "peer": true, "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" }, "engines": { "node": ">= 8" } }, "node_modules/why-is-node-running": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/why-is-node-running/-/why-is-node-running-2.3.0.tgz", "integrity": "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==", "dev": true, "license": "MIT", "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" }, "engines": { "node": ">=8" } }, "node_modules/win-guid": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/win-guid/-/win-guid-0.2.1.tgz", "integrity": "sha512-gEIQU4mkgl2OPeoNrWflcJFJ3Ae2BPd4eCsHHA/XikslkIVms/nHhvnvzIZV7VLmBvtFlDOzLt9rrZT+n6D67A==", "license": "MIT", "peer": true }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/wrap-ansi-cjs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/wrap-ansi-cjs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/wrap-ansi/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/wrap-ansi/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/wrap-ansi/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "license": "ISC", "peer": true }, "node_modules/ws": { "version": "8.19.0", "resolved": "https://registry.npmjs.org/ws/-/ws-8.19.0.tgz", "integrity": "sha512-blAT2mjOEIi0ZzruJfIhb3nps74PRWTCz1IjglWEEpQl5XS/UNama6u2/rjFkDDouqr4L67ry+1aGIALViWjDg==", "license": "MIT", "peer": true, "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { "optional": true }, "utf-8-validate": { "optional": true } } }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "license": "ISC", "peer": true, "engines": { "node": ">=10" } }, "node_modules/yallist": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", "license": "BlueOak-1.0.0", "peer": true, "engines": { "node": ">=18" } }, "node_modules/yaml": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", "peer": true, "bin": { "yaml": "bin.mjs" }, "engines": { "node": ">= 14.6" }, "funding": { "url": "https://github.com/sponsors/eemeli" } }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "license": "MIT", "peer": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" }, "engines": { "node": ">=12" } }, "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "license": "ISC", "peer": true, "engines": { "node": ">=12" } }, "node_modules/yargs/node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/yargs/node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", "peer": true, "engines": { "node": ">=8" } }, "node_modules/yargs/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/yargs/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "license": "MIT", "peer": true, "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/yoctocolors": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.2.tgz", "integrity": "sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==", "license": "MIT", "peer": true, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/zod": { "version": "4.3.6", "resolved": "https://registry.npmjs.org/zod/-/zod-4.3.6.tgz", "integrity": "sha512-rftlrkhHZOcjDwkGlnUtZZkvaPHCsDATp4pGpuOOMDaTdDDXF91wuVDJoWoPsKX/3YPQ5fHuF3STjcYyKr+Qhg==", "license": "MIT", "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } }, "node_modules/zod-to-json-schema": { "version": "3.25.1", "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.25.1.tgz", "integrity": "sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==", "license": "ISC", "peer": true, "peerDependencies": { "zod": "^3.25 || ^4" } } } } ================================================ FILE: integrations/openclaw/package.json ================================================ { "name": "@memorilabs/openclaw-memori", "version": "0.0.4", "description": "Official MemoriLabs.ai long-term memory plugin for OpenClaw", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", "files": [ "dist", "openclaw.plugin.json" ], "scripts": { "build": "tsc", "build:dev": "tsc --watch", "typecheck": "tsc --noEmit", "lint": "eslint .", "lint:fix": "eslint . --fix", "format": "prettier --write .", "format:check": "prettier --check .", "check": "npm run format:check && npm run lint && npm run typecheck", "test": "vitest run", "test:coverage": "vitest run --coverage" }, "peerDependencies": { "openclaw": "^2026.3.2" }, "devDependencies": { "@eslint/js": "^9.0.0", "@types/node": "^20.0.0", "@vitest/coverage-v8": "^4.0.18", "eslint": "^9.0.0", "eslint-config-prettier": "^10.1.8", "eslint-plugin-tsdoc": "^0.5.1", "prettier": "^3.0.0", "typescript": "^5.0.0", "typescript-eslint": "^8.0.0", "vitest": "^4.0.18" }, "keywords": [ "openclaw", "plugin", "memory", "memori", "long-term-memory" ], "author": "Memori Labs", "license": "Apache-2.0", "repository": { "type": "git", "url": "https://github.com/MemoriLabs/Memori.git", "directory": "integrations/openclaw" }, "bugs": { "url": "https://github.com/MemoriLabs/Memori/issues" }, "homepage": "https://github.com/MemoriLabs/Memori", "openclaw": { "extensions": [ "./dist/index.js" ] }, "engines": { "node": ">=22.0.0" }, "dependencies": { "@memorilabs/memori": "^0.0.5" }, "overrides": { "@hono/node-server": "^1.19.10" } } ================================================ FILE: integrations/openclaw/src/constants.ts ================================================ export const PLUGIN_CONFIG = { ID: 'openclaw-memori', NAME: 'Memori System', LOG_PREFIX: '[Memori]', } as const; export const RECALL_CONFIG = { MIN_PROMPT_LENGTH: 2, } as const; export const AUGMENTATION_CONFIG = { MAX_CONTEXT_MESSAGES: 5, } as const; ================================================ FILE: integrations/openclaw/src/handlers/augmentation.ts ================================================ import { IntegrationRequest, IntegrationMetadata } from '@memorilabs/memori/integrations'; import { OpenClawEvent, OpenClawContext, MemoriPluginConfig } from '../types.js'; import { extractContext, MemoriLogger, initializeMemoriClient } from '../utils/index.js'; import { cleanText, isSystemMessage } from '../sanitizer.js'; import { AUGMENTATION_CONFIG } from '../constants.js'; import { SDK_VERSION } from '../version.js'; function extractLLMMetadata(event: OpenClawEvent): IntegrationMetadata { const messages = event.messages || []; const lastAssistant = messages.findLast((m) => m.role === 'assistant'); return { provider: (lastAssistant?.provider as string) || null, model: (lastAssistant?.model as string) || null, sdkVersion: null, integrationSdkVersion: SDK_VERSION, platform: 'openclaw', }; } export async function handleAugmentation( event: OpenClawEvent, ctx: OpenClawContext, config: MemoriPluginConfig, logger: MemoriLogger ): Promise { logger.section('AUGMENTATION HOOK START'); if (!event.success || !event.messages || event.messages.length < 2) { logger.info('No messages or unsuccessful event. Skipping augmentation.'); logger.endSection('AUGMENTATION HOOK END'); return; } try { const recentMessages = event.messages.slice(-AUGMENTATION_CONFIG.MAX_CONTEXT_MESSAGES); let lastUserMsg: { role: string; content: string } | undefined; let lastAiMsg: { role: string; content: string } | undefined; for (let i = recentMessages.length - 1; i >= 0; i--) { const msg = recentMessages[i]; const role = msg.role; if (role !== 'user' && role !== 'assistant') continue; const cleanedContent = cleanText(msg.content); if (!cleanedContent) continue; let finalContent = cleanedContent; if (role === 'assistant') { finalContent = finalContent.replace(/^\[\[.*?\]\]\s*/, ''); } if (role === 'assistant' && !lastAiMsg) { lastAiMsg = { role, content: finalContent }; } if (role === 'user' && !lastUserMsg) { lastUserMsg = { role, content: finalContent }; } if (lastUserMsg && lastAiMsg) break; } if (!lastUserMsg || !lastAiMsg) { logger.info('Missing user or assistant message. Skipping.'); logger.endSection('AUGMENTATION HOOK END'); return; } if (isSystemMessage(lastUserMsg.content)) { logger.info('User message is a system message. Skipping augmentation.'); logger.endSection('AUGMENTATION HOOK END'); return; } if (lastAiMsg.content === 'NO_REPLY' || lastAiMsg.content === 'SILENT_REPLY') { logger.info('Assistant used tool-based messaging. Using synthetic response.'); lastAiMsg = { role: 'assistant', content: "Okay, I'll remember that for you.", }; } const context = extractContext(event, ctx, config.entityId); const memoriClient = initializeMemoriClient(config.apiKey, context); const payload: IntegrationRequest = { userMessage: lastUserMsg.content, agentResponse: lastAiMsg.content, metadata: extractLLMMetadata(event), }; await memoriClient.augmentation(payload); logger.info('Augmentation successful!'); } catch (err) { logger.error(`Augmentation failed: ${err instanceof Error ? err.message : String(err)}`); } finally { logger.endSection('AUGMENTATION HOOK END'); } } ================================================ FILE: integrations/openclaw/src/handlers/recall.ts ================================================ import { cleanText, isSystemMessage } from '../sanitizer.js'; import { OpenClawEvent, OpenClawContext, MemoriPluginConfig } from '../types.js'; import { RECALL_CONFIG } from '../constants.js'; import { extractContext, MemoriLogger, initializeMemoriClient } from '../utils/index.js'; export async function handleRecall( event: OpenClawEvent, ctx: OpenClawContext, config: MemoriPluginConfig, logger: MemoriLogger ): Promise<{ prependContext: string } | undefined> { logger.section('RECALL HOOK START'); try { const context = extractContext(event, ctx, config.entityId); const promptText = cleanText(event.prompt); if ( !promptText || promptText.length < RECALL_CONFIG.MIN_PROMPT_LENGTH || isSystemMessage(promptText) ) { logger.info('Prompt too short or is a system message. Aborting recall.'); return undefined; } const memoriClient = initializeMemoriClient(config.apiKey, context); logger.info('Executing SDK Recall...'); const recallText = await memoriClient.recall(promptText); const hookReturn = recallText ? { prependContext: recallText } : undefined; if (hookReturn) { logger.info('Successfully injected memory context.'); } else { logger.info('No relevant memories found.'); } return hookReturn; } catch (err) { logger.error(`Recall failed: ${err instanceof Error ? err.message : String(err)}`); return undefined; } finally { logger.endSection('RECALL HOOK END'); } } ================================================ FILE: integrations/openclaw/src/index.ts ================================================ import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'; import { handleRecall } from './handlers/recall.js'; import { handleAugmentation } from './handlers/augmentation.js'; import { OpenClawEvent, OpenClawContext, MemoriPluginConfig } from './types.js'; import { PLUGIN_CONFIG } from './constants.js'; import { MemoriLogger } from './utils/index.js'; const memoriPlugin = { id: PLUGIN_CONFIG.ID, name: PLUGIN_CONFIG.NAME, description: 'Hosted memory backend', register(api: OpenClawPluginApi) { const rawConfig = api.pluginConfig; const config: MemoriPluginConfig = { apiKey: rawConfig?.apiKey as string, entityId: rawConfig?.entityId as string, }; if (!config.apiKey || !config.entityId) { api.logger.warn( `${PLUGIN_CONFIG.LOG_PREFIX} Missing apiKey or entityId in config. Plugin disabled.` ); return; } const logger = new MemoriLogger(api); logger.info(`\n=== ${PLUGIN_CONFIG.LOG_PREFIX} INITIALIZING PLUGIN ===`); logger.info(`${PLUGIN_CONFIG.LOG_PREFIX} Tracking Entity ID: ${config.entityId}`); api.on('before_prompt_build', (event: unknown, ctx: unknown) => handleRecall(event as OpenClawEvent, ctx as OpenClawContext, config, logger) ); api.on('agent_end', (event: unknown, ctx: unknown) => handleAugmentation(event as OpenClawEvent, ctx as OpenClawContext, config, logger) ); }, }; export default memoriPlugin; ================================================ FILE: integrations/openclaw/src/sanitizer.ts ================================================ import { OpenClawMessageBlock } from './types.js'; const SYSTEM_MESSAGE_PATTERNS = [ 'a new session was started', '/new or /reset', 'session startup sequence', 'use persona', ] as const; const TIMESTAMP_PREFIX_REGEX = /^\[[A-Z][a-z]{2}\s+\d{4}-\d{2}-\d{2}\s+\d{2}:\d{2}\s+[A-Z]{2,4}\]\s*/; function isMessageBlockArray(value: unknown): value is OpenClawMessageBlock[] { return Array.isArray(value) && value.every((item) => item !== null && typeof item === 'object'); } export function isSystemMessage(text: string): boolean { if (!text) return true; const lowerText = text.toLowerCase(); return SYSTEM_MESSAGE_PATTERNS.some((pattern) => lowerText.includes(pattern)); } /** * Extracts the actual user message from OpenClaw's formatted content. * * OpenClaw wraps metadata in markdown code fences (triple tick). * The actual message is always after the LAST closing fence. * The message might aslo contain a timestamp prefix like: [Day YYYY-MM-DD HH:MM TZ], that will need to be removed */ function extractRawUserMessage(content: string): string { let message = content; if (message.includes('```')) { const lastFenceIndex = message.lastIndexOf('```'); if (lastFenceIndex !== -1) { message = message.substring(lastFenceIndex + 3).trim(); } } const timestampMatch = message.match(TIMESTAMP_PREFIX_REGEX); if (timestampMatch) { message = message.substring(timestampMatch[0].length).trim(); } return message; } function extractMessageText(content: unknown): string { if (!content) return ''; if (typeof content === 'string') { return content; } if (isMessageBlockArray(content)) { return content .filter((block) => (block.type === 'text' || typeof block.text === 'string') && block.text) .map((block) => block.text) .join('\n\n'); } return ''; } export function cleanText(rawContent: unknown): string { let text = extractMessageText(rawContent); if (!text) return ''; text = extractRawUserMessage(text); text = text.replace(/[\s\S]*?<\/memori_context>\s*/g, ''); return text.trim(); } ================================================ FILE: integrations/openclaw/src/types.ts ================================================ export interface MemoriPluginConfig { apiKey: string; entityId: string; } export interface OpenClawMessageBlock { type?: string; text?: string; thinking?: string; [key: string]: unknown; } export interface OpenClawMessage { role: 'user' | 'assistant' | 'system'; content: string | OpenClawMessageBlock[]; timestamp?: number; [key: string]: unknown; } export interface OpenClawEvent { prompt?: string; messages?: OpenClawMessage[]; completion?: string; success?: boolean; error?: string; durationMs?: number; userId?: string; sessionId?: string; messageProvider?: string; } export interface OpenClawContext { agentId?: string; sessionKey?: string; sessionId?: string; workspaceDir?: string; messageProvider?: string; } ================================================ FILE: integrations/openclaw/src/utils/context.ts ================================================ import { OpenClawEvent, OpenClawContext } from '../types.js'; /** * Extracted context information from OpenClaw events */ export interface ExtractedContext { entityId: string; sessionId: string; provider: string; } /** * Extracts and normalizes context information from OpenClaw event and context objects. * Throws an error if required context fields cannot be resolved. * * @param event - OpenClaw event object * @param ctx - OpenClaw context object * @param configuredEntityId - Hardcoded entity ID from plugin config * @returns Normalized context with entityId, sessionId, and provider * @throws Error If entityId, sessionId, or provider cannot be determined */ export function extractContext( event: OpenClawEvent, ctx: OpenClawContext, configuredEntityId: string ): ExtractedContext { const sessionId = ctx.sessionKey || event.sessionId; const provider = ctx.messageProvider || event.messageProvider; if (!sessionId) { throw new Error('Failed to extract context: Missing sessionId in OpenClaw context and event.'); } if (!provider) { throw new Error( 'Failed to extract context: Missing message provider in OpenClaw context and event.' ); } return { entityId: configuredEntityId, sessionId, provider, }; } ================================================ FILE: integrations/openclaw/src/utils/index.ts ================================================ export { extractContext, type ExtractedContext } from './context.js'; export { MemoriLogger } from './logger.js'; export { initializeMemoriClient } from './memori-client.js'; ================================================ FILE: integrations/openclaw/src/utils/logger.ts ================================================ import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'; import { PLUGIN_CONFIG } from '../constants.js'; export class MemoriLogger { constructor(private api: OpenClawPluginApi) {} private prefix(msg: string): string { return `${PLUGIN_CONFIG.LOG_PREFIX} ${msg}`; } info(message: string): void { this.api.logger.info(this.prefix(message)); } warn(message: string): void { this.api.logger.warn(this.prefix(message)); } error(message: string): void { this.api.logger.error(this.prefix(message)); } section(title: string): void { this.api.logger.info(`\n=== ${this.prefix(title)} ===`); } endSection(title: string): void { this.api.logger.info(`=== ${this.prefix(title)} ===\n`); } } ================================================ FILE: integrations/openclaw/src/utils/memori-client.ts ================================================ import { Memori } from '@memorilabs/memori'; import { OpenClawIntegration } from '@memorilabs/memori/integrations'; import { ExtractedContext } from './context.js'; /** * Initializes and configures a Memori OpenClaw integration instance * * @param apiKey - Memori API key * @param context - Extracted context information * @returns Configured OpenClawIntegration instance */ export function initializeMemoriClient( apiKey: string, context: ExtractedContext ): OpenClawIntegration { const memori = new Memori(); memori.config.apiKey = apiKey; const openclaw = memori.integrate(OpenClawIntegration); openclaw.setAttribution(context.entityId, context.provider); openclaw.setSession(context.sessionId); return openclaw; } ================================================ FILE: integrations/openclaw/src/version.ts ================================================ // This file is auto-generated during CI builds. export const SDK_VERSION = '0.0.0'; ================================================ FILE: integrations/openclaw/tests/handlers/augmentation.test.ts ================================================ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { handleAugmentation } from '../../src/handlers/augmentation.js'; import type { OpenClawEvent, OpenClawContext, MemoriPluginConfig, OpenClawMessage, } from '../../src/types.js'; import type { MemoriLogger } from '../../src/utils/logger.js'; import { SDK_VERSION } from '../../src/version.js'; vi.mock('../../src/sanitizer.js', () => ({ cleanText: vi.fn((content) => { if (typeof content === 'string') return content; return 'cleaned text'; }), isSystemMessage: vi.fn(() => false), })); vi.mock('../../src/utils/index.js', () => ({ extractContext: vi.fn(() => ({ entityId: 'test-entity', sessionId: 'test-session', provider: 'test-provider', })), initializeMemoriClient: vi.fn(() => ({ augmentation: vi.fn(async () => {}), })), })); describe('handlers/augmentation', () => { let mockLogger: MemoriLogger; let config: MemoriPluginConfig; let event: OpenClawEvent; let ctx: OpenClawContext; beforeEach(() => { vi.clearAllMocks(); mockLogger = { section: vi.fn(), endSection: vi.fn(), info: vi.fn(), error: vi.fn(), } as unknown as MemoriLogger; config = { apiKey: 'test-api-key', entityId: 'test-entity-id', }; event = { success: true, messages: [ { role: 'user', content: 'Hello, how are you?' }, { role: 'assistant', content: "I'm doing well, thank you!" }, ], }; ctx = { sessionKey: 'session-123', messageProvider: 'test-provider', }; }); describe('successful augmentation', () => { it('should send user and assistant messages to memori', async () => { const { initializeMemoriClient } = await import('../../src/utils/index.js'); await handleAugmentation(event, ctx, config, mockLogger); const client = vi.mocked(initializeMemoriClient).mock.results[0].value; expect(client.augmentation).toHaveBeenCalledWith( expect.objectContaining({ userMessage: 'Hello, how are you?', agentResponse: "I'm doing well, thank you!", }) ); }); it('should include metadata in request', async () => { const { initializeMemoriClient } = await import('../../src/utils/index.js'); await handleAugmentation(event, ctx, config, mockLogger); const client = vi.mocked(initializeMemoriClient).mock.results[0].value; expect(client.augmentation).toHaveBeenCalledWith( expect.objectContaining({ metadata: expect.objectContaining({ platform: 'openclaw', integrationSdkVersion: SDK_VERSION, }), }) ); }); }); describe('event validation', () => { it('should skip when event is unsuccessful', async () => { event.success = false; await handleAugmentation(event, ctx, config, mockLogger); expect(mockLogger.info).toHaveBeenCalledWith( 'No messages or unsuccessful event. Skipping augmentation.' ); const { initializeMemoriClient } = await import('../../src/utils/index.js'); expect(initializeMemoriClient).not.toHaveBeenCalled(); }); it('should skip when messages array is empty', async () => { event.messages = []; await handleAugmentation(event, ctx, config, mockLogger); expect(mockLogger.info).toHaveBeenCalledWith( 'No messages or unsuccessful event. Skipping augmentation.' ); }); it('should skip when messages has only one message', async () => { event.messages = [{ role: 'user', content: 'Hello' }]; await handleAugmentation(event, ctx, config, mockLogger); expect(mockLogger.info).toHaveBeenCalledWith( 'No messages or unsuccessful event. Skipping augmentation.' ); }); it('should skip when messages is undefined', async () => { event.messages = undefined; await handleAugmentation(event, ctx, config, mockLogger); expect(mockLogger.info).toHaveBeenCalledWith( 'No messages or unsuccessful event. Skipping augmentation.' ); }); }); describe('message extraction', () => { it('should extract last user and assistant messages from recent history', async () => { event.messages = [ { role: 'user', content: 'First message' }, { role: 'assistant', content: 'First response' }, { role: 'user', content: 'Second message' }, { role: 'assistant', content: 'Second response' }, { role: 'user', content: 'Third message' }, { role: 'assistant', content: 'Third response' }, ]; const { initializeMemoriClient } = await import('../../src/utils/index.js'); await handleAugmentation(event, ctx, config, mockLogger); const client = vi.mocked(initializeMemoriClient).mock.results[0].value; expect(client.augmentation).toHaveBeenCalledWith( expect.objectContaining({ userMessage: 'Third message', agentResponse: 'Third response', }) ); }); it('should only consider last 5 messages', async () => { event.messages = [ { role: 'user', content: 'Message 1' }, { role: 'assistant', content: 'Response 1' }, { role: 'user', content: 'Message 2' }, { role: 'assistant', content: 'Response 2' }, { role: 'user', content: 'Message 3' }, { role: 'assistant', content: 'Response 3' }, { role: 'user', content: 'Message 4' }, { role: 'assistant', content: 'Response 4' }, { role: 'user', content: 'Latest message' }, { role: 'assistant', content: 'Latest response' }, ]; const { initializeMemoriClient } = await import('../../src/utils/index.js'); await handleAugmentation(event, ctx, config, mockLogger); const client = vi.mocked(initializeMemoriClient).mock.results[0].value; expect(client.augmentation).toHaveBeenCalledWith( expect.objectContaining({ userMessage: 'Latest message', agentResponse: 'Latest response', }) ); }); it('should skip system role messages', async () => { event.messages = [ { role: 'system', content: 'System prompt' }, { role: 'user', content: 'User message' }, { role: 'assistant', content: 'Assistant response' }, ]; const { initializeMemoriClient } = await import('../../src/utils/index.js'); await handleAugmentation(event, ctx, config, mockLogger); const client = vi.mocked(initializeMemoriClient).mock.results[0].value; expect(client.augmentation).toHaveBeenCalledWith( expect.objectContaining({ userMessage: 'User message', agentResponse: 'Assistant response', }) ); }); it('should skip when user or assistant message is missing', async () => { event.messages = [ { role: 'system', content: 'Sys' }, { role: 'user', content: 'Only user message' }, ]; await handleAugmentation(event, ctx, config, mockLogger); expect(mockLogger.info).toHaveBeenCalledWith('Missing user or assistant message. Skipping.'); const { initializeMemoriClient } = await import('../../src/utils/index.js'); expect(initializeMemoriClient).not.toHaveBeenCalled(); }); it('should skip messages with empty cleaned content', async () => { const { cleanText } = await import('../../src/sanitizer.js'); vi.mocked(cleanText).mockReturnValueOnce(''); event.messages = [ { role: 'user', content: 'Some content' }, { role: 'assistant', content: 'Some response' }, ]; await handleAugmentation(event, ctx, config, mockLogger); expect(mockLogger.info).toHaveBeenCalledWith('Missing user or assistant message. Skipping.'); }); }); describe('system message filtering', () => { it('should skip when user message is a system message', async () => { const { isSystemMessage } = await import('../../src/sanitizer.js'); vi.mocked(isSystemMessage).mockReturnValueOnce(true); await handleAugmentation(event, ctx, config, mockLogger); expect(mockLogger.info).toHaveBeenCalledWith( 'User message is a system message. Skipping augmentation.' ); const { initializeMemoriClient } = await import('../../src/utils/index.js'); expect(initializeMemoriClient).not.toHaveBeenCalled(); }); }); describe('synthetic responses', () => { it('should replace NO_REPLY with synthetic response', async () => { event.messages = [ { role: 'user', content: 'Remember my name is John' }, { role: 'assistant', content: 'NO_REPLY' }, ]; const { initializeMemoriClient } = await import('../../src/utils/index.js'); await handleAugmentation(event, ctx, config, mockLogger); const client = vi.mocked(initializeMemoriClient).mock.results[0].value; expect(client.augmentation).toHaveBeenCalledWith( expect.objectContaining({ agentResponse: "Okay, I'll remember that for you.", }) ); expect(mockLogger.info).toHaveBeenCalledWith( 'Assistant used tool-based messaging. Using synthetic response.' ); }); it('should replace SILENT_REPLY with synthetic response', async () => { event.messages = [ { role: 'user', content: 'Save this for later' }, { role: 'assistant', content: 'SILENT_REPLY' }, ]; const { initializeMemoriClient } = await import('../../src/utils/index.js'); await handleAugmentation(event, ctx, config, mockLogger); const client = vi.mocked(initializeMemoriClient).mock.results[0].value; expect(client.augmentation).toHaveBeenCalledWith( expect.objectContaining({ agentResponse: "Okay, I'll remember that for you.", }) ); }); }); describe('thinking block removal', () => { it('should strip thinking blocks from assistant messages', async () => { const { cleanText } = await import('../../src/sanitizer.js'); vi.mocked(cleanText).mockImplementationOnce((content) => { if (typeof content === 'string' && content.includes('[[')) { return '[[This is my thought process.]]\n\nActual response here.'; } return content as string; }); event.messages = [ { role: 'user', content: 'Question?' }, { role: 'assistant', content: '[[This is my thought process.]]\n\nActual response here.', }, ]; const { initializeMemoriClient } = await import('../../src/utils/index.js'); await handleAugmentation(event, ctx, config, mockLogger); const client = vi.mocked(initializeMemoriClient).mock.results[0].value; expect(client.augmentation).toHaveBeenCalledWith( expect.objectContaining({ agentResponse: 'Actual response here.', }) ); }); }); describe('metadata extraction', () => { it('should extract provider and model from last assistant message', async () => { event.messages = [ { role: 'user', content: 'Question' }, { role: 'assistant', content: 'Answer', provider: 'anthropic' as any, model: 'claude-3-sonnet' as any, }, ]; const { initializeMemoriClient } = await import('../../src/utils/index.js'); await handleAugmentation(event, ctx, config, mockLogger); const client = vi.mocked(initializeMemoriClient).mock.results[0].value; expect(client.augmentation).toHaveBeenCalledWith( expect.objectContaining({ metadata: expect.objectContaining({ provider: 'anthropic', model: 'claude-3-sonnet', }), }) ); }); it('should use null for missing provider and model', async () => { const { initializeMemoriClient } = await import('../../src/utils/index.js'); await handleAugmentation(event, ctx, config, mockLogger); const client = vi.mocked(initializeMemoriClient).mock.results[0].value; expect(client.augmentation).toHaveBeenCalledWith( expect.objectContaining({ metadata: expect.objectContaining({ provider: null, model: null, }), }) ); }); }); describe('error handling', () => { it('should handle errors gracefully', async () => { const { extractContext } = await import('../../src/utils/index.js'); vi.mocked(extractContext).mockImplementationOnce(() => { throw new Error('Context extraction failed'); }); await handleAugmentation(event, ctx, config, mockLogger); expect(mockLogger.error).toHaveBeenCalledWith( 'Augmentation failed: Context extraction failed' ); }); it('should log non-Error objects as strings', async () => { const { extractContext } = await import('../../src/utils/index.js'); vi.mocked(extractContext).mockImplementationOnce(() => { throw 'String error'; }); await handleAugmentation(event, ctx, config, mockLogger); expect(mockLogger.error).toHaveBeenCalledWith('Augmentation failed: String error'); }); it('should handle API errors from memori client', async () => { const { initializeMemoriClient } = await import('../../src/utils/index.js'); vi.mocked(initializeMemoriClient).mockReturnValueOnce({ augmentation: vi.fn(async () => { throw new Error('API connection failed'); }), } as any); await handleAugmentation(event, ctx, config, mockLogger); expect(mockLogger.error).toHaveBeenCalledWith('Augmentation failed: API connection failed'); }); }); }); ================================================ FILE: integrations/openclaw/tests/handlers/recall.test.ts ================================================ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { handleRecall } from '../../src/handlers/recall.js'; import type { OpenClawEvent, OpenClawContext, MemoriPluginConfig } from '../../src/types.js'; import type { MemoriLogger } from '../../src/utils/logger.js'; vi.mock('../../src/sanitizer.js', () => ({ cleanText: vi.fn((text) => text), isSystemMessage: vi.fn(() => false), })); vi.mock('../../src/utils/index.js', () => ({ extractContext: vi.fn(() => ({ entityId: 'test-entity', sessionId: 'test-session', provider: 'test-provider', })), initializeMemoriClient: vi.fn(() => ({ recall: vi.fn(async () => 'Relevant memories'), })), })); describe('handlers/recall', () => { let mockLogger: MemoriLogger; let config: MemoriPluginConfig; let event: OpenClawEvent; let ctx: OpenClawContext; beforeEach(() => { vi.clearAllMocks(); mockLogger = { section: vi.fn(), endSection: vi.fn(), info: vi.fn(), error: vi.fn(), } as unknown as MemoriLogger; config = { apiKey: 'test-api-key', entityId: 'test-entity-id', }; event = { prompt: 'What is the weather today?', }; ctx = { sessionKey: 'session-123', messageProvider: 'test-provider', }; }); describe('successful recall', () => { it('should return prependContext when memories are found', async () => { const result = await handleRecall(event, ctx, config, mockLogger); expect(result).toEqual({ prependContext: 'Relevant memories', }); }); it('should call extractContext with correct parameters', async () => { const { extractContext } = await import('../../src/utils/index.js'); await handleRecall(event, ctx, config, mockLogger); expect(extractContext).toHaveBeenCalledWith(event, ctx, 'test-entity-id'); }); it('should call initializeMemoriClient with correct parameters', async () => { const { initializeMemoriClient } = await import('../../src/utils/index.js'); await handleRecall(event, ctx, config, mockLogger); expect(initializeMemoriClient).toHaveBeenCalledWith('test-api-key', { entityId: 'test-entity', sessionId: 'test-session', provider: 'test-provider', }); }); }); describe('no memories found', () => { it('should return undefined when memori returns empty string', async () => { const { initializeMemoriClient } = await import('../../src/utils/index.js'); vi.mocked(initializeMemoriClient).mockReturnValueOnce({ recall: vi.fn(async () => ''), } as any); const result = await handleRecall(event, ctx, config, mockLogger); expect(result).toBeUndefined(); expect(mockLogger.info).toHaveBeenCalledWith('No relevant memories found.'); }); it('should return undefined when memori returns null', async () => { const { initializeMemoriClient } = await import('../../src/utils/index.js'); vi.mocked(initializeMemoriClient).mockReturnValueOnce({ recall: vi.fn(async () => null), } as any); const result = await handleRecall(event, ctx, config, mockLogger); expect(result).toBeUndefined(); }); }); describe('prompt validation', () => { it('should abort when prompt is empty after cleaning', async () => { const { cleanText } = await import('../../src/sanitizer.js'); vi.mocked(cleanText).mockReturnValueOnce(''); const result = await handleRecall(event, ctx, config, mockLogger); expect(result).toBeUndefined(); expect(mockLogger.info).toHaveBeenCalledWith( 'Prompt too short or is a system message. Aborting recall.' ); }); it('should abort when prompt is too short', async () => { const { cleanText } = await import('../../src/sanitizer.js'); vi.mocked(cleanText).mockReturnValueOnce('a'); const result = await handleRecall(event, ctx, config, mockLogger); expect(result).toBeUndefined(); expect(mockLogger.info).toHaveBeenCalledWith( 'Prompt too short or is a system message. Aborting recall.' ); }); it('should abort when prompt is a system message', async () => { const { isSystemMessage } = await import('../../src/sanitizer.js'); vi.mocked(isSystemMessage).mockReturnValueOnce(true); const result = await handleRecall(event, ctx, config, mockLogger); expect(result).toBeUndefined(); expect(mockLogger.info).toHaveBeenCalledWith( 'Prompt too short or is a system message. Aborting recall.' ); }); it('should proceed when prompt meets minimum length', async () => { const { cleanText } = await import('../../src/sanitizer.js'); vi.mocked(cleanText).mockReturnValueOnce('Hello'); const result = await handleRecall(event, ctx, config, mockLogger); expect(result).toBeDefined(); }); }); describe('error handling', () => { it('should handle errors gracefully and return undefined', async () => { const { extractContext } = await import('../../src/utils/index.js'); vi.mocked(extractContext).mockImplementationOnce(() => { throw new Error('Context extraction failed'); }); const result = await handleRecall(event, ctx, config, mockLogger); expect(result).toBeUndefined(); expect(mockLogger.error).toHaveBeenCalledWith('Recall failed: Context extraction failed'); }); it('should log non-Error objects as strings', async () => { const { extractContext } = await import('../../src/utils/index.js'); vi.mocked(extractContext).mockImplementationOnce(() => { throw 'String error'; }); await handleRecall(event, ctx, config, mockLogger); expect(mockLogger.error).toHaveBeenCalledWith('Recall failed: String error'); }); it('should handle API errors from memori client', async () => { const { initializeMemoriClient } = await import('../../src/utils/index.js'); vi.mocked(initializeMemoriClient).mockReturnValueOnce({ recall: vi.fn(async () => { throw new Error('API connection failed'); }), } as any); const result = await handleRecall(event, ctx, config, mockLogger); expect(result).toBeUndefined(); expect(mockLogger.error).toHaveBeenCalledWith('Recall failed: API connection failed'); }); }); describe('edge cases', () => { it('should handle missing prompt gracefully', async () => { const eventWithoutPrompt: OpenClawEvent = {}; const result = await handleRecall(eventWithoutPrompt, ctx, config, mockLogger); expect(result).toBeUndefined(); }); it('should handle null prompt', async () => { const eventWithNullPrompt: OpenClawEvent = { prompt: null as any }; const result = await handleRecall(eventWithNullPrompt, ctx, config, mockLogger); expect(result).toBeUndefined(); }); it('should call cleanText with the prompt', async () => { const { cleanText } = await import('../../src/sanitizer.js'); await handleRecall(event, ctx, config, mockLogger); expect(cleanText).toHaveBeenCalledWith('What is the weather today?'); }); }); }); ================================================ FILE: integrations/openclaw/tests/index.test.ts ================================================ import { describe, it, expect, vi, beforeEach } from 'vitest'; import memoriPlugin from '../src/index.js'; import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'; vi.mock('../src/handlers/recall.js', () => ({ handleRecall: vi.fn(), })); vi.mock('../src/handlers/augmentation.js', () => ({ handleAugmentation: vi.fn(), })); describe('plugin index', () => { let mockApi: OpenClawPluginApi; beforeEach(() => { vi.clearAllMocks(); mockApi = { pluginConfig: { apiKey: 'test-api-key', entityId: 'test-entity-id', }, logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), }, on: vi.fn(), } as unknown as OpenClawPluginApi; }); describe('plugin metadata', () => { it('should have correct id', () => { expect(memoriPlugin.id).toBe('openclaw-memori'); }); it('should have correct name', () => { expect(memoriPlugin.name).toBe('Memori System'); }); it('should have description', () => { expect(memoriPlugin.description).toBe('Hosted memory backend'); }); }); describe('register', () => { it('should register hooks when config is valid', () => { memoriPlugin.register(mockApi); expect(mockApi.on).toHaveBeenCalledWith('before_prompt_build', expect.any(Function)); expect(mockApi.on).toHaveBeenCalledWith('agent_end', expect.any(Function)); expect(mockApi.on).toHaveBeenCalledTimes(2); }); it('should not register when apiKey is missing', () => { mockApi.pluginConfig = { entityId: 'test-entity-id', }; memoriPlugin.register(mockApi); expect(mockApi.logger.warn).toHaveBeenCalledWith( expect.stringContaining('Missing apiKey or entityId') ); expect(mockApi.on).not.toHaveBeenCalled(); }); it('should not register when entityId is missing', () => { mockApi.pluginConfig = { apiKey: 'test-api-key', }; memoriPlugin.register(mockApi); expect(mockApi.logger.warn).toHaveBeenCalledWith( expect.stringContaining('Missing apiKey or entityId') ); expect(mockApi.on).not.toHaveBeenCalled(); }); it('should not register when both apiKey and entityId are missing', () => { mockApi.pluginConfig = {}; memoriPlugin.register(mockApi); expect(mockApi.logger.warn).toHaveBeenCalledWith( expect.stringContaining('Missing apiKey or entityId') ); expect(mockApi.on).not.toHaveBeenCalled(); }); it('should not register when pluginConfig is undefined', () => { mockApi.pluginConfig = undefined; memoriPlugin.register(mockApi); expect(mockApi.logger.warn).toHaveBeenCalledWith( expect.stringContaining('Missing apiKey or entityId') ); expect(mockApi.on).not.toHaveBeenCalled(); }); it('should not register when apiKey is empty string', () => { mockApi.pluginConfig = { apiKey: '', entityId: 'test-entity-id', }; memoriPlugin.register(mockApi); expect(mockApi.logger.warn).toHaveBeenCalledWith( expect.stringContaining('Missing apiKey or entityId') ); expect(mockApi.on).not.toHaveBeenCalled(); }); it('should not register when entityId is empty string', () => { mockApi.pluginConfig = { apiKey: 'test-api-key', entityId: '', }; memoriPlugin.register(mockApi); expect(mockApi.logger.warn).toHaveBeenCalledWith( expect.stringContaining('Missing apiKey or entityId') ); expect(mockApi.on).not.toHaveBeenCalled(); }); }); describe('hook handlers', () => { it('should call handleRecall for before_prompt_build event', async () => { const { handleRecall } = await import('../src/handlers/recall.js'); memoriPlugin.register(mockApi); const beforePromptBuildHandler = vi .mocked(mockApi.on) .mock.calls.find((call) => call[0] === 'before_prompt_build')?.[1]; expect(beforePromptBuildHandler).toBeDefined(); const mockEvent = { prompt: 'test' } as any; const mockCtx = { sessionKey: 'session-123' } as any; await beforePromptBuildHandler?.(mockEvent, mockCtx); expect(handleRecall).toHaveBeenCalledWith( mockEvent, mockCtx, { apiKey: 'test-api-key', entityId: 'test-entity-id' }, expect.any(Object) ); }); it('should call handleAugmentation for agent_end event', async () => { const { handleAugmentation } = await import('../src/handlers/augmentation.js'); memoriPlugin.register(mockApi); const agentEndHandler = vi .mocked(mockApi.on) .mock.calls.find((call) => call[0] === 'agent_end')?.[1]; expect(agentEndHandler).toBeDefined(); const mockEvent = { success: true, messages: [] } as any; const mockCtx = { sessionKey: 'session-123' } as any; await agentEndHandler?.(mockEvent, mockCtx); expect(handleAugmentation).toHaveBeenCalledWith( mockEvent, mockCtx, { apiKey: 'test-api-key', entityId: 'test-entity-id' }, expect.any(Object) ); }); }); describe('configuration handling', () => { it('should extract apiKey from pluginConfig', () => { mockApi.pluginConfig = { apiKey: 'custom-key-123', entityId: 'entity-456', }; memoriPlugin.register(mockApi); expect(mockApi.on).toHaveBeenCalled(); }); it('should handle additional config properties gracefully', () => { mockApi.pluginConfig = { apiKey: 'test-api-key', entityId: 'test-entity-id', extraProperty: 'should be ignored', anotherExtra: 12345, }; memoriPlugin.register(mockApi); expect(mockApi.on).toHaveBeenCalledTimes(2); }); }); describe('logger creation', () => { it('should create MemoriLogger with api', () => { memoriPlugin.register(mockApi); expect(mockApi.logger.info).toHaveBeenCalledWith(expect.stringContaining('[Memori]')); }); }); }); ================================================ FILE: integrations/openclaw/tests/sanitizer.test.ts ================================================ import { describe, it, expect } from 'vitest'; import { isSystemMessage, cleanText } from '../src/sanitizer.js'; import type { OpenClawMessageBlock } from '../src/types.js'; describe('sanitizer', () => { describe('isSystemMessage', () => { it('should return true for empty text', () => { expect(isSystemMessage('')).toBe(true); }); it('should return true for system startup messages', () => { expect(isSystemMessage('a new session was started')).toBe(true); expect(isSystemMessage('A NEW SESSION WAS STARTED')).toBe(true); }); it('should return true for reset command messages', () => { expect(isSystemMessage('/new or /reset')).toBe(true); expect(isSystemMessage('Use /new or /reset to start fresh')).toBe(true); }); it('should return true for session startup sequence', () => { expect(isSystemMessage('session startup sequence initiated')).toBe(true); }); it('should return true for persona messages', () => { expect(isSystemMessage('use persona: helpful assistant')).toBe(true); }); it('should return false for regular user messages', () => { expect(isSystemMessage('Hello, how are you?')).toBe(false); expect(isSystemMessage('What is the weather today?')).toBe(false); expect(isSystemMessage('Can you help me with this code?')).toBe(false); }); }); describe('cleanText', () => { describe('string content', () => { it('should return the string as-is when no special formatting', () => { const input = 'Hello, world!'; expect(cleanText(input)).toBe('Hello, world!'); }); it('should extract message after last code fence', () => { const input = '```metadata\nsome: data\n```\nActual message here'; expect(cleanText(input)).toBe('Actual message here'); }); it('should remove timestamp prefix from message', () => { const input = '```metadata```\n[Mon 2024-03-09 14:30 UTC] Hello there'; expect(cleanText(input)).toBe('Hello there'); }); it('should handle timestamp with timezone offset', () => { const input = '[Tue 2024-03-10 09:15 EST] Good morning'; expect(cleanText(input)).toBe('Good morning'); }); it('should remove memori_context tags', () => { const input = 'Previous facts\nNew message'; expect(cleanText(input)).toBe('New message'); }); it('should handle multiple memori_context blocks', () => { const input = 'Block 1\nText\nBlock 2'; expect(cleanText(input)).toBe('Text'); }); it('should handle complex combination of formatting', () => { const input = `\`\`\`metadata sessionId: abc-123 \`\`\` [Wed 2024-03-11 16:45 UTC] Previous context What is the capital of France?`; expect(cleanText(input)).toBe('What is the capital of France?'); }); it('should return empty string when only metadata present', () => { const input = '```metadata\ndata\n```'; expect(cleanText(input)).toBe(''); }); it('should handle content without code fences', () => { const input = '[Thu 2024-03-12 10:00 PST] Hello'; expect(cleanText(input)).toBe('Hello'); }); }); describe('message block array content', () => { it('should extract text from text blocks', () => { const blocks: OpenClawMessageBlock[] = [ { type: 'text', text: 'First part' }, { type: 'text', text: 'Second part' }, ]; expect(cleanText(blocks)).toBe('First part\n\nSecond part'); }); it('should filter out non-text blocks', () => { const blocks: OpenClawMessageBlock[] = [ { type: 'text', text: 'User message' }, { type: 'thinking', thinking: 'Internal thought' }, { type: 'tool_use', name: 'search' }, ]; expect(cleanText(blocks)).toBe('User message'); }); it('should handle blocks without explicit type but with text', () => { const blocks: OpenClawMessageBlock[] = [{ text: 'Message 1' }, { text: 'Message 2' }]; expect(cleanText(blocks)).toBe('Message 1\n\nMessage 2'); }); it('should filter out empty text blocks', () => { const blocks: OpenClawMessageBlock[] = [ { type: 'text', text: '' }, { type: 'text', text: 'Valid message' }, { type: 'text', text: '' }, ]; expect(cleanText(blocks)).toBe('Valid message'); }); it('should handle empty array', () => { expect(cleanText([])).toBe(''); }); it('should apply post-processing to extracted text', () => { const blocks: OpenClawMessageBlock[] = [ { type: 'text', text: '```metadata```\n[Fri 2024-03-13 11:20 UTC] Clean this message', }, ]; expect(cleanText(blocks)).toBe('Clean this message'); }); }); describe('edge cases', () => { it('should return empty string for null input', () => { expect(cleanText(null)).toBe(''); }); it('should return empty string for undefined input', () => { expect(cleanText(undefined)).toBe(''); }); it('should handle objects that are not arrays or strings', () => { expect(cleanText({ random: 'object' })).toBe(''); }); it('should handle number input', () => { expect(cleanText(42)).toBe(''); }); it('should trim whitespace from final result', () => { expect(cleanText(' \n Hello \n ')).toBe('Hello'); }); it('should preserve internal whitespace', () => { const input = 'Hello world\n\nNew paragraph'; expect(cleanText(input)).toBe('Hello world\n\nNew paragraph'); }); }); }); }); ================================================ FILE: integrations/openclaw/tests/utils/context.test.ts ================================================ import { describe, it, expect } from 'vitest'; import { extractContext } from '../../src/utils/context.js'; import type { OpenClawEvent, OpenClawContext } from '../../src/types.js'; describe('utils/context', () => { describe('extractContext', () => { const configuredEntityId = 'test-entity-123'; it('should extract context successfully with all required fields', () => { const event: OpenClawEvent = { sessionId: 'event-session-456', messageProvider: 'event-provider', }; const ctx: OpenClawContext = { sessionKey: 'ctx-session-789', messageProvider: 'ctx-provider', }; const result = extractContext(event, ctx, configuredEntityId); expect(result).toEqual({ entityId: 'test-entity-123', sessionId: 'ctx-session-789', provider: 'ctx-provider', }); }); it('should use event sessionId when ctx.sessionKey is missing', () => { const event: OpenClawEvent = { sessionId: 'event-session-456', messageProvider: 'event-provider', }; const ctx: OpenClawContext = { messageProvider: 'ctx-provider', }; const result = extractContext(event, ctx, configuredEntityId); expect(result.sessionId).toBe('event-session-456'); }); it('should use event messageProvider when ctx.messageProvider is missing', () => { const event: OpenClawEvent = { sessionId: 'event-session-456', messageProvider: 'event-provider', }; const ctx: OpenClawContext = { sessionKey: 'ctx-session-789', }; const result = extractContext(event, ctx, configuredEntityId); expect(result.provider).toBe('event-provider'); }); it('should throw error when sessionId cannot be determined', () => { const event: OpenClawEvent = { messageProvider: 'event-provider', }; const ctx: OpenClawContext = { messageProvider: 'ctx-provider', }; expect(() => extractContext(event, ctx, configuredEntityId)).toThrow( 'Failed to extract context: Missing sessionId in OpenClaw context and event.' ); }); it('should throw error when provider cannot be determined', () => { const event: OpenClawEvent = { sessionId: 'event-session-456', }; const ctx: OpenClawContext = { sessionKey: 'ctx-session-789', }; expect(() => extractContext(event, ctx, configuredEntityId)).toThrow( 'Failed to extract context: Missing message provider in OpenClaw context and event.' ); }); it('should throw error when both sessionId and provider are missing', () => { const event: OpenClawEvent = {}; const ctx: OpenClawContext = {}; expect(() => extractContext(event, ctx, configuredEntityId)).toThrow(); }); it('should handle empty string sessionId as missing', () => { const event: OpenClawEvent = { messageProvider: 'provider', }; const ctx: OpenClawContext = { sessionKey: '', messageProvider: 'ctx-provider', }; expect(() => extractContext(event, ctx, configuredEntityId)).toThrow( 'Failed to extract context: Missing sessionId' ); }); it('should handle empty string provider as missing', () => { const event: OpenClawEvent = { sessionId: 'session-123', }; const ctx: OpenClawContext = { sessionKey: 'ctx-session-789', messageProvider: '', }; expect(() => extractContext(event, ctx, configuredEntityId)).toThrow( 'Failed to extract context: Missing message provider' ); }); it('should always use configuredEntityId from plugin config', () => { const event: OpenClawEvent = { sessionId: 'session-123', messageProvider: 'provider', userId: 'different-user-id', }; const ctx: OpenClawContext = { sessionKey: 'session-789', messageProvider: 'ctx-provider', agentId: 'agent-id', }; const result = extractContext(event, ctx, 'my-configured-entity'); expect(result.entityId).toBe('my-configured-entity'); }); }); }); ================================================ FILE: integrations/openclaw/tests/utils/logger.test.ts ================================================ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { MemoriLogger } from '../../src/utils/logger.js'; import type { OpenClawPluginApi } from 'openclaw/plugin-sdk'; describe('utils/logger', () => { let mockApi: OpenClawPluginApi; let logger: MemoriLogger; beforeEach(() => { mockApi = { logger: { info: vi.fn(), warn: vi.fn(), error: vi.fn(), }, } as unknown as OpenClawPluginApi; logger = new MemoriLogger(mockApi); }); describe('info', () => { it('should call api.logger.info with prefixed message', () => { logger.info('Test message'); expect(mockApi.logger.info).toHaveBeenCalledWith('[Memori] Test message'); }); it('should handle empty strings', () => { logger.info(''); expect(mockApi.logger.info).toHaveBeenCalledWith('[Memori] '); }); it('should preserve message content exactly', () => { logger.info('Message with spaces\nand newlines'); expect(mockApi.logger.info).toHaveBeenCalledWith( '[Memori] Message with spaces\nand newlines' ); }); }); describe('warn', () => { it('should call api.logger.warn with prefixed message', () => { logger.warn('Warning message'); expect(mockApi.logger.warn).toHaveBeenCalledWith('[Memori] Warning message'); }); }); describe('error', () => { it('should call api.logger.error with prefixed message', () => { logger.error('Error message'); expect(mockApi.logger.error).toHaveBeenCalledWith('[Memori] Error message'); }); it('should handle error messages with special characters', () => { logger.error('Error: Failed to connect (code: 500)'); expect(mockApi.logger.error).toHaveBeenCalledWith( '[Memori] Error: Failed to connect (code: 500)' ); }); }); describe('section', () => { it('should log section start with formatting', () => { logger.section('HOOK START'); expect(mockApi.logger.info).toHaveBeenCalledWith('\n=== [Memori] HOOK START ==='); }); it('should handle multi-word section titles', () => { logger.section('AUGMENTATION HOOK START'); expect(mockApi.logger.info).toHaveBeenCalledWith( '\n=== [Memori] AUGMENTATION HOOK START ===' ); }); }); describe('endSection', () => { it('should log section end with formatting', () => { logger.endSection('HOOK END'); expect(mockApi.logger.info).toHaveBeenCalledWith('=== [Memori] HOOK END ===\n'); }); }); describe('prefix consistency', () => { it('should use consistent prefix across all log levels', () => { logger.info('info'); logger.warn('warn'); logger.error('error'); expect(mockApi.logger.info).toHaveBeenCalledWith('[Memori] info'); expect(mockApi.logger.warn).toHaveBeenCalledWith('[Memori] warn'); expect(mockApi.logger.error).toHaveBeenCalledWith('[Memori] error'); }); }); describe('call count verification', () => { it('should call underlying logger exactly once per method call', () => { logger.info('test'); expect(mockApi.logger.info).toHaveBeenCalledTimes(1); logger.warn('test'); expect(mockApi.logger.warn).toHaveBeenCalledTimes(1); logger.error('test'); expect(mockApi.logger.error).toHaveBeenCalledTimes(1); }); it('should accumulate calls correctly', () => { logger.info('first'); logger.info('second'); logger.info('third'); expect(mockApi.logger.info).toHaveBeenCalledTimes(3); }); }); }); ================================================ FILE: integrations/openclaw/tests/utils/memori-client.test.ts ================================================ import { describe, it, expect, vi, beforeEach } from 'vitest'; import { initializeMemoriClient } from '../../src/utils/memori-client.js'; import type { ExtractedContext } from '../../src/utils/context.js'; vi.mock('@memorilabs/memori', () => { const mockOpenClawIntegration = { setAttribution: vi.fn(), setSession: vi.fn(), }; const mockMemori = { config: {}, integrate: vi.fn(() => mockOpenClawIntegration), }; return { Memori: vi.fn(function () { return mockMemori; }), }; }); vi.mock('@memorilabs/memori/integrations', () => ({ OpenClawIntegration: class MockOpenClawIntegration {}, })); describe('utils/memori-client', () => { beforeEach(() => { vi.clearAllMocks(); }); describe('initializeMemoriClient', () => { it('should create Memori instance with API key', async () => { const { Memori } = await import('@memorilabs/memori'); const apiKey = 'test-api-key-123'; const context: ExtractedContext = { entityId: 'entity-456', sessionId: 'session-789', provider: 'test-provider', }; initializeMemoriClient(apiKey, context); expect(Memori).toHaveBeenCalled(); const memoriInstance = vi.mocked(Memori).mock.results[0].value; expect(memoriInstance.config.apiKey).toBe('test-api-key-123'); }); it('should integrate with OpenClawIntegration', async () => { const { Memori } = await import('@memorilabs/memori'); const { OpenClawIntegration } = await import('@memorilabs/memori/integrations'); const apiKey = 'test-api-key'; const context: ExtractedContext = { entityId: 'entity-123', sessionId: 'session-456', provider: 'provider-789', }; initializeMemoriClient(apiKey, context); const memoriInstance = vi.mocked(Memori).mock.results[0].value; expect(memoriInstance.integrate).toHaveBeenCalledWith(OpenClawIntegration); }); it('should set attribution with entityId and provider', async () => { const { Memori } = await import('@memorilabs/memori'); const apiKey = 'test-api-key'; const context: ExtractedContext = { entityId: 'user-abc', sessionId: 'session-xyz', provider: 'openai', }; initializeMemoriClient(apiKey, context); const memoriInstance = vi.mocked(Memori).mock.results[0].value; const integration = memoriInstance.integrate.mock.results[0].value; expect(integration.setAttribution).toHaveBeenCalledWith('user-abc', 'openai'); }); it('should set session with sessionId', async () => { const { Memori } = await import('@memorilabs/memori'); const apiKey = 'test-api-key'; const context: ExtractedContext = { entityId: 'entity-123', sessionId: 'my-session-id', provider: 'anthropic', }; initializeMemoriClient(apiKey, context); const memoriInstance = vi.mocked(Memori).mock.results[0].value; const integration = memoriInstance.integrate.mock.results[0].value; expect(integration.setSession).toHaveBeenCalledWith('my-session-id'); }); it('should return the OpenClawIntegration instance', async () => { const { Memori } = await import('@memorilabs/memori'); const apiKey = 'test-api-key'; const context: ExtractedContext = { entityId: 'entity-123', sessionId: 'session-456', provider: 'provider-789', }; const result = initializeMemoriClient(apiKey, context); const memoriInstance = vi.mocked(Memori).mock.results[0].value; const expectedIntegration = memoriInstance.integrate.mock.results[0].value; expect(result).toBe(expectedIntegration); }); it('should configure client with correct sequence', async () => { const { Memori } = await import('@memorilabs/memori'); const apiKey = 'secret-key'; const context: ExtractedContext = { entityId: 'user-999', sessionId: 'sess-888', provider: 'google', }; const result = initializeMemoriClient(apiKey, context); expect(Memori).toHaveBeenCalled(); const memoriInstance = vi.mocked(Memori).mock.results[0].value; expect(memoriInstance.config.apiKey).toBe('secret-key'); expect(memoriInstance.integrate).toHaveBeenCalled(); expect(result.setAttribution).toHaveBeenCalledWith('user-999', 'google'); expect(result.setSession).toHaveBeenCalledWith('sess-888'); }); }); }); ================================================ FILE: integrations/openclaw/tsconfig.json ================================================ { "compilerOptions": { "target": "ES2023", "lib": ["ES2023", "DOM"], "module": "NodeNext", "moduleResolution": "NodeNext", "declaration": true, "outDir": "dist", "rootDir": "src", "strict": true, "esModuleInterop": true, "skipLibCheck": true }, "include": ["src"], "exclude": ["test", "**/*.test.ts", "**/*.spec.ts"] } ================================================ FILE: integrations/openclaw/vitest.config.ts ================================================ import { defineConfig } from 'vitest/config'; import path from 'node:path'; export default defineConfig({ test: { globals: true, environment: 'node', coverage: { provider: 'v8', reporter: ['text', 'json', 'html'], include: ['src/**'], exclude: [ 'node_modules/', 'dist/', 'tests/', 'examples/', '**/*.config.ts', '**/*.d.ts', '**/types/**', '**/index.ts', ], }, include: ['tests/**/*.test.ts'], exclude: ['node_modules/', 'dist/'], }, resolve: { alias: { '@': path.resolve(__dirname, './src'), }, }, }); ================================================ FILE: memori/__init__.py ================================================ r""" __ __ _ | \/ | ___ _ __ ___ ___ _ __(_) | |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | | | | __/ | | | | | (_) | | | | |_| |_|\___|_| |_| |_|\___/|_| |_| perfectam memoriam memorilabs.ai """ import os from collections.abc import Callable from typing import Any from uuid import uuid4 from memori._config import Config from memori._exceptions import ( MissingMemoriApiKeyError, MissingPsycopgError, QuotaExceededError, UnsupportedLLMProviderError, warn_if_legacy_memorisdk_installed, ) from memori.embeddings import embed_texts from memori.llm._providers import Agno as LlmProviderAgno from memori.llm._providers import Anthropic as LlmProviderAnthropic from memori.llm._providers import Google as LlmProviderGoogle from memori.llm._providers import LangChain as LlmProviderLangChain from memori.llm._providers import OpenAi as LlmProviderOpenAi from memori.llm._providers import PydanticAi as LlmProviderPydanticAi from memori.llm._providers import XAi as LlmProviderXAi from memori.memory.augmentation import Manager as AugmentationManager from memori.memory.recall import Recall from memori.storage import Manager as StorageManager __all__ = ["Memori", "QuotaExceededError", "UnsupportedLLMProviderError"] warn_if_legacy_memorisdk_installed() class LlmRegistry: def __init__(self, memori): self.memori = memori def register( self, client=None, openai_chat=None, claude=None, gemini=None, xai=None, chatbedrock=None, chatgooglegenai=None, chatopenai=None, chatvertexai=None, ): from memori.llm._registry import register_llm return register_llm( self.memori, client=client, openai_chat=openai_chat, claude=claude, gemini=gemini, xai=xai, chatbedrock=chatbedrock, chatgooglegenai=chatgooglegenai, chatopenai=chatopenai, chatvertexai=chatvertexai, ) class Memori: def __init__( self, conn: Callable[[], Any] | Any | None = None, debug_truncate: bool = True, ): from memori._logging import set_truncate_enabled self.config = Config() self.config.api_key = os.environ.get("MEMORI_API_KEY", None) self.config.session_id = uuid4() self.config.debug_truncate = debug_truncate set_truncate_enabled(debug_truncate) if conn is None: conn = self._get_default_connection() else: self.config.cloud = False self.config.storage = StorageManager(self.config).start(conn) self.config.augmentation = AugmentationManager(self.config).start(conn) self.augmentation = self.config.augmentation self.llm = LlmRegistry(self) self.agno = LlmProviderAgno(self) self.anthropic = LlmProviderAnthropic(self) self.google = LlmProviderGoogle(self) self.langchain = LlmProviderLangChain(self) self.openai = LlmProviderOpenAi(self) self.pydantic_ai = LlmProviderPydanticAi(self) self.xai = LlmProviderXAi(self) def _get_default_connection(self) -> Callable[[], Any] | None: connection_string = os.environ.get("MEMORI_COCKROACHDB_CONNECTION_STRING", None) if connection_string: try: import psycopg except ImportError as e: raise MissingPsycopgError("CockroachDB") from e self.config.cloud = False return lambda: psycopg.connect(connection_string) self.config.cloud = True api_key = os.environ.get("MEMORI_API_KEY", None) if api_key is None or api_key == "": raise MissingMemoriApiKeyError() return None def attribution(self, entity_id=None, process_id=None): if entity_id is not None: entity_id = str(entity_id) if len(entity_id) > 100: raise RuntimeError("entity_id cannot be greater than 100 characters") if process_id is not None: process_id = str(process_id) if len(process_id) > 100: raise RuntimeError("process_id cannot be greater than 100 characters") self.config.entity_id = entity_id self.config.process_id = process_id return self def new_session(self): self.config.session_id = uuid4() self.config.reset_cache() return self def set_session(self, id): self.config.session_id = id return self def recall(self, query: str, limit: int | None = None): return Recall(self.config).search_facts(query, limit) def close(self) -> None: """Close the underlying storage connection/session, if any. This is especially important for long-running processes (e.g. web servers) where you want to explicitly release database connections. """ storage = getattr(self.config, "storage", None) adapter = getattr(storage, "adapter", None) if storage is not None else None if adapter is None: return try: adapter.close() except Exception: # nosec B110 pass def __enter__(self) -> "Memori": return self def __exit__(self, exc_type, exc, tb) -> None: self.close() def embed_texts(self, texts: str | list[str], *, async_: bool = False) -> Any: embeddings_cfg = self.config.embeddings return embed_texts( texts, model=embeddings_cfg.model, async_=async_, ) ================================================ FILE: memori/__main__.py ================================================ r""" __ __ _ | \/ | ___ _ __ ___ ___ _ __(_) | |\/| |/ _ \ '_ ` _ \ / _ \| '__| | | | | | __/ | | | | | (_) | | | | |_| |_|\___|_| |_| |_|\___/|_| |_| perfectam memoriam memorilabs.ai """ import sys from typing import Any from memori._cli import Cli from memori._config import Config from memori._setup import Manager as SetupManager from memori.api._quota import Manager as ApiQuotaManager from memori.api._sign_up import Manager as ApiSignUpManager from memori.storage.cockroachdb._cluster_manager import ( ClusterManager as CockroachDBClusterManager, ) def main(): cli = Cli(Config()) cli.banner() options: dict[str, dict[str, Any]] = { "cockroachdb": { "description": "Manager a CockroachDB cluster", "params": ["cluster", ""], "obj": CockroachDBClusterManager, }, "quota": { "description": "Check your quota", "params": [], "obj": ApiQuotaManager, }, "setup": { "description": "Execute suggested setup steps", "params": [], "obj": SetupManager, }, "sign-up": { "description": "Sign up for an API key", "params": [""], "obj": ApiSignUpManager, }, } if len(sys.argv) <= 1 or sys.argv[1] not in options: cli.print("{:<15}{:<45}{:<6}".format("Option", "Description", "Params")) cli.print("{:<15}{:<45}{:<6}".format("------", "-----------", "------")) for key, value in options.items(): params = value["params"] cli.print( "{:<15}{:<45}{:>6}".format( key, value["description"], "Y" if len(params) > 0 else "N" ) ) cli.print("\nusage: python -m memori