From 0f62817f4ad4b8eaa02af95eccf62192eac44957 Mon Sep 17 00:00:00 2001 From: Rahul Karajgikar Date: Fri, 27 Mar 2026 18:49:46 +0530 Subject: [PATCH] fix(core): replace browser/public enums with list-derived unions --- .changeset/tough-countries-sing.md | 5 + docs/static/openapi/openapi.json | 2 +- .../agent-management/src/config/errors.ts | 42 ++--- .../src/config/loader.test.ts | 25 ++- .../agent-management/src/plugins/errors.ts | 66 ++++---- .../src/plugins/marketplace/errors.ts | 62 ++++---- .../src/preferences/errors.ts | 15 +- .../src/preferences/loader.test.ts | 13 +- .../src/preferences/schemas.ts | 9 +- .../agent-management/src/registry/errors.ts | 32 ++-- .../src/registry/registry.test.ts | 9 +- .../agent-management/src/resolver.test.ts | 33 ++-- .../agent-management/src/runtime/errors.ts | 42 ++--- .../tool-factories/agent-spawner/errors.ts | 22 +-- .../tool-factories/agent-spawner/runtime.ts | 9 +- packages/agent-management/src/writer.test.ts | 17 +- .../src/agent/DextoAgent.lifecycle.test.ts | 21 ++- packages/core/src/agent/errors.ts | 33 ++-- packages/core/src/approval/errors.ts | 58 +++---- packages/core/src/context/errors.ts | 73 +++++---- packages/core/src/errors/DextoRuntimeError.ts | 4 +- packages/core/src/errors/index.ts | 4 +- packages/core/src/errors/types.ts | 62 ++++---- packages/core/src/hooks/manager.ts | 34 ++-- packages/core/src/index.browser.ts | 7 +- packages/core/src/llm/errors.ts | 42 +++-- .../core/src/llm/executor/turn-executor.ts | 17 +- .../src/llm/providers/codex-app-server.ts | 38 ++--- .../core/src/llm/providers/local/errors.ts | 41 +++-- packages/core/src/llm/registry/index.test.ts | 21 ++- packages/core/src/llm/registry/sync.ts | 16 +- packages/core/src/llm/resolver.ts | 34 ++-- packages/core/src/llm/schemas.ts | 37 +++-- .../llm/services/vercel.integration.test.ts | 5 +- packages/core/src/llm/validation.ts | 14 +- packages/core/src/logger/v2/errors.ts | 25 ++- packages/core/src/mcp/errors.ts | 45 +++--- packages/core/src/mcp/manager.test.ts | 5 +- packages/core/src/mcp/resolver.ts | 10 +- packages/core/src/mcp/schemas.ts | 5 +- packages/core/src/memory/errors.ts | 37 +++-- packages/core/src/prompts/errors.ts | 37 +++-- packages/core/src/resources/errors.ts | 37 +++-- packages/core/src/session/chat-session.ts | 6 +- packages/core/src/session/errors.ts | 21 ++- .../core/src/session/history/database.test.ts | 13 +- .../core/src/session/session-manager.test.ts | 29 ++-- packages/core/src/storage/error-codes.ts | 81 ++++------ packages/core/src/storage/errors.ts | 146 +++++++++--------- packages/core/src/storage/index.ts | 3 +- .../src/systemPrompt/contributors.test.ts | 9 +- packages/core/src/systemPrompt/errors.ts | 21 ++- .../core/src/systemPrompt/manager.test.ts | 5 +- packages/core/src/telemetry/errors.ts | 21 ++- packages/core/src/tools/errors.ts | 77 ++++----- .../tools/tool-manager.integration.test.ts | 5 +- packages/core/src/tools/tool-manager.test.ts | 17 +- packages/core/src/tools/tool-manager.ts | 6 +- packages/core/src/utils/result.test.ts | 9 +- packages/core/src/utils/result.ts | 9 +- packages/core/src/workspace/errors.ts | 3 +- packages/server/src/hono/middleware/error.ts | 25 +-- packages/server/src/hono/routes/agents.ts | 14 +- packages/server/src/hono/routes/llm.ts | 6 +- packages/server/src/hono/routes/schedules.ts | 4 +- packages/server/src/hono/routes/sessions.ts | 12 +- .../server/src/hono/routes/system-prompt.ts | 10 +- packages/storage/src/cache/schemas.ts | 8 +- packages/storage/src/database/schemas.ts | 8 +- .../delegate-to-url-tool.test.ts | 10 +- .../implementations/delegate-to-url-tool.ts | 14 +- .../src/implementations/http-request-tool.ts | 54 ++++--- packages/tools-filesystem/src/errors.ts | 54 +++---- packages/tools-plan/src/errors.ts | 14 +- packages/tools-process/src/errors.ts | 36 ++--- packages/tools-scheduler/src/errors.ts | 30 ++-- packages/tools-todo/src/errors.ts | 10 +- 77 files changed, 936 insertions(+), 1019 deletions(-) create mode 100644 .changeset/tough-countries-sing.md diff --git a/.changeset/tough-countries-sing.md b/.changeset/tough-countries-sing.md new file mode 100644 index 000000000..fd5e25252 --- /dev/null +++ b/.changeset/tough-countries-sing.md @@ -0,0 +1,5 @@ +--- +'@dexto/core': patch +--- + +Replace `ErrorScope`, `ErrorType`, and `StorageErrorCode` enums with const-list-derived unions and update internal consumers to use plain string literals. diff --git a/docs/static/openapi/openapi.json b/docs/static/openapi/openapi.json index af023c886..db249a379 100644 --- a/docs/static/openapi/openapi.json +++ b/docs/static/openapi/openapi.json @@ -2,7 +2,7 @@ "openapi": "3.0.0", "info": { "title": "Dexto API", - "version": "1.6.17", + "version": "1.6.19", "description": "OpenAPI spec for the Dexto REST API server" }, "servers": [ diff --git a/packages/agent-management/src/config/errors.ts b/packages/agent-management/src/config/errors.ts index 19aabe1f0..1869a79cf 100644 --- a/packages/agent-management/src/config/errors.ts +++ b/packages/agent-management/src/config/errors.ts @@ -1,4 +1,4 @@ -import { DextoRuntimeError, ErrorScope, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; import { ConfigErrorCode } from './error-codes.js'; /** @@ -10,8 +10,8 @@ export class ConfigError { static fileNotFound(configPath: string) { return new DextoRuntimeError( ConfigErrorCode.FILE_NOT_FOUND, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Configuration file not found: ${configPath}`, { configPath }, 'Ensure the configuration file exists at the specified path' @@ -21,8 +21,8 @@ export class ConfigError { static fileReadError(configPath: string, cause: string) { return new DextoRuntimeError( ConfigErrorCode.FILE_READ_ERROR, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to read configuration file: ${cause}`, { configPath, cause }, 'Check file permissions and ensure the file is not corrupted' @@ -32,8 +32,8 @@ export class ConfigError { static fileWriteError(configPath: string, cause: string) { return new DextoRuntimeError( ConfigErrorCode.FILE_WRITE_ERROR, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to write configuration file '${configPath}': ${cause}`, { configPath, cause }, 'Check file permissions and available disk space' @@ -44,8 +44,8 @@ export class ConfigError { static parseError(configPath: string, cause: string) { return new DextoRuntimeError( ConfigErrorCode.PARSE_ERROR, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Failed to parse configuration file: ${cause}`, { configPath, cause }, 'Ensure the configuration file contains valid YAML syntax' @@ -56,8 +56,8 @@ export class ConfigError { static noProjectDefault(projectPath: string) { return new DextoRuntimeError( ConfigErrorCode.NO_PROJECT_DEFAULT, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `No project agent found and no global preferences configured.\nSet a primaryAgent in a workspace registry under agents/, create a single workspace agent under agents/, or run \`dexto setup\` to configure preferences.`, { projectPath }, 'Run `dexto setup` or create a project-specific agent config' @@ -67,8 +67,8 @@ export class ConfigError { static invalidProjectPrimary(registryPath: string, primaryAgent: string, reason?: string) { return new DextoRuntimeError( ConfigErrorCode.INVALID_PROJECT_PRIMARY, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Invalid primaryAgent '${primaryAgent}' in ${registryPath}${reason ? `: ${reason}` : ''}`, { registryPath, primaryAgent, reason }, 'Update the workspace registry so primaryAgent points to a valid workspace agent config' @@ -78,8 +78,8 @@ export class ConfigError { static noGlobalPreferences() { return new DextoRuntimeError( ConfigErrorCode.NO_GLOBAL_PREFERENCES, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `No global preferences found. Run \`dexto setup\` to get started.`, {}, 'Run `dexto setup` to configure your AI preferences' @@ -89,8 +89,8 @@ export class ConfigError { static setupIncomplete() { return new DextoRuntimeError( ConfigErrorCode.SETUP_INCOMPLETE, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Global preferences setup is incomplete. Run \`dexto setup\` to complete.`, {}, 'Run `dexto setup` to complete your configuration' @@ -100,8 +100,8 @@ export class ConfigError { static bundledNotFound(bundledPath: string) { return new DextoRuntimeError( ConfigErrorCode.BUNDLED_NOT_FOUND, - ErrorScope.CONFIG, - ErrorType.NOT_FOUND, + 'config', + 'not_found', `Bundled default agent not found: ${bundledPath}. Run npm run build first.`, { path: bundledPath }, 'Run `npm run build` to build the bundled agents' @@ -111,8 +111,8 @@ export class ConfigError { static unknownContext(context: string) { return new DextoRuntimeError( ConfigErrorCode.UNKNOWN_CONTEXT, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Unknown execution context: ${context}`, { context }, 'This is an internal error - please report it' diff --git a/packages/agent-management/src/config/loader.test.ts b/packages/agent-management/src/config/loader.test.ts index 56bc1bda4..5ff8ea6c8 100644 --- a/packages/agent-management/src/config/loader.test.ts +++ b/packages/agent-management/src/config/loader.test.ts @@ -3,7 +3,6 @@ import { promises as fs } from 'fs'; import * as path from 'path'; import { fileURLToPath } from 'node:url'; import { loadAgentConfig } from './loader.js'; -import { ErrorScope, ErrorType } from '@dexto/core'; import { ConfigErrorCode } from './error-codes.js'; // Temp config file path relative to this test file (stable across monorepo runners) @@ -59,8 +58,8 @@ mcpServers: await expect(loadAgentConfig(missing)).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.FILE_NOT_FOUND, - scope: ErrorScope.CONFIG, - type: ErrorType.USER, + scope: 'config', + type: 'user', }) ); }); @@ -70,8 +69,8 @@ mcpServers: await expect(loadAgentConfig(tmpFile)).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.FILE_READ_ERROR, - scope: ErrorScope.CONFIG, - type: ErrorType.SYSTEM, + scope: 'config', + type: 'system', }) ); await fs.unlink(tmpFile); @@ -94,8 +93,8 @@ mcpServers: await expect(loadAgentConfig(tmpFile)).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.PARSE_ERROR, - scope: ErrorScope.CONFIG, - type: ErrorType.USER, + scope: 'config', + type: 'user', }) ); }); @@ -106,8 +105,8 @@ mcpServers: await expect(loadAgentConfig(nonExistentPath)).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.FILE_NOT_FOUND, - scope: ErrorScope.CONFIG, - type: ErrorType.USER, + scope: 'config', + type: 'user', }) ); }); @@ -202,8 +201,8 @@ mcpServers: await expect(loadAgentConfig(tmpFile)).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.PARSE_ERROR, - scope: ErrorScope.CONFIG, - type: ErrorType.USER, + scope: 'config', + type: 'user', }) ); }); @@ -244,8 +243,8 @@ tools: await expect(loadAgentConfig(tmpFile)).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.PARSE_ERROR, - scope: ErrorScope.CONFIG, - type: ErrorType.USER, + scope: 'config', + type: 'user', }) ); }); diff --git a/packages/agent-management/src/plugins/errors.ts b/packages/agent-management/src/plugins/errors.ts index 36d9d2868..309d21403 100644 --- a/packages/agent-management/src/plugins/errors.ts +++ b/packages/agent-management/src/plugins/errors.ts @@ -1,4 +1,4 @@ -import { DextoRuntimeError, ErrorScope, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; import { PluginErrorCode } from './error-codes.js'; /** @@ -10,8 +10,8 @@ export class PluginError { static manifestNotFound(pluginPath: string) { return new DextoRuntimeError( PluginErrorCode.MANIFEST_NOT_FOUND, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Plugin manifest not found: ${pluginPath}/.claude-plugin/plugin.json`, { pluginPath }, 'Ensure the plugin has a valid .claude-plugin/plugin.json file' @@ -21,8 +21,8 @@ export class PluginError { static manifestInvalid(pluginPath: string, issues: string) { return new DextoRuntimeError( PluginErrorCode.MANIFEST_INVALID, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Invalid plugin manifest at ${pluginPath}: ${issues}`, { pluginPath, issues }, 'Check the plugin.json file matches the expected schema (name is required)' @@ -32,8 +32,8 @@ export class PluginError { static manifestParseError(pluginPath: string, cause: string) { return new DextoRuntimeError( PluginErrorCode.MANIFEST_PARSE_ERROR, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Failed to parse plugin manifest at ${pluginPath}: ${cause}`, { pluginPath, cause }, 'Ensure plugin.json contains valid JSON' @@ -44,8 +44,8 @@ export class PluginError { static directoryReadError(pluginPath: string, cause: string) { return new DextoRuntimeError( PluginErrorCode.DIRECTORY_READ_ERROR, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to read plugin directory ${pluginPath}: ${cause}`, { pluginPath, cause }, 'Check file permissions and that the directory exists' @@ -55,8 +55,8 @@ export class PluginError { static mcpConfigInvalid(pluginPath: string, cause: string) { return new DextoRuntimeError( PluginErrorCode.MCP_CONFIG_INVALID, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Invalid MCP config in plugin ${pluginPath}: ${cause}`, { pluginPath, cause }, 'Check the .mcp.json file contains valid JSON' @@ -67,8 +67,8 @@ export class PluginError { static installSourceNotFound(sourcePath: string) { return new DextoRuntimeError( PluginErrorCode.INSTALL_SOURCE_NOT_FOUND, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Plugin source not found: ${sourcePath}`, { sourcePath }, 'Ensure the path points to a valid plugin directory with .claude-plugin/plugin.json' @@ -78,8 +78,8 @@ export class PluginError { static installAlreadyExists(pluginName: string, existingPath: string) { return new DextoRuntimeError( PluginErrorCode.INSTALL_ALREADY_EXISTS, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Plugin '${pluginName}' is already installed at ${existingPath}`, { pluginName, existingPath }, 'Use --force to overwrite the existing installation' @@ -89,8 +89,8 @@ export class PluginError { static installCopyFailed(sourcePath: string, destPath: string, cause: string) { return new DextoRuntimeError( PluginErrorCode.INSTALL_COPY_FAILED, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to copy plugin from ${sourcePath} to ${destPath}: ${cause}`, { sourcePath, destPath, cause }, 'Check file permissions and disk space' @@ -100,8 +100,8 @@ export class PluginError { static installManifestWriteFailed(manifestPath: string, cause: string) { return new DextoRuntimeError( PluginErrorCode.INSTALL_MANIFEST_WRITE_FAILED, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to update installed plugins manifest at ${manifestPath}: ${cause}`, { manifestPath, cause }, 'Check file permissions and ensure the directory exists' @@ -111,8 +111,8 @@ export class PluginError { static invalidScope(scope: unknown) { return new DextoRuntimeError( PluginErrorCode.INSTALL_INVALID_SCOPE, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Invalid installation scope: ${scope}. Must be 'user', 'project', or 'local'.`, { scope }, "Check the scope parameter is one of: 'user', 'project', 'local'" @@ -123,8 +123,8 @@ export class PluginError { static importNotFound(pluginName: string) { return new DextoRuntimeError( PluginErrorCode.IMPORT_NOT_FOUND, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Plugin '${pluginName}' not found`, { pluginName }, 'Use `dexto plugin list` to see available plugins' @@ -135,8 +135,8 @@ export class PluginError { static uninstallNotFound(pluginName: string, hint?: string) { return new DextoRuntimeError( PluginErrorCode.UNINSTALL_NOT_FOUND, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Plugin '${pluginName}' is not installed`, { pluginName }, hint || 'Use `dexto plugin list` to see installed plugins' @@ -146,8 +146,8 @@ export class PluginError { static uninstallDeleteFailed(pluginPath: string, cause: string) { return new DextoRuntimeError( PluginErrorCode.UNINSTALL_DELETE_FAILED, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to delete plugin at ${pluginPath}: ${cause}`, { pluginPath, cause }, 'Check file permissions and ensure the plugin is not in use' @@ -157,8 +157,8 @@ export class PluginError { static uninstallManifestUpdateFailed(manifestPath: string, cause: string) { return new DextoRuntimeError( PluginErrorCode.UNINSTALL_MANIFEST_UPDATE_FAILED, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to update installed plugins manifest at ${manifestPath}: ${cause}`, { manifestPath, cause }, 'Check file permissions' @@ -169,8 +169,8 @@ export class PluginError { static validationInvalidStructure(pluginPath: string, details: string) { return new DextoRuntimeError( PluginErrorCode.VALIDATION_INVALID_STRUCTURE, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Invalid plugin structure at ${pluginPath}: ${details}`, { pluginPath, details }, 'Ensure the plugin has a .claude-plugin/plugin.json file' @@ -180,8 +180,8 @@ export class PluginError { static validationMissingRequired(pluginPath: string, missing: string[]) { return new DextoRuntimeError( PluginErrorCode.VALIDATION_MISSING_REQUIRED, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Plugin at ${pluginPath} is missing required fields: ${missing.join(', ')}`, { pluginPath, missing }, 'Add the missing fields to .claude-plugin/plugin.json' diff --git a/packages/agent-management/src/plugins/marketplace/errors.ts b/packages/agent-management/src/plugins/marketplace/errors.ts index 80f16f0b3..cdbd2e51d 100644 --- a/packages/agent-management/src/plugins/marketplace/errors.ts +++ b/packages/agent-management/src/plugins/marketplace/errors.ts @@ -1,4 +1,4 @@ -import { DextoRuntimeError, ErrorScope, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; import { MarketplaceErrorCode } from './error-codes.js'; /** @@ -10,8 +10,8 @@ export class MarketplaceError { static registryReadFailed(path: string, cause: string) { return new DextoRuntimeError( MarketplaceErrorCode.REGISTRY_READ_FAILED, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to read marketplace registry at ${path}: ${cause}`, { path, cause }, 'Check file permissions and ensure the file exists' @@ -21,8 +21,8 @@ export class MarketplaceError { static registryWriteFailed(path: string, cause: string) { return new DextoRuntimeError( MarketplaceErrorCode.REGISTRY_WRITE_FAILED, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to write marketplace registry at ${path}: ${cause}`, { path, cause }, 'Check file permissions and disk space' @@ -33,8 +33,8 @@ export class MarketplaceError { static addAlreadyExists(name: string, existingPath: string) { return new DextoRuntimeError( MarketplaceErrorCode.ADD_ALREADY_EXISTS, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Marketplace '${name}' already exists at ${existingPath}`, { name, existingPath }, 'Use a different name or remove the existing marketplace first' @@ -44,8 +44,8 @@ export class MarketplaceError { static addCloneFailed(source: string, cause: string) { return new DextoRuntimeError( MarketplaceErrorCode.ADD_CLONE_FAILED, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to clone marketplace from ${source}: ${cause}`, { source, cause }, 'Check the URL is correct and you have network access' @@ -55,8 +55,8 @@ export class MarketplaceError { static addInvalidSource(source: string, reason: string) { return new DextoRuntimeError( MarketplaceErrorCode.ADD_INVALID_SOURCE, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Invalid marketplace source '${source}': ${reason}`, { source, reason }, 'Use format: owner/repo (GitHub), git URL, or local path' @@ -66,8 +66,8 @@ export class MarketplaceError { static addLocalNotFound(path: string) { return new DextoRuntimeError( MarketplaceErrorCode.ADD_LOCAL_NOT_FOUND, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Local marketplace path not found: ${path}`, { path }, 'Check the path exists and is a directory' @@ -78,8 +78,8 @@ export class MarketplaceError { static removeNotFound(name: string) { return new DextoRuntimeError( MarketplaceErrorCode.REMOVE_NOT_FOUND, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Marketplace '${name}' not found`, { name }, 'Use `dexto plugin marketplace list` to see registered marketplaces' @@ -89,8 +89,8 @@ export class MarketplaceError { static removeDeleteFailed(name: string, path: string, cause: string) { return new DextoRuntimeError( MarketplaceErrorCode.REMOVE_DELETE_FAILED, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to delete marketplace '${name}' at ${path}: ${cause}`, { name, path, cause }, 'Check file permissions' @@ -101,8 +101,8 @@ export class MarketplaceError { static updateNotFound(name: string) { return new DextoRuntimeError( MarketplaceErrorCode.UPDATE_NOT_FOUND, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Marketplace '${name}' not found`, { name }, 'Use `dexto plugin marketplace list` to see registered marketplaces' @@ -112,8 +112,8 @@ export class MarketplaceError { static updatePullFailed(name: string, cause: string) { return new DextoRuntimeError( MarketplaceErrorCode.UPDATE_PULL_FAILED, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to update marketplace '${name}': ${cause}`, { name, cause }, 'Check network connectivity and that the repository is accessible' @@ -123,8 +123,8 @@ export class MarketplaceError { static updateLocalNotSupported(name: string) { return new DextoRuntimeError( MarketplaceErrorCode.UPDATE_LOCAL_NOT_SUPPORTED, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Cannot update local marketplace '${name}'`, { name }, 'Local marketplaces do not support automatic updates' @@ -135,8 +135,8 @@ export class MarketplaceError { static installMarketplaceNotFound(marketplace: string) { return new DextoRuntimeError( MarketplaceErrorCode.INSTALL_MARKETPLACE_NOT_FOUND, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Marketplace '${marketplace}' not found`, { marketplace }, 'Use `dexto plugin marketplace list` to see registered marketplaces, or `dexto plugin marketplace add` to register one' @@ -147,8 +147,8 @@ export class MarketplaceError { const marketplaceInfo = marketplace ? ` in marketplace '${marketplace}'` : ''; return new DextoRuntimeError( MarketplaceErrorCode.INSTALL_PLUGIN_NOT_FOUND, - ErrorScope.CONFIG, - ErrorType.USER, + 'config', + 'user', `Plugin '${pluginName}' not found${marketplaceInfo}`, { pluginName, marketplace }, 'Use `dexto plugin marketplace` to browse available plugins' @@ -158,8 +158,8 @@ export class MarketplaceError { static installCopyFailed(pluginName: string, cause: string) { return new DextoRuntimeError( MarketplaceErrorCode.INSTALL_COPY_FAILED, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to copy plugin '${pluginName}' from marketplace: ${cause}`, { pluginName, cause }, 'Check file permissions and disk space' @@ -170,8 +170,8 @@ export class MarketplaceError { static scanFailed(marketplacePath: string, cause: string) { return new DextoRuntimeError( MarketplaceErrorCode.SCAN_FAILED, - ErrorScope.CONFIG, - ErrorType.SYSTEM, + 'config', + 'system', `Failed to scan marketplace at ${marketplacePath}: ${cause}`, { marketplacePath, cause }, 'Check the marketplace directory is accessible' diff --git a/packages/agent-management/src/preferences/errors.ts b/packages/agent-management/src/preferences/errors.ts index 6c2222062..a3bcf1db6 100644 --- a/packages/agent-management/src/preferences/errors.ts +++ b/packages/agent-management/src/preferences/errors.ts @@ -1,6 +1,7 @@ // packages/core/src/preferences/errors.ts -import { DextoRuntimeError, DextoValidationError, ErrorType } from '@dexto/core'; +import { DextoRuntimeError, DextoValidationError } from '@dexto/core'; +import type { Issue } from '@dexto/core'; import { type ZodError } from 'zod'; import { PreferenceErrorCode } from './error-codes.js'; @@ -11,7 +12,7 @@ export class PreferenceError { return new DextoRuntimeError( PreferenceErrorCode.FILE_NOT_FOUND, 'preference', - ErrorType.USER, + 'user', `Preferences file not found: ${preferencesPath}`, { preferencesPath }, 'Run `dexto setup` to create preferences' @@ -22,7 +23,7 @@ export class PreferenceError { return new DextoRuntimeError( PreferenceErrorCode.FILE_READ_ERROR, 'preference', - ErrorType.SYSTEM, + 'system', `Failed to read preferences: ${cause}`, { preferencesPath, cause }, 'Check file permissions and ensure the file is not corrupted' @@ -33,7 +34,7 @@ export class PreferenceError { return new DextoRuntimeError( PreferenceErrorCode.FILE_WRITE_ERROR, 'preference', - ErrorType.SYSTEM, + 'system', `Failed to save preferences: ${cause}`, { preferencesPath, cause }, 'Check file permissions and available disk space' @@ -41,11 +42,11 @@ export class PreferenceError { } static validationFailed(zodError: ZodError) { - const issues = zodError.issues.map((issue) => ({ + const issues: Issue[] = zodError.issues.map((issue) => ({ code: PreferenceErrorCode.VALIDATION_ERROR, message: `${issue.path.join('.')}: ${issue.message}`, scope: 'preference', - type: ErrorType.USER, + type: 'user', severity: 'error' as const, })); @@ -58,7 +59,7 @@ export class PreferenceError { code: PreferenceErrorCode.INVALID_PREFERENCE_VALUE, message: `agentId is invalid: ${agentId}`, scope: 'preference', - type: ErrorType.USER, + type: 'user', severity: 'error' as const, }, ]); diff --git a/packages/agent-management/src/preferences/loader.test.ts b/packages/agent-management/src/preferences/loader.test.ts index 39887fb2a..eb9f7b756 100644 --- a/packages/agent-management/src/preferences/loader.test.ts +++ b/packages/agent-management/src/preferences/loader.test.ts @@ -12,7 +12,6 @@ import { } from './loader.js'; import { type GlobalPreferences } from './schemas.js'; import { PreferenceErrorCode } from './error-codes.js'; -import { ErrorType } from '@dexto/core'; // Mock getDextoGlobalPath to use a temporary directory import * as pathUtils from '../utils/path.js'; @@ -182,7 +181,7 @@ describe('Preferences Loader', () => { expect.objectContaining({ code: PreferenceErrorCode.VALIDATION_ERROR, scope: 'preference', - type: ErrorType.USER, + type: 'user', }), ]), }) @@ -239,7 +238,7 @@ describe('Preferences Loader', () => { expect.objectContaining({ code: PreferenceErrorCode.FILE_NOT_FOUND, scope: 'preference', - type: ErrorType.USER, + type: 'user', }) ); }); @@ -252,7 +251,7 @@ describe('Preferences Loader', () => { expect.objectContaining({ code: PreferenceErrorCode.FILE_READ_ERROR, scope: 'preference', - type: ErrorType.SYSTEM, + type: 'system', }) ); }); @@ -274,7 +273,7 @@ describe('Preferences Loader', () => { expect.objectContaining({ code: PreferenceErrorCode.VALIDATION_ERROR, scope: 'preference', - type: ErrorType.USER, + type: 'user', }), ]), }) @@ -299,7 +298,7 @@ setup: expect.objectContaining({ code: PreferenceErrorCode.VALIDATION_ERROR, scope: 'preference', - type: ErrorType.USER, + type: 'user', }), ]), }) @@ -485,7 +484,7 @@ setup: expect.objectContaining({ code: PreferenceErrorCode.VALIDATION_ERROR, scope: 'preference', - type: ErrorType.USER, + type: 'user', }), ]), }) diff --git a/packages/agent-management/src/preferences/schemas.ts b/packages/agent-management/src/preferences/schemas.ts index 925f8c087..b23ce03bd 100644 --- a/packages/agent-management/src/preferences/schemas.ts +++ b/packages/agent-management/src/preferences/schemas.ts @@ -12,7 +12,6 @@ import { LLM_PROVIDERS, NonEmptyTrimmed, OptionalURL, - ErrorType, } from '@dexto/core'; import { PreferenceErrorCode } from './error-codes.js'; @@ -79,7 +78,7 @@ export const PreferenceLLMSchema = z params: { code: PreferenceErrorCode.MODEL_INCOMPATIBLE, scope: 'preference', - type: ErrorType.USER, + type: 'user', }, }); } @@ -93,7 +92,7 @@ export const PreferenceLLMSchema = z params: { code: PreferenceErrorCode.INVALID_PREFERENCE_VALUE, scope: 'preference', - type: ErrorType.USER, + type: 'user', }, }); } @@ -113,7 +112,7 @@ export const PreferenceLLMSchema = z params: { code: PreferenceErrorCode.INVALID_PREFERENCE_VALUE, scope: 'preference', - type: ErrorType.USER, + type: 'user', }, }); } @@ -128,7 +127,7 @@ export const PreferenceLLMSchema = z params: { code: PreferenceErrorCode.INVALID_PREFERENCE_VALUE, scope: 'preference', - type: ErrorType.USER, + type: 'user', }, }); } diff --git a/packages/agent-management/src/registry/errors.ts b/packages/agent-management/src/registry/errors.ts index d3d270a14..0a9d52055 100644 --- a/packages/agent-management/src/registry/errors.ts +++ b/packages/agent-management/src/registry/errors.ts @@ -1,4 +1,4 @@ -import { DextoRuntimeError, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; import { RegistryErrorCode } from './error-codes.js'; /** @@ -11,7 +11,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.AGENT_NOT_FOUND, 'agent_registry', - ErrorType.USER, + 'user', `Agent '${agentId}' not found in registry`, { agentId, availableAgents }, `Available agents: ${availableAgents.join(', ')}. Use a file path for custom agents.` @@ -22,7 +22,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.AGENT_INVALID_ENTRY, 'agent_registry', - ErrorType.SYSTEM, + 'system', `Registry entry for '${agentId}' is invalid: ${reason}`, { agentId, reason }, 'This indicates a problem with the agent registry - please report this issue' @@ -33,7 +33,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.AGENT_ALREADY_EXISTS, 'agent_registry', - ErrorType.USER, + 'user', `Agent '${agentId}' already exists in user registry`, { agentId }, 'Choose a different name or uninstall the existing agent first' @@ -44,7 +44,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.AGENT_ALREADY_EXISTS, 'agent_registry', - ErrorType.USER, + 'user', `Cannot create custom agent '${agentId}': name conflicts with builtin agent`, { agentId, conflictType: 'builtin' }, 'Choose a different name for your custom agent' @@ -56,7 +56,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.INSTALLATION_FAILED, 'agent_registry', - ErrorType.SYSTEM, + 'system', `Failed to install agent '${agentId}': ${cause}`, { agentId, cause }, 'Check network connection and available disk space' @@ -67,7 +67,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.INSTALLATION_VALIDATION_FAILED, 'agent_registry', - ErrorType.SYSTEM, + 'system', `Installation validation failed for '${agentId}': missing main config`, { agentId, missingPath }, 'This indicates a problem with the agent bundle - please report this issue' @@ -79,7 +79,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.CONFIG_NOT_FOUND, 'agent_registry', - ErrorType.SYSTEM, + 'system', `Agent config file not found: ${configPath}`, { configPath }, 'This indicates a problem with the agent installation' @@ -90,7 +90,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.MAIN_CONFIG_MISSING, 'agent_registry', - ErrorType.SYSTEM, + 'system', `Main config file not found for agent '${agentId}': ${expectedPath}`, { agentId, expectedPath }, 'This indicates a problem with the agent bundle structure' @@ -102,7 +102,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.AGENT_NOT_INSTALLED, 'agent_registry', - ErrorType.USER, + 'user', `Agent '${agentId}' is not installed`, { agentId }, 'Use "dexto list-agents --installed" to see installed agents' @@ -113,7 +113,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.AGENT_PROTECTED, 'agent_registry', - ErrorType.USER, + 'user', `Agent '${agentId}' is protected and cannot be uninstalled. Use --force to override (not recommended for critical agents)`, { agentId }, 'Use --force to override (not recommended for critical agents)' @@ -124,7 +124,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.UNINSTALLATION_FAILED, 'agent_registry', - ErrorType.SYSTEM, + 'system', `Failed to uninstall agent '${agentId}': ${cause}`, { agentId, cause }, 'Check file permissions and ensure no processes are using the agent' @@ -136,7 +136,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.REGISTRY_NOT_FOUND, 'agent_registry', - ErrorType.SYSTEM, + 'system', `Agent registry not found: ${registryPath}: ${cause}`, { registryPath }, 'This indicates a problem with the Dexto installation - please reinstall or report this issue' @@ -147,7 +147,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.REGISTRY_PARSE_ERROR, 'agent_registry', - ErrorType.SYSTEM, + 'system', `Failed to parse agent registry from ${registryPath}: ${cause}`, { registryPath, cause }, 'This indicates a corrupted registry file - please reinstall Dexto' @@ -158,7 +158,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.REGISTRY_WRITE_ERROR, 'agent_registry', - ErrorType.SYSTEM, + 'system', `Failed to save agent registry to ${registryPath}: ${cause}`, { registryPath, cause }, 'Check file permissions and available disk space' @@ -170,7 +170,7 @@ export class RegistryError { return new DextoRuntimeError( RegistryErrorCode.AGENT_NOT_INSTALLED_AUTO_INSTALL_DISABLED, 'agent_registry', - ErrorType.USER, + 'user', `Agent '${agentId}' is not installed locally and auto-install is disabled`, { agentId, availableAgents }, `Use 'dexto install ${agentId}' to install it manually, or use a file path for custom agents` diff --git a/packages/agent-management/src/registry/registry.test.ts b/packages/agent-management/src/registry/registry.test.ts index 9f2d15389..6aea0e7b9 100644 --- a/packages/agent-management/src/registry/registry.test.ts +++ b/packages/agent-management/src/registry/registry.test.ts @@ -4,7 +4,6 @@ import * as path from 'path'; import { tmpdir } from 'os'; import { LocalAgentRegistry } from './registry.js'; import { RegistryErrorCode } from './error-codes.js'; -import { ErrorType } from '@dexto/core'; // Mock dependencies vi.mock('../utils/path.js', () => ({ @@ -222,7 +221,7 @@ describe('LocalAgentRegistry', () => { await expect(registry.installAgent('unknown-agent')).rejects.toMatchObject({ code: RegistryErrorCode.AGENT_NOT_FOUND, scope: 'agent_registry', - type: ErrorType.USER, + type: 'user', context: { agentId: 'unknown-agent', availableAgents: expect.arrayContaining(['test-agent', 'dir-agent']), @@ -321,7 +320,7 @@ describe('LocalAgentRegistry', () => { await expect(registry.resolveAgent('unknown-agent')).rejects.toMatchObject({ code: RegistryErrorCode.AGENT_NOT_FOUND, scope: 'agent_registry', - type: ErrorType.USER, + type: 'user', context: { agentId: 'unknown-agent', availableAgents: expect.arrayContaining(['test-agent', 'dir-agent']), @@ -411,7 +410,7 @@ describe('LocalAgentRegistry', () => { { code: RegistryErrorCode.AGENT_NOT_INSTALLED_AUTO_INSTALL_DISABLED, scope: 'agent_registry', - type: ErrorType.USER, + type: 'user', context: { agentId: 'auto-test-agent', availableAgents: expect.arrayContaining([ @@ -474,7 +473,7 @@ describe('LocalAgentRegistry', () => { expect.objectContaining({ code: RegistryErrorCode.AGENT_INVALID_ENTRY, scope: 'agent_registry', - type: ErrorType.SYSTEM, + type: 'system', context: { agentId: 'bad-dir-agent', reason: 'directory entry missing main field', diff --git a/packages/agent-management/src/resolver.test.ts b/packages/agent-management/src/resolver.test.ts index 5539888ee..d5e2cd6f0 100644 --- a/packages/agent-management/src/resolver.test.ts +++ b/packages/agent-management/src/resolver.test.ts @@ -3,7 +3,6 @@ import { promises as fs, mkdtempSync } from 'fs'; import * as path from 'path'; import { tmpdir } from 'os'; import { resolveAgentPath, updateDefaultAgentPreference } from './resolver.js'; -import { ErrorScope, ErrorType } from '@dexto/core'; import { ConfigErrorCode } from './config/index.js'; // Mock dependencies - use vi.fn() in factory to avoid hoisting issues @@ -118,8 +117,8 @@ describe('Agent Resolver', () => { await expect(resolveAgentPath(nonExistentFile)).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.FILE_NOT_FOUND, - scope: ErrorScope.CONFIG, - type: ErrorType.USER, + scope: 'config', + type: 'user', }) ); }); @@ -356,8 +355,8 @@ describe('Agent Resolver', () => { await expect(resolveAgentPath()).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.BUNDLED_NOT_FOUND, - scope: ErrorScope.CONFIG, - type: ErrorType.NOT_FOUND, + scope: 'config', + type: 'not_found', }) ); }); @@ -370,8 +369,8 @@ describe('Agent Resolver', () => { await expect(resolveAgentPath()).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.BUNDLED_NOT_FOUND, - scope: ErrorScope.CONFIG, - type: ErrorType.NOT_FOUND, + scope: 'config', + type: 'not_found', }) ); }); @@ -478,8 +477,8 @@ describe('Agent Resolver', () => { await expect(resolveAgentPath()).rejects.toMatchObject({ code: ConfigErrorCode.INVALID_PROJECT_PRIMARY, - scope: ErrorScope.CONFIG, - type: ErrorType.USER, + scope: 'config', + type: 'user', }); }); @@ -554,8 +553,8 @@ describe('Agent Resolver', () => { await expect(resolveAgentPath()).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.SETUP_INCOMPLETE, - scope: ErrorScope.CONFIG, - type: ErrorType.USER, + scope: 'config', + type: 'user', }) ); }); @@ -617,8 +616,8 @@ describe('Agent Resolver', () => { await expect(resolveAgentPath()).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.NO_GLOBAL_PREFERENCES, - scope: ErrorScope.CONFIG, - type: ErrorType.USER, + scope: 'config', + type: 'user', }) ); }); @@ -633,8 +632,8 @@ describe('Agent Resolver', () => { await expect(resolveAgentPath()).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.SETUP_INCOMPLETE, - scope: ErrorScope.CONFIG, - type: ErrorType.USER, + scope: 'config', + type: 'user', }) ); }); @@ -647,8 +646,8 @@ describe('Agent Resolver', () => { await expect(resolveAgentPath()).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.UNKNOWN_CONTEXT, - scope: ErrorScope.CONFIG, - type: ErrorType.SYSTEM, + scope: 'config', + type: 'system', }) ); }); diff --git a/packages/agent-management/src/runtime/errors.ts b/packages/agent-management/src/runtime/errors.ts index 6adff6594..8612754c7 100644 --- a/packages/agent-management/src/runtime/errors.ts +++ b/packages/agent-management/src/runtime/errors.ts @@ -1,4 +1,4 @@ -import { DextoRuntimeError, ErrorScope, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; import { RuntimeErrorCode } from './error-codes.js'; /** @@ -10,8 +10,8 @@ export class RuntimeError { static maxAgentsExceeded(currentCount: number, maxAllowed: number) { return new DextoRuntimeError( RuntimeErrorCode.MAX_AGENTS_EXCEEDED, - ErrorScope.AGENT, - ErrorType.USER, + 'agent', + 'user', `Maximum agents limit exceeded. Current: ${currentCount}, Max: ${maxAllowed}`, { currentCount, maxAllowed }, 'Stop some existing agents before spawning new ones' @@ -22,8 +22,8 @@ export class RuntimeError { static agentNotFound(agentId: string) { return new DextoRuntimeError( RuntimeErrorCode.AGENT_NOT_FOUND, - ErrorScope.AGENT, - ErrorType.NOT_FOUND, + 'agent', + 'not_found', `Agent '${agentId}' not found`, { agentId }, 'Ensure the agent ID is correct and the agent has been spawned' @@ -33,8 +33,8 @@ export class RuntimeError { static agentAlreadyExists(agentId: string) { return new DextoRuntimeError( RuntimeErrorCode.AGENT_ALREADY_EXISTS, - ErrorScope.AGENT, - ErrorType.USER, + 'agent', + 'user', `Agent with ID '${agentId}' already exists`, { agentId }, 'Use a different agent ID or stop the existing agent first' @@ -44,8 +44,8 @@ export class RuntimeError { static agentNotStarted(agentId: string) { return new DextoRuntimeError( RuntimeErrorCode.AGENT_NOT_STARTED, - ErrorScope.AGENT, - ErrorType.USER, + 'agent', + 'user', `Agent '${agentId}' has not been started`, { agentId }, 'Start the agent before executing tasks' @@ -55,8 +55,8 @@ export class RuntimeError { static agentAlreadyStopped(agentId: string) { return new DextoRuntimeError( RuntimeErrorCode.AGENT_ALREADY_STOPPED, - ErrorScope.AGENT, - ErrorType.USER, + 'agent', + 'user', `Agent '${agentId}' has already been stopped`, { agentId }, 'Spawn a new agent if you need to continue' @@ -67,8 +67,8 @@ export class RuntimeError { static spawnFailed(cause: string, agentId?: string) { return new DextoRuntimeError( RuntimeErrorCode.SPAWN_FAILED, - ErrorScope.AGENT, - ErrorType.SYSTEM, + 'agent', + 'system', agentId ? `Failed to spawn agent '${agentId}': ${cause}` : `Failed to spawn agent: ${cause}`, @@ -80,8 +80,8 @@ export class RuntimeError { static invalidConfig(message: string, details?: Record) { return new DextoRuntimeError( RuntimeErrorCode.INVALID_CONFIG, - ErrorScope.AGENT, - ErrorType.USER, + 'agent', + 'user', `Invalid agent configuration: ${message}`, details ?? {}, 'Check the configuration and ensure all required fields are provided' @@ -92,8 +92,8 @@ export class RuntimeError { static taskTimeout(agentId: string, timeoutMs: number) { return new DextoRuntimeError( RuntimeErrorCode.TASK_TIMEOUT, - ErrorScope.AGENT, - ErrorType.TIMEOUT, + 'agent', + 'timeout', `Task execution timed out for agent '${agentId}' after ${timeoutMs}ms`, { agentId, timeoutMs }, 'Increase the timeout or simplify the task' @@ -103,8 +103,8 @@ export class RuntimeError { static taskFailed(agentId: string, cause: string) { return new DextoRuntimeError( RuntimeErrorCode.TASK_FAILED, - ErrorScope.AGENT, - ErrorType.SYSTEM, + 'agent', + 'system', `Task execution failed for agent '${agentId}': ${cause}`, { agentId, cause }, 'Check the task requirements and agent configuration' @@ -114,8 +114,8 @@ export class RuntimeError { static taskCancelled(agentId: string) { return new DextoRuntimeError( RuntimeErrorCode.TASK_CANCELLED, - ErrorScope.AGENT, - ErrorType.USER, + 'agent', + 'user', `Task execution was cancelled for agent '${agentId}'`, { agentId }, 'The task was cancelled by user or system request' diff --git a/packages/agent-management/src/tool-factories/agent-spawner/errors.ts b/packages/agent-management/src/tool-factories/agent-spawner/errors.ts index a66e0a6bd..55bde7635 100644 --- a/packages/agent-management/src/tool-factories/agent-spawner/errors.ts +++ b/packages/agent-management/src/tool-factories/agent-spawner/errors.ts @@ -1,4 +1,4 @@ -import { DextoRuntimeError, ErrorScope, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; import { AgentSpawnerErrorCode } from './error-codes.js'; /** @@ -8,8 +8,8 @@ export class AgentSpawnerError { static spawningDisabled() { return new DextoRuntimeError( AgentSpawnerErrorCode.SPAWNING_DISABLED, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', 'Agent spawning is disabled in configuration', {}, 'Enable spawning in the agent-spawner tool configuration' @@ -19,8 +19,8 @@ export class AgentSpawnerError { static spawnFailed(cause: string) { return new DextoRuntimeError( AgentSpawnerErrorCode.SPAWN_FAILED, - ErrorScope.TOOLS, - ErrorType.SYSTEM, + 'tools', + 'system', `Failed to spawn sub-agent: ${cause}`, { cause }, 'Check the configuration and try again' @@ -30,8 +30,8 @@ export class AgentSpawnerError { static agentNotFound(agentId: string) { return new DextoRuntimeError( AgentSpawnerErrorCode.AGENT_NOT_FOUND, - ErrorScope.TOOLS, - ErrorType.NOT_FOUND, + 'tools', + 'not_found', `Sub-agent '${agentId}' not found`, { agentId }, 'Ensure the agent ID is correct and the agent is still active' @@ -41,8 +41,8 @@ export class AgentSpawnerError { static taskFailed(agentId: string, cause: string) { return new DextoRuntimeError( AgentSpawnerErrorCode.TASK_FAILED, - ErrorScope.TOOLS, - ErrorType.SYSTEM, + 'tools', + 'system', `Task execution failed for agent '${agentId}': ${cause}`, { agentId, cause }, 'Check the task requirements and try again' @@ -52,8 +52,8 @@ export class AgentSpawnerError { static invalidConfig(message: string) { return new DextoRuntimeError( AgentSpawnerErrorCode.INVALID_CONFIG, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `Invalid agent spawner configuration: ${message}`, {}, 'Check the configuration and ensure all required fields are provided' diff --git a/packages/agent-management/src/tool-factories/agent-spawner/runtime.ts b/packages/agent-management/src/tool-factories/agent-spawner/runtime.ts index 6b947b9a5..da64ac40d 100644 --- a/packages/agent-management/src/tool-factories/agent-spawner/runtime.ts +++ b/packages/agent-management/src/tool-factories/agent-spawner/runtime.ts @@ -14,12 +14,7 @@ import { randomUUID } from 'crypto'; import type { AgentConfig } from '@dexto/agent-config'; import type { DextoAgent, Logger, TaskForker } from '@dexto/core'; -import { - DextoRuntimeError, - ErrorType, - getReasoningProfile, - supportsReasoningVariant, -} from '@dexto/core'; +import { DextoRuntimeError, getReasoningProfile, supportsReasoningVariant } from '@dexto/core'; import { AgentRuntime } from '../../runtime/AgentRuntime.js'; import { createDelegatingApprovalHandler } from '../../runtime/approval-delegation.js'; import { loadAgentConfig } from '../../config/loader.js'; @@ -634,7 +629,7 @@ export class AgentSpawnerRuntime implements TaskForker { if (error.scope === 'llm') return true; // Payment / quota style errors should trigger fallback - if (error.type === ErrorType.PAYMENT_REQUIRED || error.type === ErrorType.FORBIDDEN) { + if (error.type === 'payment_required' || error.type === 'forbidden') { return true; } } diff --git a/packages/agent-management/src/writer.test.ts b/packages/agent-management/src/writer.test.ts index 751c1833f..6ee46f096 100644 --- a/packages/agent-management/src/writer.test.ts +++ b/packages/agent-management/src/writer.test.ts @@ -9,7 +9,6 @@ import { type LLMOverrides, } from './writer.js'; import { type AgentConfig } from '@dexto/agent-config'; -import { ErrorScope, ErrorType } from '@dexto/core'; import { ConfigErrorCode } from './config/index.js'; import { type GlobalPreferences } from './preferences/schemas.js'; @@ -115,8 +114,8 @@ describe('Config Writer', () => { await expect(writeConfigFile(invalidPath, sampleConfig)).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.FILE_WRITE_ERROR, - scope: ErrorScope.CONFIG, - type: ErrorType.SYSTEM, + scope: 'config', + type: 'system', }) ); }); @@ -227,8 +226,8 @@ describe('Config Writer', () => { await expect(writeLLMPreferences(nonExistentPath, samplePreferences)).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.FILE_READ_ERROR, - scope: ErrorScope.CONFIG, - type: ErrorType.SYSTEM, + scope: 'config', + type: 'system', }) ); }); @@ -240,8 +239,8 @@ describe('Config Writer', () => { await expect(writeLLMPreferences(tempConfigPath, samplePreferences)).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.PARSE_ERROR, - scope: ErrorScope.CONFIG, - type: ErrorType.USER, + scope: 'config', + type: 'user', }) ); }); @@ -371,8 +370,8 @@ describe('Config Writer', () => { ).rejects.toThrow( expect.objectContaining({ code: ConfigErrorCode.FILE_READ_ERROR, - scope: ErrorScope.CONFIG, - type: ErrorType.SYSTEM, + scope: 'config', + type: 'system', }) ); }); diff --git a/packages/core/src/agent/DextoAgent.lifecycle.test.ts b/packages/core/src/agent/DextoAgent.lifecycle.test.ts index 39055664e..beed6b40a 100644 --- a/packages/core/src/agent/DextoAgent.lifecycle.test.ts +++ b/packages/core/src/agent/DextoAgent.lifecycle.test.ts @@ -11,7 +11,6 @@ import { PromptsSchema } from '../prompts/schemas.js'; import { ServersConfigSchema } from '../mcp/schemas.js'; import type { AgentServices } from '../utils/service-initializer.js'; import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { AgentErrorCode } from './error-codes.js'; import { createLogger } from '../logger/factory.js'; import { @@ -175,8 +174,8 @@ describe('DextoAgent Lifecycle Management', () => { Promise.resolve(Reflect.apply(agent.getSystemPrompt, agent, [''])) ).rejects.toMatchObject({ code: AgentErrorCode.API_VALIDATION_ERROR, - scope: ErrorScope.AGENT, - type: ErrorType.USER, + scope: 'agent', + type: 'user', }); expect(mockServices.toolManager.buildContributorContext).not.toHaveBeenCalled(); }); @@ -234,8 +233,8 @@ describe('DextoAgent Lifecycle Management', () => { await expect(agent.start()).rejects.toThrow( expect.objectContaining({ code: AgentErrorCode.ALREADY_STARTED, - scope: ErrorScope.AGENT, - type: ErrorType.USER, + scope: 'agent', + type: 'user', }) ); }); @@ -269,8 +268,8 @@ describe('DextoAgent Lifecycle Management', () => { await expect(agent.stop()).rejects.toThrow( expect.objectContaining({ code: AgentErrorCode.NOT_STARTED, - scope: ErrorScope.AGENT, - type: ErrorType.USER, + scope: 'agent', + type: 'user', }) ); }); @@ -337,8 +336,8 @@ describe('DextoAgent Lifecycle Management', () => { expect(thrownError).toBeDefined(); expect(thrownError).toMatchObject({ code: AgentErrorCode.NOT_STARTED, - scope: ErrorScope.AGENT, - type: ErrorType.USER, + scope: 'agent', + type: 'user', }); }); @@ -358,8 +357,8 @@ describe('DextoAgent Lifecycle Management', () => { expect(thrownError).toBeDefined(); expect(thrownError).toMatchObject({ code: AgentErrorCode.STOPPED, - scope: ErrorScope.AGENT, - type: ErrorType.USER, + scope: 'agent', + type: 'user', }); }); diff --git a/packages/core/src/agent/errors.ts b/packages/core/src/agent/errors.ts index de382396d..1b2246e79 100644 --- a/packages/core/src/agent/errors.ts +++ b/packages/core/src/agent/errors.ts @@ -1,5 +1,4 @@ import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { AgentErrorCode } from './error-codes.js'; /** @@ -14,8 +13,8 @@ export class AgentError { static notStarted() { return new DextoRuntimeError( AgentErrorCode.NOT_STARTED, - ErrorScope.AGENT, - ErrorType.USER, + 'agent', + 'user', 'Agent must be started before use', undefined, 'Call agent.start() before using other methods' @@ -28,8 +27,8 @@ export class AgentError { static alreadyStarted() { return new DextoRuntimeError( AgentErrorCode.ALREADY_STARTED, - ErrorScope.AGENT, - ErrorType.USER, + 'agent', + 'user', 'Agent is already started', undefined, 'Call agent.stop() before starting again' @@ -42,8 +41,8 @@ export class AgentError { static stopped() { return new DextoRuntimeError( AgentErrorCode.STOPPED, - ErrorScope.AGENT, - ErrorType.USER, + 'agent', + 'user', 'Agent has been stopped and cannot be used', undefined, 'Create a new agent instance or restart this one' @@ -56,8 +55,8 @@ export class AgentError { static switchInProgress() { return new DextoRuntimeError( AgentErrorCode.SWITCH_IN_PROGRESS, - ErrorScope.AGENT, - ErrorType.CONFLICT, + 'agent', + 'conflict', 'Agent switch already in progress', undefined, 'Wait for the current switch operation to complete before starting a new one' @@ -70,8 +69,8 @@ export class AgentError { static initializationFailed(reason: string, details?: unknown) { return new DextoRuntimeError( AgentErrorCode.INITIALIZATION_FAILED, - ErrorScope.AGENT, - ErrorType.SYSTEM, + 'agent', + 'system', `Agent initialization failed: ${reason}`, details, 'Check logs for initialization errors' @@ -84,8 +83,8 @@ export class AgentError { static noConfigPath() { return new DextoRuntimeError( AgentErrorCode.NO_CONFIG_PATH, - ErrorScope.AGENT, - ErrorType.SYSTEM, + 'agent', + 'system', 'No configuration file path is available', undefined, 'Agent was created without a config file path, cannot perform file operations' @@ -98,8 +97,8 @@ export class AgentError { static apiValidationError(message: string, details?: unknown) { return new DextoRuntimeError( AgentErrorCode.API_VALIDATION_ERROR, - ErrorScope.AGENT, - ErrorType.USER, + 'agent', + 'user', message, details, 'Check the request parameters and try again' @@ -112,8 +111,8 @@ export class AgentError { static streamFailed(message: string, details?: unknown) { return new DextoRuntimeError( AgentErrorCode.STREAM_FAILED, - ErrorScope.AGENT, - ErrorType.SYSTEM, + 'agent', + 'system', message, details, 'Check logs for details' diff --git a/packages/core/src/approval/errors.ts b/packages/core/src/approval/errors.ts index afccf70fa..3709e85a2 100644 --- a/packages/core/src/approval/errors.ts +++ b/packages/core/src/approval/errors.ts @@ -1,4 +1,4 @@ -import { DextoRuntimeError, ErrorScope, ErrorType } from '../errors/index.js'; +import { DextoRuntimeError } from '../errors/index.js'; import { ApprovalErrorCode } from './error-codes.js'; import type { ApprovalType, DenialReason } from './types.js'; @@ -53,8 +53,8 @@ export class ApprovalError { ): DextoRuntimeError { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_INVALID_REQUEST, - ErrorScope.TOOLS, // Approvals are part of tool execution flow - ErrorType.USER, + 'tools', // Approvals are part of tool execution flow + 'user', `Invalid approval request: ${reason}`, context, ['Check the approval request structure', 'Ensure all required fields are provided'] @@ -70,8 +70,8 @@ export class ApprovalError { ): DextoRuntimeError { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_INVALID_RESPONSE, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `Invalid approval response: ${reason}`, context, [ @@ -91,8 +91,8 @@ export class ApprovalError { ): DextoRuntimeError { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_INVALID_METADATA, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `Invalid metadata for ${type}: ${reason}`, { type, reason }, ['Check the metadata structure for this approval type'] @@ -105,8 +105,8 @@ export class ApprovalError { static invalidSchema(reason: string): DextoRuntimeError { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_INVALID_SCHEMA, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `Invalid elicitation schema: ${reason}`, { reason }, ['Ensure the schema is a valid JSON Schema', 'Check MCP server implementation'] @@ -134,8 +134,8 @@ export class ApprovalError { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_TIMEOUT, - ErrorScope.TOOLS, - ErrorType.TIMEOUT, + 'tools', + 'timeout', `Approval request timed out after ${timeout}ms`, context, [ @@ -169,8 +169,8 @@ export class ApprovalError { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_CANCELLED, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', message, context ); @@ -192,8 +192,8 @@ export class ApprovalError { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_CANCELLED_ALL, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', message, context ); @@ -205,8 +205,8 @@ export class ApprovalError { static providerNotConfigured(): DextoRuntimeError> { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_PROVIDER_NOT_CONFIGURED, - ErrorScope.TOOLS, - ErrorType.SYSTEM, + 'tools', + 'system', 'Approval provider not configured', {}, [ @@ -228,8 +228,8 @@ export class ApprovalError { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_PROVIDER_ERROR, - ErrorScope.TOOLS, - ErrorType.SYSTEM, + 'tools', + 'system', `Approval provider error: ${message}`, context, ['Check approval provider implementation', 'Review system logs for details'] @@ -242,8 +242,8 @@ export class ApprovalError { static notFound(approvalId: string): DextoRuntimeError<{ approvalId: string }> { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_NOT_FOUND, - ErrorScope.TOOLS, - ErrorType.NOT_FOUND, + 'tools', + 'not_found', `Approval request not found: ${approvalId}`, { approvalId }, [ @@ -301,8 +301,8 @@ export class ApprovalError { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_TOOL_APPROVAL_DENIED, - ErrorScope.TOOLS, - ErrorType.FORBIDDEN, + 'tools', + 'forbidden', message, context, suggestions @@ -383,8 +383,8 @@ export class ApprovalError { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_ELICITATION_DENIED, - ErrorScope.TOOLS, - ErrorType.FORBIDDEN, + 'tools', + 'forbidden', message, context, suggestions @@ -401,8 +401,8 @@ export class ApprovalError { ): DextoRuntimeError { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_ELICITATION_VALIDATION_FAILED, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `Elicitation form validation failed: ${errors.join(', ')}`, { approvalId, serverName, errors }, ['Check the form inputs match the schema requirements', 'Review validation errors'] @@ -415,8 +415,8 @@ export class ApprovalError { static invalidConfig(reason: string): DextoRuntimeError<{ reason: string }> { return new DextoRuntimeError( ApprovalErrorCode.APPROVAL_CONFIG_INVALID, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `Invalid approval configuration: ${reason}`, { reason }, ['Check approval configuration in agent.yml', 'Review approval.mode and related fields'] diff --git a/packages/core/src/context/errors.ts b/packages/core/src/context/errors.ts index 36537b423..6880972a9 100644 --- a/packages/core/src/context/errors.ts +++ b/packages/core/src/context/errors.ts @@ -1,5 +1,4 @@ import { DextoRuntimeError } from '../errors/index.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { ContextErrorCode } from './error-codes.js'; /** @@ -11,8 +10,8 @@ export class ContextError { static messageRoleMissing() { return new DextoRuntimeError( ContextErrorCode.MESSAGE_ROLE_MISSING, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', 'Message must have a role', {}, 'Ensure all messages have a valid role field' @@ -22,8 +21,8 @@ export class ContextError { static userMessageContentInvalid() { return new DextoRuntimeError( ContextErrorCode.USER_MESSAGE_CONTENT_INVALID, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', 'User message content should be a non-empty string or a non-empty array of parts', {}, 'Provide valid content for user messages' @@ -33,8 +32,8 @@ export class ContextError { static assistantMessageContentOrToolsRequired() { return new DextoRuntimeError( ContextErrorCode.ASSISTANT_MESSAGE_CONTENT_OR_TOOLS_REQUIRED, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', 'Assistant message must have content or toolCalls', {}, 'Provide either content or toolCalls for assistant messages' @@ -44,8 +43,8 @@ export class ContextError { static assistantMessageToolCallsInvalid() { return new DextoRuntimeError( ContextErrorCode.ASSISTANT_MESSAGE_TOOL_CALLS_INVALID, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', 'Invalid toolCalls structure in assistant message', {}, 'Ensure toolCalls have proper structure with function name and arguments' @@ -55,8 +54,8 @@ export class ContextError { static toolMessageFieldsMissing() { return new DextoRuntimeError( ContextErrorCode.TOOL_MESSAGE_FIELDS_MISSING, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', 'Tool message missing required fields (toolCallId, name, content)', {}, 'Ensure tool messages have toolCallId, name, and content fields' @@ -66,8 +65,8 @@ export class ContextError { static systemMessageContentInvalid() { return new DextoRuntimeError( ContextErrorCode.SYSTEM_MESSAGE_CONTENT_INVALID, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', 'System message content must be a non-empty string', {}, 'Provide valid string content for system messages' @@ -77,8 +76,8 @@ export class ContextError { static userMessageContentEmpty() { return new DextoRuntimeError( ContextErrorCode.MESSAGE_CONTENT_EMPTY, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', 'Content must be a non-empty string or have imageData/fileData', {}, 'Provide non-empty content or attach image/file data' @@ -88,8 +87,8 @@ export class ContextError { static toolCallIdNameRequired() { return new DextoRuntimeError( ContextErrorCode.TOOL_CALL_ID_NAME_REQUIRED, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', 'toolCallId and name are required', {}, 'Provide both toolCallId and name for tool results' @@ -103,8 +102,8 @@ export class ContextError { static preserveValuesNegative() { return new DextoRuntimeError( ContextErrorCode.PRESERVE_VALUES_NEGATIVE, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', 'preserveStart and preserveEnd must be non-negative', {}, 'Set preserveStart and preserveEnd to zero or positive values' @@ -114,8 +113,8 @@ export class ContextError { static tokenCountFailed(cause: string) { return new DextoRuntimeError( ContextErrorCode.TOKEN_COUNT_FAILED, - ErrorScope.CONTEXT, - ErrorType.SYSTEM, + 'context', + 'system', `Failed to count tokens: ${cause}`, { cause }, 'Check tokenizer implementation and message content structure' @@ -125,8 +124,8 @@ export class ContextError { static minMessagesNegative() { return new DextoRuntimeError( ContextErrorCode.MIN_MESSAGES_NEGATIVE, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', 'minMessagesToKeep must be non-negative', {}, 'Set minMessagesToKeep to zero or positive value' @@ -136,8 +135,8 @@ export class ContextError { static compactionInvalidType(type: string, available: string[]) { return new DextoRuntimeError( ContextErrorCode.COMPACTION_INVALID_TYPE, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', `Unknown compaction provider type: '${type}'`, { type, available }, `Use one of the available types: ${available.join(', ')}` @@ -147,8 +146,8 @@ export class ContextError { static compactionValidation(type: string, errors: unknown) { return new DextoRuntimeError( ContextErrorCode.COMPACTION_VALIDATION, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', `Invalid configuration for compaction provider '${type}'`, { type, errors }, 'Check the configuration schema for this provider' @@ -158,8 +157,8 @@ export class ContextError { static compactionMissingLLM(type: string) { return new DextoRuntimeError( ContextErrorCode.COMPACTION_MISSING_LLM, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', `Compaction provider '${type}' requires LLM service but none provided`, { type }, 'Ensure LLM service is initialized before creating this compaction provider' @@ -169,8 +168,8 @@ export class ContextError { static compactionProviderAlreadyRegistered(type: string) { return new DextoRuntimeError( ContextErrorCode.COMPACTION_PROVIDER_ALREADY_REGISTERED, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', `Compaction provider '${type}' is already registered`, { type }, 'Each provider type can only be registered once' @@ -181,8 +180,8 @@ export class ContextError { static messageNotFound(messageId: string) { return new DextoRuntimeError( ContextErrorCode.MESSAGE_NOT_FOUND, - ErrorScope.CONTEXT, - ErrorType.NOT_FOUND, + 'context', + 'not_found', `Message with ID ${messageId} not found`, { messageId } ); @@ -191,8 +190,8 @@ export class ContextError { static messageNotAssistant(messageId: string) { return new DextoRuntimeError( ContextErrorCode.MESSAGE_NOT_ASSISTANT, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', `Message with ID ${messageId} is not an assistant message`, { messageId } ); @@ -201,8 +200,8 @@ export class ContextError { static assistantContentNotString() { return new DextoRuntimeError( ContextErrorCode.ASSISTANT_CONTENT_NOT_STRING, - ErrorScope.CONTEXT, - ErrorType.USER, + 'context', + 'user', 'Cannot append text to non-string assistant message content', {} ); diff --git a/packages/core/src/errors/DextoRuntimeError.ts b/packages/core/src/errors/DextoRuntimeError.ts index d3b38d9bf..a69d1b79a 100644 --- a/packages/core/src/errors/DextoRuntimeError.ts +++ b/packages/core/src/errors/DextoRuntimeError.ts @@ -1,6 +1,6 @@ import { DextoBaseError } from './DextoBaseError.js'; -import { ErrorScope } from './types.js'; -import { ErrorType } from './types.js'; +import type { ErrorScope } from './types.js'; +import type { ErrorType } from './types.js'; import type { DextoErrorCode } from './types.js'; /** diff --git a/packages/core/src/errors/index.ts b/packages/core/src/errors/index.ts index ff5ba062f..705708326 100644 --- a/packages/core/src/errors/index.ts +++ b/packages/core/src/errors/index.ts @@ -6,6 +6,6 @@ export { DextoBaseError } from './DextoBaseError.js'; export { DextoRuntimeError } from './DextoRuntimeError.js'; export { DextoValidationError } from './DextoValidationError.js'; -export { ErrorScope, ErrorType } from './types.js'; -export type { Issue, Severity, DextoErrorCode } from './types.js'; +export { ERROR_SCOPES, ERROR_TYPES } from './types.js'; +export type { ErrorScope, ErrorType, Issue, Severity, DextoErrorCode } from './types.js'; export { ensureOk } from './result-bridge.js'; diff --git a/packages/core/src/errors/types.ts b/packages/core/src/errors/types.ts index cff8b856c..7b3509b24 100644 --- a/packages/core/src/errors/types.ts +++ b/packages/core/src/errors/types.ts @@ -19,40 +19,44 @@ import type { TelemetryErrorCode } from '../telemetry/error-codes.js'; * Error scopes representing functional domains in the system * Each scope owns its validation and error logic */ -export enum ErrorScope { - LLM = 'llm', // LLM operations, model compatibility, input validation for LLMs - AGENT = 'agent', // Agent lifecycle, configuration - CONFIG = 'config', // Configuration file operations, parsing, validation - CONTEXT = 'context', // Context management, message validation, token processing - SESSION = 'session', // Session lifecycle, management, and state - MCP = 'mcp', // MCP server connections and protocol - TOOLS = 'tools', // Tool execution and authorization - STORAGE = 'storage', // Storage backend operations - LOGGER = 'logger', // Logging system operations, transports, and configuration - SYSTEM_PROMPT = 'system_prompt', // System prompt contributors and file processing - RESOURCE = 'resource', // Resource management (MCP/internal) discovery and access - PROMPT = 'prompt', // Prompt management, resolution, and providers - MEMORY = 'memory', // Memory management and storage - HOOK = 'hook', // Hook loading, validation, and execution - TELEMETRY = 'telemetry', // Telemetry initialization and export operations -} +export const ERROR_SCOPES = [ + 'llm', + 'agent', + 'config', + 'context', + 'session', + 'mcp', + 'tools', + 'storage', + 'logger', + 'system_prompt', + 'resource', + 'prompt', + 'memory', + 'hook', + 'telemetry', +] as const; + +export type ErrorScope = (typeof ERROR_SCOPES)[number]; /** * Error types that map directly to HTTP status codes * Each type represents the nature of the error */ -export enum ErrorType { - USER = 'user', // 400 - bad input, config errors, validation failures - PAYMENT_REQUIRED = 'payment_required', // 402 - insufficient credits, billing issue - FORBIDDEN = 'forbidden', // 403 - permission denied, unauthorized - NOT_FOUND = 'not_found', // 404 - resource doesn't exist (session, file, etc.) - TIMEOUT = 'timeout', // 408 - operation timed out - CONFLICT = 'conflict', // 409 - resource conflict, concurrent operation - RATE_LIMIT = 'rate_limit', // 429 - too many requests - SYSTEM = 'system', // 500 - bugs, internal failures, unexpected states - THIRD_PARTY = 'third_party', // 502 - upstream provider failures, API errors - UNKNOWN = 'unknown', // 500 - unclassified errors, fallback -} +export const ERROR_TYPES = [ + 'user', + 'payment_required', + 'forbidden', + 'not_found', + 'timeout', + 'conflict', + 'rate_limit', + 'system', + 'third_party', + 'unknown', +] as const; + +export type ErrorType = (typeof ERROR_TYPES)[number]; /** * Union type for all error codes across domains diff --git a/packages/core/src/hooks/manager.ts b/packages/core/src/hooks/manager.ts index a464e012a..fb67c13c0 100644 --- a/packages/core/src/hooks/manager.ts +++ b/packages/core/src/hooks/manager.ts @@ -1,4 +1,4 @@ -import { DextoRuntimeError, ErrorScope, ErrorType } from '../errors/index.js'; +import { DextoRuntimeError } from '../errors/index.js'; import { HookErrorCode } from './error-codes.js'; import { getContext } from '../utils/async-context.js'; import type { ExtensionPoint, HookExecutionContext, Hook, HookResult } from './types.js'; @@ -67,8 +67,8 @@ export class HookManager { if (this.initialized) { throw new DextoRuntimeError( HookErrorCode.HOOK_CONFIGURATION_INVALID, - ErrorScope.HOOK, - ErrorType.SYSTEM, + 'hook', + 'system', 'Cannot set hooks after initialization' ); } @@ -90,8 +90,8 @@ export class HookManager { if (this.initialized) { throw new DextoRuntimeError( HookErrorCode.HOOK_CONFIGURATION_INVALID, - ErrorScope.HOOK, - ErrorType.SYSTEM, + 'hook', + 'system', 'HookManager already initialized' ); } @@ -157,8 +157,8 @@ export class HookManager { if (payload === null || typeof payload !== 'object') { throw new DextoRuntimeError( HookErrorCode.HOOK_INVALID_SHAPE, - ErrorScope.HOOK, - ErrorType.USER, + 'hook', + 'user', `Payload for ${extensionPoint} must be an object (got ${payload === null ? 'null' : typeof payload})`, { extensionPoint, payloadType: typeof payload } ); @@ -241,8 +241,8 @@ export class HookManager { if (result.cancel) { throw new DextoRuntimeError( HookErrorCode.HOOK_BLOCKED_EXECUTION, - ErrorScope.HOOK, - ErrorType.FORBIDDEN, + 'hook', + 'forbidden', result.message || `Operation blocked by hook '${hookName}'`, { hook: hookName, @@ -270,8 +270,8 @@ export class HookManager { if (result.cancel) { throw new DextoRuntimeError( HookErrorCode.HOOK_BLOCKED_EXECUTION, - ErrorScope.HOOK, - ErrorType.FORBIDDEN, + 'hook', + 'forbidden', result.message || `Operation cancelled by hook '${hookName}'`, { hook: hookName, @@ -296,8 +296,8 @@ export class HookManager { throw new DextoRuntimeError( HookErrorCode.HOOK_EXECUTION_FAILED, - ErrorScope.HOOK, - ErrorType.SYSTEM, + 'hook', + 'system', `Hook '${hookName}' failed: ${ error instanceof Error ? error.message : String(error) }`, @@ -327,8 +327,8 @@ export class HookManager { reject( new DextoRuntimeError( HookErrorCode.HOOK_EXECUTION_TIMEOUT, - ErrorScope.HOOK, - ErrorType.TIMEOUT, + 'hook', + 'timeout', `Hook '${hookName}' execution timed out after ${ms}ms` ) ); @@ -430,8 +430,8 @@ export class HookManager { if (!hasExtensionPoint) { throw new DextoRuntimeError( HookErrorCode.HOOK_INVALID_SHAPE, - ErrorScope.HOOK, - ErrorType.USER, + 'hook', + 'user', `Hook '${this.deriveHookName(hook, index)}' must implement at least one extension point method`, { availableExtensionPoints: extensionPoints } ); diff --git a/packages/core/src/index.browser.ts b/packages/core/src/index.browser.ts index 2584a517e..170d3313c 100644 --- a/packages/core/src/index.browser.ts +++ b/packages/core/src/index.browser.ts @@ -5,10 +5,10 @@ export { toError } from './utils/error-conversion.js'; // Used by webui package export { zodToIssues } from './utils/result.js'; // Used by client-sdk package export { EnvExpandedString } from './utils/result.js'; // Used by @dexto/storage schemas in browser bundles -export { ErrorScope, ErrorType } from './errors/types.js'; // Used by client-sdk package +export { ERROR_SCOPES, ERROR_TYPES } from './errors/types.js'; // Used by client-sdk package // Type-only exports (used as types, no runtime overhead) -export type { Issue, Severity, DextoErrorCode } from './errors/types.js'; +export type { ErrorScope, ErrorType, Issue, Severity, DextoErrorCode } from './errors/types.js'; // Context/message types (used by webui package) export type { @@ -56,7 +56,8 @@ export { } from './mcp/schemas.js'; // Storage errors (used by @dexto/storage schemas in browser bundles) -export { StorageErrorCode } from './storage/error-codes.js'; +export { STORAGE_ERROR_CODES } from './storage/error-codes.js'; +export type { StorageErrorCode } from './storage/error-codes.js'; // Tool permissions types and constants (used by webui) export type { PermissionsMode, AllowedToolsStorageType } from './tools/schemas.js'; diff --git a/packages/core/src/llm/errors.ts b/packages/core/src/llm/errors.ts index 94b3be3ce..db62ccdc8 100644 --- a/packages/core/src/llm/errors.ts +++ b/packages/core/src/llm/errors.ts @@ -1,6 +1,4 @@ import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; -import { ErrorScope } from '../errors/types.js'; -import { ErrorType } from '../errors/types.js'; import { LLMErrorCode } from './error-codes.js'; // Use types solely from types.ts to avoid duplication import { getSupportedProviders } from './registry/index.js'; @@ -18,8 +16,8 @@ export class LLMError { static unknownModel(provider: LLMProvider, model: string) { return new DextoRuntimeError( LLMErrorCode.MODEL_UNKNOWN, - ErrorScope.LLM, - ErrorType.USER, + 'llm', + 'user', `Unknown model '${model}' for provider '${provider}'`, { provider, model } ); @@ -28,8 +26,8 @@ export class LLMError { static baseUrlMissing(provider: LLMProvider) { return new DextoRuntimeError( LLMErrorCode.BASE_URL_MISSING, - ErrorScope.LLM, - ErrorType.USER, + 'llm', + 'user', `Provider '${provider}' requires a baseURL (set config.baseURL or OPENAI_BASE_URL environment variable)`, { provider } ); @@ -38,8 +36,8 @@ export class LLMError { static missingConfig(provider: LLMProvider, configName: string) { return new DextoRuntimeError( LLMErrorCode.CONFIG_MISSING, - ErrorScope.LLM, - ErrorType.USER, + 'llm', + 'user', `Provider '${provider}' requires ${configName}`, { provider, configName } ); @@ -49,8 +47,8 @@ export class LLMError { const availableProviders = getSupportedProviders(); return new DextoRuntimeError( LLMErrorCode.PROVIDER_UNSUPPORTED, - ErrorScope.LLM, - ErrorType.USER, + 'llm', + 'user', `Provider '${provider}' is not supported. Available providers: ${availableProviders.join(', ')}`, { provider, availableProviders } ); @@ -64,8 +62,8 @@ export class LLMError { static apiKeyMissing(provider: LLMProvider, envVar: string) { return new DextoRuntimeError( LLMErrorCode.API_KEY_MISSING, - ErrorScope.LLM, - ErrorType.USER, + 'llm', + 'user', `API key required for provider '${provider}'`, { provider, envVar }, `Set the ${envVar} environment variable or configure it in Settings` @@ -76,8 +74,8 @@ export class LLMError { const availableProviders = getSupportedProviders(); return new DextoRuntimeError( LLMErrorCode.MODEL_UNKNOWN, - ErrorScope.LLM, - ErrorType.USER, + 'llm', + 'user', `Unknown model '${model}' - could not infer provider. Available providers: ${availableProviders.join(', ')}`, { model, availableProviders }, 'Specify the provider explicitly or use a recognized model name' @@ -89,8 +87,8 @@ export class LLMError { static rateLimitExceeded(provider: LLMProvider, retryAfter?: number) { return new DextoRuntimeError( LLMErrorCode.RATE_LIMIT_EXCEEDED, - ErrorScope.LLM, - ErrorType.RATE_LIMIT, + 'llm', + 'rate_limit', `Rate limit exceeded for ${provider}`, { details: { provider, retryAfter }, @@ -109,8 +107,8 @@ export class LLMError { const balanceStr = balance !== undefined ? `$${balance.toFixed(2)}` : 'low'; return new DextoRuntimeError( LLMErrorCode.INSUFFICIENT_CREDITS, - ErrorScope.LLM, - ErrorType.FORBIDDEN, + 'llm', + 'forbidden', `Insufficient Dexto credits. Balance: ${balanceStr}`, { balance }, 'Run `dexto billing` to check your balance' @@ -121,8 +119,8 @@ export class LLMError { static generationFailed(error: string, provider: LLMProvider, model: string) { return new DextoRuntimeError( LLMErrorCode.GENERATION_FAILED, - ErrorScope.LLM, - ErrorType.THIRD_PARTY, + 'llm', + 'third_party', `Generation failed: ${error}`, { details: { error, provider, model } } ); @@ -132,8 +130,8 @@ export class LLMError { static switchInputMissing() { return new DextoRuntimeError( LLMErrorCode.SWITCH_INPUT_MISSING, - ErrorScope.LLM, - ErrorType.USER, + 'llm', + 'user', 'At least model or provider must be specified for LLM switch', {}, 'Provide either a model name, provider, or both' diff --git a/packages/core/src/llm/executor/turn-executor.ts b/packages/core/src/llm/executor/turn-executor.ts index 1e9f905b9..9dd552a23 100644 --- a/packages/core/src/llm/executor/turn-executor.ts +++ b/packages/core/src/llm/executor/turn-executor.ts @@ -35,7 +35,6 @@ import type { StreamProcessorConfig } from './stream-processor.js'; import type { CoalescedMessage } from '../../session/types.js'; import { defer } from '../../utils/defer.js'; import { DextoRuntimeError } from '../../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../../errors/types.js'; import { LLMErrorCode } from '../error-codes.js'; import { toError } from '../../utils/error-conversion.js'; import type { CompactionStrategy } from '../../context/compaction/types.js'; @@ -1198,8 +1197,8 @@ export class TurnExecutor { } return new DextoRuntimeError( LLMErrorCode.INSUFFICIENT_CREDITS, - ErrorScope.LLM, - ErrorType.PAYMENT_REQUIRED, + 'llm', + 'payment_required', `Insufficient Dexto credits${balance !== undefined ? `. Balance: $${balance.toFixed(2)}` : ''}`, { sessionId: this.sessionId, @@ -1215,8 +1214,8 @@ export class TurnExecutor { if (status === 429) { return new DextoRuntimeError( LLMErrorCode.RATE_LIMIT_EXCEEDED, - ErrorScope.LLM, - ErrorType.RATE_LIMIT, + 'llm', + 'rate_limit', `Rate limit exceeded${body ? ` - ${body}` : ''}`, { sessionId: this.sessionId, @@ -1231,8 +1230,8 @@ export class TurnExecutor { if (status === 408) { return new DextoRuntimeError( LLMErrorCode.GENERATION_FAILED, - ErrorScope.LLM, - ErrorType.TIMEOUT, + 'llm', + 'timeout', `Provider timed out${body ? ` - ${body}` : ''}`, { sessionId: this.sessionId, @@ -1245,8 +1244,8 @@ export class TurnExecutor { } return new DextoRuntimeError( LLMErrorCode.GENERATION_FAILED, - ErrorScope.LLM, - ErrorType.THIRD_PARTY, + 'llm', + 'third_party', `Provider error ${status}${body ? ` - ${body}` : ''}`, { sessionId: this.sessionId, diff --git a/packages/core/src/llm/providers/codex-app-server.ts b/packages/core/src/llm/providers/codex-app-server.ts index 723f0b7f0..15b6336ad 100644 --- a/packages/core/src/llm/providers/codex-app-server.ts +++ b/packages/core/src/llm/providers/codex-app-server.ts @@ -15,7 +15,7 @@ import type { LanguageModelV2Usage, } from '@ai-sdk/provider'; import { DextoRuntimeError } from '../../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../../errors/types.js'; +import type { ErrorType } from '../../errors/types.js'; import { getDextoGlobalPath } from '../../utils/path.js'; import { safeStringify } from '../../utils/safe-stringify.js'; import { LLMErrorCode } from '../error-codes.js'; @@ -135,27 +135,15 @@ function createCodexProtocolError( message: string, context?: Record ): DextoRuntimeError | undefined> { - return new DextoRuntimeError( - CODEX_PROTOCOL_ERROR_CODE, - ErrorScope.LLM, - ErrorType.THIRD_PARTY, - message, - context - ); + return new DextoRuntimeError(CODEX_PROTOCOL_ERROR_CODE, 'llm', 'third_party', message, context); } function createCodexClientRuntimeError( message: string, context?: Record, - type: ErrorType = ErrorType.SYSTEM + type: ErrorType = 'system' ): DextoRuntimeError | undefined> { - return new DextoRuntimeError( - CODEX_CLIENT_RUNTIME_ERROR_CODE, - ErrorScope.LLM, - type, - message, - context - ); + return new DextoRuntimeError(CODEX_CLIENT_RUNTIME_ERROR_CODE, 'llm', type, message, context); } function createCodexClientExitedError(input: { @@ -168,7 +156,7 @@ function createCodexClientExitedError(input: { ...(input.code !== undefined ? { code: input.code } : {}), ...(input.signal !== undefined ? { signal: input.signal } : {}), }, - ErrorType.THIRD_PARTY + 'third_party' ); } @@ -636,8 +624,8 @@ function toChatGPTUsageLimitError( return new DextoRuntimeError( LLMErrorCode.RATE_LIMIT_EXCEEDED, - ErrorScope.LLM, - ErrorType.RATE_LIMIT, + 'llm', + 'rate_limit', message, { provider: 'openai-compatible', @@ -1183,7 +1171,7 @@ export class CodexAppServerClient { createCodexClientRuntimeError( `Timed out waiting for Codex notification: ${method}`, { method }, - ErrorType.TIMEOUT + 'timeout' ) ); }, timeoutMs); @@ -1197,7 +1185,7 @@ export class CodexAppServerClient { : createCodexClientRuntimeError( 'Codex operation aborted', { method }, - ErrorType.USER + 'user' ) ); }; @@ -1313,9 +1301,7 @@ export class CodexAppServerClient { if (isRecord(payload['error'])) { const message = getString(payload['error']['message']) ?? 'Codex JSON-RPC request failed'; - pending.reject( - createCodexClientRuntimeError(message, { id }, ErrorType.THIRD_PARTY) - ); + pending.reject(createCodexClientRuntimeError(message, { id }, 'third_party')); return; } @@ -1359,7 +1345,7 @@ export class CodexAppServerClient { createCodexClientRuntimeError( `Codex request timed out: ${method}`, { method, id }, - ErrorType.TIMEOUT + 'timeout' ) ); }, this.requestTimeoutMs); @@ -1801,7 +1787,7 @@ export function createCodexLanguageModel(options: { : createCodexClientRuntimeError( 'Codex generation aborted', { modelId: options.modelId }, - ErrorType.USER + 'user' ) ); }; diff --git a/packages/core/src/llm/providers/local/errors.ts b/packages/core/src/llm/providers/local/errors.ts index 723c5575f..536f7869a 100644 --- a/packages/core/src/llm/providers/local/errors.ts +++ b/packages/core/src/llm/providers/local/errors.ts @@ -4,7 +4,6 @@ */ import { DextoRuntimeError } from '../../../errors/DextoRuntimeError.js'; -import { ErrorType } from '../../../errors/types.js'; import { LocalModelErrorCode } from './error-codes.js'; const SCOPE = 'local-models'; @@ -18,7 +17,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.NODE_LLAMA_NOT_INSTALLED, SCOPE, - ErrorType.NOT_FOUND, + 'not_found', 'node-llama-cpp is not installed. Run `dexto setup` and select "local" provider to install it.', {}, 'Run `dexto setup` and select "local" provider to install local model support' @@ -29,7 +28,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.NODE_LLAMA_INSTALL_FAILED, SCOPE, - ErrorType.THIRD_PARTY, + 'third_party', `Failed to install node-llama-cpp: ${error}`, { error }, 'Check your Node.js version and try again. CMake may be required for your platform.' @@ -40,7 +39,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.CMAKE_NOT_FOUND, SCOPE, - ErrorType.NOT_FOUND, + 'not_found', 'CMake is required to build node-llama-cpp from source but was not found.', {}, 'Install CMake: brew install cmake (macOS), apt install cmake (Linux), or download from cmake.org (Windows)' @@ -52,7 +51,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.DOWNLOAD_FAILED, SCOPE, - ErrorType.THIRD_PARTY, + 'third_party', `Failed to download model '${modelId}': ${error}`, { modelId, error }, 'Check your internet connection and try again' @@ -63,7 +62,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.DOWNLOAD_INTERRUPTED, SCOPE, - ErrorType.THIRD_PARTY, + 'third_party', `Download of model '${modelId}' was interrupted`, { modelId }, 'Run the download command again to resume' @@ -74,7 +73,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.DOWNLOAD_HASH_MISMATCH, SCOPE, - ErrorType.USER, + 'user', `Downloaded model '${modelId}' has invalid hash. Expected: ${expected}, Got: ${actual}`, { modelId, expected, actual }, 'Delete the file and download again' @@ -87,7 +86,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.INSUFFICIENT_DISK_SPACE, SCOPE, - ErrorType.USER, + 'user', `Insufficient disk space to download '${modelId}'. Required: ${requiredGB}GB, Available: ${availableGB}GB`, { modelId, required, available }, 'Free up disk space or choose a smaller model' @@ -98,7 +97,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.HF_AUTH_REQUIRED, SCOPE, - ErrorType.FORBIDDEN, + 'forbidden', `Model '${modelId}' is a gated model and requires HuggingFace authentication`, { modelId }, 'Set HF_TOKEN environment variable or run `huggingface-cli login`' @@ -110,7 +109,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.MODEL_NOT_FOUND, SCOPE, - ErrorType.NOT_FOUND, + 'not_found', `Model '${modelId}' not found in local model registry`, { modelId }, 'Run `dexto setup` and select "local" to see available models' @@ -121,7 +120,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.MODEL_NOT_DOWNLOADED, SCOPE, - ErrorType.NOT_FOUND, + 'not_found', `Model '${modelId}' is not downloaded. Download it first.`, { modelId }, 'Run `dexto setup` and select "local" to download models' @@ -132,7 +131,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.MODEL_LOAD_FAILED, SCOPE, - ErrorType.THIRD_PARTY, + 'third_party', `Failed to load model '${modelId}': ${error}`, { modelId, error }, 'The model file may be corrupted. Try re-downloading it.' @@ -143,7 +142,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.MODEL_CORRUPT, SCOPE, - ErrorType.USER, + 'user', `Model file for '${modelId}' appears to be corrupted`, { modelId, filePath }, `Delete ${filePath} and download the model again` @@ -154,7 +153,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.CONTEXT_TOO_LARGE, SCOPE, - ErrorType.USER, + 'user', `Requested context size ${requested} exceeds model's maximum of ${maxSupported}`, { modelId, requested, maxSupported }, `Use a context size of ${maxSupported} or less` @@ -166,7 +165,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.GPU_NOT_AVAILABLE, SCOPE, - ErrorType.NOT_FOUND, + 'not_found', 'No GPU acceleration available. Running on CPU.', {}, 'For better performance, ensure GPU drivers are installed' @@ -177,7 +176,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.INSUFFICIENT_VRAM, SCOPE, - ErrorType.USER, + 'user', `Model '${modelId}' requires ${required}GB VRAM but only ${available}GB available`, { modelId, required, available }, 'Use a smaller quantization or reduce GPU layers' @@ -188,7 +187,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.GPU_DRIVER_ERROR, SCOPE, - ErrorType.THIRD_PARTY, + 'third_party', `GPU driver error: ${error}`, { error }, 'Update your GPU drivers' @@ -200,7 +199,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.OLLAMA_NOT_RUNNING, SCOPE, - ErrorType.THIRD_PARTY, + 'third_party', `Ollama server is not running at ${url}`, { url }, 'Start Ollama with `ollama serve` or ensure it is running' @@ -211,7 +210,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.OLLAMA_MODEL_NOT_FOUND, SCOPE, - ErrorType.NOT_FOUND, + 'not_found', `Model '${modelName}' not found on Ollama server`, { modelName }, `Pull the model with \`ollama pull ${modelName}\`` @@ -222,7 +221,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.OLLAMA_PULL_FAILED, SCOPE, - ErrorType.THIRD_PARTY, + 'third_party', `Failed to pull model '${modelName}' from Ollama: ${error}`, { modelName, error }, 'Check your internet connection and Ollama server status' @@ -233,7 +232,7 @@ export const LocalModelError = { return new DextoRuntimeError( LocalModelErrorCode.OLLAMA_API_ERROR, SCOPE, - ErrorType.THIRD_PARTY, + 'third_party', `Ollama API error: ${error}`, { error }, 'Check Ollama server logs for details' diff --git a/packages/core/src/llm/registry/index.test.ts b/packages/core/src/llm/registry/index.test.ts index 62f3ffcec..e3aefab5b 100644 --- a/packages/core/src/llm/registry/index.test.ts +++ b/packages/core/src/llm/registry/index.test.ts @@ -27,7 +27,6 @@ import { } from './index.js'; import { MODELS_BY_PROVIDER } from './models.generated.js'; import { LLMErrorCode } from '../error-codes.js'; -import { ErrorScope, ErrorType } from '../../errors/types.js'; import type { Logger } from '../../logger/v2/types.js'; import { getCachedOpenRouterModelsWithInfo, @@ -82,8 +81,8 @@ describe('LLM Registry Core Functions', () => { expect(() => getMaxInputTokensForModel('openai', 'unknown-model', mockLogger)).toThrow( expect.objectContaining({ code: LLMErrorCode.MODEL_UNKNOWN, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }) ); }); @@ -108,8 +107,8 @@ describe('LLM Registry Core Functions', () => { expect(() => getProviderFromModel('unknown-model')).toThrow( expect.objectContaining({ code: LLMErrorCode.MODEL_UNKNOWN, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }) ); }); @@ -331,8 +330,8 @@ describe('getEffectiveMaxInputTokens', () => { expect(() => getEffectiveMaxInputTokens(config, mockLogger)).toThrow( expect.objectContaining({ code: LLMErrorCode.MODEL_UNKNOWN, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }) ); }); @@ -382,8 +381,8 @@ describe('File Support Functions', () => { expect(() => getSupportedFileTypesForModel('openai', 'unknown-model')).toThrow( expect.objectContaining({ code: LLMErrorCode.MODEL_UNKNOWN, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }) ); }); @@ -411,8 +410,8 @@ describe('File Support Functions', () => { expect(() => modelSupportsFileType('openai', 'unknown-model', 'pdf')).toThrow( expect.objectContaining({ code: LLMErrorCode.MODEL_UNKNOWN, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }) ); }); diff --git a/packages/core/src/llm/registry/sync.ts b/packages/core/src/llm/registry/sync.ts index 48e3a82f9..3fe39fc97 100644 --- a/packages/core/src/llm/registry/sync.ts +++ b/packages/core/src/llm/registry/sync.ts @@ -1,8 +1,8 @@ import type { LLMProvider, SupportedFileType } from '../types.js'; import type { ModelInfo } from './index.js'; +import type { Issue } from '../../errors/types.js'; import { DextoValidationError } from '../../errors/DextoValidationError.js'; import { DextoRuntimeError } from '../../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../../errors/types.js'; export const MODELS_DEV_URL = 'https://models.dev/api.json'; @@ -62,12 +62,12 @@ function isRecord(value: unknown): value is Record { return typeof value === 'object' && value !== null && !Array.isArray(value); } -function makeIssue(message: string, path?: Array) { +function makeIssue(message: string, path?: Array): Issue { return { code: 'llm_registry_models_dev_parse', message, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', severity: 'error' as const, ...(path ? { path } : {}), }; @@ -376,8 +376,8 @@ function buildModelsFromModelsDevProvider(params: { if (!modelsDevProvider) { throw new DextoRuntimeError( 'llm_registry_models_dev_provider_missing', - ErrorScope.LLM, - ErrorType.THIRD_PARTY, + 'llm', + 'third_party', `models.dev provider '${spec.modelsDevProviderId}' not found (needed for dexto-nova provider '${spec.provider}')`, { modelsDevProviderId: spec.modelsDevProviderId, provider: spec.provider } ); @@ -567,8 +567,8 @@ export async function buildModelsByProviderFromRemote(options?: { if (!modelsDevRes.ok) { throw new DextoRuntimeError( 'llm_registry_models_dev_fetch_failed', - ErrorScope.LLM, - ErrorType.THIRD_PARTY, + 'llm', + 'third_party', `Failed to fetch models.dev (${modelsDevRes.status} ${modelsDevRes.statusText})`, { status: modelsDevRes.status, statusText: modelsDevRes.statusText } ); diff --git a/packages/core/src/llm/resolver.ts b/packages/core/src/llm/resolver.ts index a78d81e78..00d52a126 100644 --- a/packages/core/src/llm/resolver.ts +++ b/packages/core/src/llm/resolver.ts @@ -1,5 +1,5 @@ import { Result, hasErrors, splitIssues, ok, fail, zodToIssues } from '../utils/result.js'; -import { Issue, ErrorScope, ErrorType } from '../errors/types.js'; +import { Issue } from '../errors/types.js'; import { LLMErrorCode } from './error-codes.js'; import { type ValidatedLLMConfig, type LLMUpdates, type LLMConfig } from './schemas.js'; @@ -82,8 +82,8 @@ export async function resolveLLMConfig( code: LLMErrorCode.API_KEY_CANDIDATE_MISSING, message: 'API key not provided or found in environment', severity: 'warning', - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', context: { provider }, }); } else if (typeof apiKey === 'string' && apiKey.length < 10) { @@ -91,8 +91,8 @@ export async function resolveLLMConfig( code: LLMErrorCode.API_KEY_INVALID, message: 'API key looks unusually short', severity: 'warning', - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', context: { provider }, }); } @@ -112,8 +112,8 @@ export async function resolveLLMConfig( code: LLMErrorCode.MODEL_INCOMPATIBLE, message: `Model set to default '${model}' for provider '${provider}'`, severity: 'warning', - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', context: { provider, model }, }); } @@ -134,8 +134,8 @@ export async function resolveLLMConfig( code: LLMErrorCode.MODEL_INCOMPATIBLE, message: `Model set to default '${model}' for provider '${provider}'`, severity: 'warning', - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', context: { provider, model }, }); } @@ -163,8 +163,8 @@ export async function resolveLLMConfig( 'GOOGLE_VERTEX_PROJECT environment variable is required for Vertex AI. ' + 'Set it to your GCP project ID and ensure ADC is configured via `gcloud auth application-default login`', severity: 'error', - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', context: { provider, model }, }); } @@ -184,8 +184,8 @@ export async function resolveLLMConfig( 'Also set either AWS_BEARER_TOKEN_BEDROCK (API key) or ' + 'AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY (IAM credentials).', severity: 'error', - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', context: { provider, model }, }); } @@ -214,8 +214,8 @@ export async function resolveLLMConfig( code: LLMErrorCode.MODEL_INCOMPATIBLE, message: `Model '${model}' not found in OpenRouter catalog. Check model ID at https://openrouter.ai/models`, severity: 'error', - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', context: { provider, model }, }); } @@ -292,8 +292,8 @@ export function validateLLMConfig( message: 'API key seems too short - please verify it is correct', path: ['apiKey'], severity: 'warning', - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', context: { provider: candidate.provider, model: candidate.model, diff --git a/packages/core/src/llm/schemas.ts b/packages/core/src/llm/schemas.ts index 56ba3f7f2..32de80aac 100644 --- a/packages/core/src/llm/schemas.ts +++ b/packages/core/src/llm/schemas.ts @@ -1,5 +1,4 @@ import { LLMErrorCode } from './error-codes.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { DextoRuntimeError } from '../errors/index.js'; import { NonEmptyTrimmed, EnvExpandedString, OptionalURL } from '../utils/result.js'; import { z } from 'zod'; @@ -133,8 +132,8 @@ export const LLMConfigSchema = LLMConfigBaseSchema.superRefine((data, ctx) => { `'openai/gpt-5-mini' or 'anthropic/claude-sonnet-4.5'). You provided '${data.model}'.`, params: { code: LLMErrorCode.MODEL_INCOMPATIBLE, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }, }); } @@ -149,8 +148,8 @@ export const LLMConfigSchema = LLMConfigBaseSchema.superRefine((data, ctx) => { `Use an 'openai-compatible' provider if you need a custom base URL.`, params: { code: LLMErrorCode.BASE_URL_INVALID, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }, }); } @@ -170,8 +169,8 @@ export const LLMConfigSchema = LLMConfigBaseSchema.superRefine((data, ctx) => { `Supported: ${supportedModelsList.join(', ')}`, params: { code: LLMErrorCode.MODEL_INCOMPATIBLE, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }, }); } @@ -194,8 +193,8 @@ export const LLMConfigSchema = LLMConfigBaseSchema.superRefine((data, ctx) => { `You provided ${data.maxInputTokens}`, params: { code: LLMErrorCode.TOKENS_EXCEEDED, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }, }); } @@ -225,8 +224,8 @@ export const LLMConfigSchema = LLMConfigBaseSchema.superRefine((data, ctx) => { message, params: { code: LLMErrorCode.REQUEST_INVALID_SCHEMA, - scope: ErrorScope.LLM, - type: ErrorType.SYSTEM, + scope: 'llm', + type: 'system', }, }); } @@ -248,8 +247,8 @@ export const LLMConfigSchema = LLMConfigBaseSchema.superRefine((data, ctx) => { `model '${data.model}'. Supported: ${profile.variants.map((entry) => entry.id).join(', ')}`, params: { code: LLMErrorCode.MODEL_INCOMPATIBLE, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }, }); } @@ -263,8 +262,8 @@ export const LLMConfigSchema = LLMConfigBaseSchema.superRefine((data, ctx) => { `model '${data.model}'. Remove reasoning.budgetTokens to use provider defaults.`, params: { code: LLMErrorCode.MODEL_INCOMPATIBLE, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }, }); } @@ -318,8 +317,8 @@ export const LLMUpdatesSchema = z `model '${data.model}'. Supported: ${profile.variants.map((entry) => entry.id).join(', ')}`, params: { code: LLMErrorCode.MODEL_INCOMPATIBLE, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }, }); } @@ -333,8 +332,8 @@ export const LLMUpdatesSchema = z `model '${data.model}'. Remove reasoning.budgetTokens to use provider defaults.`, params: { code: LLMErrorCode.MODEL_INCOMPATIBLE, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }, }); } diff --git a/packages/core/src/llm/services/vercel.integration.test.ts b/packages/core/src/llm/services/vercel.integration.test.ts index 483d9b5c5..bbde0781e 100644 --- a/packages/core/src/llm/services/vercel.integration.test.ts +++ b/packages/core/src/llm/services/vercel.integration.test.ts @@ -5,7 +5,6 @@ import { providerRequiresApiKey, cleanupTestEnvironment, } from './test-utils.integration.js'; -import { ErrorScope, ErrorType } from '../../errors/index.js'; import { LLMErrorCode } from '../error-codes.js'; import { resolveApiKeyForProvider } from '../../utils/api-key-resolver.js'; import type { LLMProvider } from '../types.js'; @@ -244,8 +243,8 @@ describe('Vercel AI SDK LLM Service Integration', () => { issues: [ expect.objectContaining({ code: LLMErrorCode.INPUT_FILE_UNSUPPORTED, - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', }), ], }); diff --git a/packages/core/src/llm/validation.ts b/packages/core/src/llm/validation.ts index 22f61b2c5..b9d6e2fd5 100644 --- a/packages/core/src/llm/validation.ts +++ b/packages/core/src/llm/validation.ts @@ -3,7 +3,7 @@ import type { LLMProvider } from './types.js'; import type { Logger } from '../logger/v2/types.js'; import type { ImageData, FileData } from '../context/types.js'; import { Result, ok, fail } from '../utils/result.js'; -import { Issue, ErrorScope, ErrorType } from '../errors/types.js'; +import { Issue } from '../errors/types.js'; import { LLMErrorCode } from './error-codes.js'; // TOOD: Refactor/simplify this file @@ -79,8 +79,8 @@ export function validateInputForLLM( issues.push({ code: LLMErrorCode.INPUT_FILE_UNSUPPORTED, message: fileValidation.error || 'File type not supported by current LLM', - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', severity: 'error', context: { ...context, @@ -102,8 +102,8 @@ export function validateInputForLLM( issues.push({ code: LLMErrorCode.INPUT_IMAGE_UNSUPPORTED, message: imageValidation.error || 'Image format not supported by current LLM', - scope: ErrorScope.LLM, - type: ErrorType.USER, + scope: 'llm', + type: 'user', severity: 'error', context: { ...context, @@ -124,8 +124,8 @@ export function validateInputForLLM( { code: LLMErrorCode.REQUEST_INVALID_SCHEMA, message: 'Failed to validate input', - scope: ErrorScope.LLM, - type: ErrorType.SYSTEM, + scope: 'llm', + type: 'system', severity: 'error', context: { provider: config.provider, diff --git a/packages/core/src/logger/v2/errors.ts b/packages/core/src/logger/v2/errors.ts index 3ba8a8b27..78ffd8462 100644 --- a/packages/core/src/logger/v2/errors.ts +++ b/packages/core/src/logger/v2/errors.ts @@ -1,5 +1,4 @@ import { DextoRuntimeError } from '../../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../../errors/types.js'; import { LoggerErrorCode } from './error-codes.js'; /** @@ -16,8 +15,8 @@ export class LoggerError { ): DextoRuntimeError { return new DextoRuntimeError( LoggerErrorCode.TRANSPORT_NOT_IMPLEMENTED, - ErrorScope.LOGGER, - ErrorType.USER, + 'logger', + 'user', `${transportType} transport not yet implemented. Available transports: ${availableTransports.join(', ')}`, { transportType, availableTransports } ); @@ -29,8 +28,8 @@ export class LoggerError { static unknownTransportType(transportType: string): DextoRuntimeError { return new DextoRuntimeError( LoggerErrorCode.TRANSPORT_UNKNOWN_TYPE, - ErrorScope.LOGGER, - ErrorType.USER, + 'logger', + 'user', `Unknown transport type: ${transportType}`, { transportType } ); @@ -46,8 +45,8 @@ export class LoggerError { ): DextoRuntimeError { return new DextoRuntimeError( LoggerErrorCode.TRANSPORT_INITIALIZATION_FAILED, - ErrorScope.LOGGER, - ErrorType.SYSTEM, + 'logger', + 'system', `Failed to initialize ${transportType} transport: ${reason}`, { transportType, reason, ...(details ?? {}) } ); @@ -59,8 +58,8 @@ export class LoggerError { static transportWriteFailed(transportType: string, error: unknown): DextoRuntimeError { return new DextoRuntimeError( LoggerErrorCode.TRANSPORT_WRITE_FAILED, - ErrorScope.LOGGER, - ErrorType.SYSTEM, + 'logger', + 'system', `Transport write failed for ${transportType}`, { transportType, @@ -75,8 +74,8 @@ export class LoggerError { static invalidConfig(message: string, context?: Record): DextoRuntimeError { return new DextoRuntimeError( LoggerErrorCode.INVALID_CONFIG, - ErrorScope.LOGGER, - ErrorType.USER, + 'logger', + 'user', `Invalid logger configuration: ${message}`, context ); @@ -88,8 +87,8 @@ export class LoggerError { static invalidLogLevel(level: string, validLevels: string[]): DextoRuntimeError { return new DextoRuntimeError( LoggerErrorCode.INVALID_LOG_LEVEL, - ErrorScope.LOGGER, - ErrorType.USER, + 'logger', + 'user', `Invalid log level '${level}'. Valid levels: ${validLevels.join(', ')}`, { level, validLevels } ); diff --git a/packages/core/src/mcp/errors.ts b/packages/core/src/mcp/errors.ts index fe7b870ca..c2c77ddf3 100644 --- a/packages/core/src/mcp/errors.ts +++ b/packages/core/src/mcp/errors.ts @@ -1,5 +1,4 @@ import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { MCPErrorCode } from './error-codes.js'; /** @@ -13,8 +12,8 @@ export class MCPError { static connectionFailed(serverName: string, reason: string) { return new DextoRuntimeError( MCPErrorCode.CONNECTION_FAILED, - ErrorScope.MCP, - ErrorType.THIRD_PARTY, + 'mcp', + 'third_party', `Failed to connect to MCP server '${serverName}': ${reason}`, { serverName, reason }, 'Check that the MCP server is running and accessible' @@ -27,8 +26,8 @@ export class MCPError { static disconnectionFailed(serverName: string, reason: string) { return new DextoRuntimeError( MCPErrorCode.DISCONNECTION_FAILED, - ErrorScope.MCP, - ErrorType.SYSTEM, + 'mcp', + 'system', `Failed to disconnect MCP server '${serverName}': ${reason}`, { serverName, reason }, 'Try restarting the application if the server remains in an inconsistent state' @@ -41,8 +40,8 @@ export class MCPError { static protocolError(message: string, details?: unknown) { return new DextoRuntimeError( MCPErrorCode.PROTOCOL_ERROR, - ErrorScope.MCP, - ErrorType.THIRD_PARTY, + 'mcp', + 'third_party', `MCP protocol error: ${message}`, details, 'Check MCP server compatibility and protocol version' @@ -55,8 +54,8 @@ export class MCPError { static authenticationRequired(serverName: string, reason?: string) { return new DextoRuntimeError( MCPErrorCode.AUTH_REQUIRED, - ErrorScope.MCP, - ErrorType.THIRD_PARTY, + 'mcp', + 'third_party', `Authentication required for MCP server '${serverName}'${reason ? `: ${reason}` : ''}`, { serverName, reason }, 'Authenticate with the MCP server using the CLI /mcp flow' @@ -69,8 +68,8 @@ export class MCPError { static duplicateName(name: string, existingName: string) { return new DextoRuntimeError( MCPErrorCode.DUPLICATE_NAME, - ErrorScope.MCP, - ErrorType.USER, + 'mcp', + 'user', `Server name '${name}' conflicts with existing '${existingName}'`, { name, existingName }, 'Use a unique name for each MCP server' @@ -83,8 +82,8 @@ export class MCPError { static serverNotFound(serverName: string, reason?: string) { return new DextoRuntimeError( MCPErrorCode.SERVER_NOT_FOUND, - ErrorScope.MCP, - ErrorType.NOT_FOUND, + 'mcp', + 'not_found', `MCP server '${serverName}' not found${reason ? `: ${reason}` : ''}`, { serverName, reason } ); @@ -96,8 +95,8 @@ export class MCPError { static toolNotFound(toolName: string) { return new DextoRuntimeError( MCPErrorCode.TOOL_NOT_FOUND, - ErrorScope.MCP, - ErrorType.NOT_FOUND, + 'mcp', + 'not_found', `No MCP tool found: ${toolName}`, { toolName } ); @@ -109,8 +108,8 @@ export class MCPError { static promptNotFound(promptName: string) { return new DextoRuntimeError( MCPErrorCode.PROMPT_NOT_FOUND, - ErrorScope.MCP, - ErrorType.NOT_FOUND, + 'mcp', + 'not_found', `No client found for prompt: ${promptName}`, { promptName } ); @@ -122,8 +121,8 @@ export class MCPError { static resourceNotFound(resourceUri: string) { return new DextoRuntimeError( MCPErrorCode.RESOURCE_NOT_FOUND, - ErrorScope.MCP, - ErrorType.NOT_FOUND, + 'mcp', + 'not_found', `No client found for resource: ${resourceUri}`, { resourceUri } ); @@ -135,8 +134,8 @@ export class MCPError { static clientNotConnected(context?: string) { return new DextoRuntimeError( MCPErrorCode.CONNECTION_FAILED, - ErrorScope.MCP, - ErrorType.SYSTEM, + 'mcp', + 'system', `MCP client is not connected${context ? `: ${context}` : ''}`, { context } ); @@ -148,8 +147,8 @@ export class MCPError { static invalidToolSchema(toolName: string, reason: string) { return new DextoRuntimeError( MCPErrorCode.PROTOCOL_ERROR, - ErrorScope.MCP, - ErrorType.THIRD_PARTY, + 'mcp', + 'third_party', `Tool '${toolName}' has invalid schema: ${reason}`, { toolName, reason } ); diff --git a/packages/core/src/mcp/manager.test.ts b/packages/core/src/mcp/manager.test.ts index 7f98a169d..e405dccd9 100644 --- a/packages/core/src/mcp/manager.test.ts +++ b/packages/core/src/mcp/manager.test.ts @@ -4,7 +4,6 @@ import { MCPManager } from './manager.js'; import type { McpClient, MCPResourceSummary } from './types.js'; import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; import { MCPErrorCode } from './error-codes.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { eventBus } from '../events/index.js'; import type { JSONSchema7 } from 'json-schema'; import type { Prompt } from '@modelcontextprotocol/sdk/types.js'; @@ -310,8 +309,8 @@ describe('MCPManager Tool Conflict Resolution', () => { })() as DextoRuntimeError; expect(error).toBeInstanceOf(DextoRuntimeError); expect(error.code).toBe(MCPErrorCode.DUPLICATE_NAME); - expect(error.scope).toBe(ErrorScope.MCP); - expect(error.type).toBe(ErrorType.USER); + expect(error.scope).toBe('mcp'); + expect(error.type).toBe('user'); }); it('should allow re-registering the same server name', () => { diff --git a/packages/core/src/mcp/resolver.ts b/packages/core/src/mcp/resolver.ts index 903adabfc..bc101c16d 100644 --- a/packages/core/src/mcp/resolver.ts +++ b/packages/core/src/mcp/resolver.ts @@ -1,6 +1,6 @@ // src/config/mcp/resolver.ts import { ok, fail, type Result, hasErrors, zodToIssues } from '../utils/result.js'; -import { type Issue, ErrorScope, ErrorType } from '../errors/types.js'; +import { type Issue } from '../errors/types.js'; import { MCPErrorCode } from './error-codes.js'; import { @@ -40,8 +40,8 @@ function resolveMcpServerConfig( code: MCPErrorCode.SCHEMA_VALIDATION, message: 'Server name must be a non-empty string', severity: 'error', - scope: ErrorScope.MCP, - type: ErrorType.USER, + scope: 'mcp', + type: 'user', context: { serverName }, }); } @@ -55,8 +55,8 @@ function resolveMcpServerConfig( code: MCPErrorCode.DUPLICATE_NAME, message: `Server name '${serverName}' is similar to existing '${dup}' (case differs)`, severity: 'warning', - scope: ErrorScope.MCP, - type: ErrorType.USER, + scope: 'mcp', + type: 'user', context: { serverName }, }); } diff --git a/packages/core/src/mcp/schemas.ts b/packages/core/src/mcp/schemas.ts index d50d21e79..785775864 100644 --- a/packages/core/src/mcp/schemas.ts +++ b/packages/core/src/mcp/schemas.ts @@ -1,5 +1,4 @@ import { MCPErrorCode } from './error-codes.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { EnvExpandedString, RequiredEnvURL } from '../utils/result.js'; import { z } from 'zod'; @@ -48,8 +47,8 @@ export const StdioServerConfigSchema = z message: 'Stdio server requires a non-empty command', params: { code: MCPErrorCode.COMMAND_MISSING, - scope: ErrorScope.MCP, - type: ErrorType.USER, + scope: 'mcp', + type: 'user', }, }); } diff --git a/packages/core/src/memory/errors.ts b/packages/core/src/memory/errors.ts index 14f789730..aebfb685e 100644 --- a/packages/core/src/memory/errors.ts +++ b/packages/core/src/memory/errors.ts @@ -1,5 +1,4 @@ import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { MemoryErrorCode } from './error-codes.js'; /** @@ -9,8 +8,8 @@ export class MemoryError { static notFound(id: string): DextoRuntimeError { return new DextoRuntimeError( MemoryErrorCode.MEMORY_NOT_FOUND, - ErrorScope.MEMORY, - ErrorType.NOT_FOUND, + 'memory', + 'not_found', `Memory not found: ${id}`, { id } ); @@ -19,8 +18,8 @@ export class MemoryError { static alreadyExists(id: string): DextoRuntimeError { return new DextoRuntimeError( MemoryErrorCode.MEMORY_ALREADY_EXISTS, - ErrorScope.MEMORY, - ErrorType.USER, + 'memory', + 'user', `Memory already exists: ${id}`, { id } ); @@ -29,8 +28,8 @@ export class MemoryError { static contentRequired(): DextoRuntimeError { return new DextoRuntimeError( MemoryErrorCode.MEMORY_CONTENT_REQUIRED, - ErrorScope.MEMORY, - ErrorType.USER, + 'memory', + 'user', 'Memory content is required' ); } @@ -38,8 +37,8 @@ export class MemoryError { static contentTooLong(length: number, maxLength: number): DextoRuntimeError { return new DextoRuntimeError( MemoryErrorCode.MEMORY_CONTENT_TOO_LONG, - ErrorScope.MEMORY, - ErrorType.USER, + 'memory', + 'user', `Memory content too long: ${length} characters (max: ${maxLength})`, { length, maxLength } ); @@ -48,8 +47,8 @@ export class MemoryError { static invalidId(id: string): DextoRuntimeError { return new DextoRuntimeError( MemoryErrorCode.MEMORY_INVALID_ID, - ErrorScope.MEMORY, - ErrorType.USER, + 'memory', + 'user', `Invalid memory ID: ${id}`, { id } ); @@ -58,8 +57,8 @@ export class MemoryError { static invalidTags(tags: unknown): DextoRuntimeError { return new DextoRuntimeError( MemoryErrorCode.MEMORY_INVALID_TAGS, - ErrorScope.MEMORY, - ErrorType.USER, + 'memory', + 'user', `Invalid tags format: ${JSON.stringify(tags)}`, { tags } ); @@ -68,8 +67,8 @@ export class MemoryError { static storageError(message: string, cause?: Error): DextoRuntimeError { return new DextoRuntimeError( MemoryErrorCode.MEMORY_STORAGE_ERROR, - ErrorScope.MEMORY, - ErrorType.SYSTEM, + 'memory', + 'system', `Memory storage error: ${message}`, { cause } ); @@ -78,8 +77,8 @@ export class MemoryError { static retrievalError(message: string, cause?: Error): DextoRuntimeError { return new DextoRuntimeError( MemoryErrorCode.MEMORY_RETRIEVAL_ERROR, - ErrorScope.MEMORY, - ErrorType.SYSTEM, + 'memory', + 'system', `Memory retrieval error: ${message}`, { cause } ); @@ -88,8 +87,8 @@ export class MemoryError { static deleteError(message: string, cause?: Error): DextoRuntimeError { return new DextoRuntimeError( MemoryErrorCode.MEMORY_DELETE_ERROR, - ErrorScope.MEMORY, - ErrorType.SYSTEM, + 'memory', + 'system', `Memory deletion error: ${message}`, { cause } ); diff --git a/packages/core/src/prompts/errors.ts b/packages/core/src/prompts/errors.ts index 7cc3c921c..32672853e 100644 --- a/packages/core/src/prompts/errors.ts +++ b/packages/core/src/prompts/errors.ts @@ -1,6 +1,5 @@ import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; import { DextoValidationError } from '../errors/DextoValidationError.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { PromptErrorCode } from './error-codes.js'; /** @@ -14,8 +13,8 @@ export class PromptError { static notFound(name: string) { return new DextoRuntimeError( PromptErrorCode.PROMPT_NOT_FOUND, - ErrorScope.PROMPT, - ErrorType.NOT_FOUND, + 'prompt', + 'not_found', `Prompt not found: ${name}`, { name } ); @@ -29,8 +28,8 @@ export class PromptError { { code: PromptErrorCode.PROMPT_MISSING_TEXT, message: 'Prompt missing text content', - scope: ErrorScope.PROMPT, - type: ErrorType.USER, + scope: 'prompt', + type: 'user', severity: 'error', context: {}, }, @@ -45,8 +44,8 @@ export class PromptError { { code: PromptErrorCode.PROMPT_MISSING_REQUIRED_ARGUMENTS, message: `Missing required arguments: ${missingNames.join(', ')}`, - scope: ErrorScope.PROMPT, - type: ErrorType.USER, + scope: 'prompt', + type: 'user', severity: 'error', context: { missingNames }, }, @@ -59,8 +58,8 @@ export class PromptError { static providerNotFound(source: string) { return new DextoRuntimeError( PromptErrorCode.PROMPT_PROVIDER_NOT_FOUND, - ErrorScope.PROMPT, - ErrorType.NOT_FOUND, + 'prompt', + 'not_found', `No provider found for prompt source: ${source}`, { source } ); @@ -74,8 +73,8 @@ export class PromptError { { code: PromptErrorCode.PROMPT_NAME_REQUIRED, message: 'Prompt name is required', - scope: ErrorScope.PROMPT, - type: ErrorType.USER, + scope: 'prompt', + type: 'user', severity: 'error', context: {}, }, @@ -92,8 +91,8 @@ export class PromptError { { code: PromptErrorCode.PROMPT_INVALID_NAME, message: `${contextPrefix} '${name}' must be ${guidance}.${hintSuffix}`, - scope: ErrorScope.PROMPT, - type: ErrorType.USER, + scope: 'prompt', + type: 'user', severity: 'error', context: { name, guidance }, }, @@ -106,8 +105,8 @@ export class PromptError { { code: PromptErrorCode.PROMPT_ALREADY_EXISTS, message: `Prompt already exists: ${name}`, - scope: ErrorScope.PROMPT, - type: ErrorType.USER, + scope: 'prompt', + type: 'user', severity: 'error', context: { name }, }, @@ -120,8 +119,8 @@ export class PromptError { static emptyResolvedContent(name: string) { return new DextoRuntimeError( PromptErrorCode.PROMPT_EMPTY_CONTENT, - ErrorScope.PROMPT, - ErrorType.NOT_FOUND, + 'prompt', + 'not_found', `Prompt resolved to empty content: ${name}`, { name } ); @@ -135,8 +134,8 @@ export class PromptError { { code: PromptErrorCode.PROMPT_CONFIG_INVALID, message: `Invalid prompts configuration: ${details}`, - scope: ErrorScope.PROMPT, - type: ErrorType.USER, + scope: 'prompt', + type: 'user', severity: 'error', context: { details }, }, diff --git a/packages/core/src/resources/errors.ts b/packages/core/src/resources/errors.ts index 1edc1d3ea..1d13c594b 100644 --- a/packages/core/src/resources/errors.ts +++ b/packages/core/src/resources/errors.ts @@ -1,5 +1,4 @@ import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { ResourceErrorCodes } from './error-codes.js'; /** @@ -41,8 +40,8 @@ export class ResourceError { static invalidUriFormat(uri: string, expected?: string) { return new DextoRuntimeError( ResourceErrorCodes.INVALID_URI_FORMAT, - ErrorScope.RESOURCE, - ErrorType.USER, + 'resource', + 'user', `Invalid resource URI format: '${ResourceError.redactUri(uri)}'${expected ? ` (expected ${expected})` : ''}`, { uri: ResourceError.redactUri(uri), uriRaw: uri, expected }, expected ? `Use format: ${expected}` : 'Check the resource URI format' @@ -52,8 +51,8 @@ export class ResourceError { static emptyUri() { return new DextoRuntimeError( ResourceErrorCodes.EMPTY_URI, - ErrorScope.RESOURCE, - ErrorType.USER, + 'resource', + 'user', 'Resource URI cannot be empty', {}, 'Provide a valid resource URI' @@ -64,8 +63,8 @@ export class ResourceError { static resourceNotFound(uri: string) { return new DextoRuntimeError( ResourceErrorCodes.RESOURCE_NOT_FOUND, - ErrorScope.RESOURCE, - ErrorType.NOT_FOUND, + 'resource', + 'not_found', `Resource not found: '${ResourceError.redactUri(uri)}'`, { uri: ResourceError.redactUri(uri), uriRaw: uri }, 'Check that the resource exists and is accessible' @@ -75,8 +74,8 @@ export class ResourceError { static providerNotInitialized(providerType: string, uri: string) { return new DextoRuntimeError( ResourceErrorCodes.PROVIDER_NOT_INITIALIZED, - ErrorScope.RESOURCE, - ErrorType.SYSTEM, + 'resource', + 'system', `${providerType} resource provider not initialized for: '${ResourceError.redactUri(uri)}'`, { providerType, uri: ResourceError.redactUri(uri), uriRaw: uri }, 'Ensure the resource provider is properly configured' @@ -86,8 +85,8 @@ export class ResourceError { static providerNotAvailable(providerType: string) { return new DextoRuntimeError( ResourceErrorCodes.PROVIDER_NOT_AVAILABLE, - ErrorScope.RESOURCE, - ErrorType.SYSTEM, + 'resource', + 'system', `${providerType} resource provider is not available`, { providerType }, 'Check resource provider configuration and availability' @@ -99,8 +98,8 @@ export class ResourceError { const { message: reasonMsg, raw: reasonRaw } = ResourceError.toMessageAndRaw(reason); return new DextoRuntimeError( ResourceErrorCodes.READ_FAILED, - ErrorScope.RESOURCE, - ErrorType.SYSTEM, + 'resource', + 'system', `Failed to read resource '${ResourceError.redactUri(uri)}': ${reasonMsg}`, { uri: ResourceError.redactUri(uri), uriRaw: uri, reason: reasonMsg, reasonRaw }, 'Check resource permissions and availability' @@ -110,8 +109,8 @@ export class ResourceError { static accessDenied(uri: string) { return new DextoRuntimeError( ResourceErrorCodes.ACCESS_DENIED, - ErrorScope.RESOURCE, - ErrorType.FORBIDDEN, + 'resource', + 'forbidden', `Access denied to resource: '${ResourceError.redactUri(uri)}'`, { uri: ResourceError.redactUri(uri), uriRaw: uri }, 'Ensure you have permission to access this resource' @@ -122,8 +121,8 @@ export class ResourceError { static noSuitableProvider(uri: string) { return new DextoRuntimeError( ResourceErrorCodes.NO_SUITABLE_PROVIDER, - ErrorScope.RESOURCE, - ErrorType.NOT_FOUND, + 'resource', + 'not_found', `No suitable provider found for resource: '${ResourceError.redactUri(uri)}'`, { uri: ResourceError.redactUri(uri), uriRaw: uri }, 'Check that the resource type is supported' @@ -134,8 +133,8 @@ export class ResourceError { const { message: reasonMsg, raw: reasonRaw } = ResourceError.toMessageAndRaw(reason); return new DextoRuntimeError( ResourceErrorCodes.PROVIDER_ERROR, - ErrorScope.RESOURCE, - ErrorType.SYSTEM, + 'resource', + 'system', `${providerType} provider failed during ${operation}: ${reasonMsg}`, { providerType, operation, reason: reasonMsg, reasonRaw }, 'Check provider configuration and logs for details' diff --git a/packages/core/src/session/chat-session.ts b/packages/core/src/session/chat-session.ts index b8eaa8267..d6fc29340 100644 --- a/packages/core/src/session/chat-session.ts +++ b/packages/core/src/session/chat-session.ts @@ -21,7 +21,7 @@ import { } from '../events/index.js'; import type { Logger } from '../logger/v2/types.js'; import { DextoLogComponent } from '../logger/v2/types.js'; -import { DextoRuntimeError, ErrorScope, ErrorType } from '../errors/index.js'; +import { DextoRuntimeError } from '../errors/index.js'; import { HookErrorCode } from '../hooks/error-codes.js'; import type { InternalMessage, ContentPart } from '../context/types.js'; import type { UserMessageInput } from './message-queue.js'; @@ -523,8 +523,8 @@ export class ChatSession { if ( error instanceof DextoRuntimeError && error.code === HookErrorCode.HOOK_BLOCKED_EXECUTION && - error.scope === ErrorScope.HOOK && - error.type === ErrorType.FORBIDDEN + error.scope === 'hook' && + error.type === 'forbidden' ) { // Save the blocked interaction to history const textContent = parts diff --git a/packages/core/src/session/errors.ts b/packages/core/src/session/errors.ts index db67d50f6..0fd2b8d71 100644 --- a/packages/core/src/session/errors.ts +++ b/packages/core/src/session/errors.ts @@ -1,5 +1,4 @@ import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { SessionErrorCode } from './error-codes.js'; /** @@ -13,8 +12,8 @@ export class SessionError { static notFound(sessionId: string) { return new DextoRuntimeError( SessionErrorCode.SESSION_NOT_FOUND, - ErrorScope.SESSION, - ErrorType.NOT_FOUND, + 'session', + 'not_found', `Session ${sessionId} not found`, { sessionId } ); @@ -26,8 +25,8 @@ export class SessionError { static initializationFailed(sessionId: string, reason: string) { return new DextoRuntimeError( SessionErrorCode.SESSION_INITIALIZATION_FAILED, - ErrorScope.SESSION, - ErrorType.SYSTEM, + 'session', + 'system', `Failed to initialize session '${sessionId}': ${reason}`, { sessionId, reason } ); @@ -39,8 +38,8 @@ export class SessionError { static maxSessionsExceeded(currentCount: number, maxSessions: number) { return new DextoRuntimeError( SessionErrorCode.SESSION_MAX_SESSIONS_EXCEEDED, - ErrorScope.SESSION, - ErrorType.USER, + 'session', + 'user', `Maximum sessions (${maxSessions}) reached`, { currentCount, maxSessions }, 'Delete unused sessions or increase maxSessions limit in configuration' @@ -53,8 +52,8 @@ export class SessionError { static storageFailed(sessionId: string, operation: string, reason: string) { return new DextoRuntimeError( SessionErrorCode.SESSION_STORAGE_FAILED, - ErrorScope.SESSION, - ErrorType.SYSTEM, + 'session', + 'system', `Failed to ${operation} session '${sessionId}': ${reason}`, { sessionId, operation, reason } ); @@ -66,8 +65,8 @@ export class SessionError { static resetFailed(sessionId: string, reason: string) { return new DextoRuntimeError( SessionErrorCode.SESSION_RESET_FAILED, - ErrorScope.SESSION, - ErrorType.SYSTEM, + 'session', + 'system', `Failed to reset session '${sessionId}': ${reason}`, { sessionId, reason } ); diff --git a/packages/core/src/session/history/database.test.ts b/packages/core/src/session/history/database.test.ts index c0573aa16..dab3acd95 100644 --- a/packages/core/src/session/history/database.test.ts +++ b/packages/core/src/session/history/database.test.ts @@ -2,7 +2,6 @@ import { describe, test, expect, vi, beforeEach, type Mocked } from 'vitest'; import { DatabaseHistoryProvider } from './database.js'; import type { Database } from '../../storage/types.js'; import { SessionErrorCode } from '../error-codes.js'; -import { ErrorScope, ErrorType } from '../../errors/types.js'; import { createMockLogger } from '../../logger/v2/test-utils.js'; describe('DatabaseHistoryProvider error mapping', () => { @@ -36,8 +35,8 @@ describe('DatabaseHistoryProvider error mapping', () => { provider.saveMessage({ role: 'user', content: 'hi' } as any) ).rejects.toMatchObject({ code: SessionErrorCode.SESSION_STORAGE_FAILED, - scope: ErrorScope.SESSION, - type: ErrorType.SYSTEM, + scope: 'session', + type: 'system', context: expect.objectContaining({ sessionId }), }); }); @@ -46,8 +45,8 @@ describe('DatabaseHistoryProvider error mapping', () => { db.delete.mockRejectedValue(new Error('delete failed')); await expect(provider.clearHistory()).rejects.toMatchObject({ code: SessionErrorCode.SESSION_RESET_FAILED, - scope: ErrorScope.SESSION, - type: ErrorType.SYSTEM, + scope: 'session', + type: 'system', context: expect.objectContaining({ sessionId }), }); }); @@ -56,8 +55,8 @@ describe('DatabaseHistoryProvider error mapping', () => { db.getRange.mockRejectedValue(new Error('getRange failed')); await expect(provider.getHistory()).rejects.toMatchObject({ code: SessionErrorCode.SESSION_STORAGE_FAILED, - scope: ErrorScope.SESSION, - type: ErrorType.SYSTEM, + scope: 'session', + type: 'system', context: expect.objectContaining({ sessionId }), }); }); diff --git a/packages/core/src/session/session-manager.test.ts b/packages/core/src/session/session-manager.test.ts index b260b8eb2..c14e3a024 100644 --- a/packages/core/src/session/session-manager.test.ts +++ b/packages/core/src/session/session-manager.test.ts @@ -3,7 +3,6 @@ import { SessionManager } from './session-manager.js'; import { ChatSession } from './chat-session.js'; import { type ValidatedLLMConfig } from '../llm/schemas.js'; import { LLMConfigSchema } from '../llm/schemas.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { SessionErrorCode } from './error-codes.js'; import { createMockLogger } from '../logger/v2/test-utils.js'; import { createInMemoryStorageManager } from '../test-utils/in-memory-storage.js'; @@ -538,8 +537,8 @@ describe('SessionManager', () => { await expect(sessionManager.forkSession('missing-parent')).rejects.toMatchObject({ code: SessionErrorCode.SESSION_NOT_FOUND, - scope: ErrorScope.SESSION, - type: ErrorType.NOT_FOUND, + scope: 'session', + type: 'not_found', }); }); @@ -564,8 +563,8 @@ describe('SessionManager', () => { await expect(sessionManager.forkSession(parentSessionId)).rejects.toMatchObject({ code: SessionErrorCode.SESSION_MAX_SESSIONS_EXCEEDED, - scope: ErrorScope.SESSION, - type: ErrorType.USER, + scope: 'session', + type: 'user', }); expect(mockStorageManager.database.set).not.toHaveBeenCalledWith( 'session:mock-uuid-123', @@ -588,8 +587,8 @@ describe('SessionManager', () => { await expect(limitedManager.createSession()).rejects.toMatchObject({ code: SessionErrorCode.SESSION_MAX_SESSIONS_EXCEEDED, - scope: ErrorScope.SESSION, - type: ErrorType.USER, + scope: 'session', + type: 'user', }); }); @@ -994,8 +993,8 @@ describe('SessionManager', () => { }) ).rejects.toMatchObject({ code: SessionErrorCode.SESSION_STORAGE_FAILED, - scope: ErrorScope.SESSION, - type: ErrorType.SYSTEM, + scope: 'session', + type: 'system', }); await expect( @@ -1028,8 +1027,8 @@ describe('SessionManager', () => { sessionManager.getSessionSystemPromptContributors(sessionId) ).rejects.toMatchObject({ code: SessionErrorCode.SESSION_STORAGE_FAILED, - scope: ErrorScope.SESSION, - type: ErrorType.SYSTEM, + scope: 'session', + type: 'system', }); }); @@ -1140,8 +1139,8 @@ describe('SessionManager', () => { sessionManager.switchLLMForSpecificSession(newLLMConfig, 'non-existent') ).rejects.toMatchObject({ code: SessionErrorCode.SESSION_NOT_FOUND, - scope: ErrorScope.SESSION, - type: ErrorType.NOT_FOUND, + scope: 'session', + type: 'not_found', }); }); @@ -1294,8 +1293,8 @@ describe('SessionManager', () => { failures.forEach((failure) => { const err = (failure as PromiseRejectedResult).reason as any; expect(err.code).toBe(SessionErrorCode.SESSION_MAX_SESSIONS_EXCEEDED); - expect(err.scope).toBe(ErrorScope.SESSION); - expect(err.type).toBe(ErrorType.USER); + expect(err.scope).toBe('session'); + expect(err.type).toBe('user'); }); // Clean up diff --git a/packages/core/src/storage/error-codes.ts b/packages/core/src/storage/error-codes.ts index 71c457666..65aa745cc 100644 --- a/packages/core/src/storage/error-codes.ts +++ b/packages/core/src/storage/error-codes.ts @@ -1,53 +1,28 @@ -/** - * Storage-specific error codes - * Includes cache, database, and blob storage errors - */ -export enum StorageErrorCode { - // Manager lifecycle - MANAGER_NOT_INITIALIZED = 'storage_manager_not_initialized', - MANAGER_NOT_CONNECTED = 'storage_manager_not_connected', - - // Dependencies - DEPENDENCY_NOT_INSTALLED = 'storage_dependency_not_installed', - - // Connection - CONNECTION_FAILED = 'storage_connection_failed', - CONNECTION_CONFIG_MISSING = 'storage_connection_config_missing', - - // Operations - READ_FAILED = 'storage_read_failed', - WRITE_FAILED = 'storage_write_failed', - DELETE_FAILED = 'storage_delete_failed', - - // Database specific - MIGRATION_FAILED = 'storage_migration_failed', - DATABASE_INVALID_CONFIG = 'storage_database_invalid_config', - - // Cache specific - CACHE_INVALID_CONFIG = 'storage_cache_invalid_config', - - // Blob storage - Configuration errors - BLOB_INVALID_CONFIG = 'BLOB_INVALID_CONFIG', - - // Blob storage - Storage errors - BLOB_SIZE_EXCEEDED = 'BLOB_SIZE_EXCEEDED', - BLOB_TOTAL_SIZE_EXCEEDED = 'BLOB_TOTAL_SIZE_EXCEEDED', - BLOB_INVALID_INPUT = 'BLOB_INVALID_INPUT', - BLOB_ENCODING_ERROR = 'BLOB_ENCODING_ERROR', - - // Blob storage - Retrieval errors - BLOB_NOT_FOUND = 'BLOB_NOT_FOUND', - BLOB_INVALID_REFERENCE = 'BLOB_INVALID_REFERENCE', - BLOB_ACCESS_DENIED = 'BLOB_ACCESS_DENIED', - BLOB_CORRUPTED = 'BLOB_CORRUPTED', - - // Blob storage - Backend errors - BLOB_BACKEND_NOT_CONNECTED = 'BLOB_BACKEND_NOT_CONNECTED', - BLOB_BACKEND_UNAVAILABLE = 'BLOB_BACKEND_UNAVAILABLE', - - // Blob storage - Operation errors - BLOB_CLEANUP_FAILED = 'BLOB_CLEANUP_FAILED', - BLOB_OPERATION_FAILED = 'BLOB_OPERATION_FAILED', - - // Note: Registry-era error codes were removed as part of the DI refactor. -} +export const STORAGE_ERROR_CODES = [ + 'storage_manager_not_initialized', + 'storage_manager_not_connected', + 'storage_dependency_not_installed', + 'storage_connection_failed', + 'storage_connection_config_missing', + 'storage_read_failed', + 'storage_write_failed', + 'storage_delete_failed', + 'storage_migration_failed', + 'storage_database_invalid_config', + 'storage_cache_invalid_config', + 'BLOB_INVALID_CONFIG', + 'BLOB_SIZE_EXCEEDED', + 'BLOB_TOTAL_SIZE_EXCEEDED', + 'BLOB_INVALID_INPUT', + 'BLOB_ENCODING_ERROR', + 'BLOB_NOT_FOUND', + 'BLOB_INVALID_REFERENCE', + 'BLOB_ACCESS_DENIED', + 'BLOB_CORRUPTED', + 'BLOB_BACKEND_NOT_CONNECTED', + 'BLOB_BACKEND_UNAVAILABLE', + 'BLOB_CLEANUP_FAILED', + 'BLOB_OPERATION_FAILED', +] as const; + +export type StorageErrorCode = (typeof STORAGE_ERROR_CODES)[number]; diff --git a/packages/core/src/storage/errors.ts b/packages/core/src/storage/errors.ts index 5af8805ee..ec167e132 100644 --- a/packages/core/src/storage/errors.ts +++ b/packages/core/src/storage/errors.ts @@ -1,6 +1,4 @@ import { DextoRuntimeError, DextoValidationError } from '../errors/index.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; -import { StorageErrorCode } from './error-codes.js'; /** * Storage error factory with typed methods for creating storage-specific errors @@ -13,9 +11,9 @@ export class StorageError { */ static connectionFailed(reason: string, config?: Record) { return new DextoRuntimeError( - StorageErrorCode.CONNECTION_FAILED, - ErrorScope.STORAGE, - ErrorType.THIRD_PARTY, + 'storage_connection_failed', + 'storage', + 'third_party', `Storage connection failed: ${reason}`, { reason, config } ); @@ -26,9 +24,9 @@ export class StorageError { */ static notConnected(backendType: string) { return new DextoRuntimeError( - StorageErrorCode.CONNECTION_FAILED, - ErrorScope.STORAGE, - ErrorType.SYSTEM, + 'storage_connection_failed', + 'storage', + 'system', `${backendType} not connected`, { backendType } ); @@ -39,9 +37,9 @@ export class StorageError { */ static managerNotInitialized(method: string) { return new DextoRuntimeError( - StorageErrorCode.MANAGER_NOT_INITIALIZED, - ErrorScope.STORAGE, - ErrorType.USER, + 'storage_manager_not_initialized', + 'storage', + 'user', `StorageManager is not initialized. Call initialize() before ${method}()`, { method, hint: 'Call await manager.initialize() first' } ); @@ -52,9 +50,9 @@ export class StorageError { */ static managerNotConnected(method: string) { return new DextoRuntimeError( - StorageErrorCode.MANAGER_NOT_CONNECTED, - ErrorScope.STORAGE, - ErrorType.USER, + 'storage_manager_not_connected', + 'storage', + 'user', `StorageManager is not connected. Call connect() before ${method}()`, { method, hint: 'Call await manager.connect() after initialize()' } ); @@ -69,9 +67,9 @@ export class StorageError { installCommand: string ) { return new DextoRuntimeError( - StorageErrorCode.DEPENDENCY_NOT_INSTALLED, - ErrorScope.STORAGE, - ErrorType.USER, + 'storage_dependency_not_installed', + 'storage', + 'user', `${backendType} storage configured but '${packageName}' package is not installed`, { backendType, @@ -87,9 +85,9 @@ export class StorageError { */ static readFailed(operation: string, reason: string, details?: Record) { return new DextoRuntimeError( - StorageErrorCode.READ_FAILED, - ErrorScope.STORAGE, - ErrorType.SYSTEM, + 'storage_read_failed', + 'storage', + 'system', `Storage read failed for ${operation}: ${reason}`, { operation, reason, ...details } ); @@ -100,9 +98,9 @@ export class StorageError { */ static writeFailed(operation: string, reason: string, details?: Record) { return new DextoRuntimeError( - StorageErrorCode.WRITE_FAILED, - ErrorScope.STORAGE, - ErrorType.SYSTEM, + 'storage_write_failed', + 'storage', + 'system', `Storage write failed for ${operation}: ${reason}`, { operation, reason, ...details } ); @@ -113,9 +111,9 @@ export class StorageError { */ static deleteFailed(operation: string, reason: string, details?: Record) { return new DextoRuntimeError( - StorageErrorCode.DELETE_FAILED, - ErrorScope.STORAGE, - ErrorType.SYSTEM, + 'storage_delete_failed', + 'storage', + 'system', `Storage delete failed for ${operation}: ${reason}`, { operation, reason, ...details } ); @@ -126,9 +124,9 @@ export class StorageError { */ static migrationFailed(reason: string, details?: Record) { return new DextoRuntimeError( - StorageErrorCode.MIGRATION_FAILED, - ErrorScope.STORAGE, - ErrorType.SYSTEM, + 'storage_migration_failed', + 'storage', + 'system', `Database migration failed: ${reason}`, { reason, ...details } ); @@ -143,10 +141,10 @@ export class StorageError { ): DextoValidationError { return new DextoValidationError([ { - code: StorageErrorCode.DATABASE_INVALID_CONFIG, + code: 'storage_database_invalid_config', message, - scope: ErrorScope.STORAGE, - type: ErrorType.USER, + scope: 'storage', + type: 'user', severity: 'error' as const, context: context || {}, }, @@ -162,10 +160,10 @@ export class StorageError { ): DextoValidationError { return new DextoValidationError([ { - code: StorageErrorCode.CACHE_INVALID_CONFIG, + code: 'storage_cache_invalid_config', message, - scope: ErrorScope.STORAGE, - type: ErrorType.USER, + scope: 'storage', + type: 'user', severity: 'error' as const, context: context || {}, }, @@ -183,10 +181,10 @@ export class StorageError { ): DextoValidationError { return new DextoValidationError([ { - code: StorageErrorCode.BLOB_INVALID_CONFIG, + code: 'BLOB_INVALID_CONFIG', message, - scope: ErrorScope.STORAGE, - type: ErrorType.USER, + scope: 'storage', + type: 'user', severity: 'error' as const, context: context || {}, }, @@ -198,9 +196,9 @@ export class StorageError { */ static blobSizeExceeded(size: number, maxSize: number): DextoRuntimeError { return new DextoRuntimeError( - StorageErrorCode.BLOB_SIZE_EXCEEDED, - ErrorScope.STORAGE, - ErrorType.USER, + 'BLOB_SIZE_EXCEEDED', + 'storage', + 'user', `Blob size ${size} bytes exceeds maximum ${maxSize} bytes`, { size, maxSize } ); @@ -211,9 +209,9 @@ export class StorageError { */ static blobTotalSizeExceeded(totalSize: number, maxTotalSize: number): DextoRuntimeError { return new DextoRuntimeError( - StorageErrorCode.BLOB_TOTAL_SIZE_EXCEEDED, - ErrorScope.STORAGE, - ErrorType.SYSTEM, + 'BLOB_TOTAL_SIZE_EXCEEDED', + 'storage', + 'system', `Total storage size ${totalSize} bytes exceeds maximum ${maxTotalSize} bytes`, { totalSize, maxTotalSize } ); @@ -224,9 +222,9 @@ export class StorageError { */ static blobInvalidInput(input: unknown, reason: string): DextoRuntimeError { return new DextoRuntimeError( - StorageErrorCode.BLOB_INVALID_INPUT, - ErrorScope.STORAGE, - ErrorType.USER, + 'BLOB_INVALID_INPUT', + 'storage', + 'user', `Invalid blob input: ${reason}`, { inputType: typeof input, reason } ); @@ -237,9 +235,9 @@ export class StorageError { */ static blobEncodingError(operation: string, error: unknown): DextoRuntimeError { return new DextoRuntimeError( - StorageErrorCode.BLOB_ENCODING_ERROR, - ErrorScope.STORAGE, - ErrorType.SYSTEM, + 'BLOB_ENCODING_ERROR', + 'storage', + 'system', `Blob ${operation} failed: encoding error`, { operation, originalError: error instanceof Error ? error.message : String(error) } ); @@ -250,9 +248,9 @@ export class StorageError { */ static blobNotFound(reference: string): DextoRuntimeError { return new DextoRuntimeError( - StorageErrorCode.BLOB_NOT_FOUND, - ErrorScope.STORAGE, - ErrorType.NOT_FOUND, + 'BLOB_NOT_FOUND', + 'storage', + 'not_found', `Blob not found: ${reference}`, { reference } ); @@ -263,9 +261,9 @@ export class StorageError { */ static blobInvalidReference(reference: string, reason: string): DextoRuntimeError { return new DextoRuntimeError( - StorageErrorCode.BLOB_INVALID_REFERENCE, - ErrorScope.STORAGE, - ErrorType.USER, + 'BLOB_INVALID_REFERENCE', + 'storage', + 'user', `Invalid blob reference '${reference}': ${reason}`, { reference, reason } ); @@ -276,9 +274,9 @@ export class StorageError { */ static blobAccessDenied(reference: string, operation: string): DextoRuntimeError { return new DextoRuntimeError( - StorageErrorCode.BLOB_ACCESS_DENIED, - ErrorScope.STORAGE, - ErrorType.FORBIDDEN, + 'BLOB_ACCESS_DENIED', + 'storage', + 'forbidden', `Access denied for blob ${operation}: ${reference}`, { reference, operation } ); @@ -289,9 +287,9 @@ export class StorageError { */ static blobCorrupted(reference: string, reason: string): DextoRuntimeError { return new DextoRuntimeError( - StorageErrorCode.BLOB_CORRUPTED, - ErrorScope.STORAGE, - ErrorType.SYSTEM, + 'BLOB_CORRUPTED', + 'storage', + 'system', `Blob data corrupted: ${reference} (${reason})`, { reference, reason } ); @@ -302,9 +300,9 @@ export class StorageError { */ static blobBackendNotConnected(backendType: string): DextoRuntimeError { return new DextoRuntimeError( - StorageErrorCode.BLOB_BACKEND_NOT_CONNECTED, - ErrorScope.STORAGE, - ErrorType.THIRD_PARTY, + 'BLOB_BACKEND_NOT_CONNECTED', + 'storage', + 'third_party', `Blob backend ${backendType} is not connected`, { backendType } ); @@ -315,9 +313,9 @@ export class StorageError { */ static blobBackendUnavailable(backendType: string, error: unknown): DextoRuntimeError { return new DextoRuntimeError( - StorageErrorCode.BLOB_BACKEND_UNAVAILABLE, - ErrorScope.STORAGE, - ErrorType.THIRD_PARTY, + 'BLOB_BACKEND_UNAVAILABLE', + 'storage', + 'third_party', `Blob backend ${backendType} is unavailable`, { backendType, originalError: error instanceof Error ? error.message : String(error) } ); @@ -328,9 +326,9 @@ export class StorageError { */ static blobCleanupFailed(backendType: string, error: unknown): DextoRuntimeError { return new DextoRuntimeError( - StorageErrorCode.BLOB_CLEANUP_FAILED, - ErrorScope.STORAGE, - ErrorType.SYSTEM, + 'BLOB_CLEANUP_FAILED', + 'storage', + 'system', `Blob cleanup failed for backend ${backendType}`, { backendType, originalError: error instanceof Error ? error.message : String(error) } ); @@ -345,9 +343,9 @@ export class StorageError { error: unknown ): DextoRuntimeError { return new DextoRuntimeError( - StorageErrorCode.BLOB_OPERATION_FAILED, - ErrorScope.STORAGE, - ErrorType.SYSTEM, + 'BLOB_OPERATION_FAILED', + 'storage', + 'system', `Blob ${operation} failed for backend ${backendType}`, { operation, diff --git a/packages/core/src/storage/index.ts b/packages/core/src/storage/index.ts index f16b6d1f9..76cd94dc7 100644 --- a/packages/core/src/storage/index.ts +++ b/packages/core/src/storage/index.ts @@ -13,7 +13,8 @@ export { StorageManager } from './storage-manager.js'; export type { StorageBackends } from './storage-manager.js'; export { StorageError } from './errors.js'; -export { StorageErrorCode } from './error-codes.js'; +export { STORAGE_ERROR_CODES } from './error-codes.js'; +export type { StorageErrorCode } from './error-codes.js'; export type { Cache } from './cache/types.js'; export type { Database } from './database/types.js'; diff --git a/packages/core/src/systemPrompt/contributors.test.ts b/packages/core/src/systemPrompt/contributors.test.ts index 7abfec4a4..0cf80d364 100644 --- a/packages/core/src/systemPrompt/contributors.test.ts +++ b/packages/core/src/systemPrompt/contributors.test.ts @@ -5,7 +5,6 @@ import { join } from 'path'; import { DynamicContributorContext } from './types.js'; import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; import { SystemPromptErrorCode } from './error-codes.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { createMockLogger } from '../logger/v2/test-utils.js'; const mockLogger = createMockLogger(); @@ -101,8 +100,8 @@ describe('FileContributor', () => { .catch((e) => e)) as DextoRuntimeError; expect(error).toBeInstanceOf(DextoRuntimeError); expect(error.code).toBe(SystemPromptErrorCode.FILE_READ_FAILED); - expect(error.scope).toBe(ErrorScope.SYSTEM_PROMPT); - expect(error.type).toBe(ErrorType.SYSTEM); + expect(error.scope).toBe('system_prompt'); + expect(error.type).toBe('system'); }); test('should throw error for missing files with single file error mode', async () => { @@ -121,8 +120,8 @@ describe('FileContributor', () => { .catch((e) => e)) as DextoRuntimeError; expect(error).toBeInstanceOf(DextoRuntimeError); expect(error.code).toBe(SystemPromptErrorCode.FILE_READ_FAILED); - expect(error.scope).toBe(ErrorScope.SYSTEM_PROMPT); - expect(error.type).toBe(ErrorType.SYSTEM); + expect(error.scope).toBe('system_prompt'); + expect(error.type).toBe('system'); }); test('should skip large files with skip mode', async () => { diff --git a/packages/core/src/systemPrompt/errors.ts b/packages/core/src/systemPrompt/errors.ts index 953b16825..fab967300 100644 --- a/packages/core/src/systemPrompt/errors.ts +++ b/packages/core/src/systemPrompt/errors.ts @@ -1,5 +1,4 @@ import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { SystemPromptErrorCode } from './error-codes.js'; import { safeStringify } from '../utils/safe-stringify.js'; @@ -14,8 +13,8 @@ export class SystemPromptError { static invalidFileType(filePath: string, allowedExtensions: string[]) { return new DextoRuntimeError( SystemPromptErrorCode.FILE_INVALID_TYPE, - ErrorScope.SYSTEM_PROMPT, - ErrorType.USER, + 'system_prompt', + 'user', `File ${filePath} is not a ${allowedExtensions.join(' or ')} file`, { filePath, allowedExtensions } ); @@ -27,8 +26,8 @@ export class SystemPromptError { static fileTooLarge(filePath: string, fileSize: number, maxSize: number) { return new DextoRuntimeError( SystemPromptErrorCode.FILE_TOO_LARGE, - ErrorScope.SYSTEM_PROMPT, - ErrorType.USER, + 'system_prompt', + 'user', `File ${filePath} exceeds maximum size of ${maxSize} bytes`, { filePath, fileSize, maxSize } ); @@ -40,8 +39,8 @@ export class SystemPromptError { static fileReadFailed(filePath: string, reason: string) { return new DextoRuntimeError( SystemPromptErrorCode.FILE_READ_FAILED, - ErrorScope.SYSTEM_PROMPT, - ErrorType.SYSTEM, + 'system_prompt', + 'system', `Failed to read file ${filePath}: ${reason}`, { filePath, reason } ); @@ -53,8 +52,8 @@ export class SystemPromptError { static unknownContributorSource(source: string) { return new DextoRuntimeError( SystemPromptErrorCode.CONTRIBUTOR_SOURCE_UNKNOWN, - ErrorScope.SYSTEM_PROMPT, - ErrorType.USER, + 'system_prompt', + 'user', `No generator registered for dynamic contributor source: ${source}`, { source } ); @@ -66,8 +65,8 @@ export class SystemPromptError { static invalidContributorConfig(config: unknown): DextoRuntimeError { return new DextoRuntimeError( SystemPromptErrorCode.CONTRIBUTOR_CONFIG_INVALID, - ErrorScope.SYSTEM_PROMPT, - ErrorType.USER, + 'system_prompt', + 'user', `Invalid contributor config: ${safeStringify(config)}`, { config } ); diff --git a/packages/core/src/systemPrompt/manager.test.ts b/packages/core/src/systemPrompt/manager.test.ts index db540b110..95c97533c 100644 --- a/packages/core/src/systemPrompt/manager.test.ts +++ b/packages/core/src/systemPrompt/manager.test.ts @@ -5,7 +5,6 @@ import type { DynamicContributorContext } from './types.js'; import * as registry from './registry.js'; import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; import { SystemPromptErrorCode } from './error-codes.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import * as path from 'path'; // Mock the registry functions @@ -288,8 +287,8 @@ You can help with: })() as DextoRuntimeError; expect(error).toBeInstanceOf(DextoRuntimeError); expect(error.code).toBe(SystemPromptErrorCode.CONTRIBUTOR_SOURCE_UNKNOWN); - expect(error.scope).toBe(ErrorScope.SYSTEM_PROMPT); - expect(error.type).toBe(ErrorType.USER); + expect(error.scope).toBe('system_prompt'); + expect(error.type).toBe('user'); }); it('should handle multiple dynamic contributors', async () => { diff --git a/packages/core/src/telemetry/errors.ts b/packages/core/src/telemetry/errors.ts index 0cd898d28..7b198846f 100644 --- a/packages/core/src/telemetry/errors.ts +++ b/packages/core/src/telemetry/errors.ts @@ -1,5 +1,4 @@ import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { TelemetryErrorCode } from './error-codes.js'; /** @@ -13,8 +12,8 @@ export class TelemetryError { static dependencyNotInstalled(packages: string[]): DextoRuntimeError { return new DextoRuntimeError( TelemetryErrorCode.DEPENDENCY_NOT_INSTALLED, - ErrorScope.TELEMETRY, - ErrorType.USER, + 'telemetry', + 'user', 'Telemetry is enabled but required OpenTelemetry packages are not installed.', { packages, @@ -33,8 +32,8 @@ export class TelemetryError { ): DextoRuntimeError { return new DextoRuntimeError( TelemetryErrorCode.EXPORTER_DEPENDENCY_NOT_INSTALLED, - ErrorScope.TELEMETRY, - ErrorType.USER, + 'telemetry', + 'user', `OTLP ${exporterType.toUpperCase()} exporter configured but '${packageName}' is not installed.`, { exporterType, @@ -50,8 +49,8 @@ export class TelemetryError { static initializationFailed(reason: string, originalError?: unknown): DextoRuntimeError { return new DextoRuntimeError( TelemetryErrorCode.INITIALIZATION_FAILED, - ErrorScope.TELEMETRY, - ErrorType.SYSTEM, + 'telemetry', + 'system', `Failed to initialize telemetry: ${reason}`, { reason, @@ -67,8 +66,8 @@ export class TelemetryError { static notInitialized(): DextoRuntimeError { return new DextoRuntimeError( TelemetryErrorCode.NOT_INITIALIZED, - ErrorScope.TELEMETRY, - ErrorType.USER, + 'telemetry', + 'user', 'Telemetry not initialized. Call Telemetry.init() first.', { hint: 'Ensure telemetry is initialized before accessing the global instance.', @@ -82,8 +81,8 @@ export class TelemetryError { static shutdownFailed(reason: string): DextoRuntimeError { return new DextoRuntimeError( TelemetryErrorCode.SHUTDOWN_FAILED, - ErrorScope.TELEMETRY, - ErrorType.SYSTEM, + 'telemetry', + 'system', `Telemetry shutdown failed: ${reason}`, { reason } ); diff --git a/packages/core/src/tools/errors.ts b/packages/core/src/tools/errors.ts index d6b36f05b..42853af65 100644 --- a/packages/core/src/tools/errors.ts +++ b/packages/core/src/tools/errors.ts @@ -1,5 +1,4 @@ import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { ToolErrorCode } from './error-codes.js'; /** @@ -13,8 +12,8 @@ export class ToolError { static notFound(toolName: string) { return new DextoRuntimeError( ToolErrorCode.TOOL_NOT_FOUND, - ErrorScope.TOOLS, - ErrorType.NOT_FOUND, + 'tools', + 'not_found', `Tool '${toolName}' not found`, { toolName } ); @@ -26,8 +25,8 @@ export class ToolError { static executionFailed(toolName: string, reason: string, sessionId?: string) { return new DextoRuntimeError( ToolErrorCode.EXECUTION_FAILED, - ErrorScope.TOOLS, - ErrorType.SYSTEM, + 'tools', + 'system', `Tool '${toolName}' execution failed: ${reason}`, { toolName, reason, sessionId } ); @@ -45,8 +44,8 @@ export class ToolError { : `Tool '${toolName}' execution was denied by the user`; return new DextoRuntimeError( ToolErrorCode.EXECUTION_DENIED, - ErrorScope.TOOLS, - ErrorType.FORBIDDEN, + 'tools', + 'forbidden', message, { toolName, sessionId, userMessage } ); @@ -60,8 +59,8 @@ export class ToolError { static directoryAccessDenied(directory: string, sessionId?: string) { return new DextoRuntimeError( ToolErrorCode.DIRECTORY_ACCESS_DENIED, - ErrorScope.TOOLS, - ErrorType.FORBIDDEN, + 'tools', + 'forbidden', `Access to directory '${directory}' was denied`, { directory, sessionId }, 'Request access to the directory or work within the allowed working directory' @@ -76,13 +75,11 @@ export class ToolError { timeoutMs > 0 ? `Tool '${toolName}' execution timed out after ${timeoutMs}ms` : `Tool '${toolName}' execution timed out`; - return new DextoRuntimeError( - ToolErrorCode.EXECUTION_TIMEOUT, - ErrorScope.TOOLS, - ErrorType.TIMEOUT, - message, - { toolName, timeoutMs, sessionId } - ); + return new DextoRuntimeError(ToolErrorCode.EXECUTION_TIMEOUT, 'tools', 'timeout', message, { + toolName, + timeoutMs, + sessionId, + }); } /** @@ -93,8 +90,8 @@ export class ToolError { static validationFailed(toolName: string, reason: string, context?: Record) { return new DextoRuntimeError( ToolErrorCode.VALIDATION_FAILED, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `Tool '${toolName}' validation failed: ${reason}`, { toolName, reason, ...context } ); @@ -107,8 +104,8 @@ export class ToolError { static fileModifiedSincePreview(toolName: string, filePath: string) { return new DextoRuntimeError( ToolErrorCode.FILE_MODIFIED_SINCE_PREVIEW, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `File '${filePath}' was modified since the preview was generated. Please read the file again and retry the operation.`, { toolName, @@ -125,8 +122,8 @@ export class ToolError { static unauthorized(toolName: string, sessionId?: string) { return new DextoRuntimeError( ToolErrorCode.TOOL_UNAUTHORIZED, - ErrorScope.TOOLS, - ErrorType.FORBIDDEN, + 'tools', + 'forbidden', `Unauthorized access to tool '${toolName}'`, { toolName, sessionId } ); @@ -138,8 +135,8 @@ export class ToolError { static confirmationHandlerMissing(toolName: string) { return new DextoRuntimeError( ToolErrorCode.CONFIRMATION_HANDLER_MISSING, - ErrorScope.TOOLS, - ErrorType.SYSTEM, + 'tools', + 'system', `Confirmation handler missing for tool '${toolName}'`, { toolName } ); @@ -151,8 +148,8 @@ export class ToolError { static confirmationTimeout(toolName: string, timeoutMs: number, sessionId?: string) { return new DextoRuntimeError( ToolErrorCode.CONFIRMATION_TIMEOUT, - ErrorScope.TOOLS, - ErrorType.TIMEOUT, + 'tools', + 'timeout', `Tool '${toolName}' confirmation timed out after ${timeoutMs}ms`, { toolName, timeoutMs, sessionId } ); @@ -164,8 +161,8 @@ export class ToolError { static invalidName(toolName: string, reason: string) { return new DextoRuntimeError( ToolErrorCode.TOOL_INVALID_ARGS, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `Invalid tool name '${toolName}': ${reason}`, { toolName, reason } ); @@ -175,13 +172,7 @@ export class ToolError { * Invalid tool configuration */ static configInvalid(message: string) { - return new DextoRuntimeError( - ToolErrorCode.CONFIG_INVALID, - ErrorScope.TOOLS, - ErrorType.USER, - message, - {} - ); + return new DextoRuntimeError(ToolErrorCode.CONFIG_INVALID, 'tools', 'user', message, {}); } /** @@ -190,8 +181,8 @@ export class ToolError { static confirmationCancelled(toolName: string, reason: string) { return new DextoRuntimeError( ToolErrorCode.CONFIRMATION_CANCELLED, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `Tool confirmation for '${toolName}' was cancelled: ${reason}`, { toolName, reason } ); @@ -207,8 +198,8 @@ export class ToolError { ): DextoRuntimeError<{ toolName: string; missingFeatures: string[] }> { return new DextoRuntimeError( ToolErrorCode.FEATURE_DISABLED, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', message, { toolName, missingFeatures }, [ @@ -224,8 +215,8 @@ export class ToolError { static unknownCustomToolFactory(type: string, availableTypes: string[]): DextoRuntimeError { return new DextoRuntimeError( ToolErrorCode.CUSTOM_TOOL_FACTORY_UNKNOWN, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `Unknown custom tool factory: '${type}'`, { type, availableTypes }, `Available types: ${availableTypes.length > 0 ? availableTypes.join(', ') : 'none'}` @@ -238,8 +229,8 @@ export class ToolError { static customToolFactoryAlreadyRegistered(type: string): DextoRuntimeError { return new DextoRuntimeError( ToolErrorCode.CUSTOM_TOOL_FACTORY_ALREADY_REGISTERED, - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `Custom tool factory '${type}' is already registered`, { type }, `Use unregister() first if you want to replace it` diff --git a/packages/core/src/tools/tool-manager.integration.test.ts b/packages/core/src/tools/tool-manager.integration.test.ts index 438e546d7..991d17117 100644 --- a/packages/core/src/tools/tool-manager.integration.test.ts +++ b/packages/core/src/tools/tool-manager.integration.test.ts @@ -3,7 +3,6 @@ import { ToolManager } from './tool-manager.js'; import { MCPManager } from '../mcp/manager.js'; import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; import { ToolErrorCode } from './error-codes.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { z } from 'zod'; import type { McpClient } from '../mcp/types.js'; import { AgentEventBus } from '../events/index.js'; @@ -389,8 +388,8 @@ describe('ToolManager Integration Tests', () => { .catch((e) => e)) as DextoRuntimeError; expect(error).toBeInstanceOf(DextoRuntimeError); expect(error.code).toBe(ToolErrorCode.EXECUTION_DENIED); - expect(error.scope).toBe(ErrorScope.TOOLS); - expect(error.type).toBe(ErrorType.FORBIDDEN); + expect(error.scope).toBe('tools'); + expect(error.type).toBe('forbidden'); expect(mockClient.callTool).not.toHaveBeenCalled(); }); diff --git a/packages/core/src/tools/tool-manager.test.ts b/packages/core/src/tools/tool-manager.test.ts index 5eb6a1180..cd17160d3 100644 --- a/packages/core/src/tools/tool-manager.test.ts +++ b/packages/core/src/tools/tool-manager.test.ts @@ -5,7 +5,6 @@ import { defineTool } from './define-tool.js'; import { MCPManager } from '../mcp/manager.js'; import { DextoRuntimeError } from '../errors/DextoRuntimeError.js'; import { ToolErrorCode } from './error-codes.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; import { AgentEventBus } from '../events/index.js'; import type { ApprovalManager } from '../approval/manager.js'; import type { AllowedToolsProvider } from './confirmation/allowed-tools-provider/types.js'; @@ -316,8 +315,8 @@ describe('ToolManager - Unit Tests (Pure Logic)', () => { .catch((e) => e)) as DextoRuntimeError; expect(error).toBeInstanceOf(DextoRuntimeError); expect(error.code).toBe(ToolErrorCode.TOOL_NOT_FOUND); - expect(error.scope).toBe(ErrorScope.TOOLS); - expect(error.type).toBe(ErrorType.NOT_FOUND); + expect(error.scope).toBe('tools'); + expect(error.type).toBe('not_found'); }); it('should reject MCP tools with prefix but no name', async () => { @@ -337,8 +336,8 @@ describe('ToolManager - Unit Tests (Pure Logic)', () => { .catch((e) => e)) as DextoRuntimeError; expect(mcpError).toBeInstanceOf(DextoRuntimeError); expect(mcpError.code).toBe(ToolErrorCode.TOOL_INVALID_ARGS); - expect(mcpError.scope).toBe(ErrorScope.TOOLS); - expect(mcpError.type).toBe(ErrorType.USER); + expect(mcpError.scope).toBe('tools'); + expect(mcpError.type).toBe('user'); // Should NOT call the underlying managers expect(mockMcpManager.executeTool).not.toHaveBeenCalled(); @@ -361,8 +360,8 @@ describe('ToolManager - Unit Tests (Pure Logic)', () => { .catch((e) => e)) as DextoRuntimeError; expect(error).toBeInstanceOf(DextoRuntimeError); expect(error.code).toBe(ToolErrorCode.TOOL_NOT_FOUND); - expect(error.scope).toBe(ErrorScope.TOOLS); - expect(error.type).toBe(ErrorType.NOT_FOUND); + expect(error.scope).toBe('tools'); + expect(error.type).toBe('not_found'); }); }); @@ -1065,8 +1064,8 @@ describe('ToolManager - Unit Tests (Pure Logic)', () => { .catch((e) => e)) as DextoRuntimeError; expect(error).toBeInstanceOf(DextoRuntimeError); expect(error.code).toBe(ToolErrorCode.EXECUTION_DENIED); - expect(error.scope).toBe(ErrorScope.TOOLS); - expect(error.type).toBe(ErrorType.FORBIDDEN); + expect(error.scope).toBe('tools'); + expect(error.type).toBe('forbidden'); expect(mockMcpManager.executeTool).not.toHaveBeenCalled(); }); diff --git a/packages/core/src/tools/tool-manager.ts b/packages/core/src/tools/tool-manager.ts index 4ec367199..82ed099a6 100644 --- a/packages/core/src/tools/tool-manager.ts +++ b/packages/core/src/tools/tool-manager.ts @@ -10,7 +10,7 @@ import { import type { ToolDisplayData } from './display-types.js'; import { ToolError } from './errors.js'; import { ToolErrorCode } from './error-codes.js'; -import { DextoRuntimeError, ErrorScope, ErrorType } from '../errors/index.js'; +import { DextoRuntimeError } from '../errors/index.js'; import type { Logger } from '../logger/v2/types.js'; import { DextoLogComponent } from '../logger/v2/types.js'; import { convertZodSchemaToJsonSchema } from '../utils/schema.js'; @@ -1090,8 +1090,8 @@ export class ToolManager { } catch (error) { if ( error instanceof DextoRuntimeError && - error.scope === ErrorScope.SESSION && - error.type === ErrorType.NOT_FOUND + error.scope === 'session' && + error.type === 'not_found' ) { this.logger.debug('Session not found while building contributor context', { sessionId, diff --git a/packages/core/src/utils/result.test.ts b/packages/core/src/utils/result.test.ts index 271c335b8..359d0ca32 100644 --- a/packages/core/src/utils/result.test.ts +++ b/packages/core/src/utils/result.test.ts @@ -1,7 +1,6 @@ import { describe, test, expect } from 'vitest'; import { z, ZodError } from 'zod'; import { zodToIssues, ok, fail, hasErrors, splitIssues } from './result.js'; -import { ErrorScope, ErrorType } from '../errors/index.js'; import type { Issue } from '../errors/index.js'; // Helper to create test issues with less boilerplate @@ -13,8 +12,8 @@ const makeIssue = ( code, message, severity, - scope: ErrorScope.AGENT, - type: ErrorType.USER, + scope: 'agent', + type: 'user', context: {}, }); @@ -37,8 +36,8 @@ describe('zodToIssues', () => { message: 'Expected number, received string', path: ['age'], severity: 'error', - scope: ErrorScope.AGENT, - type: ErrorType.USER, + scope: 'agent', + type: 'user', }); } }); diff --git a/packages/core/src/utils/result.ts b/packages/core/src/utils/result.ts index 62b523246..0f354424d 100644 --- a/packages/core/src/utils/result.ts +++ b/packages/core/src/utils/result.ts @@ -1,7 +1,6 @@ // schemas/helpers.ts import { z, type ZodError } from 'zod'; import type { DextoErrorCode, Issue } from '../errors/types.js'; -import { ErrorScope, ErrorType } from '../errors/types.js'; /** Trim and require non-empty after trim */ export const NonEmptyTrimmed = z @@ -255,8 +254,8 @@ export function zodToIssues( issues.push({ code: (params.code ?? 'schema_validation') as DextoErrorCode, message: e.message, - scope: params.scope ?? ErrorScope.AGENT, - type: params.type ?? ErrorType.USER, + scope: params.scope ?? 'agent', + type: params.type ?? 'user', path: e.path, severity, context: params as C, @@ -268,8 +267,8 @@ export function zodToIssues( issues.push({ code: (params.code ?? 'schema_validation') as DextoErrorCode, message: e.message, - scope: params.scope ?? ErrorScope.AGENT, - type: params.type ?? ErrorType.USER, + scope: params.scope ?? 'agent', + type: params.type ?? 'user', path: e.path, severity, context: params as C, diff --git a/packages/core/src/workspace/errors.ts b/packages/core/src/workspace/errors.ts index 34d74a059..1f028676e 100644 --- a/packages/core/src/workspace/errors.ts +++ b/packages/core/src/workspace/errors.ts @@ -1,5 +1,4 @@ import { DextoValidationError } from '../errors/DextoValidationError.js'; -import { ErrorType } from '../errors/types.js'; import { WorkspaceErrorCodes } from './error-codes.js'; export class WorkspaceError { @@ -9,7 +8,7 @@ export class WorkspaceError { code: WorkspaceErrorCodes.PATH_REQUIRED, message: 'Workspace path is required', scope: 'workspace', - type: ErrorType.USER, + type: 'user', severity: 'error', path: ['path'], }, diff --git a/packages/server/src/hono/middleware/error.ts b/packages/server/src/hono/middleware/error.ts index 1bf8fcfa3..b4dfe96c8 100644 --- a/packages/server/src/hono/middleware/error.ts +++ b/packages/server/src/hono/middleware/error.ts @@ -1,4 +1,5 @@ -import { DextoRuntimeError, DextoValidationError, ErrorType, zodToIssues } from '@dexto/core'; +import { DextoRuntimeError, DextoValidationError, zodToIssues } from '@dexto/core'; +import type { ErrorType } from '@dexto/core'; import { logger } from '@dexto/core'; import { ZodError } from 'zod'; @@ -14,25 +15,25 @@ import { ZodError } from 'zod'; export const mapErrorTypeToStatus = (type: ErrorType): number => { switch (type) { - case ErrorType.USER: + case 'user': return 400; - case ErrorType.PAYMENT_REQUIRED: + case 'payment_required': return 402; - case ErrorType.FORBIDDEN: + case 'forbidden': return 403; - case ErrorType.NOT_FOUND: + case 'not_found': return 404; - case ErrorType.TIMEOUT: + case 'timeout': return 408; - case ErrorType.CONFLICT: + case 'conflict': return 409; - case ErrorType.RATE_LIMIT: + case 'rate_limit': return 429; - case ErrorType.SYSTEM: + case 'system': return 500; - case ErrorType.THIRD_PARTY: + case 'third_party': return 502; - case ErrorType.UNKNOWN: + case 'unknown': default: return 500; } @@ -40,7 +41,7 @@ export const mapErrorTypeToStatus = (type: ErrorType): number => { export const statusForValidation = (issues: ReturnType): number => { const firstError = issues.find((i) => i.severity === 'error'); - const type = firstError?.type ?? ErrorType.USER; + const type = firstError?.type ?? 'user'; return mapErrorTypeToStatus(type); }; diff --git a/packages/server/src/hono/routes/agents.ts b/packages/server/src/hono/routes/agents.ts index 8738dd212..4287269f6 100644 --- a/packages/server/src/hono/routes/agents.ts +++ b/packages/server/src/hono/routes/agents.ts @@ -14,7 +14,7 @@ import { stringify as yamlStringify, parse as yamlParse } from 'yaml'; import os from 'os'; import path from 'path'; import { promises as fs } from 'fs'; -import { DextoValidationError, AgentErrorCode, ErrorScope, ErrorType } from '@dexto/core'; +import { DextoValidationError, AgentErrorCode } from '@dexto/core'; import { AgentRegistryEntrySchema } from '../schemas/responses.js'; import type { Context } from 'hono'; import type { GetAgentConfigPathFn } from '../index.js'; @@ -839,8 +839,8 @@ export function createAgentsRouter( { code: AgentErrorCode.INVALID_CONFIG, message: `Invalid YAML syntax: ${message}`, - scope: ErrorScope.AGENT, - type: ErrorType.USER, + scope: 'agent', + type: 'user', severity: 'error', }, ]); @@ -852,8 +852,8 @@ export function createAgentsRouter( { code: AgentErrorCode.INVALID_CONFIG, message: 'Configuration must be a valid YAML object', - scope: ErrorScope.AGENT, - type: ErrorType.USER, + scope: 'agent', + type: 'user', severity: 'error', }, ]); @@ -873,8 +873,8 @@ export function createAgentsRouter( validationResult.error.errors.map((err) => ({ code: AgentErrorCode.INVALID_CONFIG, message: `${err.path.join('.')}: ${err.message}`, - scope: ErrorScope.AGENT, - type: ErrorType.USER, + scope: 'agent', + type: 'user', severity: 'error', })) ); diff --git a/packages/server/src/hono/routes/llm.ts b/packages/server/src/hono/routes/llm.ts index adef811a6..733bec09a 100644 --- a/packages/server/src/hono/routes/llm.ts +++ b/packages/server/src/hono/routes/llm.ts @@ -1,6 +1,6 @@ import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi'; import type { DextoAgent } from '@dexto/core'; -import { DextoRuntimeError, ErrorScope, ErrorType, logger } from '@dexto/core'; +import { DextoRuntimeError, logger } from '@dexto/core'; import { LLM_REGISTRY, LLM_PROVIDERS, @@ -999,8 +999,8 @@ export function createLlmRouter(getAgent: GetAgentFn) { if (!deleted) { throw new DextoRuntimeError( 'custom_model_not_found', - ErrorScope.LLM, - ErrorType.NOT_FOUND, + 'llm', + 'not_found', `Custom model '${name}' not found`, { modelName: name } ); diff --git a/packages/server/src/hono/routes/schedules.ts b/packages/server/src/hono/routes/schedules.ts index 384b82d87..e1c2cf161 100644 --- a/packages/server/src/hono/routes/schedules.ts +++ b/packages/server/src/hono/routes/schedules.ts @@ -11,7 +11,7 @@ import { ensureSchedulerManagerForAgent, getSchedulerManager, } from '@dexto/tools-scheduler/service'; -import { DextoRuntimeError, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; const CreateScheduleSchema = z .object({ @@ -35,7 +35,7 @@ const UpdateScheduleSchema = CreateScheduleSchema.partial() const isScheduleNotFoundError = (error: unknown): boolean => error instanceof DextoRuntimeError && - error.type === ErrorType.NOT_FOUND && + error.type === 'not_found' && error.code === SchedulerErrorCode.SCHEDULE_NOT_FOUND; const logSchedulerError = ( diff --git a/packages/server/src/hono/routes/sessions.ts b/packages/server/src/hono/routes/sessions.ts index 7c6c34b1e..1c25702ab 100644 --- a/packages/server/src/hono/routes/sessions.ts +++ b/packages/server/src/hono/routes/sessions.ts @@ -1,8 +1,6 @@ import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi'; import { DextoRuntimeError, - ErrorScope, - ErrorType, zodToIssues, type SessionMetadata as CoreSessionMetadata, } from '@dexto/core'; @@ -111,7 +109,7 @@ export function createSessionsRouter(getAgent: GetAgentFn) { new DextoRuntimeError( 'validation_failed', 'validation', - ErrorType.USER, + 'user', issues[0]?.message ?? 'Validation failed', { issues } ) @@ -712,8 +710,8 @@ export function createSessionsRouter(getAgent: GetAgentFn) { if (contributorId.length === 0) { throw new DextoRuntimeError( 'session_systemprompt_contributor_config_invalid', - ErrorScope.SYSTEM_PROMPT, - ErrorType.USER, + 'system_prompt', + 'user', 'A valid contributor id is required', { id: payload.id, @@ -743,8 +741,8 @@ export function createSessionsRouter(getAgent: GetAgentFn) { if (content.trim().length === 0) { throw new DextoRuntimeError( 'session_systemprompt_contributor_config_invalid', - ErrorScope.SYSTEM_PROMPT, - ErrorType.USER, + 'system_prompt', + 'user', 'Contributor content is required when enabled', { id: payload.id, diff --git a/packages/server/src/hono/routes/system-prompt.ts b/packages/server/src/hono/routes/system-prompt.ts index 464c0c86e..2bc4058ca 100644 --- a/packages/server/src/hono/routes/system-prompt.ts +++ b/packages/server/src/hono/routes/system-prompt.ts @@ -1,6 +1,6 @@ import { OpenAPIHono, createRoute, z } from '@hono/zod-openapi'; import type { DextoAgent } from '@dexto/core'; -import { DextoRuntimeError, ErrorScope, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; import { StandardErrorEnvelopeSchema } from '../schemas/responses.js'; import type { Context } from 'hono'; @@ -158,8 +158,8 @@ export function createSystemPromptRouter(getAgent: GetAgentFn) { if (contributorId.length === 0) { throw new DextoRuntimeError( 'systemprompt_contributor_config_invalid', - ErrorScope.SYSTEM_PROMPT, - ErrorType.USER, + 'system_prompt', + 'user', 'A valid contributor id is required', { id: payload.id, @@ -188,8 +188,8 @@ export function createSystemPromptRouter(getAgent: GetAgentFn) { if (!hasContent || content.trim().length === 0) { throw new DextoRuntimeError( 'systemprompt_contributor_config_invalid', - ErrorScope.SYSTEM_PROMPT, - ErrorType.USER, + 'system_prompt', + 'user', 'Contributor content is required when enabled', { id: payload.id, diff --git a/packages/storage/src/cache/schemas.ts b/packages/storage/src/cache/schemas.ts index 285c14d9f..7bd701269 100644 --- a/packages/storage/src/cache/schemas.ts +++ b/packages/storage/src/cache/schemas.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { EnvExpandedString, ErrorScope, ErrorType, StorageErrorCode } from '@dexto/core'; +import { EnvExpandedString } from '@dexto/core'; export const CACHE_TYPES = ['in-memory', 'redis'] as const; export type CacheType = (typeof CACHE_TYPES)[number]; @@ -46,9 +46,9 @@ export const RedisCacheSchema = BaseCacheSchema.extend({ message: "Redis cache requires either 'url' or 'host' to be specified", path: ['url'], params: { - code: StorageErrorCode.CONNECTION_CONFIG_MISSING, - scope: ErrorScope.STORAGE, - type: ErrorType.USER, + code: 'storage_connection_config_missing', + scope: 'storage', + type: 'user', }, }); } diff --git a/packages/storage/src/database/schemas.ts b/packages/storage/src/database/schemas.ts index 1dd788db9..e79fa641e 100644 --- a/packages/storage/src/database/schemas.ts +++ b/packages/storage/src/database/schemas.ts @@ -1,5 +1,5 @@ import { z } from 'zod'; -import { EnvExpandedString, ErrorScope, ErrorType, StorageErrorCode } from '@dexto/core'; +import { EnvExpandedString } from '@dexto/core'; export const DATABASE_TYPES = ['in-memory', 'sqlite', 'postgres'] as const; export type DatabaseType = (typeof DATABASE_TYPES)[number]; @@ -66,9 +66,9 @@ export const PostgresDatabaseSchema = BaseDatabaseSchema.extend({ "PostgreSQL database requires one of 'url', 'connectionString', or 'host' to be specified", path: ['url'], params: { - code: StorageErrorCode.CONNECTION_CONFIG_MISSING, - scope: ErrorScope.STORAGE, - type: ErrorType.USER, + code: 'storage_connection_config_missing', + scope: 'storage', + type: 'user', }, }); } diff --git a/packages/tools-builtins/src/implementations/delegate-to-url-tool.test.ts b/packages/tools-builtins/src/implementations/delegate-to-url-tool.test.ts index 6be501164..078e82453 100644 --- a/packages/tools-builtins/src/implementations/delegate-to-url-tool.test.ts +++ b/packages/tools-builtins/src/implementations/delegate-to-url-tool.test.ts @@ -1,6 +1,6 @@ import { afterEach, describe, expect, it, vi } from 'vitest'; import { createDelegateToUrlTool } from './delegate-to-url-tool.js'; -import { DextoRuntimeError, ErrorScope, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; import type { Logger, ToolExecutionContext } from '@dexto/core'; function createMockLogger(): Logger { @@ -49,8 +49,8 @@ describe('delegate_to_url tool', () => { await expect(tool.execute(input, context)).rejects.toMatchObject({ name: 'DextoRuntimeError', code: 'DELEGATION_FAILED', - scope: ErrorScope.TOOLS, - type: ErrorType.THIRD_PARTY, + scope: 'tools', + type: 'third_party', }); }); @@ -80,8 +80,8 @@ describe('delegate_to_url tool', () => { expect(error).toBeInstanceOf(DextoRuntimeError); expect(error).toMatchObject({ code: 'DELEGATION_TIMEOUT', - scope: ErrorScope.TOOLS, - type: ErrorType.TIMEOUT, + scope: 'tools', + type: 'timeout', }); } }); diff --git a/packages/tools-builtins/src/implementations/delegate-to-url-tool.ts b/packages/tools-builtins/src/implementations/delegate-to-url-tool.ts index 191d7d26f..6296a2952 100644 --- a/packages/tools-builtins/src/implementations/delegate-to-url-tool.ts +++ b/packages/tools-builtins/src/implementations/delegate-to-url-tool.ts @@ -2,8 +2,6 @@ import { z } from 'zod'; import type { Tool, ToolExecutionContext } from '@dexto/core'; import { DextoRuntimeError, - ErrorScope, - ErrorType, createLocalToolCallHeader, defineTool, truncateForHeader, @@ -137,8 +135,8 @@ class SimpleA2AClient { if (error instanceof Error && error.name === 'AbortError') { throw new DextoRuntimeError( 'DELEGATION_TIMEOUT', - ErrorScope.TOOLS, - ErrorType.TIMEOUT, + 'tools', + 'timeout', `Delegation timeout after ${this.timeout}ms` ); } @@ -150,8 +148,8 @@ class SimpleA2AClient { throw new DextoRuntimeError( 'DELEGATION_FAILED', - ErrorScope.TOOLS, - ErrorType.THIRD_PARTY, + 'tools', + 'third_party', `Failed to connect to agent at ${this.url}. Tried endpoints: ${endpoints.join(', ')}. Last error: ${lastError?.message || 'Unknown error'}` ); } @@ -230,8 +228,8 @@ export function createDelegateToUrlTool(): Tool throw new DextoRuntimeError( 'DELEGATION_ERROR', - ErrorScope.TOOLS, - ErrorType.SYSTEM, + 'tools', + 'system', `Delegation failed: ${error instanceof Error ? error.message : String(error)}` ); } diff --git a/packages/tools-builtins/src/implementations/http-request-tool.ts b/packages/tools-builtins/src/implementations/http-request-tool.ts index d2b2852ce..9c87d45f7 100644 --- a/packages/tools-builtins/src/implementations/http-request-tool.ts +++ b/packages/tools-builtins/src/implementations/http-request-tool.ts @@ -2,8 +2,6 @@ import { z } from 'zod'; import type { Tool, ToolExecutionContext } from '@dexto/core'; import { DextoRuntimeError, - ErrorScope, - ErrorType, createLocalToolCallHeader, defineTool, truncateForHeader, @@ -164,8 +162,8 @@ export function createSafeLookup(config?: { toErrnoException( new DextoRuntimeError( 'HTTP_REQUEST_UNSAFE_TARGET', - ErrorScope.TOOLS, - ErrorType.FORBIDDEN, + 'tools', + 'forbidden', `Blocked request to local hostname: ${hostname}` ) ) @@ -186,8 +184,8 @@ export function createSafeLookup(config?: { toErrnoException( new DextoRuntimeError( 'HTTP_REQUEST_DNS_FAILED', - ErrorScope.TOOLS, - ErrorType.THIRD_PARTY, + 'tools', + 'third_party', `Failed to resolve hostname: ${hostname}` ) ) @@ -201,8 +199,8 @@ export function createSafeLookup(config?: { toErrnoException( new DextoRuntimeError( 'HTTP_REQUEST_UNSAFE_TARGET', - ErrorScope.TOOLS, - ErrorType.FORBIDDEN, + 'tools', + 'forbidden', `Blocked request to private address: ${record.address}` ) ) @@ -230,8 +228,8 @@ export function createSafeLookup(config?: { toErrnoException( new DextoRuntimeError( 'HTTP_REQUEST_DNS_FAILED', - ErrorScope.TOOLS, - ErrorType.THIRD_PARTY, + 'tools', + 'third_party', `Failed to resolve hostname: ${hostname}` ) ) @@ -254,8 +252,8 @@ export function createSafeLookup(config?: { ? error : new DextoRuntimeError( 'HTTP_REQUEST_DNS_FAILED', - ErrorScope.TOOLS, - ErrorType.THIRD_PARTY, + 'tools', + 'third_party', `Failed to resolve hostname: ${hostname}` ); @@ -275,8 +273,8 @@ async function assertSafeUrl(requestUrl: URL): Promise { if (!['http:', 'https:'].includes(requestUrl.protocol)) { throw new DextoRuntimeError( 'HTTP_REQUEST_UNSUPPORTED_PROTOCOL', - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', `Unsupported URL protocol: ${requestUrl.protocol}` ); } @@ -285,8 +283,8 @@ async function assertSafeUrl(requestUrl: URL): Promise { if (!hostname) { throw new DextoRuntimeError( 'HTTP_REQUEST_INVALID_TARGET', - ErrorScope.TOOLS, - ErrorType.USER, + 'tools', + 'user', 'Request URL hostname is required' ); } @@ -294,8 +292,8 @@ async function assertSafeUrl(requestUrl: URL): Promise { if (BLOCKED_HOSTNAMES.has(hostname) || hostname.endsWith('.localhost')) { throw new DextoRuntimeError( 'HTTP_REQUEST_UNSAFE_TARGET', - ErrorScope.TOOLS, - ErrorType.FORBIDDEN, + 'tools', + 'forbidden', `Blocked request to local hostname: ${hostname}` ); } @@ -303,8 +301,8 @@ async function assertSafeUrl(requestUrl: URL): Promise { if (isPrivateAddress(hostname)) { throw new DextoRuntimeError( 'HTTP_REQUEST_UNSAFE_TARGET', - ErrorScope.TOOLS, - ErrorType.FORBIDDEN, + 'tools', + 'forbidden', `Blocked request to private address: ${hostname}` ); } @@ -317,8 +315,8 @@ async function readResponseTextWithLimit(response: Response): Promise { if (!Number.isNaN(parsed) && parsed > MAX_RESPONSE_BYTES) { throw new DextoRuntimeError( 'HTTP_REQUEST_RESPONSE_TOO_LARGE', - ErrorScope.TOOLS, - ErrorType.THIRD_PARTY, + 'tools', + 'third_party', `Response too large: ${parsed} bytes exceeds ${MAX_RESPONSE_BYTES} byte limit` ); } @@ -342,8 +340,8 @@ async function readResponseTextWithLimit(response: Response): Promise { await reader.cancel(); throw new DextoRuntimeError( 'HTTP_REQUEST_RESPONSE_TOO_LARGE', - ErrorScope.TOOLS, - ErrorType.THIRD_PARTY, + 'tools', + 'third_party', `Response too large: exceeded ${MAX_RESPONSE_BYTES} byte limit` ); } @@ -443,16 +441,16 @@ export function createHttpRequestTool(): Tool { if (error instanceof Error && error.name === 'AbortError') { throw new DextoRuntimeError( 'HTTP_REQUEST_TIMEOUT', - ErrorScope.TOOLS, - ErrorType.TIMEOUT, + 'tools', + 'timeout', `HTTP request timed out after ${timeoutMs}ms` ); } throw new DextoRuntimeError( 'HTTP_REQUEST_FAILED', - ErrorScope.TOOLS, - ErrorType.THIRD_PARTY, + 'tools', + 'third_party', `HTTP request failed: ${error instanceof Error ? error.message : String(error)}` ); } finally { diff --git a/packages/tools-filesystem/src/errors.ts b/packages/tools-filesystem/src/errors.ts index e014f4169..dd8265271 100644 --- a/packages/tools-filesystem/src/errors.ts +++ b/packages/tools-filesystem/src/errors.ts @@ -4,7 +4,7 @@ * Error classes for file system operations */ -import { DextoRuntimeError, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; /** Error scope for filesystem operations */ const FILESYSTEM_SCOPE = 'filesystem'; @@ -34,7 +34,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.FILE_NOT_FOUND, FILESYSTEM_SCOPE, - ErrorType.NOT_FOUND, + 'not_found', `File not found: ${path}`, { path } ); @@ -47,7 +47,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.DIRECTORY_NOT_FOUND, FILESYSTEM_SCOPE, - ErrorType.NOT_FOUND, + 'not_found', `Directory not found: ${path}`, { path } ); @@ -60,7 +60,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.PERMISSION_DENIED, FILESYSTEM_SCOPE, - ErrorType.FORBIDDEN, + 'forbidden', `Permission denied: cannot ${operation} ${path}`, { path, operation } ); @@ -73,7 +73,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.PATH_NOT_ALLOWED, FILESYSTEM_SCOPE, - ErrorType.USER, + 'user', `Path not allowed: ${path}. Must be within allowed paths: ${allowedPaths.join(', ')}`, { path, allowedPaths }, 'Ensure the path is within the configured allowed paths' @@ -87,7 +87,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.PATH_BLOCKED, FILESYSTEM_SCOPE, - ErrorType.FORBIDDEN, + 'forbidden', `Path is blocked: ${path}. Reason: ${reason}`, { path, reason } ); @@ -100,7 +100,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.INVALID_PATH, FILESYSTEM_SCOPE, - ErrorType.USER, + 'user', `Invalid path: ${path}. ${reason}`, { path, reason } ); @@ -113,7 +113,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.PATH_TRAVERSAL_DETECTED, FILESYSTEM_SCOPE, - ErrorType.FORBIDDEN, + 'forbidden', `Path traversal detected in: ${path}`, { path } ); @@ -126,7 +126,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.INVALID_FILE_EXTENSION, FILESYSTEM_SCOPE, - ErrorType.USER, + 'user', `Invalid file extension: ${path}. Blocked extensions: ${blockedExtensions.join(', ')}`, { path, blockedExtensions } ); @@ -139,7 +139,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.FILE_TOO_LARGE, FILESYSTEM_SCOPE, - ErrorType.USER, + 'user', `File too large: ${path} (${size} bytes). Maximum allowed: ${maxSize} bytes`, { path, size, maxSize } ); @@ -152,7 +152,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.TOO_MANY_RESULTS, FILESYSTEM_SCOPE, - ErrorType.USER, + 'user', `Too many results from ${operation}: ${count}. Maximum allowed: ${maxResults}`, { operation, count, maxResults }, 'Narrow your search pattern or increase maxResults limit' @@ -166,7 +166,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.READ_FAILED, FILESYSTEM_SCOPE, - ErrorType.SYSTEM, + 'system', `Failed to read file: ${path}. ${cause}`, { path, cause } ); @@ -179,7 +179,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.LIST_FAILED, FILESYSTEM_SCOPE, - ErrorType.SYSTEM, + 'system', `Failed to list directory: ${path}. ${cause}`, { path, cause } ); @@ -192,7 +192,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.CREATE_DIR_FAILED, FILESYSTEM_SCOPE, - ErrorType.SYSTEM, + 'system', `Failed to create directory: ${path}. ${cause}`, { path, cause } ); @@ -205,7 +205,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.DELETE_FAILED, FILESYSTEM_SCOPE, - ErrorType.SYSTEM, + 'system', `Failed to delete path: ${path}. ${cause}`, { path, cause } ); @@ -218,7 +218,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.RENAME_FAILED, FILESYSTEM_SCOPE, - ErrorType.SYSTEM, + 'system', `Failed to rename path: ${path}. ${cause}`, { path, cause } ); @@ -231,7 +231,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.WRITE_FAILED, FILESYSTEM_SCOPE, - ErrorType.SYSTEM, + 'system', `Failed to write file: ${path}. ${cause}`, { path, cause } ); @@ -244,7 +244,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.BACKUP_FAILED, FILESYSTEM_SCOPE, - ErrorType.SYSTEM, + 'system', `Failed to create backup for: ${path}. ${cause}`, { path, cause } ); @@ -257,7 +257,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.EDIT_FAILED, FILESYSTEM_SCOPE, - ErrorType.SYSTEM, + 'system', `Failed to edit file: ${path}. ${cause}`, { path, cause } ); @@ -274,7 +274,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.STRING_NOT_UNIQUE, FILESYSTEM_SCOPE, - ErrorType.USER, + 'user', `String is not unique in ${path}: "${searchString}" found ${occurrences} times. Use replaceAll=true or provide a more specific string.`, { path, searchString, occurrences }, 'Use replaceAll option or provide more context in the search string' @@ -288,7 +288,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.STRING_NOT_FOUND, FILESYSTEM_SCOPE, - ErrorType.USER, + 'user', `String not found in ${path}: "${searchString}"`, { path, searchString } ); @@ -301,7 +301,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.GLOB_FAILED, FILESYSTEM_SCOPE, - ErrorType.SYSTEM, + 'system', `Glob operation failed for pattern: ${pattern}. ${cause}`, { pattern, cause } ); @@ -314,7 +314,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.SEARCH_FAILED, FILESYSTEM_SCOPE, - ErrorType.SYSTEM, + 'system', `Search operation failed for pattern: ${pattern}. ${cause}`, { pattern, cause } ); @@ -327,7 +327,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.INVALID_PATTERN, FILESYSTEM_SCOPE, - ErrorType.USER, + 'user', `Invalid pattern: ${pattern}. ${cause}`, { pattern, cause } ); @@ -340,7 +340,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.REGEX_TIMEOUT, FILESYSTEM_SCOPE, - ErrorType.TIMEOUT, + 'timeout', `Regex operation timed out for pattern: ${pattern}`, { pattern }, 'Simplify your regex pattern or increase timeout' @@ -354,7 +354,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.INVALID_CONFIG, FILESYSTEM_SCOPE, - ErrorType.USER, + 'user', `Invalid FileSystem configuration: ${reason}`, { reason } ); @@ -367,7 +367,7 @@ export class FileSystemError { return new DextoRuntimeError( FileSystemErrorCode.SERVICE_NOT_INITIALIZED, FILESYSTEM_SCOPE, - ErrorType.SYSTEM, + 'system', 'FileSystemService has not been initialized', {}, 'Initialize the FileSystemService before using it' diff --git a/packages/tools-plan/src/errors.ts b/packages/tools-plan/src/errors.ts index 2223a62f0..ea0e3bc55 100644 --- a/packages/tools-plan/src/errors.ts +++ b/packages/tools-plan/src/errors.ts @@ -4,7 +4,7 @@ * Provides typed errors for plan operations following the DextoRuntimeError pattern. */ -import { DextoRuntimeError, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; /** * Error codes for plan operations @@ -39,7 +39,7 @@ export const PlanError = { return new DextoRuntimeError( PlanErrorCode.PLAN_ALREADY_EXISTS, 'plan', - ErrorType.USER, + 'user', `A plan already exists for session '${sessionId}'. Use plan_update to modify it.`, { sessionId }, 'Use plan_update to modify the existing plan, or plan_read to view it.' @@ -53,7 +53,7 @@ export const PlanError = { return new DextoRuntimeError( PlanErrorCode.PLAN_NOT_FOUND, 'plan', - ErrorType.NOT_FOUND, + 'not_found', `No plan found for session '${sessionId}'.`, { sessionId }, 'Use plan_create to create a new plan for this session.' @@ -67,7 +67,7 @@ export const PlanError = { return new DextoRuntimeError( PlanErrorCode.SESSION_ID_REQUIRED, 'plan', - ErrorType.USER, + 'user', 'Session ID is required for plan operations.', {}, 'Ensure the tool is called within a valid session context.' @@ -81,7 +81,7 @@ export const PlanError = { return new DextoRuntimeError( PlanErrorCode.INVALID_SESSION_ID, 'plan', - ErrorType.USER, + 'user', `Invalid session ID: '${sessionId}' contains invalid path characters.`, { sessionId }, 'Session IDs must not contain path traversal characters like "..".' @@ -95,7 +95,7 @@ export const PlanError = { return new DextoRuntimeError( PlanErrorCode.CHECKPOINT_NOT_FOUND, 'plan', - ErrorType.NOT_FOUND, + 'not_found', `Checkpoint '${checkpointId}' not found in plan for session '${sessionId}'.`, { checkpointId, sessionId }, 'Use plan_read to view available checkpoints.' @@ -109,7 +109,7 @@ export const PlanError = { return new DextoRuntimeError( PlanErrorCode.STORAGE_ERROR, 'plan', - ErrorType.SYSTEM, + 'system', `Failed to ${operation} plan for session '${sessionId}': ${cause?.message || 'unknown error'}`, { operation, sessionId, cause: cause?.message }, 'Check file system permissions and try again.' diff --git a/packages/tools-process/src/errors.ts b/packages/tools-process/src/errors.ts index cde2ecf96..bb375801a 100644 --- a/packages/tools-process/src/errors.ts +++ b/packages/tools-process/src/errors.ts @@ -4,7 +4,7 @@ * Error classes for process execution and management */ -import { DextoRuntimeError, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; /** Error scope for process operations */ const PROCESS_SCOPE = 'process'; @@ -32,7 +32,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.INVALID_COMMAND, PROCESS_SCOPE, - ErrorType.USER, + 'user', `Invalid command: ${command}. ${reason}`, { command, reason } ); @@ -45,7 +45,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.COMMAND_BLOCKED, PROCESS_SCOPE, - ErrorType.FORBIDDEN, + 'forbidden', `Command is blocked: ${command}. ${reason}`, { command, reason } ); @@ -58,7 +58,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.COMMAND_TOO_LONG, PROCESS_SCOPE, - ErrorType.USER, + 'user', `Command too long: ${length} characters. Maximum allowed: ${maxLength}`, { length, maxLength } ); @@ -71,7 +71,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.INJECTION_DETECTED, PROCESS_SCOPE, - ErrorType.FORBIDDEN, + 'forbidden', `Potential command injection detected in: ${command}. Pattern: ${pattern}`, { command, pattern } ); @@ -84,7 +84,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.APPROVAL_REQUIRED, PROCESS_SCOPE, - ErrorType.FORBIDDEN, + 'forbidden', `Command requires approval: ${command}${reason ? `. ${reason}` : ''}`, { command, reason }, 'Provide an approval function to execute dangerous commands' @@ -98,7 +98,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.APPROVAL_DENIED, PROCESS_SCOPE, - ErrorType.FORBIDDEN, + 'forbidden', `Command approval denied by user: ${command}`, { command } ); @@ -111,7 +111,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.EXECUTION_FAILED, PROCESS_SCOPE, - ErrorType.SYSTEM, + 'system', `Command execution failed: ${command}. ${cause}`, { command, cause } ); @@ -124,7 +124,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.TIMEOUT, PROCESS_SCOPE, - ErrorType.TIMEOUT, + 'timeout', `Command timed out after ${timeout}ms: ${command}`, { command, timeout }, 'Increase timeout or optimize the command' @@ -138,7 +138,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.PERMISSION_DENIED, PROCESS_SCOPE, - ErrorType.FORBIDDEN, + 'forbidden', `Permission denied: ${command}`, { command } ); @@ -151,7 +151,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.COMMAND_NOT_FOUND, PROCESS_SCOPE, - ErrorType.NOT_FOUND, + 'not_found', `Command not found: ${command}`, { command }, 'Ensure the command is installed and available in PATH' @@ -165,7 +165,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.WORKING_DIRECTORY_INVALID, PROCESS_SCOPE, - ErrorType.USER, + 'user', `Invalid working directory: ${path}. ${reason}`, { path, reason } ); @@ -178,7 +178,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.PROCESS_NOT_FOUND, PROCESS_SCOPE, - ErrorType.NOT_FOUND, + 'not_found', `Process not found: ${processId}`, { processId } ); @@ -191,7 +191,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.TOO_MANY_PROCESSES, PROCESS_SCOPE, - ErrorType.USER, + 'user', `Too many concurrent processes: ${current}. Maximum allowed: ${max}`, { current, max }, 'Wait for running processes to complete or increase the limit' @@ -205,7 +205,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.KILL_FAILED, PROCESS_SCOPE, - ErrorType.SYSTEM, + 'system', `Failed to kill process ${processId}: ${cause}`, { processId, cause } ); @@ -218,7 +218,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.OUTPUT_BUFFER_FULL, PROCESS_SCOPE, - ErrorType.SYSTEM, + 'system', `Output buffer full for process ${processId}: ${size} bytes. Maximum: ${maxSize}`, { processId, size, maxSize }, 'Process output exceeded buffer limit' @@ -232,7 +232,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.INVALID_CONFIG, PROCESS_SCOPE, - ErrorType.USER, + 'user', `Invalid Process configuration: ${reason}`, { reason } ); @@ -245,7 +245,7 @@ export class ProcessError { return new DextoRuntimeError( ProcessErrorCode.SERVICE_NOT_INITIALIZED, PROCESS_SCOPE, - ErrorType.SYSTEM, + 'system', 'ProcessService has not been initialized', {}, 'Initialize the ProcessService before using it' diff --git a/packages/tools-scheduler/src/errors.ts b/packages/tools-scheduler/src/errors.ts index fb1232649..b4249af1e 100644 --- a/packages/tools-scheduler/src/errors.ts +++ b/packages/tools-scheduler/src/errors.ts @@ -2,7 +2,7 @@ * Error classes for scheduler operations */ -import { DextoRuntimeError, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; import { SchedulerErrorCode } from './error-codes.js'; /** @@ -19,7 +19,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.SCHEDULER_NOT_ENABLED, SCHEDULER_ERROR_SCOPE, - ErrorType.USER, + 'user', 'Scheduler is not enabled in configuration', undefined, 'Add scheduler-tools to customTools in your agent configuration' @@ -30,7 +30,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.SCHEDULER_MISSING_STORAGE, SCHEDULER_ERROR_SCOPE, - ErrorType.SYSTEM, + 'system', 'StorageManager is required but not available', undefined, 'Ensure StorageManager is available in context.services' @@ -41,7 +41,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.SCHEDULER_INVALID_CONFIG, SCHEDULER_ERROR_SCOPE, - ErrorType.USER, + 'user', `Invalid scheduler configuration: ${details}`, { details } ); @@ -51,7 +51,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.SCHEDULE_INVALID_CRON, SCHEDULER_ERROR_SCOPE, - ErrorType.USER, + 'user', `Invalid cron expression: ${expression}${details ? ` - ${details}` : ''}`, { expression, details } ); @@ -61,7 +61,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.SCHEDULE_INVALID_INPUT, SCHEDULER_ERROR_SCOPE, - ErrorType.USER, + 'user', `Invalid schedule input: ${details}`, { details } ); @@ -71,7 +71,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.SCHEDULE_LIMIT_REACHED, SCHEDULER_ERROR_SCOPE, - ErrorType.USER, + 'user', `Schedule limit reached: ${current}/${max}`, { current, max }, 'Delete unused schedules or increase maxSchedules in configuration' @@ -82,7 +82,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.SCHEDULE_NOT_FOUND, SCHEDULER_ERROR_SCOPE, - ErrorType.NOT_FOUND, + 'not_found', `Schedule not found: ${scheduleId}`, { scheduleId } ); @@ -92,7 +92,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.SCHEDULE_CREATE_FAILED, SCHEDULER_ERROR_SCOPE, - ErrorType.SYSTEM, + 'system', `Failed to create schedule: ${details}`, { details } ); @@ -102,7 +102,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.SCHEDULE_UPDATE_FAILED, SCHEDULER_ERROR_SCOPE, - ErrorType.SYSTEM, + 'system', `Failed to update schedule ${scheduleId}: ${details}`, { scheduleId, details } ); @@ -112,7 +112,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.SCHEDULE_DELETE_FAILED, SCHEDULER_ERROR_SCOPE, - ErrorType.SYSTEM, + 'system', `Failed to delete schedule ${scheduleId}: ${details}`, { scheduleId, details } ); @@ -122,7 +122,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.SCHEDULE_EXECUTION_FAILED, SCHEDULER_ERROR_SCOPE, - ErrorType.SYSTEM, + 'system', `Schedule execution failed for ${scheduleId}: ${details}`, { scheduleId, details } ); @@ -132,7 +132,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.SCHEDULE_EXECUTION_TIMEOUT, SCHEDULER_ERROR_SCOPE, - ErrorType.TIMEOUT, + 'timeout', `Schedule execution timed out for ${scheduleId} after ${timeout}ms`, { scheduleId, timeout } ); @@ -142,7 +142,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.STORAGE_READ_FAILED, SCHEDULER_ERROR_SCOPE, - ErrorType.SYSTEM, + 'system', `Storage read failed for ${operation}: ${details}`, { operation, details } ); @@ -152,7 +152,7 @@ export class SchedulerError { return new DextoRuntimeError( SchedulerErrorCode.STORAGE_WRITE_FAILED, SCHEDULER_ERROR_SCOPE, - ErrorType.SYSTEM, + 'system', `Storage write failed for ${operation}: ${details}`, { operation, details } ); diff --git a/packages/tools-todo/src/errors.ts b/packages/tools-todo/src/errors.ts index cbc184b7e..8cdb66510 100644 --- a/packages/tools-todo/src/errors.ts +++ b/packages/tools-todo/src/errors.ts @@ -4,7 +4,7 @@ * Error factory for todo list management operations */ -import { DextoRuntimeError, ErrorScope, ErrorType } from '@dexto/core'; +import { DextoRuntimeError } from '@dexto/core'; import { TodoErrorCode } from './error-codes.js'; /** @@ -28,7 +28,7 @@ export class TodoError { return new DextoRuntimeError( TodoErrorCode.SERVICE_NOT_INITIALIZED, TODO_ERROR_SCOPE, - ErrorType.SYSTEM, + 'system', 'TodoService has not been initialized', {}, 'Initialize the TodoService before using it' @@ -42,7 +42,7 @@ export class TodoError { return new DextoRuntimeError( TodoErrorCode.TODO_LIMIT_EXCEEDED, TODO_ERROR_SCOPE, - ErrorType.USER, + 'user', `Todo limit exceeded: ${current} todos. Maximum allowed: ${max}`, { current, max }, 'Complete or delete existing todos before adding new ones' @@ -56,7 +56,7 @@ export class TodoError { return new DextoRuntimeError( TodoErrorCode.INVALID_TODO_STATUS, TODO_ERROR_SCOPE, - ErrorType.USER, + 'user', `Invalid todo status: ${status}. Must be 'pending', 'in_progress', or 'completed'`, { status } ); @@ -69,7 +69,7 @@ export class TodoError { return new DextoRuntimeError( TodoErrorCode.DATABASE_ERROR, TODO_ERROR_SCOPE, - ErrorType.SYSTEM, + 'system', `Database error during ${operation}: ${cause}`, { operation, cause } );