diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2f1f5655..5fbe91b2 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -139,6 +139,55 @@ jobs: path: hindsight-clients/typescript/*.tgz retention-days: 1 + release-moltbot-integration: + runs-on: ubuntu-latest + environment: npm + + steps: + - uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + registry-url: 'https://registry.npmjs.org' + + - name: Install dependencies + working-directory: ./hindsight-integrations/moltbot + run: npm ci + + - name: Build + working-directory: ./hindsight-integrations/moltbot + run: npm run build + + - name: Publish to npm + working-directory: ./hindsight-integrations/moltbot + run: | + set +e + OUTPUT=$(npm publish --access public 2>&1) + EXIT_CODE=$? + echo "$OUTPUT" + if [ $EXIT_CODE -ne 0 ]; then + if echo "$OUTPUT" | grep -q "cannot publish over"; then + echo "Package version already published, skipping..." + exit 0 + fi + exit $EXIT_CODE + fi + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + + - name: Pack for GitHub release + working-directory: ./hindsight-integrations/moltbot + run: npm pack + + - name: Upload artifacts + uses: actions/upload-artifact@v4 + with: + name: moltbot-integration + path: hindsight-integrations/moltbot/*.tgz + retention-days: 1 + release-control-plane: runs-on: ubuntu-latest environment: npm @@ -366,7 +415,7 @@ jobs: create-github-release: runs-on: ubuntu-latest - needs: [release-python-packages, release-typescript-client, release-control-plane, release-rust-cli, release-docker-images, release-helm-chart] + needs: [release-python-packages, release-typescript-client, release-moltbot-integration, release-control-plane, release-rust-cli, release-docker-images, release-helm-chart] permissions: contents: write @@ -389,6 +438,12 @@ jobs: name: typescript-client path: ./artifacts/typescript-client + - name: Download Moltbot Integration + uses: actions/download-artifact@v4 + with: + name: moltbot-integration + path: ./artifacts/moltbot-integration + - name: Download Control Plane uses: actions/download-artifact@v4 with: @@ -430,6 +485,8 @@ jobs: cp artifacts/python-packages/hindsight-embed/dist/* release-assets/ || true # TypeScript client cp artifacts/typescript-client/*.tgz release-assets/ || true + # Moltbot Integration + cp artifacts/moltbot-integration/*.tgz release-assets/ || true # Control Plane cp artifacts/control-plane/*.tgz release-assets/ || true # Rust CLI binaries diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 1fec88ca..2c49c761 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -82,6 +82,29 @@ jobs: - name: Build TypeScript client run: npm run build --workspace=hindsight-clients/typescript + build-moltbot-integration: + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: '22' + + - name: Install dependencies + working-directory: ./hindsight-integrations/moltbot + run: npm ci + + - name: Run tests + working-directory: ./hindsight-integrations/moltbot + run: npm test + + - name: Build + working-directory: ./hindsight-integrations/moltbot + run: npm run build + build-control-plane: runs-on: ubuntu-latest diff --git a/hindsight-docs/docs/sdks/integrations/moltbot.md b/hindsight-docs/docs/sdks/integrations/moltbot.md new file mode 100644 index 00000000..f031113a --- /dev/null +++ b/hindsight-docs/docs/sdks/integrations/moltbot.md @@ -0,0 +1,257 @@ +--- +sidebar_position: 4 +--- + +# Moltbot (Clawdbot) + +Biomimetic long-term memory for [Moltbot](https://molt.bot) using [Hindsight](https://vectorize.io/hindsight). Automatically captures conversations and intelligently recalls relevant context. + +## Quick Start + +```bash +# 1. Install the plugin +npm install -g @vectorize-io/hindsight-moltbot-plugin + +# 2. Configure your LLM provider +export OPENAI_API_KEY="sk-your-key" +clawdbot config set 'agents.defaults.models."openai/gpt-4o-mini"' '{}' + +# 3. Enable the plugin +clawdbot plugins enable hindsight-memory + +# 4. Start Moltbot +clawdbot gateway +``` + +That's it! The plugin will automatically start capturing and recalling memories. + +## How It Works + +### Auto-Capture (Hooks) +Every conversation is **automatically stored** after each turn: +- Extracts facts, entities, and relationships +- Processes in background (non-blocking) +- Stores in PostgreSQL via embedded `hindsight-api` + +### Auto-Recall (Before Agent Start) +Before each agent response, relevant memories are **automatically injected**: +- Relevant memories retrieved (up to 1024 tokens) +- Injected into context with `` tags +- Agent seamlessly uses past context + +## Understanding Moltbot Concepts + +### Plugins +Extensions that add functionality to Moltbot. This Hindsight plugin: +- Runs a background service (manages `hindsight-embed` daemon) +- Registers hooks (automatic event handlers) + +### Hooks +Automatic event handlers that run without agent involvement: +- **`before_agent_start`**: Auto-recall - injects memories before agent processes message +- **`agent_end`**: Auto-capture - stores conversation after agent responds + +Think of hooks as "forced automation" - they always run. + +## Architecture + +``` +┌─────────────────────────────────────────┐ +│ Moltbot Gateway │ +│ │ +│ ┌───────────────────────────────────┐ │ +│ │ Hindsight Plugin │ │ +│ │ │ │ +│ │ • Service: Manages daemon │ │ +│ │ • Hook: before_agent_start │ │ +│ │ → Auto-recall (1024 tokens) │ │ +│ │ • Hook: agent_end │ │ +│ │ → Auto-capture │ │ +│ └───────────────────────────────────┘ │ +└─────────────────────────────────────────┘ + ↓ + uvx hindsight-embed + • Daemon on port 8889 + • PostgreSQL (pg0) + • Fact extraction +``` + +## Installation + +### Prerequisites + +- **Node.js** 22+ +- **Moltbot** (Clawdbot) with plugin support +- **uv/uvx** for running `hindsight-embed` +- **LLM API key** (OpenAI, Anthropic, etc.) + +### Setup + +```bash +# 1. Install the plugin +npm install -g @vectorize-io/hindsight-moltbot-plugin + +# 2. Configure your LLM provider +export OPENAI_API_KEY="sk-your-key" +clawdbot config set 'agents.defaults.models."openai/gpt-4o-mini"' '{}' + +# 3. Enable the plugin +clawdbot plugins enable hindsight-memory + +# 4. Start Moltbot +clawdbot gateway +``` + +On first start, `uvx` will automatically download `hindsight-embed` (no manual installation needed). + + +## Configuration + +Optional settings in `~/.clawdbot/clawdbot.json`: + +```json +{ + "plugins": { + "entries": { + "hindsight-memory": { + "enabled": true, + "config": { + "daemonIdleTimeout": 0 + } + } + } + } +} +``` + +**Options:** +- `daemonIdleTimeout` (number, default: `0`) - Seconds before daemon shuts down from inactivity (0 = never) +- `embedPort` (number, default: auto) - Port for embedded server +- `bankMission` (string, default: none) - Custom context for the memory bank + +## Supported LLM Providers + +The plugin auto-detects your configured provider and API key: + +| Provider | Environment Variable | Model Example | +|----------|---------------------|---------------| +| OpenAI | `OPENAI_API_KEY` | `openai/gpt-4o-mini` | +| Anthropic | `ANTHROPIC_API_KEY` | `anthropic/claude-sonnet-4` | +| Gemini | `GEMINI_API_KEY` | `gemini/gemini-2.0-flash-exp` | +| Groq | `GROQ_API_KEY` | `groq/llama-3.3-70b` | +| Ollama | None needed | `ollama/llama3` | + +Configure with: +```bash +export OPENAI_API_KEY="sk-your-key" +clawdbot config set 'agents.defaults.models."openai/gpt-4o-mini"' '{}' +``` + +## Verification + +**Check if plugin is loaded:** +```bash +clawdbot plugins list | grep hindsight +# Should show: ✓ enabled │ Hindsight Memory │ ... +``` + +**Test auto-recall:** +Send a message on any Moltbot channel (Telegram, Slack, etc.): +``` +User: My name is John and I love pizza +Bot: Got it! I'll remember that. + +User: What do I like to eat? +Bot: You love pizza! # ← Used auto-recall +``` + +**View daemon logs:** +```bash +tail -f ~/.hindsight/daemon.log +``` + +**Check memories in database:** +```bash +uvx hindsight-embed memory recall moltbot "pizza" --output json +``` + +## Troubleshooting + +**Plugin not loading?** +```bash +# Check plugin installation +npm list -g @vectorize-io/hindsight-moltbot-plugin + +# Reinstall if needed +npm install -g @vectorize-io/hindsight-moltbot-plugin +clawdbot plugins enable hindsight-memory +``` + +**Daemon not starting?** +```bash +# Check daemon status +uvx hindsight-embed daemon status + +# Manually start +uvx hindsight-embed daemon start + +# View logs +tail -f ~/.hindsight/daemon.log +``` + +**No API key error?** +```bash +# Set in shell profile +echo 'export OPENAI_API_KEY="sk-your-key"' >> ~/.zshrc +source ~/.zshrc + +# Verify +echo $OPENAI_API_KEY +``` + +**Memories not being stored?** +```bash +# Check gateway logs for auto-capture +tail -f /tmp/clawdbot/clawdbot-*.log | grep Hindsight + +# Should see: +# [Hindsight Hook] agent_end triggered +# [Hindsight] Retained X messages for session ... +``` + +## Development + +```bash +# Clone repo +git clone https://github.com/vectorize-io/hindsight.git +cd hindsight/hindsight-integrations/moltbot + +# Install dependencies +npm install + +# Build +npm run build + +# Run tests +npm test + +# Install locally +npm run build && ./install.sh +``` + +## Requirements + +- **Node.js** 22+ +- **Moltbot** (Clawdbot) with plugin support +- **uv/uvx** for running `hindsight-embed` +- **LLM API key** (OpenAI, Anthropic, etc.) + +## License + +MIT + +## Links + +- [Hindsight Documentation](https://vectorize.io/hindsight) +- [Moltbot Documentation](https://docs.molt.bot) +- [GitHub Repository](https://github.com/vectorize-io/hindsight) diff --git a/hindsight-docs/sidebars.ts b/hindsight-docs/sidebars.ts index 91fbe547..29b27cdd 100644 --- a/hindsight-docs/sidebars.ts +++ b/hindsight-docs/sidebars.ts @@ -187,6 +187,11 @@ const sidebars: SidebarsConfig = { id: 'sdks/integrations/litellm', label: 'LiteLLM', }, + { + type: 'doc', + id: 'sdks/integrations/moltbot', + label: 'Moltbot', + }, { type: 'doc', id: 'sdks/integrations/skills', diff --git a/hindsight-integrations/moltbot/.gitignore b/hindsight-integrations/moltbot/.gitignore new file mode 100644 index 00000000..dd6e803c --- /dev/null +++ b/hindsight-integrations/moltbot/.gitignore @@ -0,0 +1,4 @@ +node_modules/ +dist/ +*.log +.DS_Store diff --git a/hindsight-integrations/moltbot/README.md b/hindsight-integrations/moltbot/README.md new file mode 100644 index 00000000..88e2cbce --- /dev/null +++ b/hindsight-integrations/moltbot/README.md @@ -0,0 +1,38 @@ +# Hindsight Memory Plugin for Moltbot + +Biomimetic long-term memory for [Moltbot](https://molt.bot) using [Hindsight](https://vectorize.io/hindsight). Automatically captures conversations and intelligently recalls relevant context. + +## Quick Start + +```bash +# 1. Install the plugin +npm install -g @vectorize-io/hindsight-moltbot-plugin + +# 2. Configure your LLM provider +export OPENAI_API_KEY="sk-your-key" +clawdbot config set 'agents.defaults.models."openai/gpt-4o-mini"' '{}' + +# 3. Enable the plugin +clawdbot plugins enable hindsight-memory + +# 4. Start Moltbot +clawdbot gateway +``` + +That's it! The plugin will automatically start capturing and recalling memories. + +## Documentation + +For full documentation, configuration options, troubleshooting, and development guide, see: + +**[Moltbot Integration Documentation](https://vectorize.io/hindsight/sdks/integrations/moltbot)** + +## Links + +- [Hindsight Documentation](https://vectorize.io/hindsight) +- [Moltbot Documentation](https://docs.molt.bot) +- [GitHub Repository](https://github.com/vectorize-io/hindsight) + +## License + +MIT diff --git a/hindsight-integrations/moltbot/clawdbot.plugin.json b/hindsight-integrations/moltbot/clawdbot.plugin.json new file mode 100644 index 00000000..8e1f859c --- /dev/null +++ b/hindsight-integrations/moltbot/clawdbot.plugin.json @@ -0,0 +1,33 @@ +{ + "id": "hindsight-memory", + "name": "Hindsight Memory", + "kind": "memory", + "moltbot": { + "skills": ["skills"] + }, + "configSchema": { + "type": "object", + "properties": { + "bankMission": { + "type": "string", + "description": "Custom mission/context for the memory bank (overrides default)" + }, + "embedPort": { + "type": "number", + "description": "Port for hindsight-embed server (auto-assigned if not specified)", + "default": 0 + } + }, + "additionalProperties": false + }, + "uiHints": { + "bankMission": { + "label": "Bank Mission", + "placeholder": "Custom context for what this agent does..." + }, + "embedPort": { + "label": "Embed Server Port", + "placeholder": "0 (auto-assign)" + } + } +} diff --git a/hindsight-integrations/moltbot/hooks/retain-messages/HOOK.md b/hindsight-integrations/moltbot/hooks/retain-messages/HOOK.md new file mode 100644 index 00000000..80010847 --- /dev/null +++ b/hindsight-integrations/moltbot/hooks/retain-messages/HOOK.md @@ -0,0 +1,25 @@ +--- +name: hindsight-retain-messages +description: Automatically retains messages to Hindsight long-term memory +events: + - agent_end +metadata: + moltbot: + emoji: 🧠 +--- + +# Hindsight Message Retention + +This hook automatically retains conversation messages to Hindsight's long-term memory. + +## When It Runs + +- On `agent_end`: After each agent turn completes + +## What It Does + +1. Captures the current session messages +2. Formats them into a conversation transcript +3. Calls Hindsight's retain API with the session_id as document_id +4. Queues for background processing (async) +5. Extracts facts, entities, and relationships from the conversation diff --git a/hindsight-integrations/moltbot/hooks/retain-messages/handler.js b/hindsight-integrations/moltbot/hooks/retain-messages/handler.js new file mode 100644 index 00000000..3c4844ff --- /dev/null +++ b/hindsight-integrations/moltbot/hooks/retain-messages/handler.js @@ -0,0 +1,68 @@ +// Handler for auto-retaining messages to Hindsight +const handler = async (event) => { + console.log(`[Hindsight Hook] Received event: ${event.type}`); + + // Only process agent_end events (after each agent turn) + if (event.type !== 'agent_end') { + return; + } + + console.log('[Hindsight Hook] Processing retention after agent turn...'); + + try { + // Get client from global (set by main plugin) + const clientGlobal = global.__hindsightClient; + if (!clientGlobal) { + console.warn('[Hindsight] Client global not found, skipping retain'); + return; + } + + const client = clientGlobal.getClient(); + if (!client) { + console.warn('[Hindsight] Client not initialized, skipping retain'); + return; + } + + // Extract session information + const { sessionId, sessionKey } = event.context || {}; + if (!sessionId) { + return; + } + + // Get messages from the event context + const sessionEntry = event.context?.sessionEntry; + if (!sessionEntry || !sessionEntry.messages || sessionEntry.messages.length === 0) { + return; + } + + // Format messages into a transcript + const transcript = sessionEntry.messages + .map((msg) => { + const role = msg.role || 'unknown'; + const content = msg.content || ''; + return `${role}: ${content}`; + }) + .join('\n\n'); + + if (!transcript.trim()) { + return; + } + + // Retain to Hindsight with session_id as document_id + await client.retain({ + content: transcript, + document_id: sessionId, + metadata: { + session_key: sessionKey, + retained_at: new Date().toISOString(), + message_count: sessionEntry.messages.length, + }, + }); + + console.log(`[Hindsight] Retained ${sessionEntry.messages.length} messages for session ${sessionId}`); + } catch (error) { + console.error('[Hindsight] Error retaining messages:', error); + } +}; + +export default handler; diff --git a/hindsight-integrations/moltbot/hooks/retain-messages/handler.ts b/hindsight-integrations/moltbot/hooks/retain-messages/handler.ts new file mode 100644 index 00000000..7d9e7888 --- /dev/null +++ b/hindsight-integrations/moltbot/hooks/retain-messages/handler.ts @@ -0,0 +1,71 @@ +// Handler for auto-retaining messages to Hindsight + +import type { HookHandler } from 'moltbot/plugin-sdk'; + +const handler: HookHandler = async (event) => { + // Only process tool_result_persist and command:new events + if ( + event.type !== 'tool_result_persist' && + !(event.type === 'command' && event.action === 'new') + ) { + return; + } + + try { + // Get client from global (set by main plugin) + const clientGlobal = (global as any).__hindsightClient; + if (!clientGlobal) { + console.warn('[Hindsight] Client global not found, skipping retain'); + return; + } + + const client = clientGlobal.getClient(); + if (!client) { + console.warn('[Hindsight] Client not initialized, skipping retain'); + return; + } + + // Extract session information + const { sessionId, sessionKey } = event.context || {}; + if (!sessionId) { + return; + } + + // Get messages from the event context + // The messages are in event.context.sessionEntry or similar + const sessionEntry = event.context?.sessionEntry; + if (!sessionEntry || !sessionEntry.messages || sessionEntry.messages.length === 0) { + return; + } + + // Format messages into a transcript + const transcript = sessionEntry.messages + .map((msg: any) => { + const role = msg.role || 'unknown'; + const content = msg.content || ''; + return `${role}: ${content}`; + }) + .join('\n\n'); + + if (!transcript.trim()) { + return; + } + + // Retain to Hindsight with session_id as document_id + await client.retain({ + content: transcript, + document_id: sessionId, + metadata: { + session_key: sessionKey, + retained_at: new Date().toISOString(), + message_count: sessionEntry.messages.length, + }, + }); + + console.log(`[Hindsight] Retained ${sessionEntry.messages.length} messages for session ${sessionId}`); + } catch (error) { + console.error('[Hindsight] Error retaining messages:', error); + } +}; + +export default handler; diff --git a/hindsight-integrations/moltbot/install.sh b/hindsight-integrations/moltbot/install.sh new file mode 100755 index 00000000..438094ce --- /dev/null +++ b/hindsight-integrations/moltbot/install.sh @@ -0,0 +1,49 @@ +#!/bin/bash +set -e + +echo "🚀 Installing Hindsight Memory Plugin for Moltbot..." + +# Get the directory where this script is located +SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +INSTALL_DIR="$HOME/.clawdbot/extensions/hindsight-memory" + +# Check Node version +if ! command -v node &> /dev/null; then + echo "❌ Node.js not found. Please install Node.js 22+" + exit 1 +fi + +# Build the plugin +echo "📦 Building plugin..." +cd "$SCRIPT_DIR" +npm install +npm run build + +# Deploy to Clawdbot extensions +echo "📂 Deploying to $INSTALL_DIR..." +rm -rf "$INSTALL_DIR" +mkdir -p "$INSTALL_DIR" + +# Copy files +cp -r dist package.json clawdbot.plugin.json hooks README.md "$INSTALL_DIR/" + +# Install dependencies in deployed location +echo "📥 Installing dependencies..." +cd "$INSTALL_DIR" +npm install + +echo "" +echo "✅ Hindsight Memory Plugin installed successfully!" +echo "" +echo "📋 Next steps:" +echo "" +echo "1. Make sure you have an OpenAI API key set:" +echo " export OPENAI_API_KEY=\"sk-your-key-here\"" +echo "" +echo "2. Enable the plugin:" +echo " clawdbot plugins enable hindsight-memory" +echo "" +echo "3. Start Moltbot:" +echo " clawdbot start" +echo "" +echo "On first start, uvx will automatically download hindsight-embed (no manual install needed)" diff --git a/hindsight-integrations/moltbot/package-lock.json b/hindsight-integrations/moltbot/package-lock.json new file mode 100644 index 00000000..a4dd51c7 --- /dev/null +++ b/hindsight-integrations/moltbot/package-lock.json @@ -0,0 +1,1675 @@ +{ + "name": "@vectorize-io/hindsight-moltbot-plugin", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@vectorize-io/hindsight-moltbot-plugin", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "node-fetch": "^3.3.2" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "@vitest/ui": "^4.0.18", + "typescript": "^5.3.0", + "vitest": "^4.0.18" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "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/@polka/url": { + "version": "1.0.0-next.29", + "resolved": "https://registry.npmjs.org/@polka/url/-/url-1.0.0-next.29.tgz", + "integrity": "sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==", + "dev": true, + "license": "MIT" + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.0.tgz", + "integrity": "sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.0.tgz", + "integrity": "sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.0.tgz", + "integrity": "sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.0.tgz", + "integrity": "sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.0.tgz", + "integrity": "sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.0.tgz", + "integrity": "sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.0.tgz", + "integrity": "sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.0.tgz", + "integrity": "sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.0.tgz", + "integrity": "sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.0.tgz", + "integrity": "sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.0.tgz", + "integrity": "sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.0.tgz", + "integrity": "sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.0.tgz", + "integrity": "sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.0.tgz", + "integrity": "sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.0.tgz", + "integrity": "sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.0.tgz", + "integrity": "sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.0.tgz", + "integrity": "sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.0.tgz", + "integrity": "sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.0.tgz", + "integrity": "sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.0.tgz", + "integrity": "sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.0.tgz", + "integrity": "sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.0.tgz", + "integrity": "sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.0.tgz", + "integrity": "sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.0.tgz", + "integrity": "sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.0.tgz", + "integrity": "sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "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/@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/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/node": { + "version": "20.19.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", + "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@vitest/expect": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/expect/-/expect-4.0.18.tgz", + "integrity": "sha512-8sCWUyckXXYvx4opfzVY03EOiYVxyNrHS5QxX3DAIi5dpJAAkyJezHCP77VMX4HKA2LDT/Jpfo8i2r5BE3GnQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@standard-schema/spec": "^1.0.0", + "@types/chai": "^5.2.2", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "chai": "^6.2.1", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/mocker": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/mocker/-/mocker-4.0.18.tgz", + "integrity": "sha512-HhVd0MDnzzsgevnOWCBj5Otnzobjy5wLBe4EdeeFGv8luMsGcYqDuFRMcttKWZA5vVO8RFjexVovXvAM4JoJDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/spy": "4.0.18", + "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-0" + }, + "peerDependenciesMeta": { + "msw": { + "optional": true + }, + "vite": { + "optional": true + } + } + }, + "node_modules/@vitest/pretty-format": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/pretty-format/-/pretty-format-4.0.18.tgz", + "integrity": "sha512-P24GK3GulZWC5tz87ux0m8OADrQIUVDPIjjj65vBXYG17ZeU3qD7r+MNZ1RNv4l8CGU2vtTRqixrOi9fYk/yKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/runner": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/runner/-/runner-4.0.18.tgz", + "integrity": "sha512-rpk9y12PGa22Jg6g5M3UVVnTS7+zycIGk9ZNGN+m6tZHKQb7jrP7/77WfZy13Y/EUDd52NDsLRQhYKtv7XfPQw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.18", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/snapshot": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/snapshot/-/snapshot-4.0.18.tgz", + "integrity": "sha512-PCiV0rcl7jKQjbgYqjtakly6T1uwv/5BQ9SwBLekVg/EaYeQFPiXcgrC2Y7vDMA8dM1SUEAEV82kgSQIlXNMvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "magic-string": "^0.30.21", + "pathe": "^2.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/spy": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/spy/-/spy-4.0.18.tgz", + "integrity": "sha512-cbQt3PTSD7P2OARdVW3qWER5EGq7PHlvE+QfzSC0lbwO+xnt7+XH06ZzFjFRgzUX//JmpxrCu92VdwvEPlWSNw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "node_modules/@vitest/ui": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/ui/-/ui-4.0.18.tgz", + "integrity": "sha512-CGJ25bc8fRi8Lod/3GHSvXRKi7nBo3kxh0ApW4yCjmrWmRmlT53B5E08XRSZRliygG0aVNxLrBEqPYdz/KcCtQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/utils": "4.0.18", + "fflate": "^0.8.2", + "flatted": "^3.3.3", + "pathe": "^2.0.3", + "sirv": "^3.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + }, + "peerDependencies": { + "vitest": "4.0.18" + } + }, + "node_modules/@vitest/utils": { + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@vitest/utils/-/utils-4.0.18.tgz", + "integrity": "sha512-msMRKLMVLWygpK3u2Hybgi4MNjcYJvwTb0Ru09+fOyCXIgT5raYP041DRRdiJiI3k/2U6SEbAETB3YtBrUkCFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/pretty-format": "4.0.18", + "tinyrainbow": "^3.0.3" + }, + "funding": { + "url": "https://opencollective.com/vitest" + } + }, + "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/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/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", + "engines": { + "node": ">= 12" + } + }, + "node_modules/es-module-lexer": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", + "integrity": "sha512-jEQoCwk8hyb2AZziIOLhDqpm5+2ww5uIE6lkO/6jcOCusfk6LhMHpXXfBLXTZ7Ydyt0j4VoUQv6uGNYbdW+kBA==", + "dev": true, + "license": "MIT" + }, + "node_modules/esbuild": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" + } + }, + "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/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/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", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/flatted": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" + }, + "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", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "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/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/mrmime": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-2.0.1.tgz", + "integrity": "sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + } + }, + "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/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", + "engines": { + "node": ">=10.5.0" + } + }, + "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", + "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/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/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/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/postcss": { + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", + "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/rollup": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.0.tgz", + "integrity": "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.57.0", + "@rollup/rollup-android-arm64": "4.57.0", + "@rollup/rollup-darwin-arm64": "4.57.0", + "@rollup/rollup-darwin-x64": "4.57.0", + "@rollup/rollup-freebsd-arm64": "4.57.0", + "@rollup/rollup-freebsd-x64": "4.57.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", + "@rollup/rollup-linux-arm-musleabihf": "4.57.0", + "@rollup/rollup-linux-arm64-gnu": "4.57.0", + "@rollup/rollup-linux-arm64-musl": "4.57.0", + "@rollup/rollup-linux-loong64-gnu": "4.57.0", + "@rollup/rollup-linux-loong64-musl": "4.57.0", + "@rollup/rollup-linux-ppc64-gnu": "4.57.0", + "@rollup/rollup-linux-ppc64-musl": "4.57.0", + "@rollup/rollup-linux-riscv64-gnu": "4.57.0", + "@rollup/rollup-linux-riscv64-musl": "4.57.0", + "@rollup/rollup-linux-s390x-gnu": "4.57.0", + "@rollup/rollup-linux-x64-gnu": "4.57.0", + "@rollup/rollup-linux-x64-musl": "4.57.0", + "@rollup/rollup-openbsd-x64": "4.57.0", + "@rollup/rollup-openharmony-arm64": "4.57.0", + "@rollup/rollup-win32-arm64-msvc": "4.57.0", + "@rollup/rollup-win32-ia32-msvc": "4.57.0", + "@rollup/rollup-win32-x64-gnu": "4.57.0", + "@rollup/rollup-win32-x64-msvc": "4.57.0", + "fsevents": "~2.3.2" + } + }, + "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/sirv": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/sirv/-/sirv-3.0.2.tgz", + "integrity": "sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@polka/url": "^1.0.0-next.24", + "mrmime": "^2.0.0", + "totalist": "^3.0.0" + }, + "engines": { + "node": ">=18" + } + }, + "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/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/std-env": { + "version": "3.10.0", + "resolved": "https://registry.npmjs.org/std-env/-/std-env-3.10.0.tgz", + "integrity": "sha512-5GS12FdOZNliM5mAOxFRg7Ir0pWz8MdpYm6AY6VPkGpbA7ZzmbzNcBJQ0GPvvyWgcY7QAhCgf9Uy89I03faLkg==", + "dev": true, + "license": "MIT" + }, + "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.0.3", + "resolved": "https://registry.npmjs.org/tinyrainbow/-/tinyrainbow-3.0.3.tgz", + "integrity": "sha512-PSkbLUoxOFRzJYjjxHJt9xro7D+iilgMX/C9lawzVuYiIdcihh9DXmVibBe8lmcFrRi/VzlPjBxbN7rH24q8/Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/totalist": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/totalist/-/totalist-3.0.1.tgz", + "integrity": "sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "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==", + "dev": true, + "license": "MIT" + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmjs.org/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "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", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.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 + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "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.0.18", + "resolved": "https://registry.npmjs.org/vitest/-/vitest-4.0.18.tgz", + "integrity": "sha512-hOQuK7h0FGKgBAas7v0mSAsnvrIgAvWmRFjmzpJ7SwFHH3g1k2u37JtYwOwmEKhK6ZO3v9ggDBBm0La1LCK4uQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@vitest/expect": "4.0.18", + "@vitest/mocker": "4.0.18", + "@vitest/pretty-format": "4.0.18", + "@vitest/runner": "4.0.18", + "@vitest/snapshot": "4.0.18", + "@vitest/spy": "4.0.18", + "@vitest/utils": "4.0.18", + "es-module-lexer": "^1.7.0", + "expect-type": "^1.2.2", + "magic-string": "^0.30.21", + "obug": "^2.1.1", + "pathe": "^2.0.3", + "picomatch": "^4.0.3", + "std-env": "^3.10.0", + "tinybench": "^2.9.0", + "tinyexec": "^1.0.2", + "tinyglobby": "^0.2.15", + "tinyrainbow": "^3.0.3", + "vite": "^6.0.0 || ^7.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.0.18", + "@vitest/browser-preview": "4.0.18", + "@vitest/browser-webdriverio": "4.0.18", + "@vitest/ui": "4.0.18", + "happy-dom": "*", + "jsdom": "*" + }, + "peerDependenciesMeta": { + "@edge-runtime/vm": { + "optional": true + }, + "@opentelemetry/api": { + "optional": true + }, + "@types/node": { + "optional": true + }, + "@vitest/browser-playwright": { + "optional": true + }, + "@vitest/browser-preview": { + "optional": true + }, + "@vitest/browser-webdriverio": { + "optional": true + }, + "@vitest/ui": { + "optional": true + }, + "happy-dom": { + "optional": true + }, + "jsdom": { + "optional": true + } + } + }, + "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", + "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" + } + } + } +} diff --git a/hindsight-integrations/moltbot/package.json b/hindsight-integrations/moltbot/package.json new file mode 100644 index 00000000..d856d8fa --- /dev/null +++ b/hindsight-integrations/moltbot/package.json @@ -0,0 +1,58 @@ +{ + "name": "@vectorize-io/hindsight-moltbot-plugin", + "version": "0.1.0", + "description": "Hindsight memory plugin for Moltbot - biomimetic long-term memory with fact extraction", + "main": "dist/index.js", + "types": "dist/index.d.ts", + "type": "module", + "clawdbot": { + "extensions": [ + "./dist/index.js" + ], + "hooks": [ + "hooks/retain-messages" + ] + }, + "keywords": [ + "moltbot", + "clawdbot", + "memory", + "ai", + "agent", + "hindsight", + "long-term-memory" + ], + "author": "Vectorize ", + "license": "MIT", + "repository": { + "type": "git", + "url": "https://github.com/vectorize-io/hindsight.git", + "directory": "hindsight-integrations/moltbot" + }, + "files": [ + "dist", + "clawdbot.plugin.json", + "hooks", + "README.md" + ], + "scripts": { + "build": "tsc", + "dev": "tsc --watch", + "clean": "rm -rf dist", + "test": "vitest run", + "test:watch": "vitest", + "prepublishOnly": "npm run clean && npm run build" + }, + "dependencies": { + "node-fetch": "^3.3.2" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "@vitest/ui": "^4.0.18", + "typescript": "^5.3.0", + "vitest": "^4.0.18" + }, + "engines": { + "node": ">=22" + } +} diff --git a/hindsight-integrations/moltbot/skills/hindsight/SKILL.md b/hindsight-integrations/moltbot/skills/hindsight/SKILL.md new file mode 100644 index 00000000..553da9e5 --- /dev/null +++ b/hindsight-integrations/moltbot/skills/hindsight/SKILL.md @@ -0,0 +1,34 @@ +--- +name: memory_search +description: Search your long-term memory for relevant facts, experiences, and context using semantic and graph-based retrieval +user-invocable: false +disable-model-invocation: false +--- + +# memory_search + +Search your long-term memory for relevant information. This tool provides multi-strategy retrieval combining: +- Semantic search across facts and experiences +- BM25 keyword matching +- Entity graph traversal +- Temporal queries +- Cross-encoder reranking + +## Usage + +Call `memory_search` with a natural language query to find relevant memories: + +``` +memory_search "What does the user prefer for breakfast?" +memory_search "When did we discuss the project deadline?" +memory_search "Tell me about Paris" +``` + +## Returns + +Returns a list of relevant memory fragments with: +- Content: The actual memory text +- Score: Relevance score (0-1) +- Metadata: Source document, creation date, entities + +Use the results to inform your responses with context from past conversations. diff --git a/hindsight-integrations/moltbot/skills/hindsight/handler.ts b/hindsight-integrations/moltbot/skills/hindsight/handler.ts new file mode 100644 index 00000000..518c8461 --- /dev/null +++ b/hindsight-integrations/moltbot/skills/hindsight/handler.ts @@ -0,0 +1,46 @@ +// Handler for memory_search tool +// This will be called when the agent invokes memory_search + +import { getClient } from '../../src/index.js'; + +export interface ToolContext { + query: string; + args: Record; +} + +export async function handle(ctx: ToolContext): Promise { + try { + const { query } = ctx; + + const client = getClient(); + if (!client) { + throw new Error('Hindsight client not initialized'); + } + + // Call Hindsight recall API + const response = await client.recall({ + query, + limit: 10, + }); + + // Format results for the agent + if (!response.results || response.results.length === 0) { + return 'No relevant memories found for this query.'; + } + + const formatted = response.results + .map((result: any, idx: number) => { + const score = result.score ? ` (relevance: ${result.score.toFixed(2)})` : ''; + const date = result.metadata?.created_at + ? ` [${new Date(result.metadata.created_at).toLocaleDateString()}]` + : ''; + return `${idx + 1}. ${result.content}${score}${date}`; + }) + .join('\n\n'); + + return `Found ${response.results.length} relevant memories:\n\n${formatted}`; + } catch (error) { + console.error('[Hindsight] memory_search error:', error); + return `Error searching memories: ${error instanceof Error ? error.message : String(error)}`; + } +} diff --git a/hindsight-integrations/moltbot/src/client.test.ts b/hindsight-integrations/moltbot/src/client.test.ts new file mode 100644 index 00000000..f080ce05 --- /dev/null +++ b/hindsight-integrations/moltbot/src/client.test.ts @@ -0,0 +1,23 @@ +import { describe, it, expect } from 'vitest'; +import { HindsightClient } from './client.js'; + +describe('HindsightClient', () => { + it('should create instance with provider and API key', () => { + const client = new HindsightClient('openai', 'test-key', 'gpt-4'); + expect(client).toBeInstanceOf(HindsightClient); + }); + + it('should set bank ID', () => { + const client = new HindsightClient('openai', 'test-key'); + client.setBankId('test-bank'); + // No error thrown means success + expect(true).toBe(true); + }); + + it('should handle content escaping for single quotes', () => { + const client = new HindsightClient('openai', 'test-key'); + // This test validates the client is instantiated correctly + // Actual CLI calls would require mocking + expect(client).toBeDefined(); + }); +}); diff --git a/hindsight-integrations/moltbot/src/client.ts b/hindsight-integrations/moltbot/src/client.ts new file mode 100644 index 00000000..bd5f25a2 --- /dev/null +++ b/hindsight-integrations/moltbot/src/client.ts @@ -0,0 +1,92 @@ +import fetch from 'node-fetch'; +import { exec } from 'child_process'; +import { promisify } from 'util'; +import type { + RetainRequest, + RetainResponse, + RecallRequest, + RecallResponse, +} from './types.js'; + +const execAsync = promisify(exec); + +export class HindsightClient { + private bankId: string = 'default'; // Always use default bank + private llmProvider: string; + private llmApiKey: string; + private llmModel?: string; + + constructor(llmProvider: string, llmApiKey: string, llmModel?: string) { + this.llmProvider = llmProvider; + this.llmApiKey = llmApiKey; + this.llmModel = llmModel; + } + + setBankId(bankId: string): void { + this.bankId = bankId; + } + + private getEnv(): Record { + const env: Record = { + ...process.env, + HINDSIGHT_EMBED_LLM_PROVIDER: this.llmProvider, + HINDSIGHT_EMBED_LLM_API_KEY: this.llmApiKey, + }; + + if (this.llmModel) { + env.HINDSIGHT_EMBED_LLM_MODEL = this.llmModel; + } + + return env; + } + + async retain(request: RetainRequest): Promise { + const content = request.content.replace(/'/g, "'\\''"); // Escape single quotes + const docId = request.document_id || 'conversation'; + + const cmd = `uvx hindsight-embed memory retain ${this.bankId} '${content}' --doc-id '${docId}' --async`; + + try { + const { stdout } = await execAsync(cmd, { env: this.getEnv() }); + console.log(`[Hindsight] Retained (async): ${stdout.trim()}`); + + // Return a simple response + return { + message: 'Memory queued for background processing', + document_id: docId, + memory_unit_ids: [], + }; + } catch (error) { + throw new Error(`Failed to retain memory: ${error}`); + } + } + + async recall(request: RecallRequest): Promise { + const query = request.query.replace(/'/g, "'\\''"); // Escape single quotes + const maxTokens = request.max_tokens || 1024; + + const cmd = `uvx hindsight-embed memory recall ${this.bankId} '${query}' --output json --max-tokens ${maxTokens}`; + + try { + const { stdout } = await execAsync(cmd, { env: this.getEnv() }); + + // Parse JSON output - returns { entities: {...}, results: [...] } + const response = JSON.parse(stdout); + const results = response.results || []; + + return { + results: results.map((r: any) => ({ + content: r.text || r.content || '', + score: 1.0, // CLI doesn't return scores + metadata: { + document_id: r.document_id, + chunk_id: r.chunk_id, + ...r.metadata, + }, + })), + }; + } catch (error) { + throw new Error(`Failed to recall memories: ${error}`); + } + } +} diff --git a/hindsight-integrations/moltbot/src/embed-manager.ts b/hindsight-integrations/moltbot/src/embed-manager.ts new file mode 100644 index 00000000..651cad55 --- /dev/null +++ b/hindsight-integrations/moltbot/src/embed-manager.ts @@ -0,0 +1,143 @@ +import { spawn, ChildProcess } from 'child_process'; +import { promises as fs } from 'fs'; +import { join } from 'path'; +import { homedir } from 'os'; +import { execSync } from 'child_process'; + +export class HindsightEmbedManager { + private process: ChildProcess | null = null; + private port: number; + private baseUrl: string; + private embedDir: string; + private llmProvider: string; + private llmApiKey: string; + private llmModel?: string; + private daemonIdleTimeout: number; + + constructor( + port: number, + llmProvider: string, + llmApiKey: string, + llmModel?: string, + daemonIdleTimeout: number = 0 // Default: never timeout + ) { + this.port = 8889; // hindsight-embed uses fixed port 8889 + this.baseUrl = `http://127.0.0.1:8889`; + this.embedDir = join(homedir(), '.clawdbot', 'hindsight-embed'); + this.llmProvider = llmProvider; + this.llmApiKey = llmApiKey; + this.llmModel = llmModel; + this.daemonIdleTimeout = daemonIdleTimeout; + } + + async start(): Promise { + console.log(`[Hindsight] Starting hindsight-embed daemon...`); + + // Build environment variables + const env: NodeJS.ProcessEnv = { + ...process.env, + HINDSIGHT_EMBED_LLM_PROVIDER: this.llmProvider, + HINDSIGHT_EMBED_LLM_API_KEY: this.llmApiKey, + HINDSIGHT_EMBED_DAEMON_IDLE_TIMEOUT: this.daemonIdleTimeout.toString(), + }; + + if (this.llmModel) { + env['HINDSIGHT_EMBED_LLM_MODEL'] = this.llmModel; + } + + // Start hindsight-embed daemon (it manages itself) + const startDaemon = spawn( + 'uvx', + ['hindsight-embed', 'daemon', 'start'], + { + env, + stdio: 'pipe', + } + ); + + // Collect output + let output = ''; + startDaemon.stdout?.on('data', (data) => { + const text = data.toString(); + output += text; + console.log(`[Hindsight] ${text.trim()}`); + }); + + startDaemon.stderr?.on('data', (data) => { + const text = data.toString(); + output += text; + console.error(`[Hindsight] ${text.trim()}`); + }); + + // Wait for daemon start command to complete + await new Promise((resolve, reject) => { + startDaemon.on('exit', (code) => { + if (code === 0) { + console.log('[Hindsight] Daemon start command completed'); + resolve(); + } else { + reject(new Error(`Daemon start failed with code ${code}: ${output}`)); + } + }); + + startDaemon.on('error', (error) => { + reject(error); + }); + }); + + // Wait for server to be ready + await this.waitForReady(); + console.log('[Hindsight] Daemon is ready'); + } + + async stop(): Promise { + console.log('[Hindsight] Stopping hindsight-embed daemon...'); + + const stopDaemon = spawn('uvx', ['hindsight-embed', 'daemon', 'stop'], { + stdio: 'pipe', + }); + + await new Promise((resolve) => { + stopDaemon.on('exit', () => { + console.log('[Hindsight] Daemon stopped'); + resolve(); + }); + + stopDaemon.on('error', (error) => { + console.error('[Hindsight] Error stopping daemon:', error); + resolve(); // Resolve anyway + }); + + // Timeout after 5 seconds + setTimeout(() => { + console.log('[Hindsight] Daemon stop timeout'); + resolve(); + }, 5000); + }); + } + + private async waitForReady(maxAttempts = 30): Promise { + console.log('[Hindsight] Waiting for daemon to be ready...'); + for (let i = 0; i < maxAttempts; i++) { + try { + const response = await fetch(`${this.baseUrl}/health`); + if (response.ok) { + console.log('[Hindsight] Daemon health check passed'); + return; + } + } catch { + // Not ready yet + } + await new Promise((resolve) => setTimeout(resolve, 1000)); + } + throw new Error('Hindsight daemon failed to become ready within 30 seconds'); + } + + getBaseUrl(): string { + return this.baseUrl; + } + + isRunning(): boolean { + return this.process !== null; + } +} diff --git a/hindsight-integrations/moltbot/src/index.ts b/hindsight-integrations/moltbot/src/index.ts new file mode 100644 index 00000000..7a290d91 --- /dev/null +++ b/hindsight-integrations/moltbot/src/index.ts @@ -0,0 +1,361 @@ +import type { MoltbotPluginAPI, PluginConfig } from './types.js'; +import { HindsightEmbedManager } from './embed-manager.js'; +import { HindsightClient } from './client.js'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; + +// Module-level state +let embedManager: HindsightEmbedManager | null = null; +let client: HindsightClient | null = null; + +// Global access for hooks (Moltbot loads hooks separately) +if (typeof global !== 'undefined') { + (global as any).__hindsightClient = { + getClient: () => client, + }; +} + +// Get directory of current module +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +// Default bank name +const BANK_NAME = 'moltbot'; + +// Provider mapping: moltbot provider name -> hindsight provider name +const PROVIDER_MAP: Record = { + anthropic: 'anthropic', + openai: 'openai', + 'openai-codex': 'openai', + gemini: 'gemini', + groq: 'groq', + ollama: 'ollama', +}; + +// Environment variable mapping +const ENV_KEY_MAP: Record = { + anthropic: 'ANTHROPIC_API_KEY', + openai: 'OPENAI_API_KEY', + 'openai-codex': 'OPENAI_API_KEY', + gemini: 'GEMINI_API_KEY', + groq: 'GROQ_API_KEY', + ollama: '', // No key needed for local ollama +}; + +function detectLLMConfig(api: MoltbotPluginAPI): { + provider: string; + apiKey: string; + model?: string; + envKey?: string; +} { + // Get models from config (agents.defaults.models is a dictionary of models) + const models = api.config.agents?.defaults?.models; + if (!models || Object.keys(models).length === 0) { + throw new Error( + 'No models configured in Moltbot. Please configure at least one model in agents.defaults.models' + ); + } + + // Try all configured models to find one with an available API key + const configuredModels = Object.keys(models); + + for (const modelKey of configuredModels) { + const [moltbotProvider, ...modelParts] = modelKey.split('/'); + const model = modelParts.join('/'); + const hindsightProvider = PROVIDER_MAP[moltbotProvider]; + + if (!hindsightProvider) { + continue; // Skip unsupported providers + } + + const envKey = ENV_KEY_MAP[moltbotProvider]; + const apiKey = envKey ? process.env[envKey] || '' : ''; + + // For ollama, no key is needed + if (hindsightProvider === 'ollama') { + return { provider: hindsightProvider, apiKey: '', model, envKey: '' }; + } + + // If we found a key, use this provider + if (apiKey) { + return { provider: hindsightProvider, apiKey, model, envKey }; + } + } + + // No API keys found for any provider - show helpful error + const configuredProviders = configuredModels + .map(m => m.split('/')[0]) + .filter(p => PROVIDER_MAP[p]); + + const keyInstructions = configuredProviders + .map(p => { + const envVar = ENV_KEY_MAP[p]; + return envVar ? ` • ${envVar} (for ${p})` : null; + }) + .filter(Boolean) + .join('\n'); + + throw new Error( + `No API keys found for Hindsight memory plugin.\n\n` + + `Configured providers in Moltbot: ${configuredProviders.join(', ')}\n\n` + + `Please set one of these environment variables:\n${keyInstructions}\n\n` + + `You can set them in your shell profile (~/.zshrc or ~/.bashrc):\n` + + ` export ANTHROPIC_API_KEY="your-key-here"\n\n` + + `Or run Moltbot with the environment variable:\n` + + ` ANTHROPIC_API_KEY="your-key" clawdbot start\n\n` + + `Alternatively, configure ollama provider which doesn't require an API key.` + ); +} + +function getPluginConfig(api: MoltbotPluginAPI): PluginConfig { + const config = api.config.plugins?.entries?.['hindsight-memory']?.config || {}; + return { + bankMission: config.bankMission, + embedPort: config.embedPort || 0, + daemonIdleTimeout: config.daemonIdleTimeout !== undefined ? config.daemonIdleTimeout : 0, + }; +} + +export default function (api: MoltbotPluginAPI) { + try { + console.log('[Hindsight] Plugin loading...'); + + // Detect LLM configuration from Moltbot + console.log('[Hindsight] Detecting LLM config...'); + const llmConfig = detectLLMConfig(api); + + if (llmConfig.provider === 'ollama') { + console.log(`[Hindsight] ✓ Using provider: ${llmConfig.provider}, model: ${llmConfig.model || 'default'} (no API key required)`); + } else { + console.log(`[Hindsight] ✓ Using provider: ${llmConfig.provider}, model: ${llmConfig.model || 'default'} (API key: ${llmConfig.envKey})`); + } + + console.log('[Hindsight] Getting plugin config...'); + const pluginConfig = getPluginConfig(api); + if (pluginConfig.bankMission) { + console.log(`[Hindsight] Custom bank mission configured: "${pluginConfig.bankMission.substring(0, 50)}..."`); + } + console.log(`[Hindsight] Daemon idle timeout: ${pluginConfig.daemonIdleTimeout}s (0 = never timeout)`); + + // Determine port + const port = pluginConfig.embedPort || Math.floor(Math.random() * 10000) + 10000; + console.log(`[Hindsight] Port: ${port}`); + + // Register background service + console.log('[Hindsight] Registering service...'); + api.registerService({ + id: 'hindsight-memory', + async start() { + try { + console.log('[Hindsight] Service starting...'); + + // Initialize embed manager + console.log('[Hindsight] Creating HindsightEmbedManager...'); + embedManager = new HindsightEmbedManager( + port, + llmConfig.provider, + llmConfig.apiKey, + llmConfig.model, + pluginConfig.daemonIdleTimeout + ); + + // Start the embedded server + console.log('[Hindsight] Starting embedded server...'); + await embedManager.start(); + + // Initialize client + console.log('[Hindsight] Creating HindsightClient...'); + client = new HindsightClient(llmConfig.provider, llmConfig.apiKey, llmConfig.model); + + // Use moltbot bank + console.log(`[Hindsight] Using bank: ${BANK_NAME}`); + client.setBankId(BANK_NAME); + + console.log('[Hindsight] Service ready'); + } catch (error) { + console.error('[Hindsight] Service start error:', error); + throw error; + } + }, + + async stop() { + try { + console.log('[Hindsight] Service stopping...'); + + if (embedManager) { + await embedManager.stop(); + embedManager = null; + } + + client = null; + + console.log('[Hindsight] Service stopped'); + } catch (error) { + console.error('[Hindsight] Service stop error:', error); + throw error; + } + }, + }); + + console.log('[Hindsight] Plugin loaded successfully'); + + // Register agent_end hook for auto-retention + console.log('[Hindsight] Registering agent_end hook...'); + // Store session key for retention + let currentSessionKey: string | undefined; + + // Auto-recall: Inject relevant memories before agent processes the message + api.on('before_agent_start', async (context: any) => { + try { + // Capture session key + if (context.sessionKey) { + currentSessionKey = context.sessionKey as string; + console.log('[Hindsight] Captured session key:', currentSessionKey); + } + + // Get the user's latest message for recall + let prompt = context.prompt; + if (!prompt || typeof prompt !== 'string' || prompt.length < 5) { + return; // Skip very short messages + } + + // Extract actual message from Telegram format: [Telegram ... GMT+1] actual message + const telegramMatch = prompt.match(/\[Telegram[^\]]+\]\s*(.+)$/); + if (telegramMatch) { + prompt = telegramMatch[1].trim(); + } + + if (prompt.length < 5) { + return; // Skip very short messages after extraction + } + + // Get client from global + const clientGlobal = (global as any).__hindsightClient; + if (!clientGlobal) { + return; + } + + const client = clientGlobal.getClient(); + if (!client) { + return; + } + + console.log('[Hindsight] Auto-recall for prompt:', prompt.substring(0, 50)); + + // Recall relevant memories (up to 1024 tokens) + const response = await client.recall({ + query: prompt, + max_tokens: 1024, + }); + + if (!response.results || response.results.length === 0) { + console.log('[Hindsight] No memories found for auto-recall'); + return; + } + + // Format memories for injection + const memories = response.results + .map((result: any, idx: number) => { + const score = result.score ? ` (relevance: ${result.score.toFixed(2)})` : ''; + return `${idx + 1}. ${result.content}${score}`; + }) + .join('\n\n'); + + const contextMessage = ` +You have access to long-term memory from previous conversations. Here are relevant memories: + +${memories} + +Use this context naturally when relevant to the conversation. Don't mention "memory" or "recall" unless specifically asked about past conversations. +`; + + console.log(`[Hindsight] Auto-recall: Injecting ${response.results.length} memories`); + + // Inject context before the user message + return { prependContext: contextMessage }; + } catch (error) { + console.error('[Hindsight] Auto-recall error:', error); + return; + } + }); + + api.on('agent_end', async (event: any) => { + try { + console.log('[Hindsight Hook] agent_end triggered'); + + // Check event success and messages + if (!event.success || !Array.isArray(event.messages) || event.messages.length === 0) { + console.log('[Hindsight Hook] Skipping: success:', event.success, 'messages:', event.messages?.length); + return; + } + + // Get client from global + const clientGlobal = (global as any).__hindsightClient; + if (!clientGlobal) { + console.warn('[Hindsight] Client global not found, skipping retain'); + return; + } + + const client = clientGlobal.getClient(); + if (!client) { + console.warn('[Hindsight] Client not initialized, skipping retain'); + return; + } + + // Format messages into a transcript + const transcript = event.messages + .map((msg: any) => { + const role = msg.role || 'unknown'; + let content = ''; + + // Handle different content formats + if (typeof msg.content === 'string') { + content = msg.content; + } else if (Array.isArray(msg.content)) { + content = msg.content + .filter((block: any) => block.type === 'text') + .map((block: any) => block.text) + .join('\n'); + } + + return `[role: ${role}]\n${content}\n[${role}:end]`; + }) + .join('\n\n'); + + if (!transcript.trim() || transcript.length < 10) { + console.log('[Hindsight Hook] Transcript too short, skipping'); + return; + } + + // Use session key as document ID + const documentId = currentSessionKey || 'default-session'; + + // Retain to Hindsight + await client.retain({ + content: transcript, + document_id: documentId, + metadata: { + retained_at: new Date().toISOString(), + message_count: event.messages.length, + }, + }); + + console.log(`[Hindsight] Retained ${event.messages.length} messages for session ${documentId}`); + } catch (error) { + console.error('[Hindsight] Error retaining messages:', error); + } + }); + console.log('[Hindsight] Hook registered'); + } catch (error) { + console.error('[Hindsight] Plugin loading error:', error); + if (error instanceof Error) { + console.error('[Hindsight] Error stack:', error.stack); + } + throw error; + } +} + +// Export client getter for tools +export function getClient() { + return client; +} diff --git a/hindsight-integrations/moltbot/src/moltbot-types.ts b/hindsight-integrations/moltbot/src/moltbot-types.ts new file mode 100644 index 00000000..723c86d5 --- /dev/null +++ b/hindsight-integrations/moltbot/src/moltbot-types.ts @@ -0,0 +1,32 @@ +// Type definitions for moltbot plugin SDK +// These are minimal types based on the documentation + +declare module 'moltbot/plugin-sdk' { + export interface HookEvent { + type: 'command' | 'session' | 'agent' | 'gateway' | 'tool_result_persist'; + action?: string; + sessionKey?: string; + timestamp?: string; + messages?: string[]; + context?: { + sessionEntry?: { + messages?: Array<{ + role: string; + content: string; + }>; + }; + sessionId?: string; + sessionKey?: string; + sessionFile?: string; + commandSource?: string; + senderId?: string; + workspaceDir?: string; + bootstrapFiles?: string[]; + cfg?: any; + }; + } + + export type HookHandler = (event: HookEvent) => Promise; + + export function registerPluginHooksFromDir(api: any, dir: string): void; +} diff --git a/hindsight-integrations/moltbot/src/types.ts b/hindsight-integrations/moltbot/src/types.ts new file mode 100644 index 00000000..1ee659ff --- /dev/null +++ b/hindsight-integrations/moltbot/src/types.ts @@ -0,0 +1,84 @@ +// Moltbot plugin API types (minimal subset needed for this plugin) + +export interface MoltbotPluginAPI { + config: MoltbotConfig; + registerService(config: ServiceConfig): void; + on(event: string, handler: (context: any) => void | Promise): void; + // Add more as needed +} + +export interface MoltbotConfig { + agents?: { + defaults?: { + models?: { + [modelName: string]: { + alias?: string; + }; + }; + }; + }; + plugins?: { + entries?: { + [pluginId: string]: { + enabled?: boolean; + config?: PluginConfig; + }; + }; + }; +} + +export interface PluginConfig { + bankMission?: string; + embedPort?: number; + daemonIdleTimeout?: number; // Seconds before daemon shuts down (0 = never) +} + +export interface ServiceConfig { + id: string; + start(): Promise; + stop(): Promise; +} + +// Hindsight API types + +export interface RetainRequest { + content: string; + document_id?: string; + metadata?: Record; +} + +export interface RetainResponse { + message: string; + document_id: string; + memory_unit_ids: string[]; +} + +export interface RecallRequest { + query: string; + max_tokens?: number; +} + +export interface RecallResponse { + results: MemoryResult[]; +} + +export interface MemoryResult { + content: string; + score: number; + metadata?: { + document_id?: string; + created_at?: string; + source?: string; + }; +} + +export interface CreateBankRequest { + name: string; + background_context?: string; +} + +export interface CreateBankResponse { + bank_id: string; + name: string; + created_at: string; +} diff --git a/hindsight-integrations/moltbot/tsconfig.json b/hindsight-integrations/moltbot/tsconfig.json new file mode 100644 index 00000000..69b801e6 --- /dev/null +++ b/hindsight-integrations/moltbot/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ES2022", + "lib": ["ES2022"], + "moduleResolution": "node", + "declaration": true, + "outDir": "./dist", + "rootDir": "./src", + "strict": true, + "esModuleInterop": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true + }, + "include": ["src/**/*"], + "exclude": ["node_modules", "dist", "skills"] +} diff --git a/hindsight-integrations/moltbot/vitest.config.ts b/hindsight-integrations/moltbot/vitest.config.ts new file mode 100644 index 00000000..7dd13254 --- /dev/null +++ b/hindsight-integrations/moltbot/vitest.config.ts @@ -0,0 +1,9 @@ +import { defineConfig } from 'vitest/config'; + +export default defineConfig({ + test: { + globals: true, + environment: 'node', + include: ['src/**/*.test.ts'], + }, +});