From 6ee8faf2c55a981c1b0a5f77dcbaead3053edf7d Mon Sep 17 00:00:00 2001 From: ssdeanx Date: Thu, 15 Jan 2026 13:22:06 -0500 Subject: [PATCH 1/2] chore: update dependencies and improve code structure - Updated node version in convex.json from "24" to "20". - Enhanced api.d.ts by adding internal API reference and improving type imports. - Fixed formatting in storage.ts by adding missing semicolons. - Defined mastraWorkflowSnapshotsTable in schema.ts to ensure proper indexing. - Updated package-lock.json and package.json to bump convex and @types/node versions. - Renamed E2bSandboxTool to E2bSandboxToolComponent for clarity in e2b-sandbox-tool.tsx. - Refactored output handling in web-scraper-tool.tsx to improve data extraction. - Commented out unused ConvertDataFormatUITool type in types.ts for clarity. --- convex.json | 2 +- convex/_generated/api.d.ts | 44 ++++++++++++++-- convex/mastra/storage.ts | 4 +- convex/schema.ts | 25 +++++++--- next.config.ts | 50 ++++++++++++++++++- package-lock.json | 24 ++++----- package.json | 4 +- .../ai-elements/tools/e2b-sandbox-tool.tsx | 4 +- src/components/ai-elements/tools/types.ts | 3 +- .../ai-elements/tools/web-scraper-tool.tsx | 6 ++- 10 files changed, 128 insertions(+), 38 deletions(-) diff --git a/convex.json b/convex.json index 8b20024..a851db7 100644 --- a/convex.json +++ b/convex.json @@ -1,6 +1,6 @@ { "node": { - "nodeVersion": "24" + "nodeVersion": "20" }, "$schema": "./node_modules/convex/schemas/convex.schema.json" } diff --git a/convex/_generated/api.d.ts b/convex/_generated/api.d.ts index c9ba966..e852c0a 100644 --- a/convex/_generated/api.d.ts +++ b/convex/_generated/api.d.ts @@ -1,4 +1,4 @@ - +/* eslint-disable */ /** * Generated `api` utility. * @@ -8,8 +8,42 @@ * @module */ -import type { AnyApi, AnyComponents } from "convex/server"; +import type * as mastra_storage from "../mastra/storage.js"; + +import type { + ApiFromModules, + FilterApi, + FunctionReference, +} from "convex/server"; + +declare const fullApi: ApiFromModules<{ + "mastra/storage": typeof mastra_storage; +}>; + +/** + * A utility for referencing Convex functions in your app's public API. + * + * Usage: + * ```js + * const myFunctionReference = api.myModule.myFunction; + * ``` + */ +export declare const api: FilterApi< + typeof fullApi, + FunctionReference +>; + +/** + * A utility for referencing Convex functions in your app's internal API. + * + * Usage: + * ```js + * const myFunctionReference = internal.myModule.myFunction; + * ``` + */ +export declare const internal: FilterApi< + typeof fullApi, + FunctionReference +>; -export declare const api: AnyApi; -export declare const internal: AnyApi; -export declare const components: AnyComponents; +export declare const components: {}; diff --git a/convex/mastra/storage.ts b/convex/mastra/storage.ts index 5fe967a..f5d334a 100644 --- a/convex/mastra/storage.ts +++ b/convex/mastra/storage.ts @@ -1,3 +1,3 @@ -import { mastraStorage } from '@mastra/convex/server' +import { mastraStorage } from '@mastra/convex/server'; -export const handle = mastraStorage +export const handle = mastraStorage; diff --git a/convex/schema.ts b/convex/schema.ts index d590345..c9de4e1 100644 --- a/convex/schema.ts +++ b/convex/schema.ts @@ -1,14 +1,27 @@ +import { defineSchema, defineTable } from 'convex/server'; +import { v } from 'convex/values'; import { - mastraDocumentsTable, + mastraThreadsTable, mastraMessagesTable, mastraResourcesTable, mastraScoresTable, - mastraThreadsTable, mastraVectorIndexesTable, mastraVectorsTable, - mastraWorkflowSnapshotsTable, -} from '@mastra/convex' -import { defineSchema } from 'convex/server' + mastraDocumentsTable, +} from '@mastra/convex/schema'; + +// Explicitly define mastra_workflow_snapshots to ensure the `id` field exists +// (some downstream Convex schema variants omit `id` which causes an invalid +// index declaration during Convex schema push). +const mastraWorkflowSnapshotsTable = defineTable({ + id: v.string(), + workflow_name: v.string(), + run_id: v.string(), + resourceId: v.optional(v.string()), + snapshot: v.any(), + createdAt: v.string(), + updatedAt: v.string(), +}).index('by_record_id', ['id']).index('by_workflow_run', ['workflow_name', 'run_id']).index('by_workflow', ['workflow_name']).index('by_resource', ['resourceId']).index('by_created', ['createdAt']); export default defineSchema({ mastra_threads: mastraThreadsTable, @@ -19,4 +32,4 @@ export default defineSchema({ mastra_vector_indexes: mastraVectorIndexesTable, mastra_vectors: mastraVectorsTable, mastra_documents: mastraDocumentsTable, -}) +}); diff --git a/next.config.ts b/next.config.ts index e66aa35..ec200bc 100644 --- a/next.config.ts +++ b/next.config.ts @@ -1,6 +1,7 @@ import createMDX from '@next/mdx' import MonacoWebpackPlugin from 'monaco-editor-webpack-plugin' import type { NextConfig } from 'next' +import type { Configuration } from 'webpack' const nextConfig: NextConfig = { pageExtensions: ['js', 'jsx', 'md', 'mdx', 'ts', 'tsx'], @@ -38,7 +39,7 @@ const nextConfig: NextConfig = { typedRoutes: false, reactStrictMode: true, distDir: '.next', - webpack: (config, { isServer }) => { + webpack: (config: Configuration, { isServer }) => { if (!isServer) { config.plugins = config.plugins ?? [] config.plugins.push( @@ -52,10 +53,55 @@ const nextConfig: NextConfig = { ], }) ) + + // Sanitize resolve.alias so values are serializable for Turbopack/worker cloning + // Ensure resolve exists and normalize alias values to strings/arrays of strings + const existingResolve = config.resolve ?? {} + const aliasObj = (existingResolve as unknown as Record).alias ?? {} + if (typeof aliasObj === 'object' && aliasObj !== null) { + const normalized: Record = {} + for (const [k, v] of Object.entries(aliasObj as Record)) { + if (Array.isArray(v)) { + normalized[k] = v.map((x) => String(x)) + } else if (typeof v === 'object' && v !== null) { + // For objects, stringify to avoid '[object Object]' implicit string coercion + try { + normalized[k] = JSON.stringify(v) + } catch { + normalized[k] = '' + } + } else if (typeof v === 'function') { + // For functions, prefer the function name to avoid serializing the entire function + // Narrow to an object with an optional name property to avoid using the broad Function type + const fnName = (v as { name?: string })?.name ?? '' + normalized[k] = fnName + } else if (v === null || v === undefined) { + // Keep null/undefined normalized to an empty string + normalized[k] = '' + } else { + // At this point, expect primitives (string/number/boolean/symbol/bigint). + // Guard against objects to avoid default Object stringification '[object Object]'. + const t = typeof v + if (t === 'string' || t === 'number' || t === 'boolean' || t === 'symbol' || t === 'bigint') { + // Narrow the type for the linter to avoid base-to-string coercion warnings + normalized[k] = String(v as string | number | boolean | symbol | bigint) + } else { + // Fallback for unexpected non-serializable values + try { + normalized[k] = JSON.stringify(v) + } catch { + normalized[k] = '' + } + } + } + } + config.resolve = { ...existingResolve, alias: normalized } as Configuration['resolve'] + } } return config }, + typescript: { ignoreBuildErrors: true, tsconfigPath: './tsconfig.json', @@ -92,7 +138,7 @@ const nextConfig: NextConfig = { // optimizeCss: true, esmExternals: true, scrollRestoration: true, -// cpus: 16, + // cpus: 16, // cssChunking: true, // craCompat: true, // validateRSCRequestHeaders: true, diff --git a/package-lock.json b/package-lock.json index ab3edcc..693c0bd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -94,7 +94,7 @@ "cmdk": "^1.1.1", "concurrently": "^9.2.1", "convert-csv-to-json": "^3.20.0", - "convex": "^1.26.2", + "convex": "^1.31.4", "crawlee": "^3.15.3", "critters": "^0.0.25", "csv-parse": "^6.1.0", @@ -189,7 +189,7 @@ "@types/jsdom": "^27.0.0", "@types/leaflet.markercluster": "^1.5.6", "@types/mdx": "^2.0.13", - "@types/node": "^24.10.6", + "@types/node": "^22.19.7", "@types/rbush": "^4.0.0", "@types/react": "^19.2.8", "@types/react-dom": "^19.2.3", @@ -15484,12 +15484,12 @@ } }, "node_modules/@types/node": { - "version": "24.10.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.6.tgz", - "integrity": "sha512-B8h60xgJMR/xmgyX9fncRzEW9gCxoJjdenUhke2v1JGOd/V66KopmWrLPXi5oUI4VuiGK+d+HlXJjDRZMj21EQ==", + "version": "22.19.7", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.19.7.tgz", + "integrity": "sha512-MciR4AKGHWl7xwxkBa6xUGxQJ4VBOmPTF7sL+iGzuahOFaO0jHCsuEfS80pan1ef4gWId1oWOweIhrDEYLuaOw==", "license": "MIT", "dependencies": { - "undici-types": "~7.16.0" + "undici-types": "~6.21.0" } }, "node_modules/@types/node-fetch": { @@ -17216,12 +17216,6 @@ "undici-types": "~6.21.0" } }, - "node_modules/apache-arrow/node_modules/undici-types": { - "version": "6.21.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", - "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "license": "MIT" - }, "node_modules/append-field": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz", @@ -35618,9 +35612,9 @@ } }, "node_modules/undici-types": { - "version": "7.16.0", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", - "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", "license": "MIT" }, "node_modules/unicorn-magic": { diff --git a/package.json b/package.json index 8be2070..9446bee 100644 --- a/package.json +++ b/package.json @@ -130,7 +130,7 @@ "cmdk": "^1.1.1", "concurrently": "^9.2.1", "convert-csv-to-json": "^3.20.0", - "convex": "^1.26.2", + "convex": "^1.31.4", "crawlee": "^3.15.3", "critters": "^0.0.25", "csv-parse": "^6.1.0", @@ -225,7 +225,7 @@ "@types/jsdom": "^27.0.0", "@types/leaflet.markercluster": "^1.5.6", "@types/mdx": "^2.0.13", - "@types/node": "^24.10.6", + "@types/node": "^22.19.7", "@types/rbush": "^4.0.0", "@types/react": "^19.2.8", "@types/react-dom": "^19.2.3", diff --git a/src/components/ai-elements/tools/e2b-sandbox-tool.tsx b/src/components/ai-elements/tools/e2b-sandbox-tool.tsx index ea803c5..1c0e0bc 100644 --- a/src/components/ai-elements/tools/e2b-sandbox-tool.tsx +++ b/src/components/ai-elements/tools/e2b-sandbox-tool.tsx @@ -36,7 +36,7 @@ interface E2bSandboxToolProps { errorText?: string } -export function E2bSandboxTool({ +export function E2bSandboxToolComponent({ input, output, errorText, @@ -80,7 +80,7 @@ export function E2bSandboxTool({ } const { action, sandboxId } = input - const { result } = output + const result = output.result return (
diff --git a/src/components/ai-elements/tools/types.ts b/src/components/ai-elements/tools/types.ts index cf4165d..b9f8574 100644 --- a/src/components/ai-elements/tools/types.ts +++ b/src/components/ai-elements/tools/types.ts @@ -25,7 +25,6 @@ import type { codeSearchTool, colorChangeTool, contentCleanerTool, - convertDataFormatTool, copyDataFileTool, copywriterTool, createDataDirTool, @@ -171,7 +170,7 @@ export type CodeChunkerUITool = InferUITool export type CodeSearchUITool = InferUITool export type ColorChangeUITool = InferUITool export type ContentCleanerUITool = InferUITool -export type ConvertDataFormatUITool = InferUITool +//export type ConvertDataFormatUITool = InferUITool export type CopyDataFileUITool = InferUITool export type CopywriterUITool = InferUITool export type CreateDataDirUITool = InferUITool diff --git a/src/components/ai-elements/tools/web-scraper-tool.tsx b/src/components/ai-elements/tools/web-scraper-tool.tsx index 3d49bb4..9f71b09 100644 --- a/src/components/ai-elements/tools/web-scraper-tool.tsx +++ b/src/components/ai-elements/tools/web-scraper-tool.tsx @@ -64,14 +64,18 @@ export function WebScraperTool({ ) } + const content = output.content || {} const { extractedData, rawContent, markdownContent, + } = content + + const { metadata, images, structuredData, - } = output + } = output.analysis || {} return (
From 0c1317bb9d84ca394b6708df0212f263870fe8a6 Mon Sep 17 00:00:00 2001 From: ssdeanx Date: Thu, 15 Jan 2026 15:44:08 -0500 Subject: [PATCH 2/2] feat: update dependencies and enhance tools - Updated the "ai" package version from ^5.0.121 to ^6.0.37 in package.json. - Added "mathjs" package with version ^15.1.0 to package.json. - Imported "convexMemory" in copywriterAgent.ts for enhanced functionality. - Introduced a helper function `isErrCanceled` in arxiv.tool.ts to streamline error handling for cancellation. - Refactored error handling in arxivPdfParserTool to utilize the new `isErrCanceled` function. - Enhanced PDF data handling in arxivPdfParserTool to support various data types. - Updated temperature conversion error messages in calculator.tool.ts for better clarity. - Improved matrix multiplication error handling in calculator.tool.ts. - Refactored calendar-tool.ts to streamline logging and remove unnecessary mastra references. - Updated chartjs.tool.ts to ensure proper typing and handling of indicator data. - Enhanced code-analysis.tool.ts and code-search.tool.ts to replace mastra references with tracingContext. - Improved error handling and logging in code-chunking.ts for better debugging. - Enhanced weather-tool.ts and web-scraper-tool.ts to ensure proper tracing context usage. - Refactored data exporter logic in dataExporterTool to handle various data types more robustly. - Improved error messages and handling in various tools for better user feedback and debugging. --- mdx-components.tsx | 11 +- package-lock.json | 314 ++++++-------------- package.json | 5 +- src/mastra/agents/copywriterAgent.ts | 1 + src/mastra/tools/arxiv.tool.ts | 75 +++-- src/mastra/tools/calculator.tool.ts | 30 +- src/mastra/tools/calendar-tool.ts | 51 +--- src/mastra/tools/chartjs.tool.ts | 79 ++--- src/mastra/tools/code-analysis.tool.ts | 2 +- src/mastra/tools/code-chunking.ts | 3 +- src/mastra/tools/code-search.tool.ts | 2 +- src/mastra/tools/technical-analysis.tool.ts | 11 +- src/mastra/tools/text-analysis.tool.ts | 6 +- src/mastra/tools/weather-tool.ts | 3 +- src/mastra/tools/web-scraper-tool.ts | 201 ++++++++++--- 15 files changed, 385 insertions(+), 409 deletions(-) diff --git a/mdx-components.tsx b/mdx-components.tsx index 7e9db90..ac705b5 100644 --- a/mdx-components.tsx +++ b/mdx-components.tsx @@ -1,4 +1,5 @@ import type { MDXComponents } from 'mdx/types' +import type { ReactNode } from 'react' import Link from 'next/link' // Custom components that work with MDX plugins @@ -47,7 +48,7 @@ export function useMDXComponents(components: MDXComponents): MDXComponents { // Links with hover effects a: ({ href, children }) => ( {children} @@ -75,8 +76,8 @@ export function useMDXComponents(components: MDXComponents): MDXComponents { ), // Code blocks and inline code - these work with rehype-highlight - code: ({ children, className }) => { - const isInline = !className?.includes('language-') + code: ({ children, className }: { children?: ReactNode; className?: string }) => { + const isInline = typeof className !== 'string' || !className.includes('language-') return isInline ? ( {children} @@ -116,9 +117,9 @@ export function useMDXComponents(components: MDXComponents): MDXComponents { ), // Images with responsive design - img: ({ src, alt, title }) => ( + img: ({ src, alt, title }: { src?: string; alt?: string; title?: string }) => ( {alt=18" - } - }, - "node_modules/@ai-sdk/anthropic/node_modules/@ai-sdk/provider-utils": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.7.tgz", - "integrity": "sha512-ItzTdBxRLieGz1GHPwl9X3+HKfwTfFd9MdIa91aXRnOjUVRw68ENjAGKm3FcXGsBLkXDLaFWgjbTVdXe2igs2w==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.3", - "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.6" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, "node_modules/@ai-sdk/gateway": { - "version": "2.0.27", - "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-2.0.27.tgz", - "integrity": "sha512-8hbezMsGa0crSt7/DKjkYL1UbbJJW/UFxTfhmf5qcIeYeeWG4dTNmm+DWbUdIsTaWvp59KC4eeC9gYXBbTHd7w==", + "version": "3.0.15", + "resolved": "https://registry.npmjs.org/@ai-sdk/gateway/-/gateway-3.0.15.tgz", + "integrity": "sha512-OsWcXMfkF9U38YhU7926rYt4IAtJuZlnM1e8STN8hHb4qbiTaORBSXcFaJuOEZ1kYOf3DqqP7CxbmM543ePzIg==", "license": "Apache-2.0", "dependencies": { - "@ai-sdk/provider": "2.0.1", - "@ai-sdk/provider-utils": "3.0.20", + "@ai-sdk/provider": "3.0.3", + "@ai-sdk/provider-utils": "4.0.7", "@vercel/oidc": "3.1.0" }, "engines": { @@ -352,64 +324,6 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/google-vertex/node_modules/@ai-sdk/provider": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.3.tgz", - "integrity": "sha512-qGPYdoAuECaUXPrrz0BPX1SacZQuJ6zky0aakxpW89QW1hrY0eF4gcFm/3L9Pk8C5Fwe+RvBf2z7ZjDhaPjnlg==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/google-vertex/node_modules/@ai-sdk/provider-utils": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.7.tgz", - "integrity": "sha512-ItzTdBxRLieGz1GHPwl9X3+HKfwTfFd9MdIa91aXRnOjUVRw68ENjAGKm3FcXGsBLkXDLaFWgjbTVdXe2igs2w==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.3", - "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.6" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@ai-sdk/google/node_modules/@ai-sdk/provider": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.3.tgz", - "integrity": "sha512-qGPYdoAuECaUXPrrz0BPX1SacZQuJ6zky0aakxpW89QW1hrY0eF4gcFm/3L9Pk8C5Fwe+RvBf2z7ZjDhaPjnlg==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/google/node_modules/@ai-sdk/provider-utils": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.7.tgz", - "integrity": "sha512-ItzTdBxRLieGz1GHPwl9X3+HKfwTfFd9MdIa91aXRnOjUVRw68ENjAGKm3FcXGsBLkXDLaFWgjbTVdXe2igs2w==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.3", - "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.6" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, "node_modules/@ai-sdk/openai": { "version": "3.0.11", "resolved": "https://registry.npmjs.org/@ai-sdk/openai/-/openai-3.0.11.tgz", @@ -442,36 +356,7 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/openai-compatible/node_modules/@ai-sdk/provider": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.3.tgz", - "integrity": "sha512-qGPYdoAuECaUXPrrz0BPX1SacZQuJ6zky0aakxpW89QW1hrY0eF4gcFm/3L9Pk8C5Fwe+RvBf2z7ZjDhaPjnlg==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/openai-compatible/node_modules/@ai-sdk/provider-utils": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.7.tgz", - "integrity": "sha512-ItzTdBxRLieGz1GHPwl9X3+HKfwTfFd9MdIa91aXRnOjUVRw68ENjAGKm3FcXGsBLkXDLaFWgjbTVdXe2igs2w==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.3", - "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.6" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, - "node_modules/@ai-sdk/openai/node_modules/@ai-sdk/provider": { + "node_modules/@ai-sdk/provider": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.3.tgz", "integrity": "sha512-qGPYdoAuECaUXPrrz0BPX1SacZQuJ6zky0aakxpW89QW1hrY0eF4gcFm/3L9Pk8C5Fwe+RvBf2z7ZjDhaPjnlg==", @@ -483,7 +368,7 @@ "node": ">=18" } }, - "node_modules/@ai-sdk/openai/node_modules/@ai-sdk/provider-utils": { + "node_modules/@ai-sdk/provider-utils": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.7.tgz", "integrity": "sha512-ItzTdBxRLieGz1GHPwl9X3+HKfwTfFd9MdIa91aXRnOjUVRw68ENjAGKm3FcXGsBLkXDLaFWgjbTVdXe2igs2w==", @@ -500,35 +385,6 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/@ai-sdk/provider": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-2.0.1.tgz", - "integrity": "sha512-KCUwswvsC5VsW2PWFqF8eJgSCu5Ysj7m1TxiHTVA6g7k360bk0RNQENT8KTMAYEs+8fWPD3Uu4dEmzGHc+jGng==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/provider-utils": { - "version": "3.0.20", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-3.0.20.tgz", - "integrity": "sha512-iXHVe0apM2zUEzauqJwqmpC37A5rihrStAih5Ks+JE32iTe4LZ58y17UGBjpQQTCRw9YxMeo2UFLxLpBluyvLQ==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "2.0.1", - "@standard-schema/spec": "^1.0.0", - "eventsource-parser": "^3.0.6" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, "node_modules/@ai-sdk/provider-utils-v5": { "name": "@ai-sdk/provider-utils", "version": "3.0.12", @@ -633,35 +489,6 @@ "react": "^18 || ~19.0.1 || ~19.1.2 || ^19.2.1" } }, - "node_modules/@ai-sdk/react/node_modules/@ai-sdk/provider": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.3.tgz", - "integrity": "sha512-qGPYdoAuECaUXPrrz0BPX1SacZQuJ6zky0aakxpW89QW1hrY0eF4gcFm/3L9Pk8C5Fwe+RvBf2z7ZjDhaPjnlg==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/@ai-sdk/react/node_modules/@ai-sdk/provider-utils": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.7.tgz", - "integrity": "sha512-ItzTdBxRLieGz1GHPwl9X3+HKfwTfFd9MdIa91aXRnOjUVRw68ENjAGKm3FcXGsBLkXDLaFWgjbTVdXe2igs2w==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.3", - "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.6" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, "node_modules/@alloc/quick-lru": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", @@ -16799,15 +16626,15 @@ } }, "node_modules/ai": { - "version": "5.0.121", - "resolved": "https://registry.npmjs.org/ai/-/ai-5.0.121.tgz", - "integrity": "sha512-3iYPdARKGLryC/7OA9RgBUaym1gynvWS7UPy8NwoRNCKP52lshldtHB5xcEfVviw7liWH2zJlW9yEzsDglcIEQ==", + "version": "6.0.37", + "resolved": "https://registry.npmjs.org/ai/-/ai-6.0.37.tgz", + "integrity": "sha512-GUMBYx0TXKxXRcWy6DY3ryHJZ7cATxc99WPT7WCD8hGCYkdhFVItKPTtINeTlK+FlUomDqzjPGtiDcVftcw//w==", "license": "Apache-2.0", "peer": true, "dependencies": { - "@ai-sdk/gateway": "2.0.27", - "@ai-sdk/provider": "2.0.1", - "@ai-sdk/provider-utils": "3.0.20", + "@ai-sdk/gateway": "3.0.15", + "@ai-sdk/provider": "3.0.3", + "@ai-sdk/provider-utils": "4.0.7", "@opentelemetry/api": "1.9.0" }, "engines": { @@ -17037,35 +16864,6 @@ "zod": "^3.25.76 || ^4.1.8" } }, - "node_modules/ai-sdk-provider-opencode-sdk/node_modules/@ai-sdk/provider": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider/-/provider-3.0.3.tgz", - "integrity": "sha512-qGPYdoAuECaUXPrrz0BPX1SacZQuJ6zky0aakxpW89QW1hrY0eF4gcFm/3L9Pk8C5Fwe+RvBf2z7ZjDhaPjnlg==", - "license": "Apache-2.0", - "dependencies": { - "json-schema": "^0.4.0" - }, - "engines": { - "node": ">=18" - } - }, - "node_modules/ai-sdk-provider-opencode-sdk/node_modules/@ai-sdk/provider-utils": { - "version": "4.0.7", - "resolved": "https://registry.npmjs.org/@ai-sdk/provider-utils/-/provider-utils-4.0.7.tgz", - "integrity": "sha512-ItzTdBxRLieGz1GHPwl9X3+HKfwTfFd9MdIa91aXRnOjUVRw68ENjAGKm3FcXGsBLkXDLaFWgjbTVdXe2igs2w==", - "license": "Apache-2.0", - "dependencies": { - "@ai-sdk/provider": "3.0.3", - "@standard-schema/spec": "^1.1.0", - "eventsource-parser": "^3.0.6" - }, - "engines": { - "node": ">=18" - }, - "peerDependencies": { - "zod": "^3.25.76 || ^4.1.8" - } - }, "node_modules/ajv": { "version": "8.17.1", "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", @@ -18776,6 +18574,19 @@ "integrity": "sha512-4hm4VPpIecmlg59CHXnRDnqGplJFrbLG4aFEl5vl6cK1u76ws3LLvX7ikFnTDl5vo39sjWD6AaDPYodJp/NNHg==", "license": "MIT" }, + "node_modules/complex.js": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/complex.js/-/complex.js-2.4.3.tgz", + "integrity": "sha512-UrQVSUur14tNX6tiP4y8T4w4FeJAX3bi2cIv0pu/DTLFNxoq7z2Yh83Vfzztj6Px3X/lubqQ9IrPp7Bpn6p4MQ==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/compressible": { "version": "2.0.18", "resolved": "https://registry.npmjs.org/compressible/-/compressible-2.0.18.tgz", @@ -21203,6 +21014,12 @@ "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", "license": "MIT" }, + "node_modules/escape-latex": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/escape-latex/-/escape-latex-1.2.0.tgz", + "integrity": "sha512-nV5aVWW1K0wEiUIEdZ4erkGGH8mDxGyxSeqPzRNtWP7ataw+/olFObw7hujFWlVjNsaDFw5VZ5NzVSIqRgfTiw==", + "license": "MIT" + }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -22930,6 +22747,19 @@ "integrity": "sha512-alTFZZQDKMporBH77856pXgzhEzaUVmLCDk+egLgIgHst3Tpndzz8MnKe+GzRJRfvVdn69HhpW7cmXzvtLvJAw==", "license": "MIT" }, + "node_modules/fraction.js": { + "version": "5.3.4", + "resolved": "https://registry.npmjs.org/fraction.js/-/fraction.js-5.3.4.tgz", + "integrity": "sha512-1X1NTtiJphryn/uLQz3whtY6jK3fTqoE3ohKs0tT+Ujr1W59oopxmoEh7Lu5p6vBaPbgoM0bzveAW4Qi5RyWDQ==", + "license": "MIT", + "engines": { + "node": "*" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/rawify" + } + }, "node_modules/framer-motion": { "version": "12.26.2", "resolved": "https://registry.npmjs.org/framer-motion/-/framer-motion-12.26.2.tgz", @@ -25443,6 +25273,12 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/javascript-natural-sort": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/javascript-natural-sort/-/javascript-natural-sort-0.7.1.tgz", + "integrity": "sha512-nO6jcEfZWQXDhOiBtG2KvKyEptz7RVbpGP4vTD2hLBdmNQSsCiicO2Ioinv6UI4y9ukqnBpy+XZ9H6uLNgJTlw==", + "license": "MIT" + }, "node_modules/jest-worker": { "version": "27.5.1", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-27.5.1.tgz", @@ -26885,6 +26721,29 @@ "node": ">= 0.4" } }, + "node_modules/mathjs": { + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/mathjs/-/mathjs-15.1.0.tgz", + "integrity": "sha512-HfnAcScQm9drGryodlDqeS3WAl4gUTYGDcOtcqL/8s23MZ28Ib1i8XnYK3ZdjNuaW/L4BAp9lIp8vxAMrcuu1w==", + "license": "Apache-2.0", + "dependencies": { + "@babel/runtime": "^7.26.10", + "complex.js": "^2.2.5", + "decimal.js": "^10.4.3", + "escape-latex": "^1.2.0", + "fraction.js": "^5.2.1", + "javascript-natural-sort": "^0.7.1", + "seedrandom": "^3.0.5", + "tiny-emitter": "^2.1.0", + "typed-function": "^4.2.1" + }, + "bin": { + "mathjs": "bin/cli.js" + }, + "engines": { + "node": ">= 18" + } + }, "node_modules/mdast-util-find-and-replace": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", @@ -32201,6 +32060,12 @@ ], "license": "BSD-3-Clause" }, + "node_modules/seedrandom": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/seedrandom/-/seedrandom-3.0.5.tgz", + "integrity": "sha512-8OwmbklUNzwezjGInmZ+2clQmExQPvomqjL7LFqOYqtmuxRgQYqOD3mHaU+MvZn5FLUeVxVfQjwLZW/n/JFuqg==", + "license": "MIT" + }, "node_modules/selderee": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/selderee/-/selderee-0.11.0.tgz", @@ -34251,6 +34116,12 @@ "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", "license": "MIT" }, + "node_modules/tiny-emitter": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz", + "integrity": "sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==", + "license": "MIT" + }, "node_modules/tiny-invariant": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz", @@ -35484,6 +35355,15 @@ "url": "https://opencollective.com/express" } }, + "node_modules/typed-function": { + "version": "4.2.2", + "resolved": "https://registry.npmjs.org/typed-function/-/typed-function-4.2.2.tgz", + "integrity": "sha512-VwaXim9Gp1bngi/q3do8hgttYn2uC3MoT/gfuMWylnj1IeZBUAyPddHZlo1K05BDoj8DYPpMdiHqH1dDYdJf2A==", + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, "node_modules/typedarray": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", diff --git a/package.json b/package.json index 9446bee..41ac540 100644 --- a/package.json +++ b/package.json @@ -111,7 +111,7 @@ "@vercel/otel": "^2.1.0", "@xyflow/react": "^12.10.0", "a2a-ai-provider": "^0.4.0-alpha.2", - "ai": "^5.0.121", + "ai": "^6.0.37", "ai-sdk-ollama": "^3.1.1", "ai-sdk-provider-claude-code": "^3.2.1", "ai-sdk-provider-gemini-cli": "^2.0.1", @@ -159,6 +159,7 @@ "lenis": "^1.3.17", "lucide-react": "^0.562.0", "marked": "^17.0.1", + "mathjs": "^15.1.0", "monaco-editor": "^0.55.1", "monaco-editor-webpack-plugin": "^7.1.1", "monaco-languages": "^2.11.1", @@ -251,7 +252,7 @@ }, "overrides": { "jsondiffpatch": "0.7.3", - "ai": "^5.0.121", + "ai": "^6.0.37", "morgan": "^1.10.1", "js-yaml": "^4.1.1", "multer": "^2.0.2", diff --git a/src/mastra/agents/copywriterAgent.ts b/src/mastra/agents/copywriterAgent.ts index 1355aea..9362006 100644 --- a/src/mastra/agents/copywriterAgent.ts +++ b/src/mastra/agents/copywriterAgent.ts @@ -21,6 +21,7 @@ import type { RequestContext } from '@mastra/core/request-context' // createToneScorer, //} from '../evals/scorers/prebuilt' import { chartSupervisorTool } from '../tools/financial-chart-tools' +import { convexMemory } from '../config/convex' // Define runtime context for this agent export interface CopywriterAgentContext { diff --git a/src/mastra/tools/arxiv.tool.ts b/src/mastra/tools/arxiv.tool.ts index 7ecc99f..1468faf 100644 --- a/src/mastra/tools/arxiv.tool.ts +++ b/src/mastra/tools/arxiv.tool.ts @@ -19,6 +19,16 @@ export interface ArxivRequestContext extends RequestContext { // toolCallCounters.set('tool-id', (toolCallCounters.get('tool-id') ?? 0) + 1) const toolCallCounters = new Map() +// Helper to safely detect cancellation errors without using `any` +function isErrCanceled(err: unknown): boolean { + if (err instanceof Error && err.name === 'AbortError') {return true} + if (typeof err === 'object' && err !== null && 'code' in err) { + const code = (err as { code?: unknown }).code + return code === 'ERR_CANCELED' + } + return false +} + /** * ArXiv Tool * @@ -394,13 +404,7 @@ export const arxivTool = createTool({ }) // resp already has status/statusText/data from httpFetch } catch (err: unknown) { - if ( - (err && - typeof err === 'object' && - 'code' in err && - err.code === 'ERR_CANCELED') || - (err instanceof Error && err.name === 'AbortError') - ) { + if (isErrCanceled(err)) { throw new Error('ArXiv search cancelled during API call') } throw err @@ -588,7 +592,6 @@ export const arxivPdfParserTool = createTool({ name: 'arxiv-pdf-parser', input: inputData, requestContext: context?.requestContext, - mastra: (globalThis as any).mastra, metadata: { 'tool.id': 'arxiv-pdf-parser', 'tool.input.arxivId': inputData.arxivId, @@ -652,7 +655,7 @@ export const arxivPdfParserTool = createTool({ id: 'arxiv-pdf-parser', }) - let pdfResp: any + let pdfResp: HttpFetchResponse try { pdfResp = await httpFetch(pdfUrl, { method: 'GET', @@ -660,11 +663,8 @@ export const arxivPdfParserTool = createTool({ responseType: 'arraybuffer', signal: abortSignal, }) - } catch (err: any) { - if ( - err?.code === 'ERR_CANCELED' || - (err instanceof Error && err.name === 'AbortError') - ) { + } catch (err: unknown) { + if (isErrCanceled(err)) { const cancelMessage = `ArXiv PDF parsing cancelled for ${inputData.arxivId}` await writer?.custom({ type: 'data-tool-progress', @@ -703,7 +703,26 @@ export const arxivPdfParserTool = createTool({ ) } - const pdfBuffer = Buffer.from(pdfResp.data) + let pdfBuffer: Buffer + const pdfData = pdfResp.data + + if (Buffer.isBuffer(pdfData)) { + pdfBuffer = pdfData + } else if (pdfData instanceof ArrayBuffer) { + pdfBuffer = Buffer.from(new Uint8Array(pdfData)) + } else if (ArrayBuffer.isView(pdfData)) { + // e.g., Uint8Array or other typed array / DataView + // Buffer.from does not accept the generic ArrayBufferView type in some TS configs, + // so explicitly create a Uint8Array view over the underlying ArrayBuffer slice. + const view = pdfData as ArrayBufferView & { byteOffset?: number; byteLength?: number } + const offset = view.byteOffset ?? 0 + const length = view.byteLength ?? view.buffer.byteLength - offset + pdfBuffer = Buffer.from(view.buffer.slice(offset, offset + length)) + } else if (typeof pdfData === 'string') { + pdfBuffer = Buffer.from(pdfData, 'utf-8') + } else { + throw new Error('Unsupported PDF response data type') + } // Extract text from PDF const pdfContent = await extractPdfText( @@ -718,7 +737,7 @@ export const arxivPdfParserTool = createTool({ if (inputData.includeMetadata) { try { const apiUrl = `http://export.arxiv.org/api/query?id_list=${inputData.arxivId}&max_results=1` - let apiResp: any + let apiResp: HttpFetchResponse | undefined try { apiResp = await httpFetch(apiUrl, { method: 'GET', @@ -727,14 +746,7 @@ export const arxivPdfParserTool = createTool({ timeout: 30000, }) } catch (err: unknown) { - if ( - err && - typeof err === 'object' && - (('code' in err && - (err as any).code === 'ERR_CANCELED') || - (err instanceof Error && - err.name === 'AbortError')) - ) { + if (isErrCanceled(err)) { // metadata fetch aborted - continue without it apiResp = undefined } else { @@ -780,7 +792,7 @@ export const arxivPdfParserTool = createTool({ } } } - } catch { + } catch (err) { // Metadata fetch failed, continue without it } } @@ -1030,7 +1042,7 @@ export const arxivPaperDownloaderTool = createTool({ name: 'arxiv-paper-downloader', input: inputData, requestContext: context?.requestContext, - mastra: (globalThis as any).mastra, + metadata: { 'tool.id': 'arxiv-paper-downloader', 'tool.input.arxivId': inputData.arxivId, @@ -1080,11 +1092,14 @@ export const arxivPaperDownloaderTool = createTool({ signal: abortSignal, timeout: 30000, }) - } catch (err: any) { - if ( - err?.code === 'ERR_CANCELED' || + } catch (err: unknown) { + const isCanceled = + (err && + typeof err === 'object' && + 'code' in err && + (err as any).code === 'ERR_CANCELED') ?? (err instanceof Error && err.name === 'AbortError') - ) { + if (isCanceled) { throw new Error( `ArXiv metadata fetch cancelled for ${inputData.arxivId}` ) diff --git a/src/mastra/tools/calculator.tool.ts b/src/mastra/tools/calculator.tool.ts index 55316a2..3baec23 100644 --- a/src/mastra/tools/calculator.tool.ts +++ b/src/mastra/tools/calculator.tool.ts @@ -4,6 +4,7 @@ import type { RequestContext } from '@mastra/core/request-context' import type { InferUITool } from '@mastra/core/tools' import { createTool } from '@mastra/core/tools' import { z } from 'zod' +import { evaluate } from 'mathjs' import { log } from '../config/logger' type UserTier = 'free' | 'pro' | 'enterprise' @@ -139,28 +140,11 @@ function evaluateExpression( expression: string, variables: Record = {} ): number { - // Remove whitespace - const cleanExpr = expression.replace(/\s+/g, '') - - // Basic security checks - if (/[^0-9+\-*/().\s,a-zA-Z_]/.test(cleanExpr)) { - throw new Error('Invalid characters in expression') - } - - if ( - cleanExpr.includes('__proto__') || - cleanExpr.includes('prototype') || - cleanExpr.includes('constructor') - ) { - throw new Error('Potentially unsafe expression') - } - // Create function with safe context and variables const context = { ...createSafeContext(), ...variables } - const func = new Function(...Object.keys(context), `return ${cleanExpr}`) try { - const result = func(...Object.values(context)) + const result = evaluate(expression, context) if (typeof result !== 'number' || !isFinite(result)) { throw new Error('Expression did not evaluate to a valid number') } @@ -219,7 +203,7 @@ function convertTemperature( celsius = value - 273.15 break default: - throw new Error(`Unsupported temperature unit: ${from}`) + throw new Error(`Unsupported temperature unit: ${String(from)}`) } // Convert from Celsius to target @@ -231,7 +215,7 @@ function convertTemperature( case 'kelvin': return celsius + 273.15 default: - throw new Error(`Unsupported temperature unit: ${to}`) + throw new Error(`Unsupported temperature unit: ${String(to)}`) } } @@ -255,9 +239,9 @@ const MATRIX_OPERATIONS = { if (a[0].length !== b.length) { throw new Error('Matrix dimensions incompatible for multiplication') } - const result = Array(a.length) - .fill(0) - .map(() => Array(b[0].length).fill(0)) + const result: number[][] = Array.from({ length: a.length }, () => + Array.from({ length: b[0].length }, () => 0) + ) for (let i = 0; i < a.length; i++) { for (let j = 0; j < b[0].length; j++) { for (let k = 0; k < b.length; k++) { diff --git a/src/mastra/tools/calendar-tool.ts b/src/mastra/tools/calendar-tool.ts index 42f8967..626efd3 100644 --- a/src/mastra/tools/calendar-tool.ts +++ b/src/mastra/tools/calendar-tool.ts @@ -163,15 +163,6 @@ export const listEvents = createTool({ hook: 'onInputAvailable', }) }, - onOutput: ({ output, toolCallId, toolName, abortSignal }) => { - log.info('Calendar list events completed', { - toolCallId, - toolName, - eventsFound: output.count ?? 0, - abortSignal: abortSignal?.aborted, - hook: 'onOutput', - }) - }, execute: async (inputData, context) => { const requestCtx = context?.requestContext as | CalendarRequestContext @@ -186,7 +177,7 @@ export const listEvents = createTool({ 'user.id': requestCtx?.userId, }, requestContext: context?.requestContext, - mastra: (globalThis as any).mastra, + tracingContext: context?.tracingContext, }) log.debug('Executing calendar list events for user', { @@ -248,6 +239,15 @@ export const listEvents = createTool({ return { content: `Error: ${errorMsg}` } } }, + onOutput: ({ output, toolCallId, toolName, abortSignal }) => { + log.info('Calendar list events completed', { + toolCallId, + toolName, + eventsFound: output.count ?? 0, + abortSignal: abortSignal?.aborted, + hook: 'onOutput', + }) + }, }) export const getTodayEvents = createTool({ @@ -279,7 +279,6 @@ export const getTodayEvents = createTool({ 'user.id': requestCtx?.userId, }, requestContext: context?.requestContext, - mastra: (globalThis as any).mastra, }) log.debug('Executing get today events for user', { @@ -398,6 +397,7 @@ export const getUpcomingEvents = createTool({ ), count: z.number(), }), + execute: async (inputData, context) => { const requestCtx = context?.requestContext as | CalendarRequestContext @@ -413,7 +413,6 @@ export const getUpcomingEvents = createTool({ 'user.id': requestCtx?.userId, }, requestContext: context?.requestContext, - mastra: (globalThis as any).mastra, }) log.debug('Executing get upcoming events for user', { @@ -481,33 +480,6 @@ export const getUpcomingEvents = createTool({ return { events: [], count: 0 } } }, - onInputStart: ({ toolCallId, messages, abortSignal }) => { - log.info('Upcoming events tool input streaming started', { - toolCallId, - messageCount: messages.length, - abortSignal: abortSignal?.aborted, - hook: 'onInputStart', - }) - }, - onInputDelta: ({ inputTextDelta, toolCallId, messages, abortSignal }) => { - log.info('Upcoming events tool received input chunk', { - toolCallId, - inputTextDelta, - messageCount: messages.length, - abortSignal: abortSignal?.aborted, - hook: 'onInputDelta', - }) - }, - onInputAvailable: ({ input, toolCallId, messages, abortSignal }) => { - log.info('Upcoming events tool received input', { - toolCallId, - messageCount: messages.length, - days: input.days, - limit: input.limit, - abortSignal: abortSignal?.aborted, - hook: 'onInputAvailable', - }) - }, onOutput: ({ output, toolCallId, toolName, abortSignal }) => { log.info('Upcoming events tool completed', { toolCallId, @@ -570,7 +542,6 @@ export const findFreeSlots = createTool({ 'user.id': requestCtx?.userId, }, requestContext: context?.requestContext, - mastra: (globalThis as any).mastra, }) log.debug('Executing find free slots for user', { diff --git a/src/mastra/tools/chartjs.tool.ts b/src/mastra/tools/chartjs.tool.ts index 868bea1..78e7223 100644 --- a/src/mastra/tools/chartjs.tool.ts +++ b/src/mastra/tools/chartjs.tool.ts @@ -159,24 +159,22 @@ export const chartJsTool = createTool({ try { // Calculate indicators for (const ind of indicators) { - let indicatorData: number[] = [] + let indicatorData: Array = [] let label = '' const color = ind.color ?? '#ff0000' try { switch (ind.type) { - case 'SMA': - indicatorData = SMA.calculate({ + case 'SMA': { + const sma = (SMA.calculate({ period: ind.period, values: closePrices, - }) + })) ?? [] label = `SMA (${ind.period})` // Pad beginning with nulls to match length - indicatorData = Array( - closePrices.length - indicatorData.length - ) - .fill(null) - .concat(indicatorData) + indicatorData = (Array( + closePrices.length - sma.length + ).fill(null) as Array).concat(sma) datasets.push({ label, data: indicatorData, @@ -187,17 +185,14 @@ export const chartJsTool = createTool({ yAxisID: 'y', }) break - case 'EMA': - indicatorData = EMA.calculate({ + } + case 'EMA': { + const ema = EMA.calculate({ period: ind.period, values: closePrices, - }) + }) ?? [] label = `EMA (${ind.period})` - indicatorData = Array( - closePrices.length - indicatorData.length - ) - .fill(null) - .concat(indicatorData) + indicatorData = (Array(closePrices.length - ema.length).fill(null) as Array).concat(ema) datasets.push({ label, data: indicatorData, @@ -208,17 +203,18 @@ export const chartJsTool = createTool({ yAxisID: 'y', }) break - case 'RSI': - indicatorData = RSI.calculate({ + } + case 'RSI': { + const rsiValues = (RSI.calculate({ period: ind.period, values: closePrices, - }) + }) ?? []) label = `RSI (${ind.period})` indicatorData = Array( - closePrices.length - indicatorData.length + closePrices.length - rsiValues.length ) .fill(null) - .concat(indicatorData) + .concat(rsiValues) as Array datasets.push({ label, data: indicatorData, @@ -230,21 +226,31 @@ export const chartJsTool = createTool({ type: 'line', }) break + } case 'MACD': { - const macdResult = MACD.calculate({ + const macdResult = (MACD.calculate({ fastPeriod: ind.fastPeriod ?? 12, slowPeriod: ind.slowPeriod ?? 26, signalPeriod: ind.signalPeriod ?? 9, values: closePrices, SimpleMAOscillator: false, SimpleMASignal: false, - }) - const macd = macdResult.map((m) => m.MACD) - const signal = macdResult.map((m) => m.signal) - const histogram = macdResult.map((m) => m.histogram) + }) ?? []) as Array> + + // Ensure the mapped series are typed as numbers to avoid `any[]` assignments + const macd = macdResult.map((m: Record) => + Number((m.MACD) as number) + ) + const signal = macdResult.map((m: Record) => + Number((m.signal) as number) + ) + const histogram = macdResult.map((m: Record) => + Number((m.histogram) as number) + ) + const padding = Array( closePrices.length - macd.length - ).fill(null) + ).fill(null) as Array datasets.push({ label: 'MACD', @@ -275,15 +281,20 @@ export const chartJsTool = createTool({ } case 'BollingerBands': { - const bb = BollingerBands.calculate({ + const bb = (BollingerBands.calculate({ period: ind.period, stdDev: ind.stdDev ?? 2, values: closePrices, - }) - const upper = bb.map((b) => b.upper) - const middle = bb.map((b) => b.middle) - const lower = bb.map((b) => b.lower) - const padding = Array( + }) ?? []) as Array<{ + upper: number + middle: number + lower: number + }> + + const upper: number[] = bb.map((b) => Number(b.upper)) + const middle: number[] = bb.map((b) => Number(b.middle)) + const lower: number[] = bb.map((b) => Number(b.lower)) + const padding: Array = new Array( closePrices.length - bb.length ).fill(null) diff --git a/src/mastra/tools/code-analysis.tool.ts b/src/mastra/tools/code-analysis.tool.ts index 5834c26..1bc8f4b 100644 --- a/src/mastra/tools/code-analysis.tool.ts +++ b/src/mastra/tools/code-analysis.tool.ts @@ -452,7 +452,7 @@ Use for code review preparation, quality assessment, and refactoring planning.`, : inputData.target, }, requestContext: context?.requestContext, - mastra: (globalThis as any).mastra, + tracingContext: context?.tracingContext, }) // Create child span for code analysis operation diff --git a/src/mastra/tools/code-chunking.ts b/src/mastra/tools/code-chunking.ts index 46002f0..d962675 100644 --- a/src/mastra/tools/code-chunking.ts +++ b/src/mastra/tools/code-chunking.ts @@ -202,8 +202,9 @@ export const codeChunkerTool = createTool({ return { chunks } } } catch (e) { + const errMsg: string = e instanceof Error ? e.message : String(e) log.warn( - `Python parsing failed for ${filePath}, falling back to text chunking: ${e}` + `Python parsing failed for ${filePath}, falling back to text chunking: ${errMsg}` ) } } diff --git a/src/mastra/tools/code-search.tool.ts b/src/mastra/tools/code-search.tool.ts index 3e273b8..dd2dee4 100644 --- a/src/mastra/tools/code-search.tool.ts +++ b/src/mastra/tools/code-search.tool.ts @@ -156,7 +156,7 @@ Use for finding usages, identifying patterns, and code exploration.`, 'tool.input.pattern': input.pattern, }, requestContext: context?.requestContext, - mastra: (globalThis as any).mastra, + tracingContext: context?.tracingContext, }) // Create child span for search operation diff --git a/src/mastra/tools/technical-analysis.tool.ts b/src/mastra/tools/technical-analysis.tool.ts index c1985c5..ee76dca 100644 --- a/src/mastra/tools/technical-analysis.tool.ts +++ b/src/mastra/tools/technical-analysis.tool.ts @@ -535,7 +535,14 @@ export const pivotPointsTool = createTool({ try { const { high, low, close } = inputData const pp = (high + low + close) / 3 - const results: any = { success: true } + const results: { + success: true + standard?: StandardPivot + woodie?: WoodiePivot + camarilla?: CamarillaPivot + fibonacci?: FibonacciPivot + message?: string + } = { success: true } results.standard = { pp, @@ -1556,7 +1563,7 @@ export const heikinAshiTool = createTool({ try { const candlesRaw = heikinashi(inputData) const candles = Array.isArray(candlesRaw) - ? candlesRaw.map((c: any) => ({ + ? (candlesRaw as HeikinAshiOutput[]).map((c) => ({ open: Number(c.open), high: Number(c.high), low: Number(c.low), diff --git a/src/mastra/tools/text-analysis.tool.ts b/src/mastra/tools/text-analysis.tool.ts index 69a4382..2f4476a 100644 --- a/src/mastra/tools/text-analysis.tool.ts +++ b/src/mastra/tools/text-analysis.tool.ts @@ -85,7 +85,7 @@ export const textAnalysisTool = createTool({ 'user.id': requestCtx?.userId, }, requestContext: context?.requestContext, - mastra: (globalThis as any).mastra, + tracingContext: context?.tracingContext, }) // Create child span for text analysis @@ -324,7 +324,7 @@ export const textProcessingTool = createTool({ 'user.id': requestCtx?.userId, }, requestContext: context?.requestContext, - mastra: (globalThis as any).mastra, + tracingContext: context?.tracingContext, }) // Create child span for text processing @@ -389,7 +389,7 @@ export const textProcessingTool = createTool({ extracted[operation] = [processedText.length.toString()] break default: - throw new Error(`Unknown operation: ${operation}`) + throw new Error(`Unknown operation: `) } } diff --git a/src/mastra/tools/weather-tool.ts b/src/mastra/tools/weather-tool.ts index 59cb20f..827f922 100644 --- a/src/mastra/tools/weather-tool.ts +++ b/src/mastra/tools/weather-tool.ts @@ -125,8 +125,7 @@ export const weatherTool = createTool({ 'workspace.id': workspaceId, }, requestContext: context?.requestContext, - // eslint-disable-next-line object-shorthand - tracingContext: tracingContext, + tracingContext: context?.tracingContext, }) // Create child span for weather lookup operation diff --git a/src/mastra/tools/web-scraper-tool.ts b/src/mastra/tools/web-scraper-tool.ts index 7c9f511..f7ee19a 100644 --- a/src/mastra/tools/web-scraper-tool.ts +++ b/src/mastra/tools/web-scraper-tool.ts @@ -18,7 +18,7 @@ import type { TracingContext } from '@mastra/core/observability' import type { InferUITool } from '@mastra/core/tools' import { createTool } from '@mastra/core/tools' import * as cheerio from 'cheerio' -import { CheerioCrawler, Request } from 'crawlee' +import { CheerioCrawler, enqueueLinks, Request } from 'crawlee' import type { CheerioCrawlingContext, RequestOptions } from 'crawlee' import { JSDOM } from 'jsdom' @@ -26,19 +26,29 @@ import { JSDOM } from 'jsdom' function createJSDOM( html: string, options?: { contentType?: string; includeNodeLocations?: boolean } -) { +): JSDOM { try { - const Ctor = JSDOM as unknown as new (html: string, opts?: any) => any + // Prefer constructor shape that returns a JSDOM instance + const Ctor = JSDOM as unknown as new ( + html: string, + opts?: { contentType?: string; includeNodeLocations?: boolean } + ) => JSDOM return new Ctor(html, options) } catch (e) { - // Fallback to calling as factory - const Factory = JSDOM as unknown as (html: string, opts?: any) => any - return Factory(html, options) + // Fallback to factory shape that returns a JSDOM instance + // Be explicit about the return type to avoid unsafe `any` returns reported by the linter. + const Factory = JSDOM as unknown as ( + html: string, + opts?: { contentType?: string; includeNodeLocations?: boolean } + ) => unknown + const instance = Factory(html, options) as JSDOM + return instance } } import { marked } from 'marked' import * as fs from 'node:fs/promises' import * as path from 'node:path' +import { inspect } from 'node:util' import RE2 from 're2' import { z } from 'zod' import { log } from '../config/logger' @@ -848,7 +858,7 @@ export const webScraperTool = createTool({ requestHandlerTimeoutSecs: (inputData.request?.timeout ?? 30000) / 1000, async requestHandler(ctx: CheerioCrawlingContext) { - const { request, body, response, enqueueLinks } = ctx + const { request, body, response } = ctx try { scrapedUrl = request.url @@ -885,9 +895,12 @@ export const webScraperTool = createTool({ rawContent = HtmlProcessor.sanitizeHtml(rawContent) if (followLinks) { + const safeRequest = request as Request & { + userData?: Record + } const currentDepth = - typeof request.userData?.depth === 'number' - ? request.userData.depth + typeof safeRequest.userData?.depth === 'number' + ? safeRequest.userData.depth : 0 if (currentDepth < maxDepth) { @@ -906,39 +919,100 @@ export const webScraperTool = createTool({ }) try { if (typeof enqueueLinks === 'function') { - await enqueueLinks({ - selector: 'a[href]', - baseUrl: request.url, - strategy: 'same-hostname', - limit: Math.max( - 1, - Math.min(maxPages, 100) - ), + // Define the expected type for enqueueLinks to avoid 'any' + interface EnqueueLinksOptions { + requestQueue: typeof ctx.requestQueue + urls: string[] + transformRequestFunction: ( + original: unknown + ) => RequestOptions | null + } + type EnqueueLinksFn = ( + options: EnqueueLinksOptions + ) => Promise + + // Collect links manually using cheerio + const $ = cheerio.load(rawContent) + const links = $('a[href]') + .toArray() + .map((el) => $(el).attr('href')) + .filter((href) => href) as string[] + const absoluteLinks = links + .map((href) => { + try { + return new URL( + href, + request.url + ).href + } catch { + return null + } + }) + .filter( + (href) => + href && + ValidationUtils.isUrlAllowed( + href, + allowedDomains + ) + ) as string[] + const limitedLinks = + absoluteLinks.slice( + 0, + Math.max( + 1, + Math.min(maxPages, 100) + ) + ) + + const enqueueFn = + enqueueLinks as unknown as EnqueueLinksFn + await enqueueFn({ + requestQueue: ctx.requestQueue, + urls: limitedLinks, transformRequestFunction: ( original: unknown ): RequestOptions | null => { - // Accept the original request shape from Crawlee and return a RequestOptions object. - // Returning a Request instance can conflict with some Crawlee versions due to private members. const nextDepth = currentDepth + 1 - // Define a safe shape for the original request to avoid the use of `any` - interface TransformRequest { - [k: string]: unknown - url?: string - userData?: Record< - string, - unknown - > - headers?: Record< - string, - string - > + let nextUrl: string + let origHeaders: Record< + string, + string + > = {} + let origUserData: Record< + string, + unknown + > = {} + + if ( + typeof original === 'string' + ) { + nextUrl = original + } else { + // Define a safe shape for the original request to avoid the use of `any` + interface TransformRequest { + [k: string]: unknown + url?: string + userData?: Record< + string, + unknown + > + headers?: Record< + string, + string + > + } + const orig = + original as TransformRequest + nextUrl = orig?.url ?? '' + origHeaders = + orig.headers ?? {} + origUserData = + orig.userData ?? {} } - const orig = - original as TransformRequest - const nextUrl = orig?.url if ( typeof nextUrl !== 'string' || @@ -955,13 +1029,11 @@ export const webScraperTool = createTool({ { url: nextUrl, headers: { - ...(orig.headers ?? - {}), + ...origHeaders, ...headers, }, userData: { - ...(orig.userData ?? - {}), + ...origUserData, depth: nextDepth, }, } @@ -1117,7 +1189,7 @@ export const webScraperTool = createTool({ ) jsonLdScripts.forEach((script) => { try { - const data = JSON.parse( + const data: unknown = JSON.parse( script.textContent || '' ) structuredData.push(data) @@ -1791,7 +1863,7 @@ export const batchWebScraperTool = createTool({ const crawler = createCheerioCrawler({ maxRequestsPerCrawl: 1, requestHandlerTimeoutSecs: 20, - async requestHandler({ + requestHandler({ body, }: { body?: Buffer | string @@ -1850,13 +1922,18 @@ export const batchWebScraperTool = createTool({ }, failedRequestHandler(ctx: CheerioCrawlingContext) { const { request, error } = ctx + let errorMessage: string + if (error instanceof Error) { + errorMessage = error.message + } else if (typeof error === 'string') { + errorMessage = error + } else { + errorMessage = 'Unknown error' + } results.push({ url: request?.url ?? url, success: false, - errorMessage: - error instanceof Error - ? error.message - : String(error ?? 'Unknown error'), + errorMessage, }) }, }) @@ -2480,7 +2557,7 @@ export const linkExtractorTool = createTool({ maxRequestsPerCrawl: 10, maxConcurrency: 10, requestHandlerTimeoutSecs: 20, - async requestHandler({ + requestHandler({ request, body, }: { @@ -3819,7 +3896,20 @@ export const dataExporterTool = createTool({ for (const item of inputData.data) { xmlContent += ' \n' for (const [key, value] of Object.entries(item)) { - xmlContent += ` <${key}>${String(value ?? '').replace(/[<>&"]/g, (c) => ({ '<': '<', '>': '>', '&': '&', '"': '"' })[c] ?? c)}\n` + let valueStr: string + if (value === null || value === undefined) { + valueStr = '' + } else if (typeof value === 'string') { + valueStr = value + } else if ( + typeof value === 'number' || + typeof value === 'boolean' + ) { + valueStr = String(value) + } else { + valueStr = JSON.stringify(value) + } + xmlContent += ` <${key}>${valueStr.replace(/[<>&"]/g, (c) => ({ '<': '<', '>': '>', '&': '&', '"': '"' })[c] ?? c)}\n` } xmlContent += ' \n' } @@ -3838,11 +3928,26 @@ export const dataExporterTool = createTool({ message = `Database export to ${inputData.destination} not implemented (placeholder)` break - default: + default: { + const rawFormat = (inputData as { format?: unknown }).format + let formatValue: string + if (typeof rawFormat === 'string') { + formatValue = rawFormat + } else if (rawFormat === undefined) { + formatValue = 'unknown' + } else { + try { + formatValue = JSON.stringify(rawFormat) + } catch { + // Fallback to a safe string if JSON.stringify fails (e.g., circular structures) + formatValue = 'invalid format value' + } + } throw new ScrapingError( - `Unsupported format: ${inputData.format}`, + 'Unsupported format: ' + formatValue, 'UNSUPPORTED_FORMAT' ) + } } exportSpan?.update({