From 66f5227249c0713d3ab5f477c122720624fe20bc Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Tue, 29 Jul 2025 14:06:25 +0100 Subject: [PATCH 01/14] feat: add meta-tools dependencies - Add @nozbe/microfuzz for fuzzy search functionality - Add @antfu/utils and type-fest as dev dependencies - Update bun.lock with new dependencies --- bun.lock | 156 +++++++++++++++++++++++++-------------------------- package.json | 2 + 2 files changed, 79 insertions(+), 79 deletions(-) diff --git a/bun.lock b/bun.lock index b5d71ad9..7df2183d 100644 --- a/bun.lock +++ b/bun.lock @@ -2,12 +2,14 @@ "lockfileVersion": 1, "workspaces": { "": { - "name": "stackone-ai-ts", + "name": "@stackone/ai", "dependencies": { + "@nozbe/microfuzz": "^1.0.0", "json-schema": "^0.4.0", }, "devDependencies": { "@ai-sdk/openai": "^1.1.14", + "@antfu/utils": "^9.2.0", "@biomejs/biome": "^1.5.3", "@types/bun": "^1.2.4", "@types/json-schema": "^7.0.15", @@ -33,25 +35,27 @@ }, }, "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=="], + "@antfu/utils": ["@antfu/utils@9.2.0", "", {}, "sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw=="], + + "@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 +81,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 +95,19 @@ "@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.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="], - "@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=="], + "@nozbe/microfuzz": ["@nozbe/microfuzz@1.0.0", "", {}, "sha512-XKIg/guk+s1tkPTkHch9hfGOWgsKojT7BqSQddXTppOfVr3SWQhhTCqbgQaPTbppf9gc2kFeG0gpBZZ612UXHA=="], "@open-draft/deferred-promise": ["@open-draft/deferred-promise@2.2.0", "", {}, "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA=="], @@ -113,43 +117,43 @@ "@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=="], + "@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 +161,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 +197,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 +233,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 +245,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 +259,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 +295,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 +307,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 +331,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 +357,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 +369,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 +383,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 +413,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 +425,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 +443,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 +453,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 +461,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 +481,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/package.json b/package.json index dfc8da02..9461fc3a 100644 --- a/package.json +++ b/package.json @@ -34,10 +34,12 @@ "format": "biome format --write ." }, "dependencies": { + "@nozbe/microfuzz": "^1.0.0", "json-schema": "^0.4.0" }, "devDependencies": { "@ai-sdk/openai": "^1.1.14", + "@antfu/utils": "^9.2.0", "@biomejs/biome": "^1.5.3", "@types/bun": "^1.2.4", "@types/json-schema": "^7.0.15", From 53f3269f61b2fafba3da232a8b9c0d31003cbf40 Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Tue, 29 Jul 2025 14:06:45 +0100 Subject: [PATCH 02/14] feat: create meta-tools core implementation - Add consts.ts with configuration constants - Add types.ts with TypeScript type definitions - Add get-relevant-tools.ts for intelligent tool discovery using fuzzy search - Add execute-tool-chain.ts for orchestrating tool execution workflows - Add index.ts to export all meta-tools functionality - Add comprehensive test suite for meta-tools --- src/meta-tools/consts.ts | 4 + src/meta-tools/execute-tool-chain.ts | 351 +++++++++++++ src/meta-tools/get-relevant-tools.ts | 215 ++++++++ src/meta-tools/index.ts | 15 + .../tests/execute-tool-chain.spec.ts | 495 ++++++++++++++++++ .../tests/get-relevant-tools.spec.ts | 204 ++++++++ src/meta-tools/types.ts | 189 +++++++ 7 files changed, 1473 insertions(+) create mode 100644 src/meta-tools/consts.ts create mode 100644 src/meta-tools/execute-tool-chain.ts create mode 100644 src/meta-tools/get-relevant-tools.ts create mode 100644 src/meta-tools/index.ts create mode 100644 src/meta-tools/tests/execute-tool-chain.spec.ts create mode 100644 src/meta-tools/tests/get-relevant-tools.spec.ts create mode 100644 src/meta-tools/types.ts 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..77a0af85 --- /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 { toArray } from '@antfu/utils'; +import { BaseTool, StackOneTool, type Tools } from '../tool'; +import type { ExecuteConfig, JsonDict, ToolParameters } from '../types'; +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( + 'execute_tool_chain', + `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..efe1d3d7 --- /dev/null +++ b/src/meta-tools/get-relevant-tools.ts @@ -0,0 +1,215 @@ +/** + * 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 { toArray } from '@antfu/utils'; +import createFuzzySearch from '@nozbe/microfuzz'; +import type { Arrayable } from 'type-fest'; +import { BaseTool } from '../tool'; +import type { ExecuteConfig, JsonDict, ToolParameters } from '../types'; +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[]; + + 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_*")', + }, + accountId: { + type: 'string', + description: 'Account ID to use for StackOne tools', + }, + }, + required: ['query'], + }; + + const executeConfig: ExecuteConfig = { + method: 'LOCAL', + url: 'local://get-relevant-tools', + bodyType: 'json', + params: [], + }; + + super( + 'get_relevant_tools', + `Search for relevant tools based on natural language query. ${BETA_WARNING}`, + parameters, + executeConfig + ); + + this.availableTools = tools; + } + + /** + * 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'); + } + + // Perform the search + const results = 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 searchTools(config: ToolSearchConfig): ToolSearchResult[] { + const { query, limit = 10, minScore = 0.3, filterPatterns } = config; + + // Filter tools based on patterns first + const candidateTools = filterPatterns + ? this.availableTools.filter((tool) => this.matchesFilter(tool.name, filterPatterns)) + : this.availableTools; + + // Create a fuzzy search function + const fuzzySearch = createFuzzySearch(candidateTools, { + // Index on both name and description + getText: (tool) => [tool.name, tool.description], + }); + + // Search for matches + const results = fuzzySearch(query); + + // Convert results to our format + // Note: microfuzz uses lower scores for better matches, so we need to invert + const scoredTools: ToolSearchResult[] = results + .map((result) => { + const tool = result.item; + const microfuzzScore = result.score; + + // Convert microfuzz score (lower is better) to our score (higher is better) + // microfuzz scores: 0 = exact, 0.1 = full match, 0.5 = starts with, etc. + // We'll map these to 0-1 where 1 is best + let normalizedScore: number; + let matchReason: string; + + if (microfuzzScore === 0) { + normalizedScore = 1.0; + matchReason = 'exact name match'; + } else if (microfuzzScore <= 0.1) { + normalizedScore = 0.95; + matchReason = 'full match (case-insensitive)'; + } else if (microfuzzScore <= 0.5) { + normalizedScore = 0.9; + matchReason = 'starts with query'; + } else if (microfuzzScore <= 1) { + normalizedScore = 0.8; + matchReason = 'contains query at word boundary'; + } else if (microfuzzScore <= 1.5) { + normalizedScore = 0.7; + matchReason = 'contains query words'; + } else if (microfuzzScore <= 2) { + normalizedScore = 0.6; + matchReason = 'contains query'; + } else { + // Fuzzy match - map score 2-10 to 0.5-0.1 + normalizedScore = Math.max(0.1, 0.5 - (microfuzzScore - 2) * 0.05); + matchReason = 'fuzzy match'; + } + + return { + tool, + score: normalizedScore, + matchReason, + }; + }) + .filter((result) => result.score >= minScore); + + // 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 { + const regexPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*').replace(/\?/g, '.'); + 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; +} From 7934e056a5a9a62eab5b06c09291d76924b8001f Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Tue, 29 Jul 2025 14:07:02 +0100 Subject: [PATCH 03/14] feat: export meta-tools from main index - Export ExecuteToolChain and GetRelevantTools classes - Export all meta-tools types for public API --- src/index.ts | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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'; From 683eb7bdeb7e9ff37d70ba162eb9ea8a4cacf526 Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Tue, 29 Jul 2025 14:07:21 +0100 Subject: [PATCH 04/14] feat: integrate meta-tools into StackOneToolSet - Add includeMetaTools option to StackOneToolSetConfig (default: true) - Import meta-tools classes (GetRelevantTools and ExecuteToolChain) - Implement shouldIncludeMetaTools method for filtering logic - Automatically include meta-tools when getting StackOne tools - Meta-tools can be disabled by setting includeMetaTools to false --- src/toolsets/stackone.ts | 56 ++++++++++++++++++++++++++++++++++++++-- 1 file changed, 54 insertions(+), 2 deletions(-) diff --git a/src/toolsets/stackone.ts b/src/toolsets/stackone.ts index 907b5a92..8fedf1cf 100644 --- a/src/toolsets/stackone.ts +++ b/src/toolsets/stackone.ts @@ -1,5 +1,6 @@ +import { ExecuteToolChain, GetRelevantTools } from '../meta-tools'; import { loadStackOneSpecs } from '../openapi/loader'; -import { StackOneTool, type Tools } from '../tool'; +import { StackOneTool, Tools } from '../tool'; import type { ToolDefinition } from '../types'; import { removeJsonSchemaProperty } from '../utils/schema'; import { type BaseToolSetConfig, ToolSet, ToolSetConfigError } from './base'; @@ -12,6 +13,7 @@ export interface StackOneToolSetConfig extends BaseToolSetConfig { accountId?: string; strict?: boolean; removedParams?: string[]; // List of parameters to remove from all tools + includeMetaTools?: boolean; // Whether to include meta tools (default: true) } /** @@ -35,6 +37,7 @@ export class StackOneToolSet extends ToolSet { */ private accountId?: string; private readonly _removedParams: string[]; + private readonly includeMetaTools: boolean; /** * Initialize StackOne toolset with API key and optional account ID @@ -79,6 +82,7 @@ export class StackOneToolSet extends ToolSet { this.accountId = accountId; this._removedParams = ['source_value']; + this.includeMetaTools = config?.includeMetaTools !== false; // Default to true // Load tools this.loadTools(); @@ -100,7 +104,20 @@ export class StackOneToolSet extends ToolSet { : {}; // Get tools with headers - return this.getTools(filterPattern, headers); + const tools = this.getTools(filterPattern, headers); + + // If meta tools are included and no specific filter is provided or filter matches meta tools + if (this.includeMetaTools && this.shouldIncludeMetaTools(filterPattern)) { + const allTools = tools.toArray(); + + // Add meta tools + const metaTools = [new GetRelevantTools(allTools), new ExecuteToolChain(tools)]; + + // Return combined tools + return new Tools([...allTools, ...metaTools]); + } + + return tools; } /** @@ -159,4 +176,39 @@ export class StackOneToolSet extends ToolSet { ); } } + + /** + * Check if meta tools should be included based on filter pattern + * @param filterPattern Filter pattern to check + * @returns Whether meta tools should be included + */ + private shouldIncludeMetaTools(filterPattern?: string | string[]): boolean { + // If no filter, include meta tools + if (!filterPattern) { + return true; + } + + // Check if any pattern would match meta tool names + const metaToolNames = ['get_relevant_tools', 'execute_tool_chain']; + const patterns = Array.isArray(filterPattern) ? filterPattern : [filterPattern]; + + for (const pattern of patterns) { + // If pattern starts with !, it's a negative pattern + if (pattern.startsWith('!')) { + const negPattern = pattern.substring(1); + // If any meta tool matches negative pattern, exclude meta tools + if (metaToolNames.some((name) => this._matchGlob(name, negPattern))) { + return false; + } + } else { + // If any meta tool matches positive pattern, include meta tools + if (metaToolNames.some((name) => this._matchGlob(name, pattern))) { + return true; + } + } + } + + // If only positive patterns and no match, exclude meta tools + return patterns.every((p) => p.startsWith('!')); + } } From e25d80a214949f45b15e484cbfb1927d2497d9b8 Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Tue, 29 Jul 2025 14:07:37 +0100 Subject: [PATCH 05/14] feat: add meta-tools example - Demonstrate GetRelevantTools for discovering tools by natural language - Show ExecuteToolChain for orchestrating complex workflows - Include practical examples with employee onboarding scenario --- examples/meta-tools.ts | 211 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 211 insertions(+) create mode 100644 examples/meta-tools.ts diff --git a/examples/meta-tools.ts b/examples/meta-tools.ts new file mode 100644 index 00000000..aaca5a48 --- /dev/null +++ b/examples/meta-tools.ts @@ -0,0 +1,211 @@ +/** + * 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 } from '../src'; +import { ACCOUNT_IDS } from './constants'; + +// Example 1: Using GetRelevantTools to discover tools +async function discoverToolsExample() { + console.log('\n=== Example 1: Discovering Relevant Tools ==='); + + const toolset = new StackOneToolSet(); + const tools = toolset.getStackOneTools(['*']); // Use wildcard to avoid warning + + // Get the get_relevant_tools meta tool + const relevantToolsFinder = tools.getTool('get_relevant_tools'); + + if (!relevantToolsFinder) { + console.error('get_relevant_tools not found'); + return; + } + + // 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 ExecuteToolChain for complex workflows +async function toolChainExample() { + console.log('\n=== Example 2: Executing Tool Chains ==='); + + const toolset = new StackOneToolSet(); + const tools = toolset.getStackOneTools(['*'], ACCOUNT_IDS.HRIS); // Use wildcard to avoid warning + + // Get the execute_tool_chain meta tool + const toolChain = tools.getTool('execute_tool_chain'); + + if (!toolChain) { + console.error('execute_tool_chain not found'); + return; + } + + // 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(); + // Limit tools to HRIS tools only (plus meta tools) to stay under 128 limit + const tools = toolset.getStackOneTools( + ['hris_*', 'get_relevant_tools', 'execute_tool_chain'], + ACCOUNT_IDS.HRIS + ); + + // Convert tools to AI SDK format (includes meta tools) + const aiTools = tools.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 get_relevant_tools to find tools + related to listing employees. Then use execute_tool_chain 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: Filtering with meta tools +async function filteringExample() { + console.log('\n=== Example 4: Filtering with Meta Tools ==='); + + const toolset = new StackOneToolSet(); + + // Include only HRIS tools and meta tools + const hrisAndMetaTools = toolset.getStackOneTools([ + 'hris_*', + 'get_relevant_tools', + 'execute_tool_chain', + ]); + + console.log(`\nTotal tools available: ${hrisAndMetaTools.length}`); + + // Exclude meta tools + const noMetaTools = toolset.getStackOneTools(['*', '!get_relevant_tools', '!execute_tool_chain']); + + console.log(`Tools without meta tools: ${noMetaTools.length}`); + + // Disable meta tools entirely + const toolsetNoMeta = new StackOneToolSet({ includeMetaTools: false }); + const allToolsNoMeta = toolsetNoMeta.getStackOneTools(); + + console.log(`Tools with meta tools disabled: ${allToolsNoMeta.length}`); +} + +// 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 filteringExample(); + } catch (error) { + console.error('Error in examples:', error); + } +} + +// Only run if this file is executed directly +if (import.meta.main) { + main(); +} From 5e954173a6c10e43efb65775ac4014edfbdfbdd6 Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Tue, 29 Jul 2025 14:08:12 +0100 Subject: [PATCH 06/14] docs: add comprehensive meta-tools documentation - Document GetRelevantTools for intelligent tool discovery - Document ExecuteToolChain for workflow orchestration - Add practical usage examples with code snippets - Include beta warning for API stability - Show how meta-tools integrate with AI agents --- README.md | 70 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/README.md b/README.md index 3988eedf..a9d58ee8 100644 --- a/README.md +++ b/README.md @@ -16,6 +16,76 @@ These toolsets provide functionality to filter, transform, and execute tools and 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: + +#### GetRelevantTools + +Discover relevant tools based on natural language queries: + +```typescript +import { StackOneToolSet } from "@stackone/ai"; + +const toolset = new StackOneToolSet(); +const tools = toolset.getStackOneTools(); + +// Get the meta tool +const relevantToolsFinder = tools.getTool("get_relevant_tools"); + +// Find tools for specific tasks +const result = await relevantToolsFinder.execute({ + query: "read jira ticket", + limit: 5, + minScore: 0.5 +}); + +// Returns tools ranked by relevance with scores and match reasons +``` + +#### ExecuteToolChain + +Orchestrate multiple tool executions with parameter passing: + +```typescript +const toolChain = tools.getTool("execute_tool_chain"); + +// 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" +}); +``` + +Meta tools are included by default but can be disabled: + +```typescript +// Disable meta tools +const toolset = new StackOneToolSet({ includeMetaTools: false }); + +// Filter meta tools +const tools = toolset.getStackOneTools(["*", "!get_relevant_tools", "!execute_tool_chain"]); +``` + +[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. From 83d111130e028cc5f614ce8b23bf249c23980cab Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Tue, 29 Jul 2025 14:15:21 +0100 Subject: [PATCH 07/14] ENG-10458 create meta tool From 5369eccc3062b6e99b355fecd67f65134f6bea19 Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Wed, 30 Jul 2025 18:27:09 +0100 Subject: [PATCH 08/14] implement toArray --- bun.lock | 3 --- package.json | 1 - src/meta-tools/execute-tool-chain.ts | 2 +- src/meta-tools/get-relevant-tools.ts | 2 +- 4 files changed, 2 insertions(+), 6 deletions(-) diff --git a/bun.lock b/bun.lock index 7df2183d..2087876c 100644 --- a/bun.lock +++ b/bun.lock @@ -9,7 +9,6 @@ }, "devDependencies": { "@ai-sdk/openai": "^1.1.14", - "@antfu/utils": "^9.2.0", "@biomejs/biome": "^1.5.3", "@types/bun": "^1.2.4", "@types/json-schema": "^7.0.15", @@ -45,8 +44,6 @@ "@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=="], - "@antfu/utils": ["@antfu/utils@9.2.0", "", {}, "sha512-Oq1d9BGZakE/FyoEtcNeSwM7MpDO2vUBi11RWBZXf75zPsbUVWmUs03EqkRFrcgbXyKTas0BdZWC1wcuSoqSAw=="], - "@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=="], diff --git a/package.json b/package.json index 9461fc3a..791010f8 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,6 @@ }, "devDependencies": { "@ai-sdk/openai": "^1.1.14", - "@antfu/utils": "^9.2.0", "@biomejs/biome": "^1.5.3", "@types/bun": "^1.2.4", "@types/json-schema": "^7.0.15", diff --git a/src/meta-tools/execute-tool-chain.ts b/src/meta-tools/execute-tool-chain.ts index 77a0af85..f89a5cd5 100644 --- a/src/meta-tools/execute-tool-chain.ts +++ b/src/meta-tools/execute-tool-chain.ts @@ -3,9 +3,9 @@ * @beta This is a beta feature and may change in future versions */ -import { toArray } from '@antfu/utils'; 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'; diff --git a/src/meta-tools/get-relevant-tools.ts b/src/meta-tools/get-relevant-tools.ts index efe1d3d7..8264d5bd 100644 --- a/src/meta-tools/get-relevant-tools.ts +++ b/src/meta-tools/get-relevant-tools.ts @@ -3,11 +3,11 @@ * @beta This is a beta feature and may change in future versions */ -import { toArray } from '@antfu/utils'; import createFuzzySearch from '@nozbe/microfuzz'; 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'; From 81a57ea06c7fffad41cea6af95cf90bf3e52dbb1 Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Thu, 31 Jul 2025 11:33:55 +0100 Subject: [PATCH 09/14] feat: replace fuzzy search with Orama for semantic meta-search - Replace @nozbe/microfuzz with @orama/orama for improved semantic search - Add metaSearchTools method to StackOneToolSet for natural language tool discovery - Update GetRelevantTools to use Orama with BM25 scoring algorithm - Add stemming tokenizer for better search quality - Implement field boosting (name: 2x, tags: 1.5x, description: 1x) - Maintain backward compatibility with existing score normalization - Fix linting issue: use unknown[] instead of any[] in array tests --- bun.lock | 6 +- package.json | 2 +- src/meta-tools/get-relevant-tools.ts | 169 ++++++++++++++++++--------- src/toolsets/stackone.ts | 111 ++++++++++++++++++ 4 files changed, 228 insertions(+), 60 deletions(-) diff --git a/bun.lock b/bun.lock index 2087876c..0e4b8465 100644 --- a/bun.lock +++ b/bun.lock @@ -4,7 +4,7 @@ "": { "name": "@stackone/ai", "dependencies": { - "@nozbe/microfuzz": "^1.0.0", + "@orama/orama": "^3.1.11", "json-schema": "^0.4.0", }, "devDependencies": { @@ -104,8 +104,6 @@ "@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=="], - "@nozbe/microfuzz": ["@nozbe/microfuzz@1.0.0", "", {}, "sha512-XKIg/guk+s1tkPTkHch9hfGOWgsKojT7BqSQddXTppOfVr3SWQhhTCqbgQaPTbppf9gc2kFeG0gpBZZ612UXHA=="], - "@open-draft/deferred-promise": ["@open-draft/deferred-promise@2.2.0", "", {}, "sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA=="], "@open-draft/logger": ["@open-draft/logger@0.3.0", "", { "dependencies": { "is-node-process": "^1.2.0", "outvariant": "^1.4.0" } }, "sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ=="], @@ -114,6 +112,8 @@ "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + "@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.71.0", "", {}, "sha512-5CwQ4MI+P4MQbjLWXgNurA+igGwu/opNetIE13LBs9+V93R64MLvDKOOLZIXSzEfovU3Zef3q3GjPnMTgJTn2w=="], diff --git a/package.json b/package.json index 791010f8..9f5b81d6 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,7 @@ "format": "biome format --write ." }, "dependencies": { - "@nozbe/microfuzz": "^1.0.0", + "@orama/orama": "^3.1.11", "json-schema": "^0.4.0" }, "devDependencies": { diff --git a/src/meta-tools/get-relevant-tools.ts b/src/meta-tools/get-relevant-tools.ts index 8264d5bd..fbd454ea 100644 --- a/src/meta-tools/get-relevant-tools.ts +++ b/src/meta-tools/get-relevant-tools.ts @@ -3,7 +3,7 @@ * @beta This is a beta feature and may change in future versions */ -import createFuzzySearch from '@nozbe/microfuzz'; +import { create, insert, search } from '@orama/orama'; import type { Arrayable } from 'type-fest'; import { BaseTool } from '../tool'; import type { ExecuteConfig, JsonDict, ToolParameters } from '../types'; @@ -18,6 +18,8 @@ import type { ToolSearchConfig, ToolSearchResult } from './types'; */ export class GetRelevantTools extends BaseTool { private availableTools: BaseTool[]; + private oramaDb: unknown; + private dbInitialized = false; constructor(tools: BaseTool[]) { const parameters: ToolParameters = { @@ -72,6 +74,49 @@ export class GetRelevantTools extends BaseTool { 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 */ @@ -84,8 +129,11 @@ export class GetRelevantTools extends BaseTool { throw new StackOneError('Query parameter is required'); } + // Initialize Orama DB if needed + await this.initializeOramaDb(); + // Perform the search - const results = this.searchTools(config); + const results = await this.searchTools(config); // Format results for output const formattedResults = results.map((result) => ({ @@ -117,67 +165,76 @@ export class GetRelevantTools extends BaseTool { /** * Search for tools based on the configuration */ - private searchTools(config: ToolSearchConfig): ToolSearchResult[] { + private async searchTools(config: ToolSearchConfig): Promise { const { query, limit = 10, minScore = 0.3, filterPatterns } = config; - // Filter tools based on patterns first - const candidateTools = filterPatterns - ? this.availableTools.filter((tool) => this.matchesFilter(tool.name, filterPatterns)) - : this.availableTools; - - // Create a fuzzy search function - const fuzzySearch = createFuzzySearch(candidateTools, { - // Index on both name and description - getText: (tool) => [tool.name, tool.description], + // 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 + }, }); - // Search for matches - const results = fuzzySearch(query); - - // Convert results to our format - // Note: microfuzz uses lower scores for better matches, so we need to invert - const scoredTools: ToolSearchResult[] = results - .map((result) => { - const tool = result.item; - const microfuzzScore = result.score; - - // Convert microfuzz score (lower is better) to our score (higher is better) - // microfuzz scores: 0 = exact, 0.1 = full match, 0.5 = starts with, etc. - // We'll map these to 0-1 where 1 is best - let normalizedScore: number; - let matchReason: string; - - if (microfuzzScore === 0) { - normalizedScore = 1.0; - matchReason = 'exact name match'; - } else if (microfuzzScore <= 0.1) { - normalizedScore = 0.95; - matchReason = 'full match (case-insensitive)'; - } else if (microfuzzScore <= 0.5) { - normalizedScore = 0.9; - matchReason = 'starts with query'; - } else if (microfuzzScore <= 1) { - normalizedScore = 0.8; - matchReason = 'contains query at word boundary'; - } else if (microfuzzScore <= 1.5) { - normalizedScore = 0.7; - matchReason = 'contains query words'; - } else if (microfuzzScore <= 2) { - normalizedScore = 0.6; - matchReason = 'contains query'; - } else { - // Fuzzy match - map score 2-10 to 0.5-0.1 - normalizedScore = Math.max(0.1, 0.5 - (microfuzzScore - 2) * 0.05); - matchReason = 'fuzzy match'; - } - - return { + // 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, - }; - }) - .filter((result) => result.score >= minScore); + }); + } + } // Sort by score (descending) and limit results scoredTools.sort((a, b) => b.score - a.score); diff --git a/src/toolsets/stackone.ts b/src/toolsets/stackone.ts index 8fedf1cf..923afcd6 100644 --- a/src/toolsets/stackone.ts +++ b/src/toolsets/stackone.ts @@ -1,3 +1,4 @@ +import { create, insert, search } from '@orama/orama'; import { ExecuteToolChain, GetRelevantTools } from '../meta-tools'; import { loadStackOneSpecs } from '../openapi/loader'; import { StackOneTool, Tools } from '../tool'; @@ -38,6 +39,8 @@ export class StackOneToolSet extends ToolSet { private accountId?: string; private readonly _removedParams: string[]; private readonly includeMetaTools: boolean; + private metaSearchDb: unknown; + private metaSearchDbInitialized = false; /** * Initialize StackOne toolset with API key and optional account ID @@ -177,6 +180,114 @@ export class StackOneToolSet extends ToolSet { } } + /** + * Initialize Orama database for meta search + */ + private async initializeMetaSearchDb(): Promise { + if (this.metaSearchDbInitialized) return; + + // Create Orama database schema with BM25 scoring + this.metaSearchDb = await 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.tools) { + // 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.metaSearchDb) throw new Error('Meta search DB not initialized'); + await insert(this.metaSearchDb as Parameters[0], { + name: tool.name, + description: tool.description, + category: category, + tags: [...parts, ...actions], + }); + } + + this.metaSearchDbInitialized = true; + } + + /** + * Meta search for tools using natural language query + * @param query Natural language query describing desired tools + * @param options Search options + * @returns Filtered collection of tools matching the query + */ + async metaSearchTools( + query: string, + options?: { + limit?: number; + minScore?: number; + accountId?: string; + } + ): Promise { + // Initialize meta search DB if needed + await this.initializeMetaSearchDb(); + + const limit = options?.limit || 10; + const minScore = options?.minScore || 0.3; + const effectiveAccountId = options?.accountId || this.accountId; + + // Perform semantic search using Orama + if (!this.metaSearchDb) throw new Error('Meta search DB not initialized'); + const searchResults = await search(this.metaSearchDb 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 + }, + }); + + // Collect matching tools + const matchingTools: StackOneTool[] = []; + + for (const hit of searchResults.hits) { + const toolName = hit.document.name as string; + const tool = this.tools.find((t) => t.name === toolName); + + if (!tool || !(tool instanceof StackOneTool)) continue; + + // Normalize Orama score + const oramaScore = hit.score || 0; + const normalizedScore = Math.min(1, oramaScore / 20); // Normalize to 0-1 + + if (normalizedScore >= minScore) { + // Clone tool with account ID if provided + const toolWithAccount = effectiveAccountId + ? new StackOneTool(tool.name, tool.description, tool.parameters, tool.executeConfig, { + ...tool.getHeaders(), + 'x-account-id': effectiveAccountId, + }) + : tool; + + matchingTools.push(toolWithAccount); + + if (matchingTools.length >= limit) break; + } + } + + return new Tools(matchingTools); + } + /** * Check if meta tools should be included based on filter pattern * @param filterPattern Filter pattern to check From 4dc6aeaeeb92586547750875a7ff31090ff6831d Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Thu, 31 Jul 2025 11:34:54 +0100 Subject: [PATCH 10/14] chore: add orama docs mcp --- .mcp.json | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) 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": {} } } } From ed0c19c67fc0ffbaad8d62d36dabf0344d8e6ecb Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Thu, 31 Jul 2025 19:05:11 +0100 Subject: [PATCH 11/14] use namespace import for orama --- src/toolsets/stackone.ts | 29 ++++++++++++++++------------- 1 file changed, 16 insertions(+), 13 deletions(-) diff --git a/src/toolsets/stackone.ts b/src/toolsets/stackone.ts index 923afcd6..40acbf0c 100644 --- a/src/toolsets/stackone.ts +++ b/src/toolsets/stackone.ts @@ -1,4 +1,4 @@ -import { create, insert, search } from '@orama/orama'; +import * as orama from '@orama/orama'; import { ExecuteToolChain, GetRelevantTools } from '../meta-tools'; import { loadStackOneSpecs } from '../openapi/loader'; import { StackOneTool, Tools } from '../tool'; @@ -187,7 +187,7 @@ export class StackOneToolSet extends ToolSet { if (this.metaSearchDbInitialized) return; // Create Orama database schema with BM25 scoring - this.metaSearchDb = await create({ + this.metaSearchDb = orama.create({ schema: { name: 'string' as const, description: 'string' as const, @@ -212,7 +212,7 @@ export class StackOneToolSet extends ToolSet { const actions = parts.filter((p) => actionTypes.includes(p)); if (!this.metaSearchDb) throw new Error('Meta search DB not initialized'); - await insert(this.metaSearchDb as Parameters[0], { + await orama.insert(this.metaSearchDb as Parameters[0], { name: tool.name, description: tool.description, category: category, @@ -246,16 +246,19 @@ export class StackOneToolSet extends ToolSet { // Perform semantic search using Orama if (!this.metaSearchDb) throw new Error('Meta search DB not initialized'); - const searchResults = await search(this.metaSearchDb 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 - }, - }); + const searchResults = await orama.search( + this.metaSearchDb 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 + }, + } + ); // Collect matching tools const matchingTools: StackOneTool[] = []; From fe8c8ffc58a08cde3525222fdbd07930e088915c Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Mon, 4 Aug 2025 09:23:39 +0100 Subject: [PATCH 12/14] fix: address PR review comments - Fix matchGlob regex escaping to handle all metacharacters - Remove unused accountId parameter from GetRelevantTools schema - Remove unused import mockFetch from openapi.spec.ts --- src/meta-tools/get-relevant-tools.ts | 10 +++++----- src/toolsets/tests/openapi.spec.ts | 1 - 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/src/meta-tools/get-relevant-tools.ts b/src/meta-tools/get-relevant-tools.ts index fbd454ea..858397d2 100644 --- a/src/meta-tools/get-relevant-tools.ts +++ b/src/meta-tools/get-relevant-tools.ts @@ -49,10 +49,6 @@ export class GetRelevantTools extends BaseTool { ], description: 'Optional glob patterns to filter results (e.g., "hris_*", "!*_delete_*")', }, - accountId: { - type: 'string', - description: 'Account ID to use for StackOne tools', - }, }, required: ['query'], }; @@ -265,7 +261,11 @@ export class GetRelevantTools extends BaseTool { * Simple glob pattern matching */ private matchGlob(str: string, pattern: string): boolean { - const regexPattern = pattern.replace(/\./g, '\\.').replace(/\*/g, '.*').replace(/\?/g, '.'); + // 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/toolsets/tests/openapi.spec.ts b/src/toolsets/tests/openapi.spec.ts index e50b07c0..2af46635 100644 --- a/src/toolsets/tests/openapi.spec.ts +++ b/src/toolsets/tests/openapi.spec.ts @@ -1,7 +1,6 @@ import { afterEach, beforeEach, describe, expect, it, mock, spyOn } from 'bun:test'; import path from 'node:path'; import * as OpenAPILoader from '../../openapi/loader'; -import { mockFetch } from '../../tests/utils/fetch-mock'; import { ParameterLocation } from '../../types'; import type { AuthenticationConfig } from '../base'; import { OpenAPIToolSet, type OpenAPIToolSetConfigFromUrl } from '../openapi'; From 01eaf6809e198d486bf6f98c494378e4e2b4561d Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Mon, 4 Aug 2025 09:25:37 +0100 Subject: [PATCH 13/14] feat: add getRelevantMetaTool method and update snapshots - Add getRelevantMetaTool method to StackOneToolSet as suggested by Matt - Update README with documentation for the new API - Update test snapshots after merging latest OpenAPI spec changes --- README.md | 13 +++++++ .../__snapshots__/openapi-parser.spec.ts.snap | 38 +++++++++++++++++-- src/toolsets/stackone.ts | 17 +++++++++ .../tests/__snapshots__/stackone.spec.ts.snap | 38 +++++++++++++++++-- 4 files changed, 98 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index a9d58ee8..3b5c06b9 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,19 @@ const result = await relevantToolsFinder.execute({ // Returns tools ranked by relevance with scores and match reasons ``` +**Alternative API**: You can also get a pre-configured GetRelevantTools instance with specific filters: + +```typescript +// Get a tool finder pre-filtered for HRIS tools +const relevantToolsFinder = toolset.getRelevantMetaTool("hris_*"); + +// Use it to search within the filtered set +const result = await relevantToolsFinder.execute({ + query: "create employee", + limit: 5 +}); +``` + #### ExecuteToolChain Orchestrate multiple tool executions with parameter passing: 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/toolsets/stackone.ts b/src/toolsets/stackone.ts index 40acbf0c..daacc5d0 100644 --- a/src/toolsets/stackone.ts +++ b/src/toolsets/stackone.ts @@ -291,6 +291,23 @@ export class StackOneToolSet extends ToolSet { return new Tools(matchingTools); } + /** + * Get the GetRelevantTools meta tool configured with specific filter patterns + * @param filterPattern Glob pattern(s) to filter tools (e.g., "hris_*") + * @returns GetRelevantTools instance pre-configured with the filter pattern + */ + getRelevantMetaTool(filterPattern: string | string[]): GetRelevantTools { + // Create a new GetRelevantTools instance with filtered tools + const filteredTools = this.tools.filter((tool) => { + if (typeof filterPattern === 'string') { + return this._matchGlob(tool.name, filterPattern); + } + return filterPattern.some((pattern) => this._matchGlob(tool.name, pattern)); + }); + + return new GetRelevantTools(filteredTools); + } + /** * Check if meta tools should be included based on filter pattern * @param filterPattern Filter pattern to check 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", }, }, From 895f99d2f1562b8b7ff98f4b1006617bd2a47bad Mon Sep 17 00:00:00 2001 From: ryoppippi <1560508+ryoppippi@users.noreply.github.com> Date: Mon, 4 Aug 2025 18:00:37 +0100 Subject: [PATCH 14/14] refactor: simplify meta tools API following ACI metafunctions pattern MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add metaRelevantTool(), metaExecuteTool(), and metaTools() methods to Tools class - Rename meta tool names: get_relevant_tools → meta_relevant_tool, execute_tool_chain → meta_execute_tool - Remove unnecessary code from StackOneToolSet (includeMetaTools, metaSearchDb, etc.) - Update examples and documentation to use the new simplified API - Fix import statement in examples (remove require) --- README.md | 51 ++++---- examples/meta-tools.ts | 79 +++++------ src/meta-tools/execute-tool-chain.ts | 2 +- src/meta-tools/get-relevant-tools.ts | 2 +- src/tool.ts | 31 +++++ src/toolsets/stackone.ts | 187 +-------------------------- 6 files changed, 90 insertions(+), 262 deletions(-) diff --git a/README.md b/README.md index 3b5c06b9..c5486480 100644 --- a/README.md +++ b/README.md @@ -23,22 +23,22 @@ Under the hood the StackOneToolSet uses the same OpenAPIParser as the OpenAPIToo StackOne AI SDK includes two powerful meta tools that enable AI agents to discover and orchestrate tool usage intelligently: -#### GetRelevantTools +#### Discovering Relevant Tools -Discover relevant tools based on natural language queries: +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.getStackOneTools(); +const tools = toolset.getTools("hris_*", "account-id"); -// Get the meta tool -const relevantToolsFinder = tools.getTool("get_relevant_tools"); +// Get the meta tool for finding relevant tools +const relevantToolsFinder = tools.metaRelevantTool(); // Find tools for specific tasks const result = await relevantToolsFinder.execute({ - query: "read jira ticket", + query: "list employees", limit: 5, minScore: 0.5 }); @@ -46,25 +46,15 @@ const result = await relevantToolsFinder.execute({ // Returns tools ranked by relevance with scores and match reasons ``` -**Alternative API**: You can also get a pre-configured GetRelevantTools instance with specific filters: +#### Executing Tool Chains -```typescript -// Get a tool finder pre-filtered for HRIS tools -const relevantToolsFinder = toolset.getRelevantMetaTool("hris_*"); - -// Use it to search within the filtered set -const result = await relevantToolsFinder.execute({ - query: "create employee", - limit: 5 -}); -``` - -#### ExecuteToolChain - -Orchestrate multiple tool executions with parameter passing: +Use the `metaExecuteTool()` method to orchestrate multiple tool executions with parameter passing: ```typescript -const toolChain = tools.getTool("execute_tool_chain"); +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({ @@ -87,14 +77,21 @@ const result = await toolChain.execute({ }); ``` -Meta tools are included by default but can be disabled: +#### Getting Both Meta Tools + +You can get both meta tools at once using the `metaTools()` method: ```typescript -// Disable meta tools -const toolset = new StackOneToolSet({ includeMetaTools: false }); +const tools = toolset.getTools("hris_*", "account-id"); + +// Get both meta tools +const { metaRelevantTool, metaExecuteTool } = tools.metaTools(); -// Filter meta tools -const tools = toolset.getStackOneTools(["*", "!get_relevant_tools", "!execute_tool_chain"]); +// Use them in your AI agent +const aiTools = new Tools([ + metaRelevantTool, + metaExecuteTool +]).toAISDK(); ``` [View full examples](examples/meta-tools.ts) diff --git a/examples/meta-tools.ts b/examples/meta-tools.ts index aaca5a48..801304f4 100644 --- a/examples/meta-tools.ts +++ b/examples/meta-tools.ts @@ -9,23 +9,18 @@ import { openai } from '@ai-sdk/openai'; import { generateText } from 'ai'; -import { StackOneToolSet } from '../src'; +import { StackOneToolSet, Tools } from '../src'; import { ACCOUNT_IDS } from './constants'; -// Example 1: Using GetRelevantTools to discover tools +// 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.getStackOneTools(['*']); // Use wildcard to avoid warning + const tools = toolset.getTools('hris_*', ACCOUNT_IDS.HRIS); - // Get the get_relevant_tools meta tool - const relevantToolsFinder = tools.getTool('get_relevant_tools'); - - if (!relevantToolsFinder) { - console.error('get_relevant_tools not found'); - return; - } + // Get the meta tool for finding relevant tools + const relevantToolsFinder = tools.metaRelevantTool(); // Example queries for different use cases const queries = [ @@ -56,20 +51,15 @@ async function discoverToolsExample() { } } -// Example 2: Using ExecuteToolChain for complex workflows +// 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.getStackOneTools(['*'], ACCOUNT_IDS.HRIS); // Use wildcard to avoid warning + const tools = toolset.getTools('hris_*', ACCOUNT_IDS.HRIS); - // Get the execute_tool_chain meta tool - const toolChain = tools.getTool('execute_tool_chain'); - - if (!toolChain) { - console.error('execute_tool_chain not found'); - return; - } + // Get the meta tool for executing tool chains + const toolChain = tools.metaExecuteTool(); // Example: Employee onboarding workflow console.log('\nExecuting employee onboarding workflow...'); @@ -130,22 +120,21 @@ async function aiAgentExample() { } const toolset = new StackOneToolSet(); - // Limit tools to HRIS tools only (plus meta tools) to stay under 128 limit - const tools = toolset.getStackOneTools( - ['hris_*', 'get_relevant_tools', 'execute_tool_chain'], - ACCOUNT_IDS.HRIS - ); + const tools = toolset.getTools('hris_*', ACCOUNT_IDS.HRIS); + + // Get both meta tools + const { metaRelevantTool, metaExecuteTool } = tools.metaTools(); - // Convert tools to AI SDK format (includes meta tools) - const aiTools = tools.toAISDK(); + // 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 get_relevant_tools to find tools - related to listing employees. Then use execute_tool_chain to get the + 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, }); @@ -162,31 +151,25 @@ async function aiAgentExample() { } } -// Example 4: Filtering with meta tools -async function filteringExample() { - console.log('\n=== Example 4: Filtering with Meta Tools ==='); +// 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(); - // Include only HRIS tools and meta tools - const hrisAndMetaTools = toolset.getStackOneTools([ - 'hris_*', - 'get_relevant_tools', - 'execute_tool_chain', - ]); - - console.log(`\nTotal tools available: ${hrisAndMetaTools.length}`); - - // Exclude meta tools - const noMetaTools = toolset.getStackOneTools(['*', '!get_relevant_tools', '!execute_tool_chain']); + // Method 1: Get tools and then get meta tools + const tools = toolset.getTools('hris_*', ACCOUNT_IDS.HRIS); - console.log(`Tools without meta tools: ${noMetaTools.length}`); + // Get individual meta tools + const relevantTool = tools.metaRelevantTool(); + console.log(`Got meta tool: ${relevantTool.name}`); - // Disable meta tools entirely - const toolsetNoMeta = new StackOneToolSet({ includeMetaTools: false }); - const allToolsNoMeta = toolsetNoMeta.getStackOneTools(); + const executeTool = tools.metaExecuteTool(); + console.log(`Got meta tool: ${executeTool.name}`); - console.log(`Tools with meta tools disabled: ${allToolsNoMeta.length}`); + // Get both meta tools at once + const { metaRelevantTool, metaExecuteTool } = tools.metaTools(); + console.log(`Got both meta tools: ${metaRelevantTool.name}, ${metaExecuteTool.name}`); } // Main execution @@ -199,7 +182,7 @@ async function main() { await discoverToolsExample(); await toolChainExample(); await aiAgentExample(); - await filteringExample(); + await usageExample(); } catch (error) { console.error('Error in examples:', error); } diff --git a/src/meta-tools/execute-tool-chain.ts b/src/meta-tools/execute-tool-chain.ts index f89a5cd5..9cf8de1a 100644 --- a/src/meta-tools/execute-tool-chain.ts +++ b/src/meta-tools/execute-tool-chain.ts @@ -79,7 +79,7 @@ export class ExecuteToolChain extends BaseTool { }; super( - 'execute_tool_chain', + 'meta_execute_tool', `Execute multiple tools in sequence with parameter passing between steps. ${BETA_WARNING}`, parameters, executeConfig diff --git a/src/meta-tools/get-relevant-tools.ts b/src/meta-tools/get-relevant-tools.ts index 858397d2..b8ab4649 100644 --- a/src/meta-tools/get-relevant-tools.ts +++ b/src/meta-tools/get-relevant-tools.ts @@ -61,7 +61,7 @@ export class GetRelevantTools extends BaseTool { }; super( - 'get_relevant_tools', + 'meta_relevant_tool', `Search for relevant tools based on natural language query. ${BETA_WARNING}`, parameters, executeConfig 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/stackone.ts b/src/toolsets/stackone.ts index daacc5d0..907b5a92 100644 --- a/src/toolsets/stackone.ts +++ b/src/toolsets/stackone.ts @@ -1,7 +1,5 @@ -import * as orama from '@orama/orama'; -import { ExecuteToolChain, GetRelevantTools } from '../meta-tools'; import { loadStackOneSpecs } from '../openapi/loader'; -import { StackOneTool, Tools } from '../tool'; +import { StackOneTool, type Tools } from '../tool'; import type { ToolDefinition } from '../types'; import { removeJsonSchemaProperty } from '../utils/schema'; import { type BaseToolSetConfig, ToolSet, ToolSetConfigError } from './base'; @@ -14,7 +12,6 @@ export interface StackOneToolSetConfig extends BaseToolSetConfig { accountId?: string; strict?: boolean; removedParams?: string[]; // List of parameters to remove from all tools - includeMetaTools?: boolean; // Whether to include meta tools (default: true) } /** @@ -38,9 +35,6 @@ export class StackOneToolSet extends ToolSet { */ private accountId?: string; private readonly _removedParams: string[]; - private readonly includeMetaTools: boolean; - private metaSearchDb: unknown; - private metaSearchDbInitialized = false; /** * Initialize StackOne toolset with API key and optional account ID @@ -85,7 +79,6 @@ export class StackOneToolSet extends ToolSet { this.accountId = accountId; this._removedParams = ['source_value']; - this.includeMetaTools = config?.includeMetaTools !== false; // Default to true // Load tools this.loadTools(); @@ -107,20 +100,7 @@ export class StackOneToolSet extends ToolSet { : {}; // Get tools with headers - const tools = this.getTools(filterPattern, headers); - - // If meta tools are included and no specific filter is provided or filter matches meta tools - if (this.includeMetaTools && this.shouldIncludeMetaTools(filterPattern)) { - const allTools = tools.toArray(); - - // Add meta tools - const metaTools = [new GetRelevantTools(allTools), new ExecuteToolChain(tools)]; - - // Return combined tools - return new Tools([...allTools, ...metaTools]); - } - - return tools; + return this.getTools(filterPattern, headers); } /** @@ -179,167 +159,4 @@ export class StackOneToolSet extends ToolSet { ); } } - - /** - * Initialize Orama database for meta search - */ - private async initializeMetaSearchDb(): Promise { - if (this.metaSearchDbInitialized) return; - - // Create Orama database schema with BM25 scoring - this.metaSearchDb = orama.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.tools) { - // 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.metaSearchDb) throw new Error('Meta search DB not initialized'); - await orama.insert(this.metaSearchDb as Parameters[0], { - name: tool.name, - description: tool.description, - category: category, - tags: [...parts, ...actions], - }); - } - - this.metaSearchDbInitialized = true; - } - - /** - * Meta search for tools using natural language query - * @param query Natural language query describing desired tools - * @param options Search options - * @returns Filtered collection of tools matching the query - */ - async metaSearchTools( - query: string, - options?: { - limit?: number; - minScore?: number; - accountId?: string; - } - ): Promise { - // Initialize meta search DB if needed - await this.initializeMetaSearchDb(); - - const limit = options?.limit || 10; - const minScore = options?.minScore || 0.3; - const effectiveAccountId = options?.accountId || this.accountId; - - // Perform semantic search using Orama - if (!this.metaSearchDb) throw new Error('Meta search DB not initialized'); - const searchResults = await orama.search( - this.metaSearchDb 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 - }, - } - ); - - // Collect matching tools - const matchingTools: StackOneTool[] = []; - - for (const hit of searchResults.hits) { - const toolName = hit.document.name as string; - const tool = this.tools.find((t) => t.name === toolName); - - if (!tool || !(tool instanceof StackOneTool)) continue; - - // Normalize Orama score - const oramaScore = hit.score || 0; - const normalizedScore = Math.min(1, oramaScore / 20); // Normalize to 0-1 - - if (normalizedScore >= minScore) { - // Clone tool with account ID if provided - const toolWithAccount = effectiveAccountId - ? new StackOneTool(tool.name, tool.description, tool.parameters, tool.executeConfig, { - ...tool.getHeaders(), - 'x-account-id': effectiveAccountId, - }) - : tool; - - matchingTools.push(toolWithAccount); - - if (matchingTools.length >= limit) break; - } - } - - return new Tools(matchingTools); - } - - /** - * Get the GetRelevantTools meta tool configured with specific filter patterns - * @param filterPattern Glob pattern(s) to filter tools (e.g., "hris_*") - * @returns GetRelevantTools instance pre-configured with the filter pattern - */ - getRelevantMetaTool(filterPattern: string | string[]): GetRelevantTools { - // Create a new GetRelevantTools instance with filtered tools - const filteredTools = this.tools.filter((tool) => { - if (typeof filterPattern === 'string') { - return this._matchGlob(tool.name, filterPattern); - } - return filterPattern.some((pattern) => this._matchGlob(tool.name, pattern)); - }); - - return new GetRelevantTools(filteredTools); - } - - /** - * Check if meta tools should be included based on filter pattern - * @param filterPattern Filter pattern to check - * @returns Whether meta tools should be included - */ - private shouldIncludeMetaTools(filterPattern?: string | string[]): boolean { - // If no filter, include meta tools - if (!filterPattern) { - return true; - } - - // Check if any pattern would match meta tool names - const metaToolNames = ['get_relevant_tools', 'execute_tool_chain']; - const patterns = Array.isArray(filterPattern) ? filterPattern : [filterPattern]; - - for (const pattern of patterns) { - // If pattern starts with !, it's a negative pattern - if (pattern.startsWith('!')) { - const negPattern = pattern.substring(1); - // If any meta tool matches negative pattern, exclude meta tools - if (metaToolNames.some((name) => this._matchGlob(name, negPattern))) { - return false; - } - } else { - // If any meta tool matches positive pattern, include meta tools - if (metaToolNames.some((name) => this._matchGlob(name, pattern))) { - return true; - } - } - } - - // If only positive patterns and no match, exclude meta tools - return patterns.every((p) => p.startsWith('!')); - } }