@agentuity/server 0.1.16 → 0.1.18

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.
Files changed (93) hide show
  1. package/dist/api/api.d.ts +11 -6
  2. package/dist/api/api.d.ts.map +1 -1
  3. package/dist/api/api.js +19 -12
  4. package/dist/api/api.js.map +1 -1
  5. package/dist/api/index.d.ts +1 -0
  6. package/dist/api/index.d.ts.map +1 -1
  7. package/dist/api/index.js +1 -0
  8. package/dist/api/index.js.map +1 -1
  9. package/dist/api/org/env-delete.d.ts.map +1 -1
  10. package/dist/api/org/env-delete.js.map +1 -1
  11. package/dist/api/org/env-get.d.ts.map +1 -1
  12. package/dist/api/org/env-get.js.map +1 -1
  13. package/dist/api/org/env-update.d.ts.map +1 -1
  14. package/dist/api/org/env-update.js.map +1 -1
  15. package/dist/api/queue/analytics.d.ts +108 -0
  16. package/dist/api/queue/analytics.d.ts.map +1 -0
  17. package/dist/api/queue/analytics.js +245 -0
  18. package/dist/api/queue/analytics.js.map +1 -0
  19. package/dist/api/queue/destinations.d.ts +108 -0
  20. package/dist/api/queue/destinations.d.ts.map +1 -0
  21. package/dist/api/queue/destinations.js +238 -0
  22. package/dist/api/queue/destinations.js.map +1 -0
  23. package/dist/api/queue/dlq.d.ts +100 -0
  24. package/dist/api/queue/dlq.d.ts.map +1 -0
  25. package/dist/api/queue/dlq.js +204 -0
  26. package/dist/api/queue/dlq.js.map +1 -0
  27. package/dist/api/queue/index.d.ts +55 -0
  28. package/dist/api/queue/index.d.ts.map +1 -0
  29. package/dist/api/queue/index.js +86 -0
  30. package/dist/api/queue/index.js.map +1 -0
  31. package/dist/api/queue/messages.d.ts +332 -0
  32. package/dist/api/queue/messages.d.ts.map +1 -0
  33. package/dist/api/queue/messages.js +637 -0
  34. package/dist/api/queue/messages.js.map +1 -0
  35. package/dist/api/queue/queues.d.ts +153 -0
  36. package/dist/api/queue/queues.d.ts.map +1 -0
  37. package/dist/api/queue/queues.js +319 -0
  38. package/dist/api/queue/queues.js.map +1 -0
  39. package/dist/api/queue/sources.d.ts +132 -0
  40. package/dist/api/queue/sources.d.ts.map +1 -0
  41. package/dist/api/queue/sources.js +285 -0
  42. package/dist/api/queue/sources.js.map +1 -0
  43. package/dist/api/queue/types.d.ts +1129 -0
  44. package/dist/api/queue/types.d.ts.map +1 -0
  45. package/dist/api/queue/types.js +949 -0
  46. package/dist/api/queue/types.js.map +1 -0
  47. package/dist/api/queue/util.d.ts +262 -0
  48. package/dist/api/queue/util.d.ts.map +1 -0
  49. package/dist/api/queue/util.js +171 -0
  50. package/dist/api/queue/util.js.map +1 -0
  51. package/dist/api/queue/validation.d.ts +247 -0
  52. package/dist/api/queue/validation.d.ts.map +1 -0
  53. package/dist/api/queue/validation.js +513 -0
  54. package/dist/api/queue/validation.js.map +1 -0
  55. package/dist/api/sandbox/get.d.ts.map +1 -1
  56. package/dist/api/sandbox/get.js +5 -0
  57. package/dist/api/sandbox/get.js.map +1 -1
  58. package/dist/api/sandbox/index.d.ts +3 -3
  59. package/dist/api/sandbox/index.d.ts.map +1 -1
  60. package/dist/api/sandbox/index.js +1 -1
  61. package/dist/api/sandbox/index.js.map +1 -1
  62. package/dist/api/sandbox/run.d.ts.map +1 -1
  63. package/dist/api/sandbox/run.js +5 -2
  64. package/dist/api/sandbox/run.js.map +1 -1
  65. package/dist/api/sandbox/snapshot-build.d.ts +2 -0
  66. package/dist/api/sandbox/snapshot-build.d.ts.map +1 -1
  67. package/dist/api/sandbox/snapshot-build.js +4 -0
  68. package/dist/api/sandbox/snapshot-build.js.map +1 -1
  69. package/dist/api/sandbox/snapshot.d.ts +143 -1
  70. package/dist/api/sandbox/snapshot.d.ts.map +1 -1
  71. package/dist/api/sandbox/snapshot.js +183 -4
  72. package/dist/api/sandbox/snapshot.js.map +1 -1
  73. package/package.json +4 -4
  74. package/src/api/api.ts +62 -13
  75. package/src/api/index.ts +1 -0
  76. package/src/api/org/env-delete.ts +1 -4
  77. package/src/api/org/env-get.ts +1 -4
  78. package/src/api/org/env-update.ts +1 -4
  79. package/src/api/queue/analytics.ts +313 -0
  80. package/src/api/queue/destinations.ts +321 -0
  81. package/src/api/queue/dlq.ts +283 -0
  82. package/src/api/queue/index.ts +261 -0
  83. package/src/api/queue/messages.ts +875 -0
  84. package/src/api/queue/queues.ts +448 -0
  85. package/src/api/queue/sources.ts +384 -0
  86. package/src/api/queue/types.ts +1253 -0
  87. package/src/api/queue/util.ts +204 -0
  88. package/src/api/queue/validation.ts +560 -0
  89. package/src/api/sandbox/get.ts +5 -0
  90. package/src/api/sandbox/index.ts +13 -1
  91. package/src/api/sandbox/run.ts +5 -2
  92. package/src/api/sandbox/snapshot-build.ts +4 -0
  93. package/src/api/sandbox/snapshot.ts +223 -5
@@ -0,0 +1,321 @@
1
+ import { z } from 'zod';
2
+ import { APIClient, APIResponseSchema, APIResponseSchemaNoData, APIError } from '../api';
3
+ import {
4
+ DestinationSchema,
5
+ type Destination,
6
+ type CreateDestinationRequest,
7
+ type UpdateDestinationRequest,
8
+ type QueueApiOptions,
9
+ CreateDestinationRequestSchema,
10
+ UpdateDestinationRequestSchema,
11
+ } from './types';
12
+ import {
13
+ QueueError,
14
+ QueueNotFoundError,
15
+ DestinationNotFoundError,
16
+ DestinationAlreadyExistsError,
17
+ queueApiPath,
18
+ buildQueueHeaders,
19
+ } from './util';
20
+ import { validateQueueName, validateDestinationId, validateDestinationConfig } from './validation';
21
+
22
+ const DestinationResponseSchema = APIResponseSchema(z.object({ destination: DestinationSchema }));
23
+ const DestinationsListResponseSchema = APIResponseSchema(
24
+ z.object({
25
+ destinations: z.array(DestinationSchema),
26
+ })
27
+ );
28
+ const DeleteDestinationResponseSchema = APIResponseSchemaNoData();
29
+
30
+ /**
31
+ * Create a destination for a queue.
32
+ *
33
+ * Destinations are webhook endpoints that automatically receive messages when
34
+ * they are published to a queue. When a message is published, it will be
35
+ * delivered via HTTP POST to all active destinations configured for that queue.
36
+ *
37
+ * @param client - The API client instance
38
+ * @param queueName - The name of the queue to add the destination to
39
+ * @param params - Destination configuration including URL and optional settings
40
+ * @returns The created destination with assigned ID
41
+ * @throws {QueueValidationError} If validation fails (invalid queue name or config)
42
+ * @throws {QueueNotFoundError} If the queue does not exist
43
+ * @throws {QueueError} If the API request fails
44
+ *
45
+ * @example
46
+ * ```typescript
47
+ * const destination = await createDestination(client, 'order-events', {
48
+ * url: 'https://api.example.com/webhooks/orders',
49
+ * config: {
50
+ * retry_attempts: 3,
51
+ * timeout_seconds: 30,
52
+ * },
53
+ * });
54
+ * console.log(`Created destination ${destination.id}`);
55
+ * ```
56
+ */
57
+ export async function createDestination(
58
+ client: APIClient,
59
+ queueName: string,
60
+ params: CreateDestinationRequest,
61
+ options?: QueueApiOptions
62
+ ): Promise<Destination> {
63
+ validateQueueName(queueName);
64
+ if (params.config) {
65
+ validateDestinationConfig(params.config);
66
+ }
67
+
68
+ const url = queueApiPath('destinations/create', queueName);
69
+
70
+ try {
71
+ const resp = await client.post(
72
+ url,
73
+ params,
74
+ DestinationResponseSchema,
75
+ CreateDestinationRequestSchema,
76
+ undefined,
77
+ buildQueueHeaders(options?.orgId)
78
+ );
79
+
80
+ if (resp.success) {
81
+ return resp.data.destination;
82
+ }
83
+
84
+ if (resp.message?.includes('queue') && resp.message?.includes('not found')) {
85
+ throw new QueueNotFoundError({
86
+ queueName,
87
+ message: resp.message,
88
+ });
89
+ }
90
+
91
+ if (resp.message?.includes('already exists')) {
92
+ throw new DestinationAlreadyExistsError({
93
+ queueName,
94
+ url: params.config?.url,
95
+ message: `A destination with URL "${params.config?.url}" already exists for queue "${queueName}"`,
96
+ });
97
+ }
98
+
99
+ throw new QueueError({
100
+ queueName,
101
+ message: resp.message || 'Failed to create destination',
102
+ });
103
+ } catch (error) {
104
+ if (error instanceof APIError) {
105
+ const message = error.message || '';
106
+ if (message.includes('already exists')) {
107
+ throw new DestinationAlreadyExistsError({
108
+ queueName,
109
+ url: params.config?.url,
110
+ message: `A destination with URL "${params.config?.url}" already exists for queue "${queueName}"`,
111
+ });
112
+ }
113
+ if (message.includes('queue') && message.includes('not found')) {
114
+ throw new QueueNotFoundError({
115
+ queueName,
116
+ message,
117
+ });
118
+ }
119
+ throw new QueueError({
120
+ queueName,
121
+ message: message || 'Failed to create destination',
122
+ });
123
+ }
124
+ throw error;
125
+ }
126
+ }
127
+
128
+ /**
129
+ * List all destinations for a queue.
130
+ *
131
+ * Retrieves all webhook destinations configured for a queue. Each destination
132
+ * represents an endpoint that receives messages when they are published.
133
+ *
134
+ * @param client - The API client instance
135
+ * @param queueName - The name of the queue
136
+ * @returns Array of destinations configured for the queue
137
+ * @throws {QueueValidationError} If validation fails (invalid queue name)
138
+ * @throws {QueueNotFoundError} If the queue does not exist
139
+ * @throws {QueueError} If the API request fails
140
+ *
141
+ * @example
142
+ * ```typescript
143
+ * const destinations = await listDestinations(client, 'order-events');
144
+ * for (const dest of destinations) {
145
+ * console.log(`Destination ${dest.id}: ${dest.url} (${dest.enabled ? 'enabled' : 'disabled'})`);
146
+ * }
147
+ * ```
148
+ */
149
+ export async function listDestinations(
150
+ client: APIClient,
151
+ queueName: string,
152
+ options?: QueueApiOptions
153
+ ): Promise<Destination[]> {
154
+ validateQueueName(queueName);
155
+ const url = queueApiPath('destinations/list', queueName);
156
+ const resp = await client.get(
157
+ url,
158
+ DestinationsListResponseSchema,
159
+ undefined,
160
+ buildQueueHeaders(options?.orgId)
161
+ );
162
+
163
+ if (resp.success) {
164
+ return resp.data.destinations;
165
+ }
166
+
167
+ if (resp.message?.includes('not found')) {
168
+ throw new QueueNotFoundError({
169
+ queueName,
170
+ message: resp.message,
171
+ });
172
+ }
173
+
174
+ throw new QueueError({
175
+ queueName,
176
+ message: resp.message || 'Failed to list destinations',
177
+ });
178
+ }
179
+
180
+ /**
181
+ * Update a destination's configuration.
182
+ *
183
+ * Modifies an existing destination's settings such as URL, enabled status,
184
+ * or retry configuration. Only the fields provided in params will be updated.
185
+ *
186
+ * @param client - The API client instance
187
+ * @param queueName - The name of the queue
188
+ * @param destinationId - The destination ID to update (prefixed with dest_)
189
+ * @param params - Fields to update (partial update supported)
190
+ * @returns The updated destination
191
+ * @throws {QueueValidationError} If validation fails (invalid queue name, destination ID, or config)
192
+ * @throws {DestinationNotFoundError} If the destination does not exist
193
+ * @throws {QueueNotFoundError} If the queue does not exist
194
+ * @throws {QueueError} If the API request fails
195
+ *
196
+ * @example
197
+ * ```typescript
198
+ * // Disable a destination temporarily
199
+ * const updated = await updateDestination(client, 'order-events', 'dest_abc123', {
200
+ * enabled: false,
201
+ * });
202
+ *
203
+ * // Update URL and retry settings
204
+ * const updated = await updateDestination(client, 'order-events', 'dest_abc123', {
205
+ * url: 'https://api.example.com/v2/webhooks/orders',
206
+ * config: {
207
+ * retry_attempts: 5,
208
+ * },
209
+ * });
210
+ * ```
211
+ */
212
+ export async function updateDestination(
213
+ client: APIClient,
214
+ queueName: string,
215
+ destinationId: string,
216
+ params: UpdateDestinationRequest,
217
+ options?: QueueApiOptions
218
+ ): Promise<Destination> {
219
+ validateQueueName(queueName);
220
+ validateDestinationId(destinationId);
221
+ if (params.config) {
222
+ validateDestinationConfig(params.config);
223
+ }
224
+
225
+ const url = queueApiPath('destinations/update', queueName, destinationId);
226
+ const resp = await client.patch(
227
+ url,
228
+ params,
229
+ DestinationResponseSchema,
230
+ UpdateDestinationRequestSchema,
231
+ undefined,
232
+ buildQueueHeaders(options?.orgId)
233
+ );
234
+
235
+ if (resp.success) {
236
+ return resp.data.destination;
237
+ }
238
+
239
+ if (resp.message?.includes('destination') && resp.message?.includes('not found')) {
240
+ throw new DestinationNotFoundError({
241
+ queueName,
242
+ destinationId,
243
+ message: resp.message,
244
+ });
245
+ }
246
+
247
+ if (resp.message?.includes('queue') && resp.message?.includes('not found')) {
248
+ throw new QueueNotFoundError({
249
+ queueName,
250
+ message: resp.message,
251
+ });
252
+ }
253
+
254
+ throw new QueueError({
255
+ queueName,
256
+ message: resp.message || 'Failed to update destination',
257
+ });
258
+ }
259
+
260
+ /**
261
+ * Delete a destination from a queue.
262
+ *
263
+ * Permanently removes a webhook destination. Messages will no longer be
264
+ * delivered to this endpoint. This action cannot be undone.
265
+ *
266
+ * @param client - The API client instance
267
+ * @param queueName - The name of the queue
268
+ * @param destinationId - The destination ID to delete (prefixed with dest_)
269
+ * @returns void
270
+ * @throws {QueueValidationError} If validation fails (invalid queue name or destination ID)
271
+ * @throws {DestinationNotFoundError} If the destination does not exist
272
+ * @throws {QueueNotFoundError} If the queue does not exist
273
+ * @throws {QueueError} If the API request fails
274
+ *
275
+ * @example
276
+ * ```typescript
277
+ * await deleteDestination(client, 'order-events', 'dest_abc123');
278
+ * console.log('Destination deleted');
279
+ * ```
280
+ */
281
+ export async function deleteDestination(
282
+ client: APIClient,
283
+ queueName: string,
284
+ destinationId: string,
285
+ options?: QueueApiOptions
286
+ ): Promise<void> {
287
+ validateQueueName(queueName);
288
+ validateDestinationId(destinationId);
289
+
290
+ const url = queueApiPath('destinations/delete', queueName, destinationId);
291
+ const resp = await client.delete(
292
+ url,
293
+ DeleteDestinationResponseSchema,
294
+ undefined,
295
+ buildQueueHeaders(options?.orgId)
296
+ );
297
+
298
+ if (resp.success) {
299
+ return;
300
+ }
301
+
302
+ if (resp.message?.includes('destination') && resp.message?.includes('not found')) {
303
+ throw new DestinationNotFoundError({
304
+ queueName,
305
+ destinationId,
306
+ message: resp.message,
307
+ });
308
+ }
309
+
310
+ if (resp.message?.includes('queue') && resp.message?.includes('not found')) {
311
+ throw new QueueNotFoundError({
312
+ queueName,
313
+ message: resp.message,
314
+ });
315
+ }
316
+
317
+ throw new QueueError({
318
+ queueName,
319
+ message: resp.message || 'Failed to delete destination',
320
+ });
321
+ }
@@ -0,0 +1,283 @@
1
+ import { z } from 'zod';
2
+ import { APIClient, APIResponseSchema, APIResponseSchemaNoData } from '../api';
3
+ import {
4
+ DeadLetterMessageSchema,
5
+ MessageSchema,
6
+ type DeadLetterMessage,
7
+ type Message,
8
+ type ListDlqRequest,
9
+ type QueueApiOptions,
10
+ } from './types';
11
+ import {
12
+ QueueError,
13
+ QueueNotFoundError,
14
+ MessageNotFoundError,
15
+ queueApiPath,
16
+ queueApiPathWithQuery,
17
+ buildQueueHeaders,
18
+ } from './util';
19
+ import { validateQueueName, validateMessageId, validateLimit, validateOffset } from './validation';
20
+
21
+ const DlqListResponseSchema = APIResponseSchema(
22
+ z.object({
23
+ messages: z.array(DeadLetterMessageSchema),
24
+ total: z.number().optional(),
25
+ })
26
+ );
27
+ const ReplayDlqResponseSchema = APIResponseSchema(z.object({ message: MessageSchema }));
28
+ const DeleteDlqResponseSchema = APIResponseSchemaNoData();
29
+
30
+ /**
31
+ * List messages in the dead letter queue.
32
+ *
33
+ * Retrieves messages that failed processing after exhausting all retries.
34
+ * These messages can be inspected, replayed back to the main queue, or deleted.
35
+ *
36
+ * @param client - The API client instance
37
+ * @param queueName - The name of the queue whose DLQ to list
38
+ * @param params - Optional pagination parameters (limit, offset)
39
+ * @returns Object containing dead letter messages and optional total count
40
+ * @throws {QueueValidationError} If validation fails (invalid queue name, limit, or offset)
41
+ * @throws {QueueNotFoundError} If the queue does not exist
42
+ * @throws {QueueError} If the API request fails
43
+ *
44
+ * @example
45
+ * ```typescript
46
+ * // List first 10 dead letter messages
47
+ * const result = await listDeadLetterMessages(client, 'order-queue', { limit: 10 });
48
+ * for (const msg of result.messages) {
49
+ * console.log(`Failed message ${msg.id}: ${msg.failure_reason}`);
50
+ * console.log(`Attempts: ${msg.delivery_attempts}, Moved at: ${msg.moved_at}`);
51
+ * }
52
+ * ```
53
+ */
54
+ export async function listDeadLetterMessages(
55
+ client: APIClient,
56
+ queueName: string,
57
+ params?: ListDlqRequest,
58
+ options?: QueueApiOptions
59
+ ): Promise<{ messages: DeadLetterMessage[]; total?: number }> {
60
+ validateQueueName(queueName);
61
+ if (params?.limit !== undefined) {
62
+ validateLimit(params.limit);
63
+ }
64
+ if (params?.offset !== undefined) {
65
+ validateOffset(params.offset);
66
+ }
67
+
68
+ const searchParams = new URLSearchParams();
69
+ if (params?.limit !== undefined) {
70
+ searchParams.set('limit', String(params.limit));
71
+ }
72
+ if (params?.offset !== undefined) {
73
+ searchParams.set('offset', String(params.offset));
74
+ }
75
+
76
+ const queryString = searchParams.toString();
77
+ const url = queueApiPathWithQuery('dlq/list', queryString || undefined, queueName);
78
+ const resp = await client.get(
79
+ url,
80
+ DlqListResponseSchema,
81
+ undefined,
82
+ buildQueueHeaders(options?.orgId)
83
+ );
84
+
85
+ if (resp.success) {
86
+ return { messages: resp.data.messages, total: resp.data.total };
87
+ }
88
+
89
+ if (resp.message?.includes('not found')) {
90
+ throw new QueueNotFoundError({
91
+ queueName,
92
+ message: resp.message,
93
+ });
94
+ }
95
+
96
+ throw new QueueError({
97
+ queueName,
98
+ message: resp.message || 'Failed to list dead letter messages',
99
+ });
100
+ }
101
+
102
+ /**
103
+ * Replay a dead letter message back to the main queue.
104
+ *
105
+ * Moves a message from the dead letter queue back to the main queue for
106
+ * reprocessing. The message state is reset to pending and retry count is
107
+ * preserved. Use this after fixing the underlying issue that caused the failure.
108
+ *
109
+ * @param client - The API client instance
110
+ * @param queueName - The name of the queue
111
+ * @param messageId - The message ID to replay (prefixed with msg_)
112
+ * @returns The replayed message with updated state
113
+ * @throws {QueueValidationError} If validation fails (invalid queue name or message ID)
114
+ * @throws {MessageNotFoundError} If the message does not exist in the DLQ
115
+ * @throws {QueueNotFoundError} If the queue does not exist
116
+ * @throws {QueueError} If the API request fails
117
+ *
118
+ * @example
119
+ * ```typescript
120
+ * // Replay a failed message after fixing the bug
121
+ * const message = await replayDeadLetterMessage(client, 'order-queue', 'msg_abc123');
122
+ * console.log(`Replayed message ${message.id}, now in state: ${message.state}`);
123
+ * ```
124
+ */
125
+ export async function replayDeadLetterMessage(
126
+ client: APIClient,
127
+ queueName: string,
128
+ messageId: string,
129
+ options?: QueueApiOptions
130
+ ): Promise<Message> {
131
+ validateQueueName(queueName);
132
+ validateMessageId(messageId);
133
+
134
+ const url = queueApiPath('dlq/replay', queueName, messageId);
135
+ const resp = await client.post(
136
+ url,
137
+ undefined,
138
+ ReplayDlqResponseSchema,
139
+ undefined,
140
+ undefined,
141
+ buildQueueHeaders(options?.orgId)
142
+ );
143
+
144
+ if (resp.success) {
145
+ return resp.data.message;
146
+ }
147
+
148
+ if (resp.message?.includes('message') && resp.message?.includes('not found')) {
149
+ throw new MessageNotFoundError({
150
+ queueName,
151
+ messageId,
152
+ message: resp.message,
153
+ });
154
+ }
155
+
156
+ if (resp.message?.includes('queue') && resp.message?.includes('not found')) {
157
+ throw new QueueNotFoundError({
158
+ queueName,
159
+ message: resp.message,
160
+ });
161
+ }
162
+
163
+ throw new QueueError({
164
+ queueName,
165
+ message: resp.message || 'Failed to replay dead letter message',
166
+ });
167
+ }
168
+
169
+ /**
170
+ * Purge all messages from a dead letter queue.
171
+ *
172
+ * Permanently deletes all messages in the dead letter queue. This operation
173
+ * cannot be undone. Use with caution - consider reviewing or exporting
174
+ * messages before purging.
175
+ *
176
+ * @param client - The API client instance
177
+ * @param queueName - The name of the queue whose DLQ to purge
178
+ * @returns void
179
+ * @throws {QueueValidationError} If validation fails (invalid queue name)
180
+ * @throws {QueueNotFoundError} If the queue does not exist
181
+ * @throws {QueueError} If the API request fails
182
+ *
183
+ * @example
184
+ * ```typescript
185
+ * // Purge all dead letter messages after investigation
186
+ * await purgeDeadLetter(client, 'order-queue');
187
+ * console.log('All dead letter messages have been deleted');
188
+ * ```
189
+ */
190
+ export async function purgeDeadLetter(
191
+ client: APIClient,
192
+ queueName: string,
193
+ options?: QueueApiOptions
194
+ ): Promise<void> {
195
+ validateQueueName(queueName);
196
+ const url = queueApiPath('dlq/purge', queueName);
197
+ const resp = await client.delete(
198
+ url,
199
+ DeleteDlqResponseSchema,
200
+ undefined,
201
+ buildQueueHeaders(options?.orgId)
202
+ );
203
+
204
+ if (resp.success) {
205
+ return;
206
+ }
207
+
208
+ if (resp.message?.includes('not found')) {
209
+ throw new QueueNotFoundError({
210
+ queueName,
211
+ message: resp.message,
212
+ });
213
+ }
214
+
215
+ throw new QueueError({
216
+ queueName,
217
+ message: resp.message || 'Failed to purge dead letter queue',
218
+ });
219
+ }
220
+
221
+ /**
222
+ * Delete a specific message from the dead letter queue.
223
+ *
224
+ * Permanently removes a single message from the dead letter queue.
225
+ * Use this when you've determined that a specific failed message
226
+ * should not be retried and can be discarded.
227
+ *
228
+ * @param client - The API client instance
229
+ * @param queueName - The name of the queue
230
+ * @param messageId - The message ID to delete (prefixed with msg_)
231
+ * @returns void
232
+ * @throws {QueueValidationError} If validation fails (invalid queue name or message ID)
233
+ * @throws {MessageNotFoundError} If the message does not exist in the DLQ
234
+ * @throws {QueueError} If the API request fails
235
+ *
236
+ * @example
237
+ * ```typescript
238
+ * // Delete a message that cannot be recovered
239
+ * await deleteDeadLetterMessage(client, 'order-queue', 'msg_abc123');
240
+ * console.log('Dead letter message deleted');
241
+ * ```
242
+ */
243
+ export async function deleteDeadLetterMessage(
244
+ client: APIClient,
245
+ queueName: string,
246
+ messageId: string,
247
+ options?: QueueApiOptions
248
+ ): Promise<void> {
249
+ validateQueueName(queueName);
250
+ validateMessageId(messageId);
251
+
252
+ const url = queueApiPath('dlq/delete', queueName, messageId);
253
+ const resp = await client.delete(
254
+ url,
255
+ DeleteDlqResponseSchema,
256
+ undefined,
257
+ buildQueueHeaders(options?.orgId)
258
+ );
259
+
260
+ if (resp.success) {
261
+ return;
262
+ }
263
+
264
+ if (resp.message?.includes('queue') && resp.message?.includes('not found')) {
265
+ throw new QueueNotFoundError({
266
+ queueName,
267
+ message: resp.message,
268
+ });
269
+ }
270
+
271
+ if (resp.message?.includes('message') && resp.message?.includes('not found')) {
272
+ throw new MessageNotFoundError({
273
+ queueName,
274
+ messageId,
275
+ message: resp.message,
276
+ });
277
+ }
278
+
279
+ throw new QueueError({
280
+ queueName,
281
+ message: resp.message || 'Failed to delete dead letter message',
282
+ });
283
+ }