├── .env.example ├── .gitignore ├── .prettierignore ├── Dockerfile ├── LICENSE ├── README.md ├── docker-compose.yml ├── exampleout.md ├── package-lock.json ├── package.json ├── prettier.config.mjs ├── src ├── ai │ ├── observability.ts │ ├── providers.ts │ ├── text-splitter.test.ts │ └── text-splitter.ts ├── config.ts ├── deep-research.ts ├── feedback.ts ├── http-server.ts ├── mcp-server.ts ├── output-manager.ts ├── progress-manager.ts ├── prompt.ts └── run.ts └── tsconfig.json /.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY= # Your OpenAI API key 2 | FIRECRAWL_KEY= # Your Firecrawl API key (not needed if using local instance) 3 | 4 | # Firecrawl Configuration 5 | FIRECRAWL_CONCURRENCY=2 # Number of concurrent searches (default: 2) 6 | FIRECRAWL_BASE_URL= # Local Firecrawl URL (e.g. "http://localhost:3002") 7 | 8 | # OpenAI Configuration 9 | CONTEXT_SIZE="128000" 10 | # If you want to use other OpenAI compatible API, add the following below: 11 | # OPENAI_ENDPOINT="http://localhost:11434/v1" 12 | # OPENAI_MODEL="llama3.1" 13 | 14 | 15 | # Observability (Optional) 16 | LANGFUSE_PUBLIC_KEY= 17 | LANGFUSE_SECRET_KEY= 18 | LANGFUSE_BASEURL= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # Output files 4 | output.md 5 | 6 | # Dependencies 7 | node_modules 8 | .pnp 9 | .pnp.js 10 | 11 | # Local env files 12 | .env 13 | .env.local 14 | .env.development.local 15 | .env.test.local 16 | .env.production.local 17 | 18 | # Testing 19 | coverage 20 | 21 | # Turbo 22 | .turbo 23 | 24 | # Vercel 25 | .vercel 26 | 27 | # Build Outputs 28 | .next/ 29 | out/ 30 | build 31 | dist 32 | 33 | 34 | # Debug 35 | npm-debug.log* 36 | yarn-debug.log* 37 | yarn-error.log* 38 | 39 | # Misc 40 | .DS_Store 41 | *.pem 42 | bun.lockb 43 | 44 | # Tasks 45 | tasks.md -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | *.hbs -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM node:18-alpine 2 | 3 | WORKDIR /app 4 | 5 | COPY . . 6 | COPY package.json ./ 7 | COPY .env.local ./.env.local 8 | 9 | RUN npm install 10 | 11 | CMD ["npm", "run", "docker"] 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Open Deep Research MCP Server 2 | 3 | An AI-powered research assistant that performs deep, iterative research on any topic. It combines search engines, web scraping, and AI to explore topics in depth and generate comprehensive reports. Available as a Model Context Protocol (MCP) tool or standalone CLI. Look at exampleout.md to see what a report might look like. 4 | 5 | ## Quick Start 6 | 7 | 1. Clone and install: 8 | ```bash 9 | git clone https://github.com/Ozamatash/deep-research 10 | cd deep-research 11 | npm install 12 | ``` 13 | 14 | 2. Set up environment in `.env.local`: 15 | ```bash 16 | # Copy the example environment file 17 | cp .env.example .env.local 18 | ``` 19 | 20 | 3. Build: 21 | ```bash 22 | # Build the server 23 | npm run build 24 | ``` 25 | 26 | 4. Run the cli version: 27 | ```bash 28 | npm run start 29 | ``` 30 | 5. Test MCP Server with Claude Desktop: 31 | Follow the guide thats at the bottom of server quickstart to add the server to Claude Desktop: 32 | https://modelcontextprotocol.io/quickstart/server 33 | 34 | For remote servers: Streamable HTTP 35 | ```bash 36 | npm run start:http 37 | ``` 38 | Server runs on `http://localhost:3000/mcp` without session management. 39 | 40 | ## Features 41 | 42 | - Performs deep, iterative research by generating targeted search queries 43 | - Controls research scope with depth (how deep) and breadth (how wide) parameters 44 | - Evaluates source reliability with detailed scoring (0-1) and reasoning 45 | - Prioritizes high-reliability sources (≥0.7) and verifies less reliable information 46 | - Generates follow-up questions to better understand research needs 47 | - Produces detailed markdown reports with findings, sources, and reliability assessments 48 | - Available as a Model Context Protocol (MCP) tool for AI agents 49 | - For now MCP version doesn't ask follow up questions 50 | 51 | ## How It Works 52 | 53 | ```mermaid 54 | flowchart TB 55 | subgraph Input 56 | Q[User Query] 57 | B[Breadth Parameter] 58 | D[Depth Parameter] 59 | FQ[Feedback Questions] 60 | end 61 | 62 | subgraph Research[Deep Research] 63 | direction TB 64 | SQ[Generate SERP Queries] 65 | SR[Search] 66 | RE[Source Reliability Evaluation] 67 | PR[Process Results] 68 | end 69 | 70 | subgraph Results[Research Output] 71 | direction TB 72 | L((Learnings with 73 | Reliability Scores)) 74 | SM((Source Metadata)) 75 | ND((Next Directions: 76 | Prior Goals, 77 | New Questions)) 78 | end 79 | 80 | %% Main Flow 81 | Q & FQ --> CQ[Combined Query] 82 | CQ & B & D --> SQ 83 | SQ --> SR 84 | SR --> RE 85 | RE --> PR 86 | 87 | %% Results Flow 88 | PR --> L 89 | PR --> SM 90 | PR --> ND 91 | 92 | %% Depth Decision and Recursion 93 | L & ND --> DP{depth > 0?} 94 | DP -->|Yes| SQ 95 | 96 | %% Final Output 97 | DP -->|No| MR[Markdown Report] 98 | 99 | %% Styling 100 | classDef input fill:#7bed9f,stroke:#2ed573,color:black 101 | classDef process fill:#70a1ff,stroke:#1e90ff,color:black 102 | classDef output fill:#ff4757,stroke:#ff6b81,color:black 103 | classDef results fill:#a8e6cf,stroke:#3b7a57,color:black,width:150px,height:150px 104 | 105 | class Q,B,D,FQ input 106 | class SQ,SR,RE,PR process 107 | class MR output 108 | class L,SM,ND results 109 | ``` 110 | ## Advanced Setup 111 | 112 | ### Using Local Firecrawl (Free Option) 113 | 114 | Instead of using the Firecrawl API, you can run a local instance. You can use the official repo or my fork which uses searXNG as the search backend to avoid using a searchapi key: 115 | 116 | 1. Set up local Firecrawl: 117 | ```bash 118 | git clone https://github.com/Ozamatash/localfirecrawl 119 | cd localfirecrawl 120 | # Follow setup in localfirecrawl README 121 | ``` 122 | 123 | 2. Update `.env.local`: 124 | ```bash 125 | FIRECRAWL_BASE_URL="http://localhost:3002" 126 | ``` 127 | 128 | ### Optional: Observability 129 | 130 | Add observability to track research flows, queries, and results using Langfuse: 131 | 132 | ```bash 133 | # Add to .env.local 134 | LANGFUSE_PUBLIC_KEY="your_langfuse_public_key" 135 | LANGFUSE_SECRET_KEY="your_langfuse_secret_key" 136 | ``` 137 | 138 | The app works normally without observability if no Langfuse keys are provided. 139 | 140 | ## License 141 | 142 | MIT License 143 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | deep-research: 3 | container_name: deep-research 4 | build: . 5 | env_file: 6 | - .env.local 7 | volumes: 8 | - ./:/app/ 9 | tty: true 10 | stdin_open: true 11 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-deep-research", 3 | "version": "0.0.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "open-deep-research", 9 | "version": "0.0.1", 10 | "license": "ISC", 11 | "dependencies": { 12 | "@ai-sdk/openai": "^1.3.22", 13 | "@mendable/firecrawl-js": "^1.16.0", 14 | "@modelcontextprotocol/sdk": "^1.12.1", 15 | "ai": "^4.3.16", 16 | "dotenv": "^16.4.7", 17 | "express": "^5.1.0", 18 | "js-tiktoken": "^1.0.17", 19 | "langfuse": "^3.35.2", 20 | "lodash-es": "^4.17.21", 21 | "p-limit": "^6.2.0", 22 | "zod": "^3.24.1" 23 | }, 24 | "devDependencies": { 25 | "@ianvs/prettier-plugin-sort-imports": "^4.4.1", 26 | "@types/express": "^5.0.2", 27 | "@types/lodash-es": "^4.17.12", 28 | "@types/node": "^22.13.0", 29 | "prettier": "^3.4.2", 30 | "tsx": "^4.19.2", 31 | "typescript": "^5.7.3" 32 | }, 33 | "engines": { 34 | "node": "22.x" 35 | } 36 | }, 37 | "node_modules/@ai-sdk/openai": { 38 | "version": "1.3.22", 39 | "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-1.3.22.tgz", 40 | "integrity": "sha512-QwA+2EkG0QyjVR+7h6FE7iOu2ivNqAVMm9UJZkVxxTk5OIq5fFJDTEI/zICEMuHImTTXR2JjsL6EirJ28Jc4cw==", 41 | "license": "Apache-2.0", 42 | "dependencies": { 43 | "@ai-sdk/provider": "1.1.3", 44 | "@ai-sdk/provider-utils": "2.2.8" 45 | }, 46 | "engines": { 47 | "node": ">=18" 48 | }, 49 | "peerDependencies": { 50 | "zod": "^3.0.0" 51 | } 52 | }, 53 | "node_modules/@ai-sdk/provider": { 54 | "version": "1.1.3", 55 | "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-1.1.3.tgz", 56 | "integrity": "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg==", 57 | "license": "Apache-2.0", 58 | "dependencies": { 59 | "json-schema": "^0.4.0" 60 | }, 61 | "engines": { 62 | "node": ">=18" 63 | } 64 | }, 65 | "node_modules/@ai-sdk/provider-utils": { 66 | "version": "2.2.8", 67 | "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-2.2.8.tgz", 68 | "integrity": "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA==", 69 | "license": "Apache-2.0", 70 | "dependencies": { 71 | "@ai-sdk/provider": "1.1.3", 72 | "nanoid": "^3.3.8", 73 | "secure-json-parse": "^2.7.0" 74 | }, 75 | "engines": { 76 | "node": ">=18" 77 | }, 78 | "peerDependencies": { 79 | "zod": "^3.23.8" 80 | } 81 | }, 82 | "node_modules/@ai-sdk/react": { 83 | "version": "1.2.12", 84 | "resolved": "https://registry.npmjs.org/@ai-sdk/react/-/react-1.2.12.tgz", 85 | "integrity": "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g==", 86 | "license": "Apache-2.0", 87 | "dependencies": { 88 | "@ai-sdk/provider-utils": "2.2.8", 89 | "@ai-sdk/ui-utils": "1.2.11", 90 | "swr": "^2.2.5", 91 | "throttleit": "2.1.0" 92 | }, 93 | "engines": { 94 | "node": ">=18" 95 | }, 96 | "peerDependencies": { 97 | "react": "^18 || ^19 || ^19.0.0-rc", 98 | "zod": "^3.23.8" 99 | }, 100 | "peerDependenciesMeta": { 101 | "zod": { 102 | "optional": true 103 | } 104 | } 105 | }, 106 | "node_modules/@ai-sdk/ui-utils": { 107 | "version": "1.2.11", 108 | "resolved": "https://registry.npmjs.org/@ai-sdk/ui-utils/-/ui-utils-1.2.11.tgz", 109 | "integrity": "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w==", 110 | "license": "Apache-2.0", 111 | "dependencies": { 112 | "@ai-sdk/provider": "1.1.3", 113 | "@ai-sdk/provider-utils": "2.2.8", 114 | "zod-to-json-schema": "^3.24.1" 115 | }, 116 | "engines": { 117 | "node": ">=18" 118 | }, 119 | "peerDependencies": { 120 | "zod": "^3.23.8" 121 | } 122 | }, 123 | "node_modules/@babel/code-frame": { 124 | "version": "7.26.2", 125 | "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", 126 | "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", 127 | "dev": true, 128 | "license": "MIT", 129 | "dependencies": { 130 | "@babel/helper-validator-identifier": "^7.25.9", 131 | "js-tokens": "^4.0.0", 132 | "picocolors": "^1.0.0" 133 | }, 134 | "engines": { 135 | "node": ">=6.9.0" 136 | } 137 | }, 138 | "node_modules/@babel/generator": { 139 | "version": "7.26.5", 140 | "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.5.tgz", 141 | "integrity": "sha512-2caSP6fN9I7HOe6nqhtft7V4g7/V/gfDsC3Ag4W7kEzzvRGKqiv0pu0HogPiZ3KaVSoNDhUws6IJjDjpfmYIXw==", 142 | "dev": true, 143 | "license": "MIT", 144 | "dependencies": { 145 | "@babel/parser": "^7.26.5", 146 | "@babel/types": "^7.26.5", 147 | "@jridgewell/gen-mapping": "^0.3.5", 148 | "@jridgewell/trace-mapping": "^0.3.25", 149 | "jsesc": "^3.0.2" 150 | }, 151 | "engines": { 152 | "node": ">=6.9.0" 153 | } 154 | }, 155 | "node_modules/@babel/helper-string-parser": { 156 | "version": "7.25.9", 157 | "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", 158 | "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", 159 | "dev": true, 160 | "license": "MIT", 161 | "engines": { 162 | "node": ">=6.9.0" 163 | } 164 | }, 165 | "node_modules/@babel/helper-validator-identifier": { 166 | "version": "7.25.9", 167 | "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", 168 | "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", 169 | "dev": true, 170 | "license": "MIT", 171 | "engines": { 172 | "node": ">=6.9.0" 173 | } 174 | }, 175 | "node_modules/@babel/parser": { 176 | "version": "7.26.7", 177 | "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.7.tgz", 178 | "integrity": "sha512-kEvgGGgEjRUutvdVvZhbn/BxVt+5VSpwXz1j3WYXQbXDo8KzFOPNG2GQbdAiNq8g6wn1yKk7C/qrke03a84V+w==", 179 | "dev": true, 180 | "license": "MIT", 181 | "dependencies": { 182 | "@babel/types": "^7.26.7" 183 | }, 184 | "bin": { 185 | "parser": "bin/babel-parser.js" 186 | }, 187 | "engines": { 188 | "node": ">=6.0.0" 189 | } 190 | }, 191 | "node_modules/@babel/template": { 192 | "version": "7.25.9", 193 | "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz", 194 | "integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==", 195 | "dev": true, 196 | "license": "MIT", 197 | "dependencies": { 198 | "@babel/code-frame": "^7.25.9", 199 | "@babel/parser": "^7.25.9", 200 | "@babel/types": "^7.25.9" 201 | }, 202 | "engines": { 203 | "node": ">=6.9.0" 204 | } 205 | }, 206 | "node_modules/@babel/traverse": { 207 | "version": "7.26.7", 208 | "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.7.tgz", 209 | "integrity": "sha512-1x1sgeyRLC3r5fQOM0/xtQKsYjyxmFjaOrLJNtZ81inNjyJHGIolTULPiSc/2qe1/qfpFLisLQYFnnZl7QoedA==", 210 | "dev": true, 211 | "license": "MIT", 212 | "dependencies": { 213 | "@babel/code-frame": "^7.26.2", 214 | "@babel/generator": "^7.26.5", 215 | "@babel/parser": "^7.26.7", 216 | "@babel/template": "^7.25.9", 217 | "@babel/types": "^7.26.7", 218 | "debug": "^4.3.1", 219 | "globals": "^11.1.0" 220 | }, 221 | "engines": { 222 | "node": ">=6.9.0" 223 | } 224 | }, 225 | "node_modules/@babel/types": { 226 | "version": "7.26.7", 227 | "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.7.tgz", 228 | "integrity": "sha512-t8kDRGrKXyp6+tjUh7hw2RLyclsW4TRoRvRHtSyAX9Bb5ldlFh+90YAYY6awRXrlB4G5G2izNeGySpATlFzmOg==", 229 | "dev": true, 230 | "license": "MIT", 231 | "dependencies": { 232 | "@babel/helper-string-parser": "^7.25.9", 233 | "@babel/helper-validator-identifier": "^7.25.9" 234 | }, 235 | "engines": { 236 | "node": ">=6.9.0" 237 | } 238 | }, 239 | "node_modules/@esbuild/aix-ppc64": { 240 | "version": "0.23.1", 241 | "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", 242 | "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", 243 | "cpu": [ 244 | "ppc64" 245 | ], 246 | "dev": true, 247 | "license": "MIT", 248 | "optional": true, 249 | "os": [ 250 | "aix" 251 | ], 252 | "engines": { 253 | "node": ">=18" 254 | } 255 | }, 256 | "node_modules/@esbuild/android-arm": { 257 | "version": "0.23.1", 258 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", 259 | "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", 260 | "cpu": [ 261 | "arm" 262 | ], 263 | "dev": true, 264 | "license": "MIT", 265 | "optional": true, 266 | "os": [ 267 | "android" 268 | ], 269 | "engines": { 270 | "node": ">=18" 271 | } 272 | }, 273 | "node_modules/@esbuild/android-arm64": { 274 | "version": "0.23.1", 275 | "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", 276 | "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", 277 | "cpu": [ 278 | "arm64" 279 | ], 280 | "dev": true, 281 | "license": "MIT", 282 | "optional": true, 283 | "os": [ 284 | "android" 285 | ], 286 | "engines": { 287 | "node": ">=18" 288 | } 289 | }, 290 | "node_modules/@esbuild/android-x64": { 291 | "version": "0.23.1", 292 | "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", 293 | "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", 294 | "cpu": [ 295 | "x64" 296 | ], 297 | "dev": true, 298 | "license": "MIT", 299 | "optional": true, 300 | "os": [ 301 | "android" 302 | ], 303 | "engines": { 304 | "node": ">=18" 305 | } 306 | }, 307 | "node_modules/@esbuild/darwin-arm64": { 308 | "version": "0.23.1", 309 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", 310 | "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", 311 | "cpu": [ 312 | "arm64" 313 | ], 314 | "dev": true, 315 | "license": "MIT", 316 | "optional": true, 317 | "os": [ 318 | "darwin" 319 | ], 320 | "engines": { 321 | "node": ">=18" 322 | } 323 | }, 324 | "node_modules/@esbuild/darwin-x64": { 325 | "version": "0.23.1", 326 | "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", 327 | "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", 328 | "cpu": [ 329 | "x64" 330 | ], 331 | "dev": true, 332 | "license": "MIT", 333 | "optional": true, 334 | "os": [ 335 | "darwin" 336 | ], 337 | "engines": { 338 | "node": ">=18" 339 | } 340 | }, 341 | "node_modules/@esbuild/freebsd-arm64": { 342 | "version": "0.23.1", 343 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", 344 | "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", 345 | "cpu": [ 346 | "arm64" 347 | ], 348 | "dev": true, 349 | "license": "MIT", 350 | "optional": true, 351 | "os": [ 352 | "freebsd" 353 | ], 354 | "engines": { 355 | "node": ">=18" 356 | } 357 | }, 358 | "node_modules/@esbuild/freebsd-x64": { 359 | "version": "0.23.1", 360 | "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", 361 | "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", 362 | "cpu": [ 363 | "x64" 364 | ], 365 | "dev": true, 366 | "license": "MIT", 367 | "optional": true, 368 | "os": [ 369 | "freebsd" 370 | ], 371 | "engines": { 372 | "node": ">=18" 373 | } 374 | }, 375 | "node_modules/@esbuild/linux-arm": { 376 | "version": "0.23.1", 377 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", 378 | "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", 379 | "cpu": [ 380 | "arm" 381 | ], 382 | "dev": true, 383 | "license": "MIT", 384 | "optional": true, 385 | "os": [ 386 | "linux" 387 | ], 388 | "engines": { 389 | "node": ">=18" 390 | } 391 | }, 392 | "node_modules/@esbuild/linux-arm64": { 393 | "version": "0.23.1", 394 | "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", 395 | "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", 396 | "cpu": [ 397 | "arm64" 398 | ], 399 | "dev": true, 400 | "license": "MIT", 401 | "optional": true, 402 | "os": [ 403 | "linux" 404 | ], 405 | "engines": { 406 | "node": ">=18" 407 | } 408 | }, 409 | "node_modules/@esbuild/linux-ia32": { 410 | "version": "0.23.1", 411 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", 412 | "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", 413 | "cpu": [ 414 | "ia32" 415 | ], 416 | "dev": true, 417 | "license": "MIT", 418 | "optional": true, 419 | "os": [ 420 | "linux" 421 | ], 422 | "engines": { 423 | "node": ">=18" 424 | } 425 | }, 426 | "node_modules/@esbuild/linux-loong64": { 427 | "version": "0.23.1", 428 | "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", 429 | "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", 430 | "cpu": [ 431 | "loong64" 432 | ], 433 | "dev": true, 434 | "license": "MIT", 435 | "optional": true, 436 | "os": [ 437 | "linux" 438 | ], 439 | "engines": { 440 | "node": ">=18" 441 | } 442 | }, 443 | "node_modules/@esbuild/linux-mips64el": { 444 | "version": "0.23.1", 445 | "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", 446 | "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", 447 | "cpu": [ 448 | "mips64el" 449 | ], 450 | "dev": true, 451 | "license": "MIT", 452 | "optional": true, 453 | "os": [ 454 | "linux" 455 | ], 456 | "engines": { 457 | "node": ">=18" 458 | } 459 | }, 460 | "node_modules/@esbuild/linux-ppc64": { 461 | "version": "0.23.1", 462 | "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", 463 | "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", 464 | "cpu": [ 465 | "ppc64" 466 | ], 467 | "dev": true, 468 | "license": "MIT", 469 | "optional": true, 470 | "os": [ 471 | "linux" 472 | ], 473 | "engines": { 474 | "node": ">=18" 475 | } 476 | }, 477 | "node_modules/@esbuild/linux-riscv64": { 478 | "version": "0.23.1", 479 | "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", 480 | "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", 481 | "cpu": [ 482 | "riscv64" 483 | ], 484 | "dev": true, 485 | "license": "MIT", 486 | "optional": true, 487 | "os": [ 488 | "linux" 489 | ], 490 | "engines": { 491 | "node": ">=18" 492 | } 493 | }, 494 | "node_modules/@esbuild/linux-s390x": { 495 | "version": "0.23.1", 496 | "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", 497 | "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", 498 | "cpu": [ 499 | "s390x" 500 | ], 501 | "dev": true, 502 | "license": "MIT", 503 | "optional": true, 504 | "os": [ 505 | "linux" 506 | ], 507 | "engines": { 508 | "node": ">=18" 509 | } 510 | }, 511 | "node_modules/@esbuild/linux-x64": { 512 | "version": "0.23.1", 513 | "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", 514 | "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", 515 | "cpu": [ 516 | "x64" 517 | ], 518 | "dev": true, 519 | "license": "MIT", 520 | "optional": true, 521 | "os": [ 522 | "linux" 523 | ], 524 | "engines": { 525 | "node": ">=18" 526 | } 527 | }, 528 | "node_modules/@esbuild/netbsd-x64": { 529 | "version": "0.23.1", 530 | "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", 531 | "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", 532 | "cpu": [ 533 | "x64" 534 | ], 535 | "dev": true, 536 | "license": "MIT", 537 | "optional": true, 538 | "os": [ 539 | "netbsd" 540 | ], 541 | "engines": { 542 | "node": ">=18" 543 | } 544 | }, 545 | "node_modules/@esbuild/openbsd-arm64": { 546 | "version": "0.23.1", 547 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", 548 | "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", 549 | "cpu": [ 550 | "arm64" 551 | ], 552 | "dev": true, 553 | "license": "MIT", 554 | "optional": true, 555 | "os": [ 556 | "openbsd" 557 | ], 558 | "engines": { 559 | "node": ">=18" 560 | } 561 | }, 562 | "node_modules/@esbuild/openbsd-x64": { 563 | "version": "0.23.1", 564 | "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", 565 | "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", 566 | "cpu": [ 567 | "x64" 568 | ], 569 | "dev": true, 570 | "license": "MIT", 571 | "optional": true, 572 | "os": [ 573 | "openbsd" 574 | ], 575 | "engines": { 576 | "node": ">=18" 577 | } 578 | }, 579 | "node_modules/@esbuild/sunos-x64": { 580 | "version": "0.23.1", 581 | "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", 582 | "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", 583 | "cpu": [ 584 | "x64" 585 | ], 586 | "dev": true, 587 | "license": "MIT", 588 | "optional": true, 589 | "os": [ 590 | "sunos" 591 | ], 592 | "engines": { 593 | "node": ">=18" 594 | } 595 | }, 596 | "node_modules/@esbuild/win32-arm64": { 597 | "version": "0.23.1", 598 | "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", 599 | "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", 600 | "cpu": [ 601 | "arm64" 602 | ], 603 | "dev": true, 604 | "license": "MIT", 605 | "optional": true, 606 | "os": [ 607 | "win32" 608 | ], 609 | "engines": { 610 | "node": ">=18" 611 | } 612 | }, 613 | "node_modules/@esbuild/win32-ia32": { 614 | "version": "0.23.1", 615 | "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", 616 | "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", 617 | "cpu": [ 618 | "ia32" 619 | ], 620 | "dev": true, 621 | "license": "MIT", 622 | "optional": true, 623 | "os": [ 624 | "win32" 625 | ], 626 | "engines": { 627 | "node": ">=18" 628 | } 629 | }, 630 | "node_modules/@esbuild/win32-x64": { 631 | "version": "0.23.1", 632 | "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", 633 | "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", 634 | "cpu": [ 635 | "x64" 636 | ], 637 | "dev": true, 638 | "license": "MIT", 639 | "optional": true, 640 | "os": [ 641 | "win32" 642 | ], 643 | "engines": { 644 | "node": ">=18" 645 | } 646 | }, 647 | "node_modules/@ianvs/prettier-plugin-sort-imports": { 648 | "version": "4.4.1", 649 | "resolved": "https://registry.npmjs.org/@ianvs/prettier-plugin-sort-imports/-/prettier-plugin-sort-imports-4.4.1.tgz", 650 | "integrity": "sha512-F0/Hrcfpy8WuxlQyAWJTEren/uxKhYonOGY4OyWmwRdeTvkh9mMSCxowZLjNkhwi/2ipqCgtXwwOk7tW0mWXkA==", 651 | "dev": true, 652 | "license": "Apache-2.0", 653 | "dependencies": { 654 | "@babel/generator": "^7.26.2", 655 | "@babel/parser": "^7.26.2", 656 | "@babel/traverse": "^7.25.9", 657 | "@babel/types": "^7.26.0", 658 | "semver": "^7.5.2" 659 | }, 660 | "peerDependencies": { 661 | "@vue/compiler-sfc": "2.7.x || 3.x", 662 | "prettier": "2 || 3" 663 | }, 664 | "peerDependenciesMeta": { 665 | "@vue/compiler-sfc": { 666 | "optional": true 667 | } 668 | } 669 | }, 670 | "node_modules/@jridgewell/gen-mapping": { 671 | "version": "0.3.8", 672 | "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", 673 | "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", 674 | "dev": true, 675 | "license": "MIT", 676 | "dependencies": { 677 | "@jridgewell/set-array": "^1.2.1", 678 | "@jridgewell/sourcemap-codec": "^1.4.10", 679 | "@jridgewell/trace-mapping": "^0.3.24" 680 | }, 681 | "engines": { 682 | "node": ">=6.0.0" 683 | } 684 | }, 685 | "node_modules/@jridgewell/resolve-uri": { 686 | "version": "3.1.2", 687 | "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", 688 | "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", 689 | "dev": true, 690 | "license": "MIT", 691 | "engines": { 692 | "node": ">=6.0.0" 693 | } 694 | }, 695 | "node_modules/@jridgewell/set-array": { 696 | "version": "1.2.1", 697 | "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", 698 | "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", 699 | "dev": true, 700 | "license": "MIT", 701 | "engines": { 702 | "node": ">=6.0.0" 703 | } 704 | }, 705 | "node_modules/@jridgewell/sourcemap-codec": { 706 | "version": "1.5.0", 707 | "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz", 708 | "integrity": "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ==", 709 | "dev": true, 710 | "license": "MIT" 711 | }, 712 | "node_modules/@jridgewell/trace-mapping": { 713 | "version": "0.3.25", 714 | "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", 715 | "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", 716 | "dev": true, 717 | "license": "MIT", 718 | "dependencies": { 719 | "@jridgewell/resolve-uri": "^3.1.0", 720 | "@jridgewell/sourcemap-codec": "^1.4.14" 721 | } 722 | }, 723 | "node_modules/@mendable/firecrawl-js": { 724 | "version": "1.16.0", 725 | "resolved": "https://registry.npmjs.org/@mendable/firecrawl-js/-/firecrawl-js-1.16.0.tgz", 726 | "integrity": "sha512-u5UVkgmR5yLRBVOaf8gnycAN1RyVHxqjpNkzYCeuCaqnWX7k6ad0OAUMSwf9DR8cPXuHw2/H0VPHqwMhBG/8Xw==", 727 | "license": "MIT", 728 | "dependencies": { 729 | "axios": "^1.6.8", 730 | "isows": "^1.0.4", 731 | "typescript-event-target": "^1.1.1", 732 | "zod": "^3.23.8", 733 | "zod-to-json-schema": "^3.23.0" 734 | } 735 | }, 736 | "node_modules/@modelcontextprotocol/sdk": { 737 | "version": "1.12.1", 738 | "resolved": "https://registry.npmjs.org/@modelcontextprotocol/sdk/-/sdk-1.12.1.tgz", 739 | "integrity": "sha512-KG1CZhZfWg+u8pxeM/mByJDScJSrjjxLc8fwQqbsS8xCjBmQfMNEBTotYdNanKekepnfRI85GtgQlctLFpcYPw==", 740 | "license": "MIT", 741 | "dependencies": { 742 | "ajv": "^6.12.6", 743 | "content-type": "^1.0.5", 744 | "cors": "^2.8.5", 745 | "cross-spawn": "^7.0.5", 746 | "eventsource": "^3.0.2", 747 | "express": "^5.0.1", 748 | "express-rate-limit": "^7.5.0", 749 | "pkce-challenge": "^5.0.0", 750 | "raw-body": "^3.0.0", 751 | "zod": "^3.23.8", 752 | "zod-to-json-schema": "^3.24.1" 753 | }, 754 | "engines": { 755 | "node": ">=18" 756 | } 757 | }, 758 | "node_modules/@opentelemetry/api": { 759 | "version": "1.9.0", 760 | "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", 761 | "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", 762 | "license": "Apache-2.0", 763 | "engines": { 764 | "node": ">=8.0.0" 765 | } 766 | }, 767 | "node_modules/@types/body-parser": { 768 | "version": "1.19.5", 769 | "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.5.tgz", 770 | "integrity": "sha512-fB3Zu92ucau0iQ0JMCFQE7b/dv8Ot07NI3KaZIkIUNXq82k4eBAqUaneXfleGY9JWskeS9y+u0nXMyspcuQrCg==", 771 | "dev": true, 772 | "license": "MIT", 773 | "dependencies": { 774 | "@types/connect": "*", 775 | "@types/node": "*" 776 | } 777 | }, 778 | "node_modules/@types/connect": { 779 | "version": "3.4.38", 780 | "resolved": "https://registry.npmjs.org/@types/connect/-/connect-3.4.38.tgz", 781 | "integrity": "sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==", 782 | "dev": true, 783 | "license": "MIT", 784 | "dependencies": { 785 | "@types/node": "*" 786 | } 787 | }, 788 | "node_modules/@types/diff-match-patch": { 789 | "version": "1.0.36", 790 | "resolved": "https://registry.npmjs.org/@types/diff-match-patch/-/diff-match-patch-1.0.36.tgz", 791 | "integrity": "sha512-xFdR6tkm0MWvBfO8xXCSsinYxHcqkQUlcHeSpMC2ukzOb6lwQAfDmW+Qt0AvlGd8HpsS28qKsB+oPeJn9I39jg==", 792 | "license": "MIT" 793 | }, 794 | "node_modules/@types/express": { 795 | "version": "5.0.2", 796 | "resolved": "https://registry.npmjs.org/@types/express/-/express-5.0.2.tgz", 797 | "integrity": "sha512-BtjL3ZwbCQriyb0DGw+Rt12qAXPiBTPs815lsUvtt1Grk0vLRMZNMUZ741d5rjk+UQOxfDiBZ3dxpX00vSkK3g==", 798 | "dev": true, 799 | "license": "MIT", 800 | "dependencies": { 801 | "@types/body-parser": "*", 802 | "@types/express-serve-static-core": "^5.0.0", 803 | "@types/serve-static": "*" 804 | } 805 | }, 806 | "node_modules/@types/express-serve-static-core": { 807 | "version": "5.0.6", 808 | "resolved": "https://registry.npmjs.org/@types/express-serve-static-core/-/express-serve-static-core-5.0.6.tgz", 809 | "integrity": "sha512-3xhRnjJPkULekpSzgtoNYYcTWgEZkp4myc+Saevii5JPnHNvHMRlBSHDbs7Bh1iPPoVTERHEZXyhyLbMEsExsA==", 810 | "dev": true, 811 | "license": "MIT", 812 | "dependencies": { 813 | "@types/node": "*", 814 | "@types/qs": "*", 815 | "@types/range-parser": "*", 816 | "@types/send": "*" 817 | } 818 | }, 819 | "node_modules/@types/http-errors": { 820 | "version": "2.0.4", 821 | "resolved": "https://registry.npmjs.org/@types/http-errors/-/http-errors-2.0.4.tgz", 822 | "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", 823 | "dev": true, 824 | "license": "MIT" 825 | }, 826 | "node_modules/@types/lodash": { 827 | "version": "4.17.15", 828 | "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.17.15.tgz", 829 | "integrity": "sha512-w/P33JFeySuhN6JLkysYUK2gEmy9kHHFN7E8ro0tkfmlDOgxBDzWEZ/J8cWA+fHqFevpswDTFZnDx+R9lbL6xw==", 830 | "dev": true, 831 | "license": "MIT" 832 | }, 833 | "node_modules/@types/lodash-es": { 834 | "version": "4.17.12", 835 | "resolved": "https://registry.npmjs.org/@types/lodash-es/-/lodash-es-4.17.12.tgz", 836 | "integrity": "sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==", 837 | "dev": true, 838 | "license": "MIT", 839 | "dependencies": { 840 | "@types/lodash": "*" 841 | } 842 | }, 843 | "node_modules/@types/mime": { 844 | "version": "1.3.5", 845 | "resolved": "https://registry.npmjs.org/@types/mime/-/mime-1.3.5.tgz", 846 | "integrity": "sha512-/pyBZWSLD2n0dcHE3hq8s8ZvcETHtEuF+3E7XVt0Ig2nvsVQXdghHVcEkIWjy9A0wKfTn97a/PSDYohKIlnP/w==", 847 | "dev": true, 848 | "license": "MIT" 849 | }, 850 | "node_modules/@types/node": { 851 | "version": "22.13.0", 852 | "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.0.tgz", 853 | "integrity": "sha512-ClIbNe36lawluuvq3+YYhnIN2CELi+6q8NpnM7PYp4hBn/TatfboPgVSm2rwKRfnV2M+Ty9GWDFI64KEe+kysA==", 854 | "dev": true, 855 | "license": "MIT", 856 | "dependencies": { 857 | "undici-types": "~6.20.0" 858 | } 859 | }, 860 | "node_modules/@types/qs": { 861 | "version": "6.14.0", 862 | "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.14.0.tgz", 863 | "integrity": "sha512-eOunJqu0K1923aExK6y8p6fsihYEn/BYuQ4g0CxAAgFc4b/ZLN4CrsRZ55srTdqoiLzU2B2evC+apEIxprEzkQ==", 864 | "dev": true, 865 | "license": "MIT" 866 | }, 867 | "node_modules/@types/range-parser": { 868 | "version": "1.2.7", 869 | "resolved": "https://registry.npmjs.org/@types/range-parser/-/range-parser-1.2.7.tgz", 870 | "integrity": "sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==", 871 | "dev": true, 872 | "license": "MIT" 873 | }, 874 | "node_modules/@types/send": { 875 | "version": "0.17.4", 876 | "resolved": "https://registry.npmjs.org/@types/send/-/send-0.17.4.tgz", 877 | "integrity": "sha512-x2EM6TJOybec7c52BX0ZspPodMsQUd5L6PRwOunVyVUhXiBSKf3AezDL8Dgvgt5o0UfKNfuA0eMLr2wLT4AiBA==", 878 | "dev": true, 879 | "license": "MIT", 880 | "dependencies": { 881 | "@types/mime": "^1", 882 | "@types/node": "*" 883 | } 884 | }, 885 | "node_modules/@types/serve-static": { 886 | "version": "1.15.7", 887 | "resolved": "https://registry.npmjs.org/@types/serve-static/-/serve-static-1.15.7.tgz", 888 | "integrity": "sha512-W8Ym+h8nhuRwaKPaDw34QUkwsGi6Rc4yYqvKFo5rm2FUEhCFbzVWrxXUxuKK8TASjWsysJY0nsmNCGhCOIsrOw==", 889 | "dev": true, 890 | "license": "MIT", 891 | "dependencies": { 892 | "@types/http-errors": "*", 893 | "@types/node": "*", 894 | "@types/send": "*" 895 | } 896 | }, 897 | "node_modules/accepts": { 898 | "version": "2.0.0", 899 | "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", 900 | "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", 901 | "license": "MIT", 902 | "dependencies": { 903 | "mime-types": "^3.0.0", 904 | "negotiator": "^1.0.0" 905 | }, 906 | "engines": { 907 | "node": ">= 0.6" 908 | } 909 | }, 910 | "node_modules/accepts/node_modules/mime-db": { 911 | "version": "1.54.0", 912 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", 913 | "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", 914 | "license": "MIT", 915 | "engines": { 916 | "node": ">= 0.6" 917 | } 918 | }, 919 | "node_modules/accepts/node_modules/mime-types": { 920 | "version": "3.0.1", 921 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", 922 | "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", 923 | "license": "MIT", 924 | "dependencies": { 925 | "mime-db": "^1.54.0" 926 | }, 927 | "engines": { 928 | "node": ">= 0.6" 929 | } 930 | }, 931 | "node_modules/ai": { 932 | "version": "4.3.16", 933 | "resolved": "https://registry.npmjs.org/ai/-/ai-4.3.16.tgz", 934 | "integrity": "sha512-KUDwlThJ5tr2Vw0A1ZkbDKNME3wzWhuVfAOwIvFUzl1TPVDFAXDFTXio3p+jaKneB+dKNCvFFlolYmmgHttG1g==", 935 | "license": "Apache-2.0", 936 | "dependencies": { 937 | "@ai-sdk/provider": "1.1.3", 938 | "@ai-sdk/provider-utils": "2.2.8", 939 | "@ai-sdk/react": "1.2.12", 940 | "@ai-sdk/ui-utils": "1.2.11", 941 | "@opentelemetry/api": "1.9.0", 942 | "jsondiffpatch": "0.6.0" 943 | }, 944 | "engines": { 945 | "node": ">=18" 946 | }, 947 | "peerDependencies": { 948 | "react": "^18 || ^19 || ^19.0.0-rc", 949 | "zod": "^3.23.8" 950 | }, 951 | "peerDependenciesMeta": { 952 | "react": { 953 | "optional": true 954 | } 955 | } 956 | }, 957 | "node_modules/ajv": { 958 | "version": "6.12.6", 959 | "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", 960 | "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", 961 | "license": "MIT", 962 | "dependencies": { 963 | "fast-deep-equal": "^3.1.1", 964 | "fast-json-stable-stringify": "^2.0.0", 965 | "json-schema-traverse": "^0.4.1", 966 | "uri-js": "^4.2.2" 967 | }, 968 | "funding": { 969 | "type": "github", 970 | "url": "https://github.com/sponsors/epoberezkin" 971 | } 972 | }, 973 | "node_modules/asynckit": { 974 | "version": "0.4.0", 975 | "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", 976 | "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", 977 | "license": "MIT" 978 | }, 979 | "node_modules/axios": { 980 | "version": "1.7.9", 981 | "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.9.tgz", 982 | "integrity": "sha512-LhLcE7Hbiryz8oMDdDptSrWowmB4Bl6RCt6sIJKpRB4XtVf0iEgewX3au/pJqm+Py1kCASkb/FFKjxQaLtxJvw==", 983 | "license": "MIT", 984 | "dependencies": { 985 | "follow-redirects": "^1.15.6", 986 | "form-data": "^4.0.0", 987 | "proxy-from-env": "^1.1.0" 988 | } 989 | }, 990 | "node_modules/base64-js": { 991 | "version": "1.5.1", 992 | "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", 993 | "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", 994 | "funding": [ 995 | { 996 | "type": "github", 997 | "url": "https://github.com/sponsors/feross" 998 | }, 999 | { 1000 | "type": "patreon", 1001 | "url": "https://www.patreon.com/feross" 1002 | }, 1003 | { 1004 | "type": "consulting", 1005 | "url": "https://feross.org/support" 1006 | } 1007 | ], 1008 | "license": "MIT" 1009 | }, 1010 | "node_modules/body-parser": { 1011 | "version": "2.2.0", 1012 | "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.0.tgz", 1013 | "integrity": "sha512-02qvAaxv8tp7fBa/mw1ga98OGm+eCbqzJOKoRt70sLmfEEi+jyBYVTDGfCL/k06/4EMk/z01gCe7HoCH/f2LTg==", 1014 | "license": "MIT", 1015 | "dependencies": { 1016 | "bytes": "^3.1.2", 1017 | "content-type": "^1.0.5", 1018 | "debug": "^4.4.0", 1019 | "http-errors": "^2.0.0", 1020 | "iconv-lite": "^0.6.3", 1021 | "on-finished": "^2.4.1", 1022 | "qs": "^6.14.0", 1023 | "raw-body": "^3.0.0", 1024 | "type-is": "^2.0.0" 1025 | }, 1026 | "engines": { 1027 | "node": ">=18" 1028 | } 1029 | }, 1030 | "node_modules/bytes": { 1031 | "version": "3.1.2", 1032 | "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", 1033 | "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", 1034 | "license": "MIT", 1035 | "engines": { 1036 | "node": ">= 0.8" 1037 | } 1038 | }, 1039 | "node_modules/call-bind-apply-helpers": { 1040 | "version": "1.0.2", 1041 | "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", 1042 | "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", 1043 | "license": "MIT", 1044 | "dependencies": { 1045 | "es-errors": "^1.3.0", 1046 | "function-bind": "^1.1.2" 1047 | }, 1048 | "engines": { 1049 | "node": ">= 0.4" 1050 | } 1051 | }, 1052 | "node_modules/call-bound": { 1053 | "version": "1.0.4", 1054 | "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", 1055 | "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", 1056 | "license": "MIT", 1057 | "dependencies": { 1058 | "call-bind-apply-helpers": "^1.0.2", 1059 | "get-intrinsic": "^1.3.0" 1060 | }, 1061 | "engines": { 1062 | "node": ">= 0.4" 1063 | }, 1064 | "funding": { 1065 | "url": "https://github.com/sponsors/ljharb" 1066 | } 1067 | }, 1068 | "node_modules/chalk": { 1069 | "version": "5.4.1", 1070 | "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz", 1071 | "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==", 1072 | "license": "MIT", 1073 | "engines": { 1074 | "node": "^12.17.0 || ^14.13 || >=16.0.0" 1075 | }, 1076 | "funding": { 1077 | "url": "https://github.com/chalk/chalk?sponsor=1" 1078 | } 1079 | }, 1080 | "node_modules/combined-stream": { 1081 | "version": "1.0.8", 1082 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", 1083 | "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", 1084 | "license": "MIT", 1085 | "dependencies": { 1086 | "delayed-stream": "~1.0.0" 1087 | }, 1088 | "engines": { 1089 | "node": ">= 0.8" 1090 | } 1091 | }, 1092 | "node_modules/content-disposition": { 1093 | "version": "1.0.0", 1094 | "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.0.tgz", 1095 | "integrity": "sha512-Au9nRL8VNUut/XSzbQA38+M78dzP4D+eqg3gfJHMIHHYa3bg067xj1KxMUWj+VULbiZMowKngFFbKczUrNJ1mg==", 1096 | "license": "MIT", 1097 | "dependencies": { 1098 | "safe-buffer": "5.2.1" 1099 | }, 1100 | "engines": { 1101 | "node": ">= 0.6" 1102 | } 1103 | }, 1104 | "node_modules/content-type": { 1105 | "version": "1.0.5", 1106 | "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", 1107 | "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", 1108 | "license": "MIT", 1109 | "engines": { 1110 | "node": ">= 0.6" 1111 | } 1112 | }, 1113 | "node_modules/cookie": { 1114 | "version": "0.7.2", 1115 | "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", 1116 | "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", 1117 | "license": "MIT", 1118 | "engines": { 1119 | "node": ">= 0.6" 1120 | } 1121 | }, 1122 | "node_modules/cookie-signature": { 1123 | "version": "1.2.2", 1124 | "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", 1125 | "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", 1126 | "license": "MIT", 1127 | "engines": { 1128 | "node": ">=6.6.0" 1129 | } 1130 | }, 1131 | "node_modules/cors": { 1132 | "version": "2.8.5", 1133 | "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", 1134 | "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", 1135 | "license": "MIT", 1136 | "dependencies": { 1137 | "object-assign": "^4", 1138 | "vary": "^1" 1139 | }, 1140 | "engines": { 1141 | "node": ">= 0.10" 1142 | } 1143 | }, 1144 | "node_modules/cross-spawn": { 1145 | "version": "7.0.6", 1146 | "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", 1147 | "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", 1148 | "license": "MIT", 1149 | "dependencies": { 1150 | "path-key": "^3.1.0", 1151 | "shebang-command": "^2.0.0", 1152 | "which": "^2.0.1" 1153 | }, 1154 | "engines": { 1155 | "node": ">= 8" 1156 | } 1157 | }, 1158 | "node_modules/debug": { 1159 | "version": "4.4.0", 1160 | "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", 1161 | "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", 1162 | "license": "MIT", 1163 | "dependencies": { 1164 | "ms": "^2.1.3" 1165 | }, 1166 | "engines": { 1167 | "node": ">=6.0" 1168 | }, 1169 | "peerDependenciesMeta": { 1170 | "supports-color": { 1171 | "optional": true 1172 | } 1173 | } 1174 | }, 1175 | "node_modules/delayed-stream": { 1176 | "version": "1.0.0", 1177 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", 1178 | "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", 1179 | "license": "MIT", 1180 | "engines": { 1181 | "node": ">=0.4.0" 1182 | } 1183 | }, 1184 | "node_modules/depd": { 1185 | "version": "2.0.0", 1186 | "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", 1187 | "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", 1188 | "license": "MIT", 1189 | "engines": { 1190 | "node": ">= 0.8" 1191 | } 1192 | }, 1193 | "node_modules/dequal": { 1194 | "version": "2.0.3", 1195 | "resolved": "https://registry.npmjs.org/dequal/-/dequal-2.0.3.tgz", 1196 | "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", 1197 | "license": "MIT", 1198 | "engines": { 1199 | "node": ">=6" 1200 | } 1201 | }, 1202 | "node_modules/diff-match-patch": { 1203 | "version": "1.0.5", 1204 | "resolved": "https://registry.npmjs.org/diff-match-patch/-/diff-match-patch-1.0.5.tgz", 1205 | "integrity": "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw==", 1206 | "license": "Apache-2.0" 1207 | }, 1208 | "node_modules/dotenv": { 1209 | "version": "16.4.7", 1210 | "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.7.tgz", 1211 | "integrity": "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ==", 1212 | "license": "BSD-2-Clause", 1213 | "engines": { 1214 | "node": ">=12" 1215 | }, 1216 | "funding": { 1217 | "url": "https://dotenvx.com" 1218 | } 1219 | }, 1220 | "node_modules/dunder-proto": { 1221 | "version": "1.0.1", 1222 | "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", 1223 | "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", 1224 | "license": "MIT", 1225 | "dependencies": { 1226 | "call-bind-apply-helpers": "^1.0.1", 1227 | "es-errors": "^1.3.0", 1228 | "gopd": "^1.2.0" 1229 | }, 1230 | "engines": { 1231 | "node": ">= 0.4" 1232 | } 1233 | }, 1234 | "node_modules/ee-first": { 1235 | "version": "1.1.1", 1236 | "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", 1237 | "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", 1238 | "license": "MIT" 1239 | }, 1240 | "node_modules/encodeurl": { 1241 | "version": "2.0.0", 1242 | "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", 1243 | "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", 1244 | "license": "MIT", 1245 | "engines": { 1246 | "node": ">= 0.8" 1247 | } 1248 | }, 1249 | "node_modules/es-define-property": { 1250 | "version": "1.0.1", 1251 | "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", 1252 | "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", 1253 | "license": "MIT", 1254 | "engines": { 1255 | "node": ">= 0.4" 1256 | } 1257 | }, 1258 | "node_modules/es-errors": { 1259 | "version": "1.3.0", 1260 | "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", 1261 | "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", 1262 | "license": "MIT", 1263 | "engines": { 1264 | "node": ">= 0.4" 1265 | } 1266 | }, 1267 | "node_modules/es-object-atoms": { 1268 | "version": "1.1.1", 1269 | "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", 1270 | "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", 1271 | "license": "MIT", 1272 | "dependencies": { 1273 | "es-errors": "^1.3.0" 1274 | }, 1275 | "engines": { 1276 | "node": ">= 0.4" 1277 | } 1278 | }, 1279 | "node_modules/esbuild": { 1280 | "version": "0.23.1", 1281 | "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", 1282 | "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", 1283 | "dev": true, 1284 | "hasInstallScript": true, 1285 | "license": "MIT", 1286 | "bin": { 1287 | "esbuild": "bin/esbuild" 1288 | }, 1289 | "engines": { 1290 | "node": ">=18" 1291 | }, 1292 | "optionalDependencies": { 1293 | "@esbuild/aix-ppc64": "0.23.1", 1294 | "@esbuild/android-arm": "0.23.1", 1295 | "@esbuild/android-arm64": "0.23.1", 1296 | "@esbuild/android-x64": "0.23.1", 1297 | "@esbuild/darwin-arm64": "0.23.1", 1298 | "@esbuild/darwin-x64": "0.23.1", 1299 | "@esbuild/freebsd-arm64": "0.23.1", 1300 | "@esbuild/freebsd-x64": "0.23.1", 1301 | "@esbuild/linux-arm": "0.23.1", 1302 | "@esbuild/linux-arm64": "0.23.1", 1303 | "@esbuild/linux-ia32": "0.23.1", 1304 | "@esbuild/linux-loong64": "0.23.1", 1305 | "@esbuild/linux-mips64el": "0.23.1", 1306 | "@esbuild/linux-ppc64": "0.23.1", 1307 | "@esbuild/linux-riscv64": "0.23.1", 1308 | "@esbuild/linux-s390x": "0.23.1", 1309 | "@esbuild/linux-x64": "0.23.1", 1310 | "@esbuild/netbsd-x64": "0.23.1", 1311 | "@esbuild/openbsd-arm64": "0.23.1", 1312 | "@esbuild/openbsd-x64": "0.23.1", 1313 | "@esbuild/sunos-x64": "0.23.1", 1314 | "@esbuild/win32-arm64": "0.23.1", 1315 | "@esbuild/win32-ia32": "0.23.1", 1316 | "@esbuild/win32-x64": "0.23.1" 1317 | } 1318 | }, 1319 | "node_modules/escape-html": { 1320 | "version": "1.0.3", 1321 | "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", 1322 | "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", 1323 | "license": "MIT" 1324 | }, 1325 | "node_modules/etag": { 1326 | "version": "1.8.1", 1327 | "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", 1328 | "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", 1329 | "license": "MIT", 1330 | "engines": { 1331 | "node": ">= 0.6" 1332 | } 1333 | }, 1334 | "node_modules/eventsource": { 1335 | "version": "3.0.5", 1336 | "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-3.0.5.tgz", 1337 | "integrity": "sha512-LT/5J605bx5SNyE+ITBDiM3FxffBiq9un7Vx0EwMDM3vg8sWKx/tO2zC+LMqZ+smAM0F2hblaDZUVZF0te2pSw==", 1338 | "license": "MIT", 1339 | "dependencies": { 1340 | "eventsource-parser": "^3.0.0" 1341 | }, 1342 | "engines": { 1343 | "node": ">=18.0.0" 1344 | } 1345 | }, 1346 | "node_modules/eventsource-parser": { 1347 | "version": "3.0.0", 1348 | "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.0.tgz", 1349 | "integrity": "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==", 1350 | "license": "MIT", 1351 | "engines": { 1352 | "node": ">=18.0.0" 1353 | } 1354 | }, 1355 | "node_modules/express": { 1356 | "version": "5.1.0", 1357 | "resolved": "https://registry.npmjs.org/express/-/express-5.1.0.tgz", 1358 | "integrity": "sha512-DT9ck5YIRU+8GYzzU5kT3eHGA5iL+1Zd0EutOmTE9Dtk+Tvuzd23VBU+ec7HPNSTxXYO55gPV/hq4pSBJDjFpA==", 1359 | "license": "MIT", 1360 | "dependencies": { 1361 | "accepts": "^2.0.0", 1362 | "body-parser": "^2.2.0", 1363 | "content-disposition": "^1.0.0", 1364 | "content-type": "^1.0.5", 1365 | "cookie": "^0.7.1", 1366 | "cookie-signature": "^1.2.1", 1367 | "debug": "^4.4.0", 1368 | "encodeurl": "^2.0.0", 1369 | "escape-html": "^1.0.3", 1370 | "etag": "^1.8.1", 1371 | "finalhandler": "^2.1.0", 1372 | "fresh": "^2.0.0", 1373 | "http-errors": "^2.0.0", 1374 | "merge-descriptors": "^2.0.0", 1375 | "mime-types": "^3.0.0", 1376 | "on-finished": "^2.4.1", 1377 | "once": "^1.4.0", 1378 | "parseurl": "^1.3.3", 1379 | "proxy-addr": "^2.0.7", 1380 | "qs": "^6.14.0", 1381 | "range-parser": "^1.2.1", 1382 | "router": "^2.2.0", 1383 | "send": "^1.1.0", 1384 | "serve-static": "^2.2.0", 1385 | "statuses": "^2.0.1", 1386 | "type-is": "^2.0.1", 1387 | "vary": "^1.1.2" 1388 | }, 1389 | "engines": { 1390 | "node": ">= 18" 1391 | }, 1392 | "funding": { 1393 | "type": "opencollective", 1394 | "url": "https://opencollective.com/express" 1395 | } 1396 | }, 1397 | "node_modules/express-rate-limit": { 1398 | "version": "7.5.0", 1399 | "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz", 1400 | "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==", 1401 | "license": "MIT", 1402 | "engines": { 1403 | "node": ">= 16" 1404 | }, 1405 | "funding": { 1406 | "url": "https://github.com/sponsors/express-rate-limit" 1407 | }, 1408 | "peerDependencies": { 1409 | "express": "^4.11 || 5 || ^5.0.0-beta.1" 1410 | } 1411 | }, 1412 | "node_modules/express/node_modules/mime-db": { 1413 | "version": "1.54.0", 1414 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", 1415 | "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", 1416 | "license": "MIT", 1417 | "engines": { 1418 | "node": ">= 0.6" 1419 | } 1420 | }, 1421 | "node_modules/express/node_modules/mime-types": { 1422 | "version": "3.0.1", 1423 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", 1424 | "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", 1425 | "license": "MIT", 1426 | "dependencies": { 1427 | "mime-db": "^1.54.0" 1428 | }, 1429 | "engines": { 1430 | "node": ">= 0.6" 1431 | } 1432 | }, 1433 | "node_modules/fast-deep-equal": { 1434 | "version": "3.1.3", 1435 | "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", 1436 | "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", 1437 | "license": "MIT" 1438 | }, 1439 | "node_modules/fast-json-stable-stringify": { 1440 | "version": "2.1.0", 1441 | "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", 1442 | "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", 1443 | "license": "MIT" 1444 | }, 1445 | "node_modules/finalhandler": { 1446 | "version": "2.1.0", 1447 | "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.0.tgz", 1448 | "integrity": "sha512-/t88Ty3d5JWQbWYgaOGCCYfXRwV1+be02WqYYlL6h0lEiUAMPM8o8qKGO01YIkOHzka2up08wvgYD0mDiI+q3Q==", 1449 | "license": "MIT", 1450 | "dependencies": { 1451 | "debug": "^4.4.0", 1452 | "encodeurl": "^2.0.0", 1453 | "escape-html": "^1.0.3", 1454 | "on-finished": "^2.4.1", 1455 | "parseurl": "^1.3.3", 1456 | "statuses": "^2.0.1" 1457 | }, 1458 | "engines": { 1459 | "node": ">= 0.8" 1460 | } 1461 | }, 1462 | "node_modules/follow-redirects": { 1463 | "version": "1.15.9", 1464 | "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", 1465 | "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==", 1466 | "funding": [ 1467 | { 1468 | "type": "individual", 1469 | "url": "https://github.com/sponsors/RubenVerborgh" 1470 | } 1471 | ], 1472 | "license": "MIT", 1473 | "engines": { 1474 | "node": ">=4.0" 1475 | }, 1476 | "peerDependenciesMeta": { 1477 | "debug": { 1478 | "optional": true 1479 | } 1480 | } 1481 | }, 1482 | "node_modules/form-data": { 1483 | "version": "4.0.1", 1484 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.1.tgz", 1485 | "integrity": "sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw==", 1486 | "license": "MIT", 1487 | "dependencies": { 1488 | "asynckit": "^0.4.0", 1489 | "combined-stream": "^1.0.8", 1490 | "mime-types": "^2.1.12" 1491 | }, 1492 | "engines": { 1493 | "node": ">= 6" 1494 | } 1495 | }, 1496 | "node_modules/forwarded": { 1497 | "version": "0.2.0", 1498 | "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", 1499 | "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", 1500 | "license": "MIT", 1501 | "engines": { 1502 | "node": ">= 0.6" 1503 | } 1504 | }, 1505 | "node_modules/fresh": { 1506 | "version": "2.0.0", 1507 | "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", 1508 | "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", 1509 | "license": "MIT", 1510 | "engines": { 1511 | "node": ">= 0.8" 1512 | } 1513 | }, 1514 | "node_modules/fsevents": { 1515 | "version": "2.3.3", 1516 | "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", 1517 | "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", 1518 | "dev": true, 1519 | "hasInstallScript": true, 1520 | "license": "MIT", 1521 | "optional": true, 1522 | "os": [ 1523 | "darwin" 1524 | ], 1525 | "engines": { 1526 | "node": "^8.16.0 || ^10.6.0 || >=11.0.0" 1527 | } 1528 | }, 1529 | "node_modules/function-bind": { 1530 | "version": "1.1.2", 1531 | "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", 1532 | "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", 1533 | "license": "MIT", 1534 | "funding": { 1535 | "url": "https://github.com/sponsors/ljharb" 1536 | } 1537 | }, 1538 | "node_modules/get-intrinsic": { 1539 | "version": "1.3.0", 1540 | "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", 1541 | "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", 1542 | "license": "MIT", 1543 | "dependencies": { 1544 | "call-bind-apply-helpers": "^1.0.2", 1545 | "es-define-property": "^1.0.1", 1546 | "es-errors": "^1.3.0", 1547 | "es-object-atoms": "^1.1.1", 1548 | "function-bind": "^1.1.2", 1549 | "get-proto": "^1.0.1", 1550 | "gopd": "^1.2.0", 1551 | "has-symbols": "^1.1.0", 1552 | "hasown": "^2.0.2", 1553 | "math-intrinsics": "^1.1.0" 1554 | }, 1555 | "engines": { 1556 | "node": ">= 0.4" 1557 | }, 1558 | "funding": { 1559 | "url": "https://github.com/sponsors/ljharb" 1560 | } 1561 | }, 1562 | "node_modules/get-proto": { 1563 | "version": "1.0.1", 1564 | "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", 1565 | "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", 1566 | "license": "MIT", 1567 | "dependencies": { 1568 | "dunder-proto": "^1.0.1", 1569 | "es-object-atoms": "^1.0.0" 1570 | }, 1571 | "engines": { 1572 | "node": ">= 0.4" 1573 | } 1574 | }, 1575 | "node_modules/get-tsconfig": { 1576 | "version": "4.10.0", 1577 | "resolved": "https://registry.npmjs.org/get-tsconfig/-/get-tsconfig-4.10.0.tgz", 1578 | "integrity": "sha512-kGzZ3LWWQcGIAmg6iWvXn0ei6WDtV26wzHRMwDSzmAbcXrTEXxHy6IehI6/4eT6VRKyMP1eF1VqwrVUmE/LR7A==", 1579 | "dev": true, 1580 | "license": "MIT", 1581 | "dependencies": { 1582 | "resolve-pkg-maps": "^1.0.0" 1583 | }, 1584 | "funding": { 1585 | "url": "https://github.com/privatenumber/get-tsconfig?sponsor=1" 1586 | } 1587 | }, 1588 | "node_modules/globals": { 1589 | "version": "11.12.0", 1590 | "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", 1591 | "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", 1592 | "dev": true, 1593 | "license": "MIT", 1594 | "engines": { 1595 | "node": ">=4" 1596 | } 1597 | }, 1598 | "node_modules/gopd": { 1599 | "version": "1.2.0", 1600 | "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", 1601 | "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", 1602 | "license": "MIT", 1603 | "engines": { 1604 | "node": ">= 0.4" 1605 | }, 1606 | "funding": { 1607 | "url": "https://github.com/sponsors/ljharb" 1608 | } 1609 | }, 1610 | "node_modules/has-symbols": { 1611 | "version": "1.1.0", 1612 | "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", 1613 | "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", 1614 | "license": "MIT", 1615 | "engines": { 1616 | "node": ">= 0.4" 1617 | }, 1618 | "funding": { 1619 | "url": "https://github.com/sponsors/ljharb" 1620 | } 1621 | }, 1622 | "node_modules/hasown": { 1623 | "version": "2.0.2", 1624 | "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", 1625 | "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", 1626 | "license": "MIT", 1627 | "dependencies": { 1628 | "function-bind": "^1.1.2" 1629 | }, 1630 | "engines": { 1631 | "node": ">= 0.4" 1632 | } 1633 | }, 1634 | "node_modules/http-errors": { 1635 | "version": "2.0.0", 1636 | "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", 1637 | "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", 1638 | "license": "MIT", 1639 | "dependencies": { 1640 | "depd": "2.0.0", 1641 | "inherits": "2.0.4", 1642 | "setprototypeof": "1.2.0", 1643 | "statuses": "2.0.1", 1644 | "toidentifier": "1.0.1" 1645 | }, 1646 | "engines": { 1647 | "node": ">= 0.8" 1648 | } 1649 | }, 1650 | "node_modules/iconv-lite": { 1651 | "version": "0.6.3", 1652 | "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", 1653 | "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", 1654 | "license": "MIT", 1655 | "dependencies": { 1656 | "safer-buffer": ">= 2.1.2 < 3.0.0" 1657 | }, 1658 | "engines": { 1659 | "node": ">=0.10.0" 1660 | } 1661 | }, 1662 | "node_modules/inherits": { 1663 | "version": "2.0.4", 1664 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 1665 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 1666 | "license": "ISC" 1667 | }, 1668 | "node_modules/ipaddr.js": { 1669 | "version": "1.9.1", 1670 | "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", 1671 | "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", 1672 | "license": "MIT", 1673 | "engines": { 1674 | "node": ">= 0.10" 1675 | } 1676 | }, 1677 | "node_modules/is-promise": { 1678 | "version": "4.0.0", 1679 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", 1680 | "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", 1681 | "license": "MIT" 1682 | }, 1683 | "node_modules/isexe": { 1684 | "version": "2.0.0", 1685 | "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", 1686 | "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", 1687 | "license": "ISC" 1688 | }, 1689 | "node_modules/isows": { 1690 | "version": "1.0.6", 1691 | "resolved": "https://registry.npmjs.org/isows/-/isows-1.0.6.tgz", 1692 | "integrity": "sha512-lPHCayd40oW98/I0uvgaHKWCSvkzY27LjWLbtzOm64yQ+G3Q5npjjbdppU65iZXkK1Zt+kH9pfegli0AYfwYYw==", 1693 | "funding": [ 1694 | { 1695 | "type": "github", 1696 | "url": "https://github.com/sponsors/wevm" 1697 | } 1698 | ], 1699 | "license": "MIT", 1700 | "peerDependencies": { 1701 | "ws": "*" 1702 | } 1703 | }, 1704 | "node_modules/js-tiktoken": { 1705 | "version": "1.0.17", 1706 | "resolved": "https://registry.npmjs.org/js-tiktoken/-/js-tiktoken-1.0.17.tgz", 1707 | "integrity": "sha512-Fzmf02RUP8lU6eAn0CtwWEr//mSVGOgj6lK6D++OF+ZVVtFVPnaBdSAN1P2QA0/q8IZOulhIOIjnlFy6QwYNBw==", 1708 | "license": "MIT", 1709 | "dependencies": { 1710 | "base64-js": "^1.5.1" 1711 | } 1712 | }, 1713 | "node_modules/js-tokens": { 1714 | "version": "4.0.0", 1715 | "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", 1716 | "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", 1717 | "dev": true, 1718 | "license": "MIT" 1719 | }, 1720 | "node_modules/jsesc": { 1721 | "version": "3.1.0", 1722 | "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", 1723 | "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", 1724 | "dev": true, 1725 | "license": "MIT", 1726 | "bin": { 1727 | "jsesc": "bin/jsesc" 1728 | }, 1729 | "engines": { 1730 | "node": ">=6" 1731 | } 1732 | }, 1733 | "node_modules/json-schema": { 1734 | "version": "0.4.0", 1735 | "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", 1736 | "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", 1737 | "license": "(AFL-2.1 OR BSD-3-Clause)" 1738 | }, 1739 | "node_modules/json-schema-traverse": { 1740 | "version": "0.4.1", 1741 | "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", 1742 | "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", 1743 | "license": "MIT" 1744 | }, 1745 | "node_modules/jsondiffpatch": { 1746 | "version": "0.6.0", 1747 | "resolved": "https://registry.npmjs.org/jsondiffpatch/-/jsondiffpatch-0.6.0.tgz", 1748 | "integrity": "sha512-3QItJOXp2AP1uv7waBkao5nCvhEv+QmJAd38Ybq7wNI74Q+BBmnLn4EDKz6yI9xGAIQoUF87qHt+kc1IVxB4zQ==", 1749 | "license": "MIT", 1750 | "dependencies": { 1751 | "@types/diff-match-patch": "^1.0.36", 1752 | "chalk": "^5.3.0", 1753 | "diff-match-patch": "^1.0.5" 1754 | }, 1755 | "bin": { 1756 | "jsondiffpatch": "bin/jsondiffpatch.js" 1757 | }, 1758 | "engines": { 1759 | "node": "^18.0.0 || >=20.0.0" 1760 | } 1761 | }, 1762 | "node_modules/langfuse": { 1763 | "version": "3.35.2", 1764 | "resolved": "https://registry.npmjs.org/langfuse/-/langfuse-3.35.2.tgz", 1765 | "integrity": "sha512-iLKK9capLVle7vHRHKYj6tqRJVD+ncc5nVcKYEA7eidNPRSSYRdZf73Q73lW8wFBcKpbynRJN1byWT615Gg9tw==", 1766 | "license": "MIT", 1767 | "dependencies": { 1768 | "langfuse-core": "^3.35.2" 1769 | }, 1770 | "engines": { 1771 | "node": ">=18" 1772 | } 1773 | }, 1774 | "node_modules/langfuse-core": { 1775 | "version": "3.35.2", 1776 | "resolved": "https://registry.npmjs.org/langfuse-core/-/langfuse-core-3.35.2.tgz", 1777 | "integrity": "sha512-nHcoe/RPxwCAVvb91NDjVqvaXkBuVI6u9KuhCXLOasIOqMZRnXMeDhheAPE1ZtVGpmc3faYdPPGdobqRB+MdCg==", 1778 | "license": "MIT", 1779 | "dependencies": { 1780 | "mustache": "^4.2.0" 1781 | }, 1782 | "engines": { 1783 | "node": ">=18" 1784 | } 1785 | }, 1786 | "node_modules/lodash-es": { 1787 | "version": "4.17.21", 1788 | "resolved": "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz", 1789 | "integrity": "sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==", 1790 | "license": "MIT" 1791 | }, 1792 | "node_modules/math-intrinsics": { 1793 | "version": "1.1.0", 1794 | "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", 1795 | "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", 1796 | "license": "MIT", 1797 | "engines": { 1798 | "node": ">= 0.4" 1799 | } 1800 | }, 1801 | "node_modules/media-typer": { 1802 | "version": "1.1.0", 1803 | "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", 1804 | "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", 1805 | "license": "MIT", 1806 | "engines": { 1807 | "node": ">= 0.8" 1808 | } 1809 | }, 1810 | "node_modules/merge-descriptors": { 1811 | "version": "2.0.0", 1812 | "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", 1813 | "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", 1814 | "license": "MIT", 1815 | "engines": { 1816 | "node": ">=18" 1817 | }, 1818 | "funding": { 1819 | "url": "https://github.com/sponsors/sindresorhus" 1820 | } 1821 | }, 1822 | "node_modules/mime-db": { 1823 | "version": "1.52.0", 1824 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", 1825 | "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", 1826 | "license": "MIT", 1827 | "engines": { 1828 | "node": ">= 0.6" 1829 | } 1830 | }, 1831 | "node_modules/mime-types": { 1832 | "version": "2.1.35", 1833 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", 1834 | "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", 1835 | "license": "MIT", 1836 | "dependencies": { 1837 | "mime-db": "1.52.0" 1838 | }, 1839 | "engines": { 1840 | "node": ">= 0.6" 1841 | } 1842 | }, 1843 | "node_modules/ms": { 1844 | "version": "2.1.3", 1845 | "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", 1846 | "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", 1847 | "license": "MIT" 1848 | }, 1849 | "node_modules/mustache": { 1850 | "version": "4.2.0", 1851 | "resolved": "https://registry.npmjs.org/mustache/-/mustache-4.2.0.tgz", 1852 | "integrity": "sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ==", 1853 | "license": "MIT", 1854 | "bin": { 1855 | "mustache": "bin/mustache" 1856 | } 1857 | }, 1858 | "node_modules/nanoid": { 1859 | "version": "3.3.11", 1860 | "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", 1861 | "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", 1862 | "funding": [ 1863 | { 1864 | "type": "github", 1865 | "url": "https://github.com/sponsors/ai" 1866 | } 1867 | ], 1868 | "license": "MIT", 1869 | "bin": { 1870 | "nanoid": "bin/nanoid.cjs" 1871 | }, 1872 | "engines": { 1873 | "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" 1874 | } 1875 | }, 1876 | "node_modules/negotiator": { 1877 | "version": "1.0.0", 1878 | "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", 1879 | "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", 1880 | "license": "MIT", 1881 | "engines": { 1882 | "node": ">= 0.6" 1883 | } 1884 | }, 1885 | "node_modules/object-assign": { 1886 | "version": "4.1.1", 1887 | "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", 1888 | "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", 1889 | "license": "MIT", 1890 | "engines": { 1891 | "node": ">=0.10.0" 1892 | } 1893 | }, 1894 | "node_modules/object-inspect": { 1895 | "version": "1.13.4", 1896 | "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", 1897 | "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", 1898 | "license": "MIT", 1899 | "engines": { 1900 | "node": ">= 0.4" 1901 | }, 1902 | "funding": { 1903 | "url": "https://github.com/sponsors/ljharb" 1904 | } 1905 | }, 1906 | "node_modules/on-finished": { 1907 | "version": "2.4.1", 1908 | "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", 1909 | "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", 1910 | "license": "MIT", 1911 | "dependencies": { 1912 | "ee-first": "1.1.1" 1913 | }, 1914 | "engines": { 1915 | "node": ">= 0.8" 1916 | } 1917 | }, 1918 | "node_modules/once": { 1919 | "version": "1.4.0", 1920 | "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", 1921 | "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", 1922 | "license": "ISC", 1923 | "dependencies": { 1924 | "wrappy": "1" 1925 | } 1926 | }, 1927 | "node_modules/p-limit": { 1928 | "version": "6.2.0", 1929 | "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-6.2.0.tgz", 1930 | "integrity": "sha512-kuUqqHNUqoIWp/c467RI4X6mmyuojY5jGutNU0wVTmEOOfcuwLqyMVoAi9MKi2Ak+5i9+nhmrK4ufZE8069kHA==", 1931 | "license": "MIT", 1932 | "dependencies": { 1933 | "yocto-queue": "^1.1.1" 1934 | }, 1935 | "engines": { 1936 | "node": ">=18" 1937 | }, 1938 | "funding": { 1939 | "url": "https://github.com/sponsors/sindresorhus" 1940 | } 1941 | }, 1942 | "node_modules/parseurl": { 1943 | "version": "1.3.3", 1944 | "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", 1945 | "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", 1946 | "license": "MIT", 1947 | "engines": { 1948 | "node": ">= 0.8" 1949 | } 1950 | }, 1951 | "node_modules/path-key": { 1952 | "version": "3.1.1", 1953 | "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", 1954 | "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", 1955 | "license": "MIT", 1956 | "engines": { 1957 | "node": ">=8" 1958 | } 1959 | }, 1960 | "node_modules/path-to-regexp": { 1961 | "version": "8.2.0", 1962 | "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.2.0.tgz", 1963 | "integrity": "sha512-TdrF7fW9Rphjq4RjrW0Kp2AW0Ahwu9sRGTkS6bvDi0SCwZlEZYmcfDbEsTz8RVk0EHIS/Vd1bv3JhG+1xZuAyQ==", 1964 | "license": "MIT", 1965 | "engines": { 1966 | "node": ">=16" 1967 | } 1968 | }, 1969 | "node_modules/picocolors": { 1970 | "version": "1.1.1", 1971 | "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", 1972 | "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", 1973 | "dev": true, 1974 | "license": "ISC" 1975 | }, 1976 | "node_modules/pkce-challenge": { 1977 | "version": "5.0.0", 1978 | "resolved": "https://registry.npmjs.org/pkce-challenge/-/pkce-challenge-5.0.0.tgz", 1979 | "integrity": "sha512-ueGLflrrnvwB3xuo/uGob5pd5FN7l0MsLf0Z87o/UQmRtwjvfylfc9MurIxRAWywCYTgrvpXBcqjV4OfCYGCIQ==", 1980 | "license": "MIT", 1981 | "engines": { 1982 | "node": ">=16.20.0" 1983 | } 1984 | }, 1985 | "node_modules/prettier": { 1986 | "version": "3.4.2", 1987 | "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.4.2.tgz", 1988 | "integrity": "sha512-e9MewbtFo+Fevyuxn/4rrcDAaq0IYxPGLvObpQjiZBMAzB9IGmzlnG9RZy3FFas+eBMu2vA0CszMeduow5dIuQ==", 1989 | "dev": true, 1990 | "license": "MIT", 1991 | "bin": { 1992 | "prettier": "bin/prettier.cjs" 1993 | }, 1994 | "engines": { 1995 | "node": ">=14" 1996 | }, 1997 | "funding": { 1998 | "url": "https://github.com/prettier/prettier?sponsor=1" 1999 | } 2000 | }, 2001 | "node_modules/proxy-addr": { 2002 | "version": "2.0.7", 2003 | "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", 2004 | "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", 2005 | "license": "MIT", 2006 | "dependencies": { 2007 | "forwarded": "0.2.0", 2008 | "ipaddr.js": "1.9.1" 2009 | }, 2010 | "engines": { 2011 | "node": ">= 0.10" 2012 | } 2013 | }, 2014 | "node_modules/proxy-from-env": { 2015 | "version": "1.1.0", 2016 | "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", 2017 | "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", 2018 | "license": "MIT" 2019 | }, 2020 | "node_modules/punycode": { 2021 | "version": "2.3.1", 2022 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", 2023 | "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", 2024 | "license": "MIT", 2025 | "engines": { 2026 | "node": ">=6" 2027 | } 2028 | }, 2029 | "node_modules/qs": { 2030 | "version": "6.14.0", 2031 | "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", 2032 | "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", 2033 | "license": "BSD-3-Clause", 2034 | "dependencies": { 2035 | "side-channel": "^1.1.0" 2036 | }, 2037 | "engines": { 2038 | "node": ">=0.6" 2039 | }, 2040 | "funding": { 2041 | "url": "https://github.com/sponsors/ljharb" 2042 | } 2043 | }, 2044 | "node_modules/range-parser": { 2045 | "version": "1.2.1", 2046 | "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", 2047 | "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", 2048 | "license": "MIT", 2049 | "engines": { 2050 | "node": ">= 0.6" 2051 | } 2052 | }, 2053 | "node_modules/raw-body": { 2054 | "version": "3.0.0", 2055 | "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.0.tgz", 2056 | "integrity": "sha512-RmkhL8CAyCRPXCE28MMH0z2PNWQBNk2Q09ZdxM9IOOXwxwZbN+qbWaatPkdkWIKL2ZVDImrN/pK5HTRz2PcS4g==", 2057 | "license": "MIT", 2058 | "dependencies": { 2059 | "bytes": "3.1.2", 2060 | "http-errors": "2.0.0", 2061 | "iconv-lite": "0.6.3", 2062 | "unpipe": "1.0.0" 2063 | }, 2064 | "engines": { 2065 | "node": ">= 0.8" 2066 | } 2067 | }, 2068 | "node_modules/react": { 2069 | "version": "19.1.0", 2070 | "resolved": "https://registry.npmjs.org/react/-/react-19.1.0.tgz", 2071 | "integrity": "sha512-FS+XFBNvn3GTAWq26joslQgWNoFu08F4kl0J4CgdNKADkdSGXQyTCnKteIAJy96Br6YbpEU1LSzV5dYtjMkMDg==", 2072 | "license": "MIT", 2073 | "peer": true, 2074 | "engines": { 2075 | "node": ">=0.10.0" 2076 | } 2077 | }, 2078 | "node_modules/resolve-pkg-maps": { 2079 | "version": "1.0.0", 2080 | "resolved": "https://registry.npmjs.org/resolve-pkg-maps/-/resolve-pkg-maps-1.0.0.tgz", 2081 | "integrity": "sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==", 2082 | "dev": true, 2083 | "license": "MIT", 2084 | "funding": { 2085 | "url": "https://github.com/privatenumber/resolve-pkg-maps?sponsor=1" 2086 | } 2087 | }, 2088 | "node_modules/router": { 2089 | "version": "2.2.0", 2090 | "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", 2091 | "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", 2092 | "license": "MIT", 2093 | "dependencies": { 2094 | "debug": "^4.4.0", 2095 | "depd": "^2.0.0", 2096 | "is-promise": "^4.0.0", 2097 | "parseurl": "^1.3.3", 2098 | "path-to-regexp": "^8.0.0" 2099 | }, 2100 | "engines": { 2101 | "node": ">= 18" 2102 | } 2103 | }, 2104 | "node_modules/safe-buffer": { 2105 | "version": "5.2.1", 2106 | "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", 2107 | "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", 2108 | "funding": [ 2109 | { 2110 | "type": "github", 2111 | "url": "https://github.com/sponsors/feross" 2112 | }, 2113 | { 2114 | "type": "patreon", 2115 | "url": "https://www.patreon.com/feross" 2116 | }, 2117 | { 2118 | "type": "consulting", 2119 | "url": "https://feross.org/support" 2120 | } 2121 | ], 2122 | "license": "MIT" 2123 | }, 2124 | "node_modules/safer-buffer": { 2125 | "version": "2.1.2", 2126 | "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", 2127 | "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", 2128 | "license": "MIT" 2129 | }, 2130 | "node_modules/secure-json-parse": { 2131 | "version": "2.7.0", 2132 | "resolved": "https://registry.npmjs.org/secure-json-parse/-/secure-json-parse-2.7.0.tgz", 2133 | "integrity": "sha512-6aU+Rwsezw7VR8/nyvKTx8QpWH9FrcYiXXlqC4z5d5XQBDRqtbfsRjnwGyqbi3gddNtWHuEk9OANUotL26qKUw==", 2134 | "license": "BSD-3-Clause" 2135 | }, 2136 | "node_modules/semver": { 2137 | "version": "7.7.1", 2138 | "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz", 2139 | "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==", 2140 | "dev": true, 2141 | "license": "ISC", 2142 | "bin": { 2143 | "semver": "bin/semver.js" 2144 | }, 2145 | "engines": { 2146 | "node": ">=10" 2147 | } 2148 | }, 2149 | "node_modules/send": { 2150 | "version": "1.2.0", 2151 | "resolved": "https://registry.npmjs.org/send/-/send-1.2.0.tgz", 2152 | "integrity": "sha512-uaW0WwXKpL9blXE2o0bRhoL2EGXIrZxQ2ZQ4mgcfoBxdFmQold+qWsD2jLrfZ0trjKL6vOw0j//eAwcALFjKSw==", 2153 | "license": "MIT", 2154 | "dependencies": { 2155 | "debug": "^4.3.5", 2156 | "encodeurl": "^2.0.0", 2157 | "escape-html": "^1.0.3", 2158 | "etag": "^1.8.1", 2159 | "fresh": "^2.0.0", 2160 | "http-errors": "^2.0.0", 2161 | "mime-types": "^3.0.1", 2162 | "ms": "^2.1.3", 2163 | "on-finished": "^2.4.1", 2164 | "range-parser": "^1.2.1", 2165 | "statuses": "^2.0.1" 2166 | }, 2167 | "engines": { 2168 | "node": ">= 18" 2169 | } 2170 | }, 2171 | "node_modules/send/node_modules/mime-db": { 2172 | "version": "1.54.0", 2173 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", 2174 | "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", 2175 | "license": "MIT", 2176 | "engines": { 2177 | "node": ">= 0.6" 2178 | } 2179 | }, 2180 | "node_modules/send/node_modules/mime-types": { 2181 | "version": "3.0.1", 2182 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", 2183 | "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", 2184 | "license": "MIT", 2185 | "dependencies": { 2186 | "mime-db": "^1.54.0" 2187 | }, 2188 | "engines": { 2189 | "node": ">= 0.6" 2190 | } 2191 | }, 2192 | "node_modules/serve-static": { 2193 | "version": "2.2.0", 2194 | "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.0.tgz", 2195 | "integrity": "sha512-61g9pCh0Vnh7IutZjtLGGpTA355+OPn2TyDv/6ivP2h/AdAVX9azsoxmg2/M6nZeQZNYBEwIcsne1mJd9oQItQ==", 2196 | "license": "MIT", 2197 | "dependencies": { 2198 | "encodeurl": "^2.0.0", 2199 | "escape-html": "^1.0.3", 2200 | "parseurl": "^1.3.3", 2201 | "send": "^1.2.0" 2202 | }, 2203 | "engines": { 2204 | "node": ">= 18" 2205 | } 2206 | }, 2207 | "node_modules/setprototypeof": { 2208 | "version": "1.2.0", 2209 | "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", 2210 | "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", 2211 | "license": "ISC" 2212 | }, 2213 | "node_modules/shebang-command": { 2214 | "version": "2.0.0", 2215 | "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", 2216 | "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", 2217 | "license": "MIT", 2218 | "dependencies": { 2219 | "shebang-regex": "^3.0.0" 2220 | }, 2221 | "engines": { 2222 | "node": ">=8" 2223 | } 2224 | }, 2225 | "node_modules/shebang-regex": { 2226 | "version": "3.0.0", 2227 | "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", 2228 | "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", 2229 | "license": "MIT", 2230 | "engines": { 2231 | "node": ">=8" 2232 | } 2233 | }, 2234 | "node_modules/side-channel": { 2235 | "version": "1.1.0", 2236 | "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", 2237 | "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", 2238 | "license": "MIT", 2239 | "dependencies": { 2240 | "es-errors": "^1.3.0", 2241 | "object-inspect": "^1.13.3", 2242 | "side-channel-list": "^1.0.0", 2243 | "side-channel-map": "^1.0.1", 2244 | "side-channel-weakmap": "^1.0.2" 2245 | }, 2246 | "engines": { 2247 | "node": ">= 0.4" 2248 | }, 2249 | "funding": { 2250 | "url": "https://github.com/sponsors/ljharb" 2251 | } 2252 | }, 2253 | "node_modules/side-channel-list": { 2254 | "version": "1.0.0", 2255 | "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", 2256 | "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", 2257 | "license": "MIT", 2258 | "dependencies": { 2259 | "es-errors": "^1.3.0", 2260 | "object-inspect": "^1.13.3" 2261 | }, 2262 | "engines": { 2263 | "node": ">= 0.4" 2264 | }, 2265 | "funding": { 2266 | "url": "https://github.com/sponsors/ljharb" 2267 | } 2268 | }, 2269 | "node_modules/side-channel-map": { 2270 | "version": "1.0.1", 2271 | "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", 2272 | "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", 2273 | "license": "MIT", 2274 | "dependencies": { 2275 | "call-bound": "^1.0.2", 2276 | "es-errors": "^1.3.0", 2277 | "get-intrinsic": "^1.2.5", 2278 | "object-inspect": "^1.13.3" 2279 | }, 2280 | "engines": { 2281 | "node": ">= 0.4" 2282 | }, 2283 | "funding": { 2284 | "url": "https://github.com/sponsors/ljharb" 2285 | } 2286 | }, 2287 | "node_modules/side-channel-weakmap": { 2288 | "version": "1.0.2", 2289 | "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", 2290 | "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", 2291 | "license": "MIT", 2292 | "dependencies": { 2293 | "call-bound": "^1.0.2", 2294 | "es-errors": "^1.3.0", 2295 | "get-intrinsic": "^1.2.5", 2296 | "object-inspect": "^1.13.3", 2297 | "side-channel-map": "^1.0.1" 2298 | }, 2299 | "engines": { 2300 | "node": ">= 0.4" 2301 | }, 2302 | "funding": { 2303 | "url": "https://github.com/sponsors/ljharb" 2304 | } 2305 | }, 2306 | "node_modules/statuses": { 2307 | "version": "2.0.1", 2308 | "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", 2309 | "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", 2310 | "license": "MIT", 2311 | "engines": { 2312 | "node": ">= 0.8" 2313 | } 2314 | }, 2315 | "node_modules/swr": { 2316 | "version": "2.3.3", 2317 | "resolved": "https://registry.npmjs.org/swr/-/swr-2.3.3.tgz", 2318 | "integrity": "sha512-dshNvs3ExOqtZ6kJBaAsabhPdHyeY4P2cKwRCniDVifBMoG/SVI7tfLWqPXriVspf2Rg4tPzXJTnwaihIeFw2A==", 2319 | "license": "MIT", 2320 | "dependencies": { 2321 | "dequal": "^2.0.3", 2322 | "use-sync-external-store": "^1.4.0" 2323 | }, 2324 | "peerDependencies": { 2325 | "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 2326 | } 2327 | }, 2328 | "node_modules/throttleit": { 2329 | "version": "2.1.0", 2330 | "resolved": "https://registry.npmjs.org/throttleit/-/throttleit-2.1.0.tgz", 2331 | "integrity": "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw==", 2332 | "license": "MIT", 2333 | "engines": { 2334 | "node": ">=18" 2335 | }, 2336 | "funding": { 2337 | "url": "https://github.com/sponsors/sindresorhus" 2338 | } 2339 | }, 2340 | "node_modules/toidentifier": { 2341 | "version": "1.0.1", 2342 | "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", 2343 | "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", 2344 | "license": "MIT", 2345 | "engines": { 2346 | "node": ">=0.6" 2347 | } 2348 | }, 2349 | "node_modules/tsx": { 2350 | "version": "4.19.2", 2351 | "resolved": "https://registry.npmjs.org/tsx/-/tsx-4.19.2.tgz", 2352 | "integrity": "sha512-pOUl6Vo2LUq/bSa8S5q7b91cgNSjctn9ugq/+Mvow99qW6x/UZYwzxy/3NmqoT66eHYfCVvFvACC58UBPFf28g==", 2353 | "dev": true, 2354 | "license": "MIT", 2355 | "dependencies": { 2356 | "esbuild": "~0.23.0", 2357 | "get-tsconfig": "^4.7.5" 2358 | }, 2359 | "bin": { 2360 | "tsx": "dist/cli.mjs" 2361 | }, 2362 | "engines": { 2363 | "node": ">=18.0.0" 2364 | }, 2365 | "optionalDependencies": { 2366 | "fsevents": "~2.3.3" 2367 | } 2368 | }, 2369 | "node_modules/type-is": { 2370 | "version": "2.0.1", 2371 | "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", 2372 | "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", 2373 | "license": "MIT", 2374 | "dependencies": { 2375 | "content-type": "^1.0.5", 2376 | "media-typer": "^1.1.0", 2377 | "mime-types": "^3.0.0" 2378 | }, 2379 | "engines": { 2380 | "node": ">= 0.6" 2381 | } 2382 | }, 2383 | "node_modules/type-is/node_modules/mime-db": { 2384 | "version": "1.54.0", 2385 | "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", 2386 | "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", 2387 | "license": "MIT", 2388 | "engines": { 2389 | "node": ">= 0.6" 2390 | } 2391 | }, 2392 | "node_modules/type-is/node_modules/mime-types": { 2393 | "version": "3.0.1", 2394 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.1.tgz", 2395 | "integrity": "sha512-xRc4oEhT6eaBpU1XF7AjpOFD+xQmXNB5OVKwp4tqCuBpHLS/ZbBDrc07mYTDqVMg6PfxUjjNp85O6Cd2Z/5HWA==", 2396 | "license": "MIT", 2397 | "dependencies": { 2398 | "mime-db": "^1.54.0" 2399 | }, 2400 | "engines": { 2401 | "node": ">= 0.6" 2402 | } 2403 | }, 2404 | "node_modules/typescript": { 2405 | "version": "5.7.3", 2406 | "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", 2407 | "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", 2408 | "dev": true, 2409 | "license": "Apache-2.0", 2410 | "bin": { 2411 | "tsc": "bin/tsc", 2412 | "tsserver": "bin/tsserver" 2413 | }, 2414 | "engines": { 2415 | "node": ">=14.17" 2416 | } 2417 | }, 2418 | "node_modules/typescript-event-target": { 2419 | "version": "1.1.1", 2420 | "resolved": "https://registry.npmjs.org/typescript-event-target/-/typescript-event-target-1.1.1.tgz", 2421 | "integrity": "sha512-dFSOFBKV6uwaloBCCUhxlD3Pr/P1a/tJdcmPrTXCHlEFD3faj0mztjcGn6VBAhQ0/Bdy8K3VWrrqwbt/ffsYsg==", 2422 | "license": "MIT" 2423 | }, 2424 | "node_modules/undici-types": { 2425 | "version": "6.20.0", 2426 | "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz", 2427 | "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==", 2428 | "dev": true, 2429 | "license": "MIT" 2430 | }, 2431 | "node_modules/unpipe": { 2432 | "version": "1.0.0", 2433 | "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", 2434 | "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", 2435 | "license": "MIT", 2436 | "engines": { 2437 | "node": ">= 0.8" 2438 | } 2439 | }, 2440 | "node_modules/uri-js": { 2441 | "version": "4.4.1", 2442 | "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", 2443 | "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", 2444 | "license": "BSD-2-Clause", 2445 | "dependencies": { 2446 | "punycode": "^2.1.0" 2447 | } 2448 | }, 2449 | "node_modules/use-sync-external-store": { 2450 | "version": "1.5.0", 2451 | "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz", 2452 | "integrity": "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A==", 2453 | "license": "MIT", 2454 | "peerDependencies": { 2455 | "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" 2456 | } 2457 | }, 2458 | "node_modules/vary": { 2459 | "version": "1.1.2", 2460 | "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", 2461 | "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", 2462 | "license": "MIT", 2463 | "engines": { 2464 | "node": ">= 0.8" 2465 | } 2466 | }, 2467 | "node_modules/which": { 2468 | "version": "2.0.2", 2469 | "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", 2470 | "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", 2471 | "license": "ISC", 2472 | "dependencies": { 2473 | "isexe": "^2.0.0" 2474 | }, 2475 | "bin": { 2476 | "node-which": "bin/node-which" 2477 | }, 2478 | "engines": { 2479 | "node": ">= 8" 2480 | } 2481 | }, 2482 | "node_modules/wrappy": { 2483 | "version": "1.0.2", 2484 | "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", 2485 | "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", 2486 | "license": "ISC" 2487 | }, 2488 | "node_modules/ws": { 2489 | "version": "8.18.0", 2490 | "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz", 2491 | "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==", 2492 | "license": "MIT", 2493 | "peer": true, 2494 | "engines": { 2495 | "node": ">=10.0.0" 2496 | }, 2497 | "peerDependencies": { 2498 | "bufferutil": "^4.0.1", 2499 | "utf-8-validate": ">=5.0.2" 2500 | }, 2501 | "peerDependenciesMeta": { 2502 | "bufferutil": { 2503 | "optional": true 2504 | }, 2505 | "utf-8-validate": { 2506 | "optional": true 2507 | } 2508 | } 2509 | }, 2510 | "node_modules/yocto-queue": { 2511 | "version": "1.1.1", 2512 | "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz", 2513 | "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==", 2514 | "license": "MIT", 2515 | "engines": { 2516 | "node": ">=12.20" 2517 | }, 2518 | "funding": { 2519 | "url": "https://github.com/sponsors/sindresorhus" 2520 | } 2521 | }, 2522 | "node_modules/zod": { 2523 | "version": "3.24.1", 2524 | "resolved": "https://registry.npmjs.org/zod/-/zod-3.24.1.tgz", 2525 | "integrity": "sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A==", 2526 | "license": "MIT", 2527 | "funding": { 2528 | "url": "https://github.com/sponsors/colinhacks" 2529 | } 2530 | }, 2531 | "node_modules/zod-to-json-schema": { 2532 | "version": "3.24.1", 2533 | "resolved": "https://registry.npmjs.org/zod-to-json-schema/-/zod-to-json-schema-3.24.1.tgz", 2534 | "integrity": "sha512-3h08nf3Vw3Wl3PK+q3ow/lIil81IT2Oa7YpQyUUDsEWbXveMesdfK1xBd2RhCkynwZndAxixji/7SYJJowr62w==", 2535 | "license": "ISC", 2536 | "peerDependencies": { 2537 | "zod": "^3.24.1" 2538 | } 2539 | } 2540 | } 2541 | } 2542 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "open-deep-research", 3 | "version": "0.0.1", 4 | "main": "dist/mcp-server.js", 5 | "type": "module", 6 | "scripts": { 7 | "format": "prettier --write \"src/**/*.{ts,tsx}\"", 8 | "tsx": "tsx --env-file=.env.local", 9 | "start": "tsx --env-file=.env.local src/run.ts", 10 | "start:stdio": "tsx --env-file=.env.local src/mcp-server.ts", 11 | "start:http": "tsx --env-file=.env.local src/http-server.ts", 12 | "build": "tsc", 13 | "build:watch": "tsc --watch", 14 | "serve": "node --env-file=.env.local dist/mcp-server.js", 15 | "serve:http": "node --env-file=.env.local dist/http-server.js", 16 | "docker": "tsx src/run.ts", 17 | "test": "echo \"Error: no test specified\" && exit 1" 18 | }, 19 | "author": "", 20 | "license": "ISC", 21 | "description": "", 22 | "devDependencies": { 23 | "@ianvs/prettier-plugin-sort-imports": "^4.4.1", 24 | "@types/express": "^5.0.2", 25 | "@types/lodash-es": "^4.17.12", 26 | "@types/node": "^22.13.0", 27 | "prettier": "^3.4.2", 28 | "tsx": "^4.19.2", 29 | "typescript": "^5.7.3" 30 | }, 31 | "dependencies": { 32 | "@ai-sdk/openai": "^1.3.22", 33 | "@mendable/firecrawl-js": "^1.16.0", 34 | "@modelcontextprotocol/sdk": "^1.12.1", 35 | "ai": "^4.3.16", 36 | "dotenv": "^16.4.7", 37 | "express": "^5.1.0", 38 | "js-tiktoken": "^1.0.17", 39 | "langfuse": "^3.35.2", 40 | "lodash-es": "^4.17.21", 41 | "p-limit": "^6.2.0", 42 | "zod": "^3.24.1" 43 | }, 44 | "engines": { 45 | "node": "22.x" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /prettier.config.mjs: -------------------------------------------------------------------------------- 1 | /** @type {import('prettier').Config} */ 2 | export default { 3 | endOfLine: 'lf', 4 | semi: true, 5 | useTabs: false, 6 | singleQuote: true, 7 | arrowParens: 'avoid', 8 | tabWidth: 2, 9 | trailingComma: 'all', 10 | importOrder: [ 11 | '^(react/(.*)$)|^(react$)', 12 | '^(next/(.*)$)|^(next$)', 13 | '', 14 | '', 15 | '@repo/(.*)$', 16 | '', 17 | '^@/(.*)$', 18 | '', 19 | '^[./]', 20 | ], 21 | importOrderParserPlugins: ['typescript', 'jsx'], 22 | importOrderTypeScriptVersion: '5.7.2', 23 | plugins: ['@ianvs/prettier-plugin-sort-imports'], 24 | }; 25 | -------------------------------------------------------------------------------- /src/ai/observability.ts: -------------------------------------------------------------------------------- 1 | import { Langfuse } from 'langfuse'; 2 | 3 | const langfuse = new Langfuse({ 4 | secretKey: process.env.LANGFUSE_SECRET_KEY!, 5 | publicKey: process.env.LANGFUSE_PUBLIC_KEY!, 6 | baseUrl: process.env.LANGFUSE_BASEURL || 'https://us.cloud.langfuse.com', 7 | release: process.env.LANGFUSE_RELEASE || 'dev-release', 8 | requestTimeout: 10000, 9 | }); 10 | 11 | langfuse.on('error', error => { 12 | process.stderr.write(`Langfuse error: ${error}\n`); 13 | }); 14 | 15 | export default langfuse; 16 | -------------------------------------------------------------------------------- /src/ai/providers.ts: -------------------------------------------------------------------------------- 1 | import { createOpenAI, type OpenAIProviderSettings } from '@ai-sdk/openai'; 2 | import { type LanguageModelV1 } from '@ai-sdk/provider'; 3 | import { getEncoding } from 'js-tiktoken'; 4 | 5 | import langfuse from './observability.js'; 6 | import { RecursiveCharacterTextSplitter } from './text-splitter.js'; 7 | 8 | interface CustomOpenAIProviderSettings extends OpenAIProviderSettings { 9 | baseURL?: string; 10 | } 11 | 12 | // Create OpenAI provider 13 | const openai = createOpenAI({ 14 | apiKey: process.env.OPENAI_API_KEY!, 15 | baseURL: process.env.OPENAI_ENDPOINT || 'https://api.openai.com/v1', 16 | } as CustomOpenAIProviderSettings); 17 | 18 | const customModel = process.env.OPENAI_MODEL || 'o4-mini-2025-04-16'; 19 | 20 | // Create model with Langfuse instrumentation 21 | const baseModel = openai(customModel, { 22 | reasoningEffort: customModel.startsWith('o') ? 'medium' : undefined, 23 | structuredOutputs: true, 24 | }); 25 | 26 | export const o4MiniModel = { 27 | ...baseModel, 28 | defaultObjectGenerationMode: 'json', 29 | async doGenerate(options) { 30 | const generation = langfuse.generation({ 31 | name: 'LLM Generation', 32 | model: customModel, 33 | input: options, 34 | modelParameters: { 35 | reasoningEffort: customModel.startsWith('o') ? 'medium' : undefined, 36 | structuredOutputs: true, 37 | }, 38 | }); 39 | 40 | try { 41 | const result = await baseModel.doGenerate(options); 42 | generation.end({ output: result }); 43 | return result; 44 | } catch (error) { 45 | generation.end({ metadata: { error: String(error) } }); 46 | throw error; 47 | } 48 | }, 49 | } as LanguageModelV1; 50 | 51 | const MinChunkSize = 140; 52 | const encoder = getEncoding('o200k_base'); 53 | 54 | // trim prompt to maximum context size 55 | export function trimPrompt( 56 | prompt: string, 57 | contextSize = Number(process.env.CONTEXT_SIZE) || 128_000, 58 | ) { 59 | if (!prompt) { 60 | return ''; 61 | } 62 | 63 | const length = encoder.encode(prompt).length; 64 | if (length <= contextSize) { 65 | return prompt; 66 | } 67 | 68 | const overflowTokens = length - contextSize; 69 | // on average it's 3 characters per token, so multiply by 3 to get a rough estimate of the number of characters 70 | const chunkSize = prompt.length - overflowTokens * 3; 71 | if (chunkSize < MinChunkSize) { 72 | return prompt.slice(0, MinChunkSize); 73 | } 74 | 75 | const splitter = new RecursiveCharacterTextSplitter({ 76 | chunkSize, 77 | chunkOverlap: 0, 78 | }); 79 | const trimmedPrompt = splitter.splitText(prompt)[0] ?? ''; 80 | 81 | // last catch, there's a chance that the trimmed prompt is same length as the original prompt, due to how tokens are split & innerworkings of the splitter, handle this case by just doing a hard cut 82 | if (trimmedPrompt.length === prompt.length) { 83 | return trimPrompt(prompt.slice(0, chunkSize), contextSize); 84 | } 85 | 86 | // recursively trim until the prompt is within the context size 87 | return trimPrompt(trimmedPrompt, contextSize); 88 | } 89 | -------------------------------------------------------------------------------- /src/ai/text-splitter.test.ts: -------------------------------------------------------------------------------- 1 | import assert from 'node:assert'; 2 | import { beforeEach, describe, it } from 'node:test'; 3 | 4 | import { RecursiveCharacterTextSplitter } from './text-splitter.js'; 5 | 6 | describe('RecursiveCharacterTextSplitter', () => { 7 | let splitter: RecursiveCharacterTextSplitter; 8 | 9 | beforeEach(() => { 10 | splitter = new RecursiveCharacterTextSplitter({ 11 | chunkSize: 50, 12 | chunkOverlap: 10, 13 | }); 14 | }); 15 | 16 | it('Should correctly split text by separators', () => { 17 | const text = 'Hello world, this is a test of the recursive text splitter.'; 18 | 19 | // Test with initial chunkSize 20 | assert.deepEqual(splitter.splitText(text), [ 21 | 'Hello world', 22 | 'this is a test of the recursive text splitter', 23 | ]); 24 | 25 | // Test with updated chunkSize 26 | splitter.chunkSize = 100; 27 | assert.deepEqual( 28 | splitter.splitText( 29 | 'Hello world, this is a test of the recursive text splitter. If I have a period, it should split along the period.', 30 | ), 31 | [ 32 | 'Hello world, this is a test of the recursive text splitter', 33 | 'If I have a period, it should split along the period.', 34 | ], 35 | ); 36 | 37 | // Test with another updated chunkSize 38 | splitter.chunkSize = 110; 39 | assert.deepEqual( 40 | splitter.splitText( 41 | 'Hello world, this is a test of the recursive text splitter. If I have a period, it should split along the period.\nOr, if there is a new line, it should prioritize splitting on new lines instead.', 42 | ), 43 | [ 44 | 'Hello world, this is a test of the recursive text splitter', 45 | 'If I have a period, it should split along the period.', 46 | 'Or, if there is a new line, it should prioritize splitting on new lines instead.', 47 | ], 48 | ); 49 | }); 50 | 51 | it('Should handle empty string', () => { 52 | assert.deepEqual(splitter.splitText(''), []); 53 | }); 54 | 55 | it('Should handle special characters and large texts', () => { 56 | const largeText = 'A'.repeat(1000); 57 | splitter.chunkSize = 200; 58 | assert.deepEqual( 59 | splitter.splitText(largeText), 60 | Array(5).fill('A'.repeat(200)), 61 | ); 62 | 63 | const specialCharText = 'Hello!@# world$%^ &*( this) is+ a-test'; 64 | assert.deepEqual(splitter.splitText(specialCharText), [ 65 | 'Hello!@#', 66 | 'world$%^', 67 | '&*( this)', 68 | 'is+', 69 | 'a-test', 70 | ]); 71 | }); 72 | 73 | it('Should handle chunkSize equal to chunkOverlap', () => { 74 | splitter.chunkSize = 50; 75 | splitter.chunkOverlap = 50; 76 | assert.throws( 77 | () => splitter.splitText('Invalid configuration'), 78 | new Error('Cannot have chunkOverlap >= chunkSize'), 79 | ); 80 | }); 81 | }); 82 | -------------------------------------------------------------------------------- /src/ai/text-splitter.ts: -------------------------------------------------------------------------------- 1 | interface TextSplitterParams { 2 | chunkSize: number; 3 | 4 | chunkOverlap: number; 5 | } 6 | 7 | abstract class TextSplitter implements TextSplitterParams { 8 | chunkSize = 1000; 9 | chunkOverlap = 200; 10 | 11 | constructor(fields?: Partial) { 12 | this.chunkSize = fields?.chunkSize ?? this.chunkSize; 13 | this.chunkOverlap = fields?.chunkOverlap ?? this.chunkOverlap; 14 | if (this.chunkOverlap >= this.chunkSize) { 15 | throw new Error('Cannot have chunkOverlap >= chunkSize'); 16 | } 17 | } 18 | 19 | abstract splitText(text: string): string[]; 20 | 21 | createDocuments(texts: string[]): string[] { 22 | const documents: string[] = []; 23 | for (let i = 0; i < texts.length; i += 1) { 24 | const text = texts[i]; 25 | for (const chunk of this.splitText(text!)) { 26 | documents.push(chunk); 27 | } 28 | } 29 | return documents; 30 | } 31 | 32 | splitDocuments(documents: string[]): string[] { 33 | return this.createDocuments(documents); 34 | } 35 | 36 | private joinDocs(docs: string[], separator: string): string | null { 37 | const text = docs.join(separator).trim(); 38 | return text === '' ? null : text; 39 | } 40 | 41 | mergeSplits(splits: string[], separator: string): string[] { 42 | const docs: string[] = []; 43 | const currentDoc: string[] = []; 44 | let total = 0; 45 | for (const d of splits) { 46 | const _len = d.length; 47 | if (total + _len >= this.chunkSize) { 48 | if (total > this.chunkSize) { 49 | console.warn( 50 | `Created a chunk of size ${total}, + 51 | which is longer than the specified ${this.chunkSize}`, 52 | ); 53 | } 54 | if (currentDoc.length > 0) { 55 | const doc = this.joinDocs(currentDoc, separator); 56 | if (doc !== null) { 57 | docs.push(doc); 58 | } 59 | // Keep on popping if: 60 | // - we have a larger chunk than in the chunk overlap 61 | // - or if we still have any chunks and the length is long 62 | while ( 63 | total > this.chunkOverlap || 64 | (total + _len > this.chunkSize && total > 0) 65 | ) { 66 | total -= currentDoc[0]!.length; 67 | currentDoc.shift(); 68 | } 69 | } 70 | } 71 | currentDoc.push(d); 72 | total += _len; 73 | } 74 | const doc = this.joinDocs(currentDoc, separator); 75 | if (doc !== null) { 76 | docs.push(doc); 77 | } 78 | return docs; 79 | } 80 | } 81 | 82 | export interface RecursiveCharacterTextSplitterParams 83 | extends TextSplitterParams { 84 | separators: string[]; 85 | } 86 | 87 | export class RecursiveCharacterTextSplitter 88 | extends TextSplitter 89 | implements RecursiveCharacterTextSplitterParams 90 | { 91 | separators: string[] = ['\n\n', '\n', '.', ',', '>', '<', ' ', '']; 92 | 93 | constructor(fields?: Partial) { 94 | super(fields); 95 | this.separators = fields?.separators ?? this.separators; 96 | } 97 | 98 | splitText(text: string): string[] { 99 | const finalChunks: string[] = []; 100 | 101 | // Get appropriate separator to use 102 | let separator: string = this.separators[this.separators.length - 1]!; 103 | for (const s of this.separators) { 104 | if (s === '') { 105 | separator = s; 106 | break; 107 | } 108 | if (text.includes(s)) { 109 | separator = s; 110 | break; 111 | } 112 | } 113 | 114 | // Now that we have the separator, split the text 115 | let splits: string[]; 116 | if (separator) { 117 | splits = text.split(separator); 118 | } else { 119 | splits = text.split(''); 120 | } 121 | 122 | // Now go merging things, recursively splitting longer texts. 123 | let goodSplits: string[] = []; 124 | for (const s of splits) { 125 | if (s.length < this.chunkSize) { 126 | goodSplits.push(s); 127 | } else { 128 | if (goodSplits.length) { 129 | const mergedText = this.mergeSplits(goodSplits, separator); 130 | finalChunks.push(...mergedText); 131 | goodSplits = []; 132 | } 133 | const otherInfo = this.splitText(s); 134 | finalChunks.push(...otherInfo); 135 | } 136 | } 137 | if (goodSplits.length) { 138 | const mergedText = this.mergeSplits(goodSplits, separator); 139 | finalChunks.push(...mergedText); 140 | } 141 | return finalChunks; 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path'; 2 | import { fileURLToPath } from 'url'; 3 | import { config } from 'dotenv'; 4 | import { z } from 'zod'; 5 | 6 | // Get the directory name of the current module 7 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 8 | 9 | // Load environment variables from .env.local 10 | config({ path: resolve(__dirname, '../.env.local') }); 11 | 12 | // Define and validate the environment schema 13 | const envSchema = z.object({ 14 | OPENAI_API_KEY: z.string().min(1), 15 | FIRECRAWL_BASE_URL: z.string().url().optional(), 16 | FIRECRAWL_KEY: z.string().optional(), 17 | FIRECRAWL_CONCURRENCY: z.string().transform(Number).default('2'), 18 | LANGFUSE_PUBLIC_KEY: z.string().optional(), 19 | LANGFUSE_SECRET_KEY: z.string().optional(), 20 | }); 21 | 22 | // Parse and validate environment variables 23 | const env = envSchema.parse(process.env); 24 | 25 | // Export the validated config 26 | export const Config = { 27 | openai: { 28 | apiKey: env.OPENAI_API_KEY, 29 | }, 30 | firecrawl: { 31 | baseUrl: env.FIRECRAWL_BASE_URL, 32 | apiKey: env.FIRECRAWL_BASE_URL ? null : env.FIRECRAWL_KEY, // No key needed for local instance 33 | concurrency: env.FIRECRAWL_CONCURRENCY, 34 | }, 35 | langfuse: { 36 | publicKey: env.LANGFUSE_PUBLIC_KEY, 37 | secretKey: env.LANGFUSE_SECRET_KEY, 38 | }, 39 | isLocalFirecrawl: !!env.FIRECRAWL_BASE_URL, 40 | } as const; 41 | 42 | // Export individual configs for convenience 43 | export const { openai, firecrawl, langfuse } = Config; 44 | -------------------------------------------------------------------------------- /src/deep-research.ts: -------------------------------------------------------------------------------- 1 | import { resolve } from 'path'; 2 | import { fileURLToPath } from 'url'; 3 | import FirecrawlApp, { SearchResponse } from '@mendable/firecrawl-js'; 4 | import { generateObject } from 'ai'; 5 | import { config } from 'dotenv'; 6 | import { compact } from 'lodash-es'; 7 | import pLimit from 'p-limit'; 8 | import { z } from 'zod'; 9 | 10 | import { o4MiniModel, trimPrompt } from './ai/providers.js'; 11 | import { firecrawl as firecrawlConfig } from './config.js'; 12 | import { OutputManager } from './output-manager.js'; 13 | import { systemPrompt } from './prompt.js'; 14 | 15 | // Get the directory name of the current module 16 | const __dirname = fileURLToPath(new URL('.', import.meta.url)); 17 | 18 | // Load environment variables from .env.local 19 | config({ path: resolve(__dirname, '../.env.local') }); 20 | 21 | // Initialize output manager for coordinated console/progress output 22 | const output = new OutputManager(); 23 | 24 | // Replace console.log with output.log 25 | function log(...args: any[]) { 26 | output.log(...args); 27 | } 28 | 29 | export type ResearchProgress = { 30 | currentDepth: number; 31 | totalDepth: number; 32 | currentBreadth: number; 33 | totalBreadth: number; 34 | currentQuery?: string; 35 | parentQuery?: string; // Track parent query for showing relationships 36 | totalQueries: number; 37 | completedQueries: number; 38 | learningsCount?: number; // Track learnings for this branch 39 | learnings?: string[]; // The actual learnings content 40 | followUpQuestions?: string[]; // Follow-up questions generated 41 | }; 42 | 43 | type SourceMetadata = { 44 | url: string; 45 | title?: string; 46 | publishDate?: string; 47 | domain: string; 48 | relevanceScore?: number; 49 | reliabilityScore: number; 50 | reliabilityReasoning: string; 51 | }; 52 | 53 | // Configurable concurrency limit 54 | const ConcurrencyLimit = firecrawlConfig.concurrency; 55 | 56 | // Initialize Firecrawl with config 57 | const firecrawl = new FirecrawlApp({ 58 | apiKey: firecrawlConfig.apiKey, 59 | apiUrl: firecrawlConfig.baseUrl, 60 | }); 61 | 62 | type LearningWithReliability = { 63 | content: string; 64 | reliability: number; 65 | }; 66 | 67 | export type ResearchDirection = { 68 | question: string; 69 | priority: number; 70 | parentGoal?: string; // Track which research goal led to this question 71 | }; 72 | 73 | async function generateSerpQueries({ 74 | query, 75 | numQueries = 3, 76 | learnings, 77 | learningReliabilities, 78 | researchDirections = [], 79 | }: { 80 | query: string; 81 | numQueries?: number; 82 | learnings?: string[]; 83 | learningReliabilities?: number[]; 84 | researchDirections?: ResearchDirection[]; 85 | }) { 86 | // Convert to properly typed weighted learnings 87 | const weightedLearnings: LearningWithReliability[] = learnings && learningReliabilities 88 | ? learnings.map((content, i) => ({ 89 | content, 90 | reliability: learningReliabilities[i] || 0.5 91 | })) 92 | : []; 93 | 94 | const res = await generateObject({ 95 | model: o4MiniModel, 96 | system: systemPrompt(), 97 | prompt: `Given the following prompt from the user, generate a list of SERP queries to research the topic. Return a maximum of ${numQueries} queries, but feel free to return less if the original prompt is clear. Make sure each query is unique and not similar to each other. 98 | 99 | ${weightedLearnings.length > 0 100 | ? `Here are previous learnings with their reliability scores (higher score means more reliable): 101 | ${weightedLearnings.map(l => `[Reliability: ${l.reliability.toFixed(2)}] ${l.content}`).join('\n')} 102 | 103 | When generating new queries: 104 | - Follow up on promising leads from reliable sources (reliability >= 0.7) 105 | - For less reliable information (reliability < 0.7), consider generating verification queries that are likely to find authoritative sources 106 | - Make each query specific and targeted to advance the research in a clear direction` 107 | : ''} 108 | 109 | ${researchDirections.length > 0 110 | ? `\nPrioritized research directions to explore (higher priority = more important): 111 | ${researchDirections 112 | .sort((a, b) => b.priority - a.priority) 113 | .map(d => `[Priority: ${d.priority}] ${d.question}${d.parentGoal ? `\n (From previous goal: ${d.parentGoal})` : ''}`) 114 | .join('\n')} 115 | 116 | Focus on generating queries that address these research directions, especially the higher priority ones.` 117 | : ''} 118 | 119 | ${query}`, 120 | schema: z.object({ 121 | queries: z 122 | .array( 123 | z.object({ 124 | query: z.string().describe('The SERP query'), 125 | researchGoal: z 126 | .string() 127 | .describe( 128 | 'First talk about the goal of the research that this query is meant to accomplish, then go deeper into how to advance the research once the results are found, mention additional research directions. Be as specific as possible, especially for additional research directions.', 129 | ), 130 | reliabilityThreshold: z 131 | .number() 132 | .describe('Minimum reliability score (between 0 and 1) needed for sources to be considered trustworthy for this query. Higher values (e.g. 0.7+) for verification queries, lower values (e.g. 0.3) for exploratory queries.'), 133 | isVerificationQuery: z 134 | .boolean() 135 | .describe('Whether this query is specifically trying to verify information from less reliable sources'), 136 | relatedDirection: z 137 | .string() 138 | .nullable() 139 | .describe('If this query addresses a specific research direction from the input, specify which one. Set to null if not applicable.') 140 | }) 141 | ) 142 | .describe(`List of SERP queries. Generate at most ${numQueries} queries, but feel free to return less if the original prompt is clear. Each query should be unique and advance the research in a meaningful way.`), 143 | }), 144 | }); 145 | 146 | // Ensure reliability thresholds are within valid range 147 | const validatedQueries = res.object.queries.map(query => ({ 148 | ...query, 149 | reliabilityThreshold: Math.max(0, Math.min(1, query.reliabilityThreshold)) 150 | })); 151 | 152 | // Log more detailed information about query generation 153 | const verificationQueries = validatedQueries.filter(q => q.isVerificationQuery); 154 | if (verificationQueries.length > 0) { 155 | log(`Generated ${verificationQueries.length} verification queries to check information from less reliable sources`); 156 | } 157 | 158 | // Log which research directions are being addressed 159 | const queriesWithDirections = validatedQueries.filter(q => q.relatedDirection !== null); 160 | if (queriesWithDirections.length > 0) { 161 | log(`Queries addressing research directions:\n${queriesWithDirections 162 | .map(q => `- "${q.query}" addresses: ${q.relatedDirection}`) 163 | .join('\n')}`); 164 | } 165 | 166 | return validatedQueries; 167 | } 168 | 169 | async function evaluateSourceReliability(domain: string, context: string): Promise<{ 170 | score: number; 171 | reasoning: string; 172 | }> { 173 | const res = await generateObject({ 174 | model: o4MiniModel, 175 | system: systemPrompt(), 176 | prompt: `Evaluate the reliability of the following source domain for research about: "${context}" 177 | 178 | Domain: ${domain} 179 | 180 | Consider factors like: 181 | 1. Editorial standards and fact-checking processes 182 | 2. Domain expertise in the subject matter 183 | 3. Reputation for accuracy and objectivity 184 | 4. Transparency about sources and methodology 185 | 5. Professional vs user-generated content 186 | 6. Commercial biases or conflicts of interest 187 | 7. Academic or professional credentials 188 | 8. Track record in the field 189 | 190 | Return a reliability score between 0 and 1, where: 191 | - 0.9-1.0: Highest reliability (e.g. peer-reviewed journals, primary sources) 192 | - 0.7-0.89: Very reliable (e.g. respected news organizations) 193 | - 0.5-0.69: Moderately reliable (e.g. industry blogs with editorial oversight) 194 | - 0.3-0.49: Limited reliability (e.g. personal blogs, commercial sites) 195 | - 0-0.29: Low reliability (e.g. known misinformation sources)`, 196 | schema: z.object({ 197 | score: z.number().describe('Reliability score between 0 and 1'), 198 | reasoning: z.string().describe('Brief explanation of the reliability assessment, one or two sentences'), 199 | domainExpertise: z.string().describe('Assessment of domain expertise in this specific topic') 200 | }) 201 | }); 202 | 203 | return { 204 | score: res.object.score, 205 | reasoning: res.object.reasoning 206 | }; 207 | } 208 | 209 | async function processSerpResult({ 210 | query, 211 | result, 212 | numLearnings = 3, 213 | numFollowUpQuestions = 3, 214 | reliabilityThreshold = 0.3, 215 | researchGoal = '', 216 | }: { 217 | query: string; 218 | result: SearchResponse; 219 | numLearnings?: number; 220 | numFollowUpQuestions?: number; 221 | reliabilityThreshold?: number; 222 | researchGoal?: string; 223 | }): Promise<{ 224 | learnings: string[]; 225 | learningConfidences: number[]; 226 | followUpQuestions: string[]; 227 | followUpPriorities: number[]; 228 | sourceMetadata: SourceMetadata[]; 229 | weightedLearnings: LearningWithReliability[]; 230 | }> { 231 | const contents = compact(result.data.map(item => item.markdown)).map( 232 | content => trimPrompt(content, 25_000), 233 | ); 234 | 235 | // Evaluate source reliability for each domain 236 | const sourceMetadataPromises = compact(result.data.map(async item => { 237 | if (!item.url) return null; 238 | try { 239 | const domain = new URL(item.url).hostname; 240 | const reliability = await evaluateSourceReliability(domain, query); 241 | return { 242 | url: item.url, 243 | title: item.title || undefined, 244 | publishDate: undefined, 245 | domain, 246 | relevanceScore: undefined, 247 | reliabilityScore: reliability.score, 248 | reliabilityReasoning: reliability.reasoning 249 | } as SourceMetadata; 250 | } catch (e) { 251 | return null; 252 | } 253 | })); 254 | 255 | const sourceMetadata = compact(await Promise.all(sourceMetadataPromises)); 256 | 257 | // Sort and filter contents by reliability 258 | const contentWithMetadata = contents 259 | .map((content, i) => ({ 260 | content, 261 | metadata: sourceMetadata[i] 262 | })) 263 | .filter((item): item is { content: string; metadata: SourceMetadata } => !!item.metadata); 264 | 265 | // Sort by reliability and filter using the provided threshold 266 | const sortedContents = contentWithMetadata 267 | .sort((a, b) => b.metadata.reliabilityScore - a.metadata.reliabilityScore) 268 | .filter(item => item.metadata.reliabilityScore >= reliabilityThreshold) 269 | .map(item => item.content); 270 | 271 | log(`Ran ${query}, found ${contents.length} contents (${sourceMetadata.filter(m => m.reliabilityScore >= reliabilityThreshold).length} above reliability threshold ${reliabilityThreshold})`); 272 | 273 | const res = await generateObject({ 274 | model: o4MiniModel, 275 | abortSignal: AbortSignal.timeout(60_000), 276 | system: systemPrompt(), 277 | prompt: `Given the following contents from a SERP search for the query ${query}, generate a list of learnings from the contents. Return a maximum of ${numLearnings} learnings, but feel free to return less if the contents are clear. Make sure each learning is unique and not similar to each other. The learnings should be concise and to the point, as detailed and information dense as possible. Make sure to include any entities like people, places, companies, products, things, etc in the learnings, as well as any exact metrics, numbers, or dates. 278 | 279 | ${researchGoal ? `Research Goal: ${researchGoal} 280 | This research is specifically aimed at: ${researchGoal}. Focus on findings that contribute to this goal. 281 | 282 | ` : ''}Weight information by source reliability - be more confident in information from highly reliable sources and more cautious about information from less reliable sources. If possible, try to verify information from less reliable sources against more reliable ones. 283 | 284 | Also generate up to ${numFollowUpQuestions} follow-up questions, prioritized by reliability gaps and research needs${researchGoal ? ', keeping in mind the research goal' : ''}. 285 | 286 | ${contentWithMetadata 287 | .map(({ content, metadata }) => 288 | `\n${content}\n` 289 | ) 290 | .join('\n')}`, 291 | schema: z.object({ 292 | learnings: z 293 | .array(z.object({ 294 | content: z.string(), 295 | confidence: z.number().describe('Confidence in this learning based on source reliability (between 0 and 1)'), 296 | sources: z.array(z.string()).describe('List of source domains that support this learning') 297 | })) 298 | .describe(`List of learnings, max of ${numLearnings}`), 299 | followUpQuestions: z 300 | .array(z.object({ 301 | question: z.string(), 302 | priority: z.number().describe('Priority of this question (1-5) based on current source reliability gaps'), 303 | reason: z.string().describe('Why this follow-up is needed, especially regarding source reliability') 304 | })) 305 | .describe(`Follow-up questions to research, max of ${numFollowUpQuestions}, prioritized by reliability gaps`), 306 | sourceQuality: z.object({ 307 | mostReliableSources: z.array(z.string()), 308 | contentGaps: z.array(z.string()), 309 | reliabilityAnalysis: z.string() 310 | }) 311 | }), 312 | }); 313 | 314 | // Create properly typed weighted learnings 315 | const weightedLearnings: LearningWithReliability[] = res.object.learnings.map(l => ({ 316 | content: l.content, 317 | reliability: l.confidence 318 | })); 319 | 320 | // Ensure we don't exceed the numFollowUpQuestions limit 321 | const limitedFollowUpQuestions = res.object.followUpQuestions.slice(0, numFollowUpQuestions); 322 | 323 | return { 324 | ...res.object, 325 | sourceMetadata, 326 | learnings: weightedLearnings.map(l => l.content), 327 | learningConfidences: weightedLearnings.map(l => l.reliability), 328 | followUpQuestions: limitedFollowUpQuestions.map(q => q.question), 329 | followUpPriorities: limitedFollowUpQuestions.map(q => q.priority), 330 | weightedLearnings 331 | }; 332 | } 333 | 334 | export async function writeFinalReport({ 335 | prompt, 336 | learnings, 337 | sourceMetadata, 338 | }: { 339 | prompt: string; 340 | learnings: string[]; 341 | visitedUrls: string[]; 342 | sourceMetadata: SourceMetadata[]; 343 | }) { 344 | // Quick reliability analysis 345 | const reliabilityGroups = { 346 | high: sourceMetadata.filter(m => m.reliabilityScore >= 0.8), 347 | medium: sourceMetadata.filter(m => m.reliabilityScore >= 0.5 && m.reliabilityScore < 0.8), 348 | low: sourceMetadata.filter(m => m.reliabilityScore < 0.5) 349 | }; 350 | 351 | const learningsString = trimPrompt( 352 | learnings 353 | .map(learning => `\n${learning}\n`) 354 | .join('\n'), 355 | 150_000, 356 | ); 357 | 358 | const res = await generateObject({ 359 | model: o4MiniModel, 360 | system: systemPrompt(), 361 | prompt: `Given the following prompt from the user, write a final report on the topic using the learnings from research. Make it as detailed as possible, aim for 3 or more pages, include ALL the learnings from research. Consider source reliability when drawing conclusions. 362 | 363 | ${prompt} 364 | 365 | Here are all the learnings from previous research: 366 | 367 | \n${learningsString}\n`, 368 | schema: z.object({ 369 | reportMarkdown: z.string().describe('Final report on the topic in Markdown'), 370 | }), 371 | }); 372 | 373 | // Add a simple sources section with reliability scores 374 | const sourcesSection = '\n\n## Sources\n\n' + sourceMetadata 375 | .sort((a, b) => b.reliabilityScore - a.reliabilityScore) 376 | .map(metadata => { 377 | const parts = [ 378 | `- ${metadata.url}`, 379 | ` - Reliability: ${metadata.reliabilityScore.toFixed(2)} - ${metadata.reliabilityReasoning}`, 380 | ]; 381 | if (metadata.title) { 382 | parts.push(` - Title: ${metadata.title}`); 383 | } 384 | if (metadata.publishDate) { 385 | parts.push(` - Published: ${metadata.publishDate}`); 386 | } 387 | return parts.join('\n'); 388 | }) 389 | .join('\n\n'); 390 | 391 | return res.object.reportMarkdown + sourcesSection; 392 | } 393 | 394 | export async function deepResearch({ 395 | query, 396 | breadth, 397 | depth, 398 | learnings = [], 399 | learningReliabilities = [], 400 | visitedUrls = [], 401 | weightedLearnings = [], 402 | researchDirections = [], // Add structured research directions 403 | onProgress, 404 | }: { 405 | query: string; 406 | breadth: number; 407 | depth: number; 408 | learnings?: string[]; 409 | learningReliabilities?: number[]; 410 | visitedUrls?: string[]; 411 | weightedLearnings?: LearningWithReliability[]; 412 | researchDirections?: ResearchDirection[]; // New parameter 413 | onProgress?: (progress: ResearchProgress) => void; 414 | }): Promise<{ 415 | learnings: string[]; 416 | learningReliabilities: number[]; 417 | visitedUrls: string[]; 418 | sourceMetadata: SourceMetadata[]; 419 | weightedLearnings: LearningWithReliability[]; 420 | }> { 421 | const progress: ResearchProgress = { 422 | currentDepth: depth, 423 | totalDepth: depth, 424 | currentBreadth: breadth, 425 | totalBreadth: breadth, 426 | totalQueries: 0, 427 | completedQueries: 0, 428 | }; 429 | 430 | const reportProgress = (update: Partial) => { 431 | Object.assign(progress, update); 432 | onProgress?.(progress); 433 | }; 434 | 435 | const serpQueries = await generateSerpQueries({ 436 | query, 437 | learnings, 438 | learningReliabilities, 439 | numQueries: breadth, 440 | researchDirections, // Pass research directions to influence query generation 441 | }); 442 | 443 | reportProgress({ 444 | totalQueries: serpQueries.length, 445 | currentQuery: serpQueries[0]?.query, 446 | }); 447 | 448 | const limit = pLimit(ConcurrencyLimit); 449 | 450 | const results = await Promise.all( 451 | serpQueries.map(serpQuery => 452 | limit(async () => { 453 | try { 454 | const result = await firecrawl.search(serpQuery.query, { 455 | timeout: 15000, 456 | limit: serpQuery.isVerificationQuery ? 8 : 5, 457 | scrapeOptions: { 458 | formats: ['markdown'] 459 | }, 460 | }); 461 | 462 | // Collect URLs from this search 463 | const newUrls = compact(result.data.map(item => item.url)); 464 | const newBreadth = Math.ceil(breadth / 2); 465 | const newDepth = depth - 1; 466 | 467 | const processedResult = await processSerpResult({ 468 | query: serpQuery.query, 469 | result, 470 | numFollowUpQuestions: newBreadth, 471 | reliabilityThreshold: serpQuery.reliabilityThreshold, 472 | researchGoal: serpQuery.researchGoal, 473 | }); 474 | 475 | const allLearnings = [...learnings, ...processedResult.learnings]; 476 | const allUrls = [...visitedUrls, ...newUrls]; 477 | const allSourceMetadata = [...(processedResult.sourceMetadata || [])]; 478 | const allWeightedLearnings = [...weightedLearnings, ...processedResult.weightedLearnings]; 479 | 480 | if (newDepth > 0) { 481 | log( 482 | `Researching deeper, breadth: ${newBreadth}, depth: ${newDepth}`, 483 | ); 484 | 485 | reportProgress({ 486 | currentDepth: newDepth, 487 | currentBreadth: newBreadth, 488 | completedQueries: progress.completedQueries + 1, 489 | currentQuery: serpQuery.query, 490 | parentQuery: query, 491 | learningsCount: processedResult.learnings.length, 492 | learnings: processedResult.learnings, 493 | followUpQuestions: processedResult.followUpQuestions, 494 | }); 495 | 496 | const nextQuery = ` 497 | Previous research goal: ${serpQuery.researchGoal} 498 | Follow-up research directions: ${processedResult.followUpQuestions.map(q => `\n${q}`).join('')} 499 | `.trim(); 500 | 501 | return deepResearch({ 502 | query: nextQuery, 503 | breadth: newBreadth, 504 | depth: newDepth, 505 | learnings: allLearnings, 506 | learningReliabilities: processedResult.learningConfidences, 507 | visitedUrls: allUrls, 508 | weightedLearnings: allWeightedLearnings, 509 | researchDirections: processedResult.followUpQuestions.map((q, i) => ({ 510 | question: q, 511 | priority: processedResult.followUpPriorities[i] || 3, // Default priority if undefined 512 | parentGoal: serpQuery.researchGoal 513 | })), 514 | onProgress, 515 | }); 516 | } else { 517 | reportProgress({ 518 | currentDepth: 0, 519 | completedQueries: progress.completedQueries + 1, 520 | currentQuery: serpQuery.query, 521 | }); 522 | return { 523 | learnings: allLearnings, 524 | learningReliabilities: processedResult.learningConfidences, 525 | visitedUrls: allUrls, 526 | sourceMetadata: allSourceMetadata, 527 | weightedLearnings: allWeightedLearnings 528 | }; 529 | } 530 | } catch (e: any) { 531 | if (e.message && e.message.includes('Timeout')) { 532 | log(`Timeout error running query: ${serpQuery.query}: `, e); 533 | } else { 534 | log(`Error running query: ${serpQuery.query}: `, e); 535 | } 536 | return { 537 | learnings: [], 538 | learningReliabilities: [], 539 | visitedUrls: [], 540 | sourceMetadata: [], 541 | weightedLearnings: [] 542 | }; 543 | } 544 | }), 545 | ), 546 | ); 547 | 548 | const combinedResults = { 549 | learnings: [...new Set(results.flatMap(r => r.learnings))], 550 | learningReliabilities: [...new Set(results.flatMap(r => r.learningReliabilities))], 551 | visitedUrls: [...new Set(results.flatMap(r => r.visitedUrls))], 552 | sourceMetadata: [...new Set(results.flatMap(r => r.sourceMetadata))], 553 | weightedLearnings: [...new Set(results.flatMap(r => r.weightedLearnings))] 554 | }; 555 | 556 | return combinedResults; 557 | } -------------------------------------------------------------------------------- /src/feedback.ts: -------------------------------------------------------------------------------- 1 | import { generateObject } from 'ai'; 2 | import { z } from 'zod'; 3 | 4 | import { o4MiniModel } from './ai/providers.js'; 5 | import { systemPrompt } from './prompt.js'; 6 | 7 | export async function generateFeedback({ 8 | query, 9 | numQuestions = 3, 10 | }: { 11 | query: string; 12 | numQuestions?: number; 13 | }) { 14 | const userFeedback = await generateObject({ 15 | model: o4MiniModel, 16 | system: systemPrompt(), 17 | prompt: `Given the following query from the user, ask some follow up questions to clarify the research direction. Return a maximum of ${numQuestions} questions, but feel free to return less if the original query is clear: ${query}`, 18 | schema: z.object({ 19 | questions: z 20 | .array(z.string()) 21 | .describe( 22 | `Follow up questions to clarify the research direction, max of ${numQuestions}`, 23 | ), 24 | }), 25 | }); 26 | 27 | return userFeedback.object.questions.slice(0, numQuestions); 28 | } 29 | -------------------------------------------------------------------------------- /src/http-server.ts: -------------------------------------------------------------------------------- 1 | import express, { Request, Response } from 'express'; 2 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; 3 | import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'; 4 | import { z } from 'zod'; 5 | 6 | import { Config } from './config.js'; 7 | import { deepResearch, writeFinalReport } from './deep-research.js'; 8 | 9 | // Helper function to log to stderr 10 | const log = (...args: any[]) => { 11 | process.stderr.write( 12 | args 13 | .map(arg => (typeof arg === 'string' ? arg : JSON.stringify(arg))) 14 | .join(' ') + '\n', 15 | ); 16 | }; 17 | 18 | // Function to create and configure a new server instance for each request 19 | function createServer(): McpServer { 20 | const server = new McpServer({ 21 | name: 'deep-research', 22 | version: '1.0.0', 23 | }, { capabilities: { logging: {} } }); 24 | 25 | // Define the deep research tool 26 | server.tool( 27 | 'deep-research', 28 | 'Perform deep research on a topic using AI-powered web search', 29 | { 30 | query: z.string().min(1).describe("The research query to investigate"), 31 | depth: z.number().min(1).max(5).describe("How deep to go in the research tree (1-5)"), 32 | breadth: z.number().min(1).max(5).describe("How broad to make each research level (1-5)") 33 | }, 34 | async ({ query, depth, breadth }, { sendNotification }) => { 35 | try { 36 | let currentProgress = ''; 37 | 38 | const result = await deepResearch({ 39 | query, 40 | depth, 41 | breadth, 42 | onProgress: async progress => { 43 | const progressMsg = `Depth ${progress.currentDepth}/${progress.totalDepth}, Query ${progress.completedQueries}/${progress.totalQueries}: ${progress.currentQuery || ''}`; 44 | if (progressMsg !== currentProgress) { 45 | currentProgress = progressMsg; 46 | log(progressMsg); 47 | 48 | try { 49 | await sendNotification({ 50 | method: 'notifications/message', 51 | params: { 52 | level: 'info', 53 | data: progressMsg, 54 | }, 55 | }); 56 | } catch (error) { 57 | log('Error sending progress notification:', error); 58 | } 59 | } 60 | }, 61 | }); 62 | 63 | const report = await writeFinalReport({ 64 | prompt: query, 65 | learnings: result.learnings, 66 | visitedUrls: result.visitedUrls, 67 | sourceMetadata: result.sourceMetadata 68 | }); 69 | 70 | return { 71 | content: [ 72 | { 73 | type: 'text', 74 | text: report, 75 | }, 76 | ], 77 | metadata: { 78 | learnings: result.learnings, 79 | visitedUrls: result.visitedUrls, 80 | stats: { 81 | totalLearnings: result.learnings.length, 82 | totalSources: result.visitedUrls.length, 83 | averageReliability: result.weightedLearnings.reduce((acc, curr) => acc + curr.reliability, 0) / result.weightedLearnings.length 84 | }, 85 | }, 86 | }; 87 | } catch (error) { 88 | log( 89 | 'Error in deep research:', 90 | error instanceof Error ? error.message : String(error), 91 | ); 92 | return { 93 | content: [ 94 | { 95 | type: 'text', 96 | text: `Error performing research: ${error instanceof Error ? error.message : String(error)}`, 97 | }, 98 | ], 99 | isError: true, 100 | }; 101 | } 102 | }, 103 | ); 104 | 105 | return server; 106 | } 107 | 108 | // Log environment check 109 | log('Environment check:', { 110 | hasOpenAiKey: !!Config.openai.apiKey, 111 | hasFirecrawlKey: !!Config.firecrawl.apiKey, 112 | firecrawlBaseUrl: Config.firecrawl.baseUrl || '(using API)', 113 | firecrawlConcurrency: Config.firecrawl.concurrency, 114 | }); 115 | 116 | const app = express(); 117 | app.use(express.json()); 118 | 119 | app.post('/mcp', async (req: Request, res: Response) => { 120 | const server = createServer(); 121 | try { 122 | const transport: StreamableHTTPServerTransport = new StreamableHTTPServerTransport({ 123 | sessionIdGenerator: undefined, 124 | }); 125 | await server.connect(transport); 126 | await transport.handleRequest(req, res, req.body); 127 | 128 | res.on('close', () => { 129 | log('Request closed'); 130 | transport.close(); 131 | server.close(); 132 | }); 133 | } catch (error) { 134 | log('Error handling MCP request:', error); 135 | if (!res.headersSent) { 136 | res.status(500).json({ 137 | jsonrpc: '2.0', 138 | error: { 139 | code: -32603, 140 | message: 'Internal server error', 141 | }, 142 | id: null, 143 | }); 144 | } 145 | } 146 | }); 147 | 148 | app.get('/mcp', async (req: Request, res: Response) => { 149 | log('Received GET MCP request'); 150 | res.writeHead(405).end(JSON.stringify({ 151 | jsonrpc: "2.0", 152 | error: { 153 | code: -32000, 154 | message: "Method not allowed." 155 | }, 156 | id: null 157 | })); 158 | }); 159 | 160 | app.delete('/mcp', async (req: Request, res: Response) => { 161 | log('Received DELETE MCP request'); 162 | res.writeHead(405).end(JSON.stringify({ 163 | jsonrpc: "2.0", 164 | error: { 165 | code: -32000, 166 | message: "Method not allowed." 167 | }, 168 | id: null 169 | })); 170 | }); 171 | 172 | // Start the server 173 | const PORT = 3000; 174 | app.listen(PORT, () => { 175 | log(`Deep Research MCP Stateless Streamable HTTP Server listening on port ${PORT}`); 176 | }); 177 | 178 | // Handle server shutdown 179 | process.on('SIGINT', async () => { 180 | log('Shutting down server...'); 181 | process.exit(0); 182 | }); -------------------------------------------------------------------------------- /src/mcp-server.ts: -------------------------------------------------------------------------------- 1 | import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; 2 | import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'; 3 | import { z } from 'zod'; 4 | 5 | import { Config } from './config.js'; 6 | import { deepResearch, writeFinalReport } from './deep-research.js'; 7 | 8 | // Helper function to log to stderr 9 | const log = (...args: any[]) => { 10 | process.stderr.write( 11 | args 12 | .map(arg => (typeof arg === 'string' ? arg : JSON.stringify(arg))) 13 | .join(' ') + '\n', 14 | ); 15 | }; 16 | 17 | // Log environment check 18 | log('Environment check:', { 19 | hasOpenAiKey: !!Config.openai.apiKey, 20 | hasFirecrawlKey: !!Config.firecrawl.apiKey, 21 | firecrawlBaseUrl: Config.firecrawl.baseUrl || '(using API)', 22 | firecrawlConcurrency: Config.firecrawl.concurrency, 23 | }); 24 | 25 | const server = new McpServer({ 26 | name: 'deep-research', 27 | version: '1.0.0', 28 | }, { capabilities: { logging: {} } }); 29 | 30 | // Define the deep research tool 31 | server.tool( 32 | 'deep-research', 33 | 'Perform deep research on a topic using AI-powered web search', 34 | { 35 | query: z.string().min(1).describe("The research query to investigate"), 36 | depth: z.number().min(1).max(5).describe("How deep to go in the research tree (1-5)"), 37 | breadth: z.number().min(1).max(5).describe("How broad to make each research level (1-5)") 38 | }, 39 | async ({ query, depth, breadth }, { sendNotification }) => { 40 | try { 41 | let currentProgress = ''; 42 | 43 | const result = await deepResearch({ 44 | query, 45 | depth, 46 | breadth, 47 | onProgress: async progress => { 48 | const progressMsg = `Depth ${progress.currentDepth}/${progress.totalDepth}, Query ${progress.completedQueries}/${progress.totalQueries}: ${progress.currentQuery || ''}`; 49 | if (progressMsg !== currentProgress) { 50 | currentProgress = progressMsg; 51 | log(progressMsg); 52 | 53 | try { 54 | await sendNotification({ 55 | method: 'notifications/message', 56 | params: { 57 | level: 'info', 58 | data: progressMsg, 59 | }, 60 | }); 61 | } catch (error) { 62 | log('Error sending progress notification:', error); 63 | } 64 | } 65 | }, 66 | }); 67 | 68 | const report = await writeFinalReport({ 69 | prompt: query, 70 | learnings: result.learnings, 71 | visitedUrls: result.visitedUrls, 72 | sourceMetadata: result.sourceMetadata 73 | }); 74 | 75 | return { 76 | content: [ 77 | { 78 | type: 'text', 79 | text: report, 80 | }, 81 | ], 82 | metadata: { 83 | learnings: result.learnings, 84 | visitedUrls: result.visitedUrls, 85 | stats: { 86 | totalLearnings: result.learnings.length, 87 | totalSources: result.visitedUrls.length, 88 | averageReliability: result.weightedLearnings.reduce((acc, curr) => acc + curr.reliability, 0) / result.weightedLearnings.length 89 | }, 90 | }, 91 | }; 92 | } catch (error) { 93 | log( 94 | 'Error in deep research:', 95 | error instanceof Error ? error.message : String(error), 96 | ); 97 | return { 98 | content: [ 99 | { 100 | type: 'text', 101 | text: `Error performing research: ${error instanceof Error ? error.message : String(error)}`, 102 | }, 103 | ], 104 | isError: true, 105 | }; 106 | } 107 | }, 108 | ); 109 | 110 | async function main() { 111 | try { 112 | const transport = new StdioServerTransport(); 113 | await server.connect(transport); 114 | log('Deep Research MCP Server running on stdio'); 115 | } catch (error) { 116 | log('Error starting server:', error); 117 | process.exit(1); 118 | } 119 | } 120 | 121 | // Handle server shutdown 122 | process.on('SIGINT', async () => { 123 | log('Shutting down server...'); 124 | await server.close(); 125 | process.exit(0); 126 | }); 127 | 128 | main().catch(error => { 129 | log('Fatal error in main():', error); 130 | process.exit(1); 131 | }); -------------------------------------------------------------------------------- /src/output-manager.ts: -------------------------------------------------------------------------------- 1 | import { ResearchProgress } from './deep-research.js'; 2 | 3 | export class OutputManager { 4 | private progressLines: number = 4; 5 | private progressArea: string[] = []; 6 | private initialized: boolean = false; 7 | 8 | constructor() { 9 | // Initialize terminal using stderr 10 | process.stderr.write('\n'.repeat(this.progressLines)); 11 | this.initialized = true; 12 | } 13 | 14 | log(...args: any[]) { 15 | // Move cursor up to progress area 16 | if (this.initialized) { 17 | process.stderr.write(`\x1B[${this.progressLines}A`); 18 | // Clear progress area 19 | process.stderr.write('\x1B[0J'); 20 | } 21 | // Print log message to stderr 22 | console.error(...args); 23 | // Redraw progress area if initialized 24 | if (this.initialized) { 25 | this.drawProgress(); 26 | } 27 | } 28 | 29 | updateProgress(progress: ResearchProgress) { 30 | this.progressArea = [ 31 | `Depth: [${this.getProgressBar(progress.totalDepth - progress.currentDepth, progress.totalDepth)}] ${Math.round(((progress.totalDepth - progress.currentDepth) / progress.totalDepth) * 100)}%`, 32 | `Breadth: [${this.getProgressBar(progress.totalBreadth - progress.currentBreadth, progress.totalBreadth)}] ${Math.round(((progress.totalBreadth - progress.currentBreadth) / progress.totalBreadth) * 100)}%`, 33 | `Queries: [${this.getProgressBar(progress.completedQueries, progress.totalQueries)}] ${Math.round((progress.completedQueries / progress.totalQueries) * 100)}%`, 34 | progress.currentQuery ? `Current: ${progress.currentQuery}` : '', 35 | ]; 36 | this.drawProgress(); 37 | } 38 | 39 | private getProgressBar(value: number, total: number): string { 40 | const width = process.stderr.columns 41 | ? Math.min(30, process.stderr.columns - 20) 42 | : 30; 43 | const filled = Math.round((width * value) / total); 44 | return '█'.repeat(filled) + ' '.repeat(width - filled); 45 | } 46 | 47 | private drawProgress() { 48 | if (!this.initialized || this.progressArea.length === 0) return; 49 | 50 | // Move cursor to progress area 51 | const terminalHeight = process.stderr.rows || 24; 52 | process.stderr.write(`\x1B[${terminalHeight - this.progressLines};1H`); 53 | 54 | // Draw each line of the progress area 55 | for (const line of this.progressArea) { 56 | process.stderr.write(line + '\n'); 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /src/progress-manager.ts: -------------------------------------------------------------------------------- 1 | import { ResearchProgress } from './deep-research.js'; 2 | 3 | export class ProgressManager { 4 | private lastProgress: ResearchProgress | undefined; 5 | private progressLines: number = 4; // Fixed number of lines for progress display 6 | private initialized: boolean = false; 7 | 8 | constructor() { 9 | // Initialize terminal 10 | process.stdout.write('\n'.repeat(this.progressLines)); 11 | } 12 | 13 | private drawProgressBar( 14 | label: string, 15 | value: number, 16 | total: number, 17 | char: string = '=', 18 | ): string { 19 | const width = process.stdout.columns 20 | ? Math.min(30, process.stdout.columns - 20) 21 | : 30; 22 | const percent = (value / total) * 100; 23 | const filled = Math.round((width * percent) / 100); 24 | const empty = width - filled; 25 | 26 | return `${label} [${char.repeat(filled)}${' '.repeat(empty)}] ${Math.round(percent)}%`; 27 | } 28 | 29 | updateProgress(progress: ResearchProgress) { 30 | // Store progress for potential redraw 31 | this.lastProgress = progress; 32 | 33 | // Calculate position for progress bars (at bottom of terminal) 34 | const terminalHeight = process.stdout.rows || 24; 35 | const progressStart = terminalHeight - this.progressLines; 36 | 37 | // Move cursor to progress area 38 | process.stdout.write(`\x1B[${progressStart};1H\x1B[0J`); 39 | 40 | // Draw progress bars horizontally 41 | const lines = [ 42 | this.drawProgressBar( 43 | 'Depth: ', 44 | progress.totalDepth - progress.currentDepth, 45 | progress.totalDepth, 46 | '█', 47 | ), 48 | this.drawProgressBar( 49 | 'Breadth: ', 50 | progress.totalBreadth - progress.currentBreadth, 51 | progress.totalBreadth, 52 | '█', 53 | ), 54 | this.drawProgressBar( 55 | 'Queries: ', 56 | progress.completedQueries, 57 | progress.totalQueries, 58 | '█', 59 | ), 60 | ]; 61 | 62 | // Add current query if available 63 | if (progress.currentQuery) { 64 | lines.push(`Current: ${progress.currentQuery}`); 65 | } 66 | 67 | // Output progress bars at fixed position 68 | process.stdout.write(lines.join('\n') + '\n'); 69 | 70 | // Move cursor back up for next output 71 | process.stdout.write(`\x1B[${this.progressLines}A`); 72 | } 73 | 74 | stop() { 75 | // Move cursor past progress area 76 | process.stdout.write(`\x1B[${this.progressLines}B\n`); 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/prompt.ts: -------------------------------------------------------------------------------- 1 | export const systemPrompt = () => { 2 | const now = new Date().toISOString(); 3 | return `You are an expert researcher. Today is ${now}. Follow these instructions when responding: 4 | - You may be asked to research subjects that is after your knowledge cutoff, assume the user is right when presented with news. 5 | - The user is a highly experienced analyst, no need to simplify it, be as detailed as possible and make sure your response is correct. 6 | - Be highly organized. 7 | - Suggest solutions that I didn't think about. 8 | - Be proactive and anticipate my needs. 9 | - Treat me as an expert in all subject matter. 10 | - Mistakes erode my trust, so be accurate and thorough. 11 | - Provide detailed explanations, I'm comfortable with lots of detail. 12 | - Value good arguments over authorities, the source is irrelevant. 13 | - Consider new technologies and contrarian ideas, not just the conventional wisdom. 14 | - You may use high levels of speculation or prediction, just flag it for me.`; 15 | }; 16 | -------------------------------------------------------------------------------- /src/run.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs/promises'; 2 | import * as readline from 'readline'; 3 | 4 | import langfuse from './ai/observability.js'; 5 | import { deepResearch, writeFinalReport } from './deep-research.js'; 6 | import { generateFeedback } from './feedback.js'; 7 | import { OutputManager } from './output-manager.js'; 8 | 9 | const output = new OutputManager(); 10 | 11 | // Helper function for consistent logging 12 | function log(...args: any[]) { 13 | output.log(...args); 14 | } 15 | 16 | const rl = readline.createInterface({ 17 | input: process.stdin, 18 | output: process.stdout, 19 | }); 20 | 21 | // Helper function to get user input 22 | function askQuestion(query: string): Promise { 23 | return new Promise(resolve => { 24 | rl.question(query, answer => { 25 | resolve(answer); 26 | }); 27 | }); 28 | } 29 | 30 | // run the agent 31 | async function run() { 32 | // Create main research session trace 33 | const sessionTrace = langfuse.trace({ 34 | name: 'Research Session', 35 | }); 36 | 37 | // Track initial setup and query refinement 38 | const setupSpan = sessionTrace.span({ name: 'Initial Setup' }); 39 | const initialQuery = await askQuestion('What would you like to research? '); 40 | const breadth = 41 | parseInt( 42 | await askQuestion( 43 | 'Enter research breadth (recommended 2-10, default 4): ', 44 | ), 45 | 10, 46 | ) || 4; 47 | const depth = 48 | parseInt( 49 | await askQuestion('Enter research depth (recommended 1-5, default 2): '), 50 | 10, 51 | ) || 2; 52 | setupSpan.end({ 53 | output: { 54 | initialQuery, 55 | breadth, 56 | depth, 57 | }, 58 | }); 59 | 60 | // Generate and collect feedback questions 61 | log(`Creating research plan...`); 62 | const feedbackSpan = sessionTrace.span({ 63 | name: 'Generate Feedback Questions', 64 | }); 65 | 66 | const followUpQuestions = await generateFeedback({ query: initialQuery }); 67 | 68 | log( 69 | '\nTo better understand your research needs, please answer these follow-up questions:', 70 | ); 71 | const answers: string[] = []; 72 | for (const question of followUpQuestions) { 73 | const answer = await askQuestion(`\n${question}\nYour answer: `); 74 | answers.push(answer); 75 | } 76 | 77 | feedbackSpan.end({ 78 | output: { 79 | questions: followUpQuestions, 80 | answers, 81 | questionCount: followUpQuestions.length, 82 | }, 83 | }); 84 | 85 | // Combine queries and start research 86 | const combinedQuery = ` 87 | Initial Query: ${initialQuery} 88 | Follow-up Questions and Answers: 89 | ${followUpQuestions.map((q: string, i: number) => `Q: ${q}\nA: ${answers[i]}`).join('\n')} 90 | `; 91 | 92 | log('\nResearching your topic...'); 93 | log('\nStarting research with progress tracking...\n'); 94 | 95 | // Track the deep research process 96 | const researchSpan = sessionTrace.span({ 97 | name: 'Deep Research', 98 | input: { 99 | query: combinedQuery, 100 | breadth, 101 | depth, 102 | }, 103 | }); 104 | 105 | // Track research metrics 106 | const researchMetrics = { 107 | queries: new Map(), 108 | sourceReliability: { 109 | high: 0, 110 | medium: 0, 111 | low: 0 112 | }, 113 | totalLearnings: 0 114 | }; 115 | 116 | const { learnings, visitedUrls, sourceMetadata, weightedLearnings } = await deepResearch({ 117 | query: combinedQuery, 118 | breadth, 119 | depth, 120 | onProgress: progress => { 121 | output.updateProgress(progress); 122 | 123 | if (progress.currentQuery && progress.learningsCount) { 124 | // Track query chain in research span 125 | researchSpan.update({ 126 | output: { 127 | queries: { 128 | current: progress.currentQuery, 129 | parent: progress.parentQuery, 130 | depth: progress.currentDepth, 131 | learnings: progress.learnings, 132 | followUps: progress.followUpQuestions 133 | } 134 | } 135 | }); 136 | 137 | // Track metrics 138 | researchMetrics.queries.set(progress.currentQuery, { 139 | query: progress.currentQuery, 140 | parentQuery: progress.parentQuery, 141 | depth: progress.currentDepth, 142 | learnings: progress.learnings || [], 143 | followUpQuestions: progress.followUpQuestions || [] 144 | }); 145 | researchMetrics.totalLearnings += progress.learningsCount; 146 | } 147 | }, 148 | }); 149 | 150 | // Analyze source reliability distribution 151 | sourceMetadata.forEach(source => { 152 | if (source.reliabilityScore >= 0.8) researchMetrics.sourceReliability.high++; 153 | else if (source.reliabilityScore >= 0.5) researchMetrics.sourceReliability.medium++; 154 | else researchMetrics.sourceReliability.low++; 155 | }); 156 | 157 | // Calculate weighted reliability score 158 | const avgReliability = weightedLearnings.reduce((acc, curr) => acc + curr.reliability, 0) / weightedLearnings.length; 159 | 160 | researchSpan.end({ 161 | output: { 162 | summary: { 163 | totalQueries: researchMetrics.queries.size, 164 | totalLearnings: learnings.length, 165 | uniqueSources: visitedUrls.length, 166 | avgReliability, 167 | sourceReliability: researchMetrics.sourceReliability 168 | }, 169 | queries: Array.from(researchMetrics.queries.values()) 170 | } 171 | }); 172 | 173 | log(`\n\nLearnings:\n\n${learnings.join('\n')}`); 174 | log(`\n\nVisited URLs (${visitedUrls.length}):\n\n${visitedUrls.join('\n')}`); 175 | log('Writing final report...'); 176 | 177 | // Track report generation 178 | const reportSpan = sessionTrace.span({ 179 | name: 'Generate Final Report', 180 | input: { 181 | learningCount: learnings.length, 182 | sourceCount: visitedUrls.length 183 | } 184 | }); 185 | 186 | const report = await writeFinalReport({ 187 | prompt: combinedQuery, 188 | learnings, 189 | visitedUrls, 190 | sourceMetadata 191 | }); 192 | 193 | await fs.writeFile('output.md', report, 'utf-8'); 194 | 195 | reportSpan.end({ 196 | output: { 197 | reportLength: report.length, 198 | reportSaved: true 199 | } 200 | }); 201 | 202 | // Update final session metrics 203 | sessionTrace.update({ 204 | output: { 205 | summary: { 206 | totalLearnings: learnings.length, 207 | totalSources: visitedUrls.length, 208 | avgReliability, 209 | sourceReliability: researchMetrics.sourceReliability, 210 | reportLength: report.length 211 | } 212 | } 213 | }); 214 | 215 | await langfuse.shutdownAsync(); 216 | rl.close(); 217 | } 218 | 219 | // Let Langfuse handle error reporting automatically 220 | run().catch(error => { 221 | log('Error:', error); 222 | rl.close(); 223 | }); -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "compilerOptions": { 4 | "declaration": true, 5 | "declarationMap": true, 6 | "esModuleInterop": true, 7 | "incremental": false, 8 | "isolatedModules": true, 9 | "lib": ["es2022", "DOM", "DOM.Iterable"], 10 | "module": "NodeNext", 11 | "moduleDetection": "force", 12 | "moduleResolution": "NodeNext", 13 | "noUncheckedIndexedAccess": true, 14 | "resolveJsonModule": true, 15 | "skipLibCheck": true, 16 | "strict": true, 17 | "target": "ES2022", 18 | "outDir": "./dist", 19 | "rootDir": "./src" 20 | }, 21 | "include": ["src/**/*"], 22 | "exclude": ["node_modules", "dist"] 23 | } 24 | --------------------------------------------------------------------------------