@herdctl/chat 0.0.1 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/__tests__/dm-filter.test.d.ts +5 -0
- package/dist/__tests__/dm-filter.test.d.ts.map +1 -0
- package/dist/__tests__/dm-filter.test.js +136 -0
- package/dist/__tests__/dm-filter.test.js.map +1 -0
- package/dist/__tests__/error-handler.test.d.ts +5 -0
- package/dist/__tests__/error-handler.test.d.ts.map +1 -0
- package/dist/__tests__/error-handler.test.js +235 -0
- package/dist/__tests__/error-handler.test.js.map +1 -0
- package/dist/__tests__/errors.test.d.ts +5 -0
- package/dist/__tests__/errors.test.d.ts.map +1 -0
- package/dist/__tests__/errors.test.js +140 -0
- package/dist/__tests__/errors.test.js.map +1 -0
- package/dist/__tests__/index.test.d.ts +2 -0
- package/dist/__tests__/index.test.d.ts.map +1 -0
- package/dist/__tests__/index.test.js +25 -0
- package/dist/__tests__/index.test.js.map +1 -0
- package/dist/__tests__/message-extraction.test.d.ts +5 -0
- package/dist/__tests__/message-extraction.test.d.ts.map +1 -0
- package/dist/__tests__/message-extraction.test.js +157 -0
- package/dist/__tests__/message-extraction.test.js.map +1 -0
- package/dist/__tests__/message-splitting.test.d.ts +5 -0
- package/dist/__tests__/message-splitting.test.d.ts.map +1 -0
- package/dist/__tests__/message-splitting.test.js +153 -0
- package/dist/__tests__/message-splitting.test.js.map +1 -0
- package/dist/__tests__/session-manager.test.d.ts +2 -0
- package/dist/__tests__/session-manager.test.d.ts.map +1 -0
- package/dist/__tests__/session-manager.test.js +779 -0
- package/dist/__tests__/session-manager.test.js.map +1 -0
- package/dist/__tests__/status-formatting.test.d.ts +5 -0
- package/dist/__tests__/status-formatting.test.d.ts.map +1 -0
- package/dist/__tests__/status-formatting.test.js +160 -0
- package/dist/__tests__/status-formatting.test.js.map +1 -0
- package/dist/__tests__/streaming-responder.test.d.ts +5 -0
- package/dist/__tests__/streaming-responder.test.d.ts.map +1 -0
- package/dist/__tests__/streaming-responder.test.js +154 -0
- package/dist/__tests__/streaming-responder.test.js.map +1 -0
- package/dist/dm-filter.d.ts +121 -0
- package/dist/dm-filter.d.ts.map +1 -0
- package/dist/dm-filter.js +162 -0
- package/dist/dm-filter.js.map +1 -0
- package/dist/error-handler.d.ts +217 -0
- package/dist/error-handler.d.ts.map +1 -0
- package/dist/error-handler.js +313 -0
- package/dist/error-handler.js.map +1 -0
- package/dist/errors.d.ts +118 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +157 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/message-extraction.d.ts +81 -0
- package/dist/message-extraction.d.ts.map +1 -0
- package/dist/message-extraction.js +90 -0
- package/dist/message-extraction.js.map +1 -0
- package/dist/message-splitting.d.ts +133 -0
- package/dist/message-splitting.d.ts.map +1 -0
- package/dist/message-splitting.js +188 -0
- package/dist/message-splitting.js.map +1 -0
- package/dist/session-manager/errors.d.ts +59 -0
- package/dist/session-manager/errors.d.ts.map +1 -0
- package/dist/session-manager/errors.js +71 -0
- package/dist/session-manager/errors.js.map +1 -0
- package/dist/session-manager/index.d.ts +10 -0
- package/dist/session-manager/index.d.ts.map +1 -0
- package/dist/session-manager/index.js +14 -0
- package/dist/session-manager/index.js.map +1 -0
- package/dist/session-manager/session-manager.d.ts +123 -0
- package/dist/session-manager/session-manager.d.ts.map +1 -0
- package/dist/session-manager/session-manager.js +394 -0
- package/dist/session-manager/session-manager.js.map +1 -0
- package/dist/session-manager/types.d.ts +205 -0
- package/dist/session-manager/types.d.ts.map +1 -0
- package/dist/session-manager/types.js +67 -0
- package/dist/session-manager/types.js.map +1 -0
- package/dist/status-formatting.d.ts +147 -0
- package/dist/status-formatting.d.ts.map +1 -0
- package/dist/status-formatting.js +234 -0
- package/dist/status-formatting.js.map +1 -0
- package/dist/streaming-responder.d.ts +130 -0
- package/dist/streaming-responder.d.ts.map +1 -0
- package/dist/streaming-responder.js +178 -0
- package/dist/streaming-responder.js.map +1 -0
- package/dist/types.d.ts +184 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +8 -0
- package/dist/types.js.map +1 -0
- package/package.json +39 -4
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DM (Direct Message) filtering utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides utilities for:
|
|
5
|
+
* - Checking if DMs should be processed
|
|
6
|
+
* - Filtering users via allowlist/blocklist
|
|
7
|
+
* - Determining DM mode configuration
|
|
8
|
+
*
|
|
9
|
+
* These utilities are platform-agnostic and work for any chat platform
|
|
10
|
+
* that supports direct messages with user filtering.
|
|
11
|
+
*/
|
|
12
|
+
// =============================================================================
|
|
13
|
+
// DM Filtering Functions
|
|
14
|
+
// =============================================================================
|
|
15
|
+
/**
|
|
16
|
+
* Check if DMs are enabled based on configuration
|
|
17
|
+
*
|
|
18
|
+
* If no DM config is provided, DMs are enabled by default.
|
|
19
|
+
*
|
|
20
|
+
* @param dmConfig - DM configuration from agent's chat config
|
|
21
|
+
* @returns true if DMs are enabled, false otherwise
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```typescript
|
|
25
|
+
* if (isDMEnabled(agentConfig.chat.discord?.dm)) {
|
|
26
|
+
* // Process DM
|
|
27
|
+
* }
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function isDMEnabled(dmConfig) {
|
|
31
|
+
// If no DM config provided, DMs are enabled by default
|
|
32
|
+
if (!dmConfig) {
|
|
33
|
+
return true;
|
|
34
|
+
}
|
|
35
|
+
// If enabled is not specified, default to true
|
|
36
|
+
return dmConfig.enabled !== false;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Get the mode for DM processing
|
|
40
|
+
*
|
|
41
|
+
* DMs default to "auto" mode (no mention required).
|
|
42
|
+
*
|
|
43
|
+
* @param dmConfig - DM configuration from agent's chat config
|
|
44
|
+
* @returns The mode for DM processing
|
|
45
|
+
*
|
|
46
|
+
* @example
|
|
47
|
+
* ```typescript
|
|
48
|
+
* const mode = getDMMode(agentConfig.chat.discord?.dm);
|
|
49
|
+
* if (mode === 'auto') {
|
|
50
|
+
* // Process all DM messages
|
|
51
|
+
* }
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export function getDMMode(dmConfig) {
|
|
55
|
+
// DMs default to auto mode (no mention required)
|
|
56
|
+
if (!dmConfig) {
|
|
57
|
+
return "auto";
|
|
58
|
+
}
|
|
59
|
+
return dmConfig.mode ?? "auto";
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if a user is allowed to send DMs to the bot
|
|
63
|
+
*
|
|
64
|
+
* Filtering rules:
|
|
65
|
+
* 1. If DMs are disabled, no users are allowed
|
|
66
|
+
* 2. If a blocklist is defined and user is on it, they are blocked
|
|
67
|
+
* 3. If an allowlist is defined, only users on it are allowed
|
|
68
|
+
* 4. If neither list is defined, all users are allowed
|
|
69
|
+
*
|
|
70
|
+
* Note: Blocklist takes precedence over allowlist.
|
|
71
|
+
*
|
|
72
|
+
* @param userId - User ID to check
|
|
73
|
+
* @param dmConfig - DM configuration from agent's chat config
|
|
74
|
+
* @returns Filter result with allowed status and reason
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```typescript
|
|
78
|
+
* const result = checkDMUserFilter("123456789", dmConfig);
|
|
79
|
+
* if (!result.allowed) {
|
|
80
|
+
* console.log(`User blocked: ${result.reason}`);
|
|
81
|
+
* return; // Don't process message
|
|
82
|
+
* }
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
export function checkDMUserFilter(userId, dmConfig) {
|
|
86
|
+
// If DMs are disabled, no users are allowed
|
|
87
|
+
if (!isDMEnabled(dmConfig)) {
|
|
88
|
+
return {
|
|
89
|
+
allowed: false,
|
|
90
|
+
reason: "dm_disabled",
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// If no config, all users are allowed
|
|
94
|
+
if (!dmConfig) {
|
|
95
|
+
return {
|
|
96
|
+
allowed: true,
|
|
97
|
+
reason: "allowed",
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
const { allowlist, blocklist } = dmConfig;
|
|
101
|
+
// Check blocklist first (takes precedence)
|
|
102
|
+
if (blocklist && blocklist.length > 0) {
|
|
103
|
+
if (blocklist.includes(userId)) {
|
|
104
|
+
return {
|
|
105
|
+
allowed: false,
|
|
106
|
+
reason: "in_blocklist",
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
// Check allowlist (if defined, only users on it are allowed)
|
|
111
|
+
if (allowlist && allowlist.length > 0) {
|
|
112
|
+
if (!allowlist.includes(userId)) {
|
|
113
|
+
return {
|
|
114
|
+
allowed: false,
|
|
115
|
+
reason: "not_in_allowlist",
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// User is allowed
|
|
120
|
+
return {
|
|
121
|
+
allowed: true,
|
|
122
|
+
reason: "allowed",
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Check if a message should be processed in the given mode
|
|
127
|
+
*
|
|
128
|
+
* In auto mode:
|
|
129
|
+
* - All non-bot messages are processed
|
|
130
|
+
* - No mention is required
|
|
131
|
+
*
|
|
132
|
+
* In mention mode:
|
|
133
|
+
* - Only messages that mention the bot are processed
|
|
134
|
+
*
|
|
135
|
+
* Bot messages are never processed in either mode.
|
|
136
|
+
*
|
|
137
|
+
* @param isBot - Whether the message author is a bot
|
|
138
|
+
* @param mode - The channel/DM mode
|
|
139
|
+
* @param wasMentioned - Whether the bot was mentioned in the message
|
|
140
|
+
* @returns true if the message should be processed
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* ```typescript
|
|
144
|
+
* const mode = getDMMode(dmConfig);
|
|
145
|
+
* if (shouldProcessInMode(message.author.bot, mode, wasMentioned)) {
|
|
146
|
+
* // Process message
|
|
147
|
+
* }
|
|
148
|
+
* ```
|
|
149
|
+
*/
|
|
150
|
+
export function shouldProcessInMode(isBot, mode, wasMentioned) {
|
|
151
|
+
// Never process bot messages
|
|
152
|
+
if (isBot) {
|
|
153
|
+
return false;
|
|
154
|
+
}
|
|
155
|
+
// In auto mode, process all non-bot messages
|
|
156
|
+
if (mode === "auto") {
|
|
157
|
+
return true;
|
|
158
|
+
}
|
|
159
|
+
// In mention mode, only process if mentioned
|
|
160
|
+
return wasMentioned;
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=dm-filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dm-filter.js","sourceRoot":"","sources":["../src/dm-filter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAiCH,gFAAgF;AAChF,yBAAyB;AACzB,gFAAgF;AAEhF;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,WAAW,CAAC,QAA4B;IACtD,uDAAuD;IACvD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,IAAI,CAAC;IACd,CAAC;IACD,+CAA+C;IAC/C,OAAO,QAAQ,CAAC,OAAO,KAAK,KAAK,CAAC;AACpC,CAAC;AAED;;;;;;;;;;;;;;;GAeG;AACH,MAAM,UAAU,SAAS,CAAC,QAA4B;IACpD,iDAAiD;IACjD,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,QAAQ,CAAC,IAAI,IAAI,MAAM,CAAC;AACjC,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,MAAM,UAAU,iBAAiB,CAC/B,MAAc,EACd,QAA4B;IAE5B,4CAA4C;IAC5C,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC3B,OAAO;YACL,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,aAAa;SACtB,CAAC;IACJ,CAAC;IAED,sCAAsC;IACtC,IAAI,CAAC,QAAQ,EAAE,CAAC;QACd,OAAO;YACL,OAAO,EAAE,IAAI;YACb,MAAM,EAAE,SAAS;SAClB,CAAC;IACJ,CAAC;IAED,MAAM,EAAE,SAAS,EAAE,SAAS,EAAE,GAAG,QAAQ,CAAC;IAE1C,2CAA2C;IAC3C,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtC,IAAI,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC/B,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,cAAc;aACvB,CAAC;QACJ,CAAC;IACH,CAAC;IAED,6DAA6D;IAC7D,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtC,IAAI,CAAC,SAAS,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAChC,OAAO;gBACL,OAAO,EAAE,KAAK;gBACd,MAAM,EAAE,kBAAkB;aAC3B,CAAC;QACJ,CAAC;IACH,CAAC;IAED,kBAAkB;IAClB,OAAO;QACL,OAAO,EAAE,IAAI;QACb,MAAM,EAAE,SAAS;KAClB,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,MAAM,UAAU,mBAAmB,CACjC,KAAc,EACd,IAAwB,EACxB,YAAqB;IAErB,6BAA6B;IAC7B,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,KAAK,CAAC;IACf,CAAC;IAED,6CAA6C;IAC7C,IAAI,IAAI,KAAK,MAAM,EAAE,CAAC;QACpB,OAAO,IAAI,CAAC;IACd,CAAC;IAED,6CAA6C;IAC7C,OAAO,YAAY,CAAC;AACtB,CAAC"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling utilities for chat connectors
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Error classification for handling decisions
|
|
6
|
+
* - User-friendly error messages
|
|
7
|
+
* - Retry logic for transient failures
|
|
8
|
+
* - Safe execution wrappers
|
|
9
|
+
*
|
|
10
|
+
* Platform-specific error classification stays in platform packages,
|
|
11
|
+
* but the common infrastructure is here.
|
|
12
|
+
*/
|
|
13
|
+
import type { ChatConnectorLogger } from "./types.js";
|
|
14
|
+
/**
|
|
15
|
+
* Categories of errors for handling purposes
|
|
16
|
+
*
|
|
17
|
+
* Used to determine appropriate responses and whether to retry.
|
|
18
|
+
*/
|
|
19
|
+
export declare enum ErrorCategory {
|
|
20
|
+
/** Transient errors that may succeed on retry (network issues, timeouts) */
|
|
21
|
+
TRANSIENT = "transient",
|
|
22
|
+
/** Permanent errors that won't succeed on retry (invalid config) */
|
|
23
|
+
PERMANENT = "permanent",
|
|
24
|
+
/** Rate limit errors that need backoff before retry */
|
|
25
|
+
RATE_LIMIT = "rate_limit",
|
|
26
|
+
/** Configuration or setup errors */
|
|
27
|
+
CONFIGURATION = "configuration",
|
|
28
|
+
/** Authentication or authorization errors */
|
|
29
|
+
AUTH = "auth",
|
|
30
|
+
/** Network connectivity errors */
|
|
31
|
+
NETWORK = "network",
|
|
32
|
+
/** API-specific errors */
|
|
33
|
+
API = "api",
|
|
34
|
+
/** Internal/unexpected errors */
|
|
35
|
+
INTERNAL = "internal",
|
|
36
|
+
/** Unknown/unexpected errors */
|
|
37
|
+
UNKNOWN = "unknown"
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Classified error with category and handling information
|
|
41
|
+
*/
|
|
42
|
+
export interface ClassifiedError {
|
|
43
|
+
/** Original error */
|
|
44
|
+
error: Error;
|
|
45
|
+
/** Error category for handling decisions */
|
|
46
|
+
category: ErrorCategory;
|
|
47
|
+
/** User-friendly message to display */
|
|
48
|
+
userMessage: string;
|
|
49
|
+
/** Whether this error should be retried */
|
|
50
|
+
shouldRetry: boolean;
|
|
51
|
+
/** Suggested retry delay in milliseconds (if shouldRetry is true) */
|
|
52
|
+
retryDelayMs?: number;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Standard user-friendly error messages
|
|
56
|
+
*
|
|
57
|
+
* These are safe to show to end users in chat platforms.
|
|
58
|
+
*/
|
|
59
|
+
export declare const USER_ERROR_MESSAGES: {
|
|
60
|
+
/** Generic processing error */
|
|
61
|
+
readonly PROCESSING_ERROR: "Sorry, I encountered an error processing your request. Please try again.";
|
|
62
|
+
/** Connection/network error */
|
|
63
|
+
readonly CONNECTION_ERROR: "I'm having trouble connecting right now. Please try again in a moment.";
|
|
64
|
+
/** Rate limit error */
|
|
65
|
+
readonly RATE_LIMITED: "I'm receiving too many requests right now. Please wait a moment and try again.";
|
|
66
|
+
/** Command execution error */
|
|
67
|
+
readonly COMMAND_ERROR: "Sorry, I couldn't complete that command. Please try again.";
|
|
68
|
+
/** Session error */
|
|
69
|
+
readonly SESSION_ERROR: "I'm having trouble with your conversation session. Please try again.";
|
|
70
|
+
/** Timeout error */
|
|
71
|
+
readonly TIMEOUT_ERROR: "The request took too long to complete. Please try again.";
|
|
72
|
+
/** Permission error */
|
|
73
|
+
readonly PERMISSION_ERROR: "I don't have permission to do that in this channel.";
|
|
74
|
+
/** Authentication error */
|
|
75
|
+
readonly AUTH_ERROR: "I'm having trouble authenticating. Please contact an administrator.";
|
|
76
|
+
/** API error */
|
|
77
|
+
readonly API_ERROR: "Something went wrong with the chat API. Please try again.";
|
|
78
|
+
/** Internal error */
|
|
79
|
+
readonly INTERNAL_ERROR: "An internal error occurred. Please try again or start a new session.";
|
|
80
|
+
/** Unknown error */
|
|
81
|
+
readonly UNKNOWN_ERROR: "An unexpected error occurred. Please try again.";
|
|
82
|
+
};
|
|
83
|
+
export type UserErrorMessageKey = keyof typeof USER_ERROR_MESSAGES;
|
|
84
|
+
/**
|
|
85
|
+
* Options for retry operations
|
|
86
|
+
*/
|
|
87
|
+
export interface RetryOptions {
|
|
88
|
+
/** Maximum number of retry attempts (default: 3) */
|
|
89
|
+
maxAttempts?: number;
|
|
90
|
+
/** Base delay between retries in ms (default: 1000) */
|
|
91
|
+
baseDelayMs?: number;
|
|
92
|
+
/** Maximum delay between retries in ms (default: 30000) */
|
|
93
|
+
maxDelayMs?: number;
|
|
94
|
+
/** Exponential backoff multiplier (default: 2) */
|
|
95
|
+
backoffMultiplier?: number;
|
|
96
|
+
/** Logger for retry attempts */
|
|
97
|
+
logger?: ChatConnectorLogger;
|
|
98
|
+
/** Operation name for logging */
|
|
99
|
+
operationName?: string;
|
|
100
|
+
/** Predicate to determine if error should be retried */
|
|
101
|
+
shouldRetry?: (error: unknown, attempt: number) => boolean;
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Result of a retry operation
|
|
105
|
+
*/
|
|
106
|
+
export interface RetryResult<T> {
|
|
107
|
+
/** Whether the operation succeeded */
|
|
108
|
+
success: boolean;
|
|
109
|
+
/** The result value (if success is true) */
|
|
110
|
+
value?: T;
|
|
111
|
+
/** The last error encountered (if success is false) */
|
|
112
|
+
error?: Error;
|
|
113
|
+
/** Number of attempts made */
|
|
114
|
+
attempts: number;
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Execute an async operation with retry logic
|
|
118
|
+
*
|
|
119
|
+
* Uses exponential backoff for transient failures.
|
|
120
|
+
* Only retries errors when the shouldRetry predicate returns true.
|
|
121
|
+
*
|
|
122
|
+
* @param operation - Async function to execute
|
|
123
|
+
* @param options - Retry configuration
|
|
124
|
+
* @returns Result with success status, value, or error
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```typescript
|
|
128
|
+
* const result = await withRetry(
|
|
129
|
+
* async () => {
|
|
130
|
+
* const response = await fetch(url);
|
|
131
|
+
* if (!response.ok) throw new Error('Request failed');
|
|
132
|
+
* return response.json();
|
|
133
|
+
* },
|
|
134
|
+
* {
|
|
135
|
+
* maxAttempts: 3,
|
|
136
|
+
* operationName: 'fetchData',
|
|
137
|
+
* logger: myLogger,
|
|
138
|
+
* shouldRetry: (error) => isTransientError(error),
|
|
139
|
+
* }
|
|
140
|
+
* );
|
|
141
|
+
*
|
|
142
|
+
* if (result.success) {
|
|
143
|
+
* console.log('Data:', result.value);
|
|
144
|
+
* } else {
|
|
145
|
+
* console.error('Failed after', result.attempts, 'attempts:', result.error);
|
|
146
|
+
* }
|
|
147
|
+
* ```
|
|
148
|
+
*/
|
|
149
|
+
export declare function withRetry<T>(operation: () => Promise<T>, options?: RetryOptions): Promise<RetryResult<T>>;
|
|
150
|
+
/**
|
|
151
|
+
* Check if an error is a transient error (may succeed on retry)
|
|
152
|
+
*
|
|
153
|
+
* @param error - Error to check
|
|
154
|
+
* @returns true if the error appears to be transient
|
|
155
|
+
*/
|
|
156
|
+
export declare function isTransientError(error: unknown): boolean;
|
|
157
|
+
/**
|
|
158
|
+
* Check if an error is a rate limit error
|
|
159
|
+
*
|
|
160
|
+
* @param error - Error to check
|
|
161
|
+
* @returns true if the error indicates rate limiting
|
|
162
|
+
*/
|
|
163
|
+
export declare function isRateLimitError(error: unknown): boolean;
|
|
164
|
+
/**
|
|
165
|
+
* Check if an error is an authentication error
|
|
166
|
+
*
|
|
167
|
+
* @param error - Error to check
|
|
168
|
+
* @returns true if the error indicates an auth problem
|
|
169
|
+
*/
|
|
170
|
+
export declare function isAuthError(error: unknown): boolean;
|
|
171
|
+
/**
|
|
172
|
+
* Execute an async function and handle errors safely
|
|
173
|
+
*
|
|
174
|
+
* Returns the result or undefined if an error occurs.
|
|
175
|
+
* Always logs errors for debugging.
|
|
176
|
+
*
|
|
177
|
+
* @param operation - Async function to execute
|
|
178
|
+
* @param logger - Logger for error logging
|
|
179
|
+
* @param context - Description of the operation for logging
|
|
180
|
+
* @returns Result or undefined on error
|
|
181
|
+
*
|
|
182
|
+
* @example
|
|
183
|
+
* ```typescript
|
|
184
|
+
* const result = await safeExecute(
|
|
185
|
+
* () => fetchUserData(userId),
|
|
186
|
+
* logger,
|
|
187
|
+
* 'fetching user data'
|
|
188
|
+
* );
|
|
189
|
+
* if (result) {
|
|
190
|
+
* // Use result
|
|
191
|
+
* }
|
|
192
|
+
* ```
|
|
193
|
+
*/
|
|
194
|
+
export declare function safeExecute<T>(operation: () => Promise<T>, logger: ChatConnectorLogger, context: string): Promise<T | undefined>;
|
|
195
|
+
/**
|
|
196
|
+
* Execute an async function and reply with error message on failure
|
|
197
|
+
*
|
|
198
|
+
* On error, sends a user-friendly error message to the reply function.
|
|
199
|
+
*
|
|
200
|
+
* @param operation - Async function to execute
|
|
201
|
+
* @param reply - Function to send error reply
|
|
202
|
+
* @param logger - Logger for error logging
|
|
203
|
+
* @param context - Description of the operation for logging
|
|
204
|
+
* @param userMessage - Custom error message for user (optional)
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```typescript
|
|
208
|
+
* await safeExecuteWithReply(
|
|
209
|
+
* () => processMessage(message),
|
|
210
|
+
* (msg) => channel.send(msg),
|
|
211
|
+
* logger,
|
|
212
|
+
* 'processing message'
|
|
213
|
+
* );
|
|
214
|
+
* ```
|
|
215
|
+
*/
|
|
216
|
+
export declare function safeExecuteWithReply(operation: () => Promise<void>, reply: (content: string) => Promise<void>, logger: ChatConnectorLogger, context: string, userMessage?: string): Promise<void>;
|
|
217
|
+
//# sourceMappingURL=error-handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../src/error-handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling utilities for chat connectors
|
|
3
|
+
*
|
|
4
|
+
* Provides:
|
|
5
|
+
* - Error classification for handling decisions
|
|
6
|
+
* - User-friendly error messages
|
|
7
|
+
* - Retry logic for transient failures
|
|
8
|
+
* - Safe execution wrappers
|
|
9
|
+
*
|
|
10
|
+
* Platform-specific error classification stays in platform packages,
|
|
11
|
+
* but the common infrastructure is here.
|
|
12
|
+
*/
|
|
13
|
+
// =============================================================================
|
|
14
|
+
// Error Categories
|
|
15
|
+
// =============================================================================
|
|
16
|
+
/**
|
|
17
|
+
* Categories of errors for handling purposes
|
|
18
|
+
*
|
|
19
|
+
* Used to determine appropriate responses and whether to retry.
|
|
20
|
+
*/
|
|
21
|
+
export var ErrorCategory;
|
|
22
|
+
(function (ErrorCategory) {
|
|
23
|
+
/** Transient errors that may succeed on retry (network issues, timeouts) */
|
|
24
|
+
ErrorCategory["TRANSIENT"] = "transient";
|
|
25
|
+
/** Permanent errors that won't succeed on retry (invalid config) */
|
|
26
|
+
ErrorCategory["PERMANENT"] = "permanent";
|
|
27
|
+
/** Rate limit errors that need backoff before retry */
|
|
28
|
+
ErrorCategory["RATE_LIMIT"] = "rate_limit";
|
|
29
|
+
/** Configuration or setup errors */
|
|
30
|
+
ErrorCategory["CONFIGURATION"] = "configuration";
|
|
31
|
+
/** Authentication or authorization errors */
|
|
32
|
+
ErrorCategory["AUTH"] = "auth";
|
|
33
|
+
/** Network connectivity errors */
|
|
34
|
+
ErrorCategory["NETWORK"] = "network";
|
|
35
|
+
/** API-specific errors */
|
|
36
|
+
ErrorCategory["API"] = "api";
|
|
37
|
+
/** Internal/unexpected errors */
|
|
38
|
+
ErrorCategory["INTERNAL"] = "internal";
|
|
39
|
+
/** Unknown/unexpected errors */
|
|
40
|
+
ErrorCategory["UNKNOWN"] = "unknown";
|
|
41
|
+
})(ErrorCategory || (ErrorCategory = {}));
|
|
42
|
+
// =============================================================================
|
|
43
|
+
// User Error Messages
|
|
44
|
+
// =============================================================================
|
|
45
|
+
/**
|
|
46
|
+
* Standard user-friendly error messages
|
|
47
|
+
*
|
|
48
|
+
* These are safe to show to end users in chat platforms.
|
|
49
|
+
*/
|
|
50
|
+
export const USER_ERROR_MESSAGES = {
|
|
51
|
+
/** Generic processing error */
|
|
52
|
+
PROCESSING_ERROR: "Sorry, I encountered an error processing your request. Please try again.",
|
|
53
|
+
/** Connection/network error */
|
|
54
|
+
CONNECTION_ERROR: "I'm having trouble connecting right now. Please try again in a moment.",
|
|
55
|
+
/** Rate limit error */
|
|
56
|
+
RATE_LIMITED: "I'm receiving too many requests right now. Please wait a moment and try again.",
|
|
57
|
+
/** Command execution error */
|
|
58
|
+
COMMAND_ERROR: "Sorry, I couldn't complete that command. Please try again.",
|
|
59
|
+
/** Session error */
|
|
60
|
+
SESSION_ERROR: "I'm having trouble with your conversation session. Please try again.",
|
|
61
|
+
/** Timeout error */
|
|
62
|
+
TIMEOUT_ERROR: "The request took too long to complete. Please try again.",
|
|
63
|
+
/** Permission error */
|
|
64
|
+
PERMISSION_ERROR: "I don't have permission to do that in this channel.",
|
|
65
|
+
/** Authentication error */
|
|
66
|
+
AUTH_ERROR: "I'm having trouble authenticating. Please contact an administrator.",
|
|
67
|
+
/** API error */
|
|
68
|
+
API_ERROR: "Something went wrong with the chat API. Please try again.",
|
|
69
|
+
/** Internal error */
|
|
70
|
+
INTERNAL_ERROR: "An internal error occurred. Please try again or start a new session.",
|
|
71
|
+
/** Unknown error */
|
|
72
|
+
UNKNOWN_ERROR: "An unexpected error occurred. Please try again.",
|
|
73
|
+
};
|
|
74
|
+
/**
|
|
75
|
+
* Default retry options
|
|
76
|
+
*/
|
|
77
|
+
const DEFAULT_RETRY_OPTIONS = {
|
|
78
|
+
maxAttempts: 3,
|
|
79
|
+
baseDelayMs: 1000,
|
|
80
|
+
maxDelayMs: 30000,
|
|
81
|
+
backoffMultiplier: 2,
|
|
82
|
+
operationName: "operation",
|
|
83
|
+
};
|
|
84
|
+
/**
|
|
85
|
+
* Execute an async operation with retry logic
|
|
86
|
+
*
|
|
87
|
+
* Uses exponential backoff for transient failures.
|
|
88
|
+
* Only retries errors when the shouldRetry predicate returns true.
|
|
89
|
+
*
|
|
90
|
+
* @param operation - Async function to execute
|
|
91
|
+
* @param options - Retry configuration
|
|
92
|
+
* @returns Result with success status, value, or error
|
|
93
|
+
*
|
|
94
|
+
* @example
|
|
95
|
+
* ```typescript
|
|
96
|
+
* const result = await withRetry(
|
|
97
|
+
* async () => {
|
|
98
|
+
* const response = await fetch(url);
|
|
99
|
+
* if (!response.ok) throw new Error('Request failed');
|
|
100
|
+
* return response.json();
|
|
101
|
+
* },
|
|
102
|
+
* {
|
|
103
|
+
* maxAttempts: 3,
|
|
104
|
+
* operationName: 'fetchData',
|
|
105
|
+
* logger: myLogger,
|
|
106
|
+
* shouldRetry: (error) => isTransientError(error),
|
|
107
|
+
* }
|
|
108
|
+
* );
|
|
109
|
+
*
|
|
110
|
+
* if (result.success) {
|
|
111
|
+
* console.log('Data:', result.value);
|
|
112
|
+
* } else {
|
|
113
|
+
* console.error('Failed after', result.attempts, 'attempts:', result.error);
|
|
114
|
+
* }
|
|
115
|
+
* ```
|
|
116
|
+
*/
|
|
117
|
+
export async function withRetry(operation, options = {}) {
|
|
118
|
+
const opts = { ...DEFAULT_RETRY_OPTIONS, ...options };
|
|
119
|
+
const { maxAttempts, baseDelayMs, maxDelayMs, backoffMultiplier, operationName, logger, } = opts;
|
|
120
|
+
// Default shouldRetry: retry transient errors
|
|
121
|
+
const shouldRetryFn = options.shouldRetry ?? ((error) => isTransientError(error));
|
|
122
|
+
let lastError;
|
|
123
|
+
let attempt = 0;
|
|
124
|
+
while (attempt < maxAttempts) {
|
|
125
|
+
attempt++;
|
|
126
|
+
try {
|
|
127
|
+
const value = await operation();
|
|
128
|
+
return { success: true, value, attempts: attempt };
|
|
129
|
+
}
|
|
130
|
+
catch (error) {
|
|
131
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
132
|
+
// Check if we should retry
|
|
133
|
+
const shouldRetryError = shouldRetryFn(error, attempt);
|
|
134
|
+
if (!shouldRetryError || attempt >= maxAttempts) {
|
|
135
|
+
// Don't retry: either permanent error or out of attempts
|
|
136
|
+
logger?.debug(`${operationName} failed permanently after ${attempt} attempt(s)`, {
|
|
137
|
+
error: lastError.message,
|
|
138
|
+
});
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
// Calculate delay with exponential backoff
|
|
142
|
+
const delay = Math.min(baseDelayMs * Math.pow(backoffMultiplier, attempt - 1), maxDelayMs);
|
|
143
|
+
logger?.info(`${operationName} failed, retrying in ${delay}ms...`, {
|
|
144
|
+
attempt,
|
|
145
|
+
maxAttempts,
|
|
146
|
+
error: lastError.message,
|
|
147
|
+
});
|
|
148
|
+
// Wait before retrying
|
|
149
|
+
await sleep(delay);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
success: false,
|
|
154
|
+
error: lastError,
|
|
155
|
+
attempts: attempt,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
// =============================================================================
|
|
159
|
+
// Error Classification Helpers
|
|
160
|
+
// =============================================================================
|
|
161
|
+
/**
|
|
162
|
+
* Check if an error is a transient error (may succeed on retry)
|
|
163
|
+
*
|
|
164
|
+
* @param error - Error to check
|
|
165
|
+
* @returns true if the error appears to be transient
|
|
166
|
+
*/
|
|
167
|
+
export function isTransientError(error) {
|
|
168
|
+
if (!(error instanceof Error)) {
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
const message = error.message.toLowerCase();
|
|
172
|
+
const name = error.name.toLowerCase();
|
|
173
|
+
// Network errors
|
|
174
|
+
const networkPatterns = [
|
|
175
|
+
"econnreset",
|
|
176
|
+
"econnrefused",
|
|
177
|
+
"etimedout",
|
|
178
|
+
"enotfound",
|
|
179
|
+
"eai_again",
|
|
180
|
+
"enetunreach",
|
|
181
|
+
"ehostunreach",
|
|
182
|
+
"socket hang up",
|
|
183
|
+
"network",
|
|
184
|
+
"connect econnrefused",
|
|
185
|
+
"getaddrinfo",
|
|
186
|
+
"fetch failed",
|
|
187
|
+
];
|
|
188
|
+
if (networkPatterns.some((pattern) => message.includes(pattern.toLowerCase()) ||
|
|
189
|
+
name.includes(pattern.toLowerCase()))) {
|
|
190
|
+
return true;
|
|
191
|
+
}
|
|
192
|
+
// Timeout errors
|
|
193
|
+
const timeoutPatterns = ["timeout", "timed out", "etimedout", "aborterror"];
|
|
194
|
+
if (timeoutPatterns.some((pattern) => message.includes(pattern.toLowerCase()) ||
|
|
195
|
+
name.includes(pattern.toLowerCase()))) {
|
|
196
|
+
return true;
|
|
197
|
+
}
|
|
198
|
+
return false;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Check if an error is a rate limit error
|
|
202
|
+
*
|
|
203
|
+
* @param error - Error to check
|
|
204
|
+
* @returns true if the error indicates rate limiting
|
|
205
|
+
*/
|
|
206
|
+
export function isRateLimitError(error) {
|
|
207
|
+
if (!(error instanceof Error)) {
|
|
208
|
+
return false;
|
|
209
|
+
}
|
|
210
|
+
const message = error.message.toLowerCase();
|
|
211
|
+
return message.includes("rate_limit") || message.includes("ratelimited");
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Check if an error is an authentication error
|
|
215
|
+
*
|
|
216
|
+
* @param error - Error to check
|
|
217
|
+
* @returns true if the error indicates an auth problem
|
|
218
|
+
*/
|
|
219
|
+
export function isAuthError(error) {
|
|
220
|
+
if (!(error instanceof Error)) {
|
|
221
|
+
return false;
|
|
222
|
+
}
|
|
223
|
+
const message = error.message.toLowerCase();
|
|
224
|
+
return (message.includes("invalid_auth") ||
|
|
225
|
+
message.includes("token_revoked") ||
|
|
226
|
+
message.includes("token_expired") ||
|
|
227
|
+
message.includes("not_authed") ||
|
|
228
|
+
message.includes("unauthorized") ||
|
|
229
|
+
message.includes("forbidden"));
|
|
230
|
+
}
|
|
231
|
+
// =============================================================================
|
|
232
|
+
// Safe Execution Helpers
|
|
233
|
+
// =============================================================================
|
|
234
|
+
/**
|
|
235
|
+
* Execute an async function and handle errors safely
|
|
236
|
+
*
|
|
237
|
+
* Returns the result or undefined if an error occurs.
|
|
238
|
+
* Always logs errors for debugging.
|
|
239
|
+
*
|
|
240
|
+
* @param operation - Async function to execute
|
|
241
|
+
* @param logger - Logger for error logging
|
|
242
|
+
* @param context - Description of the operation for logging
|
|
243
|
+
* @returns Result or undefined on error
|
|
244
|
+
*
|
|
245
|
+
* @example
|
|
246
|
+
* ```typescript
|
|
247
|
+
* const result = await safeExecute(
|
|
248
|
+
* () => fetchUserData(userId),
|
|
249
|
+
* logger,
|
|
250
|
+
* 'fetching user data'
|
|
251
|
+
* );
|
|
252
|
+
* if (result) {
|
|
253
|
+
* // Use result
|
|
254
|
+
* }
|
|
255
|
+
* ```
|
|
256
|
+
*/
|
|
257
|
+
export async function safeExecute(operation, logger, context) {
|
|
258
|
+
try {
|
|
259
|
+
return await operation();
|
|
260
|
+
}
|
|
261
|
+
catch (error) {
|
|
262
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
263
|
+
logger.error(`Error in ${context}: ${errorMessage}`);
|
|
264
|
+
return undefined;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Execute an async function and reply with error message on failure
|
|
269
|
+
*
|
|
270
|
+
* On error, sends a user-friendly error message to the reply function.
|
|
271
|
+
*
|
|
272
|
+
* @param operation - Async function to execute
|
|
273
|
+
* @param reply - Function to send error reply
|
|
274
|
+
* @param logger - Logger for error logging
|
|
275
|
+
* @param context - Description of the operation for logging
|
|
276
|
+
* @param userMessage - Custom error message for user (optional)
|
|
277
|
+
*
|
|
278
|
+
* @example
|
|
279
|
+
* ```typescript
|
|
280
|
+
* await safeExecuteWithReply(
|
|
281
|
+
* () => processMessage(message),
|
|
282
|
+
* (msg) => channel.send(msg),
|
|
283
|
+
* logger,
|
|
284
|
+
* 'processing message'
|
|
285
|
+
* );
|
|
286
|
+
* ```
|
|
287
|
+
*/
|
|
288
|
+
export async function safeExecuteWithReply(operation, reply, logger, context, userMessage) {
|
|
289
|
+
try {
|
|
290
|
+
await operation();
|
|
291
|
+
}
|
|
292
|
+
catch (error) {
|
|
293
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
294
|
+
logger.error(`Error in ${context}: ${err.message}`);
|
|
295
|
+
try {
|
|
296
|
+
const message = userMessage ?? USER_ERROR_MESSAGES.PROCESSING_ERROR;
|
|
297
|
+
await reply(message);
|
|
298
|
+
}
|
|
299
|
+
catch (replyError) {
|
|
300
|
+
logger.error(`Failed to send error reply: ${replyError.message}`);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
// =============================================================================
|
|
305
|
+
// Utility Functions
|
|
306
|
+
// =============================================================================
|
|
307
|
+
/**
|
|
308
|
+
* Sleep for a specified duration
|
|
309
|
+
*/
|
|
310
|
+
function sleep(ms) {
|
|
311
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
312
|
+
}
|
|
313
|
+
//# sourceMappingURL=error-handler.js.map
|