diff --git a/.gitignore b/.gitignore index 6ee4ea0f5a5..e0bc892d767 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ tilt_config.json hubspot.config.yml AGENTS.md CLAUDE.md +lefthook.yml .claude diff --git a/packages/cli/package.json b/packages/cli/package.json index 87c6d0dc37d..18f4ece41f9 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "@botpress/cli", - "version": "6.2.5", + "version": "6.2.6", "description": "Botpress CLI", "scripts": { "build": "pnpm run build:types && pnpm run bundle && pnpm run template:gen", @@ -28,7 +28,7 @@ "@apidevtools/json-schema-ref-parser": "^11.7.0", "@botpress/chat": "0.5.5", "@botpress/client": "1.38.2", - "@botpress/sdk": "6.4.1", + "@botpress/sdk": "6.4.2", "@bpinternal/const": "^0.1.0", "@bpinternal/tunnel": "^0.1.1", "@bpinternal/verel": "^0.2.0", diff --git a/packages/cli/src/utils/require-utils.ts b/packages/cli/src/utils/require-utils.ts index dba7acdd1b3..f2ff1251b88 100644 --- a/packages/cli/src/utils/require-utils.ts +++ b/packages/cli/src/utils/require-utils.ts @@ -1,3 +1,4 @@ +import * as sdk from '@botpress/sdk' import Module from 'module' import pathlib from 'path' @@ -20,6 +21,12 @@ export const requireJsCode = (code: string): T => { return m.exports } catch (thrown: unknown) { const error = thrown instanceof Error ? thrown : new Error(`${thrown}`) + // sdk.errors.isDefinitionError() handles cross-bundle detection: esbuild inlines a separate + // copy of @botpress/sdk into the compiled definition artifact, so instanceof alone is + // unreliable. See the isDefinitionError() docstring in the SDK for the full explanation. + if (sdk.errors.isDefinitionError(thrown)) { + throw error + } throw _injectStackTrace(error, code, filename) } } diff --git a/packages/cli/templates/empty-bot/package.json b/packages/cli/templates/empty-bot/package.json index 1093408fdba..4e43060fbae 100644 --- a/packages/cli/templates/empty-bot/package.json +++ b/packages/cli/templates/empty-bot/package.json @@ -6,7 +6,7 @@ "private": true, "dependencies": { "@botpress/client": "1.38.2", - "@botpress/sdk": "6.4.1" + "@botpress/sdk": "6.4.2" }, "devDependencies": { "@types/node": "^22.16.4", diff --git a/packages/cli/templates/empty-integration/package.json b/packages/cli/templates/empty-integration/package.json index 17c974337b2..e44b3e1d11d 100644 --- a/packages/cli/templates/empty-integration/package.json +++ b/packages/cli/templates/empty-integration/package.json @@ -7,7 +7,7 @@ "private": true, "dependencies": { "@botpress/client": "1.38.2", - "@botpress/sdk": "6.4.1" + "@botpress/sdk": "6.4.2" }, "devDependencies": { "@types/node": "^22.16.4", diff --git a/packages/cli/templates/empty-plugin/package.json b/packages/cli/templates/empty-plugin/package.json index 0d19f6fa2bf..940124243c1 100644 --- a/packages/cli/templates/empty-plugin/package.json +++ b/packages/cli/templates/empty-plugin/package.json @@ -6,7 +6,7 @@ }, "private": true, "dependencies": { - "@botpress/sdk": "6.4.1" + "@botpress/sdk": "6.4.2" }, "devDependencies": { "@types/node": "^22.16.4", diff --git a/packages/cli/templates/hello-world/package.json b/packages/cli/templates/hello-world/package.json index ff1fd2b5b87..2731e770fe7 100644 --- a/packages/cli/templates/hello-world/package.json +++ b/packages/cli/templates/hello-world/package.json @@ -7,7 +7,7 @@ "private": true, "dependencies": { "@botpress/client": "1.38.2", - "@botpress/sdk": "6.4.1" + "@botpress/sdk": "6.4.2" }, "devDependencies": { "@types/node": "^22.16.4", diff --git a/packages/cli/templates/webhook-message/package.json b/packages/cli/templates/webhook-message/package.json index 629883658a1..641374dc417 100644 --- a/packages/cli/templates/webhook-message/package.json +++ b/packages/cli/templates/webhook-message/package.json @@ -7,7 +7,7 @@ "private": true, "dependencies": { "@botpress/client": "1.38.2", - "@botpress/sdk": "6.4.1", + "@botpress/sdk": "6.4.2", "axios": "^1.6.8" }, "devDependencies": { diff --git a/packages/cognitive/package.json b/packages/cognitive/package.json index de5d22393c5..da09ef75793 100644 --- a/packages/cognitive/package.json +++ b/packages/cognitive/package.json @@ -1,6 +1,6 @@ { "name": "@botpress/cognitive", - "version": "0.4.5", + "version": "0.4.6", "description": "Wrapper around the Botpress Client to call LLMs", "main": "./dist/index.cjs", "module": "./dist/index.mjs", diff --git a/packages/llmz/package.json b/packages/llmz/package.json index ce21d88fd8e..ff60f3922e8 100644 --- a/packages/llmz/package.json +++ b/packages/llmz/package.json @@ -2,7 +2,7 @@ "name": "llmz", "type": "module", "description": "LLMz - An LLM-native Typescript VM built on top of Zui", - "version": "0.0.63", + "version": "0.0.64", "types": "./dist/index.d.ts", "main": "./dist/index.cjs", "module": "./dist/index.js", @@ -72,7 +72,7 @@ }, "peerDependencies": { "@botpress/client": "1.38.2", - "@botpress/cognitive": "0.4.5", + "@botpress/cognitive": "0.4.6", "@bpinternal/thicktoken": "^2.0.0", "@bpinternal/zui": "^2.1.1" }, diff --git a/packages/sdk/package.json b/packages/sdk/package.json index 478671a01bb..d77120600e5 100644 --- a/packages/sdk/package.json +++ b/packages/sdk/package.json @@ -1,6 +1,6 @@ { "name": "@botpress/sdk", - "version": "6.4.1", + "version": "6.4.2", "description": "Botpress SDK", "main": "./dist/index.cjs", "module": "./dist/index.mjs", diff --git a/packages/sdk/src/bot/definition.ts b/packages/sdk/src/bot/definition.ts index e53c52ab89d..b38665176c6 100644 --- a/packages/sdk/src/bot/definition.ts +++ b/packages/sdk/src/bot/definition.ts @@ -1,6 +1,7 @@ import { Table } from '@botpress/client' import { SchemaTransformOptions } from '../common/types' import * as consts from '../consts' +import { DefinitionError } from '../errors' import { IntegrationPackage, PluginPackage } from '../package' import { PluginInterfaceExtension, PluginIntegrationExtension } from '../plugin' import { SchemaDefinition } from '../schema' @@ -272,7 +273,7 @@ export class BotDefinition< const integrationAlias = config?.alias ?? integrationPkg.name if (self.integrations[integrationAlias]) { - throw new Error(`Another integration with alias "${integrationAlias}" is already installed in the bot`) + throw new DefinitionError(`Another integration with alias "${integrationAlias}" is already installed in the bot`) } self.integrations[integrationAlias] = { @@ -295,7 +296,7 @@ export class BotDefinition< const pluginAlias = config.alias ?? pluginPkg.name if (self.plugins[pluginAlias]) { - throw new Error(`Another plugin with alias "${pluginAlias}" is already installed in the bot`) + throw new DefinitionError(`Another plugin with alias "${pluginAlias}" is already installed in the bot`) } // Resolve backing integrations for plugin interfaces: const interfaces: Record = Object.fromEntries( @@ -307,7 +308,7 @@ export class BotDefinition< if (!integrationInstance) { const availableIntegrations = Object.keys(this.integrations ?? {}).join(', ') || '(none)' - throw new Error( + throw new DefinitionError( `Interface with alias "${pluginIfaceAlias}" of plugin with alias "${pluginAlias}" ` + `references integration with alias "${pluginIfaceConfig.integrationAlias}" which is not installed. ` + 'Please make sure to add the integration via addIntegration() before calling addPlugin().\n' + @@ -322,7 +323,7 @@ export class BotDefinition< const availableInterfaces = Object.keys(integrationInstance.definition.interfaces ?? {}).join(', ') || '(none)' - throw new Error( + throw new DefinitionError( `Interface with alias "${pluginIfaceConfig.integrationInterfaceAlias}" does not exist in integration ` + `"${integrationInstance.name}" referenced by interface with alias "${pluginIfaceAlias}" of plugin ` + `with alias "${pluginAlias}".\nAvailable interface aliases: ${availableInterfaces}` @@ -562,7 +563,7 @@ export class BotDefinition< const backingIntegration = this.integrations?.[pluginInterfaceExtension.integrationAlias] if (!backingIntegration) { - throw new Error( + throw new DefinitionError( `Interface with alias "${interfaceAlias}" of plugin with alias "${pluginAlias}" references integration "${pluginInterfaceExtension.name}" which is not installed` ) } diff --git a/packages/sdk/src/errors.ts b/packages/sdk/src/errors.ts new file mode 100644 index 00000000000..b6655263359 --- /dev/null +++ b/packages/sdk/src/errors.ts @@ -0,0 +1,27 @@ +/** + * Thrown by SDK definition classes (BotDefinition, IntegrationDefinition, PluginDefinition, etc.) + * for intentional validation errors whose message is already clear. + * + * The CLI detects this class to suppress the "Offending code:" section that it normally + * appends to errors thrown during definition file evaluation. + */ +export class DefinitionError extends Error { + /** @internal Marker used by {@link isDefinitionError} to survive esbuild bundle boundaries. */ + public readonly __IS_SDK_ERROR__ = true as const + public readonly type = 'definition_error' as const +} + +/** + * Type guard to detect {@link DefinitionError} across esbuild bundle boundaries. + * + * Tries `instanceof` first (same bundle), then falls back to the `__IS_SDK_ERROR__` marker + * and `type` field for cross-bundle cases where esbuild inlines a separate copy of the SDK class. + */ +export const isDefinitionError = (error: unknown): error is DefinitionError => + error instanceof DefinitionError || + (typeof error === 'object' && + error !== null && + '__IS_SDK_ERROR__' in error && + error.__IS_SDK_ERROR__ === true && + 'type' in error && + error.type === 'definition_error') diff --git a/packages/sdk/src/index.ts b/packages/sdk/src/index.ts index 0ca8f0af4a9..4c7325473c2 100644 --- a/packages/sdk/src/index.ts +++ b/packages/sdk/src/index.ts @@ -83,6 +83,8 @@ export { export * as version from './version-utils' +export * as errors from './errors' + export { // IntegrationPackage, diff --git a/packages/sdk/src/integration/definition/index.ts b/packages/sdk/src/integration/definition/index.ts index 42aa01ba2f2..b7f4f9c4c69 100644 --- a/packages/sdk/src/integration/definition/index.ts +++ b/packages/sdk/src/integration/definition/index.ts @@ -1,5 +1,6 @@ import type * as esbuild from 'esbuild' import { SchemaTransformOptions } from '../../common/types' +import { DefinitionError } from '../../errors' import { resolveInterface } from '../../interface/resolve' import { InterfacePackage } from '../../package' import * as utils from '../../utils' @@ -316,7 +317,7 @@ export class IntegrationDefinition< const unbrandedEntity = utils.records.pairs(extensionBuilderOutput.entities).find(([_k, e]) => !isBranded(e)) if (unbrandedEntity) { // this means the user tried providing a plain schema without referencing an entity from the integration - throw new Error( + throw new DefinitionError( `Cannot extend interface "${interfacePkg.name}" with entity "${unbrandedEntity[0]}"; the provided schema is not part of the integration's entities.` ) } diff --git a/packages/sdk/src/plugin/definition.ts b/packages/sdk/src/plugin/definition.ts index 9975784d923..7c5be58d924 100644 --- a/packages/sdk/src/plugin/definition.ts +++ b/packages/sdk/src/plugin/definition.ts @@ -10,6 +10,7 @@ import { WorkflowDefinition, } from '../bot/definition' import { SchemaTransformOptions } from '../common/types' +import { DefinitionError } from '../errors' import { IntegrationPackage, InterfacePackage } from '../package' import * as typeUtils from '../utils/type-utils' import { SDK_VERSION } from '../version' @@ -254,7 +255,7 @@ export class PluginDefinition< for (const alias of [...Object.keys(props.integrations ?? {}), ...Object.keys(props.interfaces ?? {})]) { if (aliases.has(alias)) { - throw new Error( + throw new DefinitionError( `Duplicate interface or integration alias detected in plugin definition: '${alias}'. ` + 'Please use unique aliases for each interface and integration.' ) diff --git a/packages/vai/package.json b/packages/vai/package.json index be11cef47f5..edf3b0ee560 100644 --- a/packages/vai/package.json +++ b/packages/vai/package.json @@ -1,6 +1,6 @@ { "name": "@botpress/vai", - "version": "0.0.23", + "version": "0.0.24", "description": "Vitest AI (vai) – a vitest extension for testing with LLMs", "types": "./dist/index.d.ts", "exports": { diff --git a/packages/zai/package.json b/packages/zai/package.json index ebc305f7271..4fb14d2f911 100644 --- a/packages/zai/package.json +++ b/packages/zai/package.json @@ -1,7 +1,7 @@ { "name": "@botpress/zai", "description": "Zui AI (zai) – An LLM utility library written on top of Zui and the Botpress API", - "version": "2.6.10", + "version": "2.6.11", "main": "./dist/index.js", "types": "./dist/index.d.ts", "exports": { @@ -32,7 +32,7 @@ "author": "", "license": "ISC", "dependencies": { - "@botpress/cognitive": "0.4.5", + "@botpress/cognitive": "0.4.6", "json5": "^2.2.3", "jsonrepair": "^3.10.0", "lodash-es": "^4.17.21", diff --git a/plugins/conversation-insights/package.json b/plugins/conversation-insights/package.json index 8c9fd190aa8..3eb2118686d 100644 --- a/plugins/conversation-insights/package.json +++ b/plugins/conversation-insights/package.json @@ -7,7 +7,7 @@ }, "private": true, "dependencies": { - "@botpress/cognitive": "0.4.5", + "@botpress/cognitive": "0.4.6", "@botpress/sdk": "workspace:*", "browser-or-node": "^2.1.1", "jsonrepair": "^3.10.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8e3e52b4d94..308b60c0474 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2533,7 +2533,7 @@ importers: specifier: 1.38.2 version: link:../client '@botpress/sdk': - specifier: 6.4.1 + specifier: 6.4.2 version: link:../sdk '@bpinternal/const': specifier: ^0.1.0 @@ -2657,7 +2657,7 @@ importers: specifier: 1.38.2 version: link:../../../client '@botpress/sdk': - specifier: 6.4.1 + specifier: 6.4.2 version: link:../../../sdk devDependencies: '@types/node': @@ -2673,7 +2673,7 @@ importers: specifier: 1.38.2 version: link:../../../client '@botpress/sdk': - specifier: 6.4.1 + specifier: 6.4.2 version: link:../../../sdk devDependencies: '@types/node': @@ -2686,7 +2686,7 @@ importers: packages/cli/templates/empty-plugin: dependencies: '@botpress/sdk': - specifier: 6.4.1 + specifier: 6.4.2 version: link:../../../sdk devDependencies: '@types/node': @@ -2702,7 +2702,7 @@ importers: specifier: 1.38.2 version: link:../../../client '@botpress/sdk': - specifier: 6.4.1 + specifier: 6.4.2 version: link:../../../sdk devDependencies: '@types/node': @@ -2718,7 +2718,7 @@ importers: specifier: 1.38.2 version: link:../../../client '@botpress/sdk': - specifier: 6.4.1 + specifier: 6.4.2 version: link:../../../sdk axios: specifier: ^1.6.8 @@ -2872,7 +2872,7 @@ importers: specifier: 1.38.2 version: link:../client '@botpress/cognitive': - specifier: 0.4.5 + specifier: 0.4.6 version: link:../cognitive '@bpinternal/thicktoken': specifier: ^2.0.0 @@ -3058,7 +3058,7 @@ importers: packages/zai: dependencies: '@botpress/cognitive': - specifier: 0.4.5 + specifier: 0.4.6 version: link:../cognitive '@bpinternal/thicktoken': specifier: ^1.0.0 @@ -3171,7 +3171,7 @@ importers: plugins/conversation-insights: dependencies: '@botpress/cognitive': - specifier: 0.4.5 + specifier: 0.4.6 version: link:../../packages/cognitive '@botpress/sdk': specifier: workspace:*