diff --git a/.mcp.json b/.mcp.json index fe7fb663..ee48dfe4 100644 --- a/.mcp.json +++ b/.mcp.json @@ -2,9 +2,9 @@ "mcpServers": { "playwright": { "type": "stdio", - "command": "pnpm", + "command": "bun", "args": [ - "dlx", + "x", "@playwright/mcp@latest" ], "env": {} @@ -20,6 +20,18 @@ "grep": { "type": "http", "url": "https://mcp.grep.app" + }, + "oramaDocs": { + "type": "stdio", + "command": "bun", + "args": [ + "x", + "sitemcp", + "https://docs.orama.com/", + "--concurrency", + "10" + ], + "env": {} } } } diff --git a/README.md b/README.md index c20783c0..08f88f7e 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,128 @@ These toolsets provide functionality to filter, transform, and execute tools. Th Under the hood the StackOneToolSet uses the same OpenAPIParser as the OpenAPIToolSet, but provides some convenience methods for using StackOne API keys and account IDs. +### Meta Tools (Beta) + +> [!WARNING] +> Meta tools are currently in beta and their API may change in future versions. + +StackOne AI SDK includes two powerful meta tools that enable AI agents to discover and orchestrate tool usage intelligently: + +#### Discovering Relevant Tools + +Use the `metaRelevantTool()` method to get a tool that discovers other tools based on natural language queries: + +```typescript +import { StackOneToolSet } from "@stackone/ai"; + +const toolset = new StackOneToolSet(); +const tools = toolset.getTools("hris_*", "account-id"); + +// Get the meta tool for finding relevant tools +const relevantToolsFinder = tools.metaRelevantTool(); + +// Find tools for specific tasks +const result = await relevantToolsFinder.execute({ + query: "list employees", + limit: 5, + minScore: 0.5 +}); + +// Returns tools ranked by relevance with scores and match reasons +``` + +#### Executing Tool Chains + +Use the `metaExecuteTool()` method to orchestrate multiple tool executions with parameter passing: + +```typescript +const tools = toolset.getTools("hris_*", "account-id"); + +// Get the meta tool for executing tool chains +const toolChain = tools.metaExecuteTool(); + +// Execute a complex workflow +const result = await toolChain.execute({ + steps: [ + { + toolName: "hris_list_employees", + parameters: { page_size: "10" }, + stepName: "Get recent employees" + }, + { + toolName: "hris_get_employee", + parameters: { + id: "{{step0.result.items[0].id}}" // Reference previous results + }, + condition: "{{step0.result.items.length}} > 0", // Conditional execution + stepName: "Get employee details" + } + ], + accountId: "your-account-id" +}); +``` + +#### Getting Both Meta Tools + +You can get both meta tools at once using the `metaTools()` method: + +```typescript +const tools = toolset.getTools("hris_*", "account-id"); + +// Get both meta tools +const { metaRelevantTool, metaExecuteTool } = tools.metaTools(); + +// Use them in your AI agent +const aiTools = new Tools([ + metaRelevantTool, + metaExecuteTool +]).toAISDK(); +``` + +[View full examples](examples/meta-tools.ts) + +### Workflow Planning + +While building agents you may find that your workflow is too complex for a general purpose agent. + +StackOne AI SDK provides access to a state of the art planning agent which allows you to create, cache, and execute complex workflows on [verticals supported by StackOne](https://www.stackone.com/integrations). + +For example, onboard a new hire from your ATS to your HRIS. + +```typescript +import { StackOneToolSet } from "@stackone/ai"; + +const toolset = new StackOneToolSet(); + +const onboardWorkflow = await toolset.plan({ + key: "custom_onboarding", + input: "Onboard the last new hire from Teamtailor to Workday", + model: "stackone-planner-latest", + tools: ["hris_*", "ats_*"], + accountIds: ["teamtailor_account_id", "workday_account_id"], + cache: true, // saves the plan to $HOME/.stackone/plans +}); + +// Execute the workflow +await onboardWorkflow.execute(); +``` + +Or use it as a tool in a larger agent (using AI SDK) + +```typescript +await generateText({ + model: openai("gpt-4o"), + prompt: "You are a workplace agent, onboard the latest hires to our systems", + tools: onboardWorkflow.toAISDK(), + maxSteps: 3, +}); +``` + +> [!NOTE] +> The workflow planner is in closed beta and only available to design partners. Apply for the waitlist [here](https://www.stackone.com/demo). + +[View full example](examples/planning.ts) + ## Installation ```bash diff --git a/bun.lock b/bun.lock index b5d71ad9..0e4b8465 100644 --- a/bun.lock +++ b/bun.lock @@ -2,8 +2,9 @@ "lockfileVersion": 1, "workspaces": { "": { - "name": "stackone-ai-ts", + "name": "@stackone/ai", "dependencies": { + "@orama/orama": "^3.1.11", "json-schema": "^0.4.0", }, "devDependencies": { @@ -33,25 +34,25 @@ }, }, "packages": { - "@ai-sdk/openai": ["@ai-sdk/openai@1.1.14", "", { "dependencies": { "@ai-sdk/provider": "1.0.9", "@ai-sdk/provider-utils": "2.1.10" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-r5oD+Sz7z8kfxnXfqR53fYQ1xbT/BeUGhQ26FWzs5gO4j52pGUpzCt0SBm3SH1ZSjFY5O/zoKRnsbrPeBe1sNA=="], + "@ai-sdk/openai": ["@ai-sdk/openai@1.3.23", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8" }, "peerDependencies": { "zod": "^3.0.0" } }, "sha512-86U7rFp8yacUAOE/Jz8WbGcwMCqWvjK33wk5DXkfnAOEn3mx2r7tNSJdjukQFZbAK97VMXGPPHxF+aEARDXRXQ=="], - "@ai-sdk/provider": ["@ai-sdk/provider@1.0.9", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-jie6ZJT2ZR0uVOVCDc9R2xCX5I/Dum/wEK28lx21PJx6ZnFAN9EzD2WsPhcDWfCgGx3OAZZ0GyM3CEobXpa9LA=="], + "@ai-sdk/provider": ["@ai-sdk/provider@1.1.3", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-qZMxYJ0qqX/RfnuIaab+zp8UAeJn/ygXXAffR5I4N0n1IrvA6qBsjc8hXLmBiMV2zoXlifkacF7sEFnYnjBcqg=="], - "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.1.10", "", { "dependencies": { "@ai-sdk/provider": "1.0.9", "eventsource-parser": "^3.0.0", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.0.0" }, "optionalPeers": ["zod"] }, "sha512-4GZ8GHjOFxePFzkl3q42AU0DQOtTQ5w09vmaWUf/pKFXJPizlnzKSUkF0f+VkapIUfDugyMqPMT1ge8XQzVI7Q=="], + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@2.2.8", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "nanoid": "^3.3.8", "secure-json-parse": "^2.7.0" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-fqhG+4sCVv8x7nFzYnFo19ryhAa3w096Kmc3hWxMQfW/TubPOmt3A6tYZhl4mUfQWWQMsuSkLrtjlWuXBVSGQA=="], - "@ai-sdk/react": ["@ai-sdk/react@1.1.18", "", { "dependencies": { "@ai-sdk/provider-utils": "2.1.10", "@ai-sdk/ui-utils": "1.1.16", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.0.0" }, "optionalPeers": ["react", "zod"] }, "sha512-2wlWug6NVAc8zh3pgqtvwPkSNTdA6Q4x9CmrNXCeHcXfJkJ+MuHFQz/I7Wb7mLRajf0DAxsFLIhHyBCEuTkDNw=="], + "@ai-sdk/react": ["@ai-sdk/react@1.2.12", "", { "dependencies": { "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/ui-utils": "1.2.11", "swr": "^2.2.5", "throttleit": "2.1.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["zod"] }, "sha512-jK1IZZ22evPZoQW3vlkZ7wvjYGYF+tRBKXtrcolduIkQ/m/sOAVcVeVDUDvh1T91xCnWCdUGCPZg2avZ90mv3g=="], - "@ai-sdk/ui-utils": ["@ai-sdk/ui-utils@1.1.16", "", { "dependencies": { "@ai-sdk/provider": "1.0.9", "@ai-sdk/provider-utils": "2.1.10", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.0.0" }, "optionalPeers": ["zod"] }, "sha512-jfblR2yZVISmNK2zyNzJZFtkgX57WDAUQXcmn3XUBJyo8LFsADu+/vYMn5AOyBi9qJT0RBk11PEtIxIqvByw3Q=="], + "@ai-sdk/ui-utils": ["@ai-sdk/ui-utils@1.2.11", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "zod-to-json-schema": "^3.24.1" }, "peerDependencies": { "zod": "^3.23.8" } }, "sha512-3zcwCc8ezzFlwp3ZD15wAPjf2Au4s3vAbKsXQVyhxODHcmu0iyPO2Eua6D/vicq/AUm/BAo60r97O6HU+EI0+w=="], - "@babel/generator": ["@babel/generator@7.27.5", "", { "dependencies": { "@babel/parser": "^7.27.5", "@babel/types": "^7.27.3", "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25", "jsesc": "^3.0.2" } }, "sha512-ZGhA37l0e/g2s1Cnzdix0O3aLYm66eF8aufiVteOgnwxgnRP8GoyMj7VWsgWnQbVKXyge7hqrFh2K2TQM6t1Hw=="], + "@babel/generator": ["@babel/generator@7.28.0", "", { "dependencies": { "@babel/parser": "^7.28.0", "@babel/types": "^7.28.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" } }, "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg=="], "@babel/helper-string-parser": ["@babel/helper-string-parser@7.27.1", "", {}, "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA=="], "@babel/helper-validator-identifier": ["@babel/helper-validator-identifier@7.27.1", "", {}, "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow=="], - "@babel/parser": ["@babel/parser@7.27.5", "", { "dependencies": { "@babel/types": "^7.27.3" }, "bin": "./bin/babel-parser.js" }, "sha512-OsQd175SxWkGlzbny8J3K8TnnDD0N3lrIUtB92xwyRpzaenGZhxDvxN/JgU00U3CDZNj9tPuDJ5H0WS4Nt3vKg=="], + "@babel/parser": ["@babel/parser@7.28.0", "", { "dependencies": { "@babel/types": "^7.28.0" }, "bin": "./bin/babel-parser.js" }, "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g=="], - "@babel/types": ["@babel/types@7.27.6", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ETyHEk2VHHvl9b9jZP5IHPavHYk57EhanlRRuae9XCpb/j5bDCbPPMOBfCWhnl/7EDJz0jEMCi/RhccCE8r1+Q=="], + "@babel/types": ["@babel/types@7.28.2", "", { "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" } }, "sha512-ruv7Ae4J5dUYULmeXw1gmb7rYRz57OWCPM57pHojnLq/3Z1CK2lNSLTCVjxVk1F/TZHwOZZrOWi0ur95BbLxNQ=="], "@biomejs/biome": ["@biomejs/biome@1.9.4", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "1.9.4", "@biomejs/cli-darwin-x64": "1.9.4", "@biomejs/cli-linux-arm64": "1.9.4", "@biomejs/cli-linux-arm64-musl": "1.9.4", "@biomejs/cli-linux-x64": "1.9.4", "@biomejs/cli-linux-x64-musl": "1.9.4", "@biomejs/cli-win32-arm64": "1.9.4", "@biomejs/cli-win32-x64": "1.9.4" }, "bin": { "biome": "bin/biome" } }, "sha512-1rkd7G70+o9KkTn5KLmDYXihGoTaIGO9PIIN2ZB7UJxFrWw04CZHPYiMRjYsaDvVV7hP1dYNRLxSANLaBFGpog=="], @@ -77,11 +78,11 @@ "@bundled-es-modules/tough-cookie": ["@bundled-es-modules/tough-cookie@0.1.6", "", { "dependencies": { "@types/tough-cookie": "^4.0.5", "tough-cookie": "^4.1.4" } }, "sha512-dvMHbL464C0zI+Yqxbz6kZ5TOEp7GLW+pry/RWndAR8MJQAXZ2rPmIs8tziTZjeIyhSNZgZbCePtfSbdWqStJw=="], - "@emnapi/core": ["@emnapi/core@1.4.3", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.2", "tslib": "^2.4.0" } }, "sha512-4m62DuCE07lw01soJwPiBGC0nAww0Q+RY70VZ+n49yDIO13yyinhbWCeNnaob0lakDtWQzSdtNWzJeOJt2ma+g=="], + "@emnapi/core": ["@emnapi/core@1.4.5", "", { "dependencies": { "@emnapi/wasi-threads": "1.0.4", "tslib": "^2.4.0" } }, "sha512-XsLw1dEOpkSX/WucdqUhPWP7hDxSvZiY+fsUC14h+FtQ2Ifni4znbBt8punRX+Uj2JG/uDb8nEHVKvrVlvdZ5Q=="], - "@emnapi/runtime": ["@emnapi/runtime@1.4.3", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-pBPWdu6MLKROBX05wSNKcNb++m5Er+KQ9QkB+WVM+pW2Kx9hoSrVTnu3BdkI5eBLZoKu/J6mW/B6i6bJB2ytXQ=="], + "@emnapi/runtime": ["@emnapi/runtime@1.4.5", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-++LApOtY0pEEz1zrd9vy1/zXVaVJJ/EbAF3u0fXIzPJEDtnITsBGbbK0EkM72amhl/R5b+5xx0Y/QhcVOpuulg=="], - "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-5n3nTJblwRi8LlXkJ9eBzu+kZR8Yxcc7ubakyQTFzPMtIhFpUBRbsnc2Dv88IZDIbCDlBiWrknhB4Lsz7mg6BA=="], + "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.0.4", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-PJR+bOmMOPH8AtcTGAyYNiuJ3/Fcoj2XN/gBEWzDIKh254XO+mM9XoXHk5GNEhodxeMznbg7BlRojVbKN+gC6g=="], "@inquirer/confirm": ["@inquirer/confirm@5.1.14", "", { "dependencies": { "@inquirer/core": "^10.1.15", "@inquirer/type": "^3.0.8" }, "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-5yR4IBfe0kXe59r1YCTG8WXkUbl7Z35HK87Sw+WUyGD8wNUx7JvY7laahzeytyE1oLn74bQnL7hstctQxisQ8Q=="], @@ -91,19 +92,17 @@ "@inquirer/type": ["@inquirer/type@3.0.8", "", { "peerDependencies": { "@types/node": ">=18" }, "optionalPeers": ["@types/node"] }, "sha512-lg9Whz8onIHRthWaN1Q9EGLa/0LFJjyM8mEUbL1eTi6yMGvBf8gvyDLtxSXztQsxMvhxxNpJYrwa1YHdq+w4Jw=="], - "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.8", "", { "dependencies": { "@jridgewell/set-array": "^1.2.1", "@jridgewell/sourcemap-codec": "^1.4.10", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA=="], + "@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.12", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg=="], "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], - "@jridgewell/set-array": ["@jridgewell/set-array@1.2.1", "", {}, "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A=="], + "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.4", "", {}, "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw=="], - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.0", "", {}, "sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ=="], + "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.29", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ=="], - "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.25", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ=="], + "@mswjs/interceptors": ["@mswjs/interceptors@0.39.5", "", { "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", "@open-draft/until": "^2.0.0", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "strict-event-emitter": "^0.5.1" } }, "sha512-B9nHSJYtsv79uo7QdkZ/b/WoKm20IkVSmTc/WCKarmDtFwM0dRx2ouEniqwNkzCSLn3fydzKmnMzjtfdOWt3VQ=="], - "@mswjs/interceptors": ["@mswjs/interceptors@0.39.4", "", { "dependencies": { "@open-draft/deferred-promise": "^2.2.0", "@open-draft/logger": "^0.3.0", "@open-draft/until": "^2.0.0", "is-node-process": "^1.2.0", "outvariant": "^1.4.3", "strict-event-emitter": "^0.5.1" } }, "sha512-B82DbrGVCIBrNEfRJbqUFB0eNz0wVzqbenEpmbE71XLVU4yKZbDnRBuxz+7udc/uM7LDWDD4sRJ5tISzHf2QkQ=="], - - "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.11", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.9.0" } }, "sha512-9DPkXtvHydrcOsopiYpUgPHpmj0HWZKMUnL2dZqpvC42lsratuBG06V5ipyno0fUek5VlFsNQ+AcFATSrJXgMA=="], + "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], "@open-draft/deferred-promise": ["@open-draft/deferred-promise@2.2.0", "", {}, "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA=="], @@ -113,43 +112,45 @@ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], - "@oxc-project/runtime": ["@oxc-project/runtime@0.72.3", "", {}, "sha512-FtOS+0v7rZcnjXzYTTqv1vu/KDptD1UztFgoZkYBGe/6TcNFm+SP/jQoLvzau1SPir95WgDOBOUm2Gmsm+bQag=="], + "@orama/orama": ["@orama/orama@3.1.11", "", {}, "sha512-Szki0cgFiXE5F9RLx2lUyEtJllnuCSQ4B8RLDwIjXkVit6qZjoDAxH+xhJs29MjKLDz0tbPLdKFa6QrQ/qoGGA=="], + + "@oxc-project/runtime": ["@oxc-project/runtime@0.71.0", "", {}, "sha512-QwoF5WUXIGFQ+hSxWEib4U/aeLoiDN9JlP18MnBgx9LLPRDfn1iICtcow7Jgey6HLH4XFceWXQD5WBJ39dyJcw=="], - "@oxc-project/types": ["@oxc-project/types@0.72.3", "", {}, "sha512-CfAC4wrmMkUoISpQkFAIfMVvlPfQV3xg7ZlcqPXPOIMQhdKIId44G8W0mCPgtpWdFFAyJ+SFtiM+9vbyCkoVng=="], + "@oxc-project/types": ["@oxc-project/types@0.71.0", "", {}, "sha512-5CwQ4MI+P4MQbjLWXgNurA+igGwu/opNetIE13LBs9+V93R64MLvDKOOLZIXSzEfovU3Zef3q3GjPnMTgJTn2w=="], "@publint/pack": ["@publint/pack@0.1.2", "", {}, "sha512-S+9ANAvUmjutrshV4jZjaiG8XQyuJIZ8a4utWmN/vW1sgQ9IfBnPndwkmQYw53QmouOIytT874u65HEmu6H5jw=="], "@quansync/fs": ["@quansync/fs@0.1.3", "", { "dependencies": { "quansync": "^0.2.10" } }, "sha512-G0OnZbMWEs5LhDyqy2UL17vGhSVHkQIfVojMtEWVenvj0V5S84VBgy86kJIuNsGDp2p7sTKlpSIpBUWdC35OKg=="], - "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-beta.15", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YInZppDBLp5DadbJZGc7xBfDrMCSj3P6i2rPlvOCMlvjBQxJi2kX8Jquh+LufsWUiHD3JsvvH5EuUUc/tF5fkA=="], + "@rolldown/binding-darwin-arm64": ["@rolldown/binding-darwin-arm64@1.0.0-beta.9-commit.d91dfb5", "", { "os": "darwin", "cpu": "arm64" }, "sha512-Mp0/gqiPdepHjjVm7e0yL1acWvI0rJVVFQEADSezvAjon9sjQ7CEg9JnXICD4B1YrPmN9qV/e7cQZCp87tTV4w=="], - "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-beta.15", "", { "os": "darwin", "cpu": "x64" }, "sha512-Zwv8KHU/XdVwLseHG6slJ0FAFklPpiO0sjNvhrcMp1X3F2ajPzUdIO8Cnu3KLmX1GWVSvu6q1kyARLUqPvlh7Q=="], + "@rolldown/binding-darwin-x64": ["@rolldown/binding-darwin-x64@1.0.0-beta.9-commit.d91dfb5", "", { "os": "darwin", "cpu": "x64" }, "sha512-40re4rMNrsi57oavRzIOpRGmg3QRlW6Ea8Q3znaqgOuJuKVrrm2bIQInTfkZJG7a4/5YMX7T951d0+toGLTdCA=="], - "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-beta.15", "", { "os": "freebsd", "cpu": "x64" }, "sha512-FwhNC23Fz9ldHW1/rX4QaoQe4kyOybCgxO9eglue3cbb3ol28KWpQl3xJfvXc9+O6PDefAs4oFBCbtTh8seiUw=="], + "@rolldown/binding-freebsd-x64": ["@rolldown/binding-freebsd-x64@1.0.0-beta.9-commit.d91dfb5", "", { "os": "freebsd", "cpu": "x64" }, "sha512-8BDM939bbMariZupiHp3OmP5N+LXPT4mULA0hZjDaq970PCxv4krZOSMG+HkWUUwmuQROtV+/00xw39EO0P+8g=="], - "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.15", "", { "os": "linux", "cpu": "arm" }, "sha512-E60pNliWl4j7EFEVX2oeJZ5VzR+NG6fvDJoqfqRfCl8wtKIf9E1WPWVQIrT+zkz+Fhc5op8g7h25z6rtxsDy9g=="], + "@rolldown/binding-linux-arm-gnueabihf": ["@rolldown/binding-linux-arm-gnueabihf@1.0.0-beta.9-commit.d91dfb5", "", { "os": "linux", "cpu": "arm" }, "sha512-sntsPaPgrECpBB/+2xrQzVUt0r493TMPI+4kWRMhvMsmrxOqH1Ep5lM0Wua/ZdbfZNwm1aVa5pcESQfNfM4Fhw=="], - "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-beta.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-d+qo1LZ/a3EcQW08byIIZy0PBthmG/7dr69pifmNIet/azWR8jbceQaRFFczVc/NwVV3fsZDCmjG8mgJzsNEAg=="], + "@rolldown/binding-linux-arm64-gnu": ["@rolldown/binding-linux-arm64-gnu@1.0.0-beta.9-commit.d91dfb5", "", { "os": "linux", "cpu": "arm64" }, "sha512-5clBW/I+er9F2uM1OFjJFWX86y7Lcy0M+NqsN4s3o07W+8467Zk8oQa4B45vdaXoNUF/yqIAgKkA/OEdQDxZqA=="], - "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-beta.15", "", { "os": "linux", "cpu": "arm64" }, "sha512-P1hbtYF+5ftJI2Ergs4iARbAk6Xd6WnTQb3CF9kjN3KfJTsRYdo5/fvU8Lz/gzhZVvkCXXH3NxDd9308UBO8cw=="], + "@rolldown/binding-linux-arm64-musl": ["@rolldown/binding-linux-arm64-musl@1.0.0-beta.9-commit.d91dfb5", "", { "os": "linux", "cpu": "arm64" }, "sha512-wv+rnAfQDk9p/CheX8/Kmqk2o1WaFa4xhWI9gOyDMk/ljvOX0u0ubeM8nI1Qfox7Tnh71eV5AjzSePXUhFOyOg=="], - "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-beta.15", "", { "os": "linux", "cpu": "x64" }, "sha512-Q9NM9uMFN9cjcrW7gd9U087B5WzkEj9dQQHOgoENZSy+vYJYS2fINCIG40ljEVC6jXmVrJgUhJKv7elRZM1nng=="], + "@rolldown/binding-linux-x64-gnu": ["@rolldown/binding-linux-x64-gnu@1.0.0-beta.9-commit.d91dfb5", "", { "os": "linux", "cpu": "x64" }, "sha512-gxD0/xhU4Py47IH3bKZbWtvB99tMkUPGPJFRfSc5UB9Osoje0l0j1PPbxpUtXIELurYCqwLBKXIMTQGifox1BQ=="], - "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-beta.15", "", { "os": "linux", "cpu": "x64" }, "sha512-1tuCWuR8gx9PyW2pxAx2ZqnOnwhoY6NWBVP6ZmrjCKQ16NclYc61BzegFXSdugCy8w1QpBPT8/c5oh2W4E5aeA=="], + "@rolldown/binding-linux-x64-musl": ["@rolldown/binding-linux-x64-musl@1.0.0-beta.9-commit.d91dfb5", "", { "os": "linux", "cpu": "x64" }, "sha512-HotuVe3XUjDwqqEMbm3o3IRkP9gdm8raY/btd/6KE3JGLF/cv4+3ff1l6nOhAZI8wulWDPEXPtE7v+HQEaTXnA=="], - "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-beta.15", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.10" }, "cpu": "none" }, "sha512-zrSeYrpTf27hRxMLh0qpkCoWgzRKG8EyR6o09Zt9xkqCOeE5tEK/S3jV1Nii9WSqVCWFRA+OYxKzMNoykV590g=="], + "@rolldown/binding-wasm32-wasi": ["@rolldown/binding-wasm32-wasi@1.0.0-beta.9-commit.d91dfb5", "", { "dependencies": { "@napi-rs/wasm-runtime": "^0.2.4" }, "cpu": "none" }, "sha512-8Cx+ucbd8n2dIr21FqBh6rUvTVL0uTgEtKR7l+MUZ5BgY4dFh1e4mPVX8oqmoYwOxBiXrsD2JIOCz4AyKLKxWA=="], - "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-beta.15", "", { "os": "win32", "cpu": "arm64" }, "sha512-diR41DsMUnkvb9hvW8vuIrA0WaacAN1fu6lPseXhYifAOZN6kvxEwKn7Xib8i0zjdrYErLv7GNSQ48W+xiNOnA=="], + "@rolldown/binding-win32-arm64-msvc": ["@rolldown/binding-win32-arm64-msvc@1.0.0-beta.9-commit.d91dfb5", "", { "os": "win32", "cpu": "arm64" }, "sha512-Vhq5vikrVDxAa75fxsyqj0c0Y/uti/TwshXI71Xb8IeUQJOBnmLUsn5dgYf5ljpYYkNa0z9BPAvUDIDMmyDi+w=="], - "@rolldown/binding-win32-ia32-msvc": ["@rolldown/binding-win32-ia32-msvc@1.0.0-beta.15", "", { "os": "win32", "cpu": "ia32" }, "sha512-oCbbcDC3Lk8YgdxCkG23UqVrvXVvllIBgmmwq89bhq5okPP899OI/P+oTTDsUTbhljzNq1pH8a+mR6YBxAFfvw=="], + "@rolldown/binding-win32-ia32-msvc": ["@rolldown/binding-win32-ia32-msvc@1.0.0-beta.9-commit.d91dfb5", "", { "os": "win32", "cpu": "ia32" }, "sha512-lN7RIg9Iugn08zP2aZN9y/MIdG8iOOCE93M1UrFlrxMTqPf8X+fDzmR/OKhTSd1A2pYNipZHjyTcb5H8kyQSow=="], - "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-beta.15", "", { "os": "win32", "cpu": "x64" }, "sha512-w5hVsOv3dzKo10wAXizmnDvUo1yasn/ps+mcn9H9TiJ/GeRE5/15Y6hG6vUQYRQNLVbYRHUt2qG0MyOoasPcHg=="], + "@rolldown/binding-win32-x64-msvc": ["@rolldown/binding-win32-x64-msvc@1.0.0-beta.9-commit.d91dfb5", "", { "os": "win32", "cpu": "x64" }, "sha512-7/7cLIn48Y+EpQ4CePvf8reFl63F15yPUlg4ZAhl+RXJIfydkdak1WD8Ir3AwAO+bJBXzrfNL+XQbxm0mcQZmw=="], - "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.15", "", {}, "sha512-lvFtIbidq5EqyAAeiVk41ZNjGRgUoGRBIuqpe1VRJ7R8Av7TLAgGWAwGlHNhO7MFkl7MNRX350CsTtIWIYkNIQ=="], + "@rolldown/pluginutils": ["@rolldown/pluginutils@1.0.0-beta.9-commit.d91dfb5", "", {}, "sha512-8sExkWRK+zVybw3+2/kBkYBFeLnEUWz1fT7BLHplpzmtqkOfTbAQ9gkt4pzwGIIZmg4Qn5US5ACjUBenrhezwQ=="], - "@tybys/wasm-util": ["@tybys/wasm-util@0.9.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-6+7nlbMVX/PVDCwaIQ8nTOPveOcFLSt8GcXdx8hD0bt39uWxYT88uXzqTd4fTvqta7oeUJqudepapKNt2DYJFw=="], + "@tybys/wasm-util": ["@tybys/wasm-util@0.10.0", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-VyyPYFlOMNylG45GoAe0xDoLwWuowvf92F9kySqzYh8vmYm7D2u4iUJKa1tOUpS70Ku13ASrOkS4ScXFsTaCNQ=="], - "@types/bun": ["@types/bun@1.2.4", "", { "dependencies": { "bun-types": "1.2.4" } }, "sha512-QtuV5OMR8/rdKJs213iwXDpfVvnskPXY/S0ZiFbsTjQZycuqPbMW8Gf/XhLfwE5njW8sxI2WjISURXPlHypMFA=="], + "@types/bun": ["@types/bun@1.2.19", "", { "dependencies": { "bun-types": "1.2.19" } }, "sha512-d9ZCmrH3CJ2uYKXQIUuZ/pUnTqIvLDS0SK7pFmbx8ma+ziH/FRMoAq5bYpRG7y+w1gl+HgyNZbtqgMq4W4e2Lg=="], "@types/cookie": ["@types/cookie@0.6.0", "", {}, "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="], @@ -157,33 +158,33 @@ "@types/json-schema": ["@types/json-schema@7.0.15", "", {}, "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA=="], - "@types/node": ["@types/node@22.13.5", "", { "dependencies": { "undici-types": "~6.20.0" } }, "sha512-+lTU0PxZXn0Dr1NBtC7Y8cR21AJr87dLLU953CWA6pMxxv/UDc7jYAY90upcrie1nRcD6XNG5HOYEDtgW5TxAg=="], + "@types/node": ["@types/node@22.17.0", "", { "dependencies": { "undici-types": "~6.21.0" } }, "sha512-bbAKTCqX5aNVryi7qXVMi+OkB3w/OyblodicMbvE38blyAz7GxXf6XYhklokijuPwwVg9sDLKRxt0ZHXQwZVfQ=="], + + "@types/react": ["@types/react@19.1.9", "", { "dependencies": { "csstype": "^3.0.2" } }, "sha512-WmdoynAX8Stew/36uTSVMcLJJ1KRh6L3IZRx1PZ7qJtBqT3dYTgyDTx8H1qoRghErydW7xw9mSJ3wS//tCRpFA=="], "@types/statuses": ["@types/statuses@2.0.6", "", {}, "sha512-xMAgYwceFhRA2zY+XbEA7mxYbA093wdiW8Vu6gZPGWy9cmOyU9XesH1tNcEWsKFd5Vzrqx5T3D38PWx1FIIXkA=="], "@types/tough-cookie": ["@types/tough-cookie@4.0.5", "", {}, "sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA=="], - "@types/ws": ["@types/ws@8.5.14", "", { "dependencies": { "@types/node": "*" } }, "sha512-bd/YFLW+URhBzMXurx7lWByOu+xzU9+kb3RboOteXYDfW+tr+JZa99OyNmPINEGB/ahzKrEuc8rcv4gnpJmxTw=="], + "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20250731.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20250731.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20250731.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20250731.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20250731.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20250731.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20250731.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20250731.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-vvqBh80diUqVR3Z62+AbcLdup6a2g88XWtKErOzWbHQh4AlXMarRpNIBiS0eP98cL5ypNAVivkndGD9cR+nXRA=="], - "@typescript/native-preview": ["@typescript/native-preview@7.0.0-dev.20250623.1", "", { "optionalDependencies": { "@typescript/native-preview-darwin-arm64": "7.0.0-dev.20250623.1", "@typescript/native-preview-darwin-x64": "7.0.0-dev.20250623.1", "@typescript/native-preview-linux-arm": "7.0.0-dev.20250623.1", "@typescript/native-preview-linux-arm64": "7.0.0-dev.20250623.1", "@typescript/native-preview-linux-x64": "7.0.0-dev.20250623.1", "@typescript/native-preview-win32-arm64": "7.0.0-dev.20250623.1", "@typescript/native-preview-win32-x64": "7.0.0-dev.20250623.1" }, "bin": { "tsgo": "bin/tsgo.js" } }, "sha512-MLvt23yUf209lDgL4tbzQIm4qzolWK2Zg5M7U/w2QqJQHtCsY28l8H5FL1+51luuuvnS8xHAkKInsOPbaJNu2g=="], + "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20250731.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-fSCRGy6nA3y2lAxOYypiagEATGBnIi/yHFcgmBIGjeLgHzkciRmBLBbgobsZR9lZRZzazkTi3ZnG4fF/OUaqIg=="], - "@typescript/native-preview-darwin-arm64": ["@typescript/native-preview-darwin-arm64@7.0.0-dev.20250623.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2ClyG0svJfXCj6tV/kUyt1ESgVaDR3jFk5Ve0dgbz8KFNJ6QRCIwYk/+QLVfcHJJRMAxwZq8hYwP0AdXco7wHQ=="], + "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20250731.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-U4Kw2MUhYitK6atMhV/0c76/u/5+amuhP58fICwNPnQi9JN1DDJuhvQSyIVRBPqw17nyER+RyZ2j/b+G+HEPcA=="], - "@typescript/native-preview-darwin-x64": ["@typescript/native-preview-darwin-x64@7.0.0-dev.20250623.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-hB8fdupUz3ewqDNJ6LVvjj7GkKyD7eHD+BBNv0JXFG+4z6lkkpmiTktXsvEIQdNfQaV1UJ3oXFsV4GiW/y3wkQ=="], + "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20250731.1", "", { "os": "linux", "cpu": "arm" }, "sha512-bUgN2ImqUjrMYpMq7WXHxMuPgVW+eIJY4NUPmH8M50ptE4e6QWBOkjhDWfYJDxEh5/v//rI3gGuAnWlS1MrDtg=="], - "@typescript/native-preview-linux-arm": ["@typescript/native-preview-linux-arm@7.0.0-dev.20250623.1", "", { "os": "linux", "cpu": "arm" }, "sha512-nt2QiT9DreGbHNxMr/FEFD9SH0eaIq5R7rS4sr0R3IuTE75IP6Jr0SoBO3wFCNJbHRnU7cKhK0PXTjJB8KmbZA=="], + "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20250731.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-FZovNMm9RqXLuEqV804Po+dv8+1GCBTV2CVGyvdEnKFPxU1l34DRa88jNB/4JuSjOedrHU9jh2ML7OfSJmKJmw=="], - "@typescript/native-preview-linux-arm64": ["@typescript/native-preview-linux-arm64@7.0.0-dev.20250623.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-RhwWxEv8zGa6EU0yWI3f93zvELhqQnCZKchLFn8mnMsDXoPCYU30smDX+4uqFz+8AM7lkQnU+1SagxARPN9wMA=="], + "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20250731.1", "", { "os": "linux", "cpu": "x64" }, "sha512-mdXIS9UDEiXMO1R51LFx7Kr8Z6qzl8eU93C9z1NmSZYOhoSGaBl/y4eMjQZ0JS7bsbcxDGgkcgs9yYc7z/DriQ=="], - "@typescript/native-preview-linux-x64": ["@typescript/native-preview-linux-x64@7.0.0-dev.20250623.1", "", { "os": "linux", "cpu": "x64" }, "sha512-al72CAvc1h8zkj1ybbH/X9e57voPjIDacBFQX+7ssRJ9dQ5F8DA8R131iPIPxOZcF77THducn8YUsJx0WWH1KQ=="], + "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20250731.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-s+8YBAuTGM29WYTpzqH2uZXX43p344dAavE4lPUI7Ezv1SmZdEheAYm9lRR2PNgUEu5qtjCUWA/jHTa3j17ENQ=="], - "@typescript/native-preview-win32-arm64": ["@typescript/native-preview-win32-arm64@7.0.0-dev.20250623.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-XBgHgx/7hw+zvbWfSFbMmNSeEOCsGBgZW1TOgLO8wPVESrRUnbMi+6HybM5drrppA/tGKrWDtcB3nUnkrXoqZA=="], - - "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20250623.1", "", { "os": "win32", "cpu": "x64" }, "sha512-IDk3ipTB1GVPldryKbS+Sw1rfX7d+/0uP7ZRY/WHcYhra4yk82j4fHDZ6E42HmCUK6RicUV4jpfHw9IC0CGEFQ=="], + "@typescript/native-preview-win32-x64": ["@typescript/native-preview-win32-x64@7.0.0-dev.20250731.1", "", { "os": "win32", "cpu": "x64" }, "sha512-FBVKs9QGBc6+yRoANgTwGo8LvgwywTRAevIkKplfJvu2nnX3c57QpouiCyGEHHv2hCNLX8V/JU6eg+yq3rMeEQ=="], "acorn": ["acorn@8.15.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg=="], - "ai": ["ai@4.1.46", "", { "dependencies": { "@ai-sdk/provider": "1.0.9", "@ai-sdk/provider-utils": "2.1.10", "@ai-sdk/react": "1.1.18", "@ai-sdk/ui-utils": "1.1.16", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.0.0" }, "optionalPeers": ["react", "zod"] }, "sha512-VTvAktT69IN1qcNAv7OlcOuR0q4HqUlhkVacrWmMlEoprYykF9EL5RY8IECD5d036Wqg0walwbSKZlA2noHm1A=="], + "ai": ["ai@4.3.19", "", { "dependencies": { "@ai-sdk/provider": "1.1.3", "@ai-sdk/provider-utils": "2.2.8", "@ai-sdk/react": "1.2.12", "@ai-sdk/ui-utils": "1.2.11", "@opentelemetry/api": "1.9.0", "jsondiffpatch": "0.6.0" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "zod": "^3.23.8" }, "optionalPeers": ["react"] }, "sha512-dIE2bfNpqHN3r6IINp9znguYdhIOheKW2LDigAMrgt/upT3B8eBGPSCblENvaZGoq+hxaN9fSMzjWpbqloP+7Q=="], "ansi-escapes": ["ansi-escapes@7.0.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw=="], @@ -193,13 +194,13 @@ "ansis": ["ansis@4.1.0", "", {}, "sha512-BGcItUBWSMRgOCe+SVZJ+S7yTRG0eGt9cXAHev72yuGcY23hnLA7Bky5L/xLyPINoSN95geovfBkqoTlNZYa7w=="], - "ast-kit": ["ast-kit@2.1.0", "", { "dependencies": { "@babel/parser": "^7.27.3", "pathe": "^2.0.3" } }, "sha512-ROM2LlXbZBZVk97crfw8PGDOBzzsJvN2uJCmwswvPUNyfH14eg90mSN3xNqsri1JS1G9cz0VzeDUhxJkTrr4Ew=="], + "ast-kit": ["ast-kit@2.1.1", "", { "dependencies": { "@babel/parser": "^7.27.7", "pathe": "^2.0.3" } }, "sha512-mfh6a7gKXE8pDlxTvqIc/syH/P3RkzbOF6LeHdcKztLEzYe6IMsRCL7N8vI7hqTGWNxpkCuuRTpT21xNWqhRtQ=="], - "birpc": ["birpc@2.4.0", "", {}, "sha512-5IdNxTyhXHv2UlgnPHQ0h+5ypVmkrYHzL8QT+DwFZ//2N/oNV8Ch+BCRmTJ3x6/z9Axo/cXYBc9eprsUVK/Jsg=="], + "birpc": ["birpc@2.5.0", "", {}, "sha512-VSWO/W6nNQdyP520F1mhf+Lc2f8pjGQOtoHHm7Ze8Go1kX7akpVIrtTa0fn+HB0QJEDVacl6aO08YE0PgXfdnQ=="], "braces": ["braces@3.0.3", "", { "dependencies": { "fill-range": "^7.1.1" } }, "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA=="], - "bun-types": ["bun-types@1.2.4", "", { "dependencies": { "@types/node": "*", "@types/ws": "~8.5.10" } }, "sha512-nDPymR207ZZEoWD4AavvEaa/KZe/qlrbMSchqpQwovPZCKc7pwMoENjEtHgMKaAjJhy+x6vfqSBA1QU3bJgs0Q=="], + "bun-types": ["bun-types@1.2.19", "", { "dependencies": { "@types/node": "*" }, "peerDependencies": { "@types/react": "^19" } }, "sha512-uAOTaZSPuYsWIXRpj7o56Let0g/wjihKCkeRqUBhlLVM/Bt+Fj9xTo+LhC1OV1XDaGkz4hNC80et5xgy+9KTHQ=="], "cac": ["cac@6.7.14", "", {}, "sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ=="], @@ -229,7 +230,9 @@ "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], - "debug": ["debug@4.4.0", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA=="], + "csstype": ["csstype@3.1.3", "", {}, "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw=="], + + "debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], "defu": ["defu@6.1.4", "", {}, "sha512-mEQCMmwJu317oSz8CwdIOdwf3xMif1ttiM8LTufzc3g6kR+9Pe236twL8j3IYT1F7GfRgGcW6MWxzZjLIkuHIg=="], @@ -239,13 +242,13 @@ "diff-match-patch": ["diff-match-patch@1.0.5", "", {}, "sha512-IayShXAgj/QMXgB0IWmKx+rOPuGMhqm5w6jvFxmVenXKIzRqTAAsbBPT3kWQeGANj3jGgvcvv4yK6SxqYmikgw=="], - "dotenv": ["dotenv@16.4.7", "", {}, "sha512-47qPchRCykZC03FhkYAhrvwU4xDBFIj1QPqaarj6mdM/hgUzfPHcpkHJOn3mJAufFeeAxAzeGsr5X0M4k6fLZQ=="], + "dotenv": ["dotenv@16.6.1", "", {}, "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow=="], "dts-resolver": ["dts-resolver@2.1.1", "", { "peerDependencies": { "oxc-resolver": ">=11.0.0" }, "optionalPeers": ["oxc-resolver"] }, "sha512-3BiGFhB6mj5Kv+W2vdJseQUYW+SKVzAFJL6YNP6ursbrwy1fXHRotfHi3xLNxe4wZl/K8qbAFeCDjZLjzqxxRw=="], "emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], - "empathic": ["empathic@1.1.0", "", {}, "sha512-rsPft6CK3eHtrlp9Y5ALBb+hfK+DWnA4WFebbazxjWyx8vSm3rZeoM3z9irsjcqO3PYRzlfv27XIB4tz2DV7RA=="], + "empathic": ["empathic@2.0.0", "", {}, "sha512-i6UzDscO/XfAcNYD75CfICkmfLedpyPDdozrLMmQc5ORaQcdMoc21OnlEylMIqI7U8eniKrPMxxtj8k0vhmJhA=="], "environment": ["environment@1.1.0", "", {}, "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q=="], @@ -253,8 +256,6 @@ "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], - "eventsource-parser": ["eventsource-parser@3.0.0", "", {}, "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA=="], - "execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], "exsolve": ["exsolve@1.0.7", "", {}, "sha512-VO5fQUzZtI6C+vx4w/4BWJpg3s/5l+6pRQEHzFRM8WFi4XffSP1Z+4qi7GbjWbvRQEbdIco5mIMq+zX4rPuLrw=="], @@ -291,7 +292,7 @@ "isexe": ["isexe@2.0.0", "", {}, "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw=="], - "jiti": ["jiti@2.4.2", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A=="], + "jiti": ["jiti@2.5.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-twQoecYPiVA5K/h6SxtORw/Bs3ar+mLUtoPSc7iMXzQzK8d7eJ/R09wmTwAjiamETn1cXYPGfNnu7DMoHgu12w=="], "js-tokens": ["js-tokens@9.0.1", "", {}, "sha512-mxa9E9ITFOt0ban3j6L5MpjwegGz6lBQmM1IJkWeBZGcMxto50+eWdjC/52xDbS2vy0k7vIMK0Fe2wfL9OQSpQ=="], @@ -303,9 +304,9 @@ "lilconfig": ["lilconfig@3.1.3", "", {}, "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw=="], - "lint-staged": ["lint-staged@15.4.3", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^13.1.0", "debug": "^4.4.0", "execa": "^8.0.1", "lilconfig": "^3.1.3", "listr2": "^8.2.5", "micromatch": "^4.0.8", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.7.0" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-FoH1vOeouNh1pw+90S+cnuoFwRfUD9ijY2GKy5h7HS3OR7JVir2N2xrsa0+Twc1B7cW72L+88geG5cW4wIhn7g=="], + "lint-staged": ["lint-staged@15.5.2", "", { "dependencies": { "chalk": "^5.4.1", "commander": "^13.1.0", "debug": "^4.4.0", "execa": "^8.0.1", "lilconfig": "^3.1.3", "listr2": "^8.2.5", "micromatch": "^4.0.8", "pidtree": "^0.6.0", "string-argv": "^0.3.2", "yaml": "^2.7.0" }, "bin": { "lint-staged": "bin/lint-staged.js" } }, "sha512-YUSOLq9VeRNAo/CTaVmhGDKG+LBtA8KF1X4K5+ykMSwWST1vDxJRB2kv2COgLb1fvpCo+A/y9A0G0znNVmdx4w=="], - "listr2": ["listr2@8.2.5", "", { "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-iyAZCeyD+c1gPyE9qpFu8af0Y+MRtmKOncdGoA2S5EY8iFq99dmmvkNnHiWo+pj0s7yH7l3KPIgee77tKpXPWQ=="], + "listr2": ["listr2@8.3.3", "", { "dependencies": { "cli-truncate": "^4.0.0", "colorette": "^2.0.20", "eventemitter3": "^5.0.1", "log-update": "^6.1.0", "rfdc": "^1.4.1", "wrap-ansi": "^9.0.0" } }, "sha512-LWzX2KsqcB1wqQ4AHgYb4RsDXauQiqhjLk+6hjbaeHG4zpjjVAB6wC/gz6X0l+Du1cN3pUB5ZlrvTbhGSNnUQQ=="], "log-update": ["log-update@6.1.0", "", { "dependencies": { "ansi-escapes": "^7.0.0", "cli-cursor": "^5.0.0", "slice-ansi": "^7.1.0", "strip-ansi": "^7.1.0", "wrap-ansi": "^9.0.0" } }, "sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w=="], @@ -327,13 +328,13 @@ "mute-stream": ["mute-stream@2.0.0", "", {}, "sha512-WWdIxpyjEn+FhQJQQv9aQAYlHoNVdzIzUySNV1gHUPDSdZJ3yZn7pAAbQcV7B56Mvu881q9FZV+0Vx2xC44VWA=="], - "nanoid": ["nanoid@3.3.8", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-WNLf5Sd8oZxOm+TzppcYk8gVOgP+l58xNy58D0nbUnOxOWRWvlcCV4kUF7ltmI6PsrLl/BgKEyS4mqsGChFN0w=="], + "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], "npm-run-path": ["npm-run-path@5.3.0", "", { "dependencies": { "path-key": "^4.0.0" } }, "sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ=="], "onetime": ["onetime@6.0.0", "", { "dependencies": { "mimic-fn": "^4.0.0" } }, "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ=="], - "openai": ["openai@5.7.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-zXWawZl6J/P5Wz57/nKzVT3kJQZvogfuyuNVCdEp4/XU2UNrjL7SsuNpWAyLZbo6HVymwmnfno9toVzBhelygA=="], + "openai": ["openai@5.11.0", "", { "peerDependencies": { "ws": "^8.18.0", "zod": "^3.23.8" }, "optionalPeers": ["ws", "zod"], "bin": { "openai": "bin/cli" } }, "sha512-+AuTc5pVjlnTuA9zvn8rA/k+1RluPIx9AD4eDcnutv6JNwHHZxIhkFy+tmMKCvmMFDQzfA/r1ujvPWB19DQkYg=="], "openapi-types": ["openapi-types@12.1.3", "", {}, "sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw=="], @@ -353,7 +354,7 @@ "pidtree": ["pidtree@0.6.0", "", { "bin": { "pidtree": "bin/pidtree.js" } }, "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g=="], - "pkg-types": ["pkg-types@2.1.0", "", { "dependencies": { "confbox": "^0.2.1", "exsolve": "^1.0.1", "pathe": "^2.0.3" } }, "sha512-wmJwA+8ihJixSoHKxZJRBQG1oY8Yr9pGLzRmSsNms0iNWyHHAlZCa7mmKiFR10YPZuz/2k169JiS/inOjBCZ2A=="], + "pkg-types": ["pkg-types@2.2.0", "", { "dependencies": { "confbox": "^0.2.2", "exsolve": "^1.0.7", "pathe": "^2.0.3" } }, "sha512-2SM/GZGAEkPp3KWORxQZns4M+WSeXbC2HEvmOIJe3Cmiv6ieAJvdVhDldtHqM5J1Y7MrR1XhkBT/rMlhh9FdqQ=="], "psl": ["psl@1.15.0", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w=="], @@ -365,7 +366,7 @@ "querystringify": ["querystringify@2.2.0", "", {}, "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="], - "react": ["react@19.0.0", "", {}, "sha512-V8AVnmPIICiWpGfm6GLzCR/W5FXLchHop40W4nXBmdlEceh16rCN8O8LNWm5bh5XUX91fh7KpA+W0TgMKmgTpQ=="], + "react": ["react@19.1.1", "", {}, "sha512-w8nqGImo45dmMIfljjMwOGtbmC/mk4CMYhWIicdSflH91J9TyCyczcPFXJzrZ/ZXcgGRFeP6BU0BEJTw6tZdfQ=="], "readdirp": ["readdirp@4.1.2", "", {}, "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg=="], @@ -379,9 +380,9 @@ "rfdc": ["rfdc@1.4.1", "", {}, "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA=="], - "rolldown": ["rolldown@1.0.0-beta.15", "", { "dependencies": { "@oxc-project/runtime": "=0.72.3", "@oxc-project/types": "=0.72.3", "@rolldown/pluginutils": "1.0.0-beta.15", "ansis": "^4.0.0" }, "optionalDependencies": { "@rolldown/binding-darwin-arm64": "1.0.0-beta.15", "@rolldown/binding-darwin-x64": "1.0.0-beta.15", "@rolldown/binding-freebsd-x64": "1.0.0-beta.15", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.15", "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.15", "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.15", "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.15", "@rolldown/binding-linux-x64-musl": "1.0.0-beta.15", "@rolldown/binding-wasm32-wasi": "1.0.0-beta.15", "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.15", "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.15", "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.15" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-ep788NsIGl0W5gT+99hBrSGe4Hdhcwc55PqM3O0mR5H0C4ZpGpDGgu9YzTJ8a6mFDLnFnc/LYC+Dszb7oWK/dg=="], + "rolldown": ["rolldown@1.0.0-beta.9-commit.d91dfb5", "", { "dependencies": { "@oxc-project/runtime": "0.71.0", "@oxc-project/types": "0.71.0", "@rolldown/pluginutils": "1.0.0-beta.9-commit.d91dfb5", "ansis": "^4.0.0" }, "optionalDependencies": { "@rolldown/binding-darwin-arm64": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-darwin-x64": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-freebsd-x64": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-linux-arm-gnueabihf": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-linux-arm64-gnu": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-linux-arm64-musl": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-linux-x64-gnu": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-linux-x64-musl": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-wasm32-wasi": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-win32-arm64-msvc": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-win32-ia32-msvc": "1.0.0-beta.9-commit.d91dfb5", "@rolldown/binding-win32-x64-msvc": "1.0.0-beta.9-commit.d91dfb5" }, "bin": { "rolldown": "bin/cli.mjs" } }, "sha512-FHkj6gGEiEgmAXQchglofvUUdwj2Oiw603Rs+zgFAnn9Cb7T7z3fiaEc0DbN3ja4wYkW6sF2rzMEtC1V4BGx/g=="], - "rolldown-plugin-dts": ["rolldown-plugin-dts@0.13.12", "", { "dependencies": { "@babel/generator": "^7.27.5", "@babel/parser": "^7.27.5", "@babel/types": "^7.27.6", "ast-kit": "^2.1.0", "birpc": "^2.4.0", "debug": "^4.4.1", "dts-resolver": "^2.1.1", "get-tsconfig": "^4.10.1" }, "peerDependencies": { "@typescript/native-preview": ">=7.0.0-dev.20250601.1", "rolldown": "^1.0.0-beta.9", "typescript": "^5.0.0", "vue-tsc": "~2.2.0" }, "optionalPeers": ["@typescript/native-preview", "typescript", "vue-tsc"] }, "sha512-4QdQQfMOWNDLhmvoG3USAUpCm75dgwWk3IrK6SV9Fw2U5uwcSE7oQTqEcmsUGEBsNxZ58+gtM1oX38MMf0g5PA=="], + "rolldown-plugin-dts": ["rolldown-plugin-dts@0.13.14", "", { "dependencies": { "@babel/generator": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/types": "^7.28.1", "ast-kit": "^2.1.1", "birpc": "^2.5.0", "debug": "^4.4.1", "dts-resolver": "^2.1.1", "get-tsconfig": "^4.10.1" }, "peerDependencies": { "@typescript/native-preview": ">=7.0.0-dev.20250601.1", "rolldown": "^1.0.0-beta.9", "typescript": "^5.0.0", "vue-tsc": "^2.2.0 || ^3.0.0" }, "optionalPeers": ["@typescript/native-preview", "typescript", "vue-tsc"] }, "sha512-wjNhHZz9dlN6PTIXyizB6u/mAg1wEFMW9yw7imEVe3CxHSRnNHVyycIX0yDEOVJfDNISLPbkCIPEpFpizy5+PQ=="], "sade": ["sade@1.8.1", "", { "dependencies": { "mri": "^1.1.0" } }, "sha512-xal3CZX1Xlo/k4ApwCFrHVACi9fBqJ7V+mwhBsuf/1IOKbBy098Fex+Wa/5QMubw09pSZ/u8EY8PWgevJsXp1A=="], @@ -409,7 +410,7 @@ "strip-final-newline": ["strip-final-newline@3.0.0", "", {}, "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw=="], - "swr": ["swr@2.3.2", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-RosxFpiabojs75IwQ316DGoDRmOqtiAj0tg8wCcbEu4CiLZBs/a9QNtHV7TUfDXmmlgqij/NqzKq/eLelyv9xA=="], + "swr": ["swr@2.3.4", "", { "dependencies": { "dequal": "^2.0.3", "use-sync-external-store": "^1.4.0" }, "peerDependencies": { "react": "^16.11.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-bYd2lrhc+VarcpkgWclcUi92wYCpOgMws9Sd1hG1ntAu0NEy+14CbotuFjshBU2kt9rYj9TSmDcybpxpeTU1fg=="], "throttleit": ["throttleit@2.1.0", "", {}, "sha512-nt6AMGKW1p/70DF/hGBdJB57B8Tspmbp5gfJ8ilhLnt7kkr2ye7hzD6NVG8GGErk2HWF34igrL2CXmNIkzKqKw=="], @@ -421,17 +422,15 @@ "tough-cookie": ["tough-cookie@4.1.4", "", { "dependencies": { "psl": "^1.1.33", "punycode": "^2.1.1", "universalify": "^0.2.0", "url-parse": "^1.5.3" } }, "sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag=="], - "tsdown": ["tsdown@0.12.8", "", { "dependencies": { "ansis": "^4.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "debug": "^4.4.1", "diff": "^8.0.2", "empathic": "^1.1.0", "hookable": "^5.5.3", "rolldown": "1.0.0-beta.15", "rolldown-plugin-dts": "^0.13.11", "semver": "^7.7.2", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.14", "unconfig": "^7.3.2" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "publint": "^0.3.0", "typescript": "^5.0.0", "unplugin-lightningcss": "^0.4.0", "unplugin-unused": "^0.5.0" }, "optionalPeers": ["@arethetypeswrong/core", "publint", "typescript", "unplugin-lightningcss", "unplugin-unused"], "bin": { "tsdown": "dist/run.mjs" } }, "sha512-niHeVcFCNjvVZYVGTeoM4BF+/DWxP8pFH2tUs71sEKYdcKtJIbkSdEmtxByaRZeMgwVbVgPb8nv9i9okVwFLAA=="], + "tsdown": ["tsdown@0.12.9", "", { "dependencies": { "ansis": "^4.1.0", "cac": "^6.7.14", "chokidar": "^4.0.3", "debug": "^4.4.1", "diff": "^8.0.2", "empathic": "^2.0.0", "hookable": "^5.5.3", "rolldown": "^1.0.0-beta.19", "rolldown-plugin-dts": "^0.13.12", "semver": "^7.7.2", "tinyexec": "^1.0.1", "tinyglobby": "^0.2.14", "unconfig": "^7.3.2" }, "peerDependencies": { "@arethetypeswrong/core": "^0.18.1", "publint": "^0.3.0", "typescript": "^5.0.0", "unplugin-lightningcss": "^0.4.0", "unplugin-unused": "^0.5.0" }, "optionalPeers": ["@arethetypeswrong/core", "publint", "typescript", "unplugin-lightningcss", "unplugin-unused"], "bin": { "tsdown": "dist/run.mjs" } }, "sha512-MfrXm9PIlT3saovtWKf/gCJJ/NQCdE0SiREkdNC+9Qy6UHhdeDPxnkFaBD7xttVUmgp0yUHtGirpoLB+OVLuLA=="], "tslib": ["tslib@2.8.1", "", {}, "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w=="], "type-fest": ["type-fest@4.41.0", "", {}, "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA=="], - "typescript": ["typescript@5.7.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw=="], - "unconfig": ["unconfig@7.3.2", "", { "dependencies": { "@quansync/fs": "^0.1.1", "defu": "^6.1.4", "jiti": "^2.4.2", "quansync": "^0.2.8" } }, "sha512-nqG5NNL2wFVGZ0NA/aCFw0oJ2pxSf1lwg4Z5ill8wd7K4KX/rQbHlwbh+bjctXL5Ly1xtzHenHGOK0b+lG6JVg=="], - "undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="], + "undici-types": ["undici-types@6.21.0", "", {}, "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ=="], "universalify": ["universalify@0.2.0", "", {}, "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg=="], @@ -441,7 +440,7 @@ "url-parse": ["url-parse@1.5.10", "", { "dependencies": { "querystringify": "^2.1.1", "requires-port": "^1.0.0" } }, "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ=="], - "use-sync-external-store": ["use-sync-external-store@1.4.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-9WXSPC5fMv61vaupRkCKCxsPxBocVnwakBEkMIHHpkTTg6icbJtg6jzgtLDm4bl3cSHAca52rYWih0k4K3PfHw=="], + "use-sync-external-store": ["use-sync-external-store@1.5.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Rb46I4cGGVBmjamjphe8L/UnvJD+uPPtTkNvX5mZgqdbavhI4EbgIWJiIHXJ8bc/i9EQGPRh4DwEURJ552Do0A=="], "webpack-virtual-modules": ["webpack-virtual-modules@0.6.2", "", {}, "sha512-66/V2i5hQanC51vBQKPH4aI8NMAcBW59FVBs+rC7eGHupMyfn34q7rZIE+ETlJ+XTevqfUhVVBgSUNSW2flEUQ=="], @@ -451,7 +450,7 @@ "y18n": ["y18n@5.0.8", "", {}, "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA=="], - "yaml": ["yaml@2.7.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA=="], + "yaml": ["yaml@2.8.0", "", { "bin": { "yaml": "bin.mjs" } }, "sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ=="], "yargs": ["yargs@17.7.2", "", { "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" } }, "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w=="], @@ -459,9 +458,9 @@ "yoctocolors-cjs": ["yoctocolors-cjs@2.1.2", "", {}, "sha512-cYVsTjKl8b+FrnidjibDWskAv7UKOfcwaVZdp/it9n1s9fU3IkgDbhdIRKCW4JDsAlECJY0ytoVPT3sK6kideA=="], - "zod": ["zod@3.24.2", "", {}, "sha512-lY7CDW43ECgW9u1TcT3IoXHflywfVqDYze4waEz812jR/bZ8FHDsl7pFQoSZTz5N+2NqRXs8GBwnAwo3ZNxqhQ=="], + "zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], - "zod-to-json-schema": ["zod-to-json-schema@3.24.3", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-HIAfWdYIt1sssHfYZFCXp4rU1w2r8hVVXYIlmoa0r0gABLs5di3RCqPU5DDROogVz1pAdYBaz7HK5n9pSUNs3A=="], + "zod-to-json-schema": ["zod-to-json-schema@3.24.6", "", { "peerDependencies": { "zod": "^3.24.1" } }, "sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg=="], "@inquirer/core/ansi-escapes": ["ansi-escapes@4.3.2", "", { "dependencies": { "type-fest": "^0.21.3" } }, "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ=="], @@ -479,17 +478,13 @@ "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], - "rolldown-plugin-dts/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], - "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@4.0.0", "", {}, "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ=="], "string-width/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], - "tinyglobby/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], - - "tsdown/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="], + "tinyglobby/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], - "unplugin/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + "unplugin/picomatch": ["picomatch@4.0.3", "", {}, "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q=="], "wrap-ansi/string-width": ["string-width@7.2.0", "", { "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", "strip-ansi": "^7.1.0" } }, "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ=="], diff --git a/examples/meta-tools.ts b/examples/meta-tools.ts new file mode 100644 index 00000000..801304f4 --- /dev/null +++ b/examples/meta-tools.ts @@ -0,0 +1,194 @@ +/** + * Meta Tools Example + * + * This example demonstrates how to use the beta meta tools for intelligent + * tool discovery and orchestration. + * + * @beta These are beta features and may change in future versions + */ + +import { openai } from '@ai-sdk/openai'; +import { generateText } from 'ai'; +import { StackOneToolSet, Tools } from '../src'; +import { ACCOUNT_IDS } from './constants'; + +// Example 1: Using metaRelevantTool to discover tools +async function discoverToolsExample() { + console.log('\n=== Example 1: Discovering Relevant Tools ==='); + + const toolset = new StackOneToolSet(); + const tools = toolset.getTools('hris_*', ACCOUNT_IDS.HRIS); + + // Get the meta tool for finding relevant tools + const relevantToolsFinder = tools.metaRelevantTool(); + + // Example queries for different use cases + const queries = [ + 'list employees', + 'get employee', + 'create employee', + 'list companies', + 'get company', + 'list applications', + ]; + + for (const query of queries) { + console.log(`\nSearching for: "${query}"`); + const result = await relevantToolsFinder.execute({ + query, + limit: 3, + minScore: 0.5, + }); + + console.log(`Found ${result.resultsCount} tools:`); + for (const tool of result.tools as Array<{ + name: string; + score: number; + matchReason: string; + }>) { + console.log(` - ${tool.name} (score: ${tool.score.toFixed(2)}) - ${tool.matchReason}`); + } + } +} + +// Example 2: Using metaExecuteTool for complex workflows +async function toolChainExample() { + console.log('\n=== Example 2: Executing Tool Chains ==='); + + const toolset = new StackOneToolSet(); + const tools = toolset.getTools('hris_*', ACCOUNT_IDS.HRIS); + + // Get the meta tool for executing tool chains + const toolChain = tools.metaExecuteTool(); + + // Example: Employee onboarding workflow + console.log('\nExecuting employee onboarding workflow...'); + + const result = await toolChain.execute({ + steps: [ + { + toolName: 'hris_list_employees', + parameters: { + filter: { + updated_after: '2024-01-01T00:00:00Z', + }, + page_size: '10', + }, + stepName: 'Get recent employees', + }, + { + toolName: 'hris_get_employee', + parameters: { + id: '{{step0.result.items[0].id}}', // Get first employee ID + }, + stepName: 'Get employee details', + condition: '{{step0.result.items.length}} > 0', + }, + ], + accountId: ACCOUNT_IDS.HRIS, + }); + + console.log(`\nWorkflow execution ${result.success ? 'succeeded' : 'failed'}`); + console.log(`Total execution time: ${result.executionTime}ms`); + + for (const step of result.stepResults as Array<{ + stepIndex: number; + stepName: string; + success: boolean; + skipped?: boolean; + error?: string; + }>) { + console.log(`\nStep ${step.stepIndex + 1}: ${step.stepName}`); + console.log(` Status: ${step.success ? 'Success' : 'Failed'}`); + if (step.skipped) { + console.log(' Skipped due to condition'); + } + if (step.error) { + console.log(` Error: ${step.error}`); + } + } +} + +// Example 3: Using meta tools with AI agents +async function aiAgentExample() { + console.log('\n=== Example 3: Meta Tools with AI Agents ==='); + + // Check if OpenAI API key is set + if (!process.env.OPENAI_API_KEY) { + console.log('Skipping AI agent example - OPENAI_API_KEY environment variable not set'); + return; + } + + const toolset = new StackOneToolSet(); + const tools = toolset.getTools('hris_*', ACCOUNT_IDS.HRIS); + + // Get both meta tools + const { metaRelevantTool, metaExecuteTool } = tools.metaTools(); + + // Convert meta tools to AI SDK format + const aiTools = new Tools([metaRelevantTool, metaExecuteTool]).toAISDK(); + + try { + // Use with AI agent + const { text, toolCalls } = await generateText({ + model: openai('gpt-4o-mini'), + tools: aiTools, + prompt: `You are an HR assistant. First, use meta_relevant_tool to find tools + related to listing employees. Then use meta_execute_tool to get the + list of employees and fetch details for the first one.`, + maxSteps: 5, + }); + + console.log('\nAI Agent Response:'); + console.log(text); + + console.log('\nTool calls made:'); + for (const call of toolCalls || []) { + console.log(`- ${call.toolName}`); + } + } catch (error) { + console.error('Error in AI agent example:', error instanceof Error ? error.message : error); + } +} + +// Example 4: Different ways to use meta tools +async function usageExample() { + console.log('\n=== Example 4: Different Ways to Use Meta Tools ==='); + + const toolset = new StackOneToolSet(); + + // Method 1: Get tools and then get meta tools + const tools = toolset.getTools('hris_*', ACCOUNT_IDS.HRIS); + + // Get individual meta tools + const relevantTool = tools.metaRelevantTool(); + console.log(`Got meta tool: ${relevantTool.name}`); + + const executeTool = tools.metaExecuteTool(); + console.log(`Got meta tool: ${executeTool.name}`); + + // Get both meta tools at once + const { metaRelevantTool, metaExecuteTool } = tools.metaTools(); + console.log(`Got both meta tools: ${metaRelevantTool.name}, ${metaExecuteTool.name}`); +} + +// Main execution +async function main() { + console.log('Meta Tools Examples (Beta)'); + console.log('=========================='); + console.log('Note: These are beta features and may change in future versions.\n'); + + try { + await discoverToolsExample(); + await toolChainExample(); + await aiAgentExample(); + await usageExample(); + } catch (error) { + console.error('Error in examples:', error); + } +} + +// Only run if this file is executed directly +if (import.meta.main) { + main(); +} diff --git a/package.json b/package.json index dfc8da02..9f5b81d6 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "format": "biome format --write ." }, "dependencies": { + "@orama/orama": "^3.1.11", "json-schema": "^0.4.0" }, "devDependencies": { diff --git a/src/index.ts b/src/index.ts index 51473f5a..a892c7b7 100644 --- a/src/index.ts +++ b/src/index.ts @@ -30,3 +30,15 @@ export type { ParameterLocation, ToolDefinition, } from './types'; + +// Meta tools (beta) +export { + ExecuteToolChain, + GetRelevantTools, + type ToolChainConfig, + type ToolChainResult, + type ToolChainStep, + type ToolChainStepResult, + type ToolSearchConfig, + type ToolSearchResult, +} from './meta-tools'; diff --git a/src/meta-tools/consts.ts b/src/meta-tools/consts.ts new file mode 100644 index 00000000..76994d50 --- /dev/null +++ b/src/meta-tools/consts.ts @@ -0,0 +1,4 @@ +/** + * Beta warning constant for meta tools + */ +export const BETA_WARNING = 'This is a beta feature and may change in future versions.'; diff --git a/src/meta-tools/execute-tool-chain.ts b/src/meta-tools/execute-tool-chain.ts new file mode 100644 index 00000000..9cf8de1a --- /dev/null +++ b/src/meta-tools/execute-tool-chain.ts @@ -0,0 +1,351 @@ +/** + * ExecuteToolChain - A meta tool for orchestrating multiple tool executions + * @beta This is a beta feature and may change in future versions + */ + +import { BaseTool, StackOneTool, type Tools } from '../tool'; +import type { ExecuteConfig, JsonDict, ToolParameters } from '../types'; +import { toArray } from '../utils/array'; +import { StackOneError } from '../utils/errors'; +import { BETA_WARNING } from './consts'; +import type { ToolChainConfig, ToolChainResult, ToolChainStep, ToolChainStepResult } from './types'; + +/** + * A meta tool that executes multiple tools in sequence with parameter passing + * @beta + */ +export class ExecuteToolChain extends BaseTool { + private tools: Tools; + + constructor(tools: Tools) { + const parameters: ToolParameters = { + type: 'object', + properties: { + steps: { + type: 'array', + description: 'Array of tool execution steps', + items: { + type: 'object', + properties: { + toolName: { + type: 'string', + description: 'Name of the tool to execute', + }, + parameters: { + type: 'object', + description: + 'Parameters for the tool. Use {{stepN.path.to.value}} to reference previous results', + }, + condition: { + type: 'string', + description: 'Optional condition expression (e.g., "{{step1.success}} === true")', + }, + stepName: { + type: 'string', + description: 'Optional custom name for this step', + }, + continueOnError: { + type: 'boolean', + description: 'Whether to continue if this step fails', + default: false, + }, + }, + required: ['toolName', 'parameters'], + }, + }, + accountId: { + type: 'string', + description: 'Account ID to use for StackOne tools', + }, + stopOnError: { + type: 'boolean', + description: 'Whether to stop execution on first error (default: true)', + default: true, + }, + timeout: { + type: 'number', + description: 'Maximum execution time in milliseconds (default: 300000)', + default: 300000, + }, + }, + required: ['steps'], + }; + + const executeConfig: ExecuteConfig = { + method: 'LOCAL', + url: 'local://execute-tool-chain', + bodyType: 'json', + params: [], + }; + + super( + 'meta_execute_tool', + `Execute multiple tools in sequence with parameter passing between steps. ${BETA_WARNING}`, + parameters, + executeConfig + ); + + this.tools = tools; + } + + /** + * Execute the tool chain + */ + async execute(inputParams?: JsonDict | string): Promise { + const startTime = Date.now(); + + try { + const params = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {}; + const config = params as ToolChainConfig; + + if (!config.steps || toArray(config.steps).length === 0) { + throw new StackOneError('Steps array is required and must not be empty'); + } + + const result = await this.executeChain(config); + + return { + ...result, + beta: true, + warning: BETA_WARNING, + }; + } catch (error) { + const executionTime = Date.now() - startTime; + + if (error instanceof StackOneError) { + throw error; + } + + return { + success: false, + stepResults: [], + executionTime, + error: error instanceof Error ? error.message : String(error), + beta: true, + warning: BETA_WARNING, + }; + } + } + + /** + * Execute the tool chain + */ + private async executeChain(config: ToolChainConfig): Promise { + const { accountId, stopOnError = true, timeout = 300000 } = config; + const steps = toArray(config.steps); + const startTime = Date.now(); + const stepResults: ToolChainStepResult[] = []; + + // Create a timeout promise + const timeoutPromise = new Promise((_, reject) => { + setTimeout( + () => reject(new Error(`Tool chain execution timed out after ${timeout}ms`)), + timeout + ); + }); + + try { + // Execute with timeout + await Promise.race([ + this.executeSteps(steps, stepResults, accountId, stopOnError), + timeoutPromise, + ]); + + const executionTime = Date.now() - startTime; + const success = stepResults.every((result) => result.success || result.skipped); + + return { + success, + stepResults, + executionTime, + }; + } catch (error) { + const executionTime = Date.now() - startTime; + return { + success: false, + stepResults, + executionTime, + error: error instanceof Error ? error.message : String(error), + }; + } + } + + /** + * Execute the steps sequentially + */ + private async executeSteps( + steps: ToolChainStep[], + stepResults: ToolChainStepResult[], + accountId?: string, + stopOnError = true + ): Promise { + for (let i = 0; i < steps.length; i++) { + const step = steps[i]; + const stepStartTime = Date.now(); + + try { + // Check condition if provided + if (step.condition) { + const shouldExecute = this.evaluateCondition(step.condition, stepResults); + if (!shouldExecute) { + stepResults.push({ + stepIndex: i, + stepName: step.stepName || step.toolName, + toolName: step.toolName, + success: true, + skipped: true, + executionTime: Date.now() - stepStartTime, + }); + continue; + } + } + + // Get the tool + const tool = this.tools.getTool(step.toolName); + if (!tool) { + throw new Error(`Tool not found: ${step.toolName}`); + } + + // Set account ID if provided and tool is StackOne tool + if (accountId && tool instanceof StackOneTool) { + tool.setAccountId(accountId); + } + + // Process parameters with template substitution + const processedParams = this.processParameters(step.parameters, stepResults); + + // Execute the tool + const result = await tool.execute(processedParams); + + stepResults.push({ + stepIndex: i, + stepName: step.stepName || step.toolName, + toolName: step.toolName, + success: true, + result, + executionTime: Date.now() - stepStartTime, + }); + } catch (error) { + const errorMessage = error instanceof Error ? error.message : String(error); + + stepResults.push({ + stepIndex: i, + stepName: step.stepName || step.toolName, + toolName: step.toolName, + success: false, + error: errorMessage, + executionTime: Date.now() - stepStartTime, + }); + + if (stopOnError && !step.continueOnError) { + throw new Error(`Step ${i} (${step.stepName || step.toolName}) failed: ${errorMessage}`); + } + } + } + } + + /** + * Evaluate a condition expression + */ + private evaluateCondition(condition: string, stepResults: ToolChainStepResult[]): boolean { + try { + // Replace template variables with actual values + const processedCondition = this.replaceTemplateVariables(condition, stepResults); + + // Create a safe evaluation context + const context = { + step: stepResults.reduce( + (acc, result, index) => { + acc[index] = result; + return acc; + }, + {} as Record + ), + }; + + // Use Function constructor for safer evaluation + const evaluator = new Function('context', `with(context) { return ${processedCondition}; }`); + return evaluator(context); + } catch (error) { + console.warn(`Failed to evaluate condition: ${condition}`, error); + return false; + } + } + + /** + * Process parameters by replacing template variables + */ + private processParameters(parameters: JsonDict, stepResults: ToolChainStepResult[]): JsonDict { + const processedParams: JsonDict = {}; + + for (const [key, value] of Object.entries(parameters)) { + if (typeof value === 'string') { + processedParams[key] = this.replaceTemplateVariables(value, stepResults); + } else if (typeof value === 'object' && value !== null) { + processedParams[key] = this.processParameters(value as JsonDict, stepResults); + } else { + processedParams[key] = value; + } + } + + return processedParams; + } + + /** + * Replace template variables in a string + */ + private replaceTemplateVariables( + template: string, + stepResults: ToolChainStepResult[] + ): string | unknown { + // Check if the entire string is a template variable + const fullMatch = template.match(/^\{\{step(\d+)\.(.+?)\}\}$/); + if (fullMatch) { + const [, stepIndex, path] = fullMatch; + const index = Number.parseInt(stepIndex, 10); + const result = stepResults[index]; + + if (!result) { + return template; // Keep original if step not found + } + + // Navigate the path + const pathParts = path.split('.'); + let value: unknown = result; + + for (const part of pathParts) { + if (value && typeof value === 'object' && value !== null && part in value) { + value = (value as Record)[part]; + } else { + return template; // Keep original if path not found + } + } + + return value; // Return the actual value, not stringified + } + + // Otherwise, replace inline template variables + return template.replace(/\{\{step(\d+)\.(.+?)\}\}/g, (match, stepIndex, path) => { + const index = Number.parseInt(stepIndex, 10); + const result = stepResults[index]; + + if (!result) { + return match; // Keep original if step not found + } + + // Navigate the path + const pathParts = path.split('.'); + let value: unknown = result; + + for (const part of pathParts) { + if (value && typeof value === 'object' && value !== null && part in value) { + value = (value as Record)[part]; + } else { + return match; // Keep original if path not found + } + } + + return String(value); + }); + } +} diff --git a/src/meta-tools/get-relevant-tools.ts b/src/meta-tools/get-relevant-tools.ts new file mode 100644 index 00000000..b8ab4649 --- /dev/null +++ b/src/meta-tools/get-relevant-tools.ts @@ -0,0 +1,272 @@ +/** + * GetRelevantTools - A meta tool for discovering relevant tools based on user intent + * @beta This is a beta feature and may change in future versions + */ + +import { create, insert, search } from '@orama/orama'; +import type { Arrayable } from 'type-fest'; +import { BaseTool } from '../tool'; +import type { ExecuteConfig, JsonDict, ToolParameters } from '../types'; +import { toArray } from '../utils/array'; +import { StackOneError } from '../utils/errors'; +import { BETA_WARNING } from './consts'; +import type { ToolSearchConfig, ToolSearchResult } from './types'; + +/** + * A meta tool that searches for relevant tools based on a query + * @beta + */ +export class GetRelevantTools extends BaseTool { + private availableTools: BaseTool[]; + private oramaDb: unknown; + private dbInitialized = false; + + constructor(tools: BaseTool[]) { + const parameters: ToolParameters = { + type: 'object', + properties: { + query: { + type: 'string', + description: 'Natural language query describing what tools you need', + }, + limit: { + type: 'number', + description: 'Maximum number of tools to return (default: 10)', + default: 10, + }, + minScore: { + type: 'number', + description: 'Minimum relevance score (0-1) for results (default: 0.3)', + default: 0.3, + }, + filterPatterns: { + oneOf: [ + { type: 'string' }, + { + type: 'array', + items: { type: 'string' }, + }, + ], + description: 'Optional glob patterns to filter results (e.g., "hris_*", "!*_delete_*")', + }, + }, + required: ['query'], + }; + + const executeConfig: ExecuteConfig = { + method: 'LOCAL', + url: 'local://get-relevant-tools', + bodyType: 'json', + params: [], + }; + + super( + 'meta_relevant_tool', + `Search for relevant tools based on natural language query. ${BETA_WARNING}`, + parameters, + executeConfig + ); + + this.availableTools = tools; + } + + /** + * Initialize Orama database with tools + */ + private async initializeOramaDb(): Promise { + if (this.dbInitialized) return; + + // Create Orama database schema with BM25 scoring + this.oramaDb = create({ + schema: { + name: 'string' as const, + description: 'string' as const, + category: 'string' as const, + tags: 'string[]' as const, + }, + components: { + tokenizer: { + stemming: true, + }, + }, + }); + + // Index all tools + for (const tool of this.availableTools) { + // Extract category from tool name (e.g., 'hris_create_employee' -> 'hris') + const parts = tool.name.split('_'); + const category = parts[0]; + + // Extract action type + const actionTypes = ['create', 'update', 'delete', 'get', 'list', 'search']; + const actions = parts.filter((p) => actionTypes.includes(p)); + + if (!this.oramaDb) throw new Error('Orama DB not initialized'); + await insert(this.oramaDb as Parameters[0], { + name: tool.name, + description: tool.description, + category: category, + tags: [...parts, ...actions], + }); + } + + this.dbInitialized = true; + } + + /** + * Execute the tool search + */ + async execute(inputParams?: JsonDict | string): Promise { + try { + const params = typeof inputParams === 'string' ? JSON.parse(inputParams) : inputParams || {}; + const config = params as ToolSearchConfig; + + if (!config.query) { + throw new StackOneError('Query parameter is required'); + } + + // Initialize Orama DB if needed + await this.initializeOramaDb(); + + // Perform the search + const results = await this.searchTools(config); + + // Format results for output + const formattedResults = results.map((result) => ({ + name: result.tool.name, + description: result.tool.description, + score: result.score, + matchReason: result.matchReason, + parameters: result.tool.parameters, + })); + + return { + success: true, + query: config.query, + resultsCount: formattedResults.length, + tools: formattedResults, + beta: true, + warning: BETA_WARNING, + }; + } catch (error) { + if (error instanceof StackOneError) { + throw error; + } + throw new StackOneError( + `Error searching for tools: ${error instanceof Error ? error.message : String(error)}` + ); + } + } + + /** + * Search for tools based on the configuration + */ + private async searchTools(config: ToolSearchConfig): Promise { + const { query, limit = 10, minScore = 0.3, filterPatterns } = config; + + // Perform semantic search using Orama + if (!this.oramaDb) throw new Error('Orama DB not initialized'); + const searchResults = await search(this.oramaDb as Parameters[0], { + term: query, + limit: limit * 2, // Get more results to filter later + properties: ['name', 'description', 'tags'], + boost: { + name: 2, // Prioritize name matches + tags: 1.5, // Tags are also important + description: 1, // Description is baseline + }, + }); + + // Convert Orama results to our format + const scoredTools: ToolSearchResult[] = []; + + for (const hit of searchResults.hits) { + const toolName = hit.document.name as string; + const tool = this.availableTools.find((t) => t.name === toolName); + + if (!tool) continue; + + // Apply filter patterns if provided + if (filterPatterns && !this.matchesFilter(tool.name, filterPatterns)) { + continue; + } + + // Normalize Orama score (typically 0-20+) to 0-1 range + const oramaScore = hit.score || 0; + let normalizedScore: number; + let matchReason: string; + + // Determine match quality based on score ranges + if (oramaScore >= 15) { + normalizedScore = 0.95 + Math.min(oramaScore - 15, 5) * 0.01; // 0.95-1.0 + matchReason = 'excellent match'; + } else if (oramaScore >= 10) { + normalizedScore = 0.8 + ((oramaScore - 10) / 5) * 0.15; // 0.8-0.95 + matchReason = 'strong match'; + } else if (oramaScore >= 5) { + normalizedScore = 0.6 + ((oramaScore - 5) / 5) * 0.2; // 0.6-0.8 + matchReason = 'good match'; + } else if (oramaScore >= 2) { + normalizedScore = 0.4 + ((oramaScore - 2) / 3) * 0.2; // 0.4-0.6 + matchReason = 'partial match'; + } else { + normalizedScore = Math.max(0.1, oramaScore * 0.2); // 0.1-0.4 + matchReason = 'weak match'; + } + + // Check for exact matches and boost score + if (tool.name.toLowerCase() === query.toLowerCase()) { + normalizedScore = 1.0; + matchReason = 'exact name match'; + } else if (tool.name.toLowerCase().includes(query.toLowerCase())) { + normalizedScore = Math.max(normalizedScore, 0.85); + matchReason = matchReason === 'exact name match' ? matchReason : 'name contains query'; + } + + if (normalizedScore >= minScore) { + scoredTools.push({ + tool, + score: normalizedScore, + matchReason, + }); + } + } + + // Sort by score (descending) and limit results + scoredTools.sort((a, b) => b.score - a.score); + return scoredTools.slice(0, limit); + } + + /** + * Check if a tool name matches filter patterns + */ + private matchesFilter(toolName: string, filterPattern: Arrayable): boolean { + const patterns = toArray(filterPattern); + + // Split into positive and negative patterns + const positivePatterns = patterns.filter((p) => !p.startsWith('!')); + const negativePatterns = patterns.filter((p) => p.startsWith('!')).map((p) => p.substring(1)); + + // If no positive patterns, treat as match all + const matchesPositive = + positivePatterns.length === 0 || positivePatterns.some((p) => this.matchGlob(toolName, p)); + + // If any negative pattern matches, exclude the tool + const matchesNegative = negativePatterns.some((p) => this.matchGlob(toolName, p)); + + return matchesPositive && !matchesNegative; + } + + /** + * Simple glob pattern matching + */ + private matchGlob(str: string, pattern: string): boolean { + // Escape all RegExp metacharacters except * and ? + const regexPattern = pattern + .replace(/[.+^${}()|[\]\\]/g, '\\$&') // Escape all regex special chars + .replace(/\*/g, '.*') // Convert * to .* + .replace(/\?/g, '.'); // Convert ? to . + const regex = new RegExp(`^${regexPattern}$`); + return regex.test(str); + } +} diff --git a/src/meta-tools/index.ts b/src/meta-tools/index.ts new file mode 100644 index 00000000..622448f8 --- /dev/null +++ b/src/meta-tools/index.ts @@ -0,0 +1,15 @@ +/** + * Meta tools for AI agent orchestration + * @beta These are beta features and may change in future versions + */ + +export { ExecuteToolChain } from './execute-tool-chain'; +export { GetRelevantTools } from './get-relevant-tools'; +export type { + ToolChainConfig, + ToolChainResult, + ToolChainStep, + ToolChainStepResult, + ToolSearchConfig, + ToolSearchResult, +} from './types'; diff --git a/src/meta-tools/tests/execute-tool-chain.spec.ts b/src/meta-tools/tests/execute-tool-chain.spec.ts new file mode 100644 index 00000000..0a4cd560 --- /dev/null +++ b/src/meta-tools/tests/execute-tool-chain.spec.ts @@ -0,0 +1,495 @@ +import { describe, expect, it, mock } from 'bun:test'; +import { BaseTool, StackOneTool, Tools } from '../../tool'; +import type { ExecuteConfig, JsonDict, ToolParameters } from '../../types'; +import { ExecuteToolChain } from '../execute-tool-chain'; + +// Mock tool creation helper +const createMockTool = ( + name: string, + description: string, + mockExecute?: (params: JsonDict) => Promise +): BaseTool => { + const params: ToolParameters = { + type: 'object', + properties: { + id: { type: 'string' }, + data: { type: 'object' }, + }, + }; + + const executeConfig: ExecuteConfig = { + method: 'GET', + url: 'https://api.example.com/test', + bodyType: 'json', + params: [], + }; + + const tool = new BaseTool(name, description, params, executeConfig); + + if (mockExecute) { + tool.execute = mock(mockExecute); + } + + return tool; +}; + +// Create StackOne tool for testing account ID handling +const createMockStackOneTool = ( + name: string, + description: string, + mockExecute?: (params: JsonDict) => Promise +): StackOneTool => { + const params: ToolParameters = { + type: 'object', + properties: { + id: { type: 'string' }, + }, + }; + + const executeConfig: ExecuteConfig = { + method: 'GET', + url: 'https://api.stackone.com/test', + bodyType: 'json', + params: [], + }; + + const tool = new StackOneTool(name, description, params, executeConfig); + + if (mockExecute) { + tool.execute = mock(mockExecute); + } + + return tool; +}; + +describe('ExecuteToolChain', () => { + describe('Basic functionality', () => { + it('should execute a simple tool chain', async () => { + const mockTool1 = createMockTool('tool1', 'First tool', async () => ({ + result: 'value1', + data: { foo: 'bar' }, + })); + + const mockTool2 = createMockTool('tool2', 'Second tool', async (params) => ({ + received: params, + result: 'value2', + })); + + const tools = new Tools([mockTool1, mockTool2]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute({ + steps: [ + { + toolName: 'tool1', + parameters: { id: '123' }, + }, + { + toolName: 'tool2', + parameters: { id: '456', previous: '{{step0.result.result}}' }, + }, + ], + }); + + expect(result.success).toBe(true); + expect(result.stepResults).toHaveLength(2); + expect(result.stepResults[0].success).toBe(true); + expect(result.stepResults[0].result).toEqual({ result: 'value1', data: { foo: 'bar' } }); + expect(result.stepResults[1].success).toBe(true); + expect(result.stepResults[1].result.received.previous).toBe('value1'); + }); + + it('should handle conditional execution', async () => { + const mockTool1 = createMockTool('tool1', 'First tool', async () => ({ + shouldContinue: false, + })); + + const mockTool2 = createMockTool('tool2', 'Second tool', async () => ({ + result: 'should not execute', + })); + + const tools = new Tools([mockTool1, mockTool2]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute({ + steps: [ + { + toolName: 'tool1', + parameters: {}, + }, + { + toolName: 'tool2', + parameters: {}, + condition: '{{step0.result.shouldContinue}} === true', + }, + ], + }); + + expect(result.success).toBe(true); + expect(result.stepResults).toHaveLength(2); + expect(result.stepResults[1].skipped).toBe(true); + expect(mockTool2.execute).not.toHaveBeenCalled(); + }); + + it('should use custom step names', async () => { + const mockTool = createMockTool('tool1', 'Test tool', async () => ({ result: 'ok' })); + const tools = new Tools([mockTool]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute({ + steps: [ + { + toolName: 'tool1', + parameters: {}, + stepName: 'Custom Step Name', + }, + ], + }); + + expect(result.stepResults[0].stepName).toBe('Custom Step Name'); + }); + }); + + describe('Parameter substitution', () => { + it('should substitute nested parameters', async () => { + const mockTool1 = createMockTool('tool1', 'First tool', async () => ({ + data: { + nested: { + value: 'deep-value', + }, + }, + })); + + const mockTool2 = createMockTool('tool2', 'Second tool', async (params) => ({ + received: params, + })); + + const tools = new Tools([mockTool1, mockTool2]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute({ + steps: [ + { + toolName: 'tool1', + parameters: {}, + }, + { + toolName: 'tool2', + parameters: { + value: '{{step0.result.data.nested.value}}', + }, + }, + ], + }); + + expect(result.stepResults[1].result.received.value).toBe('deep-value'); + }); + + it('should handle complex parameter objects', async () => { + const mockTool1 = createMockTool('tool1', 'First tool', async () => ({ + id: 'abc123', + name: 'Test Name', + })); + + const mockTool2 = createMockTool('tool2', 'Second tool', async (params) => ({ + received: params, + })); + + const tools = new Tools([mockTool1, mockTool2]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute({ + steps: [ + { + toolName: 'tool1', + parameters: {}, + }, + { + toolName: 'tool2', + parameters: { + data: { + id: '{{step0.result.id}}', + name: '{{step0.result.name}}', + static: 'static-value', + }, + }, + }, + ], + }); + + expect(result.stepResults[1].result.received.data).toEqual({ + id: 'abc123', + name: 'Test Name', + static: 'static-value', + }); + }); + }); + + describe('Error handling', () => { + it('should stop on error by default', async () => { + const mockTool1 = createMockTool('tool1', 'First tool', async () => { + throw new Error('Tool 1 failed'); + }); + + const mockTool2 = createMockTool('tool2', 'Second tool', async () => ({ + result: 'should not execute', + })); + + const tools = new Tools([mockTool1, mockTool2]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute({ + steps: [ + { + toolName: 'tool1', + parameters: {}, + }, + { + toolName: 'tool2', + parameters: {}, + }, + ], + }); + + expect(result.success).toBe(false); + expect(result.error).toContain('Tool 1 failed'); + expect(result.stepResults).toHaveLength(1); + expect(mockTool2.execute).not.toHaveBeenCalled(); + }); + + it('should continue on error when specified', async () => { + const mockTool1 = createMockTool('tool1', 'First tool', async () => { + throw new Error('Tool 1 failed'); + }); + + const mockTool2 = createMockTool('tool2', 'Second tool', async () => ({ + result: 'success', + })); + + const tools = new Tools([mockTool1, mockTool2]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute({ + steps: [ + { + toolName: 'tool1', + parameters: {}, + continueOnError: true, + }, + { + toolName: 'tool2', + parameters: {}, + }, + ], + stopOnError: false, + }); + + expect(result.success).toBe(false); // Still false because one step failed + expect(result.stepResults).toHaveLength(2); + expect(result.stepResults[0].success).toBe(false); + expect(result.stepResults[1].success).toBe(true); + }); + + it('should handle tool not found', async () => { + const tools = new Tools([]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute({ + steps: [ + { + toolName: 'non_existent_tool', + parameters: {}, + }, + ], + }); + + expect(result.success).toBe(false); + expect(result.error).toContain('Tool not found'); + }); + + it('should handle timeout', async () => { + const mockTool = createMockTool('tool1', 'Slow tool', async () => { + await new Promise((resolve) => setTimeout(resolve, 200)); + return { result: 'ok' }; + }); + + const tools = new Tools([mockTool]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute({ + steps: [ + { + toolName: 'tool1', + parameters: {}, + }, + ], + timeout: 100, // 100ms timeout + }); + + expect(result.success).toBe(false); + expect(result.error).toContain('timed out'); + }); + }); + + describe('StackOne tool integration', () => { + it('should set account ID on StackOne tools', async () => { + const mockStackOneTool = createMockStackOneTool( + 'stackone_tool', + 'StackOne tool', + async () => ({ + result: 'ok', + }) + ); + + const setAccountIdSpy = mock((_accountId: string) => mockStackOneTool); + mockStackOneTool.setAccountId = setAccountIdSpy; + + const tools = new Tools([mockStackOneTool]); + const chain = new ExecuteToolChain(tools); + + await chain.execute({ + steps: [ + { + toolName: 'stackone_tool', + parameters: {}, + }, + ], + accountId: 'test-account-123', + }); + + expect(setAccountIdSpy).toHaveBeenCalledWith('test-account-123'); + }); + }); + + describe('Use case examples', () => { + it('should handle Jira ticket reading workflow', async () => { + const getTicket = createMockTool('jira_get_ticket', 'Get Jira ticket', async (params) => ({ + id: params.id, + title: 'Fix login bug', + description: 'Users cannot login', + assignee: 'john.doe', + })); + + const getUser = createMockTool('jira_get_user', 'Get Jira user', async (params) => ({ + username: params.username, + email: 'john.doe@example.com', + name: 'John Doe', + })); + + const tools = new Tools([getTicket, getUser]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute({ + steps: [ + { + toolName: 'jira_get_ticket', + parameters: { id: 'PROJ-123' }, + stepName: 'Fetch ticket details', + }, + { + toolName: 'jira_get_user', + parameters: { username: '{{step0.result.assignee}}' }, + stepName: 'Get assignee details', + }, + ], + }); + + expect(result.success).toBe(true); + expect(result.stepResults[0].result.title).toBe('Fix login bug'); + expect(result.stepResults[1].result.email).toBe('john.doe@example.com'); + }); + + it('should handle employee time off creation workflow', async () => { + const getEmployee = createMockTool('hris_get_employee', 'Get employee', async (params) => ({ + id: params.id, + name: 'Jane Smith', + department: 'Engineering', + manager_id: 'mgr-456', + })); + + const createTimeOff = createMockTool( + 'hris_create_time_off', + 'Create time off', + async (params) => ({ + request_id: 'TO-789', + employee_id: params.employee_id, + status: 'pending_approval', + }) + ); + + const tools = new Tools([getEmployee, createTimeOff]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute({ + steps: [ + { + toolName: 'hris_get_employee', + parameters: { id: 'emp-123' }, + }, + { + toolName: 'hris_create_time_off', + parameters: { + employee_id: '{{step0.result.id}}', + start_date: '2024-01-15', + end_date: '2024-01-19', + type: 'vacation', + }, + }, + ], + }); + + expect(result.success).toBe(true); + expect(result.stepResults[1].result.request_id).toBe('TO-789'); + }); + }); + + describe('Input validation', () => { + it('should reject empty steps array', async () => { + const tools = new Tools([]); + const chain = new ExecuteToolChain(tools); + + expect(chain.execute({ steps: [] })).rejects.toThrow( + 'Steps array is required and must not be empty' + ); + }); + + it('should handle string input', async () => { + const mockTool = createMockTool('tool1', 'Test tool', async () => ({ result: 'ok' })); + const tools = new Tools([mockTool]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute( + JSON.stringify({ + steps: [ + { + toolName: 'tool1', + parameters: {}, + }, + ], + }) + ); + + expect(result.success).toBe(true); + }); + }); + + describe('Beta warning', () => { + it('should include beta flag and warning', async () => { + const mockTool = createMockTool('tool1', 'Test tool', async () => ({ result: 'ok' })); + const tools = new Tools([mockTool]); + const chain = new ExecuteToolChain(tools); + + const result = await chain.execute({ + steps: [ + { + toolName: 'tool1', + parameters: {}, + }, + ], + }); + + expect(result.beta).toBe(true); + expect(result.warning).toContain('beta feature'); + }); + }); +}); diff --git a/src/meta-tools/tests/get-relevant-tools.spec.ts b/src/meta-tools/tests/get-relevant-tools.spec.ts new file mode 100644 index 00000000..9539abdd --- /dev/null +++ b/src/meta-tools/tests/get-relevant-tools.spec.ts @@ -0,0 +1,204 @@ +import { describe, expect, it } from 'bun:test'; +import { BaseTool } from '../../tool'; +import type { ExecuteConfig, ToolParameters } from '../../types'; +import { GetRelevantTools } from '../get-relevant-tools'; + +// Mock tools for testing +const createMockTool = (name: string, description: string): BaseTool => { + const params: ToolParameters = { + type: 'object', + properties: { + id: { type: 'string' }, + }, + }; + + const executeConfig: ExecuteConfig = { + method: 'GET', + url: 'https://api.example.com/test', + bodyType: 'json', + params: [], + }; + + return new BaseTool(name, description, params, executeConfig); +}; + +const mockTools = [ + createMockTool('jira_get_ticket', 'Get a Jira ticket by ID'), + createMockTool('jira_create_ticket', 'Create a new Jira ticket'), + createMockTool('jira_update_ticket', 'Update an existing Jira ticket'), + createMockTool('documents_search', 'Search for documents in the system'), + createMockTool('documents_upload', 'Upload a new document'), + createMockTool('hris_list_employees', 'List all employees in the HRIS system'), + createMockTool('hris_get_employee', 'Get a specific employee by ID'), + createMockTool('ats_list_candidates', 'List all candidates in the ATS'), + createMockTool('ats_move_application', 'Move a job application to a different stage'), + createMockTool('hris_create_time_off', 'Create a time off request for an employee'), +]; + +describe('GetRelevantTools', () => { + const tool = new GetRelevantTools(mockTools); + + describe('Basic functionality', () => { + it('should find tools by exact name match', async () => { + const result = await tool.execute({ + query: 'jira_get_ticket', + }); + + expect(result.success).toBe(true); + expect(result.resultsCount).toBeGreaterThan(0); + + // The first result should be the exact match + expect(result.tools[0].name).toBe('jira_get_ticket'); + expect(result.tools[0].score).toBe(1.0); + expect(result.tools[0].matchReason).toBe('exact name match'); + }); + + it('should find tools by description keywords', async () => { + const result = await tool.execute({ + query: 'search documents', + }); + + expect(result.success).toBe(true); + expect(result.resultsCount).toBeGreaterThan(0); + expect(result.tools[0].name).toBe('documents_search'); + }); + + it('should respect limit parameter', async () => { + const result = await tool.execute({ + query: 'jira', + limit: 2, + }); + + expect(result.success).toBe(true); + expect(result.tools).toHaveLength(2); + }); + + it('should respect minScore parameter', async () => { + const result = await tool.execute({ + query: 'xyz', + minScore: 0.8, + }); + + expect(result.success).toBe(true); + expect(result.resultsCount).toBe(0); + }); + }); + + describe('Use case examples', () => { + it('should find Jira ticket reading tools', async () => { + const result = await tool.execute({ + query: 'jira ticket', + }); + + expect(result.success).toBe(true); + expect(result.resultsCount).toBeGreaterThan(0); + const toolNames = result.tools.map((t: { name: string }) => t.name); + expect(toolNames).toContain('jira_get_ticket'); + }); + + it('should find document search tools', async () => { + const result = await tool.execute({ + query: 'documents search', + }); + + expect(result.success).toBe(true); + expect(result.resultsCount).toBeGreaterThan(0); + expect(result.tools[0].name).toBe('documents_search'); + }); + + it('should find employee listing tools', async () => { + const result = await tool.execute({ + query: 'list employees', + }); + + expect(result.success).toBe(true); + expect(result.resultsCount).toBeGreaterThan(0); + expect(result.tools[0].name).toBe('hris_list_employees'); + }); + + it('should find candidate listing tools', async () => { + const result = await tool.execute({ + query: 'list candidates', + }); + + expect(result.success).toBe(true); + expect(result.resultsCount).toBeGreaterThan(0); + expect(result.tools[0].name).toBe('ats_list_candidates'); + }); + + it('should find application moving tools', async () => { + const result = await tool.execute({ + query: 'move job application', + }); + + expect(result.success).toBe(true); + expect(result.resultsCount).toBeGreaterThan(0); + expect(result.tools[0].name).toBe('ats_move_application'); + }); + + it('should find time off creation tools', async () => { + const result = await tool.execute({ + query: 'create time off request', + }); + + expect(result.success).toBe(true); + expect(result.resultsCount).toBeGreaterThan(0); + expect(result.tools[0].name).toBe('hris_create_time_off'); + }); + }); + + describe('Filter patterns', () => { + it('should apply positive filter patterns', async () => { + const result = await tool.execute({ + query: 'ticket', + filterPatterns: 'jira_*', + }); + + expect(result.success).toBe(true); + const toolNames = result.tools.map((t: { name: string }) => t.name); + for (const name of toolNames) { + expect(name).toMatch(/^jira_/); + } + }); + + it('should apply negative filter patterns', async () => { + const result = await tool.execute({ + query: 'jira', + filterPatterns: ['jira_*', '!*_create_*'], + }); + + expect(result.success).toBe(true); + const toolNames = result.tools.map((t: { name: string }) => t.name); + expect(toolNames).not.toContain('jira_create_ticket'); + }); + }); + + describe('Error handling', () => { + it('should throw error when query is missing', async () => { + await expect(tool.execute({})).rejects.toThrow('Query parameter is required'); + }); + + it('should handle string input', async () => { + const result = await tool.execute( + JSON.stringify({ + query: 'jira_get_ticket', + }) + ); + + expect(result.success).toBe(true); + expect(result.resultsCount).toBeGreaterThan(0); + expect(result.tools[0].name).toBe('jira_get_ticket'); + }); + }); + + describe('Beta warning', () => { + it('should include beta flag and warning', async () => { + const result = await tool.execute({ + query: 'test', + }); + + expect(result.beta).toBe(true); + expect(result.warning).toContain('beta feature'); + }); + }); +}); diff --git a/src/meta-tools/types.ts b/src/meta-tools/types.ts new file mode 100644 index 00000000..8faf6192 --- /dev/null +++ b/src/meta-tools/types.ts @@ -0,0 +1,189 @@ +/** + * Type definitions for meta tools + */ + +import type { Arrayable } from 'type-fest'; +import type { BaseTool } from '../tool'; +import type { JsonDict } from '../types'; + +/** + * Configuration for tool search + */ +export interface ToolSearchConfig { + /** + * Query string to search for in tool names and descriptions + */ + query: string; + + /** + * Maximum number of results to return + * @default 10 + */ + limit?: number; + + /** + * Minimum relevance score (0-1) for results + * @default 0.3 + */ + minScore?: number; + + /** + * Optional filter patterns to apply after search + */ + filterPatterns?: Arrayable; + + /** + * Account ID to use for StackOne tools + */ + accountId?: string; +} + +/** + * Result of a tool search + */ +export interface ToolSearchResult { + /** + * The tool instance + */ + tool: BaseTool; + + /** + * Relevance score (0-1) + */ + score: number; + + /** + * Reason for match (e.g., "name match", "description match") + */ + matchReason: string; +} + +/** + * Configuration for a single step in a tool chain + */ +export interface ToolChainStep { + /** + * Name of the tool to execute + */ + toolName: string; + + /** + * Parameters to pass to the tool + * Can include references to previous step outputs using {{stepN.path.to.value}} syntax + */ + parameters: JsonDict; + + /** + * Optional condition to determine if this step should execute + * Can reference previous step outputs + */ + condition?: string; + + /** + * Optional custom name for this step (defaults to tool name) + */ + stepName?: string; + + /** + * Whether to continue execution if this step fails + * @default false + */ + continueOnError?: boolean; +} + +/** + * Configuration for tool chain execution + */ +export interface ToolChainConfig { + /** + * Array of steps to execute in order + */ + steps: Arrayable; + + /** + * Account ID to use for StackOne tools + */ + accountId?: string; + + /** + * Whether to stop execution on first error + * @default true + */ + stopOnError?: boolean; + + /** + * Maximum execution time in milliseconds + * @default 300000 (5 minutes) + */ + timeout?: number; +} + +/** + * Result of a tool chain execution + */ +export interface ToolChainResult { + /** + * Whether all steps executed successfully + */ + success: boolean; + + /** + * Results from each step + */ + stepResults: ToolChainStepResult[]; + + /** + * Total execution time in milliseconds + */ + executionTime: number; + + /** + * Error message if execution failed + */ + error?: string; +} + +/** + * Result from a single step in a tool chain + */ +export interface ToolChainStepResult { + /** + * Step index + */ + stepIndex: number; + + /** + * Step name + */ + stepName: string; + + /** + * Tool name that was executed + */ + toolName: string; + + /** + * Whether the step executed successfully + */ + success: boolean; + + /** + * Result data from the tool execution + */ + result?: JsonDict; + + /** + * Error message if step failed + */ + error?: string; + + /** + * Whether the step was skipped due to condition + */ + skipped?: boolean; + + /** + * Execution time in milliseconds + */ + executionTime: number; +} diff --git a/src/openapi/tests/__snapshots__/openapi-parser.spec.ts.snap b/src/openapi/tests/__snapshots__/openapi-parser.spec.ts.snap index 645c9569..ae5d92d2 100644 --- a/src/openapi/tests/__snapshots__/openapi-parser.spec.ts.snap +++ b/src/openapi/tests/__snapshots__/openapi-parser.spec.ts.snap @@ -1803,7 +1803,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "type": "array", }, "birthday": { - "description": "The employee birthday", + "description": "The next birthday of the employee (upcoming birthday)", "example": "2021-01-01T00:00:00Z", "format": "date-time", "type": "string", @@ -2178,7 +2178,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "type": "array", }, "date_of_birth": { - "description": "The employee date_of_birth", + "description": "The date when the employee was born", "example": "1990-01-01T00:00:00.000Z", "format": "date-time", "type": "string", @@ -7059,6 +7059,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -8612,6 +8613,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -8701,6 +8703,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -8790,6 +8793,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -8879,6 +8883,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -8968,6 +8973,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -9057,6 +9063,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -9146,6 +9153,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -9235,6 +9243,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -9329,6 +9338,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -9438,6 +9448,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -9537,6 +9548,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -9646,6 +9658,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -9763,6 +9776,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -9891,6 +9905,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -10007,6 +10022,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -10106,6 +10122,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -10223,6 +10240,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -10327,6 +10345,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -10416,6 +10435,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -10505,6 +10525,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -10594,6 +10615,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -10687,6 +10709,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -10812,6 +10835,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -10911,6 +10935,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -11000,6 +11025,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -11106,6 +11132,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -11224,6 +11251,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -11330,6 +11358,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -11419,6 +11448,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -11745,7 +11775,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "type": "array", }, "birthday": { - "description": "The employee birthday", + "description": "The next birthday of the employee (upcoming birthday)", "example": "2021-01-01T00:00:00Z", "format": "date-time", "type": "string", @@ -12098,7 +12128,7 @@ exports[`OpenAPIParser Snapshot Tests should parse all OpenAPI specs correctly 1 "type": "array", }, "date_of_birth": { - "description": "The employee date_of_birth", + "description": "The date when the employee was born", "example": "1990-01-01T00:00:00.000Z", "format": "date-time", "type": "string", diff --git a/src/tool.ts b/src/tool.ts index 1672c408..0d740073 100644 --- a/src/tool.ts +++ b/src/tool.ts @@ -308,4 +308,35 @@ export class Tools implements Iterable { forEach(callback: (tool: BaseTool) => void): void { this.tools.forEach(callback); } + + /** + * Get the GetRelevantTools meta tool for finding relevant tools + * @returns GetRelevantTools instance + */ + metaRelevantTool(): BaseTool { + // Lazy import to avoid circular dependency + const { GetRelevantTools } = require('./meta-tools'); + return new GetRelevantTools(this.tools); + } + + /** + * Get the ExecuteToolChain meta tool for executing tools + * @returns ExecuteToolChain instance + */ + metaExecuteTool(): BaseTool { + // Lazy import to avoid circular dependency + const { ExecuteToolChain } = require('./meta-tools'); + return new ExecuteToolChain(this); + } + + /** + * Get both meta tools + * @returns Object with both meta tools + */ + metaTools(): { metaRelevantTool: BaseTool; metaExecuteTool: BaseTool } { + return { + metaRelevantTool: this.metaRelevantTool(), + metaExecuteTool: this.metaExecuteTool(), + }; + } } diff --git a/src/toolsets/tests/__snapshots__/stackone.spec.ts.snap b/src/toolsets/tests/__snapshots__/stackone.spec.ts.snap index b9d28494..ba2586f8 100644 --- a/src/toolsets/tests/__snapshots__/stackone.spec.ts.snap +++ b/src/toolsets/tests/__snapshots__/stackone.spec.ts.snap @@ -63,6 +63,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -294,6 +295,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -432,6 +434,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -598,6 +601,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -963,7 +967,7 @@ Tools { "type": "array", }, "birthday": { - "description": "The employee birthday", + "description": "The next birthday of the employee (upcoming birthday)", "example": "2021-01-01T00:00:00Z", "format": "date-time", "type": "string", @@ -1318,7 +1322,7 @@ Tools { "type": "array", }, "date_of_birth": { - "description": "The employee date_of_birth", + "description": "The date when the employee was born", "example": "1990-01-01T00:00:00.000Z", "format": "date-time", "type": "string", @@ -3481,7 +3485,7 @@ Tools { "type": "array", }, "birthday": { - "description": "The employee birthday", + "description": "The next birthday of the employee (upcoming birthday)", "example": "2021-01-01T00:00:00Z", "format": "date-time", "type": "string", @@ -3814,7 +3818,7 @@ Tools { "type": "array", }, "date_of_birth": { - "description": "The employee date_of_birth", + "description": "The date when the employee was born", "example": "1990-01-01T00:00:00.000Z", "format": "date-time", "type": "string", @@ -5731,6 +5735,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -9515,6 +9520,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -9770,6 +9776,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -10006,6 +10013,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -13746,6 +13754,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -14031,6 +14040,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -14297,6 +14307,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -15263,6 +15274,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -15396,6 +15408,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -15529,6 +15542,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -15662,6 +15676,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -15795,6 +15810,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -15928,6 +15944,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -16649,6 +16666,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -16880,6 +16898,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -17115,6 +17134,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -17379,6 +17399,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -17627,6 +17648,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -17902,6 +17924,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -18132,6 +18155,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -18392,6 +18416,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -18657,6 +18682,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -18800,6 +18826,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -19036,6 +19063,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -19463,6 +19491,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, }, @@ -19842,6 +19871,7 @@ Tools { "additionalProperties": false, "description": "Use a string with a date to only select results updated after that given date", "example": "2020-01-01T00:00:00.000Z", + "format": "date-time", "type": "string", }, },